Skip to content

Commit fcc9d6b

Browse files
committed
Cleanup
1 parent 9029cae commit fcc9d6b

File tree

5 files changed

+36
-78
lines changed

5 files changed

+36
-78
lines changed

custom_components/sat/area.py

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919
from .heating_curve import HeatingCurve
2020
from .helpers import float_value, is_state_stale, state_age_seconds
2121
from .pid import PID, PID_UPDATE_INTERVAL
22-
from .temperature.state import TemperatureStates, TemperatureState
22+
from .temperature.state import TemperatureState
2323

2424
_LOGGER = logging.getLogger(__name__)
2525

2626
ATTR_TEMPERATURE = "temperature"
2727
ATTR_CURRENT_TEMPERATURE = "current_temperature"
28-
ATTR_CURRENT_VALVE_POSITION = "current_valve_position"
29-
3028
ATTR_SENSOR_TEMPERATURE_ID = "sensor_temperature_id"
29+
ATTR_CURRENT_VALVE_POSITION = "current_valve_position"
3130

3231
COMFORT_BAND = 0.1
3332
COOLING_SLOPE = 4.0
@@ -81,7 +80,7 @@ def climate_state(self) -> Optional[State]:
8180

8281
return state if state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE] else None
8382

84-
def temperature_state(self) -> Optional[State]:
83+
def sensor_state(self) -> Optional[State]:
8584
"""Return the source state used to calculate the current temperature."""
8685
if (self._hass is None) or (climate_state := self.climate_state) is None:
8786
return None
@@ -105,7 +104,7 @@ def temperature_state(self) -> Optional[State]:
105104
@property
106105
def current_temperature(self) -> Optional[float]:
107106
"""Retrieve the current temperature, overridden by a sensor if set."""
108-
if (state := self.temperature_state()) is None:
107+
if (state := self.sensor_state()) is None:
109108
return None
110109

111110
if sensor.DOMAIN in state.entity_id:
@@ -125,10 +124,9 @@ def target_temperature(self) -> Optional[float]:
125124
return float_value(state.attributes.get("temperature"))
126125

127126
@property
128-
def error(self) -> Optional[TemperatureState]:
129-
"""Calculate the temperature error (target - current)."""
130-
temperature_state = self.temperature_state()
131-
if temperature_state is None:
127+
def temperature_state(self) -> Optional[TemperatureState]:
128+
"""Calculate the temperature state."""
129+
if (sensor_state := self.sensor_state()) is None:
132130
return None
133131

134132
target_temperature = self.target_temperature
@@ -141,9 +139,9 @@ def error(self) -> Optional[TemperatureState]:
141139
entity_id=self._entity_id,
142140
setpoint=target_temperature,
143141
current=current_temperature,
144-
last_reported=temperature_state.last_reported,
145-
last_updated=temperature_state.last_updated,
146-
last_changed=temperature_state.last_changed,
142+
last_reported=sensor_state.last_reported,
143+
last_updated=sensor_state.last_updated,
144+
last_changed=sensor_state.last_changed,
147145
)
148146

149147
@property
@@ -228,11 +226,11 @@ async def async_outside_entity_changed(self, _event: Event[EventStateChangedData
228226

229227
def control_pid(self, _time: Optional[datetime] = None) -> None:
230228
"""Update the PID controller with the current error and heating curve."""
231-
if (error := self.error) is None:
232-
_LOGGER.debug("Skipping control loop for %s because error could not be computed", self._entity_id)
229+
if (temperature_state := self.temperature_state) is None:
230+
_LOGGER.debug("Skipping control loop for %s because the temperature is not available.", self._entity_id)
233231
return
234232

235-
self.pid.update(error)
233+
self.pid.update(temperature_state)
236234

237235

238236
class Areas:
@@ -246,12 +244,6 @@ def __init__(self, areas: list[Area]) -> None:
246244
def from_config(config: SatConfig) -> "Areas":
247245
return Areas([Area(config, entity_id) for entity_id in config.rooms])
248246

249-
@property
250-
def errors(self) -> TemperatureStates:
251-
"""Return a collection of all the error values for all areas."""
252-
error_list = [area.error for area in self._areas if area.error is not None]
253-
return TemperatureStates(error_list)
254-
255247
@property
256248
def pids(self) -> "Areas._PIDs":
257249
"""Return an interface to reset PID controllers for all areas."""
@@ -342,15 +334,14 @@ def overshoot_cap(self) -> Optional[float]:
342334
if not area.pid.available or not area.requires_heat:
343335
continue
344336

345-
error = area.error
346-
if error is None:
337+
if (temperature_state := area.temperature_state) is None:
347338
continue
348339

349-
if error.error >= -OVERSHOOT_MARGIN:
340+
if temperature_state.error >= -OVERSHOOT_MARGIN:
350341
continue
351342

352343
# Degrees above target (positive number)
353-
degrees_over = -error.error
344+
degrees_over = -temperature_state.error
354345

355346
# Start from a “max allowed in cooling” and pull it down with overshoot severity.
356347
caps.append(COLD_SETPOINT + COOLING_HEADROOM - COOLING_SLOPE * degrees_over)

custom_components/sat/climate.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def temperature_statistics(self) -> TemperatureStatistics:
227227
return self._temperature_history.statistics
228228

229229
@property
230-
def error(self) -> Optional[TemperatureState]:
230+
def temperature_state(self) -> Optional[TemperatureState]:
231231
"""Return the error value."""
232232
if self._config.sensors.inside_sensor_entity_id is None:
233233
return None
@@ -329,24 +329,25 @@ def requested_setpoint(self) -> float:
329329
def weighted_error(self) -> Optional[float]:
330330
errors: list[tuple[float, float]] = []
331331

332-
if (primary_error := self.error) is not None:
333-
errors.append((primary_error.error, 1.0))
332+
if (primary_temperature := self.temperature_state) is not None:
333+
errors.append((primary_temperature.error, 1.0))
334334

335335
for area in self.areas.items():
336-
if (area_error := area.error) is None:
336+
if (temperature := area.temperature_state) is None:
337337
continue
338338

339-
errors.append((area_error.error, area.room_weight))
339+
errors.append((temperature.error, area.room_weight))
340340

341341
if not errors:
342342
return None
343343

344-
weight_sum = sum(weight for _, weight in errors)
345-
if weight_sum <= 0.0:
344+
total = sum(weight for _, weight in errors)
345+
346+
if total <= 0.0:
346347
return None
347348

348349
weighted_error = sum(error * weight for error, weight in errors)
349-
return weighted_error / weight_sum
350+
return weighted_error / total
350351

351352
@property
352353
def valves_open(self) -> bool:
@@ -415,15 +416,14 @@ def control_pid(self, _time: Optional[datetime] = None) -> None:
415416
self.pid.reset()
416417
return
417418

418-
if (error := self.error) is None:
419-
_LOGGER.debug("Skipping control loop for %s because error value is not available.", self.entity_id)
419+
if (temperature_state := self.temperature_state) is None:
420+
_LOGGER.debug("Skipping control loop for %s because temperature is not available.", self.entity_id)
420421
return
421422

422-
if self.hvac_mode == HVACMode.HEAT and self.valves_open:
423-
if (weighted_error := self.weighted_error) is not None:
424-
self._temperature_history.record(weighted_error, event_timestamp(_time))
423+
if self.hvac_mode == HVACMode.HEAT and self.valves_open and (weighted_error := self.weighted_error) is not None:
424+
self._temperature_history.record(weighted_error, event_timestamp(_time))
425425

426-
self.pid.update(error)
426+
self.pid.update(temperature_state)
427427

428428
def schedule_heating_control_loop(self, _time: Optional[datetime] = None, force: bool = False) -> None:
429429
"""Schedule a debounced execution of the heating control loop."""

custom_components/sat/pid.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
DERIVATIVE_ALPHA1 = 0.2
2020
DERIVATIVE_ALPHA2 = 0.1
2121
DERIVATIVE_RAW_CAP = 5.0
22-
DEADBAND_EPSILON = 1e-6
2322

2423
PID_UPDATE_INTERVAL = 60
2524

@@ -184,7 +183,7 @@ def update(self, state: TemperatureState) -> None:
184183

185184
def _update_integral(self, state: TemperatureState) -> None:
186185
"""Update the integral value in the PID controller."""
187-
if abs(state.error) > (DEADBAND + DEADBAND_EPSILON):
186+
if abs(state.error) > DEADBAND:
188187
self._integral = 0.0
189188
return
190189

custom_components/sat/sensor.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
7070

7171
class SatRequestedSetpoint(SatClimateEntity, SensorEntity):
7272
async def async_added_to_hass(self) -> None:
73-
def on_pid_updated(entity_id: str) -> None:
73+
def on_pid_updated(_) -> None:
7474
self.schedule_update_ha_state()
7575

7676
await super().async_added_to_hass()
@@ -383,15 +383,15 @@ def native_unit_of_measurement(self):
383383
@property
384384
def native_value(self) -> Optional[float]:
385385
"""Return the state of the device in native units."""
386-
if (error := self._climate.error) is None:
386+
if (temperature_state := self._climate.temperature_state) is None:
387387
return None
388388

389-
return error.error
389+
return temperature_state.error
390390

391391
@property
392392
def available(self):
393393
"""Return availability of the sensor."""
394-
return self._climate.error is not None
394+
return self._climate.temperature_state is not None
395395

396396
@property
397397
def unique_id(self) -> str:
Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,5 @@
11
from dataclasses import dataclass
22
from datetime import datetime
3-
from typing import Optional, Iterable, Iterator
4-
5-
6-
class TemperatureStates:
7-
"""A collection of TemperatureState objects with utility methods."""
8-
9-
def __init__(self, errors: Optional[Iterable["TemperatureState"]] = None) -> None:
10-
self._errors: list["TemperatureState"] = list(errors) if errors else []
11-
12-
def __iter__(self) -> Iterator["TemperatureState"]:
13-
return iter(self._errors)
14-
15-
def __len__(self) -> int:
16-
return len(self._errors)
17-
18-
def __getitem__(self, index: int) -> "TemperatureState":
19-
return self._errors[index]
20-
21-
def __add__(self, other: "TemperatureStates") -> "TemperatureStates":
22-
return self.merge(other)
23-
24-
def add(self, error: "TemperatureState") -> None:
25-
self._errors.append(error)
26-
27-
def merge(self, other: "TemperatureStates") -> "TemperatureStates":
28-
return TemperatureStates(self._errors + other._errors)
29-
30-
def max(self) -> Optional["TemperatureState"]:
31-
if not self._errors:
32-
return None
33-
34-
return max(self._errors, key=lambda e: e.error)
353

364

375
@dataclass(frozen=True, slots=True)
@@ -47,4 +15,4 @@ class TemperatureState:
4715
@property
4816
def error(self) -> float:
4917
"""Return the temperature error."""
50-
return self.setpoint - self.current
18+
return round(self.setpoint - self.current, 3)

0 commit comments

Comments
 (0)