More hue implementation

This commit is contained in:
Maximilian Giller 2025-01-20 01:10:30 +01:00
parent db5e826aea
commit c78546ffcf
9 changed files with 1422 additions and 29 deletions

View file

@ -5,7 +5,7 @@ import os
from statistics import median
from typing import Optional
import requests as r
from ...endpoints.hue import hue
from ...endpoints.hue import hue_bridge
import logging
file_path: str = "bettwaage.csv"
@ -126,9 +126,9 @@ def check_for_change():
# Make room sexy
if sexy_mode_detection:
if number_of_people >= 2 and weight_increased:
hue.in_room_activate_scene("Max Zimmer", "Sexy")
hue_bridge.in_room_activate_scene("Max Zimmer", "Sexy")
elif number_of_people == 1 and not weight_increased:
hue.in_room_activate_scene("Max Zimmer", "Tageslicht")
hue_bridge.in_room_activate_scene("Max Zimmer", "Tageslicht")
def add_line_to_bed_history(line: str) -> None:

View file

@ -1,5 +1,6 @@
import json
import logging
from core import Bridge
from core import Bridge, Group
from phue import Bridge as phueBridge
from time import sleep
@ -9,15 +10,15 @@ class HueBridge(Bridge):
def __init__(
self,
*,
ip_address: str,
id: str,
ip: str,
retry_limit: int = 10,
retry_timeout_seconds: int = 5,
) -> None:
super().__init__(id=id, type="hue")
self._retry_limit = retry_limit
self._retry_timeout_seconds = retry_timeout_seconds
self._hue: phueBridge = phueBridge(ip)
self._hue: phueBridge = phueBridge(ip_address)
def disconnect(self) -> None:
self._hue = None
@ -47,6 +48,10 @@ class HueBridge(Bridge):
def list_api(self) -> dict:
return self._hue.get_api()
def get_all_lights(self) -> Group:
light_states = await self.list_api()
# TODO
def list_scenes(self) -> dict:
return self._hue.get_scene()

View file

@ -1,4 +1,4 @@
from core import Entity
from core import Entity, Color
from bridges.hue import HueBridge
@ -8,6 +8,7 @@ class HueLight(Entity):
self,
*,
bridge: HueBridge,
initial_state: dict,
id: str,
name: str,
room: str,
@ -15,8 +16,24 @@ class HueLight(Entity):
) -> None:
super().__init__(id=id, name=name, room=room, groups=groups)
self._bridge: HueBridge = bridge
self._color: Color = Color()
self._on: bool = False
self._transition_duration_sec = 0
def poll_state(self):
self.__parse_state__(initial_state)
def __parse_state__(self, state: dict):
max_int_value = 255
self._on = state["state"]["on"]
h = state["state"]["hue"] / max_int_value
s = state["state"]["sat"] / max_int_value
v = state["state"]["bri"] / max_int_value
# TODO: Update color instead of overwriting it, to better keep track of change?
self._color = Color(hue=h, saturation=s, brightness=v)
def update(self):
# TODO
pass

View file

@ -1,5 +1,5 @@
class Color:
def __init__(self, *, hue: float, saturation: float, brightness: float):
self.hue = hue
self.saturation = saturation
self.brightness = brightness
self.hue: float = hue
self.saturation: float = saturation
self.brightness: float = brightness

View file

@ -1,4 +1,4 @@
from core import Color
from .color import Color
class EntityOpNotSupportedError(Exception):
@ -48,9 +48,9 @@ class Entity:
def __str__(self) -> str:
return f"{self.name} [{self.id}, type {self.device_type}, room {self.room}, in {len(self.groups)} groups]"
async def poll_state(self):
"""Implements an entity specific poll operation to get the latest state."""
raise EntityOpNotSupportedError("poll_state")
async def update(self):
"""Implements an entity specific update operation to get the latest state."""
raise EntityOpNotSupportedError("update")
async def toggle_state(self):
"""Turns entity on, if off, and vice versa, if supported."""

View file

@ -1,6 +1,43 @@
from core import Entity
from .entity import Entity, EntityOpNotSupportedError
class Group(Entity):
def __init__(self, *, id: str, name: str):
def __init__(
self,
*,
entities: list[Entity] = ...,
id: str = "group",
name: str = "Empty Group"
):
super().__init__(id=id, name=name, room=None, device_type="group")
self._entities: list[Entity] = entities
# List of method names to dynamically create
methods_to_create = [
"set_brightness",
"set_hue",
"set_saturation",
"set_color",
"set_transition_duration",
"turn_on",
"turn_off",
]
for method_name in methods_to_create:
setattr(self, method_name, self._create_group_method(method_name))
async def __call_method__(self, method_name: str, *args, **kwargs):
for entity in self._entities:
try:
func = getattr(entity, method_name)
await func(*args, **kwargs)
except EntityOpNotSupportedError:
pass
def _create_group_method(self, method_name: str):
# Create a method that calls __call_method__ for the given method name
async def group_method(*args, **kwargs):
await self.__call_method__(method_name, *args, **kwargs)
return group_method

View file

@ -1,4 +1,4 @@
from core import Group
from .group import Group
class Room(Group):

View file

@ -5,22 +5,23 @@ from bridges.hue import HueBridge, HueLight
from fastapi import APIRouter
from fastapi.responses import HTMLResponse
from core import Group
router = APIRouter(tags=["hue"])
hue = HueBridge("192.168.178.85")
lights: dict[int, HueLight] = {}
hue_bridge = HueBridge(ip_address="192.168.178.85", id="hue-bridge")
hue_lights: Group = Group()
poll_delay_sec = 5
async def hue_service():
global lights
global hue_lights
hue_lights = await hue_bridge.get_all_lights()
while True:
try:
for light in lights:
light.poll_state()
# TODO: Get all new lights
await hue_lights.update()
await asyncio.sleep(poll_delay_sec)
except:
@ -29,7 +30,7 @@ async def hue_service():
@router.get("/scenes", tags=["scene"])
async def get_scenes():
return hue.list_scenes()
return hue_bridge.list_scenes()
@router.post(
@ -38,7 +39,7 @@ async def get_scenes():
)
async def activate_scene(room_name: str, scene_name: str):
try:
hue.in_room_activate_scene(room_name, scene_name)
hue_bridge.in_room_activate_scene(room_name, scene_name)
except Exception as e:
return HTMLResponse(status_code=400, content=str(e))
@ -49,7 +50,7 @@ async def activate_scene(room_name: str, scene_name: str):
)
async def deactivate_room(room_name: str):
try:
hue.in_room_deactivate_lights(room_name)
hue_bridge.in_room_deactivate_lights(room_name)
except Exception as e:
return HTMLResponse(status_code=400, content=str(e))
@ -60,6 +61,6 @@ async def deactivate_room(room_name: str):
)
async def activate_room(room_name: str):
try:
hue.in_room_activate_lights(room_name)
hue_bridge.in_room_activate_lights(room_name)
except Exception as e:
return HTMLResponse(status_code=400, content=str(e))

1333
src/hue_api.json Normal file

File diff suppressed because it is too large Load diff