mirror of
https://github.com/home-assistant/core.git
synced 2026-01-10 01:27:16 +01:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f8a93fb3e | ||
|
|
47f3be1fe4 | ||
|
|
e79af97fdc | ||
|
|
46b9e9cdfb | ||
|
|
94f5b262be | ||
|
|
8a2fdb5045 | ||
|
|
065a5c5df6 | ||
|
|
e2e7d39527 | ||
|
|
9fc4b878e2 | ||
|
|
638f5b1932 | ||
|
|
956cdba588 | ||
|
|
f11f0956d3 | ||
|
|
1c861b9732 | ||
|
|
86d1bb651e | ||
|
|
81bde77c04 | ||
|
|
a652a4d9e9 | ||
|
|
2189cb0ee7 | ||
|
|
15064e83b4 | ||
|
|
8538e69e28 | ||
|
|
35c719628d | ||
|
|
0eab89c8f4 | ||
|
|
a3043b9a90 | ||
|
|
c795c93034 | ||
|
|
2228a0dcac | ||
|
|
68e7f4ca5a | ||
|
|
e052bcb03b | ||
|
|
a56b604936 | ||
|
|
f6b6818fb0 | ||
|
|
93a65bf507 | ||
|
|
ec302912a3 | ||
|
|
615af773e5 | ||
|
|
9c51650ea3 | ||
|
|
01430262cd | ||
|
|
a6e3cc6617 | ||
|
|
b4481269ec | ||
|
|
662c33af85 | ||
|
|
fc384ca6d5 | ||
|
|
49e2583b08 | ||
|
|
8629b86186 | ||
|
|
68c4e5c0c9 | ||
|
|
c4d1cd0e03 | ||
|
|
8b020ea5e6 | ||
|
|
33cba4da85 | ||
|
|
3db106c562 | ||
|
|
cc595632bd | ||
|
|
f76700567e | ||
|
|
86cf02739b | ||
|
|
46cdbd273a | ||
|
|
ec3cb11e2f | ||
|
|
2016cf872e | ||
|
|
37810e010a | ||
|
|
2b69904b94 | ||
|
|
59cf6a0c79 | ||
|
|
39b249d202 | ||
|
|
d57cf01cf2 |
@@ -4,8 +4,13 @@ trigger:
|
||||
batch: true
|
||||
branches:
|
||||
include:
|
||||
- rc
|
||||
- dev
|
||||
pr: none
|
||||
- master
|
||||
pr:
|
||||
- rc
|
||||
- dev
|
||||
- master
|
||||
|
||||
resources:
|
||||
containers:
|
||||
@@ -20,6 +25,7 @@ variables:
|
||||
value: '2df3ae11-3bf6-49bc-a809-ba0d340d6a6d'
|
||||
- name: PythonMain
|
||||
value: '35'
|
||||
- group: codecov
|
||||
|
||||
stages:
|
||||
|
||||
@@ -80,7 +86,7 @@ stages:
|
||||
steps:
|
||||
- script: |
|
||||
python --version > .cache
|
||||
displayName: 'Set python $(python.version) for requirement cache'
|
||||
displayName: 'Set python $(python.container) for requirement cache'
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
displayName: 'Restore artifacts based on Requirements'
|
||||
inputs:
|
||||
@@ -97,38 +103,55 @@ stages:
|
||||
pip install pytest-azurepipelines -c homeassistant/package_constraints.txt
|
||||
displayName: 'Create Virtual Environment & Install Requirements'
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
displayName: 'Save artifacts based on Requirements'
|
||||
inputs:
|
||||
keyfile: 'requirements_test_all.txt, .cache'
|
||||
targetfolder: './venv'
|
||||
# Explicit Cache Save (instead of using RestoreAndSaveCache)
|
||||
# Dont wait with cache save for all the other task in this job to complete (±30 minutes), other parallel jobs might utilize this
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
displayName: 'Save artifacts based on Requirements'
|
||||
inputs:
|
||||
keyfile: 'requirements_test_all.txt, .cache'
|
||||
targetfolder: './venv'
|
||||
vstsFeed: '$(ArtifactFeed)'
|
||||
- script: |
|
||||
. venv/bin/activate
|
||||
pip install -e .
|
||||
displayName: 'Install Home Assistant for python $(python.version)'
|
||||
displayName: 'Install Home Assistant for python $(python.container)'
|
||||
- script: |
|
||||
. venv/bin/activate
|
||||
pytest --timeout=9 --durations=10 --junitxml=junit/test-results.xml -qq -o console_output_style=count -p no:sugar tests
|
||||
displayName: 'Run pytest for python $(python.version)'
|
||||
pytest --timeout=9 --durations=10 --junitxml=test-results.xml -qq -o console_output_style=count -p no:sugar tests
|
||||
displayName: 'Run pytest for python $(python.container)'
|
||||
condition: and(succeeded(), ne(variables['python.container'], variables['PythonMain']))
|
||||
- script: |
|
||||
. venv/bin/activate
|
||||
pytest --timeout=9 --durations=10 --junitxml=test-results.xml --cov --cov-report=xml -qq -o console_output_style=count -p no:sugar tests
|
||||
codecov
|
||||
displayName: 'Run pytest for python $(python.container) / coverage'
|
||||
env:
|
||||
CODECOV_TOKEN: '$(codecovToken)'
|
||||
condition: and(succeeded(), eq(variables['python.container'], variables['PythonMain']))
|
||||
- task: PublishTestResults@2
|
||||
condition: succeededOrFailed()
|
||||
inputs:
|
||||
testResultsFiles: '**/test-*.xml'
|
||||
testRunTitle: 'Publish test results for Python $(python.version)'
|
||||
testResultsFiles: 'test-results.xml'
|
||||
testRunTitle: 'Publish test results for Python $(python.container)'
|
||||
- task: PublishCodeCoverageResults@1
|
||||
inputs:
|
||||
codeCoverageTool: cobertura
|
||||
summaryFileLocation: coverage.xml
|
||||
displayName: 'publish coverage artifact'
|
||||
condition: and(succeeded(), eq(variables['python.container'], variables['PythonMain']))
|
||||
|
||||
- stage: 'FullCheck'
|
||||
dependsOn:
|
||||
- 'Overview'
|
||||
jobs:
|
||||
- job: 'Pytlint'
|
||||
- job: 'Pylint'
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
container: $[ variables['PythonMain'] ]
|
||||
steps:
|
||||
- script: |
|
||||
python --version > .cache
|
||||
displayName: 'Set python $(python.version) for requirement cache'
|
||||
displayName: 'Set python $(PythonMain) for requirement cache'
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
displayName: 'Restore artifacts based on Requirements'
|
||||
inputs:
|
||||
@@ -154,7 +177,7 @@ stages:
|
||||
- script: |
|
||||
. venv/bin/activate
|
||||
pip install -e .
|
||||
displayName: 'Install Home Assistant for python $(python.version)'
|
||||
displayName: 'Install Home Assistant for python $(PythonMain)'
|
||||
- script: |
|
||||
. venv/bin/activate
|
||||
pylint homeassistant
|
||||
|
||||
@@ -74,7 +74,7 @@ SET_FAN_MODE_SCHEMA = vol.Schema({
|
||||
})
|
||||
SET_PRESET_MODE_SCHEMA = vol.Schema({
|
||||
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
|
||||
vol.Required(ATTR_PRESET_MODE): vol.Maybe(cv.string),
|
||||
vol.Required(ATTR_PRESET_MODE): cv.string,
|
||||
})
|
||||
SET_HVAC_MODE_SCHEMA = vol.Schema({
|
||||
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
|
||||
|
||||
@@ -32,6 +32,8 @@ HVAC_MODES = [
|
||||
HVAC_MODE_FAN_ONLY,
|
||||
]
|
||||
|
||||
# No preset is active
|
||||
PRESET_NONE = 'none'
|
||||
|
||||
# Device is running an energy-saving mode
|
||||
PRESET_ECO = 'eco'
|
||||
|
||||
@@ -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,22 +273,36 @@ 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):
|
||||
"""Retrieve latest state."""
|
||||
await self._api.async_update()
|
||||
|
||||
async def async_turn_on(self):
|
||||
"""Turn device on."""
|
||||
await self._api.device.set({})
|
||||
|
||||
async def async_turn_off(self):
|
||||
"""Turn device off."""
|
||||
await self._api.device.set({
|
||||
HA_ATTR_TO_DAIKIN[ATTR_HVAC_MODE]:
|
||||
HA_STATE_TO_DAIKIN[HVAC_MODE_OFF]
|
||||
})
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return a device description for device registry."""
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -12,10 +12,10 @@ from homeassistant.components.climate.const import (
|
||||
ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH, SUPPORT_TARGET_TEMPERATURE,
|
||||
SUPPORT_AUX_HEAT, SUPPORT_TARGET_TEMPERATURE_RANGE, SUPPORT_FAN_MODE,
|
||||
PRESET_AWAY, FAN_AUTO, FAN_ON, CURRENT_HVAC_OFF, CURRENT_HVAC_HEAT,
|
||||
CURRENT_HVAC_COOL, SUPPORT_PRESET_MODE
|
||||
CURRENT_HVAC_COOL, SUPPORT_PRESET_MODE, PRESET_NONE
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, STATE_ON, ATTR_TEMPERATURE, TEMP_FAHRENHEIT, TEMP_CELSIUS)
|
||||
ATTR_ENTITY_ID, STATE_ON, ATTR_TEMPERATURE, TEMP_FAHRENHEIT)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_CONFIGURING = {}
|
||||
@@ -31,6 +31,8 @@ PRESET_AUX_HEAT_ONLY = 'aux_heat_only'
|
||||
PRESET_HOLD_NEXT_TRANSITION = 'next_transition'
|
||||
PRESET_HOLD_INDEFINITE = 'indefinite'
|
||||
AWAY_MODE = 'awayMode'
|
||||
PRESET_HOME = 'home'
|
||||
PRESET_SLEEP = 'sleep'
|
||||
|
||||
# Order matters, because for reverse mapping we don't want to map HEAT to AUX
|
||||
ECOBEE_HVAC_TO_HASS = collections.OrderedDict([
|
||||
@@ -47,10 +49,10 @@ PRESET_TO_ECOBEE_HOLD = {
|
||||
}
|
||||
|
||||
PRESET_MODES = [
|
||||
PRESET_NONE,
|
||||
PRESET_AWAY,
|
||||
PRESET_TEMPERATURE,
|
||||
PRESET_HOLD_NEXT_TRANSITION,
|
||||
PRESET_HOLD_INDEFINITE
|
||||
PRESET_HOME,
|
||||
PRESET_SLEEP
|
||||
]
|
||||
|
||||
SERVICE_SET_FAN_MIN_ON_TIME = 'ecobee_set_fan_min_on_time'
|
||||
@@ -168,9 +170,6 @@ class Thermostat(ClimateDevice):
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement."""
|
||||
if self.thermostat['settings']['useCelsius']:
|
||||
return TEMP_CELSIUS
|
||||
|
||||
return TEMP_FAHRENHEIT
|
||||
|
||||
@property
|
||||
@@ -308,9 +307,9 @@ class Thermostat(ClimateDevice):
|
||||
"""Return true if aux heater."""
|
||||
return 'auxHeat' in self.thermostat['equipmentStatus']
|
||||
|
||||
def set_preset(self, preset):
|
||||
def set_preset_mode(self, preset_mode):
|
||||
"""Activate a preset."""
|
||||
if preset == self.preset_mode:
|
||||
if preset_mode == self.preset_mode:
|
||||
return
|
||||
|
||||
self.update_without_throttle = True
|
||||
@@ -320,23 +319,26 @@ class Thermostat(ClimateDevice):
|
||||
self.data.ecobee.delete_vacation(
|
||||
self.thermostat_index, self.vacation)
|
||||
|
||||
if preset == PRESET_AWAY:
|
||||
if preset_mode == PRESET_AWAY:
|
||||
self.data.ecobee.set_climate_hold(self.thermostat_index, 'away',
|
||||
'indefinite')
|
||||
|
||||
elif preset == PRESET_TEMPERATURE:
|
||||
elif preset_mode == PRESET_TEMPERATURE:
|
||||
self.set_temp_hold(self.current_temperature)
|
||||
|
||||
elif preset in (PRESET_HOLD_NEXT_TRANSITION, PRESET_HOLD_INDEFINITE):
|
||||
elif preset_mode in (
|
||||
PRESET_HOLD_NEXT_TRANSITION, PRESET_HOLD_INDEFINITE):
|
||||
self.data.ecobee.set_climate_hold(
|
||||
self.thermostat_index, PRESET_TO_ECOBEE_HOLD[preset],
|
||||
self.thermostat_index, PRESET_TO_ECOBEE_HOLD[preset_mode],
|
||||
self.hold_preference())
|
||||
|
||||
elif preset is None:
|
||||
elif preset_mode is PRESET_NONE:
|
||||
self.data.ecobee.resume_program(self.thermostat_index)
|
||||
|
||||
else:
|
||||
_LOGGER.warning("Received invalid preset: %s", preset)
|
||||
self.data.ecobee.set_climate_hold(
|
||||
self.thermostat_index, preset_mode, self.hold_preference())
|
||||
self.update_without_throttle = True
|
||||
|
||||
@property
|
||||
def preset_modes(self):
|
||||
|
||||
@@ -7,7 +7,7 @@ import voluptuous as vol
|
||||
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice
|
||||
from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF, PRESET_AWAY, PRESET_BOOST,
|
||||
SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE)
|
||||
SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, PRESET_NONE)
|
||||
from homeassistant.const import (
|
||||
ATTR_TEMPERATURE, CONF_DEVICES, CONF_MAC, PRECISION_HALVES, TEMP_CELSIUS)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -66,9 +66,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
|
||||
for name, device_cfg in config[CONF_DEVICES].items():
|
||||
mac = device_cfg[CONF_MAC]
|
||||
devices.append(EQ3BTSmartThermostat(mac, name), True)
|
||||
devices.append(EQ3BTSmartThermostat(mac, name))
|
||||
|
||||
add_entities(devices)
|
||||
add_entities(devices, True)
|
||||
|
||||
|
||||
class EQ3BTSmartThermostat(ClimateDevice):
|
||||
@@ -181,7 +181,7 @@ class EQ3BTSmartThermostat(ClimateDevice):
|
||||
|
||||
def set_preset_mode(self, preset_mode):
|
||||
"""Set new preset mode."""
|
||||
if not preset_mode:
|
||||
if preset_mode == PRESET_NONE:
|
||||
self.set_hvac_mode(HVAC_MODE_HEAT)
|
||||
self._thermostat.mode = HA_TO_EQ_PRESET[preset_mode]
|
||||
|
||||
|
||||
@@ -76,6 +76,11 @@ class EsphomeClimateDevice(EsphomeEntity, ClimateDevice):
|
||||
for mode in self._static_info.supported_modes
|
||||
]
|
||||
|
||||
@property
|
||||
def preset_modes(self):
|
||||
"""Return preset modes."""
|
||||
return [PRESET_AWAY] if self._static_info.supports_away else []
|
||||
|
||||
@property
|
||||
def target_temperature_step(self) -> float:
|
||||
"""Return the supported step of target temperature."""
|
||||
@@ -97,7 +102,7 @@ class EsphomeClimateDevice(EsphomeEntity, ClimateDevice):
|
||||
"""Return the list of supported features."""
|
||||
features = 0
|
||||
if self._static_info.supports_two_point_target_temperature:
|
||||
features |= (SUPPORT_TARGET_TEMPERATURE_RANGE)
|
||||
features |= SUPPORT_TARGET_TEMPERATURE_RANGE
|
||||
else:
|
||||
features |= SUPPORT_TARGET_TEMPERATURE
|
||||
if self._static_info.supports_away:
|
||||
@@ -109,6 +114,11 @@ class EsphomeClimateDevice(EsphomeEntity, ClimateDevice):
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
return _climate_modes.from_esphome(self._state.mode)
|
||||
|
||||
@esphome_state_property
|
||||
def preset_mode(self):
|
||||
"""Return current preset mode."""
|
||||
return PRESET_AWAY if self._state.away else None
|
||||
|
||||
@esphome_state_property
|
||||
def current_temperature(self) -> Optional[float]:
|
||||
"""Return the current temperature."""
|
||||
@@ -143,29 +153,13 @@ class EsphomeClimateDevice(EsphomeEntity, ClimateDevice):
|
||||
data['target_temperature_high'] = kwargs[ATTR_TARGET_TEMP_HIGH]
|
||||
await self._client.climate_command(**data)
|
||||
|
||||
async def async_set_operation_mode(self, operation_mode) -> None:
|
||||
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
|
||||
"""Set new target operation mode."""
|
||||
await self._client.climate_command(
|
||||
key=self._static_info.key,
|
||||
mode=_climate_modes.from_hass(operation_mode),
|
||||
mode=_climate_modes.from_hass(hvac_mode),
|
||||
)
|
||||
|
||||
@property
|
||||
def preset_mode(self):
|
||||
"""Return current preset mode."""
|
||||
if self._state and self._state.away:
|
||||
return PRESET_AWAY
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def preset_modes(self):
|
||||
"""Return preset modes."""
|
||||
if self._static_info.supports_away:
|
||||
return [PRESET_AWAY]
|
||||
|
||||
return []
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode):
|
||||
"""Set preset mode."""
|
||||
away = preset_mode == PRESET_AWAY
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
PRESET_AWAY, PRESET_ECO, PRESET_HOME,
|
||||
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,26 +31,26 @@ 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: None,
|
||||
EVO_FOLLOW: PRESET_NONE,
|
||||
EVO_TEMPOVER: 'temporary',
|
||||
EVO_PERMOVER: 'permanent',
|
||||
}
|
||||
HA_PRESET_TO_EVO = {v: k for k, v in EVO_PRESET_TO_HA.items()
|
||||
if v is not None}
|
||||
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]
|
||||
@@ -103,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:
|
||||
@@ -130,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)
|
||||
|
||||
@@ -148,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]]:
|
||||
@@ -191,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]:
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -93,9 +93,10 @@ class FritzboxThermostat(ClimateDevice):
|
||||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the temperature we try to reach."""
|
||||
if self._target_temperature in (ON_API_TEMPERATURE,
|
||||
OFF_API_TEMPERATURE):
|
||||
return None
|
||||
if self._target_temperature == ON_API_TEMPERATURE:
|
||||
return ON_REPORT_SET_TEMPERATURE
|
||||
if self._target_temperature == OFF_API_TEMPERATURE:
|
||||
return OFF_REPORT_SET_TEMPERATURE
|
||||
return self._target_temperature
|
||||
|
||||
def set_temperature(self, **kwargs):
|
||||
@@ -110,7 +111,10 @@ class FritzboxThermostat(ClimateDevice):
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
"""Return the current operation mode."""
|
||||
if self._target_temperature == OFF_REPORT_SET_TEMPERATURE:
|
||||
if (
|
||||
self._target_temperature == OFF_REPORT_SET_TEMPERATURE or
|
||||
self._target_temperature == OFF_API_TEMPERATURE
|
||||
):
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
return HVAC_MODE_HEAT
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Home Assistant Frontend",
|
||||
"documentation": "https://www.home-assistant.io/components/frontend",
|
||||
"requirements": [
|
||||
"home-assistant-frontend==20190717.1"
|
||||
"home-assistant-frontend==20190721.1"
|
||||
],
|
||||
"dependencies": [
|
||||
"api",
|
||||
|
||||
@@ -8,7 +8,7 @@ from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice
|
||||
from homeassistant.components.climate.const import (
|
||||
ATTR_PRESET_MODE, CURRENT_HVAC_COOL, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE,
|
||||
CURRENT_HVAC_OFF, HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF,
|
||||
PRESET_AWAY, SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE)
|
||||
PRESET_AWAY, SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, PRESET_NONE)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, ATTR_TEMPERATURE, CONF_NAME, EVENT_HOMEASSISTANT_START,
|
||||
PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE, SERVICE_TURN_OFF,
|
||||
@@ -251,7 +251,7 @@ class GenericThermostat(ClimateDevice, RestoreEntity):
|
||||
def preset_modes(self):
|
||||
"""Return a list of available preset modes."""
|
||||
if self._away_temp:
|
||||
return [PRESET_AWAY]
|
||||
return [PRESET_NONE, PRESET_AWAY]
|
||||
return None
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
@@ -404,7 +404,7 @@ class GenericThermostat(ClimateDevice, RestoreEntity):
|
||||
self._saved_target_temp = self._target_temp
|
||||
self._target_temp = self._away_temp
|
||||
await self._async_control_heating(force=True)
|
||||
elif not preset_mode and self._is_away:
|
||||
elif preset_mode == PRESET_NONE and self._is_away:
|
||||
self._is_away = False
|
||||
self._target_temp = self._saved_target_temp
|
||||
await self._async_control_heating(force=True)
|
||||
|
||||
@@ -54,6 +54,9 @@ async def async_setup(hass, hass_config):
|
||||
exc_info=True)
|
||||
return False
|
||||
|
||||
_LOGGER.debug("zones_raw = %s", data._client.hub._zones_raw) # noqa; pylint: disable=protected-access
|
||||
_LOGGER.debug("devices_raw = %s", data._client.hub._devices_raw) # noqa; pylint: disable=protected-access
|
||||
|
||||
async_track_time_interval(hass, data.async_update, SCAN_INTERVAL)
|
||||
|
||||
for platform in ['climate', 'water_heater']:
|
||||
@@ -84,4 +87,8 @@ class GeniusData:
|
||||
except AssertionError: # assert response.status == HTTP_OK
|
||||
_LOGGER.warning("Update failed.", exc_info=True)
|
||||
return
|
||||
|
||||
_LOGGER.debug("zones_raw = %s", self._client.hub._zones_raw) # noqa; pylint: disable=protected-access
|
||||
_LOGGER.debug("devices_raw = %s", self._client.hub._devices_raw) # noqa; pylint: disable=protected-access
|
||||
|
||||
async_dispatcher_send(self._hass, DOMAIN)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Genius Hub",
|
||||
"documentation": "https://www.home-assistant.io/components/geniushub",
|
||||
"requirements": [
|
||||
"geniushub-client==0.4.12"
|
||||
"geniushub-client==0.5.00"
|
||||
],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@zxdavb"]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from homeassistant.components.climate import ClimateDevice
|
||||
from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF, PRESET_BOOST,
|
||||
SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE)
|
||||
SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, PRESET_NONE)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||
|
||||
from . import DATA_HIVE, DOMAIN
|
||||
@@ -21,7 +21,7 @@ HASS_TO_HIVE_STATE = {
|
||||
|
||||
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
|
||||
SUPPORT_HVAC = [HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF]
|
||||
SUPPORT_PRESET = [PRESET_BOOST]
|
||||
SUPPORT_PRESET = [PRESET_NONE, PRESET_BOOST]
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
@@ -168,7 +168,7 @@ class HiveClimateEntity(ClimateDevice):
|
||||
|
||||
def set_preset_mode(self, preset_mode) -> None:
|
||||
"""Set new preset mode."""
|
||||
if preset_mode is None and self.preset_mode == PRESET_BOOST:
|
||||
if preset_mode == PRESET_NONE and self.preset_mode == PRESET_BOOST:
|
||||
self.session.heating.turn_boost_off(self.node_id)
|
||||
|
||||
elif preset_mode == PRESET_BOOST:
|
||||
|
||||
@@ -66,6 +66,8 @@ class HMThermostat(HMDevice, ClimateDevice):
|
||||
|
||||
Need to be one of HVAC_MODE_*.
|
||||
"""
|
||||
if self.target_temperature <= self._hmdevice.OFF_VALUE + 0.5:
|
||||
return HVAC_MODE_OFF
|
||||
if "MANU_MODE" in self._hmdevice.ACTIONNODE:
|
||||
if self._hm_controll_mode == self._hmdevice.MANU_MODE:
|
||||
return HVAC_MODE_HEAT
|
||||
@@ -157,12 +159,12 @@ class HMThermostat(HMDevice, ClimateDevice):
|
||||
|
||||
@property
|
||||
def min_temp(self):
|
||||
"""Return the minimum temperature - 4.5 means off."""
|
||||
"""Return the minimum temperature."""
|
||||
return 4.5
|
||||
|
||||
@property
|
||||
def max_temp(self):
|
||||
"""Return the maximum temperature - 30.5 means on."""
|
||||
"""Return the maximum temperature."""
|
||||
return 30.5
|
||||
|
||||
@property
|
||||
|
||||
@@ -10,7 +10,7 @@ from homematicip.aio.home import AsyncHome
|
||||
from homeassistant.components.climate import ClimateDevice
|
||||
from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_AUTO, HVAC_MODE_HEAT, PRESET_BOOST, PRESET_ECO,
|
||||
SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE)
|
||||
SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, PRESET_NONE)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -121,7 +121,7 @@ class HomematicipHeatingGroup(HomematicipGenericDevice, ClimateDevice):
|
||||
|
||||
Requires SUPPORT_PRESET_MODE.
|
||||
"""
|
||||
return [PRESET_BOOST]
|
||||
return [PRESET_NONE, PRESET_BOOST]
|
||||
|
||||
@property
|
||||
def min_temp(self) -> float:
|
||||
|
||||
@@ -11,12 +11,12 @@ from homeassistant.components.climate import ClimateDevice, PLATFORM_SCHEMA
|
||||
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,
|
||||
SUPPORT_AUX_HEAT, SUPPORT_FAN_MODE, SUPPORT_PRESET_MODE,
|
||||
SUPPORT_TARGET_HUMIDITY, SUPPORT_TARGET_TEMPERATURE,
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
||||
CURRENT_HVAC_COOL, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, CURRENT_HVAC_OFF,
|
||||
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_AWAY, PRESET_NONE,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS, TEMP_FAHRENHEIT,
|
||||
@@ -59,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,
|
||||
}
|
||||
@@ -127,32 +127,34 @@ class HoneywellUSThermostat(ClimateDevice):
|
||||
self._username = username
|
||||
self._password = password
|
||||
|
||||
self._supported_features = (SUPPORT_PRESET_MODE |
|
||||
SUPPORT_TARGET_TEMPERATURE |
|
||||
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."""
|
||||
@@ -161,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
|
||||
@@ -173,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."""
|
||||
@@ -197,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
|
||||
@@ -209,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]:
|
||||
@@ -231,7 +257,7 @@ class HoneywellUSThermostat(ClimateDevice):
|
||||
@property
|
||||
def preset_modes(self) -> Optional[List[str]]:
|
||||
"""Return a list of available preset modes."""
|
||||
return [PRESET_AWAY]
|
||||
return [PRESET_NONE, PRESET_AWAY]
|
||||
|
||||
@property
|
||||
def is_aux_heat(self) -> Optional[str]:
|
||||
@@ -350,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.
|
||||
@@ -398,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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -219,7 +219,7 @@ class KNXClimate(ClimateDevice):
|
||||
@property
|
||||
def target_temperature_step(self):
|
||||
"""Return the supported step of target temperature."""
|
||||
return self.device.temperature_step
|
||||
return self.device.setpoint_shift_step
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
|
||||
@@ -12,7 +12,7 @@ from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_HEAT, HVAC_MODE_OFF,
|
||||
SUPPORT_AUX_HEAT, SUPPORT_FAN_MODE, SUPPORT_PRESET_MODE,
|
||||
SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE, PRESET_AWAY,
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE)
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE, PRESET_NONE)
|
||||
from homeassistant.components.fan import SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM
|
||||
from homeassistant.const import (
|
||||
ATTR_TEMPERATURE, CONF_DEVICE, CONF_NAME, CONF_VALUE_TEMPLATE, STATE_ON)
|
||||
@@ -538,6 +538,9 @@ class MqttClimate(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate,
|
||||
|
||||
presets.extend(self._config[CONF_HOLD_LIST])
|
||||
|
||||
if presets:
|
||||
presets.insert(0, PRESET_NONE)
|
||||
|
||||
return presets
|
||||
|
||||
@property
|
||||
|
||||
@@ -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)
|
||||
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,7 +29,22 @@ NEST_MODE_HEAT = 'heat'
|
||||
NEST_MODE_COOL = 'cool'
|
||||
NEST_MODE_OFF = 'off'
|
||||
|
||||
PRESET_MODES = [PRESET_AWAY, PRESET_ECO]
|
||||
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]
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
@@ -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'
|
||||
|
||||
@@ -16,15 +16,18 @@ from homeassistant.components.climate.const import (
|
||||
DEFAULT_MIN_TEMP
|
||||
)
|
||||
from homeassistant.const import (
|
||||
TEMP_CELSIUS, ATTR_TEMPERATURE, CONF_NAME, PRECISION_HALVES, STATE_OFF)
|
||||
TEMP_CELSIUS, ATTR_TEMPERATURE, CONF_NAME, PRECISION_HALVES, STATE_OFF,
|
||||
ATTR_BATTERY_LEVEL
|
||||
)
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from .const import DATA_NETATMO_AUTH
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PRESET_FROST_GUARD = 'frost guard'
|
||||
PRESET_SCHEDULE = 'schedule'
|
||||
PRESET_FROST_GUARD = 'Frost Guard'
|
||||
PRESET_SCHEDULE = 'Schedule'
|
||||
PRESET_MANUAL = 'Manual'
|
||||
|
||||
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE)
|
||||
SUPPORT_HVAC = [HVAC_MODE_HEAT, HVAC_MODE_AUTO, HVAC_MODE_OFF]
|
||||
@@ -32,7 +35,7 @@ SUPPORT_PRESET = [
|
||||
PRESET_AWAY, PRESET_BOOST, PRESET_FROST_GUARD, PRESET_SCHEDULE,
|
||||
]
|
||||
|
||||
STATE_NETATMO_SCHEDULE = PRESET_SCHEDULE
|
||||
STATE_NETATMO_SCHEDULE = 'schedule'
|
||||
STATE_NETATMO_HG = 'hg'
|
||||
STATE_NETATMO_MAX = 'max'
|
||||
STATE_NETATMO_AWAY = PRESET_AWAY
|
||||
@@ -42,18 +45,28 @@ STATE_NETATMO_MANUAL = 'manual'
|
||||
PRESET_MAP_NETATMO = {
|
||||
PRESET_FROST_GUARD: STATE_NETATMO_HG,
|
||||
PRESET_BOOST: STATE_NETATMO_MAX,
|
||||
STATE_NETATMO_MAX: STATE_NETATMO_MAX,
|
||||
PRESET_SCHEDULE: STATE_NETATMO_SCHEDULE,
|
||||
PRESET_AWAY: STATE_NETATMO_AWAY,
|
||||
STATE_NETATMO_OFF: STATE_NETATMO_OFF
|
||||
}
|
||||
|
||||
NETATMO_MAP_PRESET = {
|
||||
STATE_NETATMO_HG: PRESET_FROST_GUARD,
|
||||
STATE_NETATMO_MAX: PRESET_BOOST,
|
||||
STATE_NETATMO_SCHEDULE: PRESET_SCHEDULE,
|
||||
STATE_NETATMO_AWAY: PRESET_AWAY,
|
||||
STATE_NETATMO_OFF: STATE_NETATMO_OFF,
|
||||
STATE_NETATMO_MANUAL: STATE_NETATMO_MANUAL,
|
||||
}
|
||||
|
||||
HVAC_MAP_NETATMO = {
|
||||
STATE_NETATMO_SCHEDULE: HVAC_MODE_AUTO,
|
||||
PRESET_SCHEDULE: HVAC_MODE_AUTO,
|
||||
STATE_NETATMO_HG: HVAC_MODE_AUTO,
|
||||
STATE_NETATMO_MAX: HVAC_MODE_HEAT,
|
||||
PRESET_FROST_GUARD: HVAC_MODE_AUTO,
|
||||
PRESET_BOOST: HVAC_MODE_HEAT,
|
||||
STATE_NETATMO_OFF: HVAC_MODE_OFF,
|
||||
STATE_NETATMO_MANUAL: HVAC_MODE_AUTO,
|
||||
PRESET_MANUAL: HVAC_MODE_AUTO,
|
||||
STATE_NETATMO_AWAY: HVAC_MODE_AUTO
|
||||
}
|
||||
|
||||
@@ -142,6 +155,7 @@ class NetatmoThermostat(ClimateDevice):
|
||||
self._operation_list = [HVAC_MODE_AUTO, HVAC_MODE_HEAT]
|
||||
self._support_flags = SUPPORT_FLAGS
|
||||
self._hvac_mode = None
|
||||
self._battery_level = None
|
||||
self.update_without_throttle = False
|
||||
self._module_type = \
|
||||
self._data.room_status.get(room_id, {}).get('module_type')
|
||||
@@ -208,9 +222,9 @@ class NetatmoThermostat(ClimateDevice):
|
||||
if hvac_mode == HVAC_MODE_OFF:
|
||||
mode = STATE_NETATMO_OFF
|
||||
elif hvac_mode == HVAC_MODE_AUTO:
|
||||
mode = STATE_NETATMO_SCHEDULE
|
||||
mode = PRESET_SCHEDULE
|
||||
elif hvac_mode == HVAC_MODE_HEAT:
|
||||
mode = STATE_NETATMO_MAX
|
||||
mode = PRESET_BOOST
|
||||
|
||||
self.set_preset_mode(mode)
|
||||
|
||||
@@ -249,6 +263,8 @@ class NetatmoThermostat(ClimateDevice):
|
||||
self._data.homestatus.setThermmode(
|
||||
self._data.home_id, PRESET_MAP_NETATMO[preset_mode]
|
||||
)
|
||||
else:
|
||||
_LOGGER.error("Preset mode '%s' not available", preset_mode)
|
||||
self.update_without_throttle = True
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
@@ -274,6 +290,16 @@ class NetatmoThermostat(ClimateDevice):
|
||||
self.update_without_throttle = True
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the thermostat."""
|
||||
attr = {}
|
||||
|
||||
if self._battery_level is not None:
|
||||
attr[ATTR_BATTERY_LEVEL] = self._battery_level
|
||||
|
||||
return attr
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data from NetAtmo API and updates the states."""
|
||||
try:
|
||||
@@ -294,13 +320,16 @@ class NetatmoThermostat(ClimateDevice):
|
||||
self._data.room_status[self._room_id]['current_temperature']
|
||||
self._target_temperature = \
|
||||
self._data.room_status[self._room_id]['target_temperature']
|
||||
self._preset = \
|
||||
self._preset = NETATMO_MAP_PRESET[
|
||||
self._data.room_status[self._room_id]["setpoint_mode"]
|
||||
]
|
||||
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
|
||||
except KeyError:
|
||||
self._battery_level = \
|
||||
self._data.room_status[self._room_id].get('battery_level')
|
||||
except KeyError as err:
|
||||
_LOGGER.error(
|
||||
"The thermostat in room %s seems to be out of reach.",
|
||||
self._room_id
|
||||
"The thermostat in room %s seems to be out of reach. (%s)",
|
||||
self._room_id, err
|
||||
)
|
||||
self._away = self._hvac_mode == HVAC_MAP_NETATMO[STATE_NETATMO_AWAY]
|
||||
|
||||
@@ -423,6 +452,7 @@ class ThermostatData:
|
||||
roomstatus["module_id"] = None
|
||||
roomstatus["heating_status"] = None
|
||||
roomstatus["heating_power_request"] = None
|
||||
batterylevel = None
|
||||
for module_id in homedata_room["module_ids"]:
|
||||
if (self.homedata.modules[self.home][module_id]["type"]
|
||||
== NA_THERM
|
||||
@@ -433,6 +463,10 @@ class ThermostatData:
|
||||
rid=roomstatus["module_id"]
|
||||
)
|
||||
roomstatus["heating_status"] = self.boilerstatus
|
||||
batterylevel = (
|
||||
self.homestatus
|
||||
.thermostats[roomstatus["module_id"]]
|
||||
.get("battery_level"))
|
||||
elif roomstatus["module_type"] == NA_VALVE:
|
||||
roomstatus["heating_power_request"] = homestatus_room[
|
||||
"heating_power_request"
|
||||
@@ -445,9 +479,60 @@ class ThermostatData:
|
||||
self.boilerstatus
|
||||
and roomstatus["heating_status"]
|
||||
)
|
||||
batterylevel = (
|
||||
self.homestatus.valves[roomstatus["module_id"]]
|
||||
.get("battery_level"))
|
||||
|
||||
if batterylevel:
|
||||
batterypct = interpolate(
|
||||
batterylevel, roomstatus["module_type"])
|
||||
if roomstatus.get("battery_level") is None:
|
||||
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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -7,7 +7,7 @@ import voluptuous as vol
|
||||
from homeassistant.components.climate import ClimateDevice
|
||||
from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF, SUPPORT_PRESET_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE)
|
||||
SUPPORT_TARGET_TEMPERATURE, PRESET_NONE)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -157,6 +157,7 @@ class NuHeatThermostat(ClimateDevice):
|
||||
def preset_modes(self):
|
||||
"""Return available preset modes."""
|
||||
return [
|
||||
PRESET_NONE,
|
||||
MODE_HOLD_TEMPERATURE,
|
||||
MODE_TEMPORARY_HOLD
|
||||
]
|
||||
@@ -173,7 +174,7 @@ class NuHeatThermostat(ClimateDevice):
|
||||
|
||||
def set_preset_mode(self, preset_mode):
|
||||
"""Update the hold mode of the thermostat."""
|
||||
if preset_mode is None:
|
||||
if preset_mode == PRESET_NONE:
|
||||
schedule_mode = SCHEDULE_RUN
|
||||
|
||||
elif preset_mode == MODE_HOLD_TEMPERATURE:
|
||||
|
||||
@@ -130,9 +130,14 @@ class OpenThermClimate(ClimateDevice):
|
||||
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
"""Return current HVAC mode."""
|
||||
return self._current_operation
|
||||
|
||||
@property
|
||||
def hvac_modes(self):
|
||||
"""Return available HVAC modes."""
|
||||
return []
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
@@ -165,7 +170,7 @@ class OpenThermClimate(ClimateDevice):
|
||||
@property
|
||||
def preset_modes(self):
|
||||
"""Available preset modes to set."""
|
||||
return [PRESET_AWAY]
|
||||
return []
|
||||
|
||||
def set_preset_mode(self, preset_mode):
|
||||
"""Set the preset mode."""
|
||||
|
||||
@@ -363,7 +363,7 @@ class DailyHistory:
|
||||
def add_measurement(self, value, timestamp=None):
|
||||
"""Add a new measurement for a certain day."""
|
||||
day = (timestamp or datetime.now()).date()
|
||||
if value is None:
|
||||
if not isinstance(value, (int, float)):
|
||||
return
|
||||
if self._days is None:
|
||||
self._days = deque()
|
||||
@@ -388,4 +388,6 @@ class DailyHistory:
|
||||
oldest = self._days.popleft()
|
||||
del self._max_dict[oldest]
|
||||
self._days.append(day)
|
||||
if not isinstance(value, (int, float)):
|
||||
return
|
||||
self._max_dict[day] = value
|
||||
|
||||
@@ -224,13 +224,12 @@ class RadioThermostat(ClimateDevice):
|
||||
|
||||
if self._is_model_ct80:
|
||||
try:
|
||||
humiditydata = self.device.tstat.humidity['raw']
|
||||
humiditydata = self.device.humidity['raw']
|
||||
except radiotherm.validate.RadiothermTstatError:
|
||||
_LOGGER.warning('%s (%s) was busy (invalid value returned)',
|
||||
self._name, self.device.host)
|
||||
return
|
||||
current_humidity = humiditydata['humidity']
|
||||
self._current_humidity = current_humidity
|
||||
self._current_humidity = humiditydata
|
||||
|
||||
# Map thermostat values into various STATE_ flags.
|
||||
self._current_temperature = current_temp
|
||||
|
||||
@@ -318,6 +318,18 @@ class SensiboClimate(ClimateDevice):
|
||||
await self._client.async_set_ac_state_property(
|
||||
self._id, 'swing', swing_mode, self._ac_states)
|
||||
|
||||
async def async_turn_on(self):
|
||||
"""Turn Sensibo unit on."""
|
||||
with async_timeout.timeout(TIMEOUT):
|
||||
await self._client.async_set_ac_state_property(
|
||||
self._id, 'on', True, self._ac_states)
|
||||
|
||||
async def async_turn_off(self):
|
||||
"""Turn Sensibo unit on."""
|
||||
with async_timeout.timeout(TIMEOUT):
|
||||
await self._client.async_set_ac_state_property(
|
||||
self._id, 'on', False, self._ac_states)
|
||||
|
||||
async def async_assume_state(self, state):
|
||||
"""Set external state."""
|
||||
change_needed = \
|
||||
|
||||
@@ -323,8 +323,16 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateDevice):
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set new target operation mode."""
|
||||
await self._device.set_air_conditioner_mode(
|
||||
STATE_TO_AC_MODE[hvac_mode], set_status=True)
|
||||
if hvac_mode == HVAC_MODE_OFF:
|
||||
await self.async_turn_off()
|
||||
return
|
||||
tasks = []
|
||||
# Turn on the device if it's off before setting mode.
|
||||
if not self._device.status.switch:
|
||||
tasks.append(self._device.switch_on(set_status=True))
|
||||
tasks.append(self._device.set_air_conditioner_mode(
|
||||
STATE_TO_AC_MODE[hvac_mode], set_status=True))
|
||||
await asyncio.gather(*tasks)
|
||||
# State is set optimistically in the command above, therefore update
|
||||
# the entity state ahead of receiving the confirming push updates
|
||||
self.async_schedule_update_ha_state()
|
||||
@@ -335,7 +343,12 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateDevice):
|
||||
# operation mode
|
||||
operation_mode = kwargs.get(ATTR_HVAC_MODE)
|
||||
if operation_mode:
|
||||
tasks.append(self.async_set_hvac_mode(operation_mode))
|
||||
if operation_mode == HVAC_MODE_OFF:
|
||||
tasks.append(self._device.switch_off(set_status=True))
|
||||
else:
|
||||
if not self._device.status.switch:
|
||||
tasks.append(self._device.switch_on(set_status=True))
|
||||
tasks.append(self.async_set_hvac_mode(operation_mode))
|
||||
# temperature
|
||||
tasks.append(self._device.set_cooling_setpoint(
|
||||
kwargs[ATTR_TEMPERATURE], set_status=True))
|
||||
@@ -344,18 +357,32 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateDevice):
|
||||
# the entity state ahead of receiving the confirming push updates
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
async def async_turn_on(self):
|
||||
"""Turn device on."""
|
||||
await self._device.switch_on(set_status=True)
|
||||
# State is set optimistically in the command above, therefore update
|
||||
# the entity state ahead of receiving the confirming push updates
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
async def async_turn_off(self):
|
||||
"""Turn device off."""
|
||||
await self._device.switch_off(set_status=True)
|
||||
# State is set optimistically in the command above, therefore update
|
||||
# the entity state ahead of receiving the confirming push updates
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
async def async_update(self):
|
||||
"""Update the calculated fields of the AC."""
|
||||
operations = set()
|
||||
modes = {HVAC_MODE_OFF}
|
||||
for mode in self._device.status.supported_ac_modes:
|
||||
state = AC_MODE_TO_STATE.get(mode)
|
||||
if state is not None:
|
||||
operations.add(state)
|
||||
modes.add(state)
|
||||
else:
|
||||
_LOGGER.debug('Device %s (%s) returned an invalid supported '
|
||||
'AC mode: %s', self._device.label,
|
||||
self._device.device_id, mode)
|
||||
self._hvac_modes = operations
|
||||
self._hvac_modes = modes
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
@@ -400,6 +427,8 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateDevice):
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
if not self._device.status.switch:
|
||||
return HVAC_MODE_OFF
|
||||
return AC_MODE_TO_STATE.get(self._device.status.air_conditioner_mode)
|
||||
|
||||
@property
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -130,9 +130,8 @@ class ToonThermostatDevice(ToonDisplayDeviceEntity, ClimateDevice):
|
||||
|
||||
def set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new preset mode."""
|
||||
if preset_mode is not None:
|
||||
self._client.thermostat_state = self._preset = preset_mode
|
||||
self.schedule_update_ha_state()
|
||||
self._client.thermostat_state = self._preset = preset_mode
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def set_hvac_mode(self, hvac_mode: str) -> None:
|
||||
"""Set new target hvac mode."""
|
||||
|
||||
@@ -8,7 +8,7 @@ from homeassistant.components.climate.const import (
|
||||
ATTR_HVAC_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
|
||||
HVAC_MODE_AUTO, HVAC_MODE_COOL, HVAC_MODE_HEAT, SUPPORT_FAN_MODE,
|
||||
SUPPORT_TARGET_HUMIDITY, SUPPORT_PRESET_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE, PRESET_AWAY,
|
||||
SUPPORT_TARGET_TEMPERATURE, PRESET_AWAY, PRESET_NONE,
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
||||
HVAC_MODE_OFF)
|
||||
from homeassistant.const import (
|
||||
@@ -213,6 +213,7 @@ class VenstarThermostat(ClimateDevice):
|
||||
def preset_modes(self):
|
||||
"""Return valid preset modes."""
|
||||
return [
|
||||
PRESET_NONE,
|
||||
PRESET_AWAY,
|
||||
HOLD_MODE_TEMPERATURE,
|
||||
]
|
||||
@@ -286,7 +287,7 @@ class VenstarThermostat(ClimateDevice):
|
||||
success = self._client.set_away(self._client.AWAY_AWAY)
|
||||
elif preset_mode == HOLD_MODE_TEMPERATURE:
|
||||
success = self._client.set_schedule(0)
|
||||
elif preset_mode is None:
|
||||
elif preset_mode == PRESET_NONE:
|
||||
success = False
|
||||
if self._client.away:
|
||||
success = self._client.set_away(self._client.AWAY_HOME)
|
||||
|
||||
@@ -10,7 +10,7 @@ from homeassistant.components.climate.const import (
|
||||
FAN_LOW, FAN_MEDIUM, FAN_ON, HVAC_MODE_AUTO, HVAC_MODE_COOL,
|
||||
HVAC_MODE_FAN_ONLY, HVAC_MODE_HEAT, HVAC_MODE_OFF, PRESET_AWAY, PRESET_ECO,
|
||||
SUPPORT_AUX_HEAT, SUPPORT_FAN_MODE, SUPPORT_TARGET_TEMPERATURE,
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE)
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE, PRESET_NONE)
|
||||
from homeassistant.const import (
|
||||
ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS)
|
||||
from homeassistant.helpers.temperature import display_temp as show_temp
|
||||
@@ -44,7 +44,7 @@ SUPPORT_PRESET_THERMOSTAT = [PRESET_AWAY, PRESET_ECO]
|
||||
|
||||
SUPPORT_FLAGS_AC = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE
|
||||
SUPPORT_FAN_AC = [FAN_HIGH, FAN_LOW, FAN_MEDIUM]
|
||||
SUPPORT_PRESET_AC = [PRESET_ECO]
|
||||
SUPPORT_PRESET_AC = [PRESET_NONE, PRESET_ECO]
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
@@ -137,7 +137,7 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
@property
|
||||
def preset_mode(self):
|
||||
"""Return the current preset mode, e.g., home, away, temp."""
|
||||
mode = self.wink.current_mode()
|
||||
mode = self.wink.current_hvac_mode()
|
||||
if mode == "eco":
|
||||
return PRESET_ECO
|
||||
if self.wink.away():
|
||||
@@ -192,7 +192,7 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
"""Return true if aux heater."""
|
||||
if 'aux' not in self.wink.hvac_modes():
|
||||
return None
|
||||
if self.wink.hvac_action_mode() == 'aux':
|
||||
if self.wink.current_hvac_mode() == 'aux':
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -205,7 +205,7 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
if not self.wink.is_on():
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
wink_mode = self.wink.current_mode()
|
||||
wink_mode = self.wink.current_hvac_mode()
|
||||
if wink_mode == "aux":
|
||||
return HVAC_MODE_HEAT
|
||||
if wink_mode == "eco":
|
||||
@@ -220,7 +220,7 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
"""
|
||||
hvac_list = [HVAC_MODE_OFF]
|
||||
|
||||
modes = self.wink.modes()
|
||||
modes = self.wink.hvac_modes()
|
||||
for mode in modes:
|
||||
if mode in ("eco", "aux"):
|
||||
continue
|
||||
@@ -409,7 +409,7 @@ class WinkAC(WinkDevice, ClimateDevice):
|
||||
if not self.wink.is_on():
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
wink_mode = self.wink.current_mode()
|
||||
wink_mode = self.wink.current_hvac_mode()
|
||||
if wink_mode == "auto_eco":
|
||||
return HVAC_MODE_AUTO
|
||||
return WINK_HVAC_TO_HA.get(wink_mode)
|
||||
@@ -422,7 +422,7 @@ class WinkAC(WinkDevice, ClimateDevice):
|
||||
"""
|
||||
hvac_list = [HVAC_MODE_OFF]
|
||||
|
||||
modes = self.wink.modes()
|
||||
modes = self.wink.hvac_modes()
|
||||
for mode in modes:
|
||||
if mode == "auto_eco":
|
||||
continue
|
||||
|
||||
@@ -59,6 +59,7 @@ ATTR_COMMAND = 'command'
|
||||
ATTR_COMMAND_TYPE = 'command_type'
|
||||
ATTR_ARGS = 'args'
|
||||
ATTR_ENDPOINT_ID = 'endpoint_id'
|
||||
ATTR_AVAILABLE = 'available'
|
||||
|
||||
IN = 'in'
|
||||
OUT = 'out'
|
||||
|
||||
@@ -23,7 +23,7 @@ from .const import (
|
||||
MANUFACTURER_CODE, MODEL, NAME, NWK, OUT, POWER_CONFIGURATION_CHANNEL,
|
||||
POWER_SOURCE, QUIRK_APPLIED, QUIRK_CLASS, SERVER, SERVER_COMMANDS,
|
||||
SIGNAL_AVAILABLE, UNKNOWN_MANUFACTURER, UNKNOWN_MODEL, ZDO_CHANNEL,
|
||||
LQI, RSSI, LAST_SEEN)
|
||||
LQI, RSSI, LAST_SEEN, ATTR_AVAILABLE)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_KEEP_ALIVE_INTERVAL = 7200
|
||||
@@ -213,7 +213,8 @@ class ZHADevice:
|
||||
POWER_SOURCE: self.power_source,
|
||||
LQI: self.lqi,
|
||||
RSSI: self.rssi,
|
||||
LAST_SEEN: update_time
|
||||
LAST_SEEN: update_time,
|
||||
ATTR_AVAILABLE: self.available
|
||||
}
|
||||
|
||||
def add_cluster_channel(self, cluster_channel):
|
||||
|
||||
@@ -190,6 +190,13 @@ class ZHAGateway:
|
||||
if entity_id == entity_reference.reference_id:
|
||||
return entity_reference
|
||||
|
||||
def remove_entity_reference(self, entity):
|
||||
"""Remove entity reference for given entity_id if found."""
|
||||
if entity.zha_device.ieee in self.device_registry:
|
||||
entity_refs = self.device_registry.get(entity.zha_device.ieee)
|
||||
self.device_registry[entity.zha_device.ieee] = [
|
||||
e for e in entity_refs if e.reference_id != entity.entity_id]
|
||||
|
||||
@property
|
||||
def devices(self):
|
||||
"""Return devices."""
|
||||
|
||||
@@ -50,7 +50,7 @@ class ZhaEntity(RestoreEntity, entity.Entity):
|
||||
self._available = False
|
||||
self._component = kwargs['component']
|
||||
self._unsubs = []
|
||||
self.remove_future = asyncio.Future()
|
||||
self.remove_future = None
|
||||
for channel in channels:
|
||||
self.cluster_channels[channel.name] = channel
|
||||
|
||||
@@ -123,6 +123,7 @@ class ZhaEntity(RestoreEntity, entity.Entity):
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when about to be added to hass."""
|
||||
await super().async_added_to_hass()
|
||||
self.remove_future = asyncio.Future()
|
||||
await self.async_check_recently_seen()
|
||||
await self.async_accept_signal(
|
||||
None, "{}_{}".format(self.zha_device.available_signal, 'entity'),
|
||||
@@ -151,8 +152,10 @@ class ZhaEntity(RestoreEntity, entity.Entity):
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Disconnect entity object when removed."""
|
||||
for unsub in self._unsubs:
|
||||
for unsub in self._unsubs[:]:
|
||||
unsub()
|
||||
self._unsubs.remove(unsub)
|
||||
self.zha_device.gateway.remove_entity_reference(self)
|
||||
self.remove_future.set_result(True)
|
||||
|
||||
@callback
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"documentation": "https://www.home-assistant.io/components/zha",
|
||||
"requirements": [
|
||||
"bellows-homeassistant==0.8.2",
|
||||
"zha-quirks==0.0.18",
|
||||
"zha-quirks==0.0.19",
|
||||
"zigpy-deconz==0.2.1",
|
||||
"zigpy-homeassistant==0.7.0",
|
||||
"zigpy-xbee-homeassistant==0.4.0"
|
||||
|
||||
@@ -4,8 +4,9 @@ import logging
|
||||
|
||||
from homeassistant.components.climate import ClimateDevice
|
||||
from homeassistant.components.climate.const import (
|
||||
CURRENT_HVAC_COOL, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, CURRENT_HVAC_OFF,
|
||||
DOMAIN, HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF,
|
||||
CURRENT_HVAC_COOL, CURRENT_HVAC_FAN, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE,
|
||||
CURRENT_HVAC_OFF, DOMAIN, HVAC_MODE_COOL, HVAC_MODE_HEAT,
|
||||
HVAC_MODE_HEAT_COOL, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_OFF,
|
||||
SUPPORT_FAN_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||
from homeassistant.core import callback
|
||||
@@ -35,6 +36,11 @@ HVAC_STATE_MAPPINGS = {
|
||||
'Heat': HVAC_MODE_HEAT,
|
||||
'Heat Mode': HVAC_MODE_HEAT,
|
||||
'Heat (Default)': HVAC_MODE_HEAT,
|
||||
'Aux Heat': HVAC_MODE_HEAT,
|
||||
'Furnace': HVAC_MODE_HEAT,
|
||||
'Fan Only': HVAC_MODE_FAN_ONLY,
|
||||
'Dry Air': HVAC_MODE_DRY,
|
||||
'Moist Air': HVAC_MODE_DRY,
|
||||
'Cool': HVAC_MODE_COOL,
|
||||
'Auto': HVAC_MODE_HEAT_COOL,
|
||||
}
|
||||
@@ -43,7 +49,13 @@ HVAC_STATE_MAPPINGS = {
|
||||
HVAC_CURRENT_MAPPINGS = {
|
||||
"Idle": CURRENT_HVAC_IDLE,
|
||||
"Heat": CURRENT_HVAC_HEAT,
|
||||
"Pending Heat": CURRENT_HVAC_IDLE,
|
||||
"Heating": CURRENT_HVAC_HEAT,
|
||||
"Cool": CURRENT_HVAC_COOL,
|
||||
"Pending Cool": CURRENT_HVAC_IDLE,
|
||||
"Cooling": CURRENT_HVAC_COOL,
|
||||
"Fan Only": CURRENT_HVAC_FAN,
|
||||
"Vent / Economiser": CURRENT_HVAC_FAN,
|
||||
"Off": CURRENT_HVAC_OFF,
|
||||
}
|
||||
|
||||
@@ -232,7 +244,9 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
|
||||
|
||||
Need to be a subset of HVAC_MODES.
|
||||
"""
|
||||
return self._hvac_list
|
||||
if self.values.mode:
|
||||
return self._hvac_list
|
||||
return []
|
||||
|
||||
@property
|
||||
def hvac_action(self):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""Constants used by Home Assistant components."""
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 96
|
||||
PATCH_VERSION = '0'
|
||||
PATCH_VERSION = '5'
|
||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||
REQUIRED_PYTHON_VER = (3, 5, 3)
|
||||
|
||||
@@ -433,7 +433,7 @@ class Entity:
|
||||
async def _async_registry_updated(self, event):
|
||||
"""Handle entity registry update."""
|
||||
data = event.data
|
||||
if data['action'] != 'update' and data.get(
|
||||
if data['action'] != 'update' or data.get(
|
||||
'old_entity_id', data['entity_id']) != self.entity_id:
|
||||
return
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ certifi>=2019.6.16
|
||||
cryptography==2.7
|
||||
distro==1.4.0
|
||||
hass-nabucasa==0.15
|
||||
home-assistant-frontend==20190717.1
|
||||
home-assistant-frontend==20190721.1
|
||||
importlib-metadata==0.18
|
||||
jinja2>=2.10.1
|
||||
netdisco==2.6.0
|
||||
|
||||
@@ -505,7 +505,7 @@ gearbest_parser==1.0.7
|
||||
geizhals==0.0.9
|
||||
|
||||
# homeassistant.components.geniushub
|
||||
geniushub-client==0.4.12
|
||||
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==20190717.1
|
||||
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
|
||||
@@ -1939,7 +1939,7 @@ zengge==0.2
|
||||
zeroconf==0.23.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha-quirks==0.0.18
|
||||
zha-quirks==0.0.19
|
||||
|
||||
# homeassistant.components.zhong_hong
|
||||
zhong_hong_hvac==1.0.9
|
||||
|
||||
@@ -165,7 +165,7 @@ hdate==0.8.8
|
||||
holidays==0.9.10
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20190717.1
|
||||
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
|
||||
|
||||
@@ -66,10 +66,10 @@ class TestFritzboxClimate(unittest.TestCase):
|
||||
assert 19.5 == self.thermostat.target_temperature
|
||||
|
||||
self.thermostat._target_temperature = 126.5
|
||||
assert self.thermostat.target_temperature is None
|
||||
assert self.thermostat.target_temperature == 0.0
|
||||
|
||||
self.thermostat._target_temperature = 127.0
|
||||
assert self.thermostat.target_temperature is None
|
||||
assert self.thermostat.target_temperature == 30.0
|
||||
|
||||
@patch.object(FritzboxThermostat, 'set_hvac_mode')
|
||||
def test_set_temperature_operation_mode(self, mock_set_op):
|
||||
@@ -103,7 +103,7 @@ class TestFritzboxClimate(unittest.TestCase):
|
||||
self.thermostat._target_temperature = 127.0
|
||||
assert 'heat' == self.thermostat.hvac_mode
|
||||
self.thermostat._target_temperature = 126.5
|
||||
assert 'heat' == self.thermostat.hvac_mode
|
||||
assert 'off' == self.thermostat.hvac_mode
|
||||
self.thermostat._target_temperature = 22.0
|
||||
assert 'heat' == self.thermostat.hvac_mode
|
||||
self.thermostat._target_temperature = 16.0
|
||||
|
||||
@@ -9,7 +9,7 @@ import voluptuous as vol
|
||||
from homeassistant.components import input_boolean, switch
|
||||
from homeassistant.components.climate.const import (
|
||||
ATTR_PRESET_MODE, DOMAIN, HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF,
|
||||
PRESET_AWAY)
|
||||
PRESET_AWAY, PRESET_NONE)
|
||||
from homeassistant.const import (
|
||||
ATTR_TEMPERATURE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, STATE_ON,
|
||||
TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
@@ -202,7 +202,7 @@ async def test_set_away_mode_and_restore_prev_temp(hass, setup_comp_2):
|
||||
await common.async_set_preset_mode(hass, PRESET_AWAY)
|
||||
state = hass.states.get(ENTITY)
|
||||
assert 16 == state.attributes.get('temperature')
|
||||
await common.async_set_preset_mode(hass, None)
|
||||
await common.async_set_preset_mode(hass, PRESET_NONE)
|
||||
state = hass.states.get(ENTITY)
|
||||
assert 23 == state.attributes.get('temperature')
|
||||
|
||||
@@ -217,7 +217,7 @@ async def test_set_away_mode_twice_and_restore_prev_temp(hass, setup_comp_2):
|
||||
await common.async_set_preset_mode(hass, PRESET_AWAY)
|
||||
state = hass.states.get(ENTITY)
|
||||
assert 16 == state.attributes.get('temperature')
|
||||
await common.async_set_preset_mode(hass, None)
|
||||
await common.async_set_preset_mode(hass, PRESET_NONE)
|
||||
state = hass.states.get(ENTITY)
|
||||
assert 23 == state.attributes.get('temperature')
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -16,7 +16,7 @@ from homeassistant.components.climate.const import (
|
||||
SUPPORT_FAN_MODE,
|
||||
SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE, HVAC_MODE_AUTO,
|
||||
HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY,
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE)
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE, PRESET_NONE)
|
||||
from homeassistant.components.mqtt.discovery import async_start
|
||||
from homeassistant.const import STATE_OFF, STATE_UNAVAILABLE
|
||||
|
||||
@@ -425,7 +425,7 @@ async def test_set_away_mode(hass, mqtt_mock):
|
||||
state = hass.states.get(ENTITY_CLIMATE)
|
||||
assert state.attributes.get('preset_mode') == 'away'
|
||||
|
||||
await common.async_set_preset_mode(hass, None, ENTITY_CLIMATE)
|
||||
await common.async_set_preset_mode(hass, PRESET_NONE, ENTITY_CLIMATE)
|
||||
mqtt_mock.async_publish.assert_called_once_with(
|
||||
'away-mode-topic', 'AUS', 0, False)
|
||||
state = hass.states.get(ENTITY_CLIMATE)
|
||||
@@ -467,7 +467,7 @@ async def test_set_hold(hass, mqtt_mock):
|
||||
state = hass.states.get(ENTITY_CLIMATE)
|
||||
assert state.attributes.get('preset_mode') == 'hold-on'
|
||||
|
||||
await common.async_set_preset_mode(hass, None, ENTITY_CLIMATE)
|
||||
await common.async_set_preset_mode(hass, PRESET_NONE, ENTITY_CLIMATE)
|
||||
mqtt_mock.async_publish.assert_called_once_with(
|
||||
'hold-topic', 'off', 0, False)
|
||||
state = hass.states.get(ENTITY_CLIMATE)
|
||||
|
||||
@@ -13,15 +13,15 @@ from homeassistant.components.climate.const import (
|
||||
ATTR_FAN_MODES, ATTR_HVAC_ACTIONS, ATTR_HVAC_MODE, ATTR_HVAC_MODES,
|
||||
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, CURRENT_HVAC_IDLE,
|
||||
DOMAIN as CLIMATE_DOMAIN, HVAC_MODE_AUTO, HVAC_MODE_COOL, HVAC_MODE_DRY,
|
||||
HVAC_MODE_FAN_ONLY, HVAC_MODE_HEAT, HVAC_MODE_HEAT_COOL,
|
||||
HVAC_MODE_FAN_ONLY, HVAC_MODE_HEAT, HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF,
|
||||
SERVICE_SET_FAN_MODE, SERVICE_SET_HVAC_MODE, SERVICE_SET_TEMPERATURE,
|
||||
SUPPORT_FAN_MODE, SUPPORT_TARGET_TEMPERATURE,
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE)
|
||||
from homeassistant.components.smartthings import climate
|
||||
from homeassistant.components.smartthings.const import DOMAIN
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, STATE_OFF,
|
||||
STATE_UNKNOWN)
|
||||
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE,
|
||||
SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_UNKNOWN)
|
||||
|
||||
from .conftest import setup_platform
|
||||
|
||||
@@ -172,7 +172,7 @@ async def test_legacy_thermostat_entity_state(hass, legacy_thermostat):
|
||||
assert state.attributes[ATTR_HVAC_ACTIONS] == 'idle'
|
||||
assert state.attributes[ATTR_HVAC_MODES] == {
|
||||
HVAC_MODE_AUTO, HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL, HVAC_MODE_HEAT,
|
||||
STATE_OFF}
|
||||
HVAC_MODE_OFF}
|
||||
assert state.attributes[ATTR_FAN_MODE] == 'auto'
|
||||
assert state.attributes[ATTR_FAN_MODES] == ['auto', 'on']
|
||||
assert state.attributes[ATTR_TARGET_TEMP_LOW] == 20 # celsius
|
||||
@@ -184,12 +184,12 @@ async def test_basic_thermostat_entity_state(hass, basic_thermostat):
|
||||
"""Tests the state attributes properly match the thermostat type."""
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[basic_thermostat])
|
||||
state = hass.states.get('climate.basic_thermostat')
|
||||
assert state.state == STATE_OFF
|
||||
assert state.state == HVAC_MODE_OFF
|
||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == \
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE | SUPPORT_TARGET_TEMPERATURE
|
||||
assert ATTR_HVAC_ACTIONS not in state.attributes
|
||||
assert state.attributes[ATTR_HVAC_MODES] == {
|
||||
STATE_OFF, HVAC_MODE_HEAT_COOL, HVAC_MODE_HEAT, HVAC_MODE_COOL}
|
||||
HVAC_MODE_OFF, HVAC_MODE_HEAT_COOL, HVAC_MODE_HEAT, HVAC_MODE_COOL}
|
||||
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 21.1 # celsius
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ async def test_thermostat_entity_state(hass, thermostat):
|
||||
assert state.attributes[ATTR_HVAC_ACTIONS] == CURRENT_HVAC_IDLE
|
||||
assert state.attributes[ATTR_HVAC_MODES] == {
|
||||
HVAC_MODE_AUTO, HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_HEAT_COOL,
|
||||
STATE_OFF}
|
||||
HVAC_MODE_OFF}
|
||||
assert state.attributes[ATTR_FAN_MODE] == 'on'
|
||||
assert state.attributes[ATTR_FAN_MODES] == ['auto', 'on']
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 20 # celsius
|
||||
@@ -245,7 +245,7 @@ async def test_air_conditioner_entity_state(hass, air_conditioner):
|
||||
SUPPORT_TARGET_TEMPERATURE
|
||||
assert sorted(state.attributes[ATTR_HVAC_MODES]) == [
|
||||
HVAC_MODE_COOL, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_HEAT,
|
||||
HVAC_MODE_HEAT_COOL]
|
||||
HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF]
|
||||
assert state.attributes[ATTR_FAN_MODE] == 'medium'
|
||||
assert sorted(state.attributes[ATTR_FAN_MODES]) == \
|
||||
['auto', 'high', 'low', 'medium', 'turbo']
|
||||
@@ -277,8 +277,8 @@ async def test_set_fan_mode(hass, thermostat, air_conditioner):
|
||||
assert state.attributes[ATTR_FAN_MODE] == 'auto', entity_id
|
||||
|
||||
|
||||
async def test_set_operation_mode(hass, thermostat, air_conditioner):
|
||||
"""Test the operation mode is set successfully."""
|
||||
async def test_set_hvac_mode(hass, thermostat, air_conditioner):
|
||||
"""Test the hvac mode is set successfully."""
|
||||
await setup_platform(hass, CLIMATE_DOMAIN,
|
||||
devices=[thermostat, air_conditioner])
|
||||
entity_ids = ['climate.thermostat', 'climate.air_conditioner']
|
||||
@@ -293,6 +293,37 @@ async def test_set_operation_mode(hass, thermostat, air_conditioner):
|
||||
assert state.state == HVAC_MODE_COOL, entity_id
|
||||
|
||||
|
||||
async def test_ac_set_hvac_mode_from_off(hass, air_conditioner):
|
||||
"""Test setting HVAC mode when the unit is off."""
|
||||
air_conditioner.status.update_attribute_value(
|
||||
Attribute.air_conditioner_mode, 'heat')
|
||||
air_conditioner.status.update_attribute_value(Attribute.switch, 'off')
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[air_conditioner])
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.state == HVAC_MODE_OFF
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN, SERVICE_SET_HVAC_MODE, {
|
||||
ATTR_ENTITY_ID: 'climate.air_conditioner',
|
||||
ATTR_HVAC_MODE: HVAC_MODE_HEAT_COOL},
|
||||
blocking=True)
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.state == HVAC_MODE_HEAT_COOL
|
||||
|
||||
|
||||
async def test_ac_set_hvac_mode_off(hass, air_conditioner):
|
||||
"""Test the AC HVAC mode can be turned off set successfully."""
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[air_conditioner])
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.state != HVAC_MODE_OFF
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN, SERVICE_SET_HVAC_MODE, {
|
||||
ATTR_ENTITY_ID: 'climate.air_conditioner',
|
||||
ATTR_HVAC_MODE: HVAC_MODE_OFF},
|
||||
blocking=True)
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.state == HVAC_MODE_OFF
|
||||
|
||||
|
||||
async def test_set_temperature_heat_mode(hass, thermostat):
|
||||
"""Test the temperature is set successfully when in heat mode."""
|
||||
thermostat.status.thermostat_mode = 'heat'
|
||||
@@ -362,6 +393,39 @@ async def test_set_temperature_ac_with_mode(hass, air_conditioner):
|
||||
assert state.state == HVAC_MODE_COOL
|
||||
|
||||
|
||||
async def test_set_temperature_ac_with_mode_from_off(hass, air_conditioner):
|
||||
"""Test the temp and mode is set successfully when the unit is off."""
|
||||
air_conditioner.status.update_attribute_value(
|
||||
Attribute.air_conditioner_mode, 'heat')
|
||||
air_conditioner.status.update_attribute_value(Attribute.switch, 'off')
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[air_conditioner])
|
||||
assert hass.states.get('climate.air_conditioner').state == HVAC_MODE_OFF
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, {
|
||||
ATTR_ENTITY_ID: 'climate.air_conditioner',
|
||||
ATTR_TEMPERATURE: 27,
|
||||
ATTR_HVAC_MODE: HVAC_MODE_COOL},
|
||||
blocking=True)
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 27
|
||||
assert state.state == HVAC_MODE_COOL
|
||||
|
||||
|
||||
async def test_set_temperature_ac_with_mode_to_off(hass, air_conditioner):
|
||||
"""Test the temp and mode is set successfully to turn off the unit."""
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[air_conditioner])
|
||||
assert hass.states.get('climate.air_conditioner').state != HVAC_MODE_OFF
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, {
|
||||
ATTR_ENTITY_ID: 'climate.air_conditioner',
|
||||
ATTR_TEMPERATURE: 27,
|
||||
ATTR_HVAC_MODE: HVAC_MODE_OFF},
|
||||
blocking=True)
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 27
|
||||
assert state.state == HVAC_MODE_OFF
|
||||
|
||||
|
||||
async def test_set_temperature_with_mode(hass, thermostat):
|
||||
"""Test the temperature and mode is set successfully."""
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[thermostat])
|
||||
@@ -378,6 +442,31 @@ async def test_set_temperature_with_mode(hass, thermostat):
|
||||
assert state.state == HVAC_MODE_HEAT_COOL
|
||||
|
||||
|
||||
async def test_set_turn_off(hass, air_conditioner):
|
||||
"""Test the a/c is turned off successfully."""
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[air_conditioner])
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.state == HVAC_MODE_HEAT_COOL
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN, SERVICE_TURN_OFF,
|
||||
blocking=True)
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.state == HVAC_MODE_OFF
|
||||
|
||||
|
||||
async def test_set_turn_on(hass, air_conditioner):
|
||||
"""Test the a/c is turned on successfully."""
|
||||
air_conditioner.status.update_attribute_value(Attribute.switch, 'off')
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[air_conditioner])
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.state == HVAC_MODE_OFF
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN, SERVICE_TURN_ON,
|
||||
blocking=True)
|
||||
state = hass.states.get('climate.air_conditioner')
|
||||
assert state.state == HVAC_MODE_HEAT_COOL
|
||||
|
||||
|
||||
async def test_entity_and_device_attributes(hass, thermostat):
|
||||
"""Test the attributes of the entries are correct."""
|
||||
await setup_platform(hass, CLIMATE_DOMAIN, devices=[thermostat])
|
||||
|
||||
Reference in New Issue
Block a user