Add Guest WiFi QR-Code image entity to AVM Fritz!Tools (#95282)

This commit is contained in:
Michael 2023-06-27 23:48:28 +02:00 committed by GitHub
parent aaa4ee79b8
commit 4daacf9c4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 186 additions and 5 deletions

View File

@ -393,6 +393,7 @@ 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

View File

@ -31,6 +31,7 @@ PLATFORMS = [
Platform.BUTTON,
Platform.BINARY_SENSOR,
Platform.DEVICE_TRACKER,
Platform.IMAGE,
Platform.SENSOR,
Platform.SWITCH,
Platform.UPDATE,

View File

@ -0,0 +1,90 @@
"""FRITZ image integration."""
from __future__ import annotations
from io import BytesIO
import logging
from homeassistant.components.image import ImageEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import dt as dt_util, slugify
from .common import AvmWrapper, FritzBoxBaseEntity
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up guest WiFi QR code for device."""
avm_wrapper: AvmWrapper = hass.data[DOMAIN][entry.entry_id]
guest_wifi_info = await hass.async_add_executor_job(
avm_wrapper.fritz_guest_wifi.get_info
)
if not guest_wifi_info.get("NewEnable"):
return
async_add_entities(
[
FritzGuestWifiQRImage(
avm_wrapper, entry.title, guest_wifi_info["NewSSID"], hass
)
]
)
class FritzGuestWifiQRImage(FritzBoxBaseEntity, ImageEntity):
"""Implementation of the FritzBox guest wifi QR code image entity."""
_attr_content_type = "image/png"
_attr_entity_category = EntityCategory.DIAGNOSTIC
_attr_has_entity_name = True
def __init__(
self,
avm_wrapper: AvmWrapper,
device_friendly_name: str,
ssid: str,
hass: HomeAssistant,
) -> None:
"""Initialize the image entity."""
self._attr_name = ssid
self._attr_unique_id = slugify(f"{avm_wrapper.unique_id}-{ssid}-qr-code")
self._current_qr_bytes: bytes | None = None
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."""
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
if self._current_qr_bytes != qr_bytes:
dt_now = dt_util.utcnow()
_LOGGER.debug("qr code has changed, reset image last updated property")
self._attr_image_last_updated = dt_now
self._current_qr_bytes = qr_bytes
self.async_write_ha_state()
return qr_bytes

View File

@ -7,7 +7,7 @@
"documentation": "https://www.home-assistant.io/integrations/fritz",
"iot_class": "local_polling",
"loggers": ["fritzconnection"],
"requirements": ["fritzconnection==1.12.0", "xmltodict==0.13.0"],
"requirements": ["fritzconnection[qr]==1.12.0", "xmltodict==0.13.0"],
"ssdp": [
{
"st": "urn:schemas-upnp-org:device:fritzbox:1"

View File

@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["fritzconnection"],
"requirements": ["fritzconnection==1.12.0"]
"requirements": ["fritzconnection[qr]==1.12.0"]
}

View File

@ -818,7 +818,7 @@ freesms==0.2.0
# homeassistant.components.fritz
# homeassistant.components.fritzbox_callmonitor
fritzconnection==1.12.0
fritzconnection[qr]==1.12.0
# homeassistant.components.google_translate
gTTS==2.2.4

View File

@ -637,7 +637,7 @@ freebox-api==1.1.0
# homeassistant.components.fritz
# homeassistant.components.fritzbox_callmonitor
fritzconnection==1.12.0
fritzconnection[qr]==1.12.0
# homeassistant.components.google_translate
gTTS==2.2.4

View File

@ -195,7 +195,6 @@ MOCK_FB_SERVICES: dict[str, dict] = {
},
}
MOCK_MESH_DATA = {
"schema_version": "1.9",
"nodes": [

View File

@ -0,0 +1,90 @@
"""Tests for Fritz!Tools image platform."""
import pytest
from homeassistant.components.fritz.const import DOMAIN
from homeassistant.components.image import DOMAIN as IMAGE_DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_registry import async_get as async_get_entity_registry
from homeassistant.setup import async_setup_component
from .const import MOCK_FB_SERVICES, MOCK_USER_DATA
from tests.common import MockConfigEntry
from tests.typing import ClientSessionGenerator
GUEST_WIFI_ENABLED: dict[str, dict] = {
"WLANConfiguration0": {
"GetInfo": {
"NewEnable": True,
"NewSSID": "HomeWifi",
}
},
"WLANConfiguration1": {
"GetInfo": {
"NewEnable": True,
"NewSSID": "GuestWifi",
}
},
}
GUEST_WIFI_DISABLED: dict[str, dict] = {
"WLANConfiguration0": {
"GetInfo": {
"NewEnable": True,
"NewSSID": "HomeWifi",
}
},
"WLANConfiguration1": {
"GetInfo": {
"NewEnable": False,
"NewSSID": "GuestWifi",
}
},
}
@pytest.mark.parametrize(("fc_data"), [({**MOCK_FB_SERVICES, **GUEST_WIFI_ENABLED})])
async def test_image_entities_initialized(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
fc_class_mock,
fh_class_mock,
) -> None:
"""Test image entities."""
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
images = hass.states.async_all(IMAGE_DOMAIN)
assert len(images) == 1
assert images[0].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"
@pytest.mark.parametrize(("fc_data"), [({**MOCK_FB_SERVICES, **GUEST_WIFI_DISABLED})])
async def test_image_guest_wifi_disabled(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
fc_class_mock,
fh_class_mock,
) -> None:
"""Test image entities."""
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
images = hass.states.async_all(IMAGE_DOMAIN)
assert len(images) == 0