From dc133fe9f279a855df42a90d4a3b720104f181ea Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Thu, 21 Feb 2019 08:20:58 -0500 Subject: [PATCH 01/17] Fix ZHA bugs (#21246) * fix bugs * add comment * allow entities to be marked unavailable --- .../components/zha/core/channels/general.py | 4 +++- homeassistant/components/zha/core/device.py | 10 +++++----- homeassistant/components/zha/core/gateway.py | 13 +++++++++++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index a29b23d340b..621b0ccbee1 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -126,9 +126,11 @@ class LevelControlChannel(ZigbeeChannel): class BasicChannel(ZigbeeChannel): """Channel to interact with the basic cluster.""" + UNKNOWN = 0 BATTERY = 3 + POWER_SOURCES = { - 0: 'Unknown', + UNKNOWN: 'Unknown', 1: 'Mains (single phase)', 2: 'Mains (3 phase)', BATTERY: 'Battery', diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 12bb397fbc3..1ee800d8559 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -146,11 +146,11 @@ class ZHADevice: self._available_signal, False ) - async_dispatcher_send( - self.hass, - "{}_{}".format(self._available_signal, 'entity'), - True - ) + async_dispatcher_send( + self.hass, + "{}_{}".format(self._available_signal, 'entity'), + available + ) self._available = available @property diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index a50bfeae1be..cb5e5bf7774 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -36,6 +36,7 @@ _LOGGER = logging.getLogger(__name__) SENSOR_TYPES = {} BINARY_SENSOR_TYPES = {} +SMARTTHINGS_HUMIDITY_CLUSTER = 64581 EntityReference = collections.namedtuple( 'EntityReference', 'reference_id zha_device cluster_channels device_info') @@ -174,7 +175,8 @@ class ZHAGateway: # available and we already loaded fresh state above zha_device.update_available(True) elif not zha_device.available and zha_device.power_source is not None\ - and zha_device.power_source != BasicChannel.BATTERY: + and zha_device.power_source != BasicChannel.BATTERY\ + and zha_device.power_source != BasicChannel.UNKNOWN: # the device is currently marked unavailable and it isn't a battery # powered device so we should be able to update it now _LOGGER.debug( @@ -380,7 +382,10 @@ async def _handle_single_cluster_match(hass, zha_device, cluster, device_key, """Dispatch a single cluster match to a HA component.""" component = None # sub_component = None for cluster_type, candidate_component in device_classes.items(): - if isinstance(cluster, cluster_type): + if isinstance(cluster_type, int): + if cluster.cluster_id == cluster_type: + component = candidate_component + elif isinstance(cluster, cluster_type): component = candidate_component break @@ -473,6 +478,9 @@ def establish_device_mappings(): SINGLE_INPUT_CLUSTER_DEVICE_CLASS.update({ zcl.clusters.general.OnOff: 'switch', zcl.clusters.measurement.RelativeHumidity: 'sensor', + # this works for now but if we hit conflicts we can break it out to + # a different dict that is keyed by manufacturer + SMARTTHINGS_HUMIDITY_CLUSTER: 'sensor', zcl.clusters.measurement.TemperatureMeasurement: 'sensor', zcl.clusters.measurement.PressureMeasurement: 'sensor', zcl.clusters.measurement.IlluminanceMeasurement: 'sensor', @@ -489,6 +497,7 @@ def establish_device_mappings(): SENSOR_TYPES.update({ zcl.clusters.measurement.RelativeHumidity.cluster_id: HUMIDITY, + SMARTTHINGS_HUMIDITY_CLUSTER: HUMIDITY, zcl.clusters.measurement.TemperatureMeasurement.cluster_id: TEMPERATURE, zcl.clusters.measurement.PressureMeasurement.cluster_id: PRESSURE, From b2482d820524e96f5b8d2b4fcee0716519664f41 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 21 Feb 2019 17:26:09 +0100 Subject: [PATCH 02/17] Add missing configuration option to plex (#21264) Fixes part of: https://github.com/home-assistant/home-assistant/issues/21254 --- homeassistant/components/media_player/plex.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index c67849edee9..35000fa35c3 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -47,6 +47,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_INCLUDE_NON_CLIENTS, default=False): cv.boolean, vol.Optional(CONF_USE_EPISODE_ART, default=False): cv.boolean, vol.Optional(CONF_USE_CUSTOM_ENTITY_IDS, default=False): cv.boolean, + vol.Optional(CONF_SHOW_ALL_CONTROLS, default=False): cv.boolean, vol.Optional(CONF_REMOVE_UNAVAILABLE_CLIENTS, default=True): cv.boolean, vol.Optional(CONF_CLIENT_REMOVE_INTERVAL, default=timedelta(seconds=600)): vol.All(cv.time_period, cv.positive_timedelta), From e59776377b6b3ae50bcf3ff014178a4de085c9f2 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 21 Feb 2019 17:26:26 +0100 Subject: [PATCH 03/17] Add missing configation option (#21265) Fixes https://github.com/home-assistant/home-assistant/issues/21254 --- homeassistant/components/sensor/imap_email_content.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/sensor/imap_email_content.py b/homeassistant/components/sensor/imap_email_content.py index 225ad08f7d1..714a6c38781 100644 --- a/homeassistant/components/sensor/imap_email_content.py +++ b/homeassistant/components/sensor/imap_email_content.py @@ -35,6 +35,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_SERVER): cv.string, + vol.Required(CONF_SENDERS): [cv.string], vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_FOLDER, default='INBOX'): cv.string, From e2a57add2c4c7c7ec32ce1f214317390ae28763c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Feb 2019 08:52:45 -0800 Subject: [PATCH 04/17] Add SamsungTV Mac validation (#21268) --- homeassistant/components/media_player/samsungtv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/media_player/samsungtv.py b/homeassistant/components/media_player/samsungtv.py index db6bd317c40..e6715669da7 100644 --- a/homeassistant/components/media_player/samsungtv.py +++ b/homeassistant/components/media_player/samsungtv.py @@ -42,6 +42,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_MAC): cv.string, vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, }) From e3d29ad26a71f11d21713cd8de10a90bae674ea3 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Thu, 21 Feb 2019 14:39:55 -0500 Subject: [PATCH 05/17] update services.yaml (#21276) --- homeassistant/components/zha/services.yaml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/zha/services.yaml b/homeassistant/components/zha/services.yaml index c328d69a6c3..0d7fe06fe25 100644 --- a/homeassistant/components/zha/services.yaml +++ b/homeassistant/components/zha/services.yaml @@ -29,9 +29,12 @@ set_zigbee_cluster_attribute: description: >- Set attribute value for the specified cluster on the specified entity. fields: - entity_id: - description: Entity id - example: "binary_sensor.centralite_3130_00e8fb4e_1" + ieee: + description: IEEE address for the device + example: "00:0d:6f:00:05:7d:2d:34" + endpoint_id: + description: Endpoint id for the cluster + example: 1 cluster_id: description: ZCL cluster to retrieve attributes for example: 6 @@ -52,9 +55,12 @@ issue_zigbee_cluster_command: description: >- Issue command on the specified cluster on the specified entity. fields: - entity_id: - description: Entity id - example: "binary_sensor.centralite_3130_00e8fb4e_1" + ieee: + description: IEEE address for the device + example: "00:0d:6f:00:05:7d:2d:34" + endpoint_id: + description: Endpoint id for the cluster + example: 1 cluster_id: description: ZCL cluster to retrieve attributes for example: 6 From df5b17d139e74e9439116c7cf5847cdace84e59e Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 21 Feb 2019 13:27:34 -0700 Subject: [PATCH 06/17] Fix unhandled exception in Ambient PWS config entry (#21278) --- homeassistant/components/ambient_station/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index 4464992e5fa..16b86a0e298 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -263,7 +263,8 @@ async def async_setup_entry(hass, config_entry): Client( config_entry.data[CONF_API_KEY], config_entry.data[CONF_APP_KEY], session), - hass.data[DOMAIN][DATA_CONFIG].get(CONF_MONITORED_CONDITIONS, [])) + hass.data[DOMAIN].get(DATA_CONFIG, {}).get( + CONF_MONITORED_CONDITIONS, [])) hass.loop.create_task(ambient.ws_connect()) hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = ambient except WebsocketError as err: From 07e04e5b84dbff145c8029d70a32e8be14101af3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Feb 2019 16:39:17 -0800 Subject: [PATCH 07/17] Fix yeelight config validation (#21295) --- homeassistant/components/light/yeelight.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/yeelight.py b/homeassistant/components/light/yeelight.py index b678fcd2799..231821ffc13 100644 --- a/homeassistant/components/light/yeelight.py +++ b/homeassistant/components/light/yeelight.py @@ -77,15 +77,16 @@ DEVICE_SCHEMA = vol.Schema({ vol.Optional(CONF_MODE_MUSIC, default=False): cv.boolean, vol.Optional(CONF_SAVE_ON_CHANGE, default=False): cv.boolean, vol.Optional(CONF_MODEL): cv.string, +}) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}, vol.Optional(CONF_CUSTOM_EFFECTS): [{ vol.Required(CONF_NAME): cv.string, vol.Required(CONF_FLOW_PARAMS): YEELIGHT_FLOW_TRANSITION_SCHEMA }] }) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - {vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}, }) - SUPPORT_YEELIGHT = (SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_FLASH) From e4a21011a393524fc647a23b1971846798858bae Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Feb 2019 17:06:30 -0800 Subject: [PATCH 08/17] Bumped version to 0.88.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 9dbb06e8adf..28c76366312 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 88 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From fbfa3d1a181648a70ec9677e183675e040846f29 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 18 Feb 2019 13:04:04 -0800 Subject: [PATCH 09/17] Make sure that device trackers is always a list during creation (#21193) --- homeassistant/components/person/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index 6fb7d42e0ee..3e0b00f7442 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -176,7 +176,7 @@ class PersonManager: CONF_ID: uuid.uuid4().hex, CONF_NAME: name, CONF_USER_ID: user_id, - CONF_DEVICE_TRACKERS: device_trackers, + CONF_DEVICE_TRACKERS: device_trackers or [], } self.storage_data[person[CONF_ID]] = person self._async_schedule_save() From f6811a85b617a7ffd24f29efa20e2acf308e5c13 Mon Sep 17 00:00:00 2001 From: David Conley Date: Fri, 22 Feb 2019 18:13:40 -0500 Subject: [PATCH 10/17] Allow custom_effect to be absent from Flux configuration (#21317) * Allow custom_effect to be absent from Flux configuration * set custom effect to none during setup --- homeassistant/components/light/flux_led.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/flux_led.py b/homeassistant/components/light/flux_led.py index 5ecf3f55e10..bfbb98ad57e 100644 --- a/homeassistant/components/light/flux_led.py +++ b/homeassistant/components/light/flux_led.py @@ -151,6 +151,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): device['name'] = '{} {}'.format(device['id'], ipaddr) device[ATTR_MODE] = MODE_RGBW device[CONF_PROTOCOL] = None + device[CONF_CUSTOM_EFFECT] = None light = FluxLight(device) lights.append(light) From 0f4c2ccd1ee82c695626220517484a30f58d65e6 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Sat, 23 Feb 2019 22:38:21 +0100 Subject: [PATCH 11/17] Fix person update on create (#21355) * Update tests to set correct hass running state * Update person on adding person if hass is running * Test creating person when hass is running --- homeassistant/components/person/__init__.py | 14 ++++++++++---- tests/components/person/test_init.py | 20 +++++++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index 3e0b00f7442..f2bca91205c 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -337,12 +337,18 @@ class Person(RestoreEntity): if state: self._parse_source_state(state) - @callback - def person_start_hass(now): + if self.hass.is_running: + # Update person now if hass is already running. self.person_updated() + else: + # Wait for hass start to not have race between person + # and device trackers finishing setup. + @callback + def person_start_hass(now): + self.person_updated() - self.hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_START, person_start_hass) + self.hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_START, person_start_hass) @callback def person_updated(self): diff --git a/tests/components/person/test_init.py b/tests/components/person/test_init.py index f2d796fb204..6c8c6ebd0dd 100644 --- a/tests/components/person/test_init.py +++ b/tests/components/person/test_init.py @@ -101,6 +101,7 @@ async def test_valid_invalid_user_ids(hass, hass_admin_user): async def test_setup_tracker(hass, hass_admin_user): """Test set up person with one device tracker.""" + hass.state = CoreState.not_running user_id = hass_admin_user.id config = {DOMAIN: { 'id': '1234', 'name': 'tracked person', 'user_id': user_id, @@ -148,6 +149,7 @@ async def test_setup_tracker(hass, hass_admin_user): async def test_setup_two_trackers(hass, hass_admin_user): """Test set up person with two device trackers.""" + hass.state = CoreState.not_running user_id = hass_admin_user.id config = {DOMAIN: { 'id': '1234', 'name': 'tracked person', 'user_id': user_id, @@ -191,6 +193,7 @@ async def test_setup_two_trackers(hass, hass_admin_user): async def test_ignore_unavailable_states(hass, hass_admin_user): """Test set up person with two device trackers, one unavailable.""" + hass.state = CoreState.not_running user_id = hass_admin_user.id config = {DOMAIN: { 'id': '1234', 'name': 'tracked person', 'user_id': user_id, @@ -234,7 +237,7 @@ async def test_restore_home_state(hass, hass_admin_user): ATTR_SOURCE: DEVICE_TRACKER, ATTR_USER_ID: user_id} state = State('person.tracked_person', 'home', attrs) mock_restore_cache(hass, (state, )) - hass.state = CoreState.starting + hass.state = CoreState.not_running mock_component(hass, 'recorder') config = {DOMAIN: { 'id': '1234', 'name': 'tracked person', 'user_id': user_id, @@ -263,6 +266,21 @@ async def test_duplicate_ids(hass, hass_admin_user): assert hass.states.get('person.test_user_2') is None +async def test_create_person_during_run(hass): + """Test that person is updated if created while hass is running.""" + config = {DOMAIN: {}} + assert await async_setup_component(hass, DOMAIN, config) + hass.states.async_set(DEVICE_TRACKER, 'home') + await hass.async_block_till_done() + + await hass.components.person.async_create_person( + 'tracked person', device_trackers=[DEVICE_TRACKER]) + await hass.async_block_till_done() + + state = hass.states.get('person.tracked_person') + assert state.state == 'home' + + async def test_load_person_storage(hass, hass_admin_user, storage_setup): """Test set up person from storage.""" state = hass.states.get('person.tracked_person') From d1349d4919c3e088d3eb8281668f066385b102f9 Mon Sep 17 00:00:00 2001 From: yosilevy <37745463+yosilevy@users.noreply.github.com> Date: Sat, 23 Feb 2019 19:26:27 +0200 Subject: [PATCH 12/17] Scene validator fix (#21362) --- homeassistant/components/scene/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index 802512dbf5d..8a7934bd694 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -32,7 +32,12 @@ def _platform_validator(config): 'homeassistant.components.scene.{}'.format( config[CONF_PLATFORM])) except ImportError: - raise vol.Invalid('Invalid platform specified') from None + try: + platform = importlib.import_module( + 'homeassistant.components.{}.scene'.format( + config[CONF_PLATFORM])) + except ImportError: + raise vol.Invalid('Invalid platform specified') from None if not hasattr(platform, 'PLATFORM_SCHEMA'): return config From 2c1032794592a632db4c13330cfd46cdc8674d8a Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Sun, 24 Feb 2019 10:45:11 -0600 Subject: [PATCH 13/17] Improve tolerance of SmartThings Climate platform (#21383) * Improve resilience of platform with buggy handlers * Eliminate possibility of None in operation list * Refactor variable name --- .../components/smartthings/climate.py | 42 ++++++++++++++--- tests/components/smartthings/test_climate.py | 45 ++++++++++++++++++- 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/smartthings/climate.py b/homeassistant/components/smartthings/climate.py index ab7334f1316..6c7cc5d4c74 100644 --- a/homeassistant/components/smartthings/climate.py +++ b/homeassistant/components/smartthings/climate.py @@ -1,6 +1,7 @@ """Support for climate devices through the SmartThings cloud API.""" import asyncio -from typing import Optional, Sequence +import logging +from typing import Iterable, Optional, Sequence from homeassistant.components.climate import ( ATTR_OPERATION_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, @@ -38,6 +39,8 @@ UNIT_MAP = { 'F': TEMP_FAHRENHEIT } +_LOGGER = logging.getLogger(__name__) + async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): @@ -50,7 +53,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] async_add_entities( [SmartThingsThermostat(device) for device in broker.devices.values() - if broker.any_assigned(device.device_id, 'climate')]) + if broker.any_assigned(device.device_id, CLIMATE_DOMAIN)], True) def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]: @@ -90,6 +93,8 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice): """Init the class.""" super().__init__(device) self._supported_features = self._determine_features() + self._current_operation = None + self._operations = None def _determine_features(self): from pysmartthings import Capability @@ -127,6 +132,7 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice): if operation_state: mode = STATE_TO_MODE[operation_state] await self._device.set_thermostat_mode(mode, set_status=True) + await self.async_update() # Heat/cool setpoint heating_setpoint = None @@ -151,6 +157,33 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice): # the entity state ahead of receiving the confirming push updates self.async_schedule_update_ha_state(True) + async def async_update(self): + """Update the attributes of the climate device.""" + thermostat_mode = self._device.status.thermostat_mode + self._current_operation = MODE_TO_STATE.get(thermostat_mode) + if self._current_operation is None: + _LOGGER.debug('Device %s (%s) returned an invalid' + 'thermostat mode: %s', self._device.label, + self._device.device_id, thermostat_mode) + + supported_modes = self._device.status.supported_thermostat_modes + if isinstance(supported_modes, Iterable): + operations = set() + for mode in supported_modes: + state = MODE_TO_STATE.get(mode) + if state is not None: + operations.add(state) + else: + _LOGGER.debug('Device %s (%s) returned an invalid ' + 'supported thermostat mode: %s', + self._device.label, self._device.device_id, + mode) + self._operations = operations + else: + _LOGGER.debug('Device %s (%s) returned invalid supported ' + 'thermostat modes: %s', self._device.label, + self._device.device_id, supported_modes) + @property def current_fan_mode(self): """Return the fan setting.""" @@ -164,7 +197,7 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice): @property def current_operation(self): """Return current operation ie. heat, cool, idle.""" - return MODE_TO_STATE[self._device.status.thermostat_mode] + return self._current_operation @property def current_temperature(self): @@ -187,8 +220,7 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice): @property def operation_list(self): """Return the list of available operation modes.""" - return {MODE_TO_STATE[mode] for mode in - self._device.status.supported_thermostat_modes} + return self._operations @property def supported_features(self): diff --git a/tests/components/smartthings/test_climate.py b/tests/components/smartthings/test_climate.py index c5646fb400f..432c4d4b7cb 100644 --- a/tests/components/smartthings/test_climate.py +++ b/tests/components/smartthings/test_climate.py @@ -19,7 +19,8 @@ from homeassistant.components.climate import ( 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) + ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, STATE_OFF, + STATE_UNKNOWN) from .conftest import setup_platform @@ -95,6 +96,25 @@ def thermostat_fixture(device_factory): return device +@pytest.fixture(name="buggy_thermostat") +def buggy_thermostat_fixture(device_factory): + """Fixture returns a buggy thermostat.""" + device = device_factory( + "Buggy Thermostat", + capabilities=[ + Capability.temperature_measurement, + Capability.thermostat_cooling_setpoint, + Capability.thermostat_heating_setpoint, + Capability.thermostat_mode], + status={ + Attribute.thermostat_mode: 'heating', + Attribute.cooling_setpoint: 74, + Attribute.heating_setpoint: 68} + ) + device.status.attributes[Attribute.temperature] = Status(70, 'F', None) + return device + + async def test_async_setup_platform(): """Test setup platform does nothing (it uses config entries).""" await climate.async_setup_platform(None, None, None) @@ -152,6 +172,29 @@ async def test_thermostat_entity_state(hass, thermostat): assert state.attributes[ATTR_CURRENT_HUMIDITY] == 34 +async def test_buggy_thermostat_entity_state(hass, buggy_thermostat): + """Tests the state attributes properly match the thermostat type.""" + await setup_platform(hass, CLIMATE_DOMAIN, buggy_thermostat) + state = hass.states.get('climate.buggy_thermostat') + assert state.state == STATE_UNKNOWN + assert state.attributes[ATTR_SUPPORTED_FEATURES] == \ + SUPPORT_OPERATION_MODE | SUPPORT_TARGET_TEMPERATURE_HIGH | \ + SUPPORT_TARGET_TEMPERATURE_LOW | SUPPORT_TARGET_TEMPERATURE + assert ATTR_OPERATION_LIST not in state.attributes + assert state.attributes[ATTR_TEMPERATURE] is None + assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 21.1 # celsius + + +async def test_buggy_thermostat_invalid_mode(hass, buggy_thermostat): + """Tests when an invalid operation mode is included.""" + buggy_thermostat.status.update_attribute_value( + Attribute.supported_thermostat_modes, + ['heat', 'emergency heat', 'other']) + await setup_platform(hass, CLIMATE_DOMAIN, buggy_thermostat) + state = hass.states.get('climate.buggy_thermostat') + assert state.attributes[ATTR_OPERATION_LIST] == {'heat'} + + async def test_set_fan_mode(hass, thermostat): """Test the fan mode is set successfully.""" await setup_platform(hass, CLIMATE_DOMAIN, thermostat) From 51eb6c7b4ec97e59e9dc9eea179b39a27b828554 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Tue, 26 Feb 2019 21:37:21 +0100 Subject: [PATCH 14/17] Bump PyXiaomiGateway version to 0.11.2 (#21453) --- homeassistant/components/xiaomi_aqara/__init__.py | 2 +- homeassistant/components/xiaomi_aqara/light.py | 2 ++ requirements_all.txt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/xiaomi_aqara/__init__.py b/homeassistant/components/xiaomi_aqara/__init__.py index ce943fb2c93..5e47adc47f9 100644 --- a/homeassistant/components/xiaomi_aqara/__init__.py +++ b/homeassistant/components/xiaomi_aqara/__init__.py @@ -16,7 +16,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util.dt import utcnow -REQUIREMENTS = ['PyXiaomiGateway==0.11.1'] +REQUIREMENTS = ['PyXiaomiGateway==0.11.2'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_aqara/light.py b/homeassistant/components/xiaomi_aqara/light.py index 8ad0f2522d2..30433ccea3d 100644 --- a/homeassistant/components/xiaomi_aqara/light.py +++ b/homeassistant/components/xiaomi_aqara/light.py @@ -99,8 +99,10 @@ class XiaomiGatewayLight(XiaomiDevice, Light): if self._write_to_hub(self._sid, **{self._data_key: rgbhex}): self._state = True + self.schedule_update_ha_state() def turn_off(self, **kwargs): """Turn the light off.""" if self._write_to_hub(self._sid, **{self._data_key: 0}): self._state = False + self.schedule_update_ha_state() diff --git a/requirements_all.txt b/requirements_all.txt index ac85efe3be7..629dca5cccf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -63,7 +63,7 @@ PySwitchbot==0.5 PyTransportNSW==0.1.1 # homeassistant.components.xiaomi_aqara -PyXiaomiGateway==0.11.1 +PyXiaomiGateway==0.11.2 # homeassistant.components.rpi_gpio # RPi.GPIO==0.6.5 From 626f8a8b09666507f049e21d66e2f026d432f8ec Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Feb 2019 15:25:21 -0800 Subject: [PATCH 15/17] Bumped version to 0.88.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 28c76366312..e2329a2de26 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 88 -PATCH_VERSION = '1' +PATCH_VERSION = '2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 651b0d3506555f11e5d2d54694f17e828e6e1ff3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Feb 2019 15:27:55 -0800 Subject: [PATCH 16/17] Fix cherry-pick fail ST --- homeassistant/components/smartthings/climate.py | 2 +- tests/components/smartthings/test_climate.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/smartthings/climate.py b/homeassistant/components/smartthings/climate.py index 6c7cc5d4c74..f784ed101a7 100644 --- a/homeassistant/components/smartthings/climate.py +++ b/homeassistant/components/smartthings/climate.py @@ -8,7 +8,7 @@ from homeassistant.components.climate import ( STATE_AUTO, STATE_COOL, STATE_ECO, STATE_HEAT, SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW, - ClimateDevice) + ClimateDevice, DOMAIN as CLIMATE_DOMAIN) from homeassistant.const import ( ATTR_TEMPERATURE, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT) diff --git a/tests/components/smartthings/test_climate.py b/tests/components/smartthings/test_climate.py index 432c4d4b7cb..306bcacdb18 100644 --- a/tests/components/smartthings/test_climate.py +++ b/tests/components/smartthings/test_climate.py @@ -19,8 +19,7 @@ from homeassistant.components.climate import ( 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, STATE_UNKNOWN) from .conftest import setup_platform From 14f912500e6e1d1b065c29a7896eb0b4b4725461 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 26 Feb 2019 12:33:40 -0800 Subject: [PATCH 17/17] Pin isort (#21463) --- homeassistant/package_constraints.txt | 4 ++++ script/gen_requirements_all.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d0a192a9dc7..838c3f31bc5 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -26,3 +26,7 @@ pycrypto==1000000000.0.0 # Contains code to modify Home Assistant to work around our rules python-systemair-savecair==1000000000.0.0 + +# Newer version causes pylint to take forever +# https://github.com/timothycrosley/isort/issues/848 +isort==4.3.4 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 47028ef3530..926aadfc3a5 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -157,6 +157,10 @@ pycrypto==1000000000.0.0 # Contains code to modify Home Assistant to work around our rules python-systemair-savecair==1000000000.0.0 + +# Newer version causes pylint to take forever +# https://github.com/timothycrosley/isort/issues/848 +isort==4.3.4 """