#!/usr/bin/python3 import datetime import os import shlex import subprocess import threading import time from copy import deepcopy import gpiozero import toml import neopixel import board SKRIPTPFAD = os.path.abspath(os.path.dirname(__file__)) def load_config(pfad=SKRIPTPFAD): configfile = os.path.join(pfad, "uhr_cfg.toml") with open(configfile) as conffile: config = toml.loads(conffile.read()) return config CONFIG = load_config() # LED strip configuration LED_COUNT = 60 # Number of LED pixels. LED_PIN = board.D18 # GPIO pin connected to the pixels (must support PWM!). LED_PIXEL_ORDER = neopixel.GRB # Strip type and colour ordering # Zahl stellt den Wert in Minuten dar, wie lange kein Gerät erreichbar sein darf dass die Uhr "abgeschalten" wird ABSCHALTWERT = 4 class Uhr: def __init__(self, pixels, mode): self.helligkeit = pixels.brightness self.pixels = pixels self.helligkeit_geaendert = None self.mode_geaendert = None self.mode: str = mode self._rgb_leer = None self._rgb_sekunde = None self._rgb_minute = None self._rgb_stunde = None self.led_gesetzt = 0 self.durchlauf_pause = datetime.timedelta(seconds=0) self.sleep_time = 0 def mode_wechseln(self): mode = int(self.mode) self.mode = mode def set_helligkeit(self, helligkeit): self.helligkeit = helligkeit self.pixels.brightness = self.helligkeit self.helligkeit_geaendert = datetime.datetime.now() def helligkeit_erhoehen(self): self.helligkeit += 0.05 if self.helligkeit > 1: self.helligkeit = 0 self.set_helligkeit(self.helligkeit) def helligkeit_verringern(self): self.helligkeit -= 0.05 if self.helligkeit < 0: self.helligkeit = 1 self.set_helligkeit(self.helligkeit) def rgb_farben_lesen(self): config = deepcopy(CONFIG) self._rgb_leer = config["mode"][self.mode]["leer"] self._rgb_sekunde = config["mode"][self.mode]["sekunde"] self._rgb_minute = config["mode"][self.mode]["minute"] self._rgb_stunde = config["mode"][self.mode]["stunde"] rgbconf = {"rgb_leer": self._rgb_leer, "rgb_s": self._rgb_sekunde, "rgb_min": self._rgb_minute, "rgb_std": self._rgb_stunde} return rgbconf def mode_control(self, zeit): rgbconf = self.rgb_farben_lesen() mode = int(self.mode) if mode == 0: stdliste = stunden_led_mapping_variante_0() stunden_leds, minuten_leds, sekunden_leds, leer_leds, sonstige_leds, rgbdict, \ self.led_gesetzt = stunde_minute_sekunde_einfach_modus(zeit, stdliste, rgbconf, self.led_gesetzt) self.durchlauf_pause = datetime.timedelta(seconds=1) self.sleep_time = 0.2 elif mode == 1: stdliste = stunden_led_mapping_variante_0() stunden_leds, minuten_leds, sekunden_leds, leer_leds, sonstige_leds, \ rgbdict = stunde_minute_dauerhaft_modus(zeit, stdliste, rgbconf) self.durchlauf_pause = datetime.timedelta(minutes=1) self.sleep_time = 0.5 else: stunden_leds = [0] minuten_leds = [0] sekunden_leds = [0] sonstige_leds = [0] leer_leds = [0] rgbdict = self.rgb_farben_lesen() led_setzen(stunden_leds, minuten_leds, sekunden_leds, leer_leds, sonstige_leds, rgbdict, self.pixels) # GPIO I_MODE_TASTER = gpiozero.Button(3) I_PLUS_TASTER = gpiozero.Button(4) I_MINUS_TASTER = gpiozero.Button(17) def config_schreiben(pfad=SKRIPTPFAD): configfile = os.path.join(pfad, "uhr_cfg.toml") with open(configfile, "w") as conffile: conffile.write(toml.dumps(CONFIG)) def alle_led(r, g, b, pixels): pixels.fill((r, g, b)) pixels.show() def stunden_led_mapping_variante_0(): std0 = [0, 1, 59] std1 = [4, 5, 6] std2 = [9, 10, 11] std3 = [14, 15, 16] std4 = [19, 20, 21] std5 = [24, 25, 26] std6 = [29, 30, 31] std7 = [34, 35, 36] std8 = [39, 40, 41] std9 = [44, 45, 46] std10 = [49, 50, 51] std11 = [54, 55, 56] stdliste = [std0, std1, std2, std3, std4, std5, std6, std7, std8, std9, std10, std11] return stdliste def stunden_index_mapping_ermitteln(zeit): if zeit.hour > 11: index = zeit.hour - 12 else: index = zeit.hour return index def stunde_minute_sekunde_einfach_modus(zeit, stdliste, rgbdict, led_gesetzt): index = stunden_index_mapping_ermitteln(zeit) stunden_leds = list(stdliste[index]) minuten_leds = [zeit.minute] sekunden_leds = [zeit.second] sonstige_leds = [] # Schnittfarben berechnen falls "Zeiger" übereinander liegen # Stunde mit Minute vergleichen if bool(set(stunden_leds) & set(minuten_leds)): for counter in range(0, 3): rgbdict["rgb_min"][counter] = int((rgbdict["rgb_std"][counter] + rgbdict["rgb_min"][counter]) / 2) # Stunde mit Sekunde vergleichen if set(stunden_leds) & set(sekunden_leds): for counter in range(0, 3): rgbdict["rgb_s"][counter] = int((rgbdict["rgb_std"][counter] + rgbdict["rgb_s"][counter]) / 2) # Minute mit Sekunde vergleichen if set(minuten_leds) & set(sekunden_leds): for counter in range(0, 3): rgbdict["rgb_s"][counter] = int((rgbdict["rgb_min"][counter] + rgbdict["rgb_s"][counter]) / 2) # Neue Leere LED berechnen led_gesetzt_neu = set(stunden_leds) | set(minuten_leds) | set(sekunden_leds) if isinstance(led_gesetzt, (int, float)): led_gesetzt = [led_gesetzt] leer_leds = set(led_gesetzt) - set(led_gesetzt_neu) return stunden_leds, minuten_leds, sekunden_leds, leer_leds, sonstige_leds, rgbdict, led_gesetzt_neu def stunde_minute_dauerhaft_modus(zeit, stdliste, rgbdict): sekunden_leds = [] index = stunden_index_mapping_ermitteln(zeit) stunden_leds = list(stdliste[index]) minuten_leds = [minute for minute in range(0, zeit.minute + 1)] if zeit.minute == 0: leer_leds = list(set(range(0, 60)) - set(stunden_leds) - set(minuten_leds)) else: leer_leds = [] if max(minuten_leds) < max(stunden_leds): sonstige_leds = list(set(stunden_leds) & set(minuten_leds)) minuten_leds = list(set(minuten_leds) - set(sonstige_leds)) else: sonstige_leds = [] minuten_leds = list(set(minuten_leds) - set(stunden_leds)) stunden_leds = list(set(stunden_leds) - set(sonstige_leds)) sonstige_leds = [(led, (0, 0, 255)) for led in sonstige_leds] return stunden_leds, minuten_leds, sekunden_leds, leer_leds, sonstige_leds, rgbdict def led_setzen(stunden_leds, minuten_leds, sekunden_leds, leer_leds, sonstige_leds, rgbdict, pixels): # Stunden setzen for stunde_led in stunden_leds: pixels[stunde_led] = rgbdict["rgb_std"][0], rgbdict["rgb_std"][1], rgbdict["rgb_std"][2] # Minute setzen for minute_led in minuten_leds: pixels[minute_led] = rgbdict["rgb_min"][0], rgbdict["rgb_min"][1], rgbdict["rgb_min"][2] # Sekunde setzen for sekunde_led in sekunden_leds: pixels[sekunde_led] = rgbdict["rgb_s"][0], rgbdict["rgb_s"][1], rgbdict["rgb_s"][2] # Leer setzen for leer_led in leer_leds: pixels[leer_led] = rgbdict["rgb_leer"][0], rgbdict["rgb_leer"][1], rgbdict["rgb_leer"][2] print(sonstige_leds) # Sonstige LEDs verarbeiten # Aufbau -> [index_lednummer, (R, G, B)] for led, rgb in sonstige_leds: print(f"LED: {led} RGB: {rgb}") pixels[led] = rgb[0], rgb[1], rgb[2] pixels.show() def shutdown(): cmd = "sudo shutdown now" cmd = shlex.split(cmd) subprocess.call(cmd) # Threads def check_anwesenheit(uhr, pixels): """Funktion, welche als eigener Thread laeuft, um selbststaendig in einem gewissenen Intervall alle Geraete in der Toml Liste zu pingen arg: Objekt des neopixel LED Ringes toml File: status "anwesend", ist kein Geraet von "anwesend" oder "dimmen" erreichbar, LED Helligkeit auf 0 sobald eine Adresse von status "dimmen" erreichbar ist, wird die Helligkeit verringert""" def ping_ping(ip): """pingt die IP 2x an return (0 | !0) 0 wenn erreichbar""" befehl = "ping -c2 -W1 {}".format(ip) cmd = shlex.split(befehl) return subprocess.call(cmd) def ping_bt(bt): """pingt die IP 2x an return (0 | !0) 0 wenn erreichbar""" befehl = "sudo /usr/bin/l2ping -c1 -t1 {}".format(bt) cmd = shlex.split(befehl) return subprocess.call(cmd) def ping_arping(ip, interface): """pingt die IP 3x an return (0 | !0) 0 wenn erreichbar """ befehl = "/usr/bin/arping -q -c 3 -w 10 -b -f -I {interface} {ip}".format(interface=interface, ip=ip) cmd = shlex.split(befehl) return subprocess.call(cmd) wlanliste = CONFIG["ping"] interface = CONFIG["interface"] status_anwesend_liste = [] delta = datetime.timedelta(seconds=301) last = datetime.datetime.now() status = {} while True: helligkeit = uhr.helligkeit now = datetime.datetime.now() # Status der Geräte ermitteln if delta.seconds > 300: for key_status in wlanliste.keys(): for key_funkart in wlanliste[key_status].keys(): for ip in wlanliste[key_status][key_funkart]: if key_funkart == "ping": status_return = ping_ping(ip) elif key_funkart == "bt": status_return = ping_bt(ip) elif key_funkart == "arping": status_return = ping_arping(ip, interface) else: status_return = False if not status_return: status[key_status] = True break else: status[key_status] = False if status["anwesend"]: status_anwesend_liste = [] # Geraet von anwesend erreichbar helligkeit = uhr.helligkeit elif not status["anwesend"] and not status["dimmen"]: # Wenn kein Geraet erreichbar ist if len(status_anwesend_liste) < ABSCHALTWERT + 1: status_anwesend_liste.append(status["anwesend"]) print("Nichts los daheim die ", len(status_anwesend_liste), ".") if len(status_anwesend_liste) > ABSCHALTWERT: helligkeit = 0 if status["dimmen"]: helligkeit = 0.03 if now.hour < 5 and not status["dimmen"]: helligkeit = 0 pixels.brightness = helligkeit last = datetime.datetime.now() delta = now - last time.sleep(30) def main(): letzter_durchlauf = datetime.datetime(1970, 1, 1) pixels = neopixel.NeoPixel(LED_PIN, LED_COUNT, brightness=CONFIG["led_helligkeit"], auto_write=False, pixel_order=LED_PIXEL_ORDER) uhr = Uhr(pixels, CONFIG["mode_nummer"]) thread_check_wlan = threading.Thread(target=check_anwesenheit, args=(uhr, pixels)) thread_check_wlan.start() rgbconf = uhr.rgb_farben_lesen() alle_led(rgbconf["rgb_leer"][0], rgbconf["rgb_leer"][1], rgbconf["rgb_leer"][2], pixels) I_PLUS_TASTER.when_pressed = uhr.helligkeit_erhoehen I_MINUS_TASTER.when_pressed = uhr.helligkeit_verringern try: while True: now = datetime.datetime.now() if (now - letzter_durchlauf) > uhr.durchlauf_pause: uhr.mode_control(now) letzter_durchlauf = now if uhr.helligkeit_geaendert is not None: if (datetime.datetime.now() - uhr.helligkeit_geaendert) > datetime.timedelta(seconds=30): CONFIG["led_helligkeit"] = uhr.helligkeit config_schreiben() uhr.helligkeit_geaendert = None time.sleep(uhr.sleep_time) finally: pixels.brightness = 0 alle_led(0, 0, 0, pixels) if __name__ == "__main__": main()