Add device info to fibaro integration (#73352)
parent
73a0197cac
commit
7eae3691c2
|
@ -29,8 +29,9 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util import slugify
|
||||
|
||||
|
@ -156,16 +157,21 @@ class FibaroController:
|
|||
) # List of devices by entity platform
|
||||
self._callbacks: dict[Any, Any] = {} # Update value callbacks by deviceId
|
||||
self._state_handler = None # Fiblary's StateHandler object
|
||||
self.hub_serial = None # Unique serial number of the hub
|
||||
self.name = None # The friendly name of the hub
|
||||
self.hub_serial: str # Unique serial number of the hub
|
||||
self.hub_name: str # The friendly name of the hub
|
||||
self.hub_software_version: str
|
||||
self.hub_api_url: str = config[CONF_URL]
|
||||
# Device infos by fibaro device id
|
||||
self._device_infos: dict[int, DeviceInfo] = {}
|
||||
|
||||
def connect(self):
|
||||
"""Start the communication with the Fibaro controller."""
|
||||
try:
|
||||
login = self._client.login.get()
|
||||
info = self._client.info.get()
|
||||
self.hub_serial = slugify(info.serialNumber)
|
||||
self.name = slugify(info.hcName)
|
||||
self.hub_serial = info.serialNumber
|
||||
self.hub_name = info.hcName
|
||||
self.hub_software_version = info.softVersion
|
||||
except AssertionError:
|
||||
_LOGGER.error("Can't connect to Fibaro HC. Please check URL")
|
||||
return False
|
||||
|
@ -305,6 +311,44 @@ class FibaroController:
|
|||
platform = Platform.LIGHT
|
||||
return platform
|
||||
|
||||
def _create_device_info(self, device: Any, devices: list) -> None:
|
||||
"""Create the device info. Unrooted entities are directly shown below the home center."""
|
||||
|
||||
# The home center is always id 1 (z-wave primary controller)
|
||||
if "parentId" not in device or device.parentId <= 1:
|
||||
return
|
||||
|
||||
master_entity: Any | None = None
|
||||
if device.parentId == 1:
|
||||
master_entity = device
|
||||
else:
|
||||
for parent in devices:
|
||||
if "id" in parent and parent.id == device.parentId:
|
||||
master_entity = parent
|
||||
if master_entity is None:
|
||||
_LOGGER.error("Parent with id %s not found", device.parentId)
|
||||
return
|
||||
|
||||
if "zwaveCompany" in master_entity.properties:
|
||||
manufacturer = master_entity.properties.zwaveCompany
|
||||
else:
|
||||
manufacturer = "Unknown"
|
||||
|
||||
self._device_infos[master_entity.id] = DeviceInfo(
|
||||
identifiers={(DOMAIN, master_entity.id)},
|
||||
manufacturer=manufacturer,
|
||||
name=master_entity.name,
|
||||
via_device=(DOMAIN, self.hub_serial),
|
||||
)
|
||||
|
||||
def get_device_info(self, device: Any) -> DeviceInfo:
|
||||
"""Get the device info by fibaro device id."""
|
||||
if device.id in self._device_infos:
|
||||
return self._device_infos[device.id]
|
||||
if "parentId" in device and device.parentId in self._device_infos:
|
||||
return self._device_infos[device.parentId]
|
||||
return DeviceInfo(identifiers={(DOMAIN, self.hub_serial)})
|
||||
|
||||
def _read_scenes(self):
|
||||
scenes = self._client.scenes.list()
|
||||
self._scene_map = {}
|
||||
|
@ -321,14 +365,14 @@ class FibaroController:
|
|||
device.ha_id = (
|
||||
f"scene_{slugify(room_name)}_{slugify(device.name)}_{device.id}"
|
||||
)
|
||||
device.unique_id_str = f"{self.hub_serial}.scene.{device.id}"
|
||||
device.unique_id_str = f"{slugify(self.hub_serial)}.scene.{device.id}"
|
||||
self._scene_map[device.id] = device
|
||||
self.fibaro_devices[Platform.SCENE].append(device)
|
||||
_LOGGER.debug("%s scene -> %s", device.ha_id, device)
|
||||
|
||||
def _read_devices(self):
|
||||
"""Read and process the device list."""
|
||||
devices = self._client.devices.list()
|
||||
devices = list(self._client.devices.list())
|
||||
self._device_map = {}
|
||||
last_climate_parent = None
|
||||
last_endpoint = None
|
||||
|
@ -355,7 +399,8 @@ class FibaroController:
|
|||
device.mapped_platform = None
|
||||
if (platform := device.mapped_platform) is None:
|
||||
continue
|
||||
device.unique_id_str = f"{self.hub_serial}.{device.id}"
|
||||
device.unique_id_str = f"{slugify(self.hub_serial)}.{device.id}"
|
||||
self._create_device_info(device, devices)
|
||||
self._device_map[device.id] = device
|
||||
_LOGGER.debug(
|
||||
"%s (%s, %s) -> %s %s",
|
||||
|
@ -462,6 +507,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
for platform in PLATFORMS:
|
||||
devices[platform] = [*controller.fibaro_devices[platform]]
|
||||
|
||||
# register the hub device info separately as the hub has sometimes no entities
|
||||
device_registry = dr.async_get(hass)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, controller.hub_serial)},
|
||||
manufacturer="Fibaro",
|
||||
name=controller.hub_name,
|
||||
model=controller.hub_serial,
|
||||
sw_version=controller.hub_software_version,
|
||||
configuration_url=controller.hub_api_url.removesuffix("/api/"),
|
||||
)
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
controller.enable_state_handler()
|
||||
|
@ -490,6 +547,7 @@ class FibaroDevice(Entity):
|
|||
self.ha_id = fibaro_device.ha_id
|
||||
self._attr_name = fibaro_device.friendly_name
|
||||
self._attr_unique_id = fibaro_device.unique_id_str
|
||||
self._attr_device_info = self.controller.get_device_info(fibaro_device)
|
||||
# propagate hidden attribute set in fibaro home center to HA
|
||||
if "visible" in fibaro_device and fibaro_device.visible is False:
|
||||
self._attr_entity_registry_visible_default = False
|
||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
import logging
|
||||
from typing import Any
|
||||
|
||||
from slugify import slugify
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
|
@ -44,9 +45,12 @@ async def _validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str
|
|||
_LOGGER.debug(
|
||||
"Successfully connected to fibaro home center %s with name %s",
|
||||
controller.hub_serial,
|
||||
controller.name,
|
||||
controller.hub_name,
|
||||
)
|
||||
return {"serial_number": controller.hub_serial, "name": controller.name}
|
||||
return {
|
||||
"serial_number": slugify(controller.hub_serial),
|
||||
"name": controller.hub_name,
|
||||
}
|
||||
|
||||
|
||||
class FibaroConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
|
|
@ -7,6 +7,7 @@ from homeassistant.components.scene import Scene
|
|||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
|
@ -33,6 +34,15 @@ async def async_setup_entry(
|
|||
class FibaroScene(FibaroDevice, Scene):
|
||||
"""Representation of a Fibaro scene entity."""
|
||||
|
||||
def __init__(self, fibaro_device: Any) -> None:
|
||||
"""Initialize the Fibaro scene."""
|
||||
super().__init__(fibaro_device)
|
||||
|
||||
# All scenes are shown on hub device
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self.controller.hub_serial)}
|
||||
)
|
||||
|
||||
def activate(self, **kwargs: Any) -> None:
|
||||
"""Activate the scene."""
|
||||
self.fibaro_device.start()
|
||||
|
|
|
@ -14,17 +14,23 @@ TEST_NAME = "my_fibaro_home_center"
|
|||
TEST_URL = "http://192.168.1.1/api/"
|
||||
TEST_USERNAME = "user"
|
||||
TEST_PASSWORD = "password"
|
||||
TEST_VERSION = "4.360"
|
||||
|
||||
|
||||
@pytest.fixture(name="fibaro_client", autouse=True)
|
||||
def fibaro_client_fixture():
|
||||
"""Mock common methods and attributes of fibaro client."""
|
||||
info_mock = Mock()
|
||||
info_mock.get.return_value = Mock(serialNumber=TEST_SERIALNUMBER, hcName=TEST_NAME)
|
||||
info_mock.get.return_value = Mock(
|
||||
serialNumber=TEST_SERIALNUMBER, hcName=TEST_NAME, softVersion=TEST_VERSION
|
||||
)
|
||||
|
||||
array_mock = Mock()
|
||||
array_mock.list.return_value = []
|
||||
|
||||
client_mock = Mock()
|
||||
client_mock.base_url.return_value = TEST_URL
|
||||
|
||||
with patch("fiblary3.client.v4.client.Client.__init__", return_value=None,), patch(
|
||||
"fiblary3.client.v4.client.Client.info",
|
||||
info_mock,
|
||||
|
@ -37,6 +43,10 @@ def fibaro_client_fixture():
|
|||
"fiblary3.client.v4.client.Client.scenes",
|
||||
array_mock,
|
||||
create=True,
|
||||
), patch(
|
||||
"fiblary3.client.v4.client.Client.client",
|
||||
client_mock,
|
||||
create=True,
|
||||
):
|
||||
yield
|
||||
|
||||
|
|
Loading…
Reference in New Issue