Hacked by CoupDeGrace
Greetz:
Hmei7, BrokenPipe, SimSimi, L4663r666h05t, AntonKil, d3b~x, Index Php, Mdn_Newbie, Sultan Haikal, Brian Kamikaze
Hacked By Chinafans
https://t.me/Hack_0xTeam
https://t.me/Hello_root
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.