mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 23:27:37 +00:00
Tweak evohome migration (#25281)
* Initial commit add hvac_action to zones, remove target_temp from controller fix incorrect hvac_action de-lint Initial commit de-lint & minor refactor tweak docstrings, and type hints tweak docstrings * refactor setpoints property * tweak docstring * tweak docstring * avoid a unnecessary I/O * avoid unnecessary I/O * refactor schedule/setpoints * remove type hint * remove type hint 2 * tweak code * delint type hints * fix regression
This commit is contained in:
parent
11c74cd0d7
commit
bf37cc8371
@ -22,6 +22,7 @@ from homeassistant.helpers.dispatcher import (
|
|||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.event import (
|
from homeassistant.helpers.event import (
|
||||||
async_track_point_in_utc_time, track_time_interval)
|
async_track_point_in_utc_time, track_time_interval)
|
||||||
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
from homeassistant.util.dt import parse_datetime, utcnow
|
from homeassistant.util.dt import parse_datetime, utcnow
|
||||||
|
|
||||||
from .const import DOMAIN, STORAGE_VERSION, STORAGE_KEY, GWS, TCS
|
from .const import DOMAIN, STORAGE_VERSION, STORAGE_KEY, GWS, TCS
|
||||||
@ -101,7 +102,7 @@ def _handle_exception(err) -> bool:
|
|||||||
raise # we don't expect/handle any other HTTPErrors
|
raise # we don't expect/handle any other HTTPErrors
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, hass_config) -> bool:
|
def setup(hass: HomeAssistantType, hass_config: ConfigType) -> bool:
|
||||||
"""Create a (EMEA/EU-based) Honeywell evohome system."""
|
"""Create a (EMEA/EU-based) Honeywell evohome system."""
|
||||||
broker = EvoBroker(hass, hass_config[DOMAIN])
|
broker = EvoBroker(hass, hass_config[DOMAIN])
|
||||||
if not broker.init_client():
|
if not broker.init_client():
|
||||||
@ -270,29 +271,29 @@ class EvoDevice(Entity):
|
|||||||
self._state_attributes = []
|
self._state_attributes = []
|
||||||
|
|
||||||
self._supported_features = None
|
self._supported_features = None
|
||||||
self._setpoints = None
|
self._schedule = {}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _refresh(self, packet):
|
def _refresh(self, packet):
|
||||||
if packet['signal'] == 'refresh':
|
if packet['signal'] == 'refresh':
|
||||||
self.async_schedule_update_ha_state(force_refresh=True)
|
self.async_schedule_update_ha_state(force_refresh=True)
|
||||||
|
|
||||||
def get_setpoints(self) -> Optional[Dict[str, Any]]:
|
@property
|
||||||
"""Return the current/next scheduled switchpoints.
|
def setpoints(self) -> Dict[str, Any]:
|
||||||
|
"""Return the current/next setpoints from the schedule.
|
||||||
|
|
||||||
Only Zones & DHW controllers (but not the TCS) have schedules.
|
Only Zones & DHW controllers (but not the TCS) can have schedules.
|
||||||
"""
|
"""
|
||||||
switchpoints = {}
|
if not self._schedule['DailySchedules']:
|
||||||
schedule = self._evo_device.schedule()
|
return {}
|
||||||
|
|
||||||
if not schedule['DailySchedules']:
|
switchpoints = {}
|
||||||
return None
|
|
||||||
|
|
||||||
day_time = datetime.now()
|
day_time = datetime.now()
|
||||||
day_of_week = int(day_time.strftime('%w')) # 0 is Sunday
|
day_of_week = int(day_time.strftime('%w')) # 0 is Sunday
|
||||||
|
|
||||||
# Iterate today's switchpoints until past the current time of day...
|
# Iterate today's switchpoints until past the current time of day...
|
||||||
day = schedule['DailySchedules'][day_of_week]
|
day = self._schedule['DailySchedules'][day_of_week]
|
||||||
sp_idx = -1 # last switchpoint of the day before
|
sp_idx = -1 # last switchpoint of the day before
|
||||||
for i, tmp in enumerate(day['Switchpoints']):
|
for i, tmp in enumerate(day['Switchpoints']):
|
||||||
if day_time.strftime('%H:%M:%S') > tmp['TimeOfDay']:
|
if day_time.strftime('%H:%M:%S') > tmp['TimeOfDay']:
|
||||||
@ -311,7 +312,7 @@ class EvoDevice(Entity):
|
|||||||
spt = switchpoints[key] = {}
|
spt = switchpoints[key] = {}
|
||||||
|
|
||||||
sp_date = (day_time + timedelta(days=offset)).strftime('%Y-%m-%d')
|
sp_date = (day_time + timedelta(days=offset)).strftime('%Y-%m-%d')
|
||||||
day = schedule['DailySchedules'][(day_of_week + offset) % 7]
|
day = self._schedule['DailySchedules'][(day_of_week + offset) % 7]
|
||||||
switchpoint = day['Switchpoints'][idx]
|
switchpoint = day['Switchpoints'][idx]
|
||||||
|
|
||||||
dt_naive = datetime.strptime(
|
dt_naive = datetime.strptime(
|
||||||
@ -345,7 +346,7 @@ class EvoDevice(Entity):
|
|||||||
status[attr] = getattr(self._evo_device, attr)
|
status[attr] = getattr(self._evo_device, attr)
|
||||||
|
|
||||||
if 'setpoints' in self._state_attributes:
|
if 'setpoints' in self._state_attributes:
|
||||||
status['setpoints'] = self._setpoints
|
status['setpoints'] = self.setpoints
|
||||||
|
|
||||||
return {'status': status}
|
return {'status': status}
|
||||||
|
|
||||||
@ -373,6 +374,12 @@ class EvoDevice(Entity):
|
|||||||
"""Return the temperature unit to use in the frontend UI."""
|
"""Return the temperature unit to use in the frontend UI."""
|
||||||
return TEMP_CELSIUS
|
return TEMP_CELSIUS
|
||||||
|
|
||||||
|
def _update_schedule(self) -> None:
|
||||||
|
"""Get the latest state data."""
|
||||||
|
if not self._schedule.get('DailySchedules') or \
|
||||||
|
parse_datetime(self.setpoints['next']['from']) < utcnow():
|
||||||
|
self._schedule = self._evo_device.schedule()
|
||||||
|
|
||||||
def update(self) -> None:
|
def update(self) -> None:
|
||||||
"""Get the latest state data."""
|
"""Get the latest state data."""
|
||||||
self._setpoints = self.get_setpoints()
|
self._update_schedule()
|
||||||
|
@ -8,16 +8,17 @@ import evohomeclient2
|
|||||||
|
|
||||||
from homeassistant.components.climate import ClimateDevice
|
from homeassistant.components.climate import ClimateDevice
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
HVAC_MODE_HEAT, HVAC_MODE_AUTO, HVAC_MODE_OFF,
|
|
||||||
CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, CURRENT_HVAC_OFF,
|
CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, CURRENT_HVAC_OFF,
|
||||||
|
HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF,
|
||||||
PRESET_AWAY, PRESET_ECO, PRESET_HOME, PRESET_NONE,
|
PRESET_AWAY, PRESET_ECO, PRESET_HOME, PRESET_NONE,
|
||||||
SUPPORT_TARGET_TEMPERATURE, SUPPORT_PRESET_MODE)
|
SUPPORT_TARGET_TEMPERATURE, SUPPORT_PRESET_MODE)
|
||||||
from homeassistant.const import PRECISION_TENTHS
|
from homeassistant.const import PRECISION_TENTHS
|
||||||
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
from homeassistant.util.dt import parse_datetime
|
from homeassistant.util.dt import parse_datetime
|
||||||
|
|
||||||
from . import CONF_LOCATION_IDX, _handle_exception, EvoDevice
|
from . import CONF_LOCATION_IDX, _handle_exception, EvoDevice
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN, EVO_RESET, EVO_AUTO, EVO_AUTOECO, EVO_AWAY, EVO_DAYOFF, EVO_CUSTOM,
|
DOMAIN, EVO_RESET, EVO_AUTO, EVO_AUTOECO, EVO_AWAY, EVO_CUSTOM, EVO_DAYOFF,
|
||||||
EVO_HEATOFF, EVO_FOLLOW, EVO_TEMPOVER, EVO_PERMOVER)
|
EVO_HEATOFF, EVO_FOLLOW, EVO_TEMPOVER, EVO_PERMOVER)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -30,14 +31,15 @@ HA_HVAC_TO_TCS = {
|
|||||||
HVAC_MODE_HEAT: EVO_AUTO,
|
HVAC_MODE_HEAT: EVO_AUTO,
|
||||||
}
|
}
|
||||||
|
|
||||||
HA_PRESET_TO_TCS = {
|
TCS_PRESET_TO_HA = {
|
||||||
PRESET_AWAY: EVO_AWAY,
|
EVO_AWAY: PRESET_AWAY,
|
||||||
PRESET_CUSTOM: EVO_CUSTOM,
|
EVO_CUSTOM: PRESET_CUSTOM,
|
||||||
PRESET_ECO: EVO_AUTOECO,
|
EVO_AUTOECO: PRESET_ECO,
|
||||||
PRESET_HOME: EVO_DAYOFF,
|
EVO_DAYOFF: PRESET_HOME,
|
||||||
PRESET_RESET: EVO_RESET,
|
EVO_RESET: PRESET_RESET,
|
||||||
}
|
} # EVO_AUTO: None,
|
||||||
TCS_PRESET_TO_HA = {v: k for k, v in HA_PRESET_TO_TCS.items()}
|
|
||||||
|
HA_PRESET_TO_TCS = {v: k for k, v in TCS_PRESET_TO_HA.items()}
|
||||||
|
|
||||||
EVO_PRESET_TO_HA = {
|
EVO_PRESET_TO_HA = {
|
||||||
EVO_FOLLOW: PRESET_NONE,
|
EVO_FOLLOW: PRESET_NONE,
|
||||||
@ -47,8 +49,8 @@ EVO_PRESET_TO_HA = {
|
|||||||
HA_PRESET_TO_EVO = {v: k for k, v in EVO_PRESET_TO_HA.items()}
|
HA_PRESET_TO_EVO = {v: k for k, v in EVO_PRESET_TO_HA.items()}
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, hass_config, add_entities,
|
def setup_platform(hass: HomeAssistantType, hass_config: ConfigType,
|
||||||
discovery_info=None) -> None:
|
add_entities, discovery_info=None) -> None:
|
||||||
"""Create the evohome Controller, and its Zones, if any."""
|
"""Create the evohome Controller, and its Zones, if any."""
|
||||||
broker = hass.data[DOMAIN]['broker']
|
broker = hass.data[DOMAIN]['broker']
|
||||||
loc_idx = broker.params[CONF_LOCATION_IDX]
|
loc_idx = broker.params[CONF_LOCATION_IDX]
|
||||||
@ -102,21 +104,22 @@ class EvoClimateDevice(EvoDevice, ClimateDevice):
|
|||||||
_handle_exception(err)
|
_handle_exception(err)
|
||||||
|
|
||||||
def _set_zone_mode(self, op_mode: str) -> None:
|
def _set_zone_mode(self, op_mode: str) -> None:
|
||||||
"""Set the Zone to one of its native EVO_* operating modes.
|
"""Set a Zone to one of its native EVO_* operating modes.
|
||||||
|
|
||||||
NB: evohome Zones 'inherit' their operating mode from the Controller.
|
Zones inherit their _effective_ operating mode from the Controller.
|
||||||
|
|
||||||
Usually, Zones are in 'FollowSchedule' mode, where their setpoints are
|
Usually, Zones are in 'FollowSchedule' mode, where their setpoints are
|
||||||
a function of their schedule, and the Controller's operating_mode, e.g.
|
a function of their own schedule and the Controller's operating mode,
|
||||||
Economy mode is their scheduled setpoint less (usually) 3C.
|
e.g. 'AutoWithEco' mode means their setpoint is (by default) 3C less
|
||||||
|
than scheduled.
|
||||||
|
|
||||||
However, Zones can override these setpoints, either for a specified
|
However, Zones can _override_ these setpoints, either indefinitely,
|
||||||
period of time, 'TemporaryOverride', after which they will revert back
|
'PermanentOverride' mode, or for a period of time, 'TemporaryOverride',
|
||||||
to 'FollowSchedule' mode, or indefinitely, 'PermanentOverride'.
|
after which they will revert back to 'FollowSchedule'.
|
||||||
|
|
||||||
Some of the Controller's operating_mode are 'forced' upon the Zone,
|
Finally, some of the Controller's operating modes are _forced_ upon the
|
||||||
regardless of its override state, e.g. 'HeatingOff' (Zones to min_temp)
|
Zones, regardless of any override mode, e.g. 'HeatingOff', Zones to
|
||||||
and 'Away' (Zones to 12C).
|
(by default) 5C, and 'Away', Zones to (by default) 12C.
|
||||||
"""
|
"""
|
||||||
if op_mode == EVO_FOLLOW:
|
if op_mode == EVO_FOLLOW:
|
||||||
try:
|
try:
|
||||||
@ -129,10 +132,10 @@ class EvoClimateDevice(EvoDevice, ClimateDevice):
|
|||||||
temperature = self._evo_device.setpointStatus['targetHeatTemperature']
|
temperature = self._evo_device.setpointStatus['targetHeatTemperature']
|
||||||
until = None # EVO_PERMOVER
|
until = None # EVO_PERMOVER
|
||||||
|
|
||||||
if op_mode == EVO_TEMPOVER:
|
if op_mode == EVO_TEMPOVER and self._schedule['DailySchedules']:
|
||||||
self._setpoints = self.get_setpoints()
|
self._update_schedule()
|
||||||
if self._setpoints:
|
if self._schedule['DailySchedules']:
|
||||||
until = parse_datetime(self._setpoints['next']['from'])
|
until = parse_datetime(self.setpoints['next']['from'])
|
||||||
|
|
||||||
self._set_temperature(temperature, until=until)
|
self._set_temperature(temperature, until=until)
|
||||||
|
|
||||||
@ -147,7 +150,7 @@ class EvoClimateDevice(EvoDevice, ClimateDevice):
|
|||||||
@property
|
@property
|
||||||
def hvac_modes(self) -> List[str]:
|
def hvac_modes(self) -> List[str]:
|
||||||
"""Return the list of available hvac operation modes."""
|
"""Return the list of available hvac operation modes."""
|
||||||
return [HVAC_MODE_OFF, HVAC_MODE_HEAT]
|
return list(HA_HVAC_TO_TCS)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_modes(self) -> Optional[List[str]]:
|
def preset_modes(self) -> Optional[List[str]]:
|
||||||
|
@ -75,10 +75,10 @@ class EvoDHW(EvoDevice, WaterHeaterDevice):
|
|||||||
state = '' if op_mode == EVO_FOLLOW else HA_STATE_TO_EVO[STATE_OFF]
|
state = '' if op_mode == EVO_FOLLOW else HA_STATE_TO_EVO[STATE_OFF]
|
||||||
until = None # EVO_FOLLOW, EVO_PERMOVER
|
until = None # EVO_FOLLOW, EVO_PERMOVER
|
||||||
|
|
||||||
if op_mode == EVO_TEMPOVER:
|
if op_mode == EVO_TEMPOVER and self._schedule['DailySchedules']:
|
||||||
self._setpoints = self.get_setpoints()
|
self._update_schedule()
|
||||||
if self._setpoints:
|
if self._schedule['DailySchedules']:
|
||||||
until = parse_datetime(self._setpoints['next']['from'])
|
until = parse_datetime(self.setpoints['next']['from'])
|
||||||
until = until.strftime(EVO_STRFTIME)
|
until = until.strftime(EVO_STRFTIME)
|
||||||
|
|
||||||
data = {'Mode': op_mode, 'State': state, 'UntilTime': until}
|
data = {'Mode': op_mode, 'State': state, 'UntilTime': until}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user