import logging import math from threading import Thread, Timer import bdfparser from sense_hat.sense_hat import SenseHat from sense_hat.stick import InputEvent logger = logging.getLogger(__name__) COLORS = { "Bleu": (0, 0, 255), "Blanc": (255, 255, 255), "Rouge": (255, 0, 0), "Verte": (0, 127, 31), "Jaune": (255, 255, 0), "heat": (255, 0, 0), "heating": (255, 0, 0), "idle": (127, 0, 255), "off": (127, 127, 127), "on_setting": (255, 255, 0), "off_setting": (255, 255, 255), None: (0, 0, 0), } class Screen: def __init__(self) -> None: self.sense = SenseHat() self._value = "" self._tmp = False self._tmp_value = "" self._mode = "" self.font = bdfparser.Font("src/tom-thumb.bdf") self._secondary: dict[int, str] = dict() self._secondary_pixels: list[tuple[int, int, int]] = [(0, 0, 0)] * 8 self.timer = Timer(0, self.set_pixels) self.auto_dim = AutoDim(self.sense) self.auto_dim.start() self._held = False self.sense.stick.direction_middle = self.stick_click self.sense.stick.direction_any = self.auto_dim.undim @property def value(self) -> None | str: return self._value @value.setter def value(self, value: float) -> None: logger.debug(f"Updated value: <{value}>") self._value = format_value(value) if not self._tmp: self.set_pixels() @property def color(self) -> tuple[int, int, int]: return COLORS.get(self.mode, (0, 0, 0)) @property def mode(self) -> str: return self._mode @mode.setter def mode(self, value: str) -> None: self._mode = value if not self._tmp: self.set_pixels() @property def tmp_value(self) -> None | str: return self._tmp_value @tmp_value.setter def tmp_value(self, value: float) -> None: logger.debug(f"Show value: <{value}>") self.timer.cancel() self._tmp_value = format_value(value) self.show_tmp() def show_tmp(self) -> None: self._tmp = True self.set_pixels( self.tmp_value, color=COLORS.get("off_setting" if self.mode == "off" else "on_setting"), ) self.timer = Timer(3, self.set_pixels) self.timer.start() def set_pixels( self, value: str | None = None, color: tuple[int, int, int] | None = None, bg_color: tuple[int, int, int] = (0, 0, 0), ) -> None: if value is None: value = self.value self._tmp = False if color is None: color = self.color if value: pixels = [color if x else bg_color for x in self.data_from_value(value)] else: pixels = 48 * [(0, 0, 0)] pixels += self.secondary_pixels self.sense.set_pixels(pixels) @property def secondary(self) -> dict[int, str]: return self._secondary @secondary.setter def secondary(self, value: dict[int, str]) -> None: self._secondary = value for idx in range(2): self._secondary_pixels[4 * idx : 4 * (idx + 1)] = [ COLORS.get(value.get(idx, None), (0, 0, 0)) ] * 4 if not self._tmp: self.set_pixels() @property def secondary_pixels(self) -> list[tuple[int, int, int]]: return self._secondary_pixels def stick_click(self, event: InputEvent) -> None: match (event.action, self._held): case ("held", False): self._held = True case ("released", True): self._held = False case ("released", False): self.show_tmp() case _: pass def data_from_value(self, value: str) -> list[int]: return self.font.draw(value, mode=0).crop(8, 7).todata(3) class AutoDim(Thread): def __init__(self, sense: SenseHat): super().__init__() self.daemon = True self.sense = sense self.dim = False self.switching = False self.sense.gamma_reset() def run(self) -> None: while True: self.auto_dim() def auto_dim(self) -> None: accel_z = self.sense.get_accelerometer_raw()["z"] if not self.switching and accel_z < 0.2: self.switching = True self.dim = not self.dim elif self.switching and accel_z > 0.9: self.switching = False @property def dim(self) -> bool: return self._dim @dim.setter def dim(self, value: bool) -> None: if value: self.sense.gamma = [0] * 32 else: self.sense.gamma_reset() self._dim = value def undim(self) -> None: self.dim = False def format_value(value: float) -> str: v = math.trunc(value) d = "." if (value - v) >= 0.5 else "" return f"{v}{d}"