So Im not a programmer by any stretch and I didn’t like the limited nature of the device handlers out there so I wrote this. it has the correct parameters for the device for a start and it also shows battery and temperature
/**
*
* POPP Z-Wave Solar Siren
*
* Author: Tapion1ives
* Date: 2018-11-21
* Desc: So i took the aeon Siren dth and the generic z wave dth and i smooshed them both together and here we are.
* im a data analyst specialising in sql reporting, so this isnt really my bag, the only way i could get the parameters to set was by making it refresh. but it works so wuhooo
*/
metadata {
definition (name: "POPP Z-Wave Solar Siren", namespace: "Tapion1ives", author: "tapion1ives", ocfDeviceType: "x.com.st.d.siren", runLocally: false, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false) {
capability "Actuator"
capability "Alarm"
capability "Switch"
capability "Health Check"
capability "Polling"
capability "Refresh"
capability "Temperature Measurement"
capability "Sensor"
capability "Battery"
command "test"
fingerprint mfr: "0154", prod: "0004", model: "0002", deviceJoinName: "Popp Solar Siren"
}
simulator {
// reply messages
reply "9881002001FF,9881002002": "command: 9881, payload: 002003FF"
reply "988100200100,9881002002": "command: 9881, payload: 00200300"
reply "9881002001FF,delay 3000,988100200100,9881002002": "command: 9881, payload: 00200300"
}
tiles(scale: 2) {
multiAttributeTile(name:"alarm", type: "generic", width: 6, height: 4){
tileAttribute ("device.alarm", key: "PRIMARY_CONTROL") {
attributeState "off", label:'off', action:'alarm.siren', icon:"st.alarm.alarm.alarm", backgroundColor:"#ffffff"
attributeState "both", label:'alarm!', action:'alarm.off', icon:"st.alarm.alarm.alarm", backgroundColor:"#e86d13"
}
}
standardTile("test", "device.alarm", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'', action:"test", icon:"st.secondary.test"
}
standardTile("off", "device.alarm", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'', action:"alarm.off", icon:"st.secondary.off"
}
standardTile("refresh", "command.refresh", inactiveLabel: false,
decoration: "flat") {
state "default", label:'', action:"refresh.refresh",
icon:"st.secondary.refresh"
}
valueTile("battery", "device.battery", inactiveLabel: false,
decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:""
}
valueTile("temperature", "device.temperature") {
state("temperature", label:'${currentValue}°',
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
)
}
preferences {
// here we will set the preferences as variables to pass through on configuration and update
input "tamper", "integer", title: "Siren Tamper Mode" , defaultValue: 0, required: false//, displayDuringSetup: true
input "tempadj", "integer", title: "Temperature Adjustment" , defaultValue: 0, required: false//, displayDuringSetup: true
input "temprep", "integer", title: "Send unsolicited Temperature Report" , defaultValue: 10, required: false//, displayDuringSetup: true
input "tempperiod", "integer", title: "Send unsolicited Temperature Report periodically " , defaultValue: 15, required: false//, displayDuringSetup: true
input "mode", "integer", title: "Siren Mode", defaultValue: 2, required: false//, displayDuringSetup: true
input "autooff", "integer", title: "Auto Off in mins", defaultValue: 5, required: false//, displayDuringSetup: true
}
main (["alarm", "temperature"])
details(["alarm", "test", "off", "temperature", "refresh", "battery"])
}
}
def parse(String description) {
log.debug "parse($description)"
def result = null
if (description.startsWith("Err")) {
if (state.sec) {
result = createEvent(descriptionText:description, displayed:false)
} else {
result = createEvent(
descriptionText: "This device failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
}
} else {
def cmd = zwave.parse(description, [0x98: 1, 0x20: 1, 0x70: 1])
if (cmd) {
result = zwaveEvent(cmd)
}
}
log.debug "Parse returned ${result?.inspect()}"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1])
// log.debug "encapsulated: $encapsulatedCommand"
if (encapsulatedCommand) {
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
log.debug "rx $cmd"
[
createEvent([name: "switch", value: cmd.value ? "on" : "off", displayed: false]),
createEvent([name: "alarm", value: cmd.value ? "both" : "off"])
]
}
def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
def result
if (cmd.scale == 0) {
result = createEvent(name: "energy", value: cmd.scaledMeterValue,
unit: "kWh")
} else if (cmd.scale == 1) {
result = createEvent(name: "energy", value: cmd.scaledMeterValue,
unit: "kVAh")
} else {
result = createEvent(name: "power",
value: Math.round(cmd.scaledMeterValue), unit: "W")
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) {
def map = null
if (cmd.meterType == 1) {
if (cmd.scale == 0) {
map = [name: "energy", value: cmd.scaledMeterValue,
unit: "kWh"]
} else if (cmd.scale == 1) {
map = [name: "energy", value: cmd.scaledMeterValue,
unit: "kVAh"]
} else if (cmd.scale == 2) {
map = [name: "power", value: cmd.scaledMeterValue, unit: "W"]
} else {
map = [name: "electric", value: cmd.scaledMeterValue]
map.unit = ["pulses", "V", "A", "R/Z", ""][cmd.scale - 3]
}
} else if (cmd.meterType == 2) {
map = [name: "gas", value: cmd.scaledMeterValue]
map.unit = ["m^3", "ft^3", "", "pulses", ""][cmd.scale]
} else if (cmd.meterType == 3) {
map = [name: "water", value: cmd.scaledMeterValue]
map.unit = ["m^3", "ft^3", "gal"][cmd.scale]
}
if (map) {
if (cmd.previousMeterValue && cmd.previousMeterValue != cmd.meterValue) {
map.descriptionText = "${device.displayName} ${map.name} is ${map.value} ${map.unit}, previous: ${cmd.scaledPreviousMeterValue}"
}
createEvent(map)
} else {
null
}
}
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) {
def result
switch (cmd.sensorType) {
case 2:
result = createEvent(name:"smoke",
value: cmd.sensorValue ? "detected" : "closed")
break
case 3:
result = createEvent(name:"carbonMonoxide",
value: cmd.sensorValue ? "detected" : "clear")
break
case 4:
result = createEvent(name:"carbonDioxide",
value: cmd.sensorValue ? "detected" : "clear")
break
case 5:
result = createEvent(name:"temperature",
value: cmd.sensorValue ? "overheated" : "normal")
break
case 6:
result = createEvent(name:"water",
value: cmd.sensorValue ? "wet" : "dry")
break
case 7:
result = createEvent(name:"temperature",
value: cmd.sensorValue ? "freezing" : "normal")
break
case 8:
result = createEvent(name:"tamper",
value: cmd.sensorValue ? "detected" : "okay")
break
case 9:
result = createEvent(name:"aux",
value: cmd.sensorValue ? "active" : "inactive")
break
case 0x0A:
result = createEvent(name:"contact",
value: cmd.sensorValue ? "open" : "closed")
break
case 0x0B:
result = createEvent(name:"tilt", value: cmd.sensorValue ? "detected" : "okay")
break
case 0x0C:
result = createEvent(name:"motion",
value: cmd.sensorValue ? "active" : "inactive")
break
case 0x0D:
result = createEvent(name:"glassBreak",
value: cmd.sensorValue ? "detected" : "okay")
break
default:
result = createEvent(name:"sensor",
value: cmd.sensorValue ? "active" : "inactive")
break
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd)
{
// Version 1 of SensorBinary doesn't have a sensor type
createEvent(name:"sensor", value: cmd.sensorValue ? "active" : "inactive")
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd)
{
def map = [ displayed: true, value: cmd.scaledSensorValue.toString() ]
switch (cmd.sensorType) {
case 1:
map.name = "temperature"
map.unit = cmd.scale == 1 ? "F" : "C"
break;
case 2:
map.name = "value"
map.unit = cmd.scale == 1 ? "%" : ""
break;
case 3:
map.name = "illuminance"
map.value = cmd.scaledSensorValue.toInteger().toString()
map.unit = "lux"
break;
case 4:
map.name = "power"
map.unit = cmd.scale == 1 ? "Btu/h" : "W"
break;
case 5:
map.name = "humidity"
map.value = cmd.scaledSensorValue.toInteger().toString()
map.unit = cmd.scale == 0 ? "%" : ""
break;
case 6:
map.name = "velocity"
map.unit = cmd.scale == 1 ? "mph" : "m/s"
break;
case 8:
case 9:
map.name = "pressure"
map.unit = cmd.scale == 1 ? "inHg" : "kPa"
break;
case 0xE:
map.name = "weight"
map.unit = cmd.scale == 1 ? "lbs" : "kg"
break;
case 0xF:
map.name = "voltage"
map.unit = cmd.scale == 1 ? "mV" : "V"
break;
case 0x10:
map.name = "current"
map.unit = cmd.scale == 1 ? "mA" : "A"
break;
case 0x12:
map.name = "air flow"
map.unit = cmd.scale == 1 ? "cfm" : "m^3/h"
break;
case 0x1E:
map.name = "loudness"
map.unit = cmd.scale == 1 ? "dBA" : "dB"
break;
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
createEvent(displayed: false, descriptionText: "$device.displayName: $cmd")
}
def on() {
log.debug "sending on"
[
secure(zwave.basicV1.basicSet(value: 0xFF)),
secure(zwave.basicV1.basicGet())
]
}
def off() {
log.debug "sending off"
[
secure(zwave.basicV1.basicSet(value: 0x00)),
secure(zwave.basicV1.basicGet())
]
}
def strobe() {
on()
}
def siren() {
on()
}
def both() {
on()
}
def test() {
[
secure(zwave.basicV1.basicSet(value: 0xFF)),
"delay 3000",
secure(zwave.basicV1.basicSet(value: 0x00)),
secure(zwave.basicV1.basicGet())
]
}
def refresh() {
// Some examples of Get commands
delayBetween([
zwave.switchBinaryV1.switchBinaryGet().format(),
zwave.switchMultilevelV1.switchMultilevelGet().format(),
zwave.meterV2.meterGet(scale: 0).format(), // get kWh
zwave.meterV2.meterGet(scale: 2).format(), // get Watts
zwave.sensorMultilevelV1.sensorMultilevelGet().format(),
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1).format(), // get temp in Fahrenheit
zwave.batteryV1.batteryGet().format(),
zwave.basicV1.basicGet().format(),
secure(zwave.configurationV1.configurationSet(parameterNumber:1, size:1, configurationValue:[settings.tamper.toInteger()])),
secure(zwave.configurationV1.configurationSet(parameterNumber:2, size:1, configurationValue:[settings.tempadj.toInteger()])),
secure(zwave.configurationV1.configurationSet(parameterNumber:3, size:1, configurationValue:[settings.temprep.toInteger()])),
secure(zwave.configurationV1.configurationSet(parameterNumber:4, size:2, configurationValue:[settings.tempperiod.toInteger()])),
secure(zwave.configurationV1.configurationSet(parameterNumber:5, size:1, configurationValue:[settings.mode.toInteger()])),
secure(zwave.configurationV1.configurationSet(parameterNumber:6, size:3, configurationValue:[settings.autooff.toInteger()])),
secure(zwave.configurationV1.configurationGet(parameterNumber:1)),
secure(zwave.configurationV1.configurationGet(parameterNumber:2)),
secure(zwave.configurationV1.configurationGet(parameterNumber:3)),
secure(zwave.configurationV1.configurationGet(parameterNumber:4)),
secure(zwave.configurationV1.configurationGet(parameterNumber:5)),
secure(zwave.configurationV1.configurationGet(parameterNumber:6)),
], 1200)
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
secure(zwave.basicV1.basicGet())
}
// If you add the Configuration capability to your device type, this
// command will be called right after the device joins to set
// device-specific configuration commands.
def configure() {
log.debug "configuring"
secure(zwave.configurationV1.configurationSet(parameterNumber:1, size:1, configurationValue:[settings.tamper.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:2, size:1, configurationValue:[settings.tempadj.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:3, size:1, configurationValue:[settings.temprep.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:4, size:2, configurationValue:[settings.tempperiod.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:5, size:1, configurationValue:[settings.mode.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:6, size:3, configurationValue:[settings.autooff.toInteger()]))
}
def installed() {
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
// Get default values and set device to send us an update when alarm state changes from device
refresh()
}
def updated() {
secure(zwave.configurationV1.configurationSet(parameterNumber:1, size:1, configurationValue:[settings.tamper.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:2, size:1, configurationValue:[settings.tempadj.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:3, size:1, configurationValue:[settings.temprep.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:4, size:2, configurationValue:[settings.tempperiod.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:5, size:1, configurationValue:[settings.mode.toInteger()]))
secure(zwave.configurationV1.configurationSet(parameterNumber:6, size:3, configurationValue:[settings.autooff.toInteger()]))
def commands = []
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
log.debug "updating"
response(commands)
}