Compare commits

..

15 Commits

Author SHA1 Message Date
Paulus Schoutsen
4f8a93fb3e Merge pull request #25486 from home-assistant/rc
0.96.5
2019-07-25 10:51:55 -07:00
Paulus Schoutsen
47f3be1fe4 Bumped version to 0.96.5 2019-07-25 09:51:43 -07:00
Paulus Schoutsen
e79af97fdc Fix Nest turning off eco (#25472)
* Fix Nest turning off eco

* Add hvac action
2019-07-25 09:51:25 -07:00
Paulus Schoutsen
46b9e9cdfb Allow cors for static files (#25468) 2019-07-25 09:51:24 -07:00
David Bonnes
94f5b262be Bump geniushub client (#25458) 2019-07-25 09:51:23 -07:00
Paulus Schoutsen
8a2fdb5045 Merge pull request #25452 from home-assistant/rc
0.96.4
2019-07-23 18:24:24 -07:00
Paulus Schoutsen
065a5c5df6 Bumped version to 0.96.4 2019-07-23 17:12:08 -07:00
David Bonnes
e2e7d39527 [climate] Correct evohome hvac_action (#25407)
* inital commit

* take account of rounding up of curr_temp
2019-07-23 17:11:40 -07:00
Anders Melchiorsen
9fc4b878e2 Update pysonos to 0.0.22 (#25399) 2019-07-23 17:11:40 -07:00
Fredrik Erlandsson
638f5b1932 Update Daikin preset modes (#25395)
* fix preset documentation

* Use pydaikin set holiday method
2019-07-23 17:11:39 -07:00
David Bonnes
956cdba588 [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
2019-07-23 17:11:38 -07:00
cgtobi
f11f0956d3 Bump pyatmo version to 2.1.2 (#25296) 2019-07-23 17:11:37 -07:00
David Bonnes
1c861b9732 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
2019-07-23 17:11:37 -07:00
cgtobi
86d1bb651e Fix Netatmo climate battery level (#25165)
* Interpolate battery level

* Sort list
2019-07-23 17:11:36 -07:00
Paulus Schoutsen
81bde77c04 Updated frontend to 20190721.1 2019-07-21 13:02:26 -07:00
19 changed files with 244 additions and 123 deletions

View File

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

View File

@@ -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 = {

View File

@@ -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": [

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]]:
@@ -190,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]:

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}

View File

@@ -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",

View File

@@ -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"]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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": {

View File

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

View File

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

View File

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

View File

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

View File

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