mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 01:07:10 +00:00
Add Guest WiFi QR-Code image entity to AVM Fritz!Tools (#95282)
This commit is contained in:
parent
aaa4ee79b8
commit
4daacf9c4b
@ -393,6 +393,7 @@ omit =
|
|||||||
homeassistant/components/freebox/switch.py
|
homeassistant/components/freebox/switch.py
|
||||||
homeassistant/components/fritz/common.py
|
homeassistant/components/fritz/common.py
|
||||||
homeassistant/components/fritz/device_tracker.py
|
homeassistant/components/fritz/device_tracker.py
|
||||||
|
homeassistant/components/fritz/image.py
|
||||||
homeassistant/components/fritz/services.py
|
homeassistant/components/fritz/services.py
|
||||||
homeassistant/components/fritz/switch.py
|
homeassistant/components/fritz/switch.py
|
||||||
homeassistant/components/fritzbox_callmonitor/__init__.py
|
homeassistant/components/fritzbox_callmonitor/__init__.py
|
||||||
|
@ -31,6 +31,7 @@ PLATFORMS = [
|
|||||||
Platform.BUTTON,
|
Platform.BUTTON,
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
Platform.DEVICE_TRACKER,
|
Platform.DEVICE_TRACKER,
|
||||||
|
Platform.IMAGE,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
Platform.UPDATE,
|
Platform.UPDATE,
|
||||||
|
90
homeassistant/components/fritz/image.py
Normal file
90
homeassistant/components/fritz/image.py
Normal 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
|
@ -7,7 +7,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/fritz",
|
"documentation": "https://www.home-assistant.io/integrations/fritz",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["fritzconnection"],
|
"loggers": ["fritzconnection"],
|
||||||
"requirements": ["fritzconnection==1.12.0", "xmltodict==0.13.0"],
|
"requirements": ["fritzconnection[qr]==1.12.0", "xmltodict==0.13.0"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"st": "urn:schemas-upnp-org:device:fritzbox:1"
|
"st": "urn:schemas-upnp-org:device:fritzbox:1"
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["fritzconnection"],
|
"loggers": ["fritzconnection"],
|
||||||
"requirements": ["fritzconnection==1.12.0"]
|
"requirements": ["fritzconnection[qr]==1.12.0"]
|
||||||
}
|
}
|
||||||
|
@ -818,7 +818,7 @@ freesms==0.2.0
|
|||||||
|
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
# homeassistant.components.fritzbox_callmonitor
|
# homeassistant.components.fritzbox_callmonitor
|
||||||
fritzconnection==1.12.0
|
fritzconnection[qr]==1.12.0
|
||||||
|
|
||||||
# homeassistant.components.google_translate
|
# homeassistant.components.google_translate
|
||||||
gTTS==2.2.4
|
gTTS==2.2.4
|
||||||
|
@ -637,7 +637,7 @@ freebox-api==1.1.0
|
|||||||
|
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
# homeassistant.components.fritzbox_callmonitor
|
# homeassistant.components.fritzbox_callmonitor
|
||||||
fritzconnection==1.12.0
|
fritzconnection[qr]==1.12.0
|
||||||
|
|
||||||
# homeassistant.components.google_translate
|
# homeassistant.components.google_translate
|
||||||
gTTS==2.2.4
|
gTTS==2.2.4
|
||||||
|
@ -195,7 +195,6 @@ MOCK_FB_SERVICES: dict[str, dict] = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MOCK_MESH_DATA = {
|
MOCK_MESH_DATA = {
|
||||||
"schema_version": "1.9",
|
"schema_version": "1.9",
|
||||||
"nodes": [
|
"nodes": [
|
||||||
|
90
tests/components/fritz/test_image.py
Normal file
90
tests/components/fritz/test_image.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user