diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index b757f1f4d03..c2035d61d6e 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], entry.data.get(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/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): 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/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() 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/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/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) diff --git a/requirements_all.txt b/requirements_all.txt index 9c2fb16fa16..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 @@ -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 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 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"}, ) 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()