Use case: Integrating Homematic smoke detector (HmIP-SWSD) into openHAB - Tutorial & Channel documentation

I recently installed a couple of Homematic IP smoke detectors (HmIP-SWSD) and wanted to share how they can be integrated into openHAB. Why I chose the Homematic smoke detectors (there are plenty of other smoke detectors on the market, see here and here), is described here. To be fair, there’s also a good set of arguments against smart smoke detectors, so everyone has to think for one self.

List of available and relevant channels

Once the HmIP-SWSD is added as an openHAB-Thing, it presents itself with 20 (!) channels. While the document HomeMatic-Script DokumentationTeil 4: Datenpunkte does only offer very limited information (it appears to be clearly outdated), the Homematic IP Devices Technical Documentation offers some more (but still not all relevant) information.

The following 8 channels appear to be the most relevant relevant* for what I want the smoke detector to do within openHAB:

  1. 0#LOW_BAT
  2. 0#UNREACH
  3. 0#TIME_OF_OPERATION_STATUS
  4. 1#ERROR_CODE
  5. 1#ERROR_DEGRADED_CHAMBER
  6. 1#SMOKE_DETECTOR_ALARM_STATUS
  7. 1#SMOKE_DETECTOR_TEST_RESULT
  8. 1#SMOKE_DETECTOR_COMMAND

All other channels are either nice to have or irrelevant (someone correct me if I’m wrong).

1) Low_Bat

  • Meaning: Low battery warning with possible values “true/on” (low battery) or “false/off” (battery ok)
  • Access: read
  • Type: boolen / integer
  • Possible values: TRUE, FALSE
  • Default value: off/false

This one is pretty straight forward: If the devices tells the CCU that its battery is low, the flag is set to TRUE.

Rule:
Every morning at 8 (cron expression 0 0 8 * * ? *) a javascript rule runs…

… that then checks the battery status of the smoke detectors and sends a notification if the battery of a smoke detector is low.

var NotificationAction = org.openhab.io.openhabcloud.NotificationAction;
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);

if (itemRegistry.getItem('Rauchmelder1_LowBattery').getState() == 'ON') {
  logger.warn('Batteriestatus Rauchmelder 1 ist niedrig.');
  NotificationAction.sendNotification('xxx@yyy.de', 'Batteriestatus Rauchmelder 1 ist niedrig.')
}
if (itemRegistry.getItem('Rauchmelder2_LowBattery').getState() == 'ON') {
  logger.warn('Batteriestatus Rauchmelder 2 ist niedrig.');
  NotificationAction.sendNotification('xxx@yyy.de', 'Batteriestatus Rauchmelder 2 ist niedrig.')
}
<<repeat for more devices>>

2) Unreach

  • Meaning: Device communication status; turns “on/true” if the CCU cannot reach the device anymore, or “off/false” if the CCU can reach the device (again).
  • Access: read
  • Type: boolen / integer
  • Possible values: TRUE, FALSE
  • Default value: off/false

The “Unreach”-flag is Homematic-specific. If a device cannot be reached anymore (either battery- or signal-related) the CCU sets this flag to TRUE.

Understanding the timing is crucial: If the CCU sends a signal to a device and can’t get hold of it, “unreach” is changed to “true” by the CCU immediately. If the device runs out of battery suddenly or loses connection for some other reason *and * the CCU does not try to reach the device, it can potentially take some time for the CCU’s regular “alive-check” kicks in and the CCU sets the flag to “true”.

Rule:
Put all “unreach-items” into one group. The rule is then triggered if the state of a member of a group changes:

To not get flooded during startup (when the state is NULL but then normally turns to OFF), there’s an additional check in the rule, so that it only fires if the item was OFF and then turns to ON.

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
var NotificationAction = org.openhab.io.openhabcloud.NotificationAction;

// item = "ON": Device not reachable
// item = "OFF": Device reachable

// If "Unreach = ON" (and previous state "OFF", meaning not coming from "NULL" as during startup)
if (event.itemState == 'ON' && event.oldItemState == 'OFF') {
  // Writing a log entry
  logger.warn(ir.getItem(event.itemName).getLabel() + " offline.");
  // Send push notificatio to the App
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + " offline.")
}
// If "Unreach = OFF" (and previous state "ON", meaning not coming from "NULL" as during startup)
else if (event.itemState == 'OFF' && event.oldItemState == 'ON') {
  // Writing a log entry
  logger.warn(ir.getItem(event.itemName).getLabel() + " online.");
  // Send push notificatio to the App
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + " online.")

3) TIME_OF_OPERATION_STATUS

  • Meaning: This one is not particularly well documented. The default appears to be “NORMAL”, and presumably, if the smoke detector has exceeded its “shelf life” of 10 years, probably turns to either “OVERFLOW” or “UNKNOWN”. Thus I track the state of this item (everything other than “NORMAL” triggers a notification) via a rule.
  • Access: read
  • Type: ENUM / Integer
  • Possible values: NORMAL, UNKNOWN, OVERFLOW
  • Default value: NORMAL

Rule:
See below (rule combined with #4 and #5).

4) ERROR_CODE

  • Meaning: This one is also not particularly well documented. The default appears to be “0”, and it presumably turns to “something else” if there’s a problem. Thus I track the state of this item (everything other than “0” triggers a notification) via a rule.
  • Access: read
  • Type: Integer
  • Possible values: 0 - 255 (according to the documentation, though I have problems to imagine what the 255 different error codes represent - probably only some are actually used)
  • Default value: 0

Rule:
See below (rule combined with #3 and #5).

5) DEGRADED_CHAMBER

  • Meaning: This one appears to be a bit redundant with TIME_OF_OPERATION_STATUS: Most smoke detectors have a shelf life of 10 years (due to dust in the chamber, degradation of electrical components, etc), and two of my five previous smoke detectors had false alarms in the middle of the night in their 11th year, so I believe replacing them after 10 years is a good idea. Thus I believe “TIME_OF_OPERATION_STATUS” and “DEGRADED_CHAMBER” appear to track the same.
    The default appears to be “false” (which makes sense) and it presumably turns to “true” if the chamber is “degraded” (which is probably not good and thus should be reported). Therefore I track the state of this item (if it changes to “true”) via a rule.
  • Access: read
  • Type: Boolean / Integer
  • Possible values: ON, OFF
  • Default value: OFF

Rule:
Combined with #3 and #4.

Every morning at 8 (cron expression 0 0 8 * * ? *) a javascript rule is triggered…

that then checks the status of the items of #3, #4 and #5. If anything is not as expected (default) a notification is pushed to the App.

var NotificationAction = org.openhab.io.openhabcloud.NotificationAction;
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);

// Smoke detector - Check of "Time of Operation Status"
// Default Status is "NORMAL", "OVERFLOW" presumably means "Shelf life of 10 years exceeded"
if (itemRegistry.getItem('Rauchmelder1_TimeOfOperationStatus').getState() != 'NORMAL') {
  logger.warn('Lebensdauer Rauchmelder 1 ist überschritten.');
  NotificationAction.sendNotification('xxx@yyy.de', 'Lebensdauer Rauchmelder 1 ist überschritten.')
}
if (itemRegistry.getItem('Rauchmelder2_TimeOfOperationStatus').getState() != 'NORMAL') {
  logger.warn('Lebensdauer Rauchmelder 2 ist überschritten.');
  NotificationAction.sendNotification('xxx@yyy.de', 'Lebensdauer Rauchmelder 2 ist überschritten.')
}
<<repeat for more devices>>

// Smoke detector - Check of "Error Code" - Definition not clear
// Default is "0"
if (itemRegistry.getItem('Rauchmelder1_ErrorCode').getState() != '0') {
  logger.warn('Rauchmelder 1 - Error Code überprüfen.');
  NotificationAction.sendNotification('xxx@yyy.de', 'Rauchmelder 1 - Error Code überprüfen.')
}
if (itemRegistry.getItem('Rauchmelder2_ErrorCode').getState() != '0') {
  logger.warn('Rauchmelder 2 - Error Code überprüfen.');
  NotificationAction.sendNotification('xxx@yyy.de', 'Rauchmelder 2 - Error Code überprüfen.')
}
<<repeat for more devices>>

// Smoke detector - Check of "Degraded Chamber" - Definition not clear
// Default is "OFF"
if (itemRegistry.getItem('Rauchmelder1_ErrorDegradedChamber').getState() == 'ON') {
  logger.warn('Rauchmelder 1 - Degraded Chamber.');
  NotificationAction.sendNotification('xxx@yyy.de', 'Rauchmelder 1 - Degraded Chamber.')
}
if (itemRegistry.getItem('Rauchmelder2_ErrorDegradedChamber').getState() == 'ON') {
  logger.warn('Rauchmelder 2 - Degraded Chamber.');
  NotificationAction.sendNotification('xxx@yyy.de', 'Rauchmelder 2 - Degraded Chamber.')
}
<<repeat for more devices>>

6) SMOKE_DETECTOR_ALARM_STATUS

  • Meaning: Indicates the alarm status in which the smoke detector is in.
  • Access: read
  • Type: ENUM / Integer
  • Possible values: “0”: “IDLE_OFF”, “1”: “PRIMARY_ALARM”, “2”: “INTRUSION_ALARM”, “3”: “SECONDARY_ALARM”
  • Default value: IDLE_OFF

“IDLE_OFF” is the default (smoke alarm is silent / no alarm). “PRIMARY_ALARM” is what the smoke detector will send if it detects smoke and thus also rinds loudly. “SECONDARY_ALARM” is what the smoke detector will send if it is informed from another smoke detector connected to the same CCU that it detected smoke and thus rings loudly as well (“group alarm”). “INTRUSION_ALARM” is what the smoke detector will send if it was triggered manually to ring loudly as part of an alarm system (see #8).

Rule:
See below (combined with #7).

7) SMOKE_DETECTOR_TEST_RESULT

  • Meaning: Indicates the test result of the various test routines.
  • Access: read
  • Type: ENUM / Integer
  • Possible values: “0”: “NONE”, “1”: “SMOKE_TEST_OK”, “2”: “SMOKE_TEST_FAILED”, “3”: “COMMUNICATION_TEST_SENT”, “4”: “COMMUNICATION_TEST_OK”
  • Default value: NONE

“NONE” is the default (no test has been run). “SMOKE_TEST_OK” is what the smoke detector will send if the test button on the smoke detector is pressed shortly and the test turned out ok (rings loudly). “SMOKE_TEST_FAILED” is what the smoke detector will send if the same test did not turn out ok. “COMMUNICATION_TEST_SENT” is what the smoke detector will send if a) the test button on the smoke detector is pressed long to perfom the communication test or b) the communication test has been initiated via openHAB (see #8 below). Note that the yellow LED on the smoke detector only blinks for 5 minutes on all detectors connected to the same CCU (showing that the communication test worked) if the test has been initiated by pressing the button. If the communication test has been initiated via openHAB (#8, see below), the LEDs on the smoke detectors will remain off and the smoke detectors will only indicate via “SMOKE_DETECTOR_TEST_RESULT” how the test turned out. “COMMUNICATION_TEST_OK” is what the smoke detectors will send if they’ve been informed by another smoke detector connected to the same CCU that a test was run.

If an automatic (e.g. monthly) smoke test or communication test is to be performed by openHAB (see #8 below), this can be done by interpreting the results of the channels above.

Rule:
Combined with #6.

Put all items “SMOKE_DETECTOR_ALARM_STATUS” and “SMOKE_DETECTOR_TEST_RESULT” into one group. The rule is then triggered if the state of a member of a group changes:

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
var NotificationAction = org.openhab.io.openhabcloud.NotificationAction;

if (event.itemState == 'COMMUNICATION_TEST_SENT') {
  logger.warn(ir.getItem(event.itemName).getLabel() + ": Kommunikationstest gesendet.");
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + ": Kommunikationstest gesendet.")
}
else if (event.itemState == 'COMMUNICATION_TEST_OK') {
  logger.warn(ir.getItem(event.itemName).getLabel() + ": Kommunikationstest OK.");
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + ": Kommunikationstest OK.")
}
else if (event.itemState == 'SMOKE_TEST_OK') {
  logger.warn(ir.getItem(event.itemName).getLabel() + ": Systemtest OK.");
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + ": Kommunikationstest OK.")
}
else if (event.itemState == 'SMOKE_TEST_FAILED') {
  logger.warn(ir.getItem(event.itemName).getLabel() + ": Systemtest fehlerhaft.");
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + ": Kommunikationstest OK.")
}
else if (event.itemState == 'PRIMARY_ALARM') {
  logger.warn(ir.getItem(event.itemName).getLabel() + ": Feueralarm.");
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + ": Feueralarm.")
}
else if (event.itemState == 'SECONDARY_ALARM') {
  logger.warn(ir.getItem(event.itemName).getLabel() + ": Feueralarm (fremdausgelöst).");
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + ": Feueralarm (fremdausgelöst).")
}
else if (event.itemState == 'INTRUSION_ALARM') {
  logger.warn(ir.getItem(event.itemName).getLabel() + ": Einbruchsalarm signalisiert.");
  NotificationAction.sendNotification('xxx@yyy.de', ir.getItem(event.itemName).getLabel() + ": Einbruchsalarm signalisiert.")
}

7) SMOKE_DETECTOR_COMMAND

  • Meaning: This is a channel that can receive commands to trigger actions of the smoke detector.
  • Access: write
  • Type: ENUM / Integer
  • Possible values: “0”: “RESERVED_ALARM_OFF”, “1”: “INTRUSION_ALARM_OFF”, “2”: “INTRUSION_ALARM”, “3”: “SMOKE_TEST”, “4”: “COMMUNICATION_TEST”, “5”: “COMMUNICATION_TEST_REPEATED”
  • Default value: RESERVED_ALARM_OFF (though I’m not sure what a “default value” is for an item that receives commands)

If “COMMUNICATION_TEST” is sent to one smoke detector, it will confirm this with “COMMUNICATION_TEST_SENT” on the item “SMOKE_DETECTOR_TEST_RESULT” and . “COMMUNICATION_TEST_OK” on all other smoke detectors connected to the same CCU.

If “SMOKE_TEST” is sent to one smoke detector, it will confirm this with “SMOKE_TEST_OK” on the item “SMOKE_DETECTOR_TEST_RESULT”. Note that this will only initiate a test on one smoke detector.

If “INTRUSION_ALARM” is sent to one smoke detector, this smoke detector and all other smoke detectors connected to the same CCU will ring an intrusion alarm. Additionally, “INTRUSION_ALARM” is shown via the item “SMOKE_DETECTOR_ALARM_STATUS”. Sending INTRUSION_ALARM_OFF" to the smoke detector switches the alarm off.

The effect of “COMMUNICATION_TEST_REPEATED” and “RESERVED_ALARM_OFF” I could not yet test. If someone has an idea, let me know.

Disclaimer

On most things I’m pretty confident. If I missed something, let me know.

3 Likes

Hello Cplant, thanks for the good documentary! :slight_smile:

Unfortunately I can’t send a command to the smoke detectors via “SMOKE_DETECTOR_COMMAND”. Nothing happens :S here the event log:

2022-12-22 19:13:18.645 [INFO ] [openhab.event.ItemCommandEvent ] - Item ‘Rauchmelder_1OG_WZ_Command’ received command 4
2022-12-22 19:13:18.651 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item ‘Rauchmelder_1OG_WZ_Command’ predicted to become 4
2022-12-22 19:13:18.655 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item ‘Rauchmelder_1OG_WZ_Command’ changed from RESERVED_ALARM_OFF to 4
2022-12-22 19:21:11.224 [INFO ] [openhab.event.ItemCommandEvent ] - Item ‘Rauchmelder_1OG_WZ_Command’ received command 4
2022-12-22 19:21:11.230 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item ‘Rauchmelder_1OG_WZ_Command’ predicted to become 4
2022-12-22 19:26:27.131 [INFO ] [openhab.event.ItemCommandEvent ] - Item ‘Rauchmelder_1OG_WZ_Command’ received command 3
2022-12-22 19:26:27.133 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item ‘Rauchmelder_1OG_WZ_Command’ predicted to become 3
2022-12-22 19:26:27.135 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item ‘Rauchmelder_1OG_WZ_Command’ changed from 4 to 3
2022-12-22 19:31:47.089 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item ‘Rauchmelder_1OG_WZ_Command’ changed from 3 to RESERVED_ALARM_OFF

the item:
String Rauchmelder_1OG_WZ_Command {channel=“homematic:HmIP-SWSD:ccu:MYNUMBERFROMTHEDEVICE:1#SMOKE_DETECTOR_COMMAND”}

My Test rule:
rule “Test”
when
Item Test changed
then
Rauchmelder_1OG_WZ_Command.sendCommand(4)
end

Do you have an idea what I’m doing wrong?

From what I see you’re sending the integers to the Command-item. From what I understood, you have to send the strings (also the reason I included them above).

Hey, sorry for the delay.

I thought i have to use the numbers :wink: It works with the strings, thank you very much!