Continued implementing hue bridge and first entities

This commit is contained in:
Maximilian Giller 2024-11-09 04:22:22 +01:00
parent b9d8c5312c
commit b3106e8bae
13 changed files with 187 additions and 10 deletions

View file

@ -27,7 +27,8 @@ class HueBridge(Bridge):
for _ in range(self._retry_limit): for _ in range(self._retry_limit):
try: try:
self._hue.connect() self._hue.connect()
break logging.info(f"Connected to Hue Bridge [{self.id}].")
return
except Exception as e: except Exception as e:
logging.exception( logging.exception(
f"Failed to connect to Hue bridge [ip {self._hue.ip}]. Retrying in [{self._retry_timeout_seconds}] seconds.", f"Failed to connect to Hue bridge [ip {self._hue.ip}]. Retrying in [{self._retry_timeout_seconds}] seconds.",
@ -35,7 +36,9 @@ class HueBridge(Bridge):
) )
sleep(self._retry_timeout_seconds) sleep(self._retry_timeout_seconds)
logging.info(f"Connected to Hue Bridge [{self.id}].") logging.error(
f"Unable to connect to Hue bridge [{self.id}]. Retry count exceeded [{self._retry_limit}]."
)
@property @property
def is_connected(self) -> bool | None: def is_connected(self) -> bool | None:
@ -54,6 +57,18 @@ class HueBridge(Bridge):
return scene return scene
return None return None
def set_light(self, lights, command):
return self._hue.set_light(lights, command)
def get_light(self, id, command=None):
return self._hue.get_light(id, command)
def set_group(self, groups, command):
return self._hue.set_group(groups, command)
def get_group(self, id, command=None):
return self._hue.get_group(id, command)
def in_room_activate_scene(self, room_name: str, scene_name: str): def in_room_activate_scene(self, room_name: str, scene_name: str):
"""Activate a scene in a room. """Activate a scene in a room.

View file

@ -0,0 +1,13 @@
from mash.core.entities.light import Light
from mash.core.utilities.glow import Glow
class HueLight(Light):
def __init__(
self, *, id: str, name: str, room: str, groups: list[str] = ...
) -> None:
super().__init__(id=id, name=name, room=room, groups=groups)
def __on_change__(self, current_on: bool, current_glow: Glow):
pass
# TODO: Requires reference to bridge

View file

@ -1,5 +1,3 @@
from .entities import *
from .bridge import Bridge, BridgeException from .bridge import Bridge, BridgeException
from .entity import Entity
from .group import Group
from .home import Home
from .feature import Feature from .feature import Feature

View file

@ -0,0 +1,6 @@
from .entity import Entity
from .group import Group
from .home import Home
from .contact_sensor import ContactSensor
from .light import Light
from .device_type import DeviceTypes

View file

@ -0,0 +1,21 @@
from mash.core.entities.device_type import DeviceType
from mash.core.entities.entity import Entity
class ContactSensor(Entity):
def __init__(
self, *, id: str, name: str, room: str, groups: list[str] = ...
) -> None:
super().__init__(
id=id,
name=name,
room=room,
device_type=DeviceType.CONTACT_SENSOR,
groups=groups,
)
self._has_contact: bool = False
def is_closed(self) -> bool:
return self._has_contact
# TODO: Update state

View file

@ -0,0 +1,6 @@
from enum import Enum
class DeviceType(Enum):
LIGHT = "light"
CONTACT_SENSOR = "contact_sensor"

View file

@ -1,6 +1,15 @@
from mash.core.entities.device_type import DeviceType
class Entity: class Entity:
def __init__( def __init__(
self, *, id: str, name: str, room: str, device_type: str, groups: list[str] = [] self,
*,
id: str,
name: str,
room: str,
device_type: DeviceType,
groups: list[str] = [],
) -> None: ) -> None:
self._id = id self._id = id
self._name = name self._name = name
@ -21,7 +30,7 @@ class Entity:
return self._room return self._room
@property @property
def device_type(self) -> str: def device_type(self) -> DeviceType:
return self._device_type return self._device_type
@property @property

View file

@ -1,4 +1,4 @@
from mash.core.entity import Entity from mash.core.entities.entity import Entity
from fnmatch import fnmatch from fnmatch import fnmatch

View file

@ -1,5 +1,5 @@
from mash.core.entity import Entity from mash.core.entities.entity import Entity
from mash.core.group import Group from mash.core.entities.group import Group
class Home(Group): class Home(Group):

View file

@ -0,0 +1,66 @@
from mash.core.entities.device_type import DeviceType
from mash.core.entities.entity import Entity
from mash.core.utilities.glow import Glow
from mash.core.utilities.validation import clip_int
class Light(Entity):
def __init__(
self, *, id: str, name: str, room: str, groups: list[str] = ...
) -> None:
super().__init__(
id=id, name=name, room=room, device_type=DeviceType.LIGHT, groups=groups
)
self._glow: Glow = Glow()
self._on: bool = False
def __on_change__(self, current_on: bool, current_glow: Glow):
pass
def __check_for_change__(self, obj, prop, new_value):
old_value = getattr(obj, prop)
if old_value == new_value:
return
setattr(obj, prop, new_value)
self.__on_change__(current_on=self.on, current_glow=self._glow)
@property
def on(self) -> bool:
"""True, if light is emitting glow. False, if light is off."""
return self._on
@on.setter
def on(self, value: bool):
"""True, if light is emitting glow. False, if light is off."""
self.__check_for_change__(self, "_on", value)
@property
def brightness(self) -> int:
"""Brightness in the range [0, 254]."""
return self._glow.brightness
@brightness.setter
def brightness(self, value: int):
"""Brightness in the range [0, 254]. Value will be clipped."""
self.__check_for_change__(self._glow, "brightness", clip_int(value, 0, 254))
@property
def saturation(self) -> int:
"""Saturation in the range [0, 254]."""
return self._glow.saturation
@saturation.setter
def saturation(self, value: int):
"""Saturation in the range [0, 254]. Value will be clipped."""
self.__check_for_change__(self._glow, "saturation", clip_int(value, 0, 254))
@property
def hue(self) -> int:
"""Hue in the range [0, 65535]."""
return self._glow.hue
@hue.setter
def hue(self, value: int):
"""Hue in the range [0, 65535]. Value will be clipped."""
self.__check_for_change__(self._glow, "hue", clip_int(value, 0, 65535))

View file

@ -0,0 +1,2 @@
from .glow import Glow
from .validation import clip_int

View file

@ -0,0 +1,39 @@
class Glow:
"""Concept of colored light-rays."""
def __init__(
self, *, brightness: int = 0, saturation: int = 0, hue: int = 0
) -> None:
self._brightness: int = brightness
self._saturation: int = saturation
self._hue: int = hue
@property
def brightness(self) -> int:
"""Brightness in the range [0, 254]."""
return self._brightness
@brightness.setter
def brightness(self, value: int):
"""Brightness in the range [0, 254]."""
self._brightness = value
@property
def saturation(self) -> int:
"""Saturation in the range [0, 254]."""
return self._saturation
@saturation.setter
def saturation(self, value: int):
"""Saturation in the range [0, 254]."""
self._saturation = value
@property
def hue(self) -> int:
"""Hue in the range [0, 65535]."""
return self._hue
@hue.setter
def hue(self, value: int):
"""Hue in the range [0, 65535]."""
self._hue = value

View file

@ -0,0 +1,2 @@
def clip_int(value, min_val: int, max_val: int) -> int:
return min([max([int(value), min_val]), max_val])