Skip to content
Merged
9 changes: 7 additions & 2 deletions homeassistant/components/sunricher_dali/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import asyncio
import logging

from PySrDaliGateway import DaliGateway
Expand All @@ -22,7 +23,7 @@
from .const import CONF_SERIAL_NUMBER, DOMAIN, MANUFACTURER
from .types import DaliCenterConfigEntry, DaliCenterData

_PLATFORMS: list[Platform] = [Platform.LIGHT]
_PLATFORMS: list[Platform] = [Platform.LIGHT, Platform.SCENE]
_LOGGER = logging.getLogger(__name__)


Expand All @@ -47,7 +48,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: DaliCenterConfigEntry) -
) from exc

try:
devices = await gateway.discover_devices()
devices, scenes = await asyncio.gather(
gateway.discover_devices(),
gateway.discover_scenes(),
)
except DaliGatewayError as exc:
raise ConfigEntryNotReady(
"Unable to discover devices from the gateway"
Expand All @@ -68,6 +72,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: DaliCenterConfigEntry) -
entry.runtime_data = DaliCenterData(
gateway=gateway,
devices=devices,
scenes=scenes,
)
await hass.config_entries.async_forward_entry_setups(entry, _PLATFORMS)

Expand Down
53 changes: 53 additions & 0 deletions homeassistant/components/sunricher_dali/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Base entity for Sunricher DALI integration."""

from __future__ import annotations

import logging

from PySrDaliGateway import CallbackEventType, DaliObjectBase, Device

Check failure on line 7 in homeassistant/components/sunricher_dali/entity.py

View workflow job for this annotation

GitHub Actions / Check pylint

E0611: No name 'DaliObjectBase' in module 'PySrDaliGateway' (no-name-in-module)

Check failure on line 7 in homeassistant/components/sunricher_dali/entity.py

View workflow job for this annotation

GitHub Actions / Check mypy

Module "PySrDaliGateway" has no attribute "DaliObjectBase" [attr-defined]

from homeassistant.core import callback
from homeassistant.helpers.entity import Entity

_LOGGER = logging.getLogger(__name__)


class DaliCenterEntity(Entity):
"""Base entity for DALI Center objects (devices, scenes, etc.)."""

_attr_has_entity_name = True
_attr_should_poll = False

def __init__(self, dali_object: DaliObjectBase) -> None:
"""Initialize base entity."""
self._dali_object = dali_object
self._attr_unique_id = dali_object.unique_id
self._unavailable_logged = False
# Set initial availability from status if available (Device has it, Scene doesn't)
if isinstance(dali_object, Device):
self._attr_available = dali_object.status == "online"
else:
self._attr_available = True
Comment thread
niracler marked this conversation as resolved.
Outdated

async def async_added_to_hass(self) -> None:
"""Register availability listener."""
self.async_on_remove(
self._dali_object.register_listener(
CallbackEventType.ONLINE_STATUS,
self._handle_availability,
)
)

@callback
def _handle_availability(self, available: bool) -> None:
"""Handle availability changes."""
# Log availability transitions
if not available and not self._unavailable_logged:
_LOGGER.info("Entity %s became unavailable", self.entity_id)
self._unavailable_logged = True
elif available and self._unavailable_logged:
_LOGGER.info("Entity %s is back online", self.entity_id)
self._unavailable_logged = False

self._attr_available = available
self.schedule_update_ha_state()
27 changes: 4 additions & 23 deletions homeassistant/components/sunricher_dali/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN, MANUFACTURER
from .entity import DaliCenterEntity
from .types import DaliCenterConfigEntry

_LOGGER = logging.getLogger(__name__)
Expand All @@ -45,10 +46,9 @@ async def async_setup_entry(
)


class DaliCenterLight(LightEntity):
class DaliCenterLight(DaliCenterEntity, LightEntity):
"""Representation of a Sunricher DALI Light."""

_attr_has_entity_name = True
_attr_name = None
_attr_is_on: bool | None = None
_attr_brightness: int | None = None
Expand All @@ -60,11 +60,8 @@ class DaliCenterLight(LightEntity):

def __init__(self, light: Device) -> None:
"""Initialize the light entity."""

super().__init__(light)
self._light = light
self._unavailable_logged = False
self._attr_unique_id = light.unique_id
self._attr_available = light.status == "online"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, light.dev_id)},
name=light.name,
Expand Down Expand Up @@ -111,34 +108,18 @@ async def async_turn_off(self, **kwargs: Any) -> None:

async def async_added_to_hass(self) -> None:
"""Handle entity addition to Home Assistant."""
await super().async_added_to_hass()

self.async_on_remove(
self._light.register_listener(
CallbackEventType.LIGHT_STATUS, self._handle_device_update
)
)

self.async_on_remove(
self._light.register_listener(
CallbackEventType.ONLINE_STATUS, self._handle_availability
)
)

# read_status() only queues a request on the gateway and relies on the
# current event loop via call_later, so it must run in the loop thread.
self._light.read_status()

@callback
def _handle_availability(self, available: bool) -> None:
self._attr_available = available
if not available and not self._unavailable_logged:
_LOGGER.info("Light %s became unavailable", self._attr_unique_id)
self._unavailable_logged = True
elif available and self._unavailable_logged:
_LOGGER.info("Light %s is back online", self._attr_unique_id)
self._unavailable_logged = False
self.schedule_update_ha_state()

@callback
def _handle_device_update(self, status: LightStatus) -> None:
if status.get("is_on") is not None:
Expand Down
64 changes: 64 additions & 0 deletions homeassistant/components/sunricher_dali/scene.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Support for DALI Center Scene entities."""

import logging
from typing import Any

from propcache.api import cached_property
from PySrDaliGateway import Scene

from homeassistant.components.scene import Scene as SceneEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN
from .entity import DaliCenterEntity
from .types import DaliCenterConfigEntry

_LOGGER = logging.getLogger(__name__)

PARALLEL_UPDATES = 1


async def async_setup_entry(
hass: HomeAssistant,
entry: DaliCenterConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up DALI Center scene entities from config entry."""
async_add_entities(DaliCenterScene(scene) for scene in entry.runtime_data.scenes)


class DaliCenterScene(DaliCenterEntity, SceneEntity):
"""Representation of a DALI Center Scene."""

def __init__(self, scene: Scene) -> None:
"""Initialize the DALI scene."""
super().__init__(scene)
self._scene = scene
self._attr_name = scene.name
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, scene.gw_sn)},
)

@cached_property
def extra_state_attributes(self) -> dict[str, list[str]]:
"""Return the scene state attributes."""
ent_reg = er.async_get(self.hass)
return {
"entity_id": [
entity_id
for device in self._scene.devices

Check failure on line 52 in homeassistant/components/sunricher_dali/scene.py

View workflow job for this annotation

GitHub Actions / Check mypy

"Scene" has no attribute "devices" [attr-defined]
if (
entity_id := ent_reg.async_get_entity_id(
"light", DOMAIN, device["unique_id"]
)
)
]
}

async def async_activate(self, **kwargs: Any) -> None:
"""Activate the DALI scene."""
_LOGGER.debug("Activating scene: %s", self._attr_name)
await self.hass.async_add_executor_job(self._scene.activate)
3 changes: 2 additions & 1 deletion homeassistant/components/sunricher_dali/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dataclasses import dataclass

from PySrDaliGateway import DaliGateway, Device
from PySrDaliGateway import DaliGateway, Device, Scene

from homeassistant.config_entries import ConfigEntry

Expand All @@ -13,6 +13,7 @@ class DaliCenterData:

gateway: DaliGateway
devices: list[Device]
scenes: list[Scene]


type DaliCenterConfigEntry = ConfigEntry[DaliCenterData]
Loading
Loading