From e979b5441389ad5510e9097afeb314c4d19d7ac2 Mon Sep 17 00:00:00 2001 From: Emilv2 Date: Wed, 20 May 2020 14:53:01 +0200 Subject: [PATCH 01/11] Fix Delijn sensor naming (#35789) --- homeassistant/components/delijn/manifest.json | 2 +- homeassistant/components/delijn/sensor.py | 48 +++++++++++-------- requirements_all.txt | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/delijn/manifest.json b/homeassistant/components/delijn/manifest.json index 3f6efd0a3d7..8727dd25139 100644 --- a/homeassistant/components/delijn/manifest.json +++ b/homeassistant/components/delijn/manifest.json @@ -3,5 +3,5 @@ "name": "De Lijn", "documentation": "https://www.home-assistant.io/integrations/delijn", "codeowners": ["@bollewolle"], - "requirements": ["pydelijn==0.5.1"] + "requirements": ["pydelijn==0.6.0"] } diff --git a/homeassistant/components/delijn/sensor.py b/homeassistant/components/delijn/sensor.py index 538e071e194..2c7eec1691c 100644 --- a/homeassistant/components/delijn/sensor.py +++ b/homeassistant/components/delijn/sensor.py @@ -2,6 +2,7 @@ import logging from pydelijn.api import Passages +from pydelijn.common import HttpException import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA @@ -37,22 +38,23 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Create the sensor.""" api_key = config[CONF_API_KEY] - name = DEFAULT_NAME session = async_get_clientsession(hass) sensors = [] for nextpassage in config[CONF_NEXT_DEPARTURE]: - stop_id = nextpassage[CONF_STOP_ID] - number_of_departures = nextpassage[CONF_NUMBER_OF_DEPARTURES] - line = Passages( - hass.loop, stop_id, number_of_departures, api_key, session, True + sensors.append( + DeLijnPublicTransportSensor( + Passages( + hass.loop, + nextpassage[CONF_STOP_ID], + nextpassage[CONF_NUMBER_OF_DEPARTURES], + api_key, + session, + True, + ) + ) ) - await line.get_passages() - if line.passages is None: - _LOGGER.warning("No data received from De Lijn") - return - sensors.append(DeLijnPublicTransportSensor(line, name)) async_add_entities(sensors, True) @@ -60,20 +62,28 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= class DeLijnPublicTransportSensor(Entity): """Representation of a Ruter sensor.""" - def __init__(self, line, name): + def __init__(self, line): """Initialize the sensor.""" self.line = line self._attributes = {ATTR_ATTRIBUTION: ATTRIBUTION} - self._name = name + self._name = None self._state = None - self._available = False + self._available = True async def async_update(self): """Get the latest data from the De Lijn API.""" - await self.line.get_passages() - if self.line.passages is None: - _LOGGER.warning("No data received from De Lijn") + try: + await self.line.get_passages() + self._name = await self.line.get_stopname() + except HttpException: + self._available = False + _LOGGER.error("De Lijn http error") return + + self._attributes["stopname"] = self._name + for passage in self.line.passages: + passage["stopname"] = self._name + try: first = self.line.passages[0] if first["due_at_realtime"] is not None: @@ -81,8 +91,6 @@ class DeLijnPublicTransportSensor(Entity): else: first_passage = first["due_at_schedule"] self._state = first_passage - self._name = first["stopname"] - self._attributes["stopname"] = first["stopname"] self._attributes["line_number_public"] = first["line_number_public"] self._attributes["line_transport_type"] = first["line_transport_type"] self._attributes["final_destination"] = first["final_destination"] @@ -90,8 +98,8 @@ class DeLijnPublicTransportSensor(Entity): self._attributes["due_at_realtime"] = first["due_at_realtime"] self._attributes["next_passages"] = self.line.passages self._available = True - except (KeyError, IndexError) as error: - _LOGGER.debug("Error getting data from De Lijn: %s", error) + except (KeyError, IndexError): + _LOGGER.error("Invalid data received from De Lijn") self._available = False @property diff --git a/requirements_all.txt b/requirements_all.txt index f51773815dc..eecf3ef956e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1272,7 +1272,7 @@ pydanfossair==0.1.0 pydeconz==70 # homeassistant.components.delijn -pydelijn==0.5.1 +pydelijn==0.6.0 # homeassistant.components.zwave pydispatcher==2.0.5 From 2d76e12c21f81db9b1919baa02bea157468d40e8 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Thu, 21 May 2020 03:06:51 +0800 Subject: [PATCH 02/11] Handle None received from pyforked-daapd (#35830) * Handle None received from API in forked-daapd * Bump pyforked-daapd version in requirements * Add test --- .../components/forked_daapd/manifest.json | 2 +- .../components/forked_daapd/media_player.py | 62 ++++++++++--------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../forked_daapd/test_media_player.py | 31 +++++++++- 5 files changed, 66 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/forked_daapd/manifest.json b/homeassistant/components/forked_daapd/manifest.json index ee57f678601..171776a290a 100644 --- a/homeassistant/components/forked_daapd/manifest.json +++ b/homeassistant/components/forked_daapd/manifest.json @@ -3,7 +3,7 @@ "name": "forked-daapd", "documentation": "https://www.home-assistant.io/integrations/forked-daapd", "codeowners": ["@uvjustin"], - "requirements": ["pyforked-daapd==0.1.8", "pylibrespot-java==0.1.0"], + "requirements": ["pyforked-daapd==0.1.9", "pylibrespot-java==0.1.0"], "config_flow": true, "zeroconf": ["_daap._tcp.local."] } diff --git a/homeassistant/components/forked_daapd/media_player.py b/homeassistant/components/forked_daapd/media_player.py index e492aa1b454..07cc11807fd 100644 --- a/homeassistant/components/forked_daapd/media_player.py +++ b/homeassistant/components/forked_daapd/media_player.py @@ -794,26 +794,29 @@ class ForkedDaapdUpdater: "queue" in update_types ): # update queue, queue before player for async_play_media queue = await self._api.get_request("queue") - update_events["queue"] = asyncio.Event() - async_dispatcher_send( - self.hass, - SIGNAL_UPDATE_QUEUE.format(self._entry_id), - queue, - update_events["queue"], - ) + if queue: + update_events["queue"] = asyncio.Event() + async_dispatcher_send( + self.hass, + SIGNAL_UPDATE_QUEUE.format(self._entry_id), + queue, + update_events["queue"], + ) # order of below don't matter if not {"outputs", "volume"}.isdisjoint(update_types): # update outputs - outputs = (await self._api.get_request("outputs"))["outputs"] - update_events[ - "outputs" - ] = asyncio.Event() # only for master, zones should ignore - async_dispatcher_send( - self.hass, - SIGNAL_UPDATE_OUTPUTS.format(self._entry_id), - outputs, - update_events["outputs"], - ) - self._add_zones(outputs) + outputs = await self._api.get_request("outputs") + if outputs: + outputs = outputs["outputs"] + update_events[ + "outputs" + ] = asyncio.Event() # only for master, zones should ignore + async_dispatcher_send( + self.hass, + SIGNAL_UPDATE_OUTPUTS.format(self._entry_id), + outputs, + update_events["outputs"], + ) + self._add_zones(outputs) if not {"database"}.isdisjoint(update_types): pipes, playlists = await asyncio.gather( self._api.get_pipes(), self._api.get_playlists() @@ -832,17 +835,18 @@ class ForkedDaapdUpdater: update_types ): # update player player = await self._api.get_request("player") - update_events["player"] = asyncio.Event() - if update_events.get("queue"): - await update_events[ - "queue" - ].wait() # make sure queue done before player for async_play_media - async_dispatcher_send( - self.hass, - SIGNAL_UPDATE_PLAYER.format(self._entry_id), - player, - update_events["player"], - ) + if player: + update_events["player"] = asyncio.Event() + if update_events.get("queue"): + await update_events[ + "queue" + ].wait() # make sure queue done before player for async_play_media + async_dispatcher_send( + self.hass, + SIGNAL_UPDATE_PLAYER.format(self._entry_id), + player, + update_events["player"], + ) if update_events: await asyncio.wait( [event.wait() for event in update_events.values()] diff --git a/requirements_all.txt b/requirements_all.txt index eecf3ef956e..fda4c3435b7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1332,7 +1332,7 @@ pyflunearyou==1.0.7 pyfnip==0.2 # homeassistant.components.forked_daapd -pyforked-daapd==0.1.8 +pyforked-daapd==0.1.9 # homeassistant.components.fritzbox pyfritzhome==0.4.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b7748649a36..89f8c084907 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -554,7 +554,7 @@ pyflume==0.4.0 pyflunearyou==1.0.7 # homeassistant.components.forked_daapd -pyforked-daapd==0.1.8 +pyforked-daapd==0.1.9 # homeassistant.components.fritzbox pyfritzhome==0.4.2 diff --git a/tests/components/forked_daapd/test_media_player.py b/tests/components/forked_daapd/test_media_player.py index 8f3a6c2c139..43a39146199 100644 --- a/tests/components/forked_daapd/test_media_player.py +++ b/tests/components/forked_daapd/test_media_player.py @@ -8,6 +8,9 @@ from homeassistant.components.forked_daapd.const import ( CONF_TTS_PAUSE_TIME, CONF_TTS_VOLUME, DOMAIN, + SIGNAL_UPDATE_OUTPUTS, + SIGNAL_UPDATE_PLAYER, + SIGNAL_UPDATE_QUEUE, SOURCE_NAME_CLEAR, SOURCE_NAME_DEFAULT, SUPPORTED_FEATURES, @@ -63,7 +66,7 @@ from homeassistant.const import ( ) from tests.async_mock import patch -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_mock_signal TEST_MASTER_ENTITY_NAME = "media_player.forked_daapd_server" TEST_ZONE_ENTITY_NAMES = [ @@ -369,6 +372,32 @@ def test_master_state(hass, mock_api_object): assert not state.attributes[ATTR_MEDIA_SHUFFLE] +async def test_no_update_when_get_request_returns_none( + hass, config_entry, mock_api_object +): + """Test when get request returns None.""" + + async def get_request_side_effect(update_type): + return None + + mock_api_object.get_request.side_effect = get_request_side_effect + updater_update = mock_api_object.start_websocket_handler.call_args[0][2] + signal_output_call = async_mock_signal( + hass, SIGNAL_UPDATE_OUTPUTS.format(config_entry.entry_id) + ) + signal_player_call = async_mock_signal( + hass, SIGNAL_UPDATE_PLAYER.format(config_entry.entry_id) + ) + signal_queue_call = async_mock_signal( + hass, SIGNAL_UPDATE_QUEUE.format(config_entry.entry_id) + ) + await updater_update(["outputs", "player", "queue"]) + await hass.async_block_till_done() + assert len(signal_output_call) == 0 + assert len(signal_player_call) == 0 + assert len(signal_queue_call) == 0 + + async def _service_call( hass, entity_name, service, additional_service_data=None, blocking=True ): From 813203a38f797a312ab6a0031ad987cd875f8245 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 20 May 2020 13:25:42 +0200 Subject: [PATCH 03/11] Fix Daikin duplicate entries (#35833) --- homeassistant/components/daikin/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index 1e03938dfcf..06735d7e3b8 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -16,7 +16,7 @@ from homeassistant.helpers.typing import HomeAssistantType from homeassistant.util import Throttle from . import config_flow # noqa: F401 -from .const import CONF_KEY, CONF_UUID, TIMEOUT +from .const import CONF_KEY, CONF_UUID, KEY_MAC, TIMEOUT _LOGGER = logging.getLogger(__name__) @@ -61,6 +61,9 @@ async def async_setup(hass, config): async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): """Establish connection with Daikin.""" conf = entry.data + # For backwards compat, set unique ID + if entry.unique_id is None: + hass.config_entries.async_update_entry(entry, unique_id=conf[KEY_MAC]) daikin_api = await daikin_api_setup( hass, conf[CONF_HOST], From 1c38bcaeb52d4ad4c7847ea58abb29a7b14e6dea Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 May 2020 03:30:18 -0500 Subject: [PATCH 04/11] Homekit should skip devices that are missing in device registry (#35857) * Homekit should skip devices that are missing in device registry * Add test for this failure state --- homeassistant/components/homekit/__init__.py | 14 ++-- tests/components/homekit/test_homekit.py | 80 ++++++++++++++++++++ 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 0a208e012fb..adbf79128e3 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -627,12 +627,14 @@ class HomeKit: ent_cfg = self._config.setdefault(entity_id, {}) if ent_reg_ent.device_id: dev_reg_ent = dev_reg.async_get(ent_reg_ent.device_id) - if dev_reg_ent.manufacturer: - ent_cfg[ATTR_MANUFACTURER] = dev_reg_ent.manufacturer - if dev_reg_ent.model: - ent_cfg[ATTR_MODEL] = dev_reg_ent.model - if dev_reg_ent.sw_version: - ent_cfg[ATTR_SOFTWARE_VERSION] = dev_reg_ent.sw_version + if dev_reg_ent is not None: + # Handle missing devices + if dev_reg_ent.manufacturer: + ent_cfg[ATTR_MANUFACTURER] = dev_reg_ent.manufacturer + if dev_reg_ent.model: + ent_cfg[ATTR_MODEL] = dev_reg_ent.model + if dev_reg_ent.sw_version: + ent_cfg[ATTR_SOFTWARE_VERSION] = dev_reg_ent.sw_version if ATTR_MANUFACTURER not in ent_cfg: integration = await async_get_integration(self.hass, ent_reg_ent.platform) ent_cfg[ATTR_INTERGRATION] = integration.name diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index 8a4ac87f21b..c0e2ea90fba 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -913,3 +913,83 @@ def _write_data(path: str, data: Dict) -> None: if not os.path.isdir(os.path.dirname(path)): os.makedirs(os.path.dirname(path)) json_util.save_json(path, data) + + +async def test_homekit_ignored_missing_devices( + hass, hk_driver, debounce_patcher, device_reg, entity_reg +): + """Test HomeKit handles a device in the entity registry but missing from the device registry.""" + entry = await async_init_integration(hass) + + homekit = HomeKit( + hass, + None, + None, + None, + {}, + {"light.demo": {}}, + DEFAULT_SAFE_MODE, + advertise_ip=None, + interface_choice=None, + entry_id=entry.entry_id, + ) + homekit.driver = hk_driver + homekit._filter = Mock(return_value=True) + homekit.bridge = HomeBridge(hass, hk_driver, "mock_bridge") + + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + sw_version="0.16.0", + model="Powerwall 2", + manufacturer="Tesla", + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + + entity_reg.async_get_or_create( + "binary_sensor", + "powerwall", + "battery_charging", + device_id=device_entry.id, + device_class=DEVICE_CLASS_BATTERY_CHARGING, + ) + entity_reg.async_get_or_create( + "sensor", + "powerwall", + "battery", + device_id=device_entry.id, + device_class=DEVICE_CLASS_BATTERY, + ) + light = entity_reg.async_get_or_create( + "light", "powerwall", "demo", device_id=device_entry.id + ) + + # Delete the device to make sure we fallback + # to using the platform + device_reg.async_remove_device(device_entry.id) + + hass.states.async_set(light.entity_id, STATE_ON) + + def _mock_get_accessory(*args, **kwargs): + return [None, "acc", None] + + with patch.object(homekit.bridge, "add_accessory"), patch( + f"{PATH_HOMEKIT}.show_setup_message" + ), patch(f"{PATH_HOMEKIT}.get_accessory") as mock_get_acc, patch( + "pyhap.accessory_driver.AccessoryDriver.start" + ): + await homekit.async_start() + await hass.async_block_till_done() + + mock_get_acc.assert_called_with( + hass, + hk_driver, + ANY, + ANY, + { + "platform": "Tesla Powerwall", + "linked_battery_charging_sensor": "binary_sensor.powerwall_battery_charging", + "linked_battery_sensor": "sensor.powerwall_battery", + }, + ) From b9ab6d1e10d1c3eb211575114e4d75f40140e100 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 21 May 2020 10:25:28 +0200 Subject: [PATCH 05/11] Updated frontend to 20200519.1 (#35877) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 563e71c2eec..01d1b6eb88f 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20200519.0"], + "requirements": ["home-assistant-frontend==20200519.1"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index c59502846b6..d356e62a32e 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ cryptography==2.9.2 defusedxml==0.6.0 distro==1.5.0 hass-nabucasa==0.34.2 -home-assistant-frontend==20200519.0 +home-assistant-frontend==20200519.1 importlib-metadata==1.6.0 jinja2>=2.11.1 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index fda4c3435b7..0b8d15022b4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -731,7 +731,7 @@ hole==0.5.1 holidays==0.10.2 # homeassistant.components.frontend -home-assistant-frontend==20200519.0 +home-assistant-frontend==20200519.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 89f8c084907..570a59cc780 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -312,7 +312,7 @@ hole==0.5.1 holidays==0.10.2 # homeassistant.components.frontend -home-assistant-frontend==20200519.0 +home-assistant-frontend==20200519.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 From c7008eb7615681b40448aacf87b91f038fad7e89 Mon Sep 17 00:00:00 2001 From: Daniel Perna Date: Thu, 21 May 2020 10:35:04 +0200 Subject: [PATCH 06/11] Fix light profiles for HomeMatic lights (#35882) --- homeassistant/components/homematic/light.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homematic/light.py b/homeassistant/components/homematic/light.py index c7cfcc2ac8c..7ef632dae81 100644 --- a/homeassistant/components/homematic/light.py +++ b/homeassistant/components/homematic/light.py @@ -111,7 +111,7 @@ class HMLight(HMDevice, LightEntity): ): self._hmdevice.on(self._channel) - if ATTR_HS_COLOR in kwargs: + if ATTR_HS_COLOR in kwargs and self.supported_features & SUPPORT_COLOR: self._hmdevice.set_hs_color( hue=kwargs[ATTR_HS_COLOR][0] / 360.0, saturation=kwargs[ATTR_HS_COLOR][1] / 100.0, From ae66f4250c9b2e426abfdc8b0dd66a032c1640b6 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Wed, 20 May 2020 21:26:27 -0400 Subject: [PATCH 07/11] fix mjpeg issue along with some cameras not returning event capabilities properly (#35885) --- homeassistant/components/onvif/camera.py | 20 ++++++++++---------- homeassistant/components/onvif/device.py | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 4d39c95c3cd..570b99bfe3a 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -106,11 +106,6 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera): async def stream_source(self): """Return the stream source.""" - if self._stream_uri is None: - uri_no_auth = await self.device.async_get_stream_uri(self.profile) - self._stream_uri = uri_no_auth.replace( - "rtsp://", f"rtsp://{self.device.username}:{self.device.password}@", 1 - ) return self._stream_uri async def async_camera_image(self): @@ -118,11 +113,6 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera): image = None if self.device.capabilities.snapshot: - if self._snapshot_uri is None: - self._snapshot_uri = await self.device.async_get_snapshot_uri( - self.profile - ) - auth = None if self.device.username and self.device.password: auth = HTTPDigestAuth(self.device.username, self.device.password) @@ -181,6 +171,16 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera): finally: await stream.close() + async def async_added_to_hass(self): + """Run when entity about to be added to hass.""" + uri_no_auth = await self.device.async_get_stream_uri(self.profile) + self._stream_uri = uri_no_auth.replace( + "rtsp://", f"rtsp://{self.device.username}:{self.device.password}@", 1 + ) + + if self.device.capabilities.snapshot: + self._snapshot_uri = await self.device.async_get_snapshot_uri(self.profile) + async def async_perform_ptz( self, distance, diff --git a/homeassistant/components/onvif/device.py b/homeassistant/components/onvif/device.py index 8e69e148da3..0a35dadec26 100644 --- a/homeassistant/components/onvif/device.py +++ b/homeassistant/components/onvif/device.py @@ -223,7 +223,7 @@ class ONVIFDevice: try: media_service = self.device.create_media_service() media_capabilities = await media_service.GetServiceCapabilities() - snapshot = media_capabilities.SnapshotUri + snapshot = media_capabilities and media_capabilities.SnapshotUri except (ONVIFError, Fault): pass @@ -231,7 +231,7 @@ class ONVIFDevice: try: event_service = self.device.create_events_service() event_capabilities = await event_service.GetServiceCapabilities() - pullpoint = event_capabilities.WSPullPointSupport + pullpoint = event_capabilities and event_capabilities.WSPullPointSupport except (ONVIFError, Fault): pass From c88c011df9135f02d09e69dcd2d77204daa33050 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 May 2020 01:48:01 -0500 Subject: [PATCH 08/11] Ensure http can startup if homekit fails to load (#35888) * Ensure HomeAssistant can startup if homekit fails to load * Update homeassistant/components/logbook/manifest.json Co-authored-by: Martin Hjelmare Co-authored-by: Martin Hjelmare --- homeassistant/components/logbook/manifest.json | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/logbook/manifest.json b/homeassistant/components/logbook/manifest.json index 3980469c9c5..26586013108 100644 --- a/homeassistant/components/logbook/manifest.json +++ b/homeassistant/components/logbook/manifest.json @@ -3,6 +3,5 @@ "name": "Logbook", "documentation": "https://www.home-assistant.io/integrations/logbook", "dependencies": ["frontend", "http", "recorder"], - "after_dependencies": ["homekit"], "codeowners": [] } From ddee8b68c65ba3ec56b43967fbd21af91f40f0e6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 May 2020 03:27:40 -0500 Subject: [PATCH 09/11] Ensure storage write consume the data under the lock (#35889) If two writes trigger at the same time the data would already be consumed. --- homeassistant/helpers/storage.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/homeassistant/helpers/storage.py b/homeassistant/helpers/storage.py index b5ac942bf2f..d2b4c334937 100644 --- a/homeassistant/helpers/storage.py +++ b/homeassistant/helpers/storage.py @@ -200,14 +200,19 @@ class Store: async def _async_handle_write_data(self, *_args): """Handle writing the config.""" - data = self._data - - if "data_func" in data: - data["data"] = data.pop("data_func")() - - self._data = None async with self._write_lock: + if self._data is None: + # Another write already consumed the data + return + + data = self._data + + if "data_func" in data: + data["data"] = data.pop("data_func")() + + self._data = None + try: await self.hass.async_add_executor_job( self._write_data, self.path, data From e170e6563b903cf246aff70390e9ab50ad6ffd55 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 May 2020 02:18:45 -0500 Subject: [PATCH 10/11] Fix legacy Hunter Douglas PowerView devices (#35895) These devices are missing firmware information as the 1.0 firmware did not provide it. --- .../hunterdouglas_powerview/__init__.py | 22 ++++++++++-- .../hunterdouglas_powerview/const.py | 5 +++ .../hunterdouglas_powerview/userdata_v1.json | 34 +++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/hunterdouglas_powerview/userdata_v1.json diff --git a/homeassistant/components/hunterdouglas_powerview/__init__.py b/homeassistant/components/hunterdouglas_powerview/__init__.py index 89dc610a6fc..df06060fb75 100644 --- a/homeassistant/components/hunterdouglas_powerview/__init__.py +++ b/homeassistant/components/hunterdouglas_powerview/__init__.py @@ -31,9 +31,15 @@ from .const import ( DEVICE_REVISION, DEVICE_SERIAL_NUMBER, DOMAIN, + FIRMWARE_BUILD, FIRMWARE_IN_USERDATA, + FIRMWARE_SUB_REVISION, HUB_EXCEPTIONS, HUB_NAME, + LEGACY_DEVICE_BUILD, + LEGACY_DEVICE_MODEL, + LEGACY_DEVICE_REVISION, + LEGACY_DEVICE_SUB_REVISION, MAC_ADDRESS_IN_USERDATA, MAINPROCESSOR_IN_USERDATA_FIRMWARE, MODEL_IN_MAINPROCESSOR, @@ -159,9 +165,19 @@ async def async_get_device_info(pv_request): resources = await userdata.get_resources() userdata_data = resources[USER_DATA] - main_processor_info = userdata_data[FIRMWARE_IN_USERDATA][ - MAINPROCESSOR_IN_USERDATA_FIRMWARE - ] + if FIRMWARE_IN_USERDATA in userdata_data: + main_processor_info = userdata_data[FIRMWARE_IN_USERDATA][ + MAINPROCESSOR_IN_USERDATA_FIRMWARE + ] + else: + # Legacy devices + main_processor_info = { + REVISION_IN_MAINPROCESSOR: LEGACY_DEVICE_REVISION, + FIRMWARE_SUB_REVISION: LEGACY_DEVICE_SUB_REVISION, + FIRMWARE_BUILD: LEGACY_DEVICE_BUILD, + MODEL_IN_MAINPROCESSOR: LEGACY_DEVICE_MODEL, + } + return { DEVICE_NAME: base64_to_unicode(userdata_data[HUB_NAME]), DEVICE_MAC_ADDRESS: userdata_data[MAC_ADDRESS_IN_USERDATA], diff --git a/homeassistant/components/hunterdouglas_powerview/const.py b/homeassistant/components/hunterdouglas_powerview/const.py index 17ff3821a7a..e69fe319c0f 100644 --- a/homeassistant/components/hunterdouglas_powerview/const.py +++ b/homeassistant/components/hunterdouglas_powerview/const.py @@ -65,3 +65,8 @@ PV_ROOM_DATA = "pv_room_data" COORDINATOR = "coordinator" HUB_EXCEPTIONS = (asyncio.TimeoutError, PvApiConnectionError) + +LEGACY_DEVICE_SUB_REVISION = 1 +LEGACY_DEVICE_REVISION = 0 +LEGACY_DEVICE_BUILD = 0 +LEGACY_DEVICE_MODEL = "PV Hub1.0" diff --git a/tests/fixtures/hunterdouglas_powerview/userdata_v1.json b/tests/fixtures/hunterdouglas_powerview/userdata_v1.json new file mode 100644 index 00000000000..d97aca162f8 --- /dev/null +++ b/tests/fixtures/hunterdouglas_powerview/userdata_v1.json @@ -0,0 +1,34 @@ +{ + "userData" : { + "enableScheduledEvents" : true, + "staticIp" : false, + "sceneControllerCount" : 0, + "accessPointCount" : 0, + "shadeCount" : 5, + "ip" : "192.168.20.9", + "groupCount" : 9, + "scheduledEventCount" : 0, + "editingEnabled" : true, + "roomCount" : 5, + "setupCompleted" : false, + "sceneCount" : 18, + "sceneControllerMemberCount" : 0, + "mask" : "255.255.255.0", + "hubName" : "UG93ZXJWaWV3IEh1YiBHZW4gMQ==", + "rfID" : "0x8B2A", + "remoteConnectEnabled" : false, + "multiSceneMemberCount" : 0, + "rfStatus" : 0, + "serialNumber" : "REMOVED", + "undefinedShadeCount" : 0, + "sceneMemberCount" : 18, + "unassignedShadeCount" : 0, + "multiSceneCount" : 0, + "addressKind" : "newPrimary", + "gateway" : "192.168.20.1", + "localTimeDataSet" : true, + "dns" : "192.168.20.1", + "macAddress" : "00:00:00:00:00:eb", + "rfIDInt" : 35626 + } +} From b266d3aaaefac83490a79d2f18313de4ddb10c34 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 21 May 2020 08:47:50 +0000 Subject: [PATCH 11/11] Bump version 0.110.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index e7eddca36e0..366c2c79d99 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 110 -PATCH_VERSION = "0" +PATCH_VERSION = "1" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0)