Merge pull request #25452 from home-assistant/rc

0.96.4
This commit is contained in:
Paulus Schoutsen 2019-07-23 18:24:24 -07:00 committed by GitHub
commit 8a2fdb5045
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 190 additions and 99 deletions

View File

@ -12,7 +12,7 @@ from homeassistant.components.climate.const import (
SUPPORT_SWING_MODE, SUPPORT_SWING_MODE,
HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL,
HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY,
PRESET_AWAY, PRESET_HOME, PRESET_AWAY, PRESET_NONE,
ATTR_CURRENT_TEMPERATURE, ATTR_FAN_MODE, ATTR_CURRENT_TEMPERATURE, ATTR_FAN_MODE,
ATTR_HVAC_MODE, ATTR_SWING_MODE, ATTR_HVAC_MODE, ATTR_SWING_MODE,
ATTR_PRESET_MODE) ATTR_PRESET_MODE)
@ -20,7 +20,8 @@ import homeassistant.helpers.config_validation as cv
from . import DOMAIN as DAIKIN_DOMAIN from . import DOMAIN as DAIKIN_DOMAIN
from .const import ( 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__) _LOGGER = logging.getLogger(__name__)
@ -49,7 +50,7 @@ DAIKIN_TO_HA_STATE = {
HA_PRESET_TO_DAIKIN = { HA_PRESET_TO_DAIKIN = {
PRESET_AWAY: 'on', PRESET_AWAY: 'on',
PRESET_HOME: 'off' PRESET_NONE: 'off'
} }
HA_ATTR_TO_DAIKIN = { HA_ATTR_TO_DAIKIN = {
@ -142,9 +143,10 @@ class DaikinClimate(ClimateDevice):
ha_mode = DAIKIN_TO_HA_STATE.get(daikin_mode) ha_mode = DAIKIN_TO_HA_STATE.get(daikin_mode)
value = ha_mode value = ha_mode
elif key == ATTR_PRESET_MODE: elif key == ATTR_PRESET_MODE:
away = (self._api.device.represent(daikin_attr)[1] if self._api.device.represent(
!= HA_STATE_TO_DAIKIN[HVAC_MODE_OFF]) daikin_attr)[1] == HA_PRESET_TO_DAIKIN[PRESET_AWAY]:
value = PRESET_AWAY if away else PRESET_HOME return PRESET_AWAY
return PRESET_NONE
if value is None: if value is None:
_LOGGER.error("Invalid value requested for key %s", key) _LOGGER.error("Invalid value requested for key %s", key)
@ -164,7 +166,7 @@ class DaikinClimate(ClimateDevice):
values = {} values = {}
for attr in [ATTR_TEMPERATURE, ATTR_FAN_MODE, ATTR_SWING_MODE, for attr in [ATTR_TEMPERATURE, ATTR_FAN_MODE, ATTR_SWING_MODE,
ATTR_HVAC_MODE, ATTR_PRESET_MODE]: ATTR_HVAC_MODE]:
value = settings.get(attr) value = settings.get(attr)
if value is None: if value is None:
continue continue
@ -173,8 +175,6 @@ class DaikinClimate(ClimateDevice):
if daikin_attr is not None: if daikin_attr is not None:
if attr == ATTR_HVAC_MODE: if attr == ATTR_HVAC_MODE:
values[daikin_attr] = HA_STATE_TO_DAIKIN[value] 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]: elif value in self._list[attr]:
values[daikin_attr] = value.lower() values[daikin_attr] = value.lower()
else: else:
@ -273,16 +273,19 @@ class DaikinClimate(ClimateDevice):
@property @property
def preset_mode(self): def preset_mode(self):
"""Return the fan setting.""" """Return the preset_mode."""
return self.get(ATTR_PRESET_MODE) return self.get(ATTR_PRESET_MODE)
async def async_set_preset_mode(self, preset_mode): async def async_set_preset_mode(self, preset_mode):
"""Set new target temperature.""" """Set preset mode."""
await self._set({ATTR_PRESET_MODE: 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 @property
def preset_modes(self): def preset_modes(self):
"""List of available swing modes.""" """List of available preset modes."""
return list(HA_PRESET_TO_DAIKIN) return list(HA_PRESET_TO_DAIKIN)
async def async_update(self): async def async_update(self):

View File

@ -5,6 +5,9 @@ ATTR_TARGET_TEMPERATURE = 'target_temperature'
ATTR_INSIDE_TEMPERATURE = 'inside_temperature' ATTR_INSIDE_TEMPERATURE = 'inside_temperature'
ATTR_OUTSIDE_TEMPERATURE = 'outside_temperature' ATTR_OUTSIDE_TEMPERATURE = 'outside_temperature'
ATTR_STATE_ON = 'on'
ATTR_STATE_OFF = 'off'
SENSOR_TYPE_TEMPERATURE = 'temperature' SENSOR_TYPE_TEMPERATURE = 'temperature'
SENSOR_TYPES = { SENSOR_TYPES = {

View File

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/components/daikin", "documentation": "https://www.home-assistant.io/components/daikin",
"requirements": [ "requirements": [
"pydaikin==1.4.6" "pydaikin==1.5.1"
], ],
"dependencies": [], "dependencies": [],
"codeowners": [ "codeowners": [

View File

@ -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()

View File

@ -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]]:
@ -190,9 +193,9 @@ class EvoZone(EvoClimateDevice):
return CURRENT_HVAC_OFF return CURRENT_HVAC_OFF
if self.target_temperature <= self.min_temp: if self.target_temperature <= self.min_temp:
return CURRENT_HVAC_OFF return CURRENT_HVAC_OFF
if self.target_temperature <= self.current_temperature: if self.target_temperature < self.current_temperature:
return CURRENT_HVAC_HEAT return CURRENT_HVAC_IDLE
return CURRENT_HVAC_IDLE return CURRENT_HVAC_HEAT
@property @property
def current_temperature(self) -> Optional[float]: def current_temperature(self) -> Optional[float]:

View File

@ -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}

View File

@ -3,7 +3,7 @@
"name": "Home Assistant Frontend", "name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/components/frontend", "documentation": "https://www.home-assistant.io/components/frontend",
"requirements": [ "requirements": [
"home-assistant-frontend==20190721.0" "home-assistant-frontend==20190721.1"
], ],
"dependencies": [ "dependencies": [
"api", "api",

View File

@ -12,8 +12,9 @@ from homeassistant.components.climate.const import (
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
FAN_AUTO, FAN_DIFFUSE, FAN_ON, FAN_AUTO, FAN_DIFFUSE, FAN_ON,
SUPPORT_AUX_HEAT, SUPPORT_FAN_MODE, SUPPORT_PRESET_MODE, SUPPORT_AUX_HEAT, SUPPORT_FAN_MODE, SUPPORT_PRESET_MODE,
SUPPORT_TARGET_HUMIDITY, SUPPORT_TARGET_TEMPERATURE_RANGE, SUPPORT_TARGET_HUMIDITY, SUPPORT_TARGET_TEMPERATURE,
CURRENT_HVAC_COOL, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, CURRENT_HVAC_OFF, 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, HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL,
PRESET_AWAY, PRESET_NONE, PRESET_AWAY, PRESET_NONE,
) )
@ -58,8 +59,8 @@ HW_MODE_TO_HVAC_MODE = {
'auto': HVAC_MODE_HEAT_COOL, 'auto': HVAC_MODE_HEAT_COOL,
} }
HW_MODE_TO_HA_HVAC_ACTION = { HW_MODE_TO_HA_HVAC_ACTION = {
'off': CURRENT_HVAC_OFF, 'off': CURRENT_HVAC_IDLE,
'fan': CURRENT_HVAC_IDLE, 'fan': CURRENT_HVAC_FAN,
'heat': CURRENT_HVAC_HEAT, 'heat': CURRENT_HVAC_HEAT,
'cool': CURRENT_HVAC_COOL, 'cool': CURRENT_HVAC_COOL,
} }
@ -126,31 +127,34 @@ class HoneywellUSThermostat(ClimateDevice):
self._username = username self._username = username
self._password = password self._password = password
self._supported_features = (SUPPORT_PRESET_MODE | _LOGGER.debug("latestData = %s ", device._data) # noqa; pylint: disable=protected-access
SUPPORT_TARGET_TEMPERATURE_RANGE)
# pylint: disable=protected-access # not all honeywell HVACs support all modes
_LOGGER.debug("uiData = %s ", device._data['uiData'])
# not all honeywell HVACs upport all modes
mappings = [v for k, v in HVAC_MODE_TO_HW_MODE.items() 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()} 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 self._supported_features |= SUPPORT_TARGET_HUMIDITY
if device._data['uiData']['SwitchEmergencyHeatAllowed']:
if device.raw_ui_data['SwitchEmergencyHeatAllowed']:
self._supported_features |= SUPPORT_AUX_HEAT self._supported_features |= SUPPORT_AUX_HEAT
if not device._data['hasFan']: if not device._data['hasFan']: # pylint: disable=protected-access
return return
self._supported_features |= SUPPORT_FAN_MODE
# not all honeywell fans support all modes # not all honeywell fans support all modes
mappings = [v for k, v in FAN_MODE_TO_HW.items() 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._fan_mode_map = {k: v for d in mappings for k, v in d.items()}
self._supported_features |= SUPPORT_FAN_MODE
@property @property
def name(self) -> Optional[str]: def name(self) -> Optional[str]:
"""Return the name of the honeywell, if any.""" """Return the name of the honeywell, if any."""
@ -159,11 +163,11 @@ class HoneywellUSThermostat(ClimateDevice):
@property @property
def device_state_attributes(self) -> Dict[str, Any]: def device_state_attributes(self) -> Dict[str, Any]:
"""Return the device specific state attributes.""" """Return the device specific state attributes."""
# pylint: disable=protected-access
data = {} data = {}
if self._device._data['hasFan']: data[ATTR_FAN_ACTION] = \
data[ATTR_FAN_ACTION] = \ 'running' if self._device.fan_running else 'idle'
'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 return data
@property @property
@ -171,6 +175,24 @@ class HoneywellUSThermostat(ClimateDevice):
"""Return the list of supported features.""" """Return the list of supported features."""
return self._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 @property
def temperature_unit(self) -> str: def temperature_unit(self) -> str:
"""Return the unit of measurement.""" """Return the unit of measurement."""
@ -195,6 +217,8 @@ class HoneywellUSThermostat(ClimateDevice):
@property @property
def hvac_action(self) -> Optional[str]: def hvac_action(self) -> Optional[str]:
"""Return the current running hvac operation if supported.""" """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] return HW_MODE_TO_HA_HVAC_ACTION[self._device.equipment_output_status]
@property @property
@ -207,19 +231,23 @@ class HoneywellUSThermostat(ClimateDevice):
"""Return the temperature we try to reach.""" """Return the temperature we try to reach."""
if self.hvac_mode == HVAC_MODE_COOL: if self.hvac_mode == HVAC_MODE_COOL:
return self._device.setpoint_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 self._device.setpoint_heat
return None return None
@property @property
def target_temperature_high(self) -> Optional[float]: def target_temperature_high(self) -> Optional[float]:
"""Return the highbound target temperature we try to reach.""" """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 @property
def target_temperature_low(self) -> Optional[float]: def target_temperature_low(self) -> Optional[float]:
"""Return the lowbound target temperature we try to reach.""" """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 @property
def preset_mode(self) -> Optional[str]: def preset_mode(self) -> Optional[str]:
@ -348,7 +376,10 @@ class HoneywellUSThermostat(ClimateDevice):
def turn_aux_heat_off(self) -> None: def turn_aux_heat_off(self) -> None:
"""Turn auxiliary heater off.""" """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: def _retry(self) -> bool:
"""Recreate a new somecomfort client. """Recreate a new somecomfort client.
@ -396,3 +427,5 @@ class HoneywellUSThermostat(ClimateDevice):
raise exp raise exp
_LOGGER.error( _LOGGER.error(
"SomeComfort update failed, Retrying - Error: %s", exp) "SomeComfort update failed, Retrying - Error: %s", exp)
_LOGGER.debug("latestData = %s ", self._device._data) # noqa; pylint: disable=protected-access

View File

@ -484,13 +484,55 @@ class ThermostatData:
.get("battery_level")) .get("battery_level"))
if batterylevel: if batterylevel:
batterypct = interpolate(
batterylevel, roomstatus["module_type"])
if roomstatus.get("battery_level") is None: if roomstatus.get("battery_level") is None:
roomstatus["battery_level"] = batterylevel roomstatus["battery_level"] = batterypct
elif batterylevel < roomstatus["battery_level"]: elif batterypct < roomstatus["battery_level"]:
roomstatus["battery_level"] = batterylevel roomstatus["battery_level"] = batterypct
self.room_status[room] = roomstatus self.room_status[room] = roomstatus
except KeyError as err: except KeyError as err:
_LOGGER.error("Update of room %s failed. Error: %s", room, err) _LOGGER.error("Update of room %s failed. Error: %s", room, err)
self.away_temperature = self.homestatus.getAwaytemp(self.home) self.away_temperature = self.homestatus.getAwaytemp(self.home)
self.hg_temperature = self.homestatus.getHgtemp(self.home) self.hg_temperature = self.homestatus.getHgtemp(self.home)
self.setpoint_duration = self.homedata.setpoint_duration[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)

View File

@ -3,7 +3,7 @@
"name": "Netatmo", "name": "Netatmo",
"documentation": "https://www.home-assistant.io/components/netatmo", "documentation": "https://www.home-assistant.io/components/netatmo",
"requirements": [ "requirements": [
"pyatmo==2.1.1" "pyatmo==2.1.2"
], ],
"dependencies": [ "dependencies": [
"webhook" "webhook"

View File

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/components/sonos", "documentation": "https://www.home-assistant.io/components/sonos",
"requirements": [ "requirements": [
"pysonos==0.0.21" "pysonos==0.0.22"
], ],
"dependencies": [], "dependencies": [],
"ssdp": { "ssdp": {

View File

@ -2,7 +2,7 @@
"""Constants used by Home Assistant components.""" """Constants used by Home Assistant components."""
MAJOR_VERSION = 0 MAJOR_VERSION = 0
MINOR_VERSION = 96 MINOR_VERSION = 96
PATCH_VERSION = '3' PATCH_VERSION = '4'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 5, 3) REQUIRED_PYTHON_VER = (3, 5, 3)

View File

@ -10,7 +10,7 @@ certifi>=2019.6.16
cryptography==2.7 cryptography==2.7
distro==1.4.0 distro==1.4.0
hass-nabucasa==0.15 hass-nabucasa==0.15
home-assistant-frontend==20190721.0 home-assistant-frontend==20190721.1
importlib-metadata==0.18 importlib-metadata==0.18
jinja2>=2.10.1 jinja2>=2.10.1
netdisco==2.6.0 netdisco==2.6.0

View File

@ -610,7 +610,7 @@ hole==0.3.0
holidays==0.9.10 holidays==0.9.10
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20190721.0 home-assistant-frontend==20190721.1
# homeassistant.components.zwave # homeassistant.components.zwave
homeassistant-pyozw==0.1.4 homeassistant-pyozw==0.1.4
@ -1033,7 +1033,7 @@ pyalarmdotcom==0.3.2
pyarlo==0.2.3 pyarlo==0.2.3
# homeassistant.components.netatmo # homeassistant.components.netatmo
pyatmo==2.1.1 pyatmo==2.1.2
# homeassistant.components.apple_tv # homeassistant.components.apple_tv
pyatv==0.3.12 pyatv==0.3.12
@ -1078,7 +1078,7 @@ pycsspeechtts==1.0.2
# pycups==1.9.73 # pycups==1.9.73
# homeassistant.components.daikin # homeassistant.components.daikin
pydaikin==1.4.6 pydaikin==1.5.1
# homeassistant.components.danfoss_air # homeassistant.components.danfoss_air
pydanfossair==0.1.0 pydanfossair==0.1.0
@ -1378,7 +1378,7 @@ pysmarty==0.8
pysnmp==4.4.9 pysnmp==4.4.9
# homeassistant.components.sonos # homeassistant.components.sonos
pysonos==0.0.21 pysonos==0.0.22
# homeassistant.components.spc # homeassistant.components.spc
pyspcwebgw==0.4.0 pyspcwebgw==0.4.0

View File

@ -165,7 +165,7 @@ hdate==0.8.8
holidays==0.9.10 holidays==0.9.10
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20190721.0 home-assistant-frontend==20190721.1
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
homekit[IP]==0.14.0 homekit[IP]==0.14.0
@ -298,7 +298,7 @@ pysmartapp==0.3.2
pysmartthings==0.6.9 pysmartthings==0.6.9
# homeassistant.components.sonos # homeassistant.components.sonos
pysonos==0.0.21 pysonos==0.0.22
# homeassistant.components.spc # homeassistant.components.spc
pyspcwebgw==0.4.0 pyspcwebgw==0.4.0