oin/oin_ha/display/__init__.py

283 lines
8.4 KiB
Python
Raw Normal View History

2024-11-11 15:22:09 +01:00
import json
from threading import Timer
import bdfparser
2024-11-11 18:06:48 +01:00
from sense_hat import ACTION_HELD, ACTION_RELEASED, SenseHat
2024-11-11 15:22:09 +01:00
class Display:
def __init__(self, mqttc, uid, name):
self.mqttc = mqttc
self.sense = SenseHat()
self.uid = uid
self.name = name
self.init_lights()
self.mqttc.will_set(self.availability_topic, "offline", retain=True)
@property
def device(self):
return {
"identifiers": [self.uid],
"name": self.name,
}
@property
def availability_topic(self):
return f"{self.uid}/display/availability"
def init_lights(self):
options = {
"device": self.device,
"availability_topic": self.availability_topic,
}
self.main_light = Light(
"LED", self.uid, "led", self.sense, mqttc=self.mqttc, **options
)
2024-11-11 15:22:09 +01:00
def publish_discovery(self):
self.main_light.publish_discovery()
2024-11-11 15:22:09 +01:00
def publish_online(self):
self.subscribe()
self.mqttc.publish(self.availability_topic, "online", retain=True)
def subscribe(self):
self.main_light.subscribe()
2024-11-11 15:22:09 +01:00
def on_message(self, *args, **kwargs):
self.main_light.on_message(*args, **kwargs)
class Light:
_colors = {
"Bleu": [0, 0, 255],
"Blanc": [255, 255, 255],
"Rouge": [255, 0, 0],
"Verte": [0, 255, 0],
"Jaune": [255, 255, 0],
}
def __init__(self, name, parent_uid, slug, sense, mqttc, n=6, **kwargs):
2024-11-11 15:22:09 +01:00
self.name = name
self.parent_uid = parent_uid
self.slug = slug
self.sense = sense
self.mqttc = mqttc
2024-11-11 15:22:09 +01:00
self.options = kwargs
self._switch = False
2024-11-11 16:49:43 +01:00
self._color = [[255, 255, 255]] * n
self._pres = [""] * n
self._values = [""] * n
self._n = n
self._i = 0
2024-11-11 18:06:48 +01:00
self._toggled = False
2024-11-11 15:22:09 +01:00
self.font = bdfparser.Font("src/tom-thumb.bdf")
self.timer = Timer(0, self.__init__)
2024-11-11 16:49:43 +01:00
self.sense.stick.direction_right = self.switch_screen
self.sense.stick.direction_left = self.switch_screen_rev
self.sense.stick.direction_middle = self.toggle
2024-11-11 16:49:43 +01:00
def publish_discovery(self):
2024-11-11 16:49:43 +01:00
for i in range(self._n):
self.mqttc.publish(
2024-11-11 16:49:43 +01:00
self.get_discovery_topic(i),
json.dumps(
{
"command_topic": self.command_topic(i, "command"),
"effect_command_topic": self.command_topic(i, "effect"),
"effect_list": ["Low Light", "Normal"],
"effect_state_topic": self.state_topic,
2024-11-11 16:49:43 +01:00
"effect_value_template": "{{ value_json.effect }}",
"icon": "mdi:dots-grid",
"name": f"{self.name} {i}",
"on_command_type": "brightness",
2024-11-11 18:06:48 +01:00
"rgb_command_topic": self.command_topic(i, "rgb"),
2024-11-11 16:49:43 +01:00
"rgb_state_topic": self.state_topic,
"rgb_value_template": "{{" + f"value_json.rgb[{i}]" + "}}",
"retain": True,
"unique_id": f"{self.uid}_{i}",
"state_topic": self.state_topic,
2024-11-11 18:20:13 +01:00
"state_value_template": "{{" + f"value_json.state[{i}]" + "}}",
2024-11-11 16:49:43 +01:00
}
| self.options
),
retain=True,
)
self.publish_state()
2024-11-11 15:22:09 +01:00
def subscribe(self):
2024-11-11 16:49:43 +01:00
for i in range(self._n):
self.mqttc.subscribe(self.command_topic(i, "command"))
self.mqttc.subscribe(self.command_topic(i, "effect"))
self.mqttc.subscribe(self.command_topic(i, "rgb"))
self.mqttc.subscribe(self.command_topic(i, "value"))
self.mqttc.subscribe(self.command_topic(i, "action_color"))
2024-11-11 15:22:09 +01:00
def on_message(self, client, userdata, message):
data = message.payload.decode()
2024-11-11 16:49:43 +01:00
match message.topic.rsplit("/", maxsplit=2):
case [self.base_topic, i, "command"]:
2024-11-11 18:06:48 +01:00
self.switch = False
case [self.base_topic, i, "rgb"]:
2024-11-11 18:20:13 +01:00
self._i = int(i)
2024-11-11 18:06:48 +01:00
self.set_color(int(i), list(map(int, data.split(","))))
2024-11-11 16:49:43 +01:00
case [self.base_topic, i, "effect"]:
self.low_light = data == "Low Light"
2024-11-11 16:49:43 +01:00
case [self.base_topic, i, "value"]:
self.set_value(int(i), data)
case [self.base_topic, i, "action_color"]:
2024-11-11 18:20:13 +01:00
self.set_color(int(i), self._colors.get(data, [0, 0, 0]), switch=False)
2024-11-11 15:22:09 +01:00
case _:
return
def publish_state(self):
self.mqttc.publish(
2024-11-11 15:22:09 +01:00
self.state_topic,
json.dumps(
{
"effect": "Low Light" if self.low_light else "Normal",
2024-11-11 15:22:09 +01:00
"rgb": self.rgb,
2024-11-11 18:20:13 +01:00
"state": [
self.state if i == self._i else "OFF" for i in range(self._n)
],
2024-11-11 15:22:09 +01:00
}
),
retain=True,
)
@property
def uid(self):
return f"{self.parent_uid}_{self.slug}"
2024-11-11 16:49:43 +01:00
def get_discovery_topic(self, i):
return f"homeassistant/light/{self.uid}_{i}/config"
2024-11-11 15:22:09 +01:00
@property
def base_topic(self):
return f"{self.parent_uid}/display/{self.slug}"
2024-11-11 16:49:43 +01:00
def command_topic(self, i, cmd):
return f"{self.base_topic}/{i}/{cmd}"
2024-11-11 15:22:09 +01:00
@property
def state_topic(self):
return f"{self.parent_uid}/display/{self.slug}/state"
@property
def switch(self):
return self._switch
@switch.setter
def switch(self, value):
self._switch = value
if value:
self.update_value()
else:
self.sense.clear()
self.publish_state()
2024-11-11 15:22:09 +01:00
@property
def state(self):
return "ON" if self.switch else "OFF"
@property
def color(self):
return self._color
def set_color(self, i, value, switch=True):
2024-11-11 16:49:43 +01:00
self._color[i] = value
if switch and not self.switch:
2024-11-11 15:22:09 +01:00
self.switch = True
2024-11-11 18:20:13 +01:00
if switch:
self.update_value()
elif i == self._i:
2024-11-11 16:49:43 +01:00
self.display_value()
self.publish_state()
2024-11-11 15:22:09 +01:00
@property
def rgb(self):
2024-11-11 16:49:43 +01:00
return [",".join(map(str, self.color[i])) for i in range(self._n)]
2024-11-11 15:22:09 +01:00
@property
def value(self):
2024-11-11 16:49:43 +01:00
return f"{self._pres[self._i]} {self._values[self._i]}"
2024-11-11 15:22:09 +01:00
@property
def low_light(self):
return self.sense.low_light
@low_light.setter
def low_light(self, value):
self.sense.low_light = value
self.publish_state()
2024-11-11 16:49:43 +01:00
def set_value(self, i, value):
2024-11-11 15:22:09 +01:00
match value.split():
case [val]:
2024-11-11 16:49:43 +01:00
self._pres[i] = ""
self._values[i] = val
2024-11-11 15:22:09 +01:00
case [pre, val, *_]:
2024-11-11 16:49:43 +01:00
self._pres[i] = pre
self._values[i] = val
if i == self._i:
self.display_value()
self.publish_state()
2024-11-11 15:22:09 +01:00
def update_value(self):
if not self.switch:
return
2024-11-11 16:49:43 +01:00
if not self._pres[self._i]:
2024-11-11 15:22:09 +01:00
self.display_value()
return
2024-11-11 16:49:43 +01:00
pixels = self.to_pixels(self._pres[self._i])
2024-11-11 15:22:09 +01:00
self.sense.set_pixels(pixels)
self.timer = Timer(0.5, self.display_value, kwargs=dict(timer=True))
2024-11-11 15:22:09 +01:00
self.timer.start()
def display_value(self, timer=False):
if (not timer and self.timer.is_alive()) or not self.switch:
return
self.timer.cancel()
2024-11-11 15:22:09 +01:00
2024-11-11 16:49:43 +01:00
pixels = self.to_pixels(self._values[self._i])
2024-11-11 15:22:09 +01:00
self.sense.set_pixels(pixels)
def to_pixels(self, text):
if text:
return [
2024-11-11 16:49:43 +01:00
self.color[self._i] if x else [0, 0, 0]
2024-11-11 18:06:48 +01:00
for x in self.font.draw(text).crop(8, 8).todata(3)
2024-11-11 15:22:09 +01:00
]
2024-11-11 16:49:43 +01:00
return [self.color[self._i]] * 64
def switch_screen(self, event):
if event.action == ACTION_RELEASED:
self._i = (self._i + 1) % self._n
self.switch = True
2024-11-11 16:49:43 +01:00
def switch_screen_rev(self, event):
if event.action == ACTION_RELEASED:
self._i = (self._i - 1) % self._n
self.switch = True
def toggle(self, event):
2024-11-11 18:06:48 +01:00
if event.action == ACTION_HELD:
if not self._toggled:
self.low_light = not self.low_light
self._toggled = True
if event.action == ACTION_RELEASED:
2024-11-11 18:06:48 +01:00
if self._toggled:
self._toggled = False
else:
2024-11-11 18:06:48 +01:00
self.switch = not self.switch