Compare commits

..

10 Commits

@ -0,0 +1,10 @@
# Uhr
## Benötigte Software
apt install iputils-arping python3-systemd
Pythonmodule
sudo pip3 install toml gpiozero adafruit-circuitpython-neopixel

@ -0,0 +1,81 @@
#!/usr/bin/python3
"""
Beschreibung:
Hilfskript zum Erstellen einer logging Instanz in Abhängigkeit, ob das Skript manuell gestartet worden ist oder
ob das Skript per Service Unit gestartet wurde
Abhängig dessen wird entweder die Logging Instanz mit einem JournalHandler erstellt (Service Unit)
oder mit einem StreamHandler (manuell)
Author: Hofei
Datum: 03.08.2018
Version: 0.1
"""
import logging
import os
import shlex
import subprocess
from systemd import journal
def __setup_logging(loglevel, frm, startmethode, unitname):
"""
Erstellt die Logger Instanz für das Skript
"""
logger = logging.getLogger()
logger.setLevel(loglevel)
logger.handlers = []
if startmethode == "auto":
log_handler = journal.JournalHandler(SYSLOG_IDENTIFIER=unitname)
else:
log_handler = logging.StreamHandler()
log_handler.setLevel(loglevel)
log_handler.setFormatter(frm)
logger.addHandler(log_handler)
return logger
def __get_service_unit_pid(unitname):
"""Ermittelt ob das ausführende Skript mit einer Service Unit gestartet worden ist, wenn ja so ist das
Ergebnis (pid_service_unit) != 0"""
cmd = "systemctl show -p MainPID {}".format(unitname)
cmd = shlex.split(cmd)
antwort = subprocess.run(cmd, stdout=subprocess.PIPE)
ausgabe = antwort.stdout
# strip entfernt \n, split teilt am = in eine Liste und [1] weißt die Zahl in die Variable zu
pid_service_unit = int(ausgabe.decode().strip().split("=")[1])
return pid_service_unit
def __get_startmethode(unitname):
"""Verglicht die PID vom skript mit der pid Service Unit Prüfung
wenn die Nummern gleich sind wird auf auto gestellt, wenn nicht auf manuell"""
pid_service_unit = __get_service_unit_pid(unitname)
pid_skript = os.getpid()
if pid_service_unit == pid_skript:
startmethode = "auto"
else:
startmethode = "manuell"
return startmethode
def __set_loggerformat(startmethode):
"""Stellt die passende Formattierung ein"""
if startmethode == "auto":
frm = logging.Formatter("%(levelname)s: %(message)s", "%d.%m.%Y %H:%M:%S")
else:
frm = logging.Formatter("%(asctime)s %(levelname)s: %(message)s", "%d.%m.%Y %H:%M:%S")
return frm
def create_logger(unitname, loglevel):
"""Dies ist die aufzurufende Funktion bei der Verwendung des Moduls von außen
Liefert die fertige Logging Instanz zurück"""
startmethode = __get_startmethode(unitname)
frm = __set_loggerformat(startmethode)
return __setup_logging(loglevel, frm, startmethode, unitname)
if __name__ == "__main__":
logger = create_logger("testunit", 10)
logger.debug("Testnachricht")

366
uhr.py

@ -4,61 +4,146 @@ import datetime
import os
import shlex
import subprocess
import sys
import threading
import time
from copy import deepcopy
import gpiozero
import toml
import neopixel
import board
import setup_logging
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()
LOGGER = setup_logging.create_logger("uhr", CONFIG["loglevel"])
# LED strip configuration
LED_COUNT = 60 # Number of LED pixels.
LED_PIN = board.D18 # GPIO pin connected to the pixels (must support PWM!).
LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
LED_PIXEL_ORDER = neopixel.GRB # Strip type and colour ordering
LED_BRIGHTNESS = 0.2
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
# LED Mapping
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]
# # # # # # # # # #
# GPIO:
# # # # # # # # # #
class Uhr:
def __init__(self, pixels, mode):
self.helligkeit = pixels.brightness
self.pixels = pixels
self.helligkeit_geaendert = None
self.mode_geaendert = None
self.mode = 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)
mode_anzahl = len(CONFIG["mode"]) - 1
if mode_anzahl < 0:
mode_anzahl = 0
mode += 1
if mode > mode_anzahl:
mode = 0
self.mode = str(mode)
now = datetime.datetime.now()
self.mode_geaendert = now
alle_led(0, 0, 0, self.pixels)
self.mode_control(now)
LOGGER.info(f"Mode geändert auf: {self.mode} um {self.mode_geaendert}Uhr")
def set_helligkeit(self, helligkeit):
self.helligkeit = helligkeit
self.pixels.brightness = self.helligkeit
self.helligkeit_geaendert = datetime.datetime.now()
LOGGER.info(f"Helligkeit geändert auf: {self.helligkeit} um {self.helligkeit_geaendert}Uhr")
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(seconds=1)
self.sleep_time = 0.5
elif mode == 2:
stunden_leds, minuten_leds, sekunden_leds, leer_leds, sonstige_leds, \
rgbdict = uhr_aus_modus(rgbconf)
self.durchlauf_pause = datetime.timedelta(minutes=1)
self.sleep_time = 2
else:
stunden_leds = [0]
minuten_leds = [0]
sekunden_leds = [0]
sonstige_leds = [0]
leer_leds = [0]
rgbdict = self.rgb_farben_lesen()
LOGGER.debug(f"stunden_leds: {stunden_leds}\n"
f"minuten_leds: {minuten_leds}\n"
f"sekunden_leds: {sekunden_leds}\n"
f"leer_leds: {leer_leds}\n"
f"sonstige_leds: {sonstige_leds}\n"
f"rgbdict: {rgbdict}")
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(14)
I_MINUS_TASTER = gpiozero.Button(17)
# # # # # # # # # #
# Funktionen
# # # # # # # # # #
# Farbkonfiguration
def rgb_standard():
rgb_leer = [0, 0, 0]
rgb_s = [0, 180, 0]
rgb_min = [32, 178, 170]
rgb_std = [255, 0, 255]
rgbconf = {"rgb_leer": rgb_leer, "rgb_s": rgb_s, "rgb_min": rgb_min, "rgb_std": rgb_std}
return rgbconf
def config_schreiben(pfad=SKRIPTPFAD):
configfile = os.path.join(pfad, "uhr_cfg.toml")
with open(configfile, "w") as conffile:
conffile.write(toml.dumps(CONFIG))
LOGGER.info(f"Schreibe Config: {CONFIG}")
def alle_led(r, g, b, pixels):
@ -66,54 +151,119 @@ def alle_led(r, g, b, pixels):
pixels.show()
def led_calc(zeit, stdliste, rgbdict, led_gesetzt, pixels):
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
hour = stdliste[index]
minute = zeit.minute
second = zeit.second
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(hour) & {minute}):
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(hour) & {second}:
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 {minute} & {second}:
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)
led_gesetzt = led_setzen(hour, minute, second, rgbdict, led_gesetzt, pixels)
return zeit.second, led_gesetzt
def led_setzen(hour, minute, second, rgbdict, led_gesetzt, pixels):
if not isinstance(led_gesetzt, list):
# 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 uhr_aus_modus(rgbdict):
sekunden_leds = []
minuten_leds = []
stunden_leds = []
sonstige_leds = []
leer_leds = [led for led in range(0, 60)]
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 in hour:
pixels[stunde] = rgbdict["rgb_std"][0], rgbdict["rgb_std"][1], rgbdict["rgb_std"][2]
for stunde_led in stunden_leds:
pixels[stunde_led] = rgbdict["rgb_std"][0], rgbdict["rgb_std"][1], rgbdict["rgb_std"][2]
# Minute setzen
pixels[minute] = rgbdict["rgb_min"][0], rgbdict["rgb_min"][1], rgbdict["rgb_min"][2]
for minute_led in minuten_leds:
pixels[minute_led] = rgbdict["rgb_min"][0], rgbdict["rgb_min"][1], rgbdict["rgb_min"][2]
# Sekunde setzen
pixels[second] = rgbdict["rgb_s"][0], rgbdict["rgb_s"][1], rgbdict["rgb_s"][2]
for sekunde_led in sekunden_leds:
pixels[sekunde_led] = rgbdict["rgb_s"][0], rgbdict["rgb_s"][1], rgbdict["rgb_s"][2]
# Neue Leere LED berechnen
led_gesetzt_neu = set(hour) | {minute} | {second}
leerliste = list(set(led_gesetzt) - led_gesetzt_neu)
for leer in leerliste:
pixels[leer] = rgbdict["rgb_leer"][0], rgbdict["rgb_leer"][1], rgbdict["rgb_leer"][2]
# Leer setzen
for leer_led in leer_leds:
pixels[leer_led] = rgbdict["rgb_leer"][0], rgbdict["rgb_leer"][1], rgbdict["rgb_leer"][2]
# Sonstige LEDs verarbeiten
# Aufbau -> [index_lednummer, (R, G, B)]
for led, rgb in sonstige_leds:
pixels[led] = rgb[0], rgb[1], rgb[2]
pixels.show()
return list(led_gesetzt_neu)
def shutdown():
@ -123,46 +273,58 @@ def shutdown():
# Threads
def check_anwesenheit(pixels):
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_wlan(ip):
def ping_ping(ip):
"""pingt die IP 2x an
return (0 | !0) 0 wenn erreichbar"""
befehl = "ping -c2 -W1 {}".format(ip)
LOGGER.debug(f"Pinge: {befehl}")
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)
LOGGER.debug(f"Pinge: {befehl}")
cmd = shlex.split(befehl)
return subprocess.call(cmd)
# Tomlfile mit den IP Adressen einlesen
pfad = os.path.abspath(os.path.dirname(__file__))
configfile = os.path.join(pfad, "bt_wlan.toml")
with open(configfile) as conffile:
wlanliste = toml.loads(conffile.read())
status_anwesend_liste = []
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)
LOGGER.debug(f"Pinge: {befehl}")
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 IP Adressen ermitteln
# 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 == "wlan":
status_return = ping_wlan(ip)
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:
@ -172,15 +334,15 @@ def check_anwesenheit(pixels):
status[key_status] = False
if status["anwesend"]:
status_anwesend_liste = [] # Geraet von anwesend erreichbar
helligkeit = LED_BRIGHTNESS
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
LOGGER.debug(f"Nichts los daheim die {len(status_anwesend_liste)}.")
if len(status_anwesend_liste) > ABSCHALTWERT:
helligkeit = 0
if status["dimmen"]:
helligkeit = LED_BRIGHTNESS * 0.5
helligkeit = 0.03
if now.hour < 5 and not status["dimmen"]:
helligkeit = 0
pixels.brightness = helligkeit
@ -190,27 +352,41 @@ def check_anwesenheit(pixels):
def main():
lastsecond = None
led_gesetzt = 0
rgbconf = rgb_standard()
pixels = neopixel.NeoPixel(LED_PIN, LED_COUNT, brightness=LED_BRIGHTNESS,
auto_write=False, pixel_order=LED_PIXEL_ORDER)
letzter_durchlauf = datetime.datetime(1970, 1, 1)
thread_check_wlan = threading.Thread(target=check_anwesenheit, args=(pixels,))
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)
while True:
try:
rgbconf = rgb_standard()
I_PLUS_TASTER.when_pressed = uhr.helligkeit_erhoehen
I_MINUS_TASTER.when_pressed = uhr.helligkeit_verringern
I_MODE_TASTER.when_pressed = uhr.mode_wechseln
try:
while True:
now = datetime.datetime.now()
if lastsecond != now.second:
lastsecond, led_gesetzt = led_calc(now, stdliste, rgbconf, led_gesetzt, pixels)
time.sleep(0.2)
except KeyboardInterrupt:
print("KeyboardInterrupt")
pixels.brightness = 0
alle_led(0, 0, 0, pixels)
sys.exit()
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
if uhr.mode_geaendert is not None:
if (datetime.datetime.now() - uhr.mode_geaendert) > datetime.timedelta(seconds=30):
CONFIG["mode_nummer"] = uhr.mode
config_schreiben()
uhr.mode_geaendert = None
time.sleep(uhr.sleep_time)
finally:
pixels.brightness = 0
alle_led(0, 0, 0, pixels)
if __name__ == "__main__":

@ -0,0 +1,10 @@
# /etc/systemd/system/uhr.service
[Unit]
Description=Uhr starten
[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/pi/uhr/uhr.py
[Install]
WantedBy=multi-user.target

@ -0,0 +1,43 @@
interface = "wlan0"
led_helligkeit = 0.05
mode_nummer = "1"
loglevel = 10 # (10=Debug, 20=Info, 30=Warning, 40=Error, 50=Critical)
[mode]
# RGB Farben Definition in den Listen
# Index 0 = Rot
# Index 1 = Grün
# Index 2 = Blau
# Stunden Minuten und Sekundenzeiger einzeln
[mode.0]
leer = [0, 0, 0]
sekunde = [0, 180, 0]
minute = [32, 178, 170]
stunde = [255, 0, 255]
# Stunden und Minutenzeiger dauerhaft
[mode.1]
leer = [0, 0, 0]
sekunde = []
minute = [0, 255, 0]
stunde = [255, 0, 0]
# Uhr aus
[mode.2]
leer = [0, 0, 0]
sekunde = [0, 0, 0]
minute = [0, 0, 0]
stunde = [0, 0, 0]
[ping]
# Passende IP Adressen eintragen
[ping.anwesend]
ping = []
bt = []
arping = []
[ping.dimmen]
ping = []
bt = []
arping = []
Loading…
Cancel
Save