From aae80dca1cb101b420e7e0c0933cdba83abb50fd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 5 Dec 2019 10:40:05 -0800 Subject: [PATCH 01/16] Fix recursion --- homeassistant/requirements.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/homeassistant/requirements.py b/homeassistant/requirements.py index a0eec0f442b..eb8aeeecfae 100644 --- a/homeassistant/requirements.py +++ b/homeassistant/requirements.py @@ -3,7 +3,7 @@ import asyncio from pathlib import Path import logging import os -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Set from homeassistant.exceptions import HomeAssistantError import homeassistant.util.package as pkg_util @@ -28,16 +28,19 @@ class RequirementsNotFound(HomeAssistantError): async def async_get_integration_with_requirements( - hass: HomeAssistant, domain: str + hass: HomeAssistant, domain: str, done: Set[str] = None ) -> Integration: """Get an integration with installed requirements. This can raise IntegrationNotFound if manifest or integration is invalid, RequirementNotFound if there was some type of failure to install requirements. - - Does not handle circular dependencies. """ + if done is None: + done = {domain} + else: + done.add(domain) + integration = await async_get_integration(hass, domain) if hass.config.skip_pip: @@ -48,11 +51,18 @@ async def async_get_integration_with_requirements( hass, integration.domain, integration.requirements ) - deps = integration.dependencies + (integration.after_dependencies or []) + deps_to_check = [ + dep + for dep in integration.dependencies + (integration.after_dependencies or []) + if dep not in done + ] - if deps: + if deps_to_check: await asyncio.gather( - *[async_get_integration_with_requirements(hass, dep) for dep in deps] + *[ + async_get_integration_with_requirements(hass, dep, done) + for dep in deps_to_check + ] ) return integration From c9de5b9fefb02725341d8bfecffd06f82911e46b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 19 Dec 2019 07:53:38 +0100 Subject: [PATCH 02/16] Bumped version to 0.103.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 34e6153b966..01a2d609b87 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 103 -PATCH_VERSION = "1" +PATCH_VERSION = "2" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From 939ca63cbc2f9072628e0f3086e594a544296027 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 20 Dec 2019 10:29:18 +0100 Subject: [PATCH 03/16] =?UTF-8?q?Fix=20update=20port=20and=20api=20key=20o?= =?UTF-8?q?n=20deconz=20discovery=20config=20entry=20u=E2=80=A6=20(#30088)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix update port and api key on discovery config entry update * Remove coroutine from _update_entry --- .../components/deconz/config_flow.py | 18 +++++++++-- tests/components/deconz/test_config_flow.py | 31 ++++++++++++++++--- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index b757f1f4d03..8a34ddcbae0 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -158,12 +158,17 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): title="deCONZ-" + self.deconz_config[CONF_BRIDGEID], data=self.deconz_config ) - async def _update_entry(self, entry, host): + def _update_entry(self, entry, host, port, api_key=None): """Update existing entry.""" if entry.data[CONF_HOST] == host: return self.async_abort(reason="already_configured") entry.data[CONF_HOST] = host + entry.data[CONF_PORT] = port + + if api_key is not None: + entry.data[CONF_API_KEY] = api_key + self.hass.config_entries.async_update_entry(entry) return self.async_abort(reason="updated_instance") @@ -182,7 +187,9 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): for entry in self.hass.config_entries.async_entries(DOMAIN): if uuid == entry.data.get(CONF_UUID): - return await self._update_entry(entry, discovery_info[CONF_HOST]) + return self._update_entry( + entry, discovery_info[CONF_HOST], discovery_info[CONF_PORT] + ) bridgeid = discovery_info[ATTR_SERIAL] if any( @@ -212,7 +219,12 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if bridgeid in gateway_entries: entry = gateway_entries[bridgeid] - return await self._update_entry(entry, user_input[CONF_HOST]) + return self._update_entry( + entry, + user_input[CONF_HOST], + user_input[CONF_PORT], + user_input[CONF_API_KEY], + ) self._hassio_discovery = user_input diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 4045201bd18..51986a3bbc0 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -323,32 +323,53 @@ async def test_hassio_update_instance(hass): """Test we can update an existing config entry.""" entry = MockConfigEntry( domain=config_flow.DOMAIN, - data={config_flow.CONF_BRIDGEID: "id", config_flow.CONF_HOST: "1.2.3.4"}, + data={ + config_flow.CONF_BRIDGEID: "id", + config_flow.CONF_HOST: "1.2.3.4", + config_flow.CONF_PORT: 40850, + config_flow.CONF_API_KEY: "secret", + }, ) entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( config_flow.DOMAIN, - data={config_flow.CONF_HOST: "mock-deconz", config_flow.CONF_SERIAL: "id"}, + data={ + config_flow.CONF_HOST: "mock-deconz", + config_flow.CONF_PORT: 8080, + config_flow.CONF_API_KEY: "updated", + config_flow.CONF_SERIAL: "id", + }, context={"source": "hassio"}, ) assert result["type"] == "abort" assert result["reason"] == "updated_instance" assert entry.data[config_flow.CONF_HOST] == "mock-deconz" + assert entry.data[config_flow.CONF_PORT] == 8080 + assert entry.data[config_flow.CONF_API_KEY] == "updated" async def test_hassio_dont_update_instance(hass): """Test we can update an existing config entry.""" entry = MockConfigEntry( domain=config_flow.DOMAIN, - data={config_flow.CONF_BRIDGEID: "id", config_flow.CONF_HOST: "1.2.3.4"}, + data={ + config_flow.CONF_BRIDGEID: "id", + config_flow.CONF_HOST: "1.2.3.4", + config_flow.CONF_PORT: 8080, + config_flow.CONF_API_KEY: "secret", + }, ) entry.add_to_hass(hass) - result = await hass.config_entries.flow.async_init( config_flow.DOMAIN, - data={config_flow.CONF_HOST: "1.2.3.4", config_flow.CONF_SERIAL: "id"}, + data={ + config_flow.CONF_HOST: "1.2.3.4", + config_flow.CONF_PORT: 8080, + config_flow.CONF_API_KEY: "secret", + config_flow.CONF_SERIAL: "id", + }, context={"source": "hassio"}, ) From e6a27983d32e0743a634397c9408cb576ada3fdd Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Dec 2019 14:16:39 +0000 Subject: [PATCH 04/16] Bump version 0.103.3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 01a2d609b87..6239be3cb61 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 103 -PATCH_VERSION = "2" +PATCH_VERSION = "3" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From e58291015c86bae2677e8a88ae66515b99fb05a8 Mon Sep 17 00:00:00 2001 From: omriasta Date: Wed, 18 Dec 2019 15:04:54 -0500 Subject: [PATCH 05/16] Patch rachio (#30031) * fix binary sensor offline/online fixed self._handle_update on line 66 to produce args, kwargs. Updated the binary sensor to check the correct index in the tuple. * Fixed Standby switch Set standby switch to poll in order to get status when homeassistant starts up. Updated the index for the switch to get the status from the tuple. --- homeassistant/components/rachio/binary_sensor.py | 6 +++--- homeassistant/components/rachio/switch.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/rachio/binary_sensor.py b/homeassistant/components/rachio/binary_sensor.py index f74e3ca1802..f13eba59ac9 100644 --- a/homeassistant/components/rachio/binary_sensor.py +++ b/homeassistant/components/rachio/binary_sensor.py @@ -63,7 +63,7 @@ class RachioControllerBinarySensor(BinarySensorDevice): return # For this device - self._handle_update() + self._handle_update(args, kwargs) @abstractmethod def _poll_update(self, data=None) -> bool: @@ -119,9 +119,9 @@ class RachioControllerOnlineBinarySensor(RachioControllerBinarySensor): def _handle_update(self, *args, **kwargs) -> None: """Handle an update to the state of this sensor.""" - if args[0][KEY_SUBTYPE] == SUBTYPE_ONLINE: + if args[0][0][KEY_SUBTYPE] == SUBTYPE_ONLINE: self._state = True - elif args[0][KEY_SUBTYPE] == SUBTYPE_OFFLINE: + elif args[0][0][KEY_SUBTYPE] == SUBTYPE_OFFLINE: self._state = False self.schedule_update_ha_state() diff --git a/homeassistant/components/rachio/switch.py b/homeassistant/components/rachio/switch.py index 80c227a6df6..a3a4f6bcca1 100644 --- a/homeassistant/components/rachio/switch.py +++ b/homeassistant/components/rachio/switch.py @@ -107,7 +107,7 @@ class RachioStandbySwitch(RachioSwitch): dispatcher_connect( hass, SIGNAL_RACHIO_CONTROLLER_UPDATE, self._handle_any_update ) - super().__init__(controller, poll=False) + super().__init__(controller, poll=True) self._poll_update(controller.init_data) @property @@ -134,9 +134,9 @@ class RachioStandbySwitch(RachioSwitch): def _handle_update(self, *args, **kwargs) -> None: """Update the state using webhook data.""" - if args[0][KEY_SUBTYPE] == SUBTYPE_SLEEP_MODE_ON: + if args[0][0][KEY_SUBTYPE] == SUBTYPE_SLEEP_MODE_ON: self._state = True - elif args[0][KEY_SUBTYPE] == SUBTYPE_SLEEP_MODE_OFF: + elif args[0][0][KEY_SUBTYPE] == SUBTYPE_SLEEP_MODE_OFF: self._state = False self.schedule_update_ha_state() From 20a0557be71722ed9e2a94b055411b713ff95257 Mon Sep 17 00:00:00 2001 From: Wim Haanstra Date: Wed, 18 Dec 2019 21:18:14 +0100 Subject: [PATCH 06/16] Fix failure in transform method (#30023) * Fix failure in transform method * Fix formatting issue --- homeassistant/components/dsmr_reader/definitions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/dsmr_reader/definitions.py b/homeassistant/components/dsmr_reader/definitions.py index f4a36ebc346..45bebfeda92 100644 --- a/homeassistant/components/dsmr_reader/definitions.py +++ b/homeassistant/components/dsmr_reader/definitions.py @@ -3,7 +3,9 @@ def dsmr_transform(value): """Transform DSMR version value to right format.""" - return float(value) / 10 + if value.isdigit(): + return float(value) / 10 + return value def tariff_transform(value): From 488f26d55b93dc56dc484de4953d77546423f1f5 Mon Sep 17 00:00:00 2001 From: Fazli Sapuan Date: Fri, 20 Dec 2019 18:00:21 +0800 Subject: [PATCH 07/16] Fix homekit handling of 0 light brightness and fan speed (#29962) * Fix homekit handling of 0 light brightness and fan speed * Update homekit tests for new initial brightness/speed value --- homeassistant/components/homekit/type_fans.py | 22 +++++++++++++++++-- .../components/homekit/type_lights.py | 21 ++++++++++++++++-- tests/components/homekit/test_type_fans.py | 5 ++++- tests/components/homekit/test_type_lights.py | 4 +++- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/homekit/type_fans.py b/homeassistant/components/homekit/type_fans.py index e3fa6c42c58..e6d128d1e28 100644 --- a/homeassistant/components/homekit/type_fans.py +++ b/homeassistant/components/homekit/type_fans.py @@ -88,8 +88,11 @@ class Fan(HomeAccessory): ) if CHAR_ROTATION_SPEED in chars: + # Initial value is set to 100 because 0 is a special value (off). 100 is + # an arbitrary non-zero value. It is updated immediately by update_state + # to set to the correct initial value. self.char_speed = serv_fan.configure_char( - CHAR_ROTATION_SPEED, value=0, setter_callback=self.set_speed + CHAR_ROTATION_SPEED, value=100, setter_callback=self.set_speed ) if CHAR_SWING_MODE in chars: @@ -156,7 +159,22 @@ class Fan(HomeAccessory): speed = new_state.attributes.get(ATTR_SPEED) hk_speed_value = self.speed_mapping.speed_to_homekit(speed) if hk_speed_value is not None and self.char_speed.value != hk_speed_value: - self.char_speed.set_value(hk_speed_value) + # If the homeassistant component reports its speed as the first entry + # in its speed list but is not off, the hk_speed_value is 0. But 0 + # is a special value in homekit. When you turn on a homekit accessory + # it will try to restore the last rotation speed state which will be + # the last value saved by char_speed.set_value. But if it is set to + # 0, HomeKit will update the rotation speed to 100 as it thinks 0 is + # off. + # + # Therefore, if the hk_speed_value is 0 and the device is still on, + # the rotation speed is mapped to 1 otherwise the update is ignored + # in order to avoid this incorrect behavior. + if hk_speed_value == 0: + if state == STATE_ON: + self.char_speed.set_value(1) + else: + self.char_speed.set_value(hk_speed_value) # Handle Oscillating if self.char_swing is not None: diff --git a/homeassistant/components/homekit/type_lights.py b/homeassistant/components/homekit/type_lights.py index 8e1b07fbbff..7f195b276d6 100644 --- a/homeassistant/components/homekit/type_lights.py +++ b/homeassistant/components/homekit/type_lights.py @@ -82,8 +82,11 @@ class Light(HomeAccessory): ) if CHAR_BRIGHTNESS in self.chars: + # Initial value is set to 100 because 0 is a special value (off). 100 is + # an arbitrary non-zero value. It is updated immediately by update_state + # to set to the correct initial value. self.char_brightness = serv_light.configure_char( - CHAR_BRIGHTNESS, value=0, setter_callback=self.set_brightness + CHAR_BRIGHTNESS, value=100, setter_callback=self.set_brightness ) if CHAR_COLOR_TEMPERATURE in self.chars: min_mireds = self.hass.states.get(self.entity_id).attributes.get( @@ -183,7 +186,21 @@ class Light(HomeAccessory): if not self._flag[CHAR_BRIGHTNESS] and isinstance(brightness, int): brightness = round(brightness / 255 * 100, 0) if self.char_brightness.value != brightness: - self.char_brightness.set_value(brightness) + # The homeassistant component might report its brightness as 0 but is + # not off. But 0 is a special value in homekit. When you turn on a + # homekit accessory it will try to restore the last brightness state + # which will be the last value saved by char_brightness.set_value. + # But if it is set to 0, HomeKit will update the brightness to 100 as + # it thinks 0 is off. + # + # Therefore, if the the brighness is 0 and the device is still on, + # the brightness is mapped to 1 otherwise the update is ignored in + # order to avoid this incorrect behavior. + if brightness == 0: + if state == STATE_ON: + self.char_brightness.set_value(1) + else: + self.char_brightness.set_value(brightness) self._flag[CHAR_BRIGHTNESS] = False # Handle color temperature diff --git a/tests/components/homekit/test_type_fans.py b/tests/components/homekit/test_type_fans.py index 781df6cfb7b..5631791d7a2 100644 --- a/tests/components/homekit/test_type_fans.py +++ b/tests/components/homekit/test_type_fans.py @@ -197,7 +197,10 @@ async def test_fan_speed(hass, hk_driver, cls, events): ) await hass.async_block_till_done() acc = cls.fan(hass, hk_driver, "Fan", entity_id, 2, None) - assert acc.char_speed.value == 0 + + # Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the + # speed to 100 when turning on a fan on a freshly booted up server. + assert acc.char_speed.value != 0 await hass.async_add_job(acc.run) assert ( diff --git a/tests/components/homekit/test_type_lights.py b/tests/components/homekit/test_type_lights.py index 784a6c82346..99678324937 100644 --- a/tests/components/homekit/test_type_lights.py +++ b/tests/components/homekit/test_type_lights.py @@ -101,7 +101,9 @@ async def test_light_brightness(hass, hk_driver, cls, events): await hass.async_block_till_done() acc = cls.light(hass, hk_driver, "Light", entity_id, 2, None) - assert acc.char_brightness.value == 0 + # Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the + # brightness to 100 when turning on a light on a freshly booted up server. + assert acc.char_brightness.value != 0 await hass.async_add_job(acc.run) await hass.async_block_till_done() From 12f1a322a7e5e83bc0c308252eb98b42873ab1c8 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Dec 2019 16:20:10 +0000 Subject: [PATCH 08/16] Fix test --- homeassistant/components/deconz/config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index 8a34ddcbae0..ce890cd0103 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -188,7 +188,7 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): for entry in self.hass.config_entries.async_entries(DOMAIN): if uuid == entry.data.get(CONF_UUID): return self._update_entry( - entry, discovery_info[CONF_HOST], discovery_info[CONF_PORT] + entry, discovery_info[CONF_HOST], entry.data.get[CONF_PORT] ) bridgeid = discovery_info[ATTR_SERIAL] From 5def51835eb98ec11dc19276fcd678f6cd2bd958 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Dec 2019 17:10:04 +0000 Subject: [PATCH 09/16] Fix v2 --- homeassistant/components/deconz/config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index ce890cd0103..c2035d61d6e 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -188,7 +188,7 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): for entry in self.hass.config_entries.async_entries(DOMAIN): if uuid == entry.data.get(CONF_UUID): return self._update_entry( - entry, discovery_info[CONF_HOST], entry.data.get[CONF_PORT] + entry, discovery_info[CONF_HOST], entry.data.get(CONF_PORT) ) bridgeid = discovery_info[ATTR_SERIAL] From f229cf27967a1d27432472a72bdf42425cd63fa2 Mon Sep 17 00:00:00 2001 From: springstan <46536646+springstan@users.noreply.github.com> Date: Fri, 20 Dec 2019 20:29:12 +0100 Subject: [PATCH 10/16] Bump starlingbank to 3.2 (#30098) --- homeassistant/components/starlingbank/manifest.json | 4 +--- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/starlingbank/manifest.json b/homeassistant/components/starlingbank/manifest.json index 33dbb40f78a..d68b6ea125c 100644 --- a/homeassistant/components/starlingbank/manifest.json +++ b/homeassistant/components/starlingbank/manifest.json @@ -2,9 +2,7 @@ "domain": "starlingbank", "name": "Starlingbank", "documentation": "https://www.home-assistant.io/integrations/starlingbank", - "requirements": [ - "starlingbank==3.1" - ], + "requirements": ["starlingbank==3.2"], "dependencies": [], "codeowners": [] } diff --git a/requirements_all.txt b/requirements_all.txt index 9c2fb16fa16..3ed237919ef 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1868,7 +1868,7 @@ sqlalchemy==1.3.11 starline==0.1.3 # homeassistant.components.starlingbank -starlingbank==3.1 +starlingbank==3.2 # homeassistant.components.statsd statsd==3.2.1 From d448ed9f6d9cb515db3b2299d4a3a91bbbef462f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 20 Dec 2019 20:42:13 +0100 Subject: [PATCH 11/16] Bump ring to 0.2.5 (#30103) --- homeassistant/components/ring/manifest.json | 8 ++------ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/ring/manifest.json b/homeassistant/components/ring/manifest.json index 47fc2f3a6a8..5c23822fef9 100644 --- a/homeassistant/components/ring/manifest.json +++ b/homeassistant/components/ring/manifest.json @@ -2,11 +2,7 @@ "domain": "ring", "name": "Ring", "documentation": "https://www.home-assistant.io/integrations/ring", - "requirements": [ - "ring_doorbell==0.2.3" - ], - "dependencies": [ - "ffmpeg" - ], + "requirements": ["ring_doorbell==0.2.5"], + "dependencies": ["ffmpeg"], "codeowners": [] } diff --git a/requirements_all.txt b/requirements_all.txt index 3ed237919ef..e876b9144de 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1730,7 +1730,7 @@ rfk101py==0.0.1 rflink==0.0.46 # homeassistant.components.ring -ring_doorbell==0.2.3 +ring_doorbell==0.2.5 # homeassistant.components.fleetgo ritassist==0.9.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a59a9a57d54..86c3a657988 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -549,7 +549,7 @@ restrictedpython==5.0 rflink==0.0.46 # homeassistant.components.ring -ring_doorbell==0.2.3 +ring_doorbell==0.2.5 # homeassistant.components.yamaha rxv==0.6.0 From 697833bc917bdfc3db56fe0559c61dbf9bb8ad1f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 22 Dec 2019 10:32:42 +0100 Subject: [PATCH 12/16] Remove requirement from entity integration (#30113) --- .../components/doods/image_processing.py | 2 +- homeassistant/components/doods/manifest.json | 14 +++--- .../components/image_processing/__init__.py | 42 ----------------- .../components/image_processing/manifest.json | 8 +--- .../components/seven_segments/manifest.json | 2 +- .../components/tensorflow/image_processing.py | 2 +- .../components/tensorflow/manifest.json | 7 ++- homeassistant/util/pil.py | 47 +++++++++++++++++++ requirements_all.txt | 4 +- requirements_test_all.txt | 5 -- 10 files changed, 67 insertions(+), 66 deletions(-) create mode 100644 homeassistant/util/pil.py diff --git a/homeassistant/components/doods/image_processing.py b/homeassistant/components/doods/image_processing.py index 7f2c3fd1d42..bd631f757d5 100644 --- a/homeassistant/components/doods/image_processing.py +++ b/homeassistant/components/doods/image_processing.py @@ -15,11 +15,11 @@ from homeassistant.components.image_processing import ( CONF_SOURCE, PLATFORM_SCHEMA, ImageProcessingEntity, - draw_box, ) from homeassistant.core import split_entity_id from homeassistant.helpers import template import homeassistant.helpers.config_validation as cv +from homeassistant.util.pil import draw_box _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/doods/manifest.json b/homeassistant/components/doods/manifest.json index e0dcb48527f..551af839b5c 100644 --- a/homeassistant/components/doods/manifest.json +++ b/homeassistant/components/doods/manifest.json @@ -1,10 +1,8 @@ { - "domain": "doods", - "name": "DOODS - Distributed Outside Object Detection Service", - "documentation": "https://www.home-assistant.io/integrations/doods", - "requirements": [ - "pydoods==1.0.2" - ], - "dependencies": [], - "codeowners": [] + "domain": "doods", + "name": "DOODS - Distributed Outside Object Detection Service", + "documentation": "https://www.home-assistant.io/integrations/doods", + "requirements": ["pydoods==1.0.2", "pillow==6.2.1"], + "dependencies": [], + "codeowners": [] } diff --git a/homeassistant/components/image_processing/__init__.py b/homeassistant/components/image_processing/__init__.py index 78ae15eb537..a8f5f0f097e 100644 --- a/homeassistant/components/image_processing/__init__.py +++ b/homeassistant/components/image_processing/__init__.py @@ -2,9 +2,7 @@ import asyncio from datetime import timedelta import logging -from typing import Tuple -from PIL import ImageDraw import voluptuous as vol from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME, CONF_ENTITY_ID, CONF_NAME @@ -65,46 +63,6 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE.extend(PLATFORM_SCHEMA.schema) -def draw_box( - draw: ImageDraw, - box: Tuple[float, float, float, float], - img_width: int, - img_height: int, - text: str = "", - color: Tuple[int, int, int] = (255, 255, 0), -) -> None: - """ - Draw a bounding box on and image. - - The bounding box is defined by the tuple (y_min, x_min, y_max, x_max) - where the coordinates are floats in the range [0.0, 1.0] and - relative to the width and height of the image. - - For example, if an image is 100 x 200 pixels (height x width) and the bounding - box is `(0.1, 0.2, 0.5, 0.9)`, the upper-left and bottom-right coordinates of - the bounding box will be `(40, 10)` to `(180, 50)` (in (x,y) coordinates). - """ - - line_width = 3 - font_height = 8 - y_min, x_min, y_max, x_max = box - (left, right, top, bottom) = ( - x_min * img_width, - x_max * img_width, - y_min * img_height, - y_max * img_height, - ) - draw.line( - [(left, top), (left, bottom), (right, bottom), (right, top), (left, top)], - width=line_width, - fill=color, - ) - if text: - draw.text( - (left + line_width, abs(top - line_width - font_height)), text, fill=color - ) - - async def async_setup(hass, config): """Set up the image processing.""" component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) diff --git a/homeassistant/components/image_processing/manifest.json b/homeassistant/components/image_processing/manifest.json index e986ac6f4ca..1e9f2963a38 100644 --- a/homeassistant/components/image_processing/manifest.json +++ b/homeassistant/components/image_processing/manifest.json @@ -2,11 +2,7 @@ "domain": "image_processing", "name": "Image processing", "documentation": "https://www.home-assistant.io/integrations/image_processing", - "requirements": [ - "pillow==6.2.1" - ], - "dependencies": [ - "camera" - ], + "requirements": [], + "dependencies": ["camera"], "codeowners": [] } diff --git a/homeassistant/components/seven_segments/manifest.json b/homeassistant/components/seven_segments/manifest.json index 0bf49d4b906..6e375da0322 100644 --- a/homeassistant/components/seven_segments/manifest.json +++ b/homeassistant/components/seven_segments/manifest.json @@ -2,7 +2,7 @@ "domain": "seven_segments", "name": "Seven segments", "documentation": "https://www.home-assistant.io/integrations/seven_segments", - "requirements": [], + "requirements": ["pillow==6.2.1"], "dependencies": [], "codeowners": [] } diff --git a/homeassistant/components/tensorflow/image_processing.py b/homeassistant/components/tensorflow/image_processing.py index 5f576f176e8..dee2a021829 100644 --- a/homeassistant/components/tensorflow/image_processing.py +++ b/homeassistant/components/tensorflow/image_processing.py @@ -15,11 +15,11 @@ from homeassistant.components.image_processing import ( CONF_SOURCE, PLATFORM_SCHEMA, ImageProcessingEntity, - draw_box, ) from homeassistant.core import split_entity_id from homeassistant.helpers import template import homeassistant.helpers.config_validation as cv +from homeassistant.util.pil import draw_box _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tensorflow/manifest.json b/homeassistant/components/tensorflow/manifest.json index 278a92a481f..627fa8d6bd6 100644 --- a/homeassistant/components/tensorflow/manifest.json +++ b/homeassistant/components/tensorflow/manifest.json @@ -2,7 +2,12 @@ "domain": "tensorflow", "name": "Tensorflow", "documentation": "https://www.home-assistant.io/integrations/tensorflow", - "requirements": ["tensorflow==1.13.2", "numpy==1.17.4", "protobuf==3.6.1"], + "requirements": [ + "tensorflow==1.13.2", + "numpy==1.17.4", + "protobuf==3.6.1", + "pillow==6.2.1" + ], "dependencies": [], "codeowners": [] } diff --git a/homeassistant/util/pil.py b/homeassistant/util/pil.py new file mode 100644 index 00000000000..80c11c9c410 --- /dev/null +++ b/homeassistant/util/pil.py @@ -0,0 +1,47 @@ +"""PIL utilities. + +Can only be used by integrations that have pillow in their requirements. +""" +from typing import Tuple + +from PIL import ImageDraw + + +def draw_box( + draw: ImageDraw, + box: Tuple[float, float, float, float], + img_width: int, + img_height: int, + text: str = "", + color: Tuple[int, int, int] = (255, 255, 0), +) -> None: + """ + Draw a bounding box on and image. + + The bounding box is defined by the tuple (y_min, x_min, y_max, x_max) + where the coordinates are floats in the range [0.0, 1.0] and + relative to the width and height of the image. + + For example, if an image is 100 x 200 pixels (height x width) and the bounding + box is `(0.1, 0.2, 0.5, 0.9)`, the upper-left and bottom-right coordinates of + the bounding box will be `(40, 10)` to `(180, 50)` (in (x,y) coordinates). + """ + + line_width = 3 + font_height = 8 + y_min, x_min, y_max, x_max = box + (left, right, top, bottom) = ( + x_min * img_width, + x_max * img_width, + y_min * img_height, + y_max * img_height, + ) + draw.line( + [(left, top), (left, bottom), (right, bottom), (right, top), (left, top)], + width=line_width, + fill=color, + ) + if text: + draw.text( + (left + line_width, abs(top - line_width - font_height)), text, fill=color + ) diff --git a/requirements_all.txt b/requirements_all.txt index e876b9144de..9ee9b9a740a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -980,9 +980,11 @@ piglow==1.2.4 # homeassistant.components.pilight pilight==0.1.1 -# homeassistant.components.image_processing +# homeassistant.components.doods # homeassistant.components.proxy # homeassistant.components.qrcode +# homeassistant.components.seven_segments +# homeassistant.components.tensorflow pillow==6.2.1 # homeassistant.components.dominos diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 86c3a657988..9b71d8092be 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -320,11 +320,6 @@ pexpect==4.6.0 # homeassistant.components.pilight pilight==0.1.1 -# homeassistant.components.image_processing -# homeassistant.components.proxy -# homeassistant.components.qrcode -pillow==6.2.1 - # homeassistant.components.plex plexapi==3.3.0 From e74c4c5d998d5e3cdd44c204785d5db7ee4b4386 Mon Sep 17 00:00:00 2001 From: springstan <46536646+springstan@users.noreply.github.com> Date: Sun, 22 Dec 2019 10:23:44 +0100 Subject: [PATCH 13/16] Move imports into setup function in homekit __init__.py (#30137) --- homeassistant/components/homekit/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 525c091e177..91d0719e0e7 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -303,6 +303,7 @@ class HomeKit: def setup(self): """Set up bridge and accessory driver.""" + # pylint: disable=import-outside-toplevel from .accessories import HomeBridge, HomeDriver self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.stop) From e9b19e0465b36c7a41d8e530f0a7b122c2483e0b Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sun, 22 Dec 2019 22:24:18 +0100 Subject: [PATCH 14/16] Fix deconz SSDP updating Hassio discovery (#30153) --- homeassistant/components/deconz/config_flow.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index c2035d61d6e..62b8190ff63 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -160,7 +160,11 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): def _update_entry(self, entry, host, port, api_key=None): """Update existing entry.""" - if entry.data[CONF_HOST] == host: + if ( + entry.data[CONF_HOST] == host + and entry.data[CONF_PORT] == port + and (api_key is None or entry.data[CONF_API_KEY] == api_key) + ): return self.async_abort(reason="already_configured") entry.data[CONF_HOST] = host @@ -187,6 +191,8 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): for entry in self.hass.config_entries.async_entries(DOMAIN): if uuid == entry.data.get(CONF_UUID): + if entry.source == "hassio": + return self.async_abort(reason="already_configured") return self._update_entry( entry, discovery_info[CONF_HOST], entry.data.get(CONF_PORT) ) From 720d63f4961fdb826178e377ab5da4b75f9e0b81 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 22 Dec 2019 22:34:57 +0100 Subject: [PATCH 15/16] Bumped version to 0.103.4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 6239be3cb61..c23ed1cdd03 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 103 -PATCH_VERSION = "3" +PATCH_VERSION = "4" __short_version__ = "{}.{}".format(MAJOR_VERSION, MINOR_VERSION) __version__ = "{}.{}".format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 6, 1) From 31b17e2b95b82a5c507f26641c92b6f4e3569602 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sun, 22 Dec 2019 22:58:22 +0100 Subject: [PATCH 16/16] Allow battery value of 0 as well as make sure to not create a battery tracker if one already exist (#30155) --- homeassistant/components/deconz/sensor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 4c854a0ec11..4ffaba9b499 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -59,7 +59,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): entity_handler.add_entity(new_sensor) entities.append(new_sensor) - if sensor.battery: + if sensor.battery is not None: new_battery = DeconzBattery(sensor, gateway) if new_battery.unique_id not in batteries: batteries.add(new_battery.unique_id) @@ -225,6 +225,9 @@ class DeconzBatteryHandler: @callback def create_tracker(self, sensor): """Create new tracker for battery state.""" + for tracker in self._trackers: + if sensor == tracker.sensor: + return self._trackers.add(DeconzSensorStateTracker(sensor, self.gateway)) @callback