Hacked by CoupDeGrace
Greetz:
Hmei7, BrokenPipe, SimSimi, L4663r666h05t, AntonKil, d3b~x, Index Php, Mdn_Newbie, Sultan Haikal, Brian Kamikaze
TODO
Wer kennt nicht das Problem: man meint es gut und möchte lüften und dann vergisst man das offene Fenster. Kein Problem bei hohen Plus-Graden aber was ist im Winter bei Minus-Graden und laufender Heizung. Dies ist nicht nur eine Umweltsünde sondern macht die Wohnung auch ganz schön ungemütlich.
Aus diesem Grund habe ich mir die Zeit genommen um die bereits vorhandenen Fenstersensoren in ein Programm einzubinden, welches den Nutzer (also mich und alle im Haushalt) nach einer gewissen Zeit und vor allem periodisch über offene Fenster zu benachrichtigen.
Bridge openweathermap:weather-api:weather "OpenWeatherMap Account"
[
apikey="...",
refreshInterval=10,
language="de"
]
{
Thing weather-and-forecast local "Lokales Wetter"
[
location="48.118436,11.618119",
forecastHours=0,
forecastDays=0
]
Thing onecall-history local "Lokale Historie"
[
location="48.118436,11.618119",
forecastHours=0,
forecastDays=0
]
Thing onecall local "Lokales Wetter 2"
[
location="48.118436,11.618119",
forecastHours=0,
forecastDays=0
]
}
Switch contactWatchState "Fensterüberwachung" <window>
Switch contactWatchReset "Zurücksetzen" <button>
Number contactWatchTriggerTimer "Alarmtimer" <time>
Number contactWatchAlertSleep "Sleep between alerts in s" <time>
Number:Temperature contactWatchTriggerTemperature "Schwelle Außentemperatur [%.1f °C]" <temperatur>
Number:Temperature currentLocalTemperature "Aktuelle Temperatur [%.1f %unit%]" <temperature>
{
channel="openweathermap:weather-and-forecast:weather:local:current#temperature"
}
Group ContactWatch "Gruppe zu überwachender Fenster"
Switch contactSimulation "Fenstersimulation" <window> (ContactWatch)
import java.util.Map
import java.time.Duration
val String loggerName = "ContactWatch"
var Map<String, ZonedDateTime> contactRegistry = newHashMap
var Map<String, ZonedDateTime> lastAlerts = newHashMap
rule "Register contact that got opened"
when
Member of ContactWatch changed from CLOSED to OPEN or
Member of ContactWatch changed from OFF to ON
then
contactRegistry.put(triggeringItemName, new DateTimeType().getZonedDateTime())
lastAlerts.remove(triggeringItemName)
logError(loggerName, "Contacts {} now {}. {} contacts registered", triggeringItemName, newState, contactRegistry.size())
end
rule "Unregister contact that got closed"
when
Member of ContactWatch changed from OPEN to CLOSED or
Member of ContactWatch changed from ON to OFF
then
contactRegistry.remove(triggeringItemName)
lastAlerts.remove(triggeringItemName)
logError(loggerName, "Contacts {} now {}. {} contacts registered", triggeringItemName, newState, contactRegistry.size())
end
rule "Reset contacts and system"
when
Item contactWatchReset changed from OFF to ON
then
logWarn(loggerName, "Reset requested. Will now reboot Max Cube and reset contacts")
contactRegistry.clear()
lastAlerts.clear()
val maxCubeActions = getActions("max-cube", "max:bridge:cube")
val Boolean success = maxCubeActions.reboot()
logWarn(loggerName, "Reset requested with result {}", success)
contactWatchReset.postUpdate(OFF)
end
rule "Check and alert opened contact state"
when
Time cron "*/5 * * * * ?"
then
val ZonedDateTime now = new DateTimeType().getZonedDateTime()
val Integer triggerMinutes = (contactWatchTriggerTimer.state as Number)
val Integer sleepSeconds = (contactWatchAlertSleep.state as Number)
val Double triggerTemperature = (contactWatchTriggerTemperature.state as QuantityType<Number>).doubleValue
val Double currentTemperature = (currentLocalTemperature.state as QuantityType<Number>).doubleValue
val Boolean active = (contactWatchState.state == ON)
logDebug(loggerName, "state={}; currentTemp={}; triggerTemp={}", active, currentTemperature, triggerTemperature)
if (active && currentTemperature <=triggerTemperature)
{
for (Map.Entry<String, DateTimeType> openContact : contactRegistry.entrySet())
{
val Integer openSinceMinutes = Duration.between(openContact.getValue(), now).getSeconds() / 60
logDebug(loggerName, "triggerMinutes={}; openSinceMinutes={}", triggerMinutes, openSinceMinutes)
if (openSinceMinutes >= triggerMinutes)
{
var Boolean triggerAlert = true
val lastAlert = lastAlerts.get(openContact.getKey())
if (lastAlert !== null)
{
val Integer lastAlertBehindSeconds = Duration.between(lastAlert, now).getSeconds()
if (lastAlertBehindSeconds < sleepSeconds)
triggerAlert = false
}
if (triggerAlert)
{
lastAlerts.remove(openContact.getKey())
lastAlerts.put(openContact.getKey(), now)
val String msg = "Kontakt " + openContact.getKey() + " offen seit " + openSinceMinutes + " Minuten."
sendNotification("michael@klemm-dachau.de", msg)
logWarn(loggerName, msg)
}
}
else
{
lastAlerts.remove(openContact.getKey())
}
}
}
end

Ja ich weiß, ein Cluster besteht aus vielen Nodes. Nichtsdestotrotz ist das Konzept eines Clusters wirklich gut, so dass ich dies auch gerne auf einem einzigen Raspberry (aka Node) aufsetzte. Wer will kann das natürlich auch beliebig erweitern. Zunächst geht es mir dabei aber darum die Funktionsweise und die vorhandenen OpenSource-Werkzeuge vorzustellen.
Stacks, Applications und Services anlegen und verwalten

Anforderungen
Warum keine Fertiglösung?
Lessons learned:
CACHE_DISK=/dev/disk/by-id/usb-SanDisk_SSD_PLUS_480GB_152D00539000-0:3
DATA_DISK_1=/dev/disk/by-id/usb-SAMSUNG_HD154UI_152D00539000-0:0
DATA_DISK_2=/dev/disk/by-id/usb-SAMSUNG_HD154UI_152D00539000-0:1
DATA_DISK_3=/dev/disk/by-id/usb-SAMSUNG_HD154UI_152D00539000-0:2
DATA_RAID_LABEL=raid-data
DATA_RAID_NODE=/dev/md/${DATA_RAID_LABEL}
VOLUME_GROUP=vg1
LOGICAL_VOLUME_PREFIX=lv_data1
CACHE_MODE=writeback
hdparm -W1 ${CACHE_DISK}
hdparm -W0 ${DATA_DISK_1}
hdparm -W0 ${DATA_DISK_2}
hdparm -W0 ${DATA_DISK_3}
mdadm --create ${DATA_RAID_LABEL} --level=5 --raid-devices=3 ${DATA_DISK_1} ${DATA_DISK_2} ${DATA_DISK_3}
mdadm --detail --scan ${DATA_RAID_NODE} >> /etc/mdadm.conf
pvcreate ${DATA_RAID_NODE}
pvcreate ${CACHE_DISK} # is this required?
vgcreate ${VOLUME_GROUP} "${DATA_RAID_NODE}" "${CACHE_DISK}"
lvcreate --size 1500G --name ${LOGICAL_VOLUME_PREFIX} ${VOLUME_GROUP} "${DATA_RAID_NODE}"
lvcreate -L 450G --name ${LOGICAL_VOLUME_PREFIX}_cache ${VOLUME_GROUP} "${CACHE_DISK}"
lvcreate -L 1.6G --name ${LOGICAL_VOLUME_PREFIX}_cache_meta ${VOLUME_GROUP} "${CACHE_DISK}"
lvconvert \
--type cache-pool \
--cachemode ${CACHE_MODE} \
--poolmetadata ${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}_cache_meta \
${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}_cache
lvconvert \
--type cache \
--cachepool ${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}_cache \
${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}
CACHE_DISK=/dev/sde
DATA_DISK_1=/dev/sdd
VOLUME_GROUP=vg1
LOGICAL_VOLUME_PREFIX=lv_data1
CACHE_MODE=writeback
hdparm -W1 ${CACHE_DISK}
hdparm -W0 ${DATA_DISK_1}
pvcreate ${DATA_DISK_1}
pvcreate ${CACHE_DISK} # is this required?
vgcreate ${VOLUME_GROUP} "${DATA_DISK_1}" "${CACHE_DISK}"
lvcreate -L 1400G -n ${LOGICAL_VOLUME_PREFIX} ${VOLUME_GROUP} "${DATA_DISK_1}"
lvcreate -L 450G -n ${LOGICAL_VOLUME_PREFIX}_cache ${VOLUME_GROUP} "${CACHE_DISK}"
lvcreate -L 1.6G -n ${LOGICAL_VOLUME_PREFIX}_cache_meta ${VOLUME_GROUP} "${CACHE_DISK}"
lvconvert \
--type cache-pool \
--cachemode ${CACHE_MODE} \
--poolmetadata ${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}_cache_meta \
${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}_cache
lvconvert \
--type cache \
--cachepool ${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}_cache \
${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}
fdisk /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME_PREFIX} <<EOF
g
n
1
w
EOF
partprobe /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME_PREFIX}
mkfs.ext4 /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME_PREFIX}p1
mount /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME_PREFIX}p1 /mnt/data
dd
fdisk /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME_PREFIX} <<EOF
g
w
EOF
partprobe /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME_PREFIX}
lvconvert --splitcache ${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}
lvremove ${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}
lvremove ${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}_cache
lvremove ${VOLUME_GROUP}/${LOGICAL_VOLUME_PREFIX}_cache_meta
vgremove ${VOLUME_GROUP}
pvremove ${DATA_RAID_NODE}
pvremove ${CACHE_DISK}
Raspberry
# Create large/slow data volume on mechanical disk
pvcreate /dev/sdb
vgcreate vg-data /dev/sdb
lvcreate --name data -l 100%FREE vg-data /dev/sdb
mkfs.ext4 /dev/vg-data/data
mount /dev/vg-data/data /media/data/
# Create smaller/fast volume to store random access data (e.g. mysql DB and docker volumes)
pvcreate /dev/sda
vgextend vg-data /dev/sda
lvcreate -L 100G -n docker vg-data /dev/sda
mkfs.ext4 /dev/vg-data/docker
# Use the remaining free disk space on fast volume to create disk cache for large volume
lvcreate --name data-cache --type cache-pool -l 100%FREE vg-data /dev/sda
lvconvert --type cache --cache-pool data-cache --cachemode writeback vg-data/data
Aktivieren / Deaktivieren
lvconvert --splitcache vg-data/data lvconvert --type cache --cachemode writethrough --cachepool cache vg-data/data
Ein energieeffizientes, zuverlässiges System, das:
| Komponente | Funktion |
|---|---|
| ATtiny4313 | Steuereinheit mit Zustandsspeicher, UART-Kommunikation, Alarmlogik |
| Raspberry Pi | Hauptrechner für Netzwerk, Monitoring, Logging etc. |
| Power Switch | MOSFET oder elektronischer Schalter zur Versorgung des Pi |
| UART (TTL) | Kommunikation ATtiny ↔ Raspberry Pi (TX/RX, 5 V TTL) |
| Taster & Sensoren | Eingaben zur Modusumschaltung & Alarmüberwachung |
| 12 V → 5 V Buck | Versorgung von ATtiny & ggf. Pi via Wandler |
| Verbindung | Spannung | Pegelkompatibel? |
|---|---|---|
| ATtiny4313 Vcc | 5 V (vom Buck-Regler) | ✅ Ja |
| UART zu Raspberry Pi | 5 V TTL → 3.3 V Pi RX | ⚠️ Pegelwandler empfohlen |
| UART vom Pi TX → Tiny RX | 3.3 V → 5 V AVR RX | ✅ meistens stabil |
Empfehlung: Pegelwandler oder Spannungsteiler für ATtiny TX → Pi RX.
| ATtiny Pin | Funktion | Richtung | Kommentar |
|---|---|---|---|
| PD0 | UART RX | IN | Vom Pi |
| PD1 | UART TX | OUT | Zum Pi (5 V → 3.3 V Wandler nötig) |
| PD2 | Taster | IN | Zur Modusumschaltung |
| PD3 | Alarm Input (Schleife) | IN | Überwacht Fenster/Türen |
| PD4 | Pi Power Switch | OUT | Steuert MOSFET |
| PD5 | Status LED (optional) | OUT | Optional für Feedback |
| RESET | Programmierbar | – | ISP nötig |
| Vcc/GND | Stromversorgung | – | 5 V vom Buck |
stateDiagram-v2
[*] --> Init
Init --> Periodic : Timerstart
Init --> Permanent : Taster (lang)
Init --> Alarm : Taster (doppelklick) oder EEPROM
Periodic --> Shutdown : Timer abgelaufen
Permanent --> Shutdown : Kommando vom Pi
Alarm --> Shutdown : Alarm quittiert & Pi gibt frei
Alarm --> AlarmTrigger : Alarmkontakt geöffnet
AlarmTrigger --> Shutdown : Meldung an Pi gesendet
Shutdown --> [*] : Power-Off Pi
| Zustand | Beschreibung | Übergänge |
|---|---|---|
| Init | Wird beim Start des ATtiny gesetzt | Übergang über Taster oder gespeicherte Info |
| Periodic | Startet Pi nach Timer-Intervall, fährt nach getaner Arbeit wieder runter | → Shutdown nach Timer oder Pi-Freigabe |
| Permanent | Pi bleibt dauerhaft an, manuelle Steuerung möglich | → Shutdown durch expliziten Befehl vom Pi |
| Alarm | Alarmaktiv: ATtiny überwacht Sensoren | → AlarmTrigger bei Öffnung |
| AlarmTrigger | Alarm ausgelöst, Pi wird hochgefahren | → Shutdown durch Pi nach Quittierung |
| Shutdown | Pi wird heruntergefahren, Strom wird getrennt | → [*] |
| Pin | Funktion |
|---|---|
| RESET | Reset |
| MOSI | Daten in |
| MISO | Daten out |
| SCK | Takt |
| VCC | 5 V |
| GND | Masse |
Programmierbar z. B. über USBasp oder Arduino as ISP.
Pi → ATtiny:
STATUS=OK → LebenszeichenSHUTDOWN → Fahr herunterMODE? → Fragt den aktuellen Modus abATtiny → Pi:
MODE=PERIODIC|PERMANENT|ALARMALARM=TRIGGEREDSHUTDOWN → Power-Off in x Sekunden/dev/serial0STATUS regelmäßigWenn du möchtest, kann ich dir im nächsten Schritt liefern:
Sag mir einfach, womit du weitermachen willst.
Ziel: Extrem Energieeffizienter Betrieb eines Raspberry Pi im Wohnwagen inkl. zyklischem Start, Alarmüberwachung. Übertragung Systemstatus und Alarme an Backend/Smartphone über Wi-Fi oder LTE.
| Komponente | Modell/Typ | Funktion |
|---|---|---|
| Mikrocontroller | ATtiny4313 (DIL) | Zentrale Steuerung |
| RTC-Modul | DS3231 | Zeitgeber für Zyklusstart |
| Taster | Digital, mit Pull-Up | Start-/Shutdown-Steuerung |
| 2 x Alarmkontakt | Reed-Schalter o.Ä. | Fenster-/Türüberwachung |
| LED | 3.3 V | Statusblinker alle 10 s wenn Armed. Wenn Alarm flackern. Ansonsten aus. |
| UART | TX/RX auf 3.3 V | Kommunikation mit Raspberry Pi |
| Alarmausgang | GPIO an Treiber mit 12v schaltausgang | Anschluss von Verbraucher wie zum Beispiel Sirene |
| Schaltausgang | GPIO an Treiber der 12 V auf einen 5 V 5A DC-DC Converter bringt. | Stromzufuhr für Raspberry und weitere 5 V Verbraucher |

| Arduino PIN | Physischer Pin | Pin | Funktion | Richtung | Beschreibung |
|---|---|---|---|---|---|
| 1 | PA2 | Reset | Eingang | Für ISP | |
| 0 | 2 | PD0 | UART RX | Eingang | Kommunikation RPi → ATtiny |
| 1 | 3 | PD1 | UART TX | Ausgang | Kommunikation ATtiny → RPi |
| 4 | PA1 | ||||
| 5 | PA0 | ||||
| 2 | 6 | PD2 | INT0 (RTC Alarm) | Eingang | Weckt ATtiny |
| 3 | 7 | PD3 | Taster | Eingang | Kurz-/Lang-Druck |
| 4 | 8 | PD4 | I²C SDA | I²C | RTC-Kommunikation |
| 5 | 9 | PD5 | I²C SCL | I²C | RTC-Kommunikation |
| 10 | GND | Ground | |||
| 6 | 11 | PD6 | Status-LED | Ausgang | Blinkt alle 30s |
| 7 | 12 | PB0 | RPi Power Enable | Ausgang | Steuert MOSFET mit 12V der über Buck-Converter RPi versorgt |
| 8 | 13 | PB1 | Alarmkontakt 1 | Eingang | Fenster-/Türkontakt (Hüllschutz) |
| 9 | 14 | PB2 | Alarmkontakt 2 | Eingang | zusatzkontakte wie fliegengitter falls Fenster offen. |
| 10 | 15 | PB3 | Alarmausgang | Ausgang | Ansteuerung Verbraucher über 12 V Treiber (z.B. Sirene) |
| 11 | 16 | PB4 | |||
| 12 | 17 | PB5 | MOSI | Eingang | ISP |
| 13 | 18 | PB6 | MISO | Ausgang | ISP |
| 14 | 19 | PB7 | SCL | Eingang | ISP |
| 20 | VCC | Versorgung | Eingang | am 3,3 V Buck-Converter |
Arduino Main
ATmega328P hat 3 PCINT Gruppen jeweils für die Bänke B, D und D:
| Vektor | Pin-Gruppe | Register | Pins |
|---|---|---|---|
| PCINT0_vect | PORTB | PCMSK0 | PB0–PB7 (D8–D13 + SPI) |
| PCINT1_vect | PORTC | PCMSK1 | PC0–PC5 (A0–A5) |
| PCINT2_vect | PORTD | PCMSK2 | PD0–PD7 (D0–D7) |
Wichtige Kriterien für die Wahl der Pin:
| Use-Case | Status ✅ / ⚠️ / ❌ | Kommentar |
|---|---|---|
| 2× Taster | ✅ | D3 (INT1) & D5 (PCINT2) |
| 1× Status-LED | ✅ | A0 / PC0 |
| RTC via I²C | ✅ | PC4/PC5 (A4/A5) |
| RTC Wakeup (Interrupt) | ✅ | PD2 (INT0) |
| MPU via I²C | ✅ | PC4/PC5 (A4/A5) |
| MPU Interrupt | ✅ | PD4 (PCINT2) |
| Raspberry Power Switch | ✅ | PB6 (XTAL1) |
| Alarmgeber Schaltausgang | ✅ | PB7 (XTAL2) |
| RFID via SPI | ✅ | PB2–PB5 |
| RFID IRQ | ✅ | PD7 |
| RFID Stromschaltung | ✅ | PD6 über ULN |
| RS-485 Kommunikation | ✅ | SW Serial auf PB0 (RX) / PB1 (TX) |
| Victron VE.Direct UART | ✅ | SW Serial auf PC2/PC3 |
| I²C Geräte (RTC, MPU, MCP) | ✅ | PC4/PC5 |
| Piezo Signalgeber | ✅ | PC1 (A1) |
| HW | Pin | Arduino | Funktion | Verwendung | Interrupt | Interrupt-Vektor |
| 1 | RESET | RESET | ISP RST (evtl. auch an Raspberry zum Flashen über Bootloader HW-Serial) | |||
| 2 | PD0 | D0 | HW UART RX | Kommunikation mit Raspberry Pi über TTL 3,3V | Nein | – |
| 3 | PD1 | D1 | HW UART TX | Kommunikation mit Raspberry Pi über TTL 3,3V | Nein | – |
| 4 | PD2 | D2 | INT0 / Taster | System-Taster für Start und Shutdown | Ja | INT0 |
| 5 | PD3 | D3 | INT1 / Taster | Externer Taster um System kurzzeitig in Bereitschaft für Interaktionen (z.B. Alarm deaktivieren) zu versetzen | Ja | INT1 |
| 6 | PD4 | D4 | Digital In / Peripherie Interrupt | RTC Interrupt für tägliches Aufwach-Event | Ja | PCINT2 |
| 7 | VCC | |||||
| 8 | GND | |||||
| 9 | PB6 | XTAL1 | Digital Out / Power Switch | Raspberry Power über MOSFET Treiber um den High-Power 5V Buckconverter zu schalten | Nein | – |
| 10 | PB7 | XTAL2 | Digital Out / Alarm Switch | Alarmgeber (z.B. Sirene) mit Strom versorgen. Kann entweder über ULN2003 (<500ms) oder über MOSFET betrieben werden | Nein | – |
| 11 | PD5 | D5 | Digital In / Peripherie Interrupt | MPU IRQ / Alarm bei Erschütterung/Neigung/Bewegung des Wohnwagens | Ja | PCINT2 |
| 12 | PD6 | D6 | Digital Out / Power Switch | RFID Power (über ULN2003 für GND) | Nein | PCINT2 |
| 13 | PD7 | D7 | Digital In / Peripherie Interrupt | RFID IRQ | Ja | PCINT2 |
| 14 | PB0 | D8 | RS485 RX (SW Serial) | Software Serial an RS485 Treiber | Ja | PCINT0 |
| 15 | PB1 | D9 | RS485 TX (SW Serial) | Software Serial an RS485 Treiber | Nein | PCINT0 |
| 16 | PB2 | D10 | SPI SS | RFID SS | Nein | PCINT0 |
| 17 | PB3 | D11 | SPI MOSI | RFID MOSI / RFID MOSI | Nein | PCINT0 |
| 18 | PB4 | D12 | SPI MISO | RFID MISO / ISP MISO | Nein | PCINT0 |
| 19 | PB5 | D13 | SPI SCK | RFID SCK / ISP SCK | Nein | PCINT0 |
| 20 | AVCC | |||||
| 21 | AREF | |||||
| 22 | GND | |||||
| 23 | PC0 | A0 | Digital Out | Status LED (An/Watchdog/Alarm-Status/Fehler/Shutdown) | Nein | PCINT1 |
| 24 | PC1 | A1 | Piezo | Quittierungston für Alarm (de-)aktivierung (evtl. über Treiber ULN2003) | Nein | PCINT1 |
| 25 | PC2 | A2 | SW UART RX | Verbindung zu Victron MPPT Solar Charger | Ja (in SoftwareSerial Lib) | PCINT1 |
| 26 | PC3 | A3 | SW UART TX | Verbindung zu Victron MPPT Solar Charger | Nein | PCINT1 |
| 27 | PC4 | A4 | I2C SDA | Verbindung zu RTC, MPU und Port Extender | Nein | PCINT1 |
| 28 | PC5 | A5 | I2C SCL | Verbindung zu RTC, MPU und Port Extender | Nein | PCINT1 |
operatingModeOff: Kein BetriebPeriodic: Pi startet durch RTC-Alarme (zyklisch)On: Pi bleibt dauerhaft anAlert: Alarm wurde ausgelöststateDiagram-v2
[*] --> off
off --> alert: Alarm-In
off --> periodic: RTC event
off --> on: Button
periodic --> on: Button
periodic --> on: (PI) set_state
periodic --> alert: Alarm-In
periodic --> off: (PI) ack_shutdown
on --> alert: Alarm-In
on --> off: (PI) ack_shutdown
alert --> on: (PI) quit_alert
| State in | State out | Trigger | Comment |
| off | periodic | RTC event | Once a day the RTC will start the PI |
| off | on | Button | Start PI permanent due to user button press |
| periodic | on | Button | Start PI permanent due to user button press |
| on | off | Serial msg: “ack_shutdown” | Pi confirms that an ordinary shutdown took place. Power can be turned off |
| periodic | off | Serial msg: “ack_shutdown” | Pi confirms that an ordinary shutdown took place. Power can be turned off |
| off | alert | Alert-Contact | alarm loop has been triggered |
| periodic | alert | Alert-Contact | alarm loop has been triggered |
| on | alert | Alert-Contact | alarm loop has been triggered |
| alert | on | Serial msg: “quit_alert” | Alarm stopped. Pi keeps on. |
Ausgangslogik:
| Ausgang | Off | Periodic | On | Alert |
| Pi-Power | 0 | 1 | 1 | 1 |
| Signal | 0 | 0 | 0 | 1 |
| Status LED | Armed: Slow / Disarmed: Off | Fast |
Event- / Status-Matrix
| Event / Input | Off | Periodic | On | Alert |
| Button | On | On | noop | x |
| RTC | Periodic | noop | x | x |
| Serial set_state | noop | OK | OK | x |
| Alert-Contact | Alert | Alert | Alert | x |
| Serial ack_shutdown | noop | Off | Off | x |
| Serial quit_alert | noop | noop | noop | On |
alarmModeDisarmed: Alarmüberwachung ausArmed: Überwachung aktiv| Auslöser | Aktion |
|---|---|
| Taster kurz (<1 s) | Pi einschalten |
| Taster lang (>3 s) | Pi herunterfahren (außer im Alert-Modus) |
| RTC-Alarm (DS3231) | Pi einschalten im Periodic-Modus außer wenn Alter oder Permanent |
| Alarmkontakt öffnet | alarmMode → Alert |
| Seriell vom RPi: | |
set_mode permanent | → operatingMode permanent |
set_alarm armed | → alarmMode armed |
shutdown | → Poweroff 10s nach Bestätigung |
set_next_cycle 04:00:00 | → RTC-Alarm neu setzen |
Baudrate: 9600 bps
Pegel: 3.3 V TTL (direkt kompatibel mit Raspberry Pi)
RPi → ATtiny:
set_mode periodic
set_alarm armed
set_next_cycle 06:00:00
shutdown
ATtiny → RPi:
status armed
shutdown
| ISP Pin | Funktion |
|---|---|
| PA2 | RESET |
| PB7 | SCK |
| PB6 | MISO |
| PB5 | MOSI |

Für mein Badezimmer finde ich doch mein normales Licht etwas hell, weshalb eine indirekte LED Beleuchtung her musste die auch in der Farbe variabel einstellbar ist. Alles kein Hexenwerk aber es sollte auch mit meiner bestehenden OpenHAB 3 über alle Inputs steuerbar sein und ein schönes Nachlicht hergeben damit man auch Nachts sicher unterwegs ist ohne zu stark aufzuwachen.
Somit waren die Anforderungen recht schnell klar:
Nun gibt es bei Amazon einige Wi-Fi RGB LED Controller die alle vielversprechend aussahen. All diese Controller basieren auf mehr oder weniger dem gleichen Chipsatz ESP8266 auf welchem es eine breite Masse an alternativer Firmware gibt. Für mich ist der Tasmota die Wahl der Dinge, aber es gibt bestimmt auch andere gute Derivate. Mit ESPurna habe ich jedoch keine gute Erfahrung gemacht.
Mit dieser alternativen Firmware ist bereits der erste große Schritt gemeistert und die No-Privacy China-Firmware kann entfernt werden. Zudem bietet diese Firmware die Möglichkeit für eigene Erweiterungen (“Rules” / Scripting) und kann über MQTT angesteuert werden.
Für meine Verwendung habe ich einen größeren Controller ausgewählt, da dieser viel Power bietet, Schraubterminals besitzt und im Gehäuse genügend Platz zum basteln und. tüfteln ist.
Nun kommen wir aber zum interessanten Teil der Arbeit: Und zwar soll das Licht ja auch nicht die ganze Nacht leuchten und nur bei Bedarf angehen. Und zwar das nur für die Nacht.
Dazu kommen ein paar zusätzliche Anpassungen dazu:
TODO
Ich habe mich für die PIR-Sensoren AM312 (z.B. als 5er-Pack bei Amazon) entschieden das diese mit 3,3 V ausreichend versorgt werden und einfach einen Schließerkontakt bei Bewegung haben. Nachteil gegenüber einigen anderen ist, dass diese nicht einstellbar sind. Für diesen Use-Case sollte dies aber nicht von belang sein.

Auf der Unterseite des LED-Controllers befinden sich einige Pins die zum flashen aber auch zum herausführen der Stromversorgung genutzt werden können

Die Stiftleiste die auf IO, RX und TX geht ist ausschließlich temporär zum erstmaligen Flashen der Tasmota Firmware gedacht und wird anschließend wieder entfernt.
Nun muss noch ein Plätzchen für den Sense-PIN des PIR Moduls gefunden werden. Hierfür habe ich den GPIO 4 ausgewählt da dieser anscheinend noch nicht belegt ist (siehe Grafik)

Dieser wird auf der Vorderseite direkt an das ESP Breakout-Board angelötet


Falls nicht nur RGB sondern auch WW (Warm White) bzw. CW (Cold White) angebunden werden soll, müssen diese GPIOs auch noch konfiguriert werden. Es empfiehlt sich aber nur die benötigten Ausgänge zu konfigurieren, da Tasmota anhand der Konfiguration die Farben mischt.
Die Anpassung auf dem LED Controller habe ich in zwei Rules aufgeteilt. Dies kommt daher dass sich Rules einfach ein- und ausschalten lassen und ich damit die ganze Funktion (“schalte Licht bei Bewegung an”) ausschalten kann während noch notwendige Funktionalitäten in der anderen Rule aktiv sind. Eine Rule setze ich also zur Beobachtung des Status, wie z.B. Events/Veränderungen die über MQTT oder des ablaufenden Timers ein, also die Funktionen die immer aktiv sein sollen.
RULE2
ON event#PIR1-STATE==?
DO
VAR1 %value%
ENDON
ON event#PIR1-TIMEOUT>0
DO
VAR2 %value%
ENDON
ON VAR1#State==?
DO BACKLOG
RULE3 %value%;
Publish stat/%topic%/PIR1-STATE %value%
ENDON
ON VAR2#State>0
DO
Publish stat/%topic%/PIR1-TIMEOUT %value%
ENDON
ON Rules#Timer==3
DO
Power1 OFF
ENDON
Man sieht hier gut dass die Rule3 je nach Zustand von Var1 gesetzt wird. Var1 nutze ich somit zum (de)aktivieren der Bewegungsmelderfunktion. Diese Variable wird wiederum von dem MQTT Event “PIR1-STATE” verändert. Zudem wird das Licht ausgeschalten wenn der Timer 3 abläuft.
Eine weitere Rule soll rein auf die Bewegungen lauschen und somit auf Veränderungen an dem GPIO 4, welcher durch die vorige Konfiguration auf “Switch1” gemapped wurde. Die Veränderungen werden auch über MQTT announced und wenn der Switch1 auf ON geht (== Bewegung erkannt) wird zusätzlich Timer 3 auf die in Var2 festgelegte Zeit gesetzt. Dadurch kann man einen Nachlauf nach der zuletzt erkannten Bewegung sicherstellen.
RULE3
ON Switch1#State==ON
DO BACKLOG
Power1 %value%;
RuleTimer3 %VAR2%;
Publish stat/%topic%/PIR1-MOTION %value%
ENDON
ON Switch1#State==OFF
DO
Publish stat/%topic%/PIR1-MOTION %value%
ENDON
Im folgenden noch einige Standardwerte die einmalig definiert werden sollten. Der Rest kann dann über MQTT gesteuert werden.
BACKLOG
RULE2 ON;
RULE3 OFF;
VAR1 OFF;
VAR2 300;
Um zu verhindern dass Switch_1 den zugeordneten Output (Power) ansteuert und dass der Switch nur unserer Rule zur Verfügung steht muss die folgenden Option gesetzt werden:
SetOption114 1
Da standardmäßig die Konfiguration für einen Switch auf “Toggle” gestellt ist, wir aber in unserer Rule ein ON/OFF erwarten muss das Switch-Verhalten umgestellt werden:
SwitchMode 1
TODO
val String loggerName = "SunsetSunriseRules"
rule "Sunrise handler"
when
Channel "astro:sun:local:rise#event" triggered START
then
//bad_led_motion_detection.postUpdate(OFF)
bad_led_motion_detection.sendCommand(OFF)
logError(loggerName, "Run sunrise handler")
end
rule "Sunset handler"
when
Channel "astro:sun:local:set#event" triggered START
then
//bad_led_motion_detection.postUpdate(ON)
bad_led_motion_detection.sendCommand(ON)
logError(loggerName, "Run sunset handler")
end
Damit die zuvor definiert Rule funktioniert, müssen natürlich die Things und Items entsprechend in OpenHAB definiert sein.