Add camera platform to Freebox (#88104)
* Add Freebox cameras * Apply suggestions from code review add code corrections after PR review Co-authored-by: Quentame <polletquentin74@me.com> * Update base_class.py * add some code syntax corrections add unit tests * add unit tests * add syntax changes * Update homeassistant/components/freebox/router.py Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/router.py Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/base_class.py Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/router.py Co-authored-by: Quentame <polletquentin74@me.com> * clear code and add minor changes * correct syntax error and check home granted access * typing functions * Update tests/components/freebox/conftest.py don't needed, and will fix tests. Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py Rename _volume_micro variable Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py Use const not literal Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py set to true not needed Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py use _attr_supported_features instead _supported_features Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py overload the entity with command_flip property and set_flip not needed Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py Cameras does not default to False, Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py delete this function because is not needed Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py Co-authored-by: Quentame <polletquentin74@me.com> * consts, rollback _command flip is protected var * VALUE_NOT_SET does not exists anymore * Use HOME_COMPATIBLE_PLATFORMS * Rename FreeboxHomeBaseClass to FreeboxHomeEntity * Update Freebox Home comment * Use CATEGORY_TO_MODEL to set model attr of FreeboxHomeEntity * Use Home API from the router * Add SERVICE_FLIP const * Use SERVICE_FLIP const * Fix typo in HOME_COMPATIBLE_PLATFORMS * fix somme code issues * use SERVICE_FLIP (lost in merge) * use _attr_device_info * clear code * HOME_COMPATIBLE_PLATFORMS is a list * Update homeassistant/components/freebox/home_base.py Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/home_base.py Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/config_flow.py Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/home_base.py Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/home_base.py Co-authored-by: Quentame <polletquentin74@me.com> * clear config_flow permission * Update homeassistant/components/freebox/home_base.py Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py Co-authored-by: Quentame <polletquentin74@me.com> * add untested files to. coveragerc * clear unused attributes * add not tested file camera.py * clear unusued const * add extra_state_attributes * Update .coveragerc Co-authored-by: Quentame <polletquentin74@me.com> * Update homeassistant/components/freebox/camera.py Co-authored-by: Quentame <polletquentin74@me.com> * fetch _flip * del flip service * add device_info via_device * Update .coveragerc * Update .coveragerc * Update .coveragerc * Update .coveragerc * Remove flip reference * Fix issue on router without Home API * Fix "Home access is not granted" log repeats every 30s * Fix sensor device_info --------- Co-authored-by: Quentame <polletquentin74@me.com>pull/92036/head
parent
62bb584522
commit
2d510bfe0d
|
@ -386,7 +386,10 @@ omit =
|
|||
homeassistant/components/foscam/camera.py
|
||||
homeassistant/components/foursquare/*
|
||||
homeassistant/components/free_mobile/notify.py
|
||||
homeassistant/components/freebox/camera.py
|
||||
homeassistant/components/freebox/device_tracker.py
|
||||
homeassistant/components/freebox/home_base.py
|
||||
homeassistant/components/freebox/router.py
|
||||
homeassistant/components/freebox/sensor.py
|
||||
homeassistant/components/freebox/switch.py
|
||||
homeassistant/components/fritz/common.py
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
"""Support for Freebox cameras."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.camera import CameraEntityFeature
|
||||
from homeassistant.components.ffmpeg.camera import (
|
||||
CONF_EXTRA_ARGUMENTS,
|
||||
CONF_INPUT,
|
||||
DEFAULT_ARGUMENTS,
|
||||
FFmpegCamera,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_platform
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import ATTR_DETECTION, DOMAIN
|
||||
from .home_base import FreeboxHomeEntity
|
||||
from .router import FreeboxRouter
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up cameras."""
|
||||
router = hass.data[DOMAIN][entry.unique_id]
|
||||
tracked: set = set()
|
||||
|
||||
@callback
|
||||
def update_callback():
|
||||
add_entities(hass, router, async_add_entities, tracked)
|
||||
|
||||
router.listeners.append(
|
||||
async_dispatcher_connect(hass, router.signal_home_device_new, update_callback)
|
||||
)
|
||||
update_callback()
|
||||
|
||||
entity_platform.async_get_current_platform()
|
||||
|
||||
|
||||
@callback
|
||||
def add_entities(hass: HomeAssistant, router, async_add_entities, tracked):
|
||||
"""Add new cameras from the router."""
|
||||
new_tracked = []
|
||||
|
||||
for nodeid, node in router.home_devices.items():
|
||||
if (node["category"] != Platform.CAMERA) or (nodeid in tracked):
|
||||
continue
|
||||
new_tracked.append(FreeboxCamera(hass, router, node))
|
||||
tracked.add(nodeid)
|
||||
|
||||
if new_tracked:
|
||||
async_add_entities(new_tracked, True)
|
||||
|
||||
|
||||
class FreeboxCamera(FreeboxHomeEntity, FFmpegCamera):
|
||||
"""Representation of a Freebox camera."""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, router: FreeboxRouter, node: dict[str, Any]
|
||||
) -> None:
|
||||
"""Initialize a camera."""
|
||||
|
||||
super().__init__(hass, router, node)
|
||||
device_info = {
|
||||
CONF_NAME: node["label"].strip(),
|
||||
CONF_INPUT: node["props"]["Stream"],
|
||||
CONF_EXTRA_ARGUMENTS: DEFAULT_ARGUMENTS,
|
||||
}
|
||||
FFmpegCamera.__init__(self, hass, device_info)
|
||||
|
||||
self._supported_features = (
|
||||
CameraEntityFeature.ON_OFF | CameraEntityFeature.STREAM
|
||||
)
|
||||
|
||||
self._command_motion_detection = self.get_command_id(
|
||||
node["type"]["endpoints"], ATTR_DETECTION
|
||||
)
|
||||
self._attr_extra_state_attributes = {}
|
||||
self.update_node(node)
|
||||
|
||||
async def async_enable_motion_detection(self) -> None:
|
||||
"""Enable motion detection in the camera."""
|
||||
await self.set_home_endpoint_value(self._command_motion_detection, True)
|
||||
self._attr_motion_detection_enabled = True
|
||||
|
||||
async def async_disable_motion_detection(self) -> None:
|
||||
"""Disable motion detection in camera."""
|
||||
await self.set_home_endpoint_value(self._command_motion_detection, False)
|
||||
self._attr_motion_detection_enabled = False
|
||||
|
||||
async def async_update_signal(self) -> None:
|
||||
"""Update the camera node."""
|
||||
self.update_node(self._router.home_devices[self._id])
|
||||
self.async_write_ha_state()
|
||||
|
||||
def update_node(self, node):
|
||||
"""Update params."""
|
||||
self._name = node["label"].strip()
|
||||
|
||||
# Get status
|
||||
if self._node["status"] == "active":
|
||||
self._attr_is_streaming = True
|
||||
else:
|
||||
self._attr_is_streaming = False
|
||||
|
||||
# Parse all endpoints values
|
||||
for endpoint in filter(
|
||||
lambda x: (x["ep_type"] == "signal"), node["show_endpoints"]
|
||||
):
|
||||
self._attr_extra_state_attributes[endpoint["name"]] = endpoint["value"]
|
||||
|
||||
# Get motion detection status
|
||||
self._attr_motion_detection_enabled = self._attr_extra_state_attributes[
|
||||
ATTR_DETECTION
|
||||
]
|
|
@ -22,7 +22,7 @@ class FreeboxFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize Freebox config flow."""
|
||||
self._host = None
|
||||
self._host: str
|
||||
self._port = None
|
||||
|
||||
def _show_setup_form(self, user_input=None, errors=None):
|
||||
|
@ -42,9 +42,9 @@ class FreeboxFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
errors=errors or {},
|
||||
)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
async def async_step_user(self, user_input=None) -> FlowResult:
|
||||
"""Handle a flow initiated by the user."""
|
||||
errors = {}
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
if user_input is None:
|
||||
return self._show_setup_form(user_input, errors)
|
||||
|
@ -58,7 +58,7 @@ class FreeboxFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
return await self.async_step_link()
|
||||
|
||||
async def async_step_link(self, user_input=None):
|
||||
async def async_step_link(self, user_input=None) -> FlowResult:
|
||||
"""Attempt to link with the Freebox router.
|
||||
|
||||
Given a configured host, will ask the user to press the button
|
||||
|
@ -102,7 +102,7 @@ class FreeboxFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
return self.async_show_form(step_id="link", errors=errors)
|
||||
|
||||
async def async_step_import(self, user_input=None):
|
||||
async def async_step_import(self, user_input=None) -> FlowResult:
|
||||
"""Import a config entry."""
|
||||
return await self.async_step_user(user_input)
|
||||
|
||||
|
|
|
@ -16,7 +16,13 @@ APP_DESC = {
|
|||
}
|
||||
API_VERSION = "v6"
|
||||
|
||||
PLATFORMS = [Platform.BUTTON, Platform.DEVICE_TRACKER, Platform.SENSOR, Platform.SWITCH]
|
||||
PLATFORMS = [
|
||||
Platform.BUTTON,
|
||||
Platform.DEVICE_TRACKER,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.CAMERA,
|
||||
]
|
||||
|
||||
DEFAULT_DEVICE_NAME = "Unknown device"
|
||||
|
||||
|
@ -27,7 +33,6 @@ STORAGE_VERSION = 1
|
|||
|
||||
CONNECTION_SENSORS_KEYS = {"rate_down", "rate_up"}
|
||||
|
||||
|
||||
# Icons
|
||||
DEVICE_ICONS = {
|
||||
"freebox_delta": "mdi:television-guide",
|
||||
|
@ -48,3 +53,20 @@ DEVICE_ICONS = {
|
|||
"vg_console": "mdi:gamepad-variant",
|
||||
"workstation": "mdi:desktop-tower-monitor",
|
||||
}
|
||||
|
||||
ATTR_DETECTION = "detection"
|
||||
|
||||
|
||||
CATEGORY_TO_MODEL = {
|
||||
"pir": "F-HAPIR01A",
|
||||
"camera": "F-HACAM01A",
|
||||
"dws": "F-HADWS01A",
|
||||
"kfb": "F-HAKFB01A",
|
||||
"alarm": "F-MSEC07A",
|
||||
"rts": "RTS",
|
||||
"iohome": "IOHome",
|
||||
}
|
||||
|
||||
HOME_COMPATIBLE_PLATFORMS = [
|
||||
Platform.CAMERA,
|
||||
]
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
"""Support for Freebox base features."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
||||
from .const import CATEGORY_TO_MODEL, DOMAIN
|
||||
from .router import FreeboxRouter
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FreeboxHomeEntity(Entity):
|
||||
"""Representation of a Freebox base entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
router: FreeboxRouter,
|
||||
node: dict[str, Any],
|
||||
sub_node: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""Initialize a Freebox Home entity."""
|
||||
self._hass = hass
|
||||
self._router = router
|
||||
self._node = node
|
||||
self._sub_node = sub_node
|
||||
self._id = node["id"]
|
||||
self._attr_name = node["label"].strip()
|
||||
self._device_name = self._attr_name
|
||||
self._attr_unique_id = f"{self._router.mac}-node_{self._id}"
|
||||
|
||||
if sub_node is not None:
|
||||
self._attr_name += " " + sub_node["label"].strip()
|
||||
self._attr_unique_id += "-" + sub_node["name"].strip()
|
||||
|
||||
self._available = True
|
||||
self._firmware = node["props"].get("FwVersion")
|
||||
self._manufacturer = "Freebox SAS"
|
||||
self._remove_signal_update: Any
|
||||
|
||||
self._model = CATEGORY_TO_MODEL.get(node["category"])
|
||||
if self._model is None:
|
||||
if node["type"].get("inherit") == "node::rts":
|
||||
self._manufacturer = "Somfy"
|
||||
self._model = CATEGORY_TO_MODEL.get("rts")
|
||||
elif node["type"].get("inherit") == "node::ios":
|
||||
self._manufacturer = "Somfy"
|
||||
self._model = CATEGORY_TO_MODEL.get("iohome")
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._id)},
|
||||
manufacturer=self._manufacturer,
|
||||
model=self._model,
|
||||
name=self._device_name,
|
||||
sw_version=self._firmware,
|
||||
via_device=(
|
||||
DOMAIN,
|
||||
router.mac,
|
||||
),
|
||||
)
|
||||
|
||||
async def async_update_signal(self):
|
||||
"""Update signal."""
|
||||
self._node = self._router.home_devices[self._id]
|
||||
# Update name
|
||||
if self._sub_node is None:
|
||||
self._attr_name = self._node["label"].strip()
|
||||
else:
|
||||
self._attr_name = (
|
||||
self._node["label"].strip() + " " + self._sub_node["label"].strip()
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def set_home_endpoint_value(self, command_id: Any, value=None) -> None:
|
||||
"""Set Home endpoint value."""
|
||||
if command_id is None:
|
||||
_LOGGER.error("Unable to SET a value through the API. Command is None")
|
||||
return
|
||||
await self._router.home.set_home_endpoint_value(
|
||||
self._id, command_id, {"value": value}
|
||||
)
|
||||
|
||||
def get_command_id(self, nodes, name) -> int | None:
|
||||
"""Get the command id."""
|
||||
node = next(
|
||||
filter(lambda x: (x["name"] == name), nodes),
|
||||
None,
|
||||
)
|
||||
if not node:
|
||||
_LOGGER.warning("The Freebox Home device has no value for: %s", name)
|
||||
return None
|
||||
return node["id"]
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register state update callback."""
|
||||
self.remove_signal_update(
|
||||
async_dispatcher_connect(
|
||||
self._hass,
|
||||
self._router.signal_home_device_update,
|
||||
self.async_update_signal,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
"""When entity will be removed from hass."""
|
||||
self._remove_signal_update()
|
||||
|
||||
def remove_signal_update(self, dispacher: Any):
|
||||
"""Register state update callback."""
|
||||
self._remove_signal_update = dispacher
|
||||
|
||||
def get_value(self, ep_type, name):
|
||||
"""Get the value."""
|
||||
node = next(
|
||||
filter(
|
||||
lambda x: (x["name"] == name and x["ep_type"] == ep_type),
|
||||
self._node["show_endpoints"],
|
||||
),
|
||||
None,
|
||||
)
|
||||
if not node:
|
||||
_LOGGER.warning(
|
||||
"The Freebox Home device has no node for: " + ep_type + "/" + name
|
||||
)
|
||||
return None
|
||||
return node.get("value")
|
|
@ -3,6 +3,7 @@
|
|||
"name": "Freebox",
|
||||
"codeowners": ["@hacf-fr", "@Quentame"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["ffmpeg"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/freebox",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["freebox_api"],
|
||||
|
|
|
@ -4,14 +4,16 @@ from __future__ import annotations
|
|||
from collections.abc import Mapping
|
||||
from contextlib import suppress
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from freebox_api import Freepybox
|
||||
from freebox_api.api.call import Call
|
||||
from freebox_api.api.home import Home
|
||||
from freebox_api.api.wifi import Wifi
|
||||
from freebox_api.exceptions import NotOpenError
|
||||
from freebox_api.exceptions import HttpRequestError, NotOpenError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
|
@ -27,10 +29,13 @@ from .const import (
|
|||
APP_DESC,
|
||||
CONNECTION_SENSORS_KEYS,
|
||||
DOMAIN,
|
||||
HOME_COMPATIBLE_PLATFORMS,
|
||||
STORAGE_KEY,
|
||||
STORAGE_VERSION,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def get_api(hass: HomeAssistant, host: str) -> Freepybox:
|
||||
"""Get the Freebox API."""
|
||||
|
@ -70,11 +75,15 @@ class FreeboxRouter:
|
|||
self.sensors_temperature: dict[str, int] = {}
|
||||
self.sensors_connection: dict[str, float] = {}
|
||||
self.call_list: list[dict[str, Any]] = []
|
||||
self.home_granted = True
|
||||
self.home_devices: dict[str, Any] = {}
|
||||
self.listeners: list[dict[str, Any]] = []
|
||||
|
||||
async def update_all(self, now: datetime | None = None) -> None:
|
||||
"""Update all Freebox platforms."""
|
||||
await self.update_device_trackers()
|
||||
await self.update_sensors()
|
||||
await self.update_home_devices()
|
||||
|
||||
async def update_device_trackers(self) -> None:
|
||||
"""Update Freebox devices."""
|
||||
|
@ -146,6 +155,30 @@ class FreeboxRouter:
|
|||
for fbx_disk in fbx_disks:
|
||||
self.disks[fbx_disk["id"]] = fbx_disk
|
||||
|
||||
async def update_home_devices(self) -> None:
|
||||
"""Update Home devices (alarm, light, sensor, switch, remote ...)."""
|
||||
if not self.home_granted:
|
||||
return
|
||||
|
||||
try:
|
||||
home_nodes: list[Any] = await self.home.get_home_nodes() or []
|
||||
except HttpRequestError:
|
||||
self.home_granted = False
|
||||
_LOGGER.warning("Home access is not granted")
|
||||
return
|
||||
|
||||
new_device = False
|
||||
for home_node in home_nodes:
|
||||
if home_node["category"] in HOME_COMPATIBLE_PLATFORMS:
|
||||
if self.home_devices.get(home_node["id"]) is None:
|
||||
new_device = True
|
||||
self.home_devices[home_node["id"]] = home_node
|
||||
|
||||
async_dispatcher_send(self.hass, self.signal_home_device_update)
|
||||
|
||||
if new_device:
|
||||
async_dispatcher_send(self.hass, self.signal_home_device_new)
|
||||
|
||||
async def reboot(self) -> None:
|
||||
"""Reboot the Freebox."""
|
||||
await self._api.system.reboot()
|
||||
|
@ -172,6 +205,11 @@ class FreeboxRouter:
|
|||
"""Event specific per Freebox entry to signal new device."""
|
||||
return f"{DOMAIN}-{self._host}-device-new"
|
||||
|
||||
@property
|
||||
def signal_home_device_new(self) -> str:
|
||||
"""Event specific per Freebox entry to signal new home device."""
|
||||
return f"{DOMAIN}-{self._host}-home-device-new"
|
||||
|
||||
@property
|
||||
def signal_device_update(self) -> str:
|
||||
"""Event specific per Freebox entry to signal updates in devices."""
|
||||
|
@ -182,6 +220,11 @@ class FreeboxRouter:
|
|||
"""Event specific per Freebox entry to signal updates in sensors."""
|
||||
return f"{DOMAIN}-{self._host}-sensor-update"
|
||||
|
||||
@property
|
||||
def signal_home_device_update(self) -> str:
|
||||
"""Event specific per Freebox entry to signal update in home devices."""
|
||||
return f"{DOMAIN}-{self._host}-home-device-update"
|
||||
|
||||
@property
|
||||
def sensors(self) -> dict[str, Any]:
|
||||
"""Return sensors."""
|
||||
|
@ -196,3 +239,8 @@ class FreeboxRouter:
|
|||
def wifi(self) -> Wifi:
|
||||
"""Return the wifi."""
|
||||
return self._api.wifi
|
||||
|
||||
@property
|
||||
def home(self) -> Home:
|
||||
"""Return the home."""
|
||||
return self._api.home
|
||||
|
|
|
@ -113,6 +113,7 @@ class FreeboxSensor(SensorEntity):
|
|||
self.entity_description = description
|
||||
self._router = router
|
||||
self._attr_unique_id = f"{router.mac} {description.name}"
|
||||
self._attr_device_info = router.device_info
|
||||
|
||||
@callback
|
||||
def async_update_state(self) -> None:
|
||||
|
@ -123,11 +124,6 @@ class FreeboxSensor(SensorEntity):
|
|||
else:
|
||||
self._attr_native_value = state
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device information."""
|
||||
return self._router.device_info
|
||||
|
||||
@callback
|
||||
def async_on_demand_update(self):
|
||||
"""Update state."""
|
||||
|
@ -193,19 +189,18 @@ class FreeboxDiskSensor(FreeboxSensor):
|
|||
self._disk = disk
|
||||
self._partition = partition
|
||||
self._attr_name = f"{partition['label']} {description.name}"
|
||||
self._attr_unique_id = f"{self._router.mac} {description.key} {self._disk['id']} {self._partition['id']}"
|
||||
self._attr_unique_id = (
|
||||
f"{router.mac} {description.key} {disk['id']} {partition['id']}"
|
||||
)
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device information."""
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self._disk["id"])},
|
||||
model=self._disk["model"],
|
||||
name=f"Disk {self._disk['id']}",
|
||||
sw_version=self._disk["firmware"],
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, disk["id"])},
|
||||
model=disk["model"],
|
||||
name=f"Disk {disk['id']}",
|
||||
sw_version=disk["firmware"],
|
||||
via_device=(
|
||||
DOMAIN,
|
||||
self._router.mac,
|
||||
router.mac,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from homeassistant.helpers import device_registry as dr
|
|||
from .const import (
|
||||
DATA_CALL_GET_CALLS_LOG,
|
||||
DATA_CONNECTION_GET_STATUS,
|
||||
DATA_HOME_GET_NODES,
|
||||
DATA_LAN_GET_HOSTS_LIST,
|
||||
DATA_STORAGE_GET_DISKS,
|
||||
DATA_SYSTEM_GET_CONFIG,
|
||||
|
@ -55,6 +56,8 @@ def mock_router(mock_device_registry_devices):
|
|||
# sensor
|
||||
instance.call.get_calls_log = AsyncMock(return_value=DATA_CALL_GET_CALLS_LOG)
|
||||
instance.storage.get_disks = AsyncMock(return_value=DATA_STORAGE_GET_DISKS)
|
||||
# home devices
|
||||
instance.home.get_home_nodes = AsyncMock(return_value=DATA_HOME_GET_NODES)
|
||||
instance.connection.get_status = AsyncMock(
|
||||
return_value=DATA_CONNECTION_GET_STATUS
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue