From 81bde77c048885ed75f4404ddccf7e679f253b39 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 21 Jul 2019 13:02:16 -0700 Subject: [PATCH 01/13] Updated frontend to 20190721.1 --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 9f3f4cbc1c4..1358264b7a8 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/components/frontend", "requirements": [ - "home-assistant-frontend==20190721.0" + "home-assistant-frontend==20190721.1" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 0c4120a4c38..e80d9912758 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ certifi>=2019.6.16 cryptography==2.7 distro==1.4.0 hass-nabucasa==0.15 -home-assistant-frontend==20190721.0 +home-assistant-frontend==20190721.1 importlib-metadata==0.18 jinja2>=2.10.1 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index e6c23917b24..cab0e6ae6b0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -610,7 +610,7 @@ hole==0.3.0 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190721.0 +home-assistant-frontend==20190721.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 771fe60ab5e..0e023236fcc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -165,7 +165,7 @@ hdate==0.8.8 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190721.0 +home-assistant-frontend==20190721.1 # homeassistant.components.homekit_controller homekit[IP]==0.14.0 From 86d1bb651e748359ce94716c8ffd4a8939cfacb6 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Tue, 16 Jul 2019 04:27:28 +0200 Subject: [PATCH 02/13] Fix Netatmo climate battery level (#25165) * Interpolate battery level * Sort list --- homeassistant/components/netatmo/climate.py | 48 +++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/netatmo/climate.py b/homeassistant/components/netatmo/climate.py index 668d3056c81..2cb5637c5e3 100644 --- a/homeassistant/components/netatmo/climate.py +++ b/homeassistant/components/netatmo/climate.py @@ -484,13 +484,55 @@ class ThermostatData: .get("battery_level")) if batterylevel: + batterypct = interpolate( + batterylevel, roomstatus["module_type"]) if roomstatus.get("battery_level") is None: - roomstatus["battery_level"] = batterylevel - elif batterylevel < roomstatus["battery_level"]: - roomstatus["battery_level"] = batterylevel + roomstatus["battery_level"] = batterypct + elif batterypct < roomstatus["battery_level"]: + roomstatus["battery_level"] = batterypct self.room_status[room] = roomstatus except KeyError as err: _LOGGER.error("Update of room %s failed. Error: %s", room, err) self.away_temperature = self.homestatus.getAwaytemp(self.home) self.hg_temperature = self.homestatus.getHgtemp(self.home) self.setpoint_duration = self.homedata.setpoint_duration[self.home] + + +def interpolate(batterylevel, module_type): + """Interpolate battery level depending on device type.""" + na_battery_levels = { + NA_THERM: { + 'full': 4100, + 'high': 3600, + 'medium': 3300, + 'low': 3000, + 'empty': 2800}, + NA_VALVE: { + 'full': 3200, + 'high': 2700, + 'medium': 2400, + 'low': 2200, + 'empty': 2200}, + } + + levels = sorted(na_battery_levels[module_type].values()) + steps = [20, 50, 80, 100] + + na_battery_level = na_battery_levels[module_type] + if batterylevel >= na_battery_level['full']: + return 100 + if batterylevel >= na_battery_level['high']: + i = 3 + elif batterylevel >= na_battery_level['medium']: + i = 2 + elif batterylevel >= na_battery_level['low']: + i = 1 + else: + return 0 + + pct = steps[i-1] + ( + (steps[i] - steps[i-1]) * + (batterylevel - levels[i]) / + (levels[i+1] - levels[i]) + ) + return int(pct) From 1c861b97321428c3fa2bb109f67e021c2fb7ca6b Mon Sep 17 00:00:00 2001 From: David Bonnes Date: Mon, 22 Jul 2019 09:45:31 +0100 Subject: [PATCH 03/13] 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 --- homeassistant/components/evohome/__init__.py | 33 ++++++----- homeassistant/components/evohome/climate.py | 57 ++++++++++--------- .../components/evohome/water_heater.py | 8 +-- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index 49ddbdde156..b81d32a20f4 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -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() diff --git a/homeassistant/components/evohome/climate.py b/homeassistant/components/evohome/climate.py index a953c2e3244..b70f3052339 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -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]]: diff --git a/homeassistant/components/evohome/water_heater.py b/homeassistant/components/evohome/water_heater.py index 4706269e1cf..6f5952bdfb0 100644 --- a/homeassistant/components/evohome/water_heater.py +++ b/homeassistant/components/evohome/water_heater.py @@ -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} From f11f0956d33ee8523233f57cb5799de9dec1cd05 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Tue, 23 Jul 2019 21:29:04 +0200 Subject: [PATCH 04/13] Bump pyatmo version to 2.1.2 (#25296) --- homeassistant/components/netatmo/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json index 903de680f7d..2d386ebec5c 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -3,7 +3,7 @@ "name": "Netatmo", "documentation": "https://www.home-assistant.io/components/netatmo", "requirements": [ - "pyatmo==2.1.1" + "pyatmo==2.1.2" ], "dependencies": [ "webhook" diff --git a/requirements_all.txt b/requirements_all.txt index cab0e6ae6b0..16c414c03fb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1033,7 +1033,7 @@ pyalarmdotcom==0.3.2 pyarlo==0.2.3 # homeassistant.components.netatmo -pyatmo==2.1.1 +pyatmo==2.1.2 # homeassistant.components.apple_tv pyatv==0.3.12 From 956cdba588286d081fa2b0436acc29d3b61de12d Mon Sep 17 00:00:00 2001 From: David Bonnes Date: Tue, 23 Jul 2019 19:23:22 +0100 Subject: [PATCH 05/13] [climate] Bugfix/Tweak honeywell migration (#25369) * refactor supported_features add dr_data delint simplify code refactor target_temps, and delinting delint fix typo add min/max temps delint refactor target temps refactor target temps 2 correct typo tweak fix potential bug fix potential bug 2 bugfix entity_id fix typo * remove explicit entity_id * refactor hvac_action * refactor hvac_action * bugfix - HVAC_MODE_HEAT_COOL incorrectly added to hvac_modes --- homeassistant/components/honeywell/climate.py | 81 +++++++++++++------ 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index 4f3adaf4b2e..52a554bc731 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -12,8 +12,9 @@ from homeassistant.components.climate.const import ( ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, FAN_AUTO, FAN_DIFFUSE, FAN_ON, SUPPORT_AUX_HEAT, SUPPORT_FAN_MODE, SUPPORT_PRESET_MODE, - SUPPORT_TARGET_HUMIDITY, SUPPORT_TARGET_TEMPERATURE_RANGE, - CURRENT_HVAC_COOL, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, CURRENT_HVAC_OFF, + SUPPORT_TARGET_HUMIDITY, SUPPORT_TARGET_TEMPERATURE, + SUPPORT_TARGET_TEMPERATURE_RANGE, + CURRENT_HVAC_COOL, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, CURRENT_HVAC_FAN, HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL, PRESET_AWAY, PRESET_NONE, ) @@ -58,8 +59,8 @@ HW_MODE_TO_HVAC_MODE = { 'auto': HVAC_MODE_HEAT_COOL, } HW_MODE_TO_HA_HVAC_ACTION = { - 'off': CURRENT_HVAC_OFF, - 'fan': CURRENT_HVAC_IDLE, + 'off': CURRENT_HVAC_IDLE, + 'fan': CURRENT_HVAC_FAN, 'heat': CURRENT_HVAC_HEAT, 'cool': CURRENT_HVAC_COOL, } @@ -126,31 +127,34 @@ class HoneywellUSThermostat(ClimateDevice): self._username = username self._password = password - self._supported_features = (SUPPORT_PRESET_MODE | - SUPPORT_TARGET_TEMPERATURE_RANGE) + _LOGGER.debug("latestData = %s ", device._data) # noqa; pylint: disable=protected-access - # pylint: disable=protected-access - _LOGGER.debug("uiData = %s ", device._data['uiData']) - - # not all honeywell HVACs upport all modes + # not all honeywell HVACs support all modes mappings = [v for k, v in HVAC_MODE_TO_HW_MODE.items() - if k in device._data['uiData']] + if device.raw_ui_data[k]] self._hvac_mode_map = {k: v for d in mappings for k, v in d.items()} - if device._data['canControlHumidification']: + self._supported_features = \ + SUPPORT_PRESET_MODE | \ + SUPPORT_TARGET_TEMPERATURE | \ + SUPPORT_TARGET_TEMPERATURE_RANGE + + if device._data['canControlHumidification']: # noqa; pylint: disable=protected-access self._supported_features |= SUPPORT_TARGET_HUMIDITY - if device._data['uiData']['SwitchEmergencyHeatAllowed']: + + if device.raw_ui_data['SwitchEmergencyHeatAllowed']: self._supported_features |= SUPPORT_AUX_HEAT - if not device._data['hasFan']: + if not device._data['hasFan']: # pylint: disable=protected-access return - self._supported_features |= SUPPORT_FAN_MODE # not all honeywell fans support all modes mappings = [v for k, v in FAN_MODE_TO_HW.items() - if k in device._data['fanData']] + if device.raw_fan_data[k]] self._fan_mode_map = {k: v for d in mappings for k, v in d.items()} + self._supported_features |= SUPPORT_FAN_MODE + @property def name(self) -> Optional[str]: """Return the name of the honeywell, if any.""" @@ -159,11 +163,11 @@ class HoneywellUSThermostat(ClimateDevice): @property def device_state_attributes(self) -> Dict[str, Any]: """Return the device specific state attributes.""" - # pylint: disable=protected-access data = {} - if self._device._data['hasFan']: - data[ATTR_FAN_ACTION] = \ - 'running' if self._device.fan_running else 'idle' + data[ATTR_FAN_ACTION] = \ + 'running' if self._device.fan_running else 'idle' + if self._device.raw_dr_data: + data['dr_phase'] = self._device.raw_dr_data.get('Phase') return data @property @@ -171,6 +175,24 @@ class HoneywellUSThermostat(ClimateDevice): """Return the list of supported features.""" return self._supported_features + @property + def min_temp(self) -> float: + """Return the minimum temperature.""" + if self.hvac_mode in [HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL]: + return self._device.raw_ui_data['CoolLowerSetptLimit'] + if self.hvac_mode == HVAC_MODE_HEAT: + return self._device.raw_ui_data['HeatLowerSetptLimit'] + return None + + @property + def max_temp(self) -> float: + """Return the maximum temperature.""" + if self.hvac_mode == HVAC_MODE_COOL: + return self._device.raw_ui_data['CoolUpperSetptLimit'] + if self.hvac_mode in [HVAC_MODE_HEAT, HVAC_MODE_HEAT_COOL]: + return self._device.raw_ui_data['HeatUpperSetptLimit'] + return None + @property def temperature_unit(self) -> str: """Return the unit of measurement.""" @@ -195,6 +217,8 @@ class HoneywellUSThermostat(ClimateDevice): @property def hvac_action(self) -> Optional[str]: """Return the current running hvac operation if supported.""" + if self.hvac_mode == HVAC_MODE_OFF: + return None return HW_MODE_TO_HA_HVAC_ACTION[self._device.equipment_output_status] @property @@ -207,19 +231,23 @@ class HoneywellUSThermostat(ClimateDevice): """Return the temperature we try to reach.""" if self.hvac_mode == HVAC_MODE_COOL: return self._device.setpoint_cool - if self.hvac_mode != HVAC_MODE_HEAT: + if self.hvac_mode == HVAC_MODE_HEAT: return self._device.setpoint_heat return None @property def target_temperature_high(self) -> Optional[float]: """Return the highbound target temperature we try to reach.""" - return self._device.setpoint_cool + if self.hvac_mode == HVAC_MODE_HEAT_COOL: + return self._device.setpoint_cool + return None @property def target_temperature_low(self) -> Optional[float]: """Return the lowbound target temperature we try to reach.""" - return self._device.setpoint_heat + if self.hvac_mode == HVAC_MODE_HEAT_COOL: + return self._device.setpoint_heat + return None @property def preset_mode(self) -> Optional[str]: @@ -348,7 +376,10 @@ class HoneywellUSThermostat(ClimateDevice): def turn_aux_heat_off(self) -> None: """Turn auxiliary heater off.""" - self._device.system_mode = 'auto' + if HVAC_MODE_HEAT in self.hvac_modes: + self.set_hvac_mode(HVAC_MODE_HEAT) + else: + self.set_hvac_mode(HVAC_MODE_OFF) def _retry(self) -> bool: """Recreate a new somecomfort client. @@ -396,3 +427,5 @@ class HoneywellUSThermostat(ClimateDevice): raise exp _LOGGER.error( "SomeComfort update failed, Retrying - Error: %s", exp) + + _LOGGER.debug("latestData = %s ", self._device._data) # noqa; pylint: disable=protected-access From 638f5b1932053371c55bf9fd5f5e515f499807f0 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 24 Jul 2019 02:10:28 +0200 Subject: [PATCH 06/13] Update Daikin preset modes (#25395) * fix preset documentation * Use pydaikin set holiday method --- homeassistant/components/daikin/climate.py | 29 ++++++++++--------- homeassistant/components/daikin/const.py | 3 ++ homeassistant/components/daikin/manifest.json | 2 +- requirements_all.txt | 2 +- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/daikin/climate.py b/homeassistant/components/daikin/climate.py index 10d1a6161a3..284776bc671 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -12,7 +12,7 @@ from homeassistant.components.climate.const import ( SUPPORT_SWING_MODE, HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, - PRESET_AWAY, PRESET_HOME, + PRESET_AWAY, PRESET_NONE, ATTR_CURRENT_TEMPERATURE, ATTR_FAN_MODE, ATTR_HVAC_MODE, ATTR_SWING_MODE, ATTR_PRESET_MODE) @@ -20,7 +20,8 @@ import homeassistant.helpers.config_validation as cv from . import DOMAIN as DAIKIN_DOMAIN from .const import ( - ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, ATTR_TARGET_TEMPERATURE) + ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, ATTR_STATE_OFF, + ATTR_STATE_ON, ATTR_TARGET_TEMPERATURE) _LOGGER = logging.getLogger(__name__) @@ -49,7 +50,7 @@ DAIKIN_TO_HA_STATE = { HA_PRESET_TO_DAIKIN = { PRESET_AWAY: 'on', - PRESET_HOME: 'off' + PRESET_NONE: 'off' } HA_ATTR_TO_DAIKIN = { @@ -142,9 +143,10 @@ class DaikinClimate(ClimateDevice): ha_mode = DAIKIN_TO_HA_STATE.get(daikin_mode) value = ha_mode elif key == ATTR_PRESET_MODE: - away = (self._api.device.represent(daikin_attr)[1] - != HA_STATE_TO_DAIKIN[HVAC_MODE_OFF]) - value = PRESET_AWAY if away else PRESET_HOME + if self._api.device.represent( + daikin_attr)[1] == HA_PRESET_TO_DAIKIN[PRESET_AWAY]: + return PRESET_AWAY + return PRESET_NONE if value is None: _LOGGER.error("Invalid value requested for key %s", key) @@ -164,7 +166,7 @@ class DaikinClimate(ClimateDevice): values = {} for attr in [ATTR_TEMPERATURE, ATTR_FAN_MODE, ATTR_SWING_MODE, - ATTR_HVAC_MODE, ATTR_PRESET_MODE]: + ATTR_HVAC_MODE]: value = settings.get(attr) if value is None: continue @@ -173,8 +175,6 @@ class DaikinClimate(ClimateDevice): if daikin_attr is not None: if attr == ATTR_HVAC_MODE: values[daikin_attr] = HA_STATE_TO_DAIKIN[value] - elif attr == ATTR_PRESET_MODE: - values[daikin_attr] = HA_PRESET_TO_DAIKIN[value] elif value in self._list[attr]: values[daikin_attr] = value.lower() else: @@ -273,16 +273,19 @@ class DaikinClimate(ClimateDevice): @property def preset_mode(self): - """Return the fan setting.""" + """Return the preset_mode.""" return self.get(ATTR_PRESET_MODE) async def async_set_preset_mode(self, preset_mode): - """Set new target temperature.""" - await self._set({ATTR_PRESET_MODE: preset_mode}) + """Set preset mode.""" + if preset_mode == PRESET_AWAY: + await self._api.device.set_holiday(ATTR_STATE_ON) + else: + await self._api.device.set_holiday(ATTR_STATE_OFF) @property def preset_modes(self): - """List of available swing modes.""" + """List of available preset modes.""" return list(HA_PRESET_TO_DAIKIN) async def async_update(self): diff --git a/homeassistant/components/daikin/const.py b/homeassistant/components/daikin/const.py index 90967904579..69a8372cff4 100644 --- a/homeassistant/components/daikin/const.py +++ b/homeassistant/components/daikin/const.py @@ -5,6 +5,9 @@ ATTR_TARGET_TEMPERATURE = 'target_temperature' ATTR_INSIDE_TEMPERATURE = 'inside_temperature' ATTR_OUTSIDE_TEMPERATURE = 'outside_temperature' +ATTR_STATE_ON = 'on' +ATTR_STATE_OFF = 'off' + SENSOR_TYPE_TEMPERATURE = 'temperature' SENSOR_TYPES = { diff --git a/homeassistant/components/daikin/manifest.json b/homeassistant/components/daikin/manifest.json index bb6db101314..f41faa42091 100644 --- a/homeassistant/components/daikin/manifest.json +++ b/homeassistant/components/daikin/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/components/daikin", "requirements": [ - "pydaikin==1.4.6" + "pydaikin==1.5.1" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 16c414c03fb..e360ca591ad 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1078,7 +1078,7 @@ pycsspeechtts==1.0.2 # pycups==1.9.73 # homeassistant.components.daikin -pydaikin==1.4.6 +pydaikin==1.5.1 # homeassistant.components.danfoss_air pydanfossair==0.1.0 From 9fc4b878e29bb4fc4e5ea2b65cfaf74ebc605a34 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Tue, 23 Jul 2019 06:31:05 +0200 Subject: [PATCH 07/13] Update pysonos to 0.0.22 (#25399) --- homeassistant/components/sonos/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json index 64e7f148beb..4e65edc1f5b 100644 --- a/homeassistant/components/sonos/manifest.json +++ b/homeassistant/components/sonos/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/components/sonos", "requirements": [ - "pysonos==0.0.21" + "pysonos==0.0.22" ], "dependencies": [], "ssdp": { diff --git a/requirements_all.txt b/requirements_all.txt index e360ca591ad..08323ea4b4d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1378,7 +1378,7 @@ pysmarty==0.8 pysnmp==4.4.9 # homeassistant.components.sonos -pysonos==0.0.21 +pysonos==0.0.22 # homeassistant.components.spc pyspcwebgw==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0e023236fcc..d3658340ad2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -298,7 +298,7 @@ pysmartapp==0.3.2 pysmartthings==0.6.9 # homeassistant.components.sonos -pysonos==0.0.21 +pysonos==0.0.22 # homeassistant.components.spc pyspcwebgw==0.4.0 From e2e7d3952741851d150d78a20acdc3f2759d47a6 Mon Sep 17 00:00:00 2001 From: David Bonnes Date: Tue, 23 Jul 2019 06:14:42 +0100 Subject: [PATCH 08/13] [climate] Correct evohome hvac_action (#25407) * inital commit * take account of rounding up of curr_temp --- homeassistant/components/evohome/climate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/evohome/climate.py b/homeassistant/components/evohome/climate.py index b70f3052339..4158bed4fdd 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -193,9 +193,9 @@ class EvoZone(EvoClimateDevice): return CURRENT_HVAC_OFF if self.target_temperature <= self.min_temp: return CURRENT_HVAC_OFF - if self.target_temperature <= self.current_temperature: - return CURRENT_HVAC_HEAT - return CURRENT_HVAC_IDLE + if self.target_temperature < self.current_temperature: + return CURRENT_HVAC_IDLE + return CURRENT_HVAC_HEAT @property def current_temperature(self) -> Optional[float]: From 065a5c5df6a12ca026044e9348354a768f2b724a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 23 Jul 2019 17:12:08 -0700 Subject: [PATCH 09/13] Bumped version to 0.96.4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 9198e4a3942..e141391f5ed 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 96 -PATCH_VERSION = '3' +PATCH_VERSION = '4' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 94f5b262beb17c465e0ef8a8211953a1a59887bf Mon Sep 17 00:00:00 2001 From: David Bonnes Date: Wed, 24 Jul 2019 13:39:34 +0100 Subject: [PATCH 10/13] Bump geniushub client (#25458) --- homeassistant/components/geniushub/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/geniushub/manifest.json b/homeassistant/components/geniushub/manifest.json index d7f8601ae25..eff5a65c6b5 100644 --- a/homeassistant/components/geniushub/manifest.json +++ b/homeassistant/components/geniushub/manifest.json @@ -3,7 +3,7 @@ "name": "Genius Hub", "documentation": "https://www.home-assistant.io/components/geniushub", "requirements": [ - "geniushub-client==0.4.15" + "geniushub-client==0.5.00" ], "dependencies": [], "codeowners": ["@zxdavb"] diff --git a/requirements_all.txt b/requirements_all.txt index 08323ea4b4d..36ed2c22b27 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -505,7 +505,7 @@ gearbest_parser==1.0.7 geizhals==0.0.9 # homeassistant.components.geniushub -geniushub-client==0.4.15 +geniushub-client==0.5.00 # homeassistant.components.geo_json_events # homeassistant.components.nsw_rural_fire_service_feed From 46b9e9cdfbebba9ed3a4cdee8450c312c5a5217b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 25 Jul 2019 04:52:27 -0700 Subject: [PATCH 11/13] Allow cors for static files (#25468) --- homeassistant/components/http/cors.py | 8 ++++---- tests/components/http/test_cors.py | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/http/cors.py b/homeassistant/components/http/cors.py index 419b62be2c6..880cc47ac0d 100644 --- a/homeassistant/components/http/cors.py +++ b/homeassistant/components/http/cors.py @@ -1,5 +1,5 @@ """Provide CORS support for the HTTP component.""" -from aiohttp.web_urldispatcher import Resource, ResourceRoute +from aiohttp.web_urldispatcher import Resource, ResourceRoute, StaticResource from aiohttp.hdrs import ACCEPT, CONTENT_TYPE, ORIGIN, AUTHORIZATION from homeassistant.const import ( @@ -9,7 +9,7 @@ from homeassistant.core import callback ALLOWED_CORS_HEADERS = [ ORIGIN, ACCEPT, HTTP_HEADER_X_REQUESTED_WITH, CONTENT_TYPE, HTTP_HEADER_HA_AUTH, AUTHORIZATION] -VALID_CORS_TYPES = (Resource, ResourceRoute) +VALID_CORS_TYPES = (Resource, ResourceRoute, StaticResource) @callback @@ -56,7 +56,7 @@ def setup_cors(app, origins): async def cors_startup(app): """Initialize CORS when app starts up.""" - for route in list(app.router.routes()): - _allow_cors(route) + for resource in list(app.router.resources()): + _allow_cors(resource) app.on_startup.append(cors_startup) diff --git a/tests/components/http/test_cors.py b/tests/components/http/test_cors.py index d9fa6c11309..46a2766e541 100644 --- a/tests/components/http/test_cors.py +++ b/tests/components/http/test_cors.py @@ -1,4 +1,5 @@ """Test cors for the HTTP component.""" +from pathlib import Path from unittest.mock import patch from aiohttp import web @@ -152,3 +153,22 @@ async def test_cors_works_with_frontend(hass, hass_client): client = await hass_client() resp = await client.get('/') assert resp.status == 200 + + +async def test_cors_on_static_files(hass, hass_client): + """Test that we enable CORS for static files.""" + assert await async_setup_component(hass, 'frontend', { + 'http': { + 'cors_allowed_origins': ['http://www.example.com'] + } + }) + hass.http.register_static_path('/something', Path(__file__).parent) + + client = await hass_client() + resp = await client.options('/something/__init__.py', headers={ + 'origin': 'http://www.example.com', + ACCESS_CONTROL_REQUEST_METHOD: 'GET', + }) + assert resp.status == 200 + assert resp.headers[ACCESS_CONTROL_ALLOW_ORIGIN] == \ + 'http://www.example.com' From e79af97fdcd98934b54fe88c0437085dd2354bbd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 24 Jul 2019 21:53:51 -0700 Subject: [PATCH 12/13] Fix Nest turning off eco (#25472) * Fix Nest turning off eco * Add hvac action --- homeassistant/components/nest/climate.py | 46 ++++++++++++++---------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/nest/climate.py b/homeassistant/components/nest/climate.py index 63d91cbc829..0ea721df223 100644 --- a/homeassistant/components/nest/climate.py +++ b/homeassistant/components/nest/climate.py @@ -8,7 +8,8 @@ from homeassistant.components.climate.const import ( ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, FAN_AUTO, FAN_ON, HVAC_MODE_AUTO, HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF, SUPPORT_PRESET_MODE, SUPPORT_FAN_MODE, SUPPORT_TARGET_TEMPERATURE, - SUPPORT_TARGET_TEMPERATURE_RANGE, PRESET_AWAY, PRESET_ECO, PRESET_NONE) + SUPPORT_TARGET_TEMPERATURE_RANGE, PRESET_AWAY, PRESET_ECO, PRESET_NONE, + CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, CURRENT_HVAC_COOL) from homeassistant.const import ( ATTR_TEMPERATURE, CONF_SCAN_INTERVAL, TEMP_CELSIUS, TEMP_FAHRENHEIT) from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -28,6 +29,21 @@ NEST_MODE_HEAT = 'heat' NEST_MODE_COOL = 'cool' NEST_MODE_OFF = 'off' +MODE_HASS_TO_NEST = { + HVAC_MODE_AUTO: NEST_MODE_HEAT_COOL, + HVAC_MODE_HEAT: NEST_MODE_HEAT, + HVAC_MODE_COOL: NEST_MODE_COOL, + HVAC_MODE_OFF: NEST_MODE_OFF, +} + +MODE_NEST_TO_HASS = {v: k for k, v in MODE_HASS_TO_NEST.items()} + +ACTION_NEST_TO_HASS = { + 'off': CURRENT_HVAC_IDLE, + 'heating': CURRENT_HVAC_HEAT, + 'cooling': CURRENT_HVAC_COOL, +} + PRESET_MODES = [PRESET_NONE, PRESET_AWAY, PRESET_ECO] @@ -95,6 +111,7 @@ class NestThermostat(ClimateDevice): self._temperature = None self._temperature_scale = None self._mode = None + self._action = None self._fan = None self._eco_temperature = None self._is_locked = None @@ -157,15 +174,16 @@ class NestThermostat(ClimateDevice): @property def hvac_mode(self): """Return current operation ie. heat, cool, idle.""" - if self._mode in \ - (NEST_MODE_HEAT, NEST_MODE_COOL, NEST_MODE_OFF): - return self._mode if self._mode == NEST_MODE_ECO: # We assume the first operation in operation list is the main one return self._operation_list[0] - if self._mode == NEST_MODE_HEAT_COOL: - return HVAC_MODE_AUTO - return None + + return MODE_NEST_TO_HASS[self._mode] + + @property + def hvac_action(self): + """Return the current hvac action.""" + return ACTION_NEST_TO_HASS[self._action] @property def target_temperature(self): @@ -216,16 +234,7 @@ class NestThermostat(ClimateDevice): def set_hvac_mode(self, hvac_mode): """Set operation mode.""" - if hvac_mode in (HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_OFF): - device_mode = hvac_mode - elif hvac_mode == HVAC_MODE_AUTO: - device_mode = NEST_MODE_HEAT_COOL - else: - device_mode = HVAC_MODE_OFF - _LOGGER.error( - "An error occurred while setting device mode. " - "Invalid operation mode: %s", hvac_mode) - self.device.mode = device_mode + self.device.mode = MODE_HASS_TO_NEST[hvac_mode] @property def hvac_modes(self): @@ -259,7 +268,7 @@ class NestThermostat(ClimateDevice): self.structure.away = True if self.preset_mode == PRESET_ECO: - self.device.mode = self._operation_list[0] + self.device.mode = MODE_HASS_TO_NEST[self._operation_list[0]] elif preset_mode == PRESET_ECO: self.device.mode = NEST_MODE_ECO @@ -301,6 +310,7 @@ class NestThermostat(ClimateDevice): self._humidity = self.device.humidity self._temperature = self.device.temperature self._mode = self.device.mode + self._action = self.device.hvac_state self._target_temperature = self.device.target self._fan = self.device.fan self._away = self.structure.away == 'away' From 47f3be1fe46fcdf0644a4fbd09b7deb7173879de Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 25 Jul 2019 09:51:43 -0700 Subject: [PATCH 13/13] Bumped version to 0.96.5 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index e141391f5ed..79763c16fbc 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 96 -PATCH_VERSION = '4' +PATCH_VERSION = '5' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3)