MiOS Binding Example - wuellueb/openhab GitHub Wiki
MiOS Binding Example
This document is intended to provide MiOS Binding users with real-world example openHAB configurations.
Users typically have configurations falling into one or more of the following categories, which will be used to outline any subsequent examples:
- Augmenting - openHAB Rules that "add" to existing MiOS Scenes.
- Co-existing - Replacing MiOS Scenes with openHAB Rules, but keeping the Devices.
- Replacing - wholesale replacement of MiOS functionality (Devices|Scenes) with openHAB equivalent functionality.
MiOS has a standardized definition that most Alarm Panel plugins adhere to (DSC, Ademco, GE Caddx, Paradox, etc). This exposes a standardized UPnP-style attribute, AlarmPartition2/Alarm
, for the Alarm System being in active Alarm mode. It has the value None
or Alarm
.
Here we check the specific transition between those two states as we want to avoid being re-notified, when the Uninitialized
» Alarm
state transition occurs, should openHAB restart.
Item declaration (house.items
):
String AlarmArea1Alarm "Alarm Area 1 Alarm [%s]" (GAlarmArea1) {mios="unit:house,device:228/service/AlarmPartition2/Alarm"}
String AlarmArea1ArmMode "Alarm Area 1 Arm Mode [%s]" (GAlarmArea1) {mios="unit:house,device:228/service/AlarmPartition2/ArmMode"}
String AlarmArea1LastUser "Alarm Area 1 Last User [%s]" (GAlarmArea1) {mios="unit:house,device:228/service/AlarmPartition2/LastUser"}
Rule declaration (house-alarm.rules
):
rule "Alarm Panel Breach"
when
Item AlarmArea1Alarm changed to Active
then
pushNotification("House-Alarm", "House in ALARM!! Notification")
say("Alert: House in Alarm Notification")
end
rule "Alarm Panel Armed (Any)"
when
Item AlarmArea1ArmMode changed from Disarmed to Armed
then
say("Warning! House Armed Notification")
// Perform deferred notifications, as the User.state may not have been processed yet.
createTimer(now.plusSeconds(1)) [
logDebug("house-alarm", "Alarm-Panel-Armed-Any Deferred notification")
var user = AlarmArea1LastUser.state as StringType
if (user == null) user = "user unknown"
pushNotification("House-Armed", "House Armed Notification (" + user + ")")
]
end
rule "Alarm Panel Disarmed (Fully)"
when
Item AlarmArea1ArmMode changed from Armed to Disarmed
then
say("Warning! House Disarmed Notification")
// Perform deferred notifications, as the User.state may not have been processed yet.
createTimer(now.plusSeconds(1)) [
logDebug("house-alarm", "Alarm-Panel-Disarmed-Fully Deferred notification")
var user = AlarmArea1LastUser.state as StringType
if (user == null) user = "user unknown"
pushNotification("House-Disarmed", "House Disarmed Notification (" + user + ")")
]
end
This is typical of a declarative Scene in MiOS. In this case, the lights are left on for 5 minutes, and if new motion is detected in that time, another 5 minute clock is started.
The logging can be removed as needed.
Item declaration (house.items
):
Group GSwitch All
Switch MasterClosetLightsStatus "Master Closet Lights" (GSwitch) {mios="unit:house,device:391/service/SwitchPower1/Status"}
Switch MasterClosetFibaroLightStatus "Master Closet Fibaro Light" (GSwitch) {mios="unit:house,device:431/service/SwitchPower1/Status"}
Rule declaration (house-master.rules
):
import org.openhab.model.script.actions.Timer
import org.joda.time.*
import java.util.concurrent.locks.ReentrantLock
val int MCL_DELAY_SECONDS = 300
var Timer mclTimer = null
var ReentrantLock mclLock = new ReentrantLock(false)
rule "Master Closet Motion"
when
Item MasterClosetZoneTripped changed from CLOSED to OPEN
then
logInfo("house-master", "Master-Closet-Motion Timer lights ON")
sendCommand(MasterClosetLightsStatus, ON)
sendCommand(MasterClosetFibaroLightStatus, ON)
mclLock.lock
if (mclTimer != null) {
mclTimer.cancel
logInfo("house-master", "Master-Closet-Motion Timer Cancel")
}
mclTimer = createTimer(now.plusSeconds(MCL_DELAY_SECONDS)) [
logInfo("house-master", "Master-Closet-Motion Timer lights OFF")
sendCommand(MasterClosetLightsStatus, OFF)
sendCommand(MasterClosetFibaroLightStatus, OFF)
]
mclLock.unlock
end
A variant of the above, this Rule has parts that only run at Nighttime. Here we use the Astro Binding to compute daylight hours. See the Astro Binding configuration for details on how to setup that Binding's configuration/openhab.cfg
entry.
Item declaration (sunrise.items
):
DateTime ClockDaylightStart "Daylight Start [%1$tH:%1$tM]" <calendar> {astro="planet=sun, type=daylight, property=start, offset=-30"}
DateTime ClockDaylightEnd "Daylight End [%1$tH:%1$tM]" <calendar> {astro="planet=sun, type=daylight, property=end, offset=+30"}
Item declaration (house.items
):
Switch KitchenSinkLightStatus "Kitchen Sink Light" (GSwitch) {mios="unit:house,device:99/service/SwitchPower1/Status"}
Switch KitchenPantryLightStatus "Kitchen Pantry Light" (GSwitch) {mios="unit:house,device:425/service/SwitchPower1/Status"}
Switch PowerHotWaterPumpStatus "Power Hot Water Pump" (GSwitch) {mios="unit:house,device:303/service/SwitchPower1/Status"}
Switch KitchenPantryZoneArmed "Zone Armed [%s]" {mios="unit:house,device:426/service/SecuritySensor1/Armed"}
Rule declaration (house-kitchen.rules
):
import org.openhab.core.library.types.*
import org.openhab.model.script.actions.Timer
import org.joda.time.*
import java.util.concurrent.locks.ReentrantLock
val int K_DELAY_SECONDS = 240
var Timer kTimer = null
var ReentrantLock kLock = new ReentrantLock(false)
rule "Kitchen Motion"
when
Item KitchenMotionZoneTripped changed from CLOSED to OPEN
then
logInfo("house-kitchen", "Kitchen-Motion Timer ON")
// Ignore this Rule if the Motion sensor is bypassed.
if (KitchenMotionZoneArmed.state != ON) {
logInfo("house-kitchen", "Kitchen-Motion Not Armed, skipping")
return void
}
val DateTime daylightStart = new DateTime((ClockDaylightStart.state as DateTimeType).getCalendar)
val DateTime daylightEnd = new DateTime((ClockDaylightEnd.state as DateTimeType).getCalendar)
var boolean night = daylightStart.isAfterNow || daylightEnd.isBeforeNow
if (night) {
logInfo("house-kitchen", "Kitchen-Motion Night Time")
sendCommand(KitchenSinkLightStatus, ON)
sendCommand(KitchenPantryLightStatus, ON)
}
logInfo("house-kitchen", "Kitchen-Motion Any Time")
sendCommand(PowerHotWaterPumpStatus, ON)
kLock.lock
if (kTimer != null) {
kTimer.cancel
logInfo("house-kitchen", "Kitchen-Motion Timer Cancel")
}
kTimer = createTimer(now.plusSeconds(K_DELAY_SECONDS)) [
logInfo("house-kitchen", "Kitchen-Motion Timer OFF")
sendCommand(KitchenSinkLightStatus, OFF)
sendCommand(KitchenPantryLightStatus, OFF)
sendCommand(PowerHotWaterPumpStatus, OFF)
]
kLock.unlock
end
This originally ran as a Scene on the MiOS Unit, but was replaced with an openHAB Rule. The Items are a mix of Items, from an Alarm system running on MiOS, and the Nest Binding, running locally.
Explicitly check for the OPEN
» CLOSED
state transition, to avoid issues when openHAB restarts (Uninitialized
» OPEN
) or when duplicate values come in from the MiOS System (OPEN
» OPEN
).
Item declaration (house.items
):
Group GPersist (All)
Group GWindow "All Windows [%d]" <contact> (GContact)
Contact LivingRoomZoneTripped "Living Room (Zone 2) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:117/service/SecuritySensor1/Tripped"}
Contact KitchenZoneTripped "Kitchen (Zone 3) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:118/service/SecuritySensor1/Tripped"}
Contact FamilyRoomZoneTripped "Family Room (Zone 5) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:120/service/SecuritySensor1/Tripped"}
Contact MasterBedroomZoneTripped "Master Bedroom (Zone 8) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:122/service/SecuritySensor1/Tripped"}
Contact Bedroom3ZoneTripped "Bedroom #3 (Zone 9) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:123/service/SecuritySensor1/Tripped"}
Contact Bedroom2ZoneTripped "Bedroom #2 (Zone 10) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:124/service/SecuritySensor1/Tripped"}
Contact GuestBathZoneTripped "Guest Bathroom (Zone 11) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:125/service/SecuritySensor1/Tripped"}
Contact StairsWindowsZoneTripped "Stairs Windows (Zone 12) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:126/service/SecuritySensor1/Tripped"}
Contact MasterBath1ZoneTripped "Master Bath (Zone 19) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:133/service/SecuritySensor1/Tripped"}
Contact MasterBath2ZoneTripped "Master Bath (Zone 20) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:134/service/SecuritySensor1/Tripped"}
Contact MasterBath3ZoneTripped "Master Bath (Zone 21) [MAP(en.map):%s]" <contact> (GWindow,GPersist) {mios="unit:house,device:135/service/SecuritySensor1/Tripped"}
Rule declaration (house.rules):
rule "Windows Closed (all)
when
Item Bedroom2ZoneTripped changed from OPEN to CLOSED or
Item Bedroom3ZoneTripped changed from OPEN to CLOSED or
Item FamilyRoomZoneTripped changed from OPEN to CLOSED or
Item GuestBathZoneTripped changed from OPEN to CLOSED or
Item KitchenZoneTripped changed from OPEN to CLOSED or
Item LivingRoomZoneTripped changed from OPEN to CLOSED or
Item MasterBath1ZoneTripped changed from OPEN to CLOSED or
Item MasterBath2ZoneTripped changed from OPEN to CLOSED or
Item MasterBath3ZoneTripped changed from OPEN to CLOSED or
Item MasterBedroomZoneTripped changed from OPEN to CLOSED or
Item StairsWindowsZoneTripped changed from OPEN to CLOSED
then
if (GWindow.members.filter(s|s.state==OPEN).size == 0) {
say("Attention: All Windows closed.")
sendCommand(Nest_away, "home")
}
end
rule "Windows Opened (any)"
when
Item Bedroom2ZoneTripped changed from CLOSED to OPEN or
Item Bedroom3ZoneTripped changed from CLOSED to OPEN or
Item FamilyRoomZoneTripped changed from CLOSED to OPEN or
Item GuestBathZoneTripped changed from CLOSED to OPEN or
Item KitchenZoneTripped changed from CLOSED to OPEN or
Item LivingRoomZoneTripped changed from CLOSED to OPEN or
Item MasterBath1ZoneTripped changed from CLOSED to OPEN or
Item MasterBath2ZoneTripped changed from CLOSED to OPEN or
Item MasterBath3ZoneTripped changed from CLOSED to OPEN or
Item MasterBedroomZoneTripped changed from CLOSED to OPEN or
Item StairsWindowsZoneTripped changed from CLOSED to OPEN
then
if (GWindow.members.filter(s|s.state==OPEN).size == 1) {
say("Attention: First Window opened.")
sendCommand(Nest_away, "away")
}
end
I wrote this script to publish data from MiOS to SmartEnergyGroups (SEG) for analysis.
Here's what you do to replace it with openHAB functionality:
Item declaration (house.items
):
Group GPersist (All)
Group GMonitor (All)
Group GMonitorTemperature (GMonitor)
Group GMonitorHumidity (GMonitor)
Group GMonitorPower (GMonitor)
Group GMonitorEnergy (GMonitor)
Number WeatherTemperatureCurrentTemperature "Outside [%.1f °F]" <temperature> (GPersist,GMonitorTemperature) {mios="unit:house,device:318/service/TemperatureSensor1/CurrentTemperature"}
Number WeatherLowTemperatureCurrentTemperature "Outside Low [%.1f °F]" <temperature> (GPersist,GMonitorTemperature) {mios="unit:house,device:319/service/TemperatureSensor1/CurrentTemperature"}
Number WeatherHighTemperatureCurrentTemperature "Outside High [%.1f °F]" <temperature> (GPersist,GMonitorTemperature) {mios="unit:house,device:320/service/TemperatureSensor1/CurrentTemperature"}
Number WeatherHumidityCurrentLevel "Outside Humidity [%d %%]" (GPersist,GMonitorHumidity) {mios="unit:house,device:321/service/HumiditySensor1/CurrentLevel"}
Number NestTStatUpstairs_humidity "Humidity [%d %%]" (GPersist,GMonitorHumidity) {nest="<[thermostats(Upstairs).humidity]"}
Number NestTStatUpstairs_ambient_temperature_f "Upstairs [%.1f °F]" <temperature> (GPersist,GMonitorTemperature) {nest="<[thermostats(Upstairs).ambient_temperature_f]"}
Number NestTStatDownstairs_humidity "Humidity [%d %%]" (GPersist,GMonitorHumidity) {nest="<[thermostats(Downstairs).humidity]"}
Number NestTStatDownstairs_ambient_temperature_f "Downstairs [%.1f °F]" <temperature> (GPersist,GMonitorTemperature) {nest="<[thermostats(Downstairs).ambient_temperature_f]"}
Persistence declaration (rrd4j.persist
):
Strategies {
// for rrd charts, we need a cron strategy
everyMinute : "0 * * * * ?"
everyDay : "0 0 23 * * ?"
}
Items {
SystemDataVersion, SystemUserDataDataVersion, SystemTimeStamp, SystemLocalTime, SystemLoadTime : strategy = everyDay
GPersist* : strategy = everyChange, everyMinute, restoreOnStartup
GTemperature* : strategy = everyMinute, restoreOnStartup
}
Rule declaration (seg.rules
):
import org.openhab.core.library.types.*
import java.util.Locale
rule "Log Data to SmartEnergyGroups (SEG)"
when
Time cron "0 0/2 * * * ?" or
Item NestTStatUpstairs_ambient_temperature_f changed or
Item NestTStatDownstairs_ambient_temperature_f changed or
Item WeatherTemperatureCurrentTemperature changed or
Item WeatherLowTemperatureCurrentTemperature changed or
Item WeatherHighTemperatureCurrentTemperature changed or
Item NestTStatUpstairs_humidity changed or
Item NestTStatDownstairs_humidity changed or
Item WeatherHumidityCurrentLevel changed
then
val String SEG_SITE = "<yourSiteKeyHere>"
val String SEG_URL = "http://api.smartenergygroups.com/api_sites/stream"
val String NODE_NAME = "openHAB"
val Locale LOCALE = Locale::getDefault
var String segData = ""
GMonitorTemperature?.members.forEach(item|
segData = segData + String::format(LOCALE, "(t_%s %s)", item.name, (item.state as Number).toString)
)
GMonitorHumidity?.members.forEach(item |
segData = segData + String::format(LOCALE, "(h_%s %s)", item.name, (item.state as Number).toString)
)
GMonitorPower?.members.forEach(item |
segData = segData + String::format(LOCALE, "(p_%s %s)", item.name, (item.state as Number).toString)
)
GMonitorEnergy?.members.forEach(item |
segData = segData + String::format(LOCALE, "(e_%s %s)", item.name, (item.state as Number).toString)
)
segData = String::format("(site %s (node %s ? %s))", SEG_SITE, NODE_NAME, segData)
sendHttpPostRequest(SEG_URL, "application/x-www-form-urlencoded", segData)
end