Compare commits

..

55 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
Paulus Schoutsen
a652a4d9e9 Merge pull request #25376 from home-assistant/rc
0.96.3
2019-07-21 12:02:38 -07:00
cgtobi
2189cb0ee7 Add Netatmo climate battery level (#25143)
* Add battery level sensor

* Only update battery level if lower or nonexistent
2019-07-21 12:00:56 -07:00
Paulus Schoutsen
15064e83b4 Bumped version to 0.96.3 2019-07-21 11:10:36 -07:00
David F. Mulcahey
8538e69e28 change and condition to or condition (#25374) 2019-07-21 11:10:26 -07:00
David F. Mulcahey
35c719628d fix remove and re-add scenario (#25370) 2019-07-21 11:10:25 -07:00
Otto Winter
0eab89c8f4 Fix ESPHome climate migration (#25366) 2019-07-21 11:10:25 -07:00
David F. Mulcahey
a3043b9a90 bump quirks version (#25362) 2019-07-21 11:10:24 -07:00
Paulus Schoutsen
c795c93034 Introduce PRESET_NONE for climate (#25360)
* Introduce PRESET_NONE for climate

* Require preset mode to be a string

* Lint

* Fix tests
2019-07-21 11:10:24 -07:00
David Bonnes
2228a0dcac Improve geniushub logging and bump client (#25359)
* add debug logging

* bump geniushub client library

* delint

* bump again

* bump again, again
2019-07-21 11:10:23 -07:00
cgtobi
68e7f4ca5a Fix preset service call (#25358) 2019-07-21 11:09:59 -07:00
David F. Mulcahey
e052bcb03b add available to device info (#25349) 2019-07-21 11:08:21 -07:00
Michael Scherer
a56b604936 Fix for hvac_modes list being null (#25347)
* Fix for empty hvac_modes list

* Empty list instead of default value for hvac_modes
2019-07-21 11:08:21 -07:00
Fredrik Erlandsson
f6b6818fb0 Restore Daikin A/C on/off services (#25332) 2019-07-21 11:08:20 -07:00
eyager1
93a65bf507 Update zwave climate mappings (#25327)
hvac_action should be idle when thermostat is in Pending Heat or Pending Cool.
2019-07-21 11:08:19 -07:00
Paulus Schoutsen
ec302912a3 Restore sensiobo turn on/off methods (#25321) 2019-07-21 11:08:19 -07:00
Paulus Schoutsen
615af773e5 Updated frontend to 20190721.0 2019-07-21 09:56:12 -07:00
Paulus Schoutsen
9c51650ea3 Updated frontend to 20190720.0 2019-07-20 18:01:47 -07:00
Matthias Alphart
01430262cd temporary patch to fix KNX climate devices (#25356)
This is a temporary patch for knx climate devices. It should be reverted when #24738 is merged to release. 
It should fix https://github.com/home-assistant/home-assistant/issues/25247 for 0.96
2019-07-20 17:57:38 -07:00
Paulus Schoutsen
a6e3cc6617 Merge pull request #25313 from home-assistant/rc
0.96.2
2019-07-19 11:42:01 -07:00
Andrew Sayre
b4481269ec Turn on device before setting mode (#25314) 2019-07-19 10:19:51 -07:00
Paulus Schoutsen
662c33af85 Bumped version to 0.96.2 2019-07-19 09:44:53 -07:00
cgtobi
fc384ca6d5 Fix plant error when adding new value (#25302)
* Only add value if int or floar

* Simplify check

* Simplify check
2019-07-19 09:44:44 -07:00
Pascal Vizeli
49e2583b08 Fix HM with use wrong datapoint for off (#25298) 2019-07-19 09:44:43 -07:00
David Bonnes
8629b86186 [climate] Correct honeywell supported_features (#25292)
* Initial commit

* delint
2019-07-19 09:44:43 -07:00
William Scanlon
68c4e5c0c9 Fixed python-wink method names (#25285)
* Fixed python-wink method names

* Fixed aux heat
2019-07-19 09:44:42 -07:00
cgtobi
c4d1cd0e03 Fix fritzbox climate HVAC mode / temperature (#25275)
* Set the target temperature

* Update tests

* Update tests

* Fix linter complaints
2019-07-19 09:44:41 -07:00
Paulus Schoutsen
8b020ea5e6 Updated frontend to 20190719.0 2019-07-19 09:43:24 -07:00
Paulus Schoutsen
33cba4da85 Merge pull request #25280 from home-assistant/rc
0.96.1
2019-07-18 15:22:49 -07:00
Pascal Vizeli
3db106c562 Update azure-pipelines-ci.yml for Azure Pipelines 2019-07-18 23:20:56 +02:00
Paulus Schoutsen
cc595632bd Bumped version to 0.96.1 2019-07-18 14:08:50 -07:00
stboch
f76700567e Added states and modes for zwave climate (#25274)
* Update climate.py

Added support for Fan Only State
Added additional missing modes and states this should correct issue #25216

* Correct line lint error

* Corrected mode spelling

* Lint
2019-07-18 14:08:43 -07:00
Paulus Schoutsen
86cf02739b Add hvac modes back to opentherm (#25268) 2019-07-18 14:08:42 -07:00
Andrew Sayre
46cdbd273a Restore SmartThings A/C on/off services (#25259)
* Restore ST A/C on/off services

* Use correct OFF const

* Support AC HVAC_MODE_OFF
2019-07-18 14:08:42 -07:00
William Sutton
ec3cb11e2f Update CT80 Humidity call (#25258)
Last PR was from a few versions before, not sure how I had it working, but functioning properly now on .96
2019-07-18 14:08:41 -07:00
geekofweek
2016cf872e ecobee Preset Fix (#25256)
* ecobee Preset Fix

* Celsius Fix

* Checks Fix

* Check Fix #2

* Check Fix #3
2019-07-18 14:08:40 -07:00
cgtobi
37810e010a Fix the unit of measurement for ecobee climate (#25246)
* Fix the unit of measurement

* Remove unused const
2019-07-18 14:08:40 -07:00
cgtobi
2b69904b94 Make presets prettier (#25245) 2019-07-18 14:08:39 -07:00
Pascal Vizeli
59cf6a0c79 Fix eq3btsmart (#25238) 2019-07-18 14:08:39 -07:00
Pascal Vizeli
39b249d202 Show off value (#25236) 2019-07-18 14:08:38 -07:00
Paulus Schoutsen
d57cf01cf2 Updated frontend to 20190718.0 2019-07-18 14:08:06 -07:00
53 changed files with 641 additions and 269 deletions

View File

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

View File

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

View File

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

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

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

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

View File

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

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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'

View File

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

View File

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