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:
David Bonnes 2019-07-22 09:45:31 +01:00 committed by Pascal Vizeli
parent 11c74cd0d7
commit bf37cc8371
3 changed files with 54 additions and 44 deletions

View File

@ -22,6 +22,7 @@ from homeassistant.helpers.dispatcher import (
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import (
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 .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
def setup(hass, hass_config) -> bool:
def setup(hass: HomeAssistantType, hass_config: ConfigType) -> bool:
"""Create a (EMEA/EU-based) Honeywell evohome system."""
broker = EvoBroker(hass, hass_config[DOMAIN])
if not broker.init_client():
@ -270,29 +271,29 @@ class EvoDevice(Entity):
self._state_attributes = []
self._supported_features = None
self._setpoints = None
self._schedule = {}
@callback
def _refresh(self, packet):
if packet['signal'] == 'refresh':
self.async_schedule_update_ha_state(force_refresh=True)
def get_setpoints(self) -> Optional[Dict[str, Any]]:
"""Return the current/next scheduled switchpoints.
@property
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 = {}
schedule = self._evo_device.schedule()
if not self._schedule['DailySchedules']:
return {}
if not schedule['DailySchedules']:
return None
switchpoints = {}
day_time = datetime.now()
day_of_week = int(day_time.strftime('%w')) # 0 is Sunday
# 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
for i, tmp in enumerate(day['Switchpoints']):
if day_time.strftime('%H:%M:%S') > tmp['TimeOfDay']:
@ -311,7 +312,7 @@ class EvoDevice(Entity):
spt = switchpoints[key] = {}
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]
dt_naive = datetime.strptime(
@ -345,7 +346,7 @@ class EvoDevice(Entity):
status[attr] = getattr(self._evo_device, attr)
if 'setpoints' in self._state_attributes:
status['setpoints'] = self._setpoints
status['setpoints'] = self.setpoints
return {'status': status}
@ -373,6 +374,12 @@ class EvoDevice(Entity):
"""Return the temperature unit to use in the frontend UI."""
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:
"""Get the latest state data."""
self._setpoints = self.get_setpoints()
self._update_schedule()

View File

@ -8,16 +8,17 @@ import evohomeclient2
from homeassistant.components.climate import ClimateDevice
from homeassistant.components.climate.const import (
HVAC_MODE_HEAT, HVAC_MODE_AUTO, HVAC_MODE_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,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_PRESET_MODE)
from homeassistant.const import PRECISION_TENTHS
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from homeassistant.util.dt import parse_datetime
from . import CONF_LOCATION_IDX, _handle_exception, EvoDevice
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)
_LOGGER = logging.getLogger(__name__)
@ -30,14 +31,15 @@ HA_HVAC_TO_TCS = {
HVAC_MODE_HEAT: EVO_AUTO,
}
HA_PRESET_TO_TCS = {
PRESET_AWAY: EVO_AWAY,
PRESET_CUSTOM: EVO_CUSTOM,
PRESET_ECO: EVO_AUTOECO,
PRESET_HOME: EVO_DAYOFF,
PRESET_RESET: EVO_RESET,
}
TCS_PRESET_TO_HA = {v: k for k, v in HA_PRESET_TO_TCS.items()}
TCS_PRESET_TO_HA = {
EVO_AWAY: PRESET_AWAY,
EVO_CUSTOM: PRESET_CUSTOM,
EVO_AUTOECO: PRESET_ECO,
EVO_DAYOFF: PRESET_HOME,
EVO_RESET: PRESET_RESET,
} # EVO_AUTO: None,
HA_PRESET_TO_TCS = {v: k for k, v in TCS_PRESET_TO_HA.items()}
EVO_PRESET_TO_HA = {
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()}
def setup_platform(hass, hass_config, add_entities,
discovery_info=None) -> None:
def setup_platform(hass: HomeAssistantType, hass_config: ConfigType,
add_entities, discovery_info=None) -> None:
"""Create the evohome Controller, and its Zones, if any."""
broker = hass.data[DOMAIN]['broker']
loc_idx = broker.params[CONF_LOCATION_IDX]
@ -102,21 +104,22 @@ class EvoClimateDevice(EvoDevice, ClimateDevice):
_handle_exception(err)
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
a function of their schedule, and the Controller's operating_mode, e.g.
Economy mode is their scheduled setpoint less (usually) 3C.
a function of their own schedule and the Controller's operating mode,
e.g. 'AutoWithEco' mode means their setpoint is (by default) 3C less
than scheduled.
However, Zones can override these setpoints, either for a specified
period of time, 'TemporaryOverride', after which they will revert back
to 'FollowSchedule' mode, or indefinitely, 'PermanentOverride'.
However, Zones can _override_ these setpoints, either indefinitely,
'PermanentOverride' mode, or for a period of time, 'TemporaryOverride',
after which they will revert back to 'FollowSchedule'.
Some of the Controller's operating_mode are 'forced' upon the Zone,
regardless of its override state, e.g. 'HeatingOff' (Zones to min_temp)
and 'Away' (Zones to 12C).
Finally, some of the Controller's operating modes are _forced_ upon the
Zones, regardless of any override mode, e.g. 'HeatingOff', Zones to
(by default) 5C, and 'Away', Zones to (by default) 12C.
"""
if op_mode == EVO_FOLLOW:
try:
@ -129,10 +132,10 @@ class EvoClimateDevice(EvoDevice, ClimateDevice):
temperature = self._evo_device.setpointStatus['targetHeatTemperature']
until = None # EVO_PERMOVER
if op_mode == EVO_TEMPOVER:
self._setpoints = self.get_setpoints()
if self._setpoints:
until = parse_datetime(self._setpoints['next']['from'])
if op_mode == EVO_TEMPOVER and self._schedule['DailySchedules']:
self._update_schedule()
if self._schedule['DailySchedules']:
until = parse_datetime(self.setpoints['next']['from'])
self._set_temperature(temperature, until=until)
@ -147,7 +150,7 @@ class EvoClimateDevice(EvoDevice, ClimateDevice):
@property
def hvac_modes(self) -> List[str]:
"""Return the list of available hvac operation modes."""
return [HVAC_MODE_OFF, HVAC_MODE_HEAT]
return list(HA_HVAC_TO_TCS)
@property
def preset_modes(self) -> Optional[List[str]]:

View File

@ -75,10 +75,10 @@ class EvoDHW(EvoDevice, WaterHeaterDevice):
state = '' if op_mode == EVO_FOLLOW else HA_STATE_TO_EVO[STATE_OFF]
until = None # EVO_FOLLOW, EVO_PERMOVER
if op_mode == EVO_TEMPOVER:
self._setpoints = self.get_setpoints()
if self._setpoints:
until = parse_datetime(self._setpoints['next']['from'])
if op_mode == EVO_TEMPOVER and self._schedule['DailySchedules']:
self._update_schedule()
if self._schedule['DailySchedules']:
until = parse_datetime(self.setpoints['next']['from'])
until = until.strftime(EVO_STRFTIME)
data = {'Mode': op_mode, 'State': state, 'UntilTime': until}