Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pydeconz/models/alarm_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Any, Literal, TypedDict

from . import ResourceGroup
from .api import APIItem
from .api import ApiData, APIItem

LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -106,7 +106,7 @@ class TypedAlarmSystemDevices(TypedDict):
trigger: str


class TypedAlarmSystem(TypedDict):
class TypedAlarmSystem(ApiData):
"""Alarm system type definition."""

name: str
Expand All @@ -115,7 +115,7 @@ class TypedAlarmSystem(TypedDict):
devices: dict[str, TypedAlarmSystemDevices]


class AlarmSystem(APIItem):
class AlarmSystem(APIItem[TypedAlarmSystem]):
"""deCONZ alarm system representation.

Dresden Elektroniks documentation of alarm systems in deCONZ
Expand Down
16 changes: 12 additions & 4 deletions pydeconz/models/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from collections.abc import Callable
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Generic, TypedDict, TypeVar

if TYPE_CHECKING:
from . import ResourceGroup
Expand All @@ -15,12 +15,19 @@
UnsubscribeType = Callable[[], None]


class APIItem:
class ApiData(TypedDict):
"""API data class."""


ApiDataT = TypeVar("ApiDataT", bound=ApiData)


class APIItem(Generic[ApiDataT]):
"""Base class for a deCONZ API item."""

resource_group: ResourceGroup

def __init__(self, resource_id: str, raw: Any) -> None:
def __init__(self, resource_id: str, raw: ApiDataT) -> None:
"""Initialize API item."""
self.resource_id = resource_id
self.raw = raw
Expand Down Expand Up @@ -57,7 +64,8 @@ def unsubscribe() -> None:

return unsubscribe

def update(self, raw: dict[str, dict[str, Any]]) -> None:
def update(self, raw: ApiDataT) -> None:
# def update(self, raw: dict[str, dict[str, Any]]) -> None:
"""Update input attr in self.

Store a set of keys with changed values.
Expand Down
60 changes: 44 additions & 16 deletions pydeconz/models/deconz_device.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,81 @@
"""Python library to connect deCONZ and Home Assistant to work together."""

from .api import APIItem
from typing import NotRequired, TypeVar

from .api import ApiData, APIItem

class DeconzDevice(APIItem):

class TypedData(ApiData):
"""Scene type definition."""

etag: NotRequired[str]
manufacturername: NotRequired[str]
modelid: NotRequired[str]
name: NotRequired[str]
swversion: NotRequired[str]
type: NotRequired[str]
uniqueid: NotRequired[str]


ApiDataT = TypeVar("ApiDataT", bound=TypedData)


class DeconzDevice(APIItem[ApiDataT]):
# class DeconzDevice(APIItem[ApiDataT], Generic[ApiDataT]):
"""deCONZ resource base representation.

Dresden Elektroniks REST API documentation
http://dresden-elektronik.github.io/deconz-rest-doc/
"""

raw: ApiDataT

@property
def etag(self) -> str:
"""HTTP etag change on any action to the device."""
raw: dict[str, str] = self.raw
return raw.get("etag") or ""
# raw: dict[str, str] = self.raw
if "etag" in self.raw:
return self.raw["etag"]
return ""

@property
def manufacturer(self) -> str:
"""Device manufacturer."""
raw: dict[str, str] = self.raw
return raw.get("manufacturername") or ""
if "manufacturername" in self.raw:
return self.raw["manufacturername"]
return ""

@property
def model_id(self) -> str:
"""Device model."""
raw: dict[str, str] = self.raw
return raw.get("modelid") or ""
if "modelid" in self.raw:
return self.raw["modelid"]
return ""

@property
def name(self) -> str:
"""Name of the device."""
raw: dict[str, str] = self.raw
return raw.get("name") or ""
if "name" in self.raw:
return self.raw["name"]
return ""

@property
def software_version(self) -> str:
"""Firmware version."""
raw: dict[str, str] = self.raw
return raw.get("swversion") or ""
if "swversion" in self.raw:
return self.raw["swversion"]
return ""

@property
def type(self) -> str:
"""Human readable type of the device."""
raw: dict[str, str] = self.raw
return raw.get("type") or ""
if "type" in self.raw:
return self.raw["type"]
return ""

@property
def unique_id(self) -> str:
"""Id for unique device identification."""
raw: dict[str, str] = self.raw
return raw.get("uniqueid") or ""
if "uniqueid" in self.raw:
return self.raw["uniqueid"]
return ""
33 changes: 21 additions & 12 deletions pydeconz/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Final, Literal, NotRequired, TypedDict

from . import ResourceGroup
from .deconz_device import DeconzDevice
from .deconz_device import DeconzDevice, TypedData
from .light.light import LightColorMode, LightEffect
from .scene import TypedScene

Expand All @@ -21,14 +21,25 @@
class TypedGroupAction(TypedDict):
"""Group action type definition."""

bri: int
colormode: NotRequired[Literal["ct", "hs", "xy"]]
ct: int
effect: NotRequired[Literal["colorloop", "none"]]
hue: int
bri: NotRequired[int]
colormode: NotRequired[
Literal[
LightColorMode.CT,
LightColorMode.HS,
LightColorMode.XY,
]
]
ct: NotRequired[int]
effect: NotRequired[
Literal[
LightEffect.COLOR_LOOP,
LightEffect.NONE,
]
]
hue: NotRequired[int]
on: bool
sat: int
xy: tuple[float, float]
sat: NotRequired[int]
xy: tuple[float, float] | tuple[None, None]


class TypedGroupState(TypedDict):
Expand All @@ -38,7 +49,7 @@ class TypedGroupState(TypedDict):
any_on: bool


class TypedGroup(TypedDict):
class TypedGroup(TypedData):
"""Group type definition."""

action: TypedGroupAction
Expand All @@ -48,13 +59,11 @@ class TypedGroup(TypedDict):
lights: list[str]
lightsequence: list[str]
multideviceids: list[str]
name: str
scenes: list[TypedScene]
state: TypedGroupState
type: str


class Group(DeconzDevice):
class Group(DeconzDevice[TypedGroup]):
"""deCONZ light group representation.

Dresden Elektroniks documentation of light groups in deCONZ
Expand Down
8 changes: 3 additions & 5 deletions pydeconz/models/scene.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
"""Python library to connect deCONZ and Home Assistant to work together."""

from typing import TypedDict

from . import ResourceGroup
from .api import APIItem
from .api import ApiData, APIItem


class TypedScene(TypedDict):
class TypedScene(ApiData):
"""Scene type definition."""

id: str
Expand All @@ -15,7 +13,7 @@ class TypedScene(TypedDict):
name: str


class Scene(APIItem):
class Scene(APIItem[ApiData]):
"""deCONZ scene representation.

Dresden Elektroniks documentation of scenes in deCONZ
Expand Down