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