diff --git a/.coveragerc b/.coveragerc index d05fff927e1..6a2a0db3ea4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -393,7 +393,6 @@ omit = homeassistant/components/freebox/switch.py homeassistant/components/fritz/common.py homeassistant/components/fritz/device_tracker.py - homeassistant/components/fritz/image.py homeassistant/components/fritz/services.py homeassistant/components/fritz/switch.py homeassistant/components/fritzbox_callmonitor/__init__.py diff --git a/homeassistant/components/fritz/image.py b/homeassistant/components/fritz/image.py index d15f50b0b7b..597dd8ddb53 100644 --- a/homeassistant/components/fritz/image.py +++ b/homeassistant/components/fritz/image.py @@ -48,6 +48,7 @@ class FritzGuestWifiQRImage(FritzBoxBaseEntity, ImageEntity): _attr_content_type = "image/png" _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_has_entity_name = True + _attr_should_poll = True def __init__( self, @@ -63,22 +64,24 @@ class FritzGuestWifiQRImage(FritzBoxBaseEntity, ImageEntity): super().__init__(avm_wrapper, device_friendly_name) ImageEntity.__init__(self, hass) - async def async_added_to_hass(self) -> None: - """Set the update time.""" - self._attr_image_last_updated = dt_util.utcnow() - - async def async_image(self) -> bytes: - """Return bytes of image.""" + async def _fetch_image(self) -> bytes: + """Fetch the QR code from the Fritz!Box.""" qr_stream: BytesIO = await self.hass.async_add_executor_job( self._avm_wrapper.fritz_guest_wifi.get_wifi_qr_code, "png" ) qr_bytes = qr_stream.getvalue() - _LOGGER.debug("fetched %s bytes", len(qr_bytes)) - if self._current_qr_bytes is None: - self._current_qr_bytes = qr_bytes - return qr_bytes + return qr_bytes + + async def async_added_to_hass(self) -> None: + """Fetch and set initial data and state.""" + self._current_qr_bytes = await self._fetch_image() + self._attr_image_last_updated = dt_util.utcnow() + + async def async_update(self) -> None: + """Update the image entity data.""" + qr_bytes = await self._fetch_image() if self._current_qr_bytes != qr_bytes: dt_now = dt_util.utcnow() @@ -87,4 +90,6 @@ class FritzGuestWifiQRImage(FritzBoxBaseEntity, ImageEntity): self._current_qr_bytes = qr_bytes self.async_write_ha_state() - return qr_bytes + async def async_image(self) -> bytes | None: + """Return bytes of image.""" + return self._current_qr_bytes diff --git a/tests/components/fritz/conftest.py b/tests/components/fritz/conftest.py index 5e86c3b83f6..66f4cf2b879 100644 --- a/tests/components/fritz/conftest.py +++ b/tests/components/fritz/conftest.py @@ -43,6 +43,10 @@ class FritzConnectionMock: # pylint: disable=too-few-public-methods else: self.call_action = self._call_action + def override_services(self, services) -> None: + """Overrire services data.""" + self._services = services + def _call_action(self, service: str, action: str, **kwargs): LOGGER.debug( "_call_action service: %s, action: %s, **kwargs: %s", diff --git a/tests/components/fritz/snapshots/test_image.ambr b/tests/components/fritz/snapshots/test_image.ambr new file mode 100644 index 00000000000..b64d8601a8a --- /dev/null +++ b/tests/components/fritz/snapshots/test_image.ambr @@ -0,0 +1,13 @@ +# serializer version: 1 +# name: test_image[fc_data0] + b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x94\x00\x00\x00\x94\x01\x00\x00\x00\x00]G=y\x00\x00\x00\xf5IDATx\xda\xedVQ\x0eC!\x0c"\xbb@\xef\x7fKn\xe0\x00\xfd\xdb\xcf6\xf9|\xc6\xc4\xc6\x0f\xd2\x02\xadb},\xe2\xb9\xfb\xe5\x0e\xc0(\x18\xf2\x84/|\xaeo\xef\x847\xda\x14\x1af\x1c\xde\xe3\x19(X\tKxN\xb2\x87\x17j9\x1d None: - """Test image entities.""" + """Test image entity.""" - entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA) - entry.add_to_hass(hass) + # setup component with image platform only + with patch( + "homeassistant.components.fritz.PLATFORMS", + [Platform.IMAGE], + ): + entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA) + entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, {}) - assert await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() assert entry.state == ConfigEntryState.LOADED - images = hass.states.async_all(IMAGE_DOMAIN) - assert len(images) == 1 - assert images[0].name == "Mock Title GuestWifi" + # test image entity is generated as expected + states = hass.states.async_all(IMAGE_DOMAIN) + assert len(states) == 1 + + state = states[0] + assert state.name == "Mock Title GuestWifi" + assert state.entity_id == "image.mock_title_guestwifi" + + access_token = state.attributes["access_token"] + assert state.attributes == { + "access_token": access_token, + "entity_picture": f"/api/image_proxy/image.mock_title_guestwifi?token={access_token}", + "friendly_name": "Mock Title GuestWifi", + } entity_registry = async_get_entity_registry(hass) entity_entry = entity_registry.async_get("image.mock_title_guestwifi") - assert entity_entry.unique_id == "1c_ed_6f_12_34_11_guestwifi_qr_code" + # test image download + client = await hass_client() + resp = await client.get("/api/image_proxy/image.mock_title_guestwifi") + assert resp.status == HTTPStatus.OK + + body = await resp.read() + assert body == snapshot + + +@pytest.mark.parametrize(("fc_data"), [({**MOCK_FB_SERVICES, **GUEST_WIFI_ENABLED})]) +async def test_image_update( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + snapshot: SnapshotAssertion, + fc_class_mock, + fh_class_mock, +) -> None: + """Test image update.""" + + # setup component with image platform only + with patch( + "homeassistant.components.fritz.PLATFORMS", + [Platform.IMAGE], + ): + entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA) + entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, {}) + + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.LOADED + + client = await hass_client() + resp = await client.get("/api/image_proxy/image.mock_title_guestwifi") + resp_body = await resp.read() + assert resp.status == HTTPStatus.OK + + fc_class_mock().override_services({**MOCK_FB_SERVICES, **GUEST_WIFI_CHANGED}) + async_fire_time_changed(hass, utcnow() + timedelta(seconds=60)) + await hass.async_block_till_done() + + resp = await client.get("/api/image_proxy/image.mock_title_guestwifi") + resp_body_new = await resp.read() + + assert resp_body != resp_body_new + assert resp_body_new == snapshot + @pytest.mark.parametrize(("fc_data"), [({**MOCK_FB_SERVICES, **GUEST_WIFI_DISABLED})]) async def test_image_guest_wifi_disabled(