Compare commits

..

6 Commits

Author SHA1 Message Date
Garth Williams
62a264bbac MSA-2129: A device handler for the Devolo Humidity Sensor 2017-07-25 15:00:44 -07:00
Jack Chi
47eedf5770 Merge pull request #2170 from pchomal/hc_changes
[DHF-14][DHF-15] Updated comments & rectified typo
2017-07-25 12:25:38 +05:30
Luke Ness
3d0dbc57a1 Merge pull request #2100 from gausnes/ZwaveSirenFix
BatteryGet has a format method not property
2017-07-24 16:05:36 -05:00
piyush.c
f96ae94d12 [DHF-14][DHF-15] Health Check Aeon Key Fob and Aeon Minimote 2017-07-19 14:18:32 +05:30
Vinay Rao
de5f0683d3 Merge pull request #2168 from SmartThingsCommunity/staging
Rolling down staging to master
2017-07-18 12:24:01 -07:00
Luke Ness
5e787dd0e3 BatteryGet has a format method not property 2017-06-19 11:59:13 -05:00
15 changed files with 602 additions and 681 deletions

View File

@@ -0,0 +1,188 @@
/**
* Devolo Humidity Sensor
*
* Copyright 2017 Garth Williams
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*
* Based on definition (name: "Aeon Multisensor 6", namespace: "smartthings", author: "SmartThings")
*
*/
metadata {
definition (name: "Devolo Humidity Sensor", namespace: "garth", author: "Garth Williams") {
capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Configuration"
capability "Sensor"
capability "Battery"
capability "Health Check"
attribute "batteryStatus", "string"
fingerprint mfr: "0175", prod: "0002", model: "0020"
}
simulator {
for (int i = 0; i <= 100; i += 20) {
status "temperature ${i}F": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1)
).incomingMessage()
}
for (int i = 0; i <= 100; i += 20) {
status "humidity ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(scaledSensorValue: i, sensorType: 5)
).incomingMessage()
}
for (int i in [0, 5, 10, 15, 50, 99, 100]) {
status "battery ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: i)
).incomingMessage()
}
status "low battery alert": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: 255)
).incomingMessage()
status "wake up" : "command: 8407, payload: "
}
preferences {
}
tiles(scale: 2) {
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
state "temperature", label:'${currentValue}°',
backgroundColors:[
[value: 32, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 92, color: "#d04e00"],
[value: 98, color: "#bc2323"]
]
}
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
state "humidity", label:'${currentValue}% humidity', unit:""
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
valueTile("batteryStatus", "device.batteryStatus", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "batteryStatus", label:'${currentValue}', unit:""
}
main(["temperature", "humidity"])
details(["temperature", "humidity", "batteryStatus"])
}
}
def parse(String description) {
log.debug "parse() >> description: $description"
def result = null
if (description.startsWith("Err 106")) {
log.debug "parse() >> Err 106"
result = createEvent( name: "secureInclusion", value: "failed", isStateChange: true,
descriptionText: "This sensor 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.")
} else if (description != "updated") {
log.debug "parse() >> zwave.parse(description)"
def cmd = zwave.parse(description, [0x31: 5, 0x30: 2, 0x84: 1])
if (cmd) {
result = zwaveEvent(cmd)
}
}
log.debug "After zwaveEvent(cmd) >> Parsed '${description}' to ${result.inspect()}"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x30: 2, 0x84: 1])
state.sec = 1
log.debug "encapsulated: ${encapsulatedCommand}"
if (encapsulatedCommand) {
zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
createEvent(descriptionText: cmd.toString())
}
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
log.info "Executing zwaveEvent 98 (SecurityV1): 03 (SecurityCommandsSupportedReport) with cmd: $cmd"
state.sec = 1
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.NetworkKeyVerify cmd) {
state.sec = 1
log.info "Executing zwaveEvent 98 (SecurityV1): 07 (NetworkKeyVerify) with cmd: $cmd (node is securely included)"
def result = [createEvent(name:"secureInclusion", value:"success", descriptionText:"Secure inclusion was successful", isStateChange: true)]
result
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def result = []
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "${device.displayName} battery is low"
map.isStateChange = true
} else {
map.value = cmd.batteryLevel
}
state.lastbatt = now()
result << createEvent(map)
if (device.latestValue("powerSupply") != "USB Cable"){
result << createEvent(name: "batteryStatus", value: "${map.value} % battery", displayed: false)
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd){
def map = [:]
switch (cmd.sensorType) {
case 1:
map.name = "temperature"
def cmdScale = cmd.scale == 1 ? "F" : "C"
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
map.unit = getTemperatureScale()
break
case 5:
map.name = "humidity"
map.value = cmd.scaledSensorValue.toInteger()
map.unit = "%"
break
default:
map.descriptionText = cmd.toString()
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) {
log.debug "ConfigurationReport: $cmd"
def result = []
def value
if (cmd.parameterNumber == 9 && cmd.configurationValue[0] == 1) {
value = "Battery"
result << createEvent(name: "powerSupply", value: value, displayed: false)
} else if (cmd.parameterNumber == 101){
result << response(configure())
}
result
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
log.debug "General zwaveEvent cmd: ${cmd}"
createEvent(descriptionText: cmd.toString(), isStateChange: false)
}

View File

@@ -73,7 +73,7 @@ metadata {
[value: 64, color: "#44B621"],
[value: 80, color: "#3D79D9"],
[value: 96, color: "#0A50C2"]
], icon:"st.Weather.weather12"
]
}
valueTile("maxHum", "device.maxHum", canChangeIcon: false, canChangeBackground: false) {

View File

@@ -133,8 +133,8 @@ def updated() {
}
def initialize() {
// Arrival sensors only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
// Device only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false)
def zwMap = getZwaveInfo()
def buttons = 4 // Default for Key Fob

View File

@@ -111,7 +111,6 @@ def configure() {
return cmds
}
def installed() {
initialize()
}
@@ -121,7 +120,7 @@ def updated() {
}
def initialize() {
// Arrival sensors only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
// Device only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false)
sendEvent(name: "numberOfButtons", value: 4)
}

View File

@@ -27,9 +27,13 @@ Works with:
## Device Health
Aeon Labs MultiSensor (Gen 5) is polled by the hub.
Aeon MultiSensor Gen5 reports in once every hour.
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
* __122min__ checkInterval
* __32min__ checkInterval
## Troubleshooting

View File

@@ -100,12 +100,12 @@ metadata {
def installed(){
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
def updated(){
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
def parse(String description)

View File

@@ -28,9 +28,13 @@ Works with:
## Device Health
Aeon Labs MultiSensor is polled by the hub.
Aeon MultiSensor reports in once every hour.
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
* __122min__ checkInterval
* __32min__ checkInterval
## Battery Specification

View File

@@ -96,12 +96,12 @@ metadata {
def installed(){
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
def updated(){
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
// Parse incoming device messages to generate events

View File

@@ -6,6 +6,7 @@ metadata {
capability "Relative Humidity Measurement"
capability "Thermostat"
capability "Battery"
capability "Configuration"
capability "Refresh"
capability "Sensor"
capability "Health Check"
@@ -14,173 +15,161 @@ metadata {
command "switchMode"
command "switchFanMode"
command "lowerHeatingSetpoint"
command "raiseHeatingSetpoint"
command "lowerCoolSetpoint"
command "raiseCoolSetpoint"
command "quickSetCool"
command "quickSetHeat"
fingerprint deviceId: "0x08", inClusters: "0x43,0x40,0x44,0x31,0x80,0x85,0x60"
fingerprint mfr:"0098", prod:"6401", model:"0107", deviceJoinName: "2Gig CT100 Programmable Thermostat"
}
tiles {
multiAttributeTile(name:"temperature", type:"generic", width:3, height:2, canChangeIcon: true) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("temperature", label:'${currentValue}°', icon: "st.alarm.temperature.normal",
backgroundColors:[
// Celsius
[value: 0, color: "#153591"],
[value: 7, color: "#1e9cbb"],
[value: 15, color: "#90d2a7"],
[value: 23, color: "#44b621"],
[value: 28, color: "#f1d801"],
[value: 35, color: "#d04e00"],
[value: 37, color: "#bc2323"],
// Fahrenheit
[value: 40, 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"]
]
)
}
tileAttribute("device.batteryIcon", key: "SECONDARY_CONTROL") {
attributeState "ok_battery", label:'${currentValue}%', icon:"st.arlo.sensor_battery_4"
attributeState "low_battery", label:'Low Battery', icon:"st.arlo.sensor_battery_0"
}
}
standardTile("mode", "device.thermostatMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
state "off", action:"switchMode", nextState:"to_heat", icon: "st.thermostat.heating-cooling-off"
state "heat", action:"switchMode", nextState:"to_cool", icon: "st.thermostat.heat"
state "cool", action:"switchMode", nextState:"...", icon: "st.thermostat.cool"
state "auto", action:"switchMode", nextState:"...", icon: "st.thermostat.auto"
state "emergency heat", action:"switchMode", nextState:"...", icon: "st.thermostat.emergency-heat"
state "to_heat", action:"switchMode", nextState:"to_cool", icon: "st.secondary.secondary"
state "to_cool", action:"switchMode", nextState:"...", icon: "st.secondary.secondary"
state "...", label: "...", action:"off", nextState:"off", icon: "st.secondary.secondary"
}
standardTile("fanMode", "device.thermostatFanMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
state "auto", action:"switchFanMode", icon: "st.thermostat.fan-auto"
state "on", action:"switchFanMode", icon: "st.thermostat.fan-on"
state "circulate", action:"switchFanMode", icon: "st.thermostat.fan-circulate"
}
valueTile("humidity", "device.humidity", width:2, height:2, inactiveLabel: false, decoration: "flat") {
state "humidity", label:'${currentValue}%', icon:"st.Weather.weather12"
}
standardTile("lowerHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
state "heatingSetpoint", action:"lowerHeatingSetpoint", icon:"st.thermostat.thermostat-left"
}
valueTile("heatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
state "heatingSetpoint", label:'${currentValue}° heat', backgroundColor:"#ffffff"
}
standardTile("raiseHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
state "heatingSetpoint", action:"raiseHeatingSetpoint", icon:"st.thermostat.thermostat-right"
}
standardTile("lowerCoolSetpoint", "device.coolingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
state "coolingSetpoint", action:"lowerCoolSetpoint", icon:"st.thermostat.thermostat-left"
}
valueTile("coolingSetpoint", "device.coolingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
state "coolingSetpoint", label:'${currentValue}° cool', backgroundColor:"#ffffff"
}
standardTile("raiseCoolSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
state "heatingSetpoint", action:"raiseCoolSetpoint", icon:"st.thermostat.thermostat-right"
}
// simulator metadata
simulator {
status "off" : "command: 4003, payload: 00"
status "heat" : "command: 4003, payload: 01"
status "cool" : "command: 4003, payload: 02"
status "auto" : "command: 4003, payload: 03"
status "emergencyHeat" : "command: 4003, payload: 04"
standardTile("refresh", "device.thermostatMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
status "fanAuto" : "command: 4403, payload: 00"
status "fanOn" : "command: 4403, payload: 01"
status "fanCirculate" : "command: 4403, payload: 06"
status "heat 60" : "command: 4303, payload: 01 09 3C"
status "heat 72" : "command: 4303, payload: 01 09 48"
status "cool 76" : "command: 4303, payload: 02 09 4C"
status "cool 80" : "command: 4303, payload: 02 09 50"
status "temp 58" : "command: 3105, payload: 01 2A 02 44"
status "temp 62" : "command: 3105, payload: 01 2A 02 6C"
status "temp 78" : "command: 3105, payload: 01 2A 03 0C"
status "temp 86" : "command: 3105, payload: 01 2A 03 34"
status "idle" : "command: 4203, payload: 00"
status "heating" : "command: 4203, payload: 01"
status "cooling" : "command: 4203, payload: 02"
// reply messages
reply "2502": "command: 2503, payload: FF"
}
tiles {
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°',
backgroundColors:[
[value: 32, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 92, color: "#d04e00"],
[value: 98, color: "#bc2323"]
]
)
}
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
state "off", label:'${name}', action:"switchMode", nextState:"to_heat"
state "heat", label:'${name}', action:"switchMode", nextState:"to_cool"
state "cool", label:'${name}', action:"switchMode", nextState:"..."
state "auto", label:'${name}', action:"switchMode", nextState:"..."
state "emergency heat", label:'${name}', action:"switchMode", nextState:"..."
state "to_heat", label: "heat", action:"switchMode", nextState:"to_cool"
state "to_cool", label: "cool", action:"switchMode", nextState:"..."
state "...", label: "...", action:"off", nextState:"off"
}
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
state "fanAuto", label:'${name}', action:"switchFanMode"
state "fanOn", label:'${name}', action:"switchFanMode"
state "fanCirculate", label:'${name}', action:"switchFanMode"
}
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
state "setHeatingSetpoint", action:"quickSetHeat", backgroundColor:"#e86d13"
}
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
state "heat", label:'${currentValue}° heat', backgroundColor:"#ffffff"
}
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
state "setCoolingSetpoint", action:"quickSetCool", backgroundColor: "#00a0dc"
}
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
}
valueTile("humidity", "device.humidity", inactiveLabel: false, decoration: "flat") {
state "humidity", label:'${currentValue}% humidity', unit:""
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:""
}
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "temperature"
details(["temperature", "mode", "fanMode", "humidity", "lowerHeatingSetpoint", "heatingSetpoint", "raiseHeatingSetpoint", "lowerCoolSetpoint", "coolingSetpoint", "raiseCoolSetpoint", "refresh"])
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh", "humidity", "battery"])
}
}
def updated() {
// If not set update ManufacturerSpecific data
if (!getDataValue("manufacturer")) {
sendHubCommand(new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()))
}
initialize()
// 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])
}
def installed() {
// Configure device
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format())
cmds << new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())
sendHubCommand(cmds)
initialize()
}
def initialize() {
// 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])
// Poll device for additional data that will be updated by refresh tile
refresh()
}
def parse(String description)
{
def result = null
def result = []
if (description == "updated") {
} else {
def zwcmd = zwave.parse(description, [0x42:2, 0x43:2, 0x31: 2, 0x60: 3])
if (zwcmd) {
result = zwaveEvent(zwcmd)
// Check battery level at least once every 2 days
if (!state.lastbatt || now() - state.lastbatt > 48*60*60*1000) {
sendHubCommand(new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format()))
}
result += zwaveEvent(zwcmd)
} else {
log.debug "$device.displayName couldn't parse $description"
}
}
if (!result) {
return []
return null
}
return [result]
if (result.size() == 1 && (!state.lastbatt || now() - state.lastbatt > 48*60*60*1000)) {
result << response(zwave.batteryV1.batteryGet().format())
}
log.debug "$device.displayName parsed '$description' to $result"
result
}
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiInstanceCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 3])
log.debug ("multiinstancev1.MultiInstanceCmdEncap: command from instance ${cmd.instance}: ${encapsulatedCommand}")
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
def result = null
def encapsulatedCommand = cmd.encapsulatedCommand([0x42:2, 0x43:2, 0x31: 2])
log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}")
if (encapsulatedCommand) {
zwaveEvent(encapsulatedCommand)
result = zwaveEvent(encapsulatedCommand)
if (cmd.sourceEndPoint == 1) { // indicates a response to refresh() vs an unrequested update
def event = ([] + result)[0] // in case zwaveEvent returns a list
def resp = nextRefreshQuery(event?.name)
if (resp) {
log.debug("sending next refresh query: $resp")
result = [] + result + response(["delay 200", resp])
}
}
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
{
def sendCmd = []
def unit = getTemperatureScale()
def cmdScale = cmd.scale == 1 ? "F" : "C"
def setpoint = getTempInLocalScale(cmd.scaledValue, cmdScale)
def temp = convertTemperatureIfNeeded(cmd.scaledValue, cmdScale, cmd.precision)
def unit = getTemperatureScale()
def map1 = [ value: temp, unit: unit, displayed: false ]
switch (cmd.setpointType) {
case 1:
//map1.name = "heatingSetpoint"
sendEvent(name: "heatingSetpoint", value: setpoint, unit: unit, displayed: false)
updateThermostatSetpoint("heatingSetpoint", setpoint)
// Enforce coolingSetpoint limits, as device doesn't
if (setpoint > getTempInLocalScale("coolingSetpoint")) {
sendCmd << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
setpointType: 2, scale: cmd.scale, precision: cmd.precision, scaledValue: cmd.scaledValue).format())
sendCmd << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
sendHubCommand(sendCmd)
}
map1.name = "heatingSetpoint"
break;
case 2:
//map1.name = "coolingSetpoint"
sendEvent(name: "coolingSetpoint", value: setpoint, unit: unit, displayed: false)
updateThermostatSetpoint("coolingSetpoint", setpoint)
// Enforce heatingSetpoint limits, as device doesn't
if (setpoint < getTempInLocalScale("heatingSetpoint")) {
sendCmd << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
setpointType: 1, scale: cmd.scale, precision: cmd.precision, scaledValue: cmd.scaledValue).format())
sendCmd << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format())
sendHubCommand(sendCmd)
}
map1.name = "coolingSetpoint"
break;
default:
log.debug "unknown setpointType $cmd.setpointType"
@@ -191,55 +180,33 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpo
state.size = cmd.size
state.scale = cmd.scale
state.precision = cmd.precision
def mode = device.latestValue("thermostatMode")
if (mode && map1.name.startsWith(mode) || (mode == "emergency heat" && map1.name == "heatingSetpoint")) {
def map2 = [ name: "thermostatSetpoint", value: temp, unit: unit ]
[ createEvent(map1), createEvent(map2) ]
} else {
createEvent(map1)
}
}
// thermostatSetpoint is not displayed by any tile as it can't be predictable calculated due to
// the device's quirkiness but it is defined by the capability so it must be set, set it to the most likely value
def updateThermostatSetpoint(setpoint, value) {
def scale = getTemperatureScale()
def heatingSetpoint = (setpoint == "heatingSetpoint") ? value : getTempInLocalScale("heatingSetpoint")
def coolingSetpoint = (setpoint == "coolingSetpoint") ? value : getTempInLocalScale("coolingSetpoint")
def mode = device.currentValue("thermostatMode")
def thermostatSetpoint = heatingSetpoint // corresponds to (mode == "heat" || mode == "emergency heat")
if (mode == "cool") {
thermostatSetpoint = coolingSetpoint
}
// Just set to average of heating + cooling for mode off and auto
if (mode == "off" || mode == "auto") {
thermostatSetpoint = getTempInLocalScale((heatingSetpoint + coolingSetpoint)/2, scale)
}
sendEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: scale)
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) {
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd)
{
def map = [:]
if (cmd.sensorType == 1) {
map.name = "temperature"
map.unit = getTemperatureScale()
map.value = getTempInLocalScale(cmd.scaledSensorValue, (cmd.scale == 1 ? "F" : "C"))
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C", cmd.precision)
} else if (cmd.sensorType == 5) {
map.name = "humidity"
map.unit = "%"
map.value = cmd.scaledSensorValue
}
sendEvent(map)
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd) {
def map = [:]
if (cmd.sensorType == 1) {
map.name = "temperature"
map.unit = getTemperatureScale()
map.value = getTempInLocalScale(cmd.scaledSensorValue, (cmd.scale == 1 ? "F" : "C"))
} else if (cmd.sensorType == 5) {
map.value = cmd.scaledSensorValue
map.unit = "%"
map.name = "humidity"
}
sendEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd) {
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd)
{
def map = [name: "thermostatOperatingState" ]
switch (cmd.operatingState) {
case physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport.OPERATING_STATE_IDLE:
@@ -264,7 +231,12 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.Thermosta
map.value = "vent economizer"
break
}
sendEvent(map)
def result = createEvent(map)
if (result.isStateChange && device.latestValue("thermostatMode") == "auto" && (result.value == "heating" || result.value == "cooling")) {
def thermostatSetpoint = device.latestValue("${result.value}Setpoint")
result = [result, createEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: getTemperatureScale())]
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanStateReport cmd) {
@@ -280,256 +252,203 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanSt
map.value = "running high"
break
}
sendEvent(map)
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
def map = [name: "thermostatMode", data:[supportedThermostatModes: state.supportedModes]]
def map = [name: "thermostatMode"]
def thermostatSetpoint = null
switch (cmd.mode) {
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF:
map.value = "off"
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_HEAT:
map.value = "heat"
thermostatSetpoint = device.latestValue("heatingSetpoint")
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUXILIARY_HEAT:
map.value = "emergency heat"
thermostatSetpoint = device.latestValue("heatingSetpoint")
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_COOL:
map.value = "cool"
thermostatSetpoint = device.latestValue("coolingSetpoint")
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUTO:
map.value = "auto"
def temp = device.latestValue("temperature")
def heatingSetpoint = device.latestValue("heatingSetpoint")
def coolingSetpoint = device.latestValue("coolingSetpoint")
if (temp && heatingSetpoint && coolingSetpoint) {
if (temp < (heatingSetpoint + coolingSetpoint) / 2.0) {
thermostatSetpoint = heatingSetpoint
} else {
thermostatSetpoint = coolingSetpoint
}
}
break
}
state.lastTriedMode = map.value
sendEvent(map)
updateThermostatSetpoint(null, null)
if (thermostatSetpoint) {
[ createEvent(map), createEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: getTemperatureScale()) ]
} else {
createEvent(map)
}
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) {
def map = [name: "thermostatFanMode", data:[supportedThermostatFanModes: state.supportedFanModes]]
def map = [name: "thermostatFanMode", displayed: false]
switch (cmd.fanMode) {
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
map.value = "auto"
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
map.value = "fanAuto"
break
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW:
map.value = "on"
map.value = "fanOn"
break
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
map.value = "circulate"
map.value = "fanCirculate"
break
}
state.lastTriedFanMode = map.value
sendEvent(map)
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
def supportedModes = []
if(cmd.heat) { supportedModes << "heat" }
if(cmd.cool) { supportedModes << "cool" }
// Make sure off is before auto, this ensures the right setpoint is used based on current temperature when auto is set
if(cmd.off) { supportedModes << "off" }
if(cmd.auto) { supportedModes << "auto" }
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
def supportedModes = ""
if(cmd.off) { supportedModes += "off " }
if(cmd.heat) { supportedModes += "heat " }
if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
if(cmd.cool) { supportedModes += "cool " }
if(cmd.auto) { supportedModes += "auto " }
state.supportedModes = supportedModes
sendEvent(name: "supportedThermostatModes", value: supportedModes, isStateChange: true, displayed: false)
[ createEvent(name:"supportedModes", value: supportedModes, displayed: false),
response(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet()) ]
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
def supportedFanModes = []
if(cmd.auto) { supportedFanModes << "auto" }
if(cmd.low) { supportedFanModes << "on" }
if(cmd.circulation) { supportedFanModes << "circulate" }
def supportedFanModes = ""
if(cmd.auto) { supportedFanModes += "fanAuto " }
if(cmd.low) { supportedFanModes += "fanOn " }
if(cmd.circulation) { supportedFanModes += "fanCirculate " }
state.supportedFanModes = supportedFanModes
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, isStateChange: true, displayed: false)
[ createEvent(name:"supportedFanModes", value: supportedModes, displayed: false),
response(refresh()) ]
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
log.debug "Zwave BasicReport: $cmd"
log.debug "Zwave event received: $cmd"
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def batteryState = cmd.batteryLevel
def map = [name: "battery", unit: "%", value: cmd.batteryLevel]
if ((cmd.batteryLevel == 0xFF) || (cmd.batteryLevel == 0x00)) { // Special value for low battery alert
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "${device.displayName} battery is low"
map.isStateChange = true
batteryState = "low_battery"
} else {
map.value = cmd.batteryLevel
}
state.lastbatt = now()
sendEvent(name: "batteryIcon", value: batteryState, displayed: false)
sendEvent(map)
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
log.warn "Unexpected zwave command $cmd"
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
log.debug "ManufacturerSpecificReport ${cmd}: value:${cmd}"
if (cmd.manufacturerName) {
updateDataValue("manufacturer", cmd.manufacturerName)
}
if (cmd.productTypeId) {
updateDataValue("productTypeId", cmd.productTypeId.toString())
}
if (cmd.productId) {
updateDataValue("productId", cmd.productId.toString())
}
}
def refresh() {
// Only allow refresh every 2 minutes to prevent flooding the Zwave network
def timeNow = now()
if (!state.refreshTriggeredAt || (2 * 60 * 1000 < (timeNow - state.refreshTriggeredAt))) {
state.refreshTriggeredAt = timeNow
// refresh will request battery, prevent multiple request by setting lastbatt now
state.lastbatt = timeNow
// use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved
runIn(2, "poll", [overwrite: true])
// Use encapsulation to differentiate refresh cmds from what the thermostat sends proactively on change
def cmd = zwave.sensorMultilevelV2.sensorMultilevelGet()
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:1).encapsulate(cmd).format()
}
def nextRefreshQuery(name) {
def cmd = null
switch (name) {
case "temperature":
cmd = zwave.thermostatModeV2.thermostatModeGet()
break
case "thermostatMode":
cmd = zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1)
break
case "heatingSetpoint":
cmd = zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2)
break
case "coolingSetpoint":
cmd = zwave.thermostatFanModeV3.thermostatFanModeGet()
break
case "thermostatFanMode":
cmd = zwave.thermostatOperatingStateV2.thermostatOperatingStateGet()
break
case "thermostatOperatingState":
// get humidity, multilevel sensor get to endpoint 2
cmd = zwave.sensorMultilevelV2.sensorMultilevelGet()
return zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:2).encapsulate(cmd).format()
default: return null
}
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:1).encapsulate(cmd).format()
}
def poll() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
cmds << new physicalgraph.device.HubAction(zwave.multiChannelV3.multiInstanceCmdEncap(instance: 1).encapsulate(zwave.sensorMultilevelV3.sensorMultilevelGet()).format()) // temperature
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()) // HeatingSetpoint
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()) // CoolingSetpoint
cmds << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format())
cmds << new physicalgraph.device.HubAction(zwave.multiChannelV3.multiInstanceCmdEncap(instance: 2).encapsulate(zwave.sensorMultilevelV3.sensorMultilevelGet()).format()) // humidity
def time = getTimeAndDay()
log.debug "time: $time"
if (time) {
cmds << new physicalgraph.device.HubAction(zwave.clockV1.clockSet(time).format())
}
// Add 3 seconds delay between each command to avoid flooding the Z-Wave network choking the hub
sendHubCommand(cmds, 3000)
def quickSetHeat(degrees) {
setHeatingSetpoint(degrees, 1000)
}
def raiseHeatingSetpoint() {
alterSetpoint(null, true, "heatingSetpoint")
def setHeatingSetpoint(degrees, delay = 30000) {
setHeatingSetpoint(degrees.toDouble(), delay)
}
def lowerHeatingSetpoint() {
alterSetpoint(null, false, "heatingSetpoint")
}
def raiseCoolSetpoint() {
alterSetpoint(null, true, "coolingSetpoint")
}
def lowerCoolSetpoint() {
alterSetpoint(null, false, "coolingSetpoint")
}
// Adjusts nextHeatingSetpoint either .5° C/1° F) if raise true/false
def alterSetpoint(degrees, raise, setpoint) {
def setHeatingSetpoint(Double degrees, Integer delay = 30000) {
log.trace "setHeatingSetpoint($degrees, $delay)"
def deviceScale = state.scale ?: 1
def deviceScaleString = deviceScale == 2 ? "C" : "F"
def locationScale = getTemperatureScale()
def heatingSetpoint = getTempInLocalScale("heatingSetpoint")
def coolingSetpoint = getTempInLocalScale("coolingSetpoint")
def targetvalue = (setpoint == "heatingSetpoint") ? heatingSetpoint : coolingSetpoint
def delta = (locationScale == "F") ? 1 : 0.5
if (raise != null) {
targetvalue += raise ? delta : - delta
} else if (degrees) {
targetvalue = degrees
def p = (state.precision == null) ? 1 : state.precision
def convertedDegrees
if (locationScale == "C" && deviceScaleString == "F") {
convertedDegrees = celsiusToFahrenheit(degrees)
} else if (locationScale == "F" && deviceScaleString == "C") {
convertedDegrees = fahrenheitToCelsius(degrees)
} else {
log.warn "alterSetpoint called with neither up/down/degree information"
return
}
def data = enforceSetpointLimits(setpoint, [targetvalue: targetvalue, heatingSetpoint: heatingSetpoint, coolingSetpoint: coolingSetpoint])
// update UI without waiting for the device to respond, this to give user a smoother UI experience
// also, as runIn's have to overwrite and user can change heating/cooling setpoint separately separate runIn's have to be used
if (data.targetHeatingSetpoint) {
sendEvent("name": "heatingSetpoint", "value": data.targetHeatingSetpoint, unit: locationScale, eventType: "ENTITY_UPDATE")//, displayed: false)
runIn(4, "updateHeatingSetpoint", [data: data, overwrite: true])
}
if (data.targetCoolingSetpoint) {
sendEvent("name": "coolingSetpoint", "value": data.targetCoolingSetpoint, unit: locationScale, eventType: "ENTITY_UPDATE")//, displayed: false)
runIn(4, "updateCoolingSetpoint", [data: data, overwrite: true])
convertedDegrees = degrees
}
delayBetween([
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format(),
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
], delay)
}
def updateHeatingSetpoint(data) {
updateSetpoints(data)
def quickSetCool(degrees) {
setCoolingSetpoint(degrees, 1000)
}
def updateCoolingSetpoint(data) {
updateSetpoints(data)
def setCoolingSetpoint(degrees, delay = 30000) {
setCoolingSetpoint(degrees.toDouble(), delay)
}
def enforceSetpointLimits(setpoint, data) {
// Enforce max/min for setpoints
def maxSetpoint = getTempInLocalScale(95, "F")
def minSetpoint = getTempInLocalScale(35, "F")
def targetvalue = data.targetvalue
def heatingSetpoint = null
def coolingSetpoint = null
if (targetvalue > maxSetpoint) {
targetvalue = maxSetpoint
} else if (targetvalue < minSetpoint) {
targetvalue = minSetpoint
}
// Enforce limits, for now make sure heating <= cooling, and cooling >= heating
if (setpoint == "heatingSetpoint") {
heatingSetpoint = targetvalue
coolingSetpoint = (heatingSetpoint > data.coolingSetpoint) ? heatingSetpoint : null
}
if (setpoint == "coolingSetpoint") {
coolingSetpoint = targetvalue
heatingSetpoint = (coolingSetpoint < data.heatingSetpoint) ? coolingSetpoint : null
}
return [targetHeatingSetpoint: heatingSetpoint, targetCoolingSetpoint: coolingSetpoint]
}
def setHeatingSetpoint(degrees) {
if (degrees) {
def data = enforceSetpointLimits("heatingSetpoint",
[targetvalue: degrees.toDouble(), heatingSetpoint: getTempInLocalScale("heatingSetpoint"), coolingSetpoint: getTempInLocalScale("coolingSetpoint")])
updateSetpoints(data)
}
}
def setCoolingSetpoint(degrees) {
if (degrees) {
def data = enforceSetpointLimits("coolingSetpoint",
[targetvalue: degrees.toDouble(), heatingSetpoint: getTempInLocalScale("heatingSetpoint"), coolingSetpoint: getTempInLocalScale("coolingSetpoint")])
updateSetpoints(data)
}
}
def updateSetpoints(data) {
def cmds = []
if (data.targetHeatingSetpoint) {
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
setpointType: 1, scale: state.scale, precision: state.precision, scaledValue: convertToDeviceScale(data.targetHeatingSetpoint)).format())
}
if (data.targetCoolingSetpoint) {
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
setpointType: 2, scale: state.scale, precision: state.precision, scaledValue: convertToDeviceScale(data.targetCoolingSetpoint)).format())
}
// Always request both setpoints in case thermostat changed both
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
sendHubCommand(cmds)
}
def convertToDeviceScale(setpoint) {
def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
log.trace "setCoolingSetpoint($degrees, $delay)"
def deviceScale = state.scale ?: 1
def deviceScaleString = deviceScale == 2 ? "C" : "F"
def locationScale = getTemperatureScale()
def deviceScale = (state.scale == 1) ? "F" : "C"
return (deviceScale == locationScale) ? setpoint :
(deviceScale == "F" ? celsiusToFahrenheit(setpoint.toBigDecimal()) : roundC(fahrenheitToCelsius(setpoint.toBigDecimal())))
def p = (state.precision == null) ? 1 : state.precision
def convertedDegrees
if (locationScale == "C" && deviceScaleString == "F") {
convertedDegrees = celsiusToFahrenheit(degrees)
} else if (locationScale == "F" && deviceScaleString == "C") {
convertedDegrees = fahrenheitToCelsius(degrees)
} else {
convertedDegrees = degrees
}
delayBetween([
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 2, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format(),
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()
], delay)
}
/**
@@ -537,56 +456,78 @@ def convertToDeviceScale(setpoint) {
* */
def ping() {
log.debug "ping() called"
// Just get Operating State as it is not reported when it chnages and there's no need to flood more commands
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()))
refresh()
}
def configure() {
delayBetween([
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
], 2300)
}
def modes() {
["off", "heat", "cool", "auto", "emergency heat"]
}
def switchMode() {
def currentMode = device.currentValue("thermostatMode")
def currentMode = device.currentState("thermostatMode")?.value
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
def supportedModes = state.supportedModes
if (supportedModes) {
def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] }
def nextMode = next(lastTriedMode)
setThermostatMode(nextMode)
state.lastTriedMode = nextMode
} else {
log.warn "supportedModes not defined"
def supportedModes = getDataByName("supportedModes")
def modeOrder = modes()
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode)
if (supportedModes?.contains(currentMode)) {
while (!supportedModes.contains(nextMode) && nextMode != "off") {
nextMode = next(nextMode)
}
}
state.lastTriedMode = nextMode
delayBetween([
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[nextMode]).format(),
zwave.thermostatModeV2.thermostatModeGet().format()
], 1000)
}
def switchToMode(nextMode) {
def supportedModes = state.supportedModes
if (supportedModes && supportedModes.contains(nextMode)) {
setThermostatMode(nextMode)
def supportedModes = getDataByName("supportedModes")
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
if (nextMode in modes()) {
state.lastTriedMode = nextMode
"$nextMode"()
} else {
log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}")
log.debug("no mode method '$nextMode'")
}
}
def switchFanMode() {
def currentMode = device.currentState("thermostatFanMode")?.value
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off"
def supportedFanModes = state.supportedFanModes
if (supportedFanModes) {
def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] }
def nextMode = next(lastTriedMode)
setThermostatFanMode(nextMode)
state.lastTriedFanMode = nextMode
} else {
log.warn "supportedFanModes not defined"
def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanOn"
def modeOrder = ["fanAuto", "fanCirculate", "fanOn"]
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode)
while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") {
nextMode = next(nextMode)
}
switchToFanMode(nextMode)
}
def switchToFanMode(nextMode) {
def supportedFanModes = state.supportedFanModes
if (supportedFanModes && supportedFanModes.contains(nextMode)) {
setThermostatFanMode(nextMode)
state.lastTriedFanMode = nextMode
def supportedFanModes = getDataByName("supportedFanModes")
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
def returnCommand
if (nextMode == "fanAuto") {
returnCommand = fanAuto()
} else if (nextMode == "fanOn") {
returnCommand = fanOn()
} else if (nextMode == "fanCirculate") {
returnCommand = fanCirculate()
} else {
log.debug("FanMode $nextMode is not supported by ${device.displayName}")
log.debug("no fan mode '$nextMode'")
}
if(returnCommand) state.lastTriedFanMode = nextMode
returnCommand
}
def getDataByName(String name) {
@@ -602,10 +543,10 @@ def getModeMap() { [
]}
def setThermostatMode(String value) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
sendHubCommand(cmds)
delayBetween([
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format(),
zwave.thermostatModeV2.thermostatModeGet().format()
], standardDelay)
}
def getFanModeMap() { [
@@ -615,70 +556,69 @@ def getFanModeMap() { [
]}
def setThermostatFanMode(String value) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
sendHubCommand(cmds)
delayBetween([
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
], standardDelay)
}
def off() {
switchToMode("off")
delayBetween([
zwave.thermostatModeV2.thermostatModeSet(mode: 0).format(),
zwave.thermostatModeV2.thermostatModeGet().format()
], standardDelay)
}
def heat() {
switchToMode("heat")
delayBetween([
zwave.thermostatModeV2.thermostatModeSet(mode: 1).format(),
zwave.thermostatModeV2.thermostatModeGet().format()
], standardDelay)
}
def emergencyHeat() {
switchToMode("emergency heat")
delayBetween([
zwave.thermostatModeV2.thermostatModeSet(mode: 4).format(),
zwave.thermostatModeV2.thermostatModeGet().format()
], standardDelay)
}
def cool() {
switchToMode("cool")
delayBetween([
zwave.thermostatModeV2.thermostatModeSet(mode: 2).format(),
zwave.thermostatModeV2.thermostatModeGet().format()
], standardDelay)
}
def auto() {
switchToMode("auto")
delayBetween([
zwave.thermostatModeV2.thermostatModeSet(mode: 3).format(),
zwave.thermostatModeV2.thermostatModeGet().format()
], standardDelay)
}
def fanOn() {
switchToFanMode("on")
delayBetween([
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 1).format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
], standardDelay)
}
def fanAuto() {
switchToFanMode("auto")
delayBetween([
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 0).format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
], standardDelay)
}
def fanCirculate() {
switchToFanMode("circulate")
delayBetween([
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 6).format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
], standardDelay)
}
private getTimeAndDay() {
def timeNow = now()
// Need to check that location have timeZone as SC may have created the location without setting it
// Don't update clock more than once a day
if (location.timeZone && (!state.timeClockSet || (24 * 60 * 60 * 1000 < (timeNow - state.timeClockSet)))) {
def currentDate = Calendar.getInstance(location.timeZone)
state.timeClockSet = timeNow
return [hour: currentDate.get(Calendar.HOUR_OF_DAY), minute: currentDate.get(Calendar.MINUTE), weekday: currentDate.get(Calendar.DAY_OF_WEEK)]
}
private getStandardDelay() {
1000
}
// Get stored temperature from currentState in current local scale
def getTempInLocalScale(state) {
def temp = device.currentState(state)
if (temp && temp.value && temp.unit) {
return getTempInLocalScale(temp.value.toBigDecimal(), temp.unit)
}
return 0
}
// get/convert temperature to current local scale
def getTempInLocalScale(temp, scale) {
def scaledTemp = convertTemperatureIfNeeded(temp.toBigDecimal(), scale).toDouble()
return (getTemperatureScale() == "F" ? scaledTemp.round(0).toInteger() : roundC(scaledTemp))
}
def roundC (tempC) {
return (Math.round(tempC.toDouble() * 2))/2
}

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,39 +0,0 @@
# Z-wave Basic Smoke Alarm
Cloud Execution
Works with:
* [First Alert Smoke Detector (ZSMOKE)](https://www.smartthings.com/products/first-alert-smoke-detector)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Battery](#battery-specification)
* [Troubleshooting](#troubleshooting)
## Capabilities
* **Smoke Detector** - measure smoke and optionally carbon monoxide levels
* **Sensor** - detects sensor events
* **Battery** - defines device uses a battery
* **Health Check** - indicates ability to get device health notifications
## Device Health
First Alert Smoke Detector (ZSMOKE) is a Z-wave sleepy device and checks in every 1 hour.
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*60 + 2)mins = 122 mins.
* __122min__ checkInterval
## Battery Specification
Two AA 1.5V batteries are required.
## Troubleshooting
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
Pairing needs to be tried again by placing the device closer to the hub.
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
* [First Alert Smoke Detector (ZSMOKE) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/207150556-First-Alert-Smoke-Detector-ZSMOKE-)

View File

@@ -1,181 +0,0 @@
/**
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Z-Wave Basic Smoke Alarm", namespace: "smartthings", author: "SmartThings") {
capability "Smoke Detector"
capability "Sensor"
capability "Battery"
capability "Health Check"
fingerprint deviceId: "0xA100", inClusters: "0x20,0x80,0x70,0x85,0x71,0x72,0x86"
fingerprint mfr:"0138", prod:"0001", model:"0001", deviceJoinName: "First Alert Smoke Detector"
}
simulator {
status "smoke": "command: 7105, payload: 01 FF"
status "clear": "command: 7105, payload: 01 00"
status "test": "command: 7105, payload: 0C FF"
status "battery 100%": "command: 8003, payload: 64"
status "battery 5%": "command: 8003, payload: 05"
}
tiles (scale: 2){
multiAttributeTile(name:"smoke", type: "lighting", width: 6, height: 4){
tileAttribute ("device.smoke", key: "PRIMARY_CONTROL") {
attributeState("clear", label:"clear", icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff")
attributeState("detected", label:"SMOKE", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
attributeState("tested", label:"TEST", icon:"st.alarm.smoke.test", backgroundColor:"#e86d13")
}
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
main "smoke"
details(["smoke", "battery"])
}
}
def installed() {
// Device checks in every hour, this interval allows us to miss one check-in notification before marking offline
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
def cmds = []
createSmokeEvents("smokeClear", cmds)
cmds.each { cmd -> sendEvent(cmd) }
}
def updated() {
// Device checks in every hour, this interval allows us to miss one check-in notification before marking offline
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
def parse(String description) {
def results = []
if (description.startsWith("Err")) {
results << createEvent(descriptionText:description, displayed:true)
} else {
def cmd = zwave.parse(description, [ 0x80: 1, 0x84: 1, 0x71: 2, 0x72: 1 ])
if (cmd) {
zwaveEvent(cmd, results)
}
}
log.debug "'$description' parsed to ${results.inspect()}"
return results
}
def createSmokeEvents(name, results) {
def text = null
switch (name) {
case "smoke":
text = "$device.displayName smoke was detected!"
// these are displayed:false because the composite event is the one we want to see in the app
results << createEvent(name: "smoke", value: "detected", descriptionText: text)
break
case "tested":
text = "$device.displayName was tested"
results << createEvent(name: "smoke", value: "tested", descriptionText: text)
break
case "smokeClear":
text = "$device.displayName smoke is clear"
results << createEvent(name: "smoke", value: "clear", descriptionText: text)
name = "clear"
break
case "testClear":
text = "$device.displayName test cleared"
results << createEvent(name: "smoke", value: "clear", descriptionText: text)
name = "clear"
break
}
}
def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
if (cmd.zwaveAlarmType == physicalgraph.zwave.commands.alarmv2.AlarmReport.ZWAVE_ALARM_TYPE_SMOKE) {
if (cmd.zwaveAlarmEvent == 3) {
createSmokeEvents("tested", results)
} else {
createSmokeEvents((cmd.zwaveAlarmEvent == 1 || cmd.zwaveAlarmEvent == 2) ? "smoke" : "smokeClear", results)
}
} else switch(cmd.alarmType) {
case 1:
createSmokeEvents(cmd.alarmLevel ? "smoke" : "smokeClear", results)
break
case 12: // test button pressed
createSmokeEvents(cmd.alarmLevel ? "tested" : "testClear", results)
break
case 13: // sent every hour -- not sure what this means, just a wake up notification?
if (cmd.alarmLevel == 255) {
results << createEvent(descriptionText: "$device.displayName checked in", isStateChange: false)
} else {
results << createEvent(descriptionText: "$device.displayName code 13 is $cmd.alarmLevel", isStateChange:true, displayed:false)
}
// Clear smoke in case they pulled batteries and we missed the clear msg
if(device.currentValue("smoke") != "clear") {
createSmokeEvents("smokeClear", results)
}
// Check battery if we don't have a recent battery event
if (!state.lastbatt || (now() - state.lastbatt) >= 48*60*60*1000) {
results << response(zwave.batteryV1.batteryGet())
}
break
default:
results << createEvent(displayed: true, descriptionText: "Alarm $cmd.alarmType ${cmd.alarmLevel == 255 ? 'activated' : cmd.alarmLevel ?: 'deactivated'}".toString())
break
}
}
// SensorBinary and SensorAlarm aren't tested, but included to preemptively support future smoke alarms
//
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd, results) {
if (cmd.sensorType == physicalgraph.zwave.commandclasses.SensorBinaryV2.SENSOR_TYPE_SMOKE) {
createSmokeEvents(cmd.sensorValue ? "smoke" : "smokeClear", results)
}
}
def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd, results) {
if (cmd.sensorType == 1) {
createSmokeEvents(cmd.sensorState ? "smoke" : "smokeClear", results)
}
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) {
results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
if (!state.lastbatt || (now() - state.lastbatt) >= 56*60*60*1000) {
results << response(zwave.batteryV1.batteryGet(), "delay 2000", zwave.wakeUpV1.wakeUpNoMoreInformation())
} else {
results << response(zwave.wakeUpV1.wakeUpNoMoreInformation())
}
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) {
def map = [ name: "battery", unit: "%", isStateChange: true ]
state.lastbatt = now()
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "$device.displayName battery is low!"
} else {
map.value = cmd.batteryLevel
}
results << createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.Command cmd, results) {
def event = [ displayed: false ]
event.linkText = device.label ?: device.name
event.descriptionText = "$event.linkText: $cmd"
results << createEvent(event)
}

View File

@@ -72,7 +72,7 @@ def createEvents(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def poll() {
if (secondsPast(state.lastbatt, 36*60*60)) {
return zwave.batteryV1.batteryGet().format
return zwave.batteryV1.batteryGet().format()
} else {
return null
}

View File

@@ -21,6 +21,8 @@ metadata {
attribute "alarmState", "string"
fingerprint deviceId: "0xA100", inClusters: "0x20,0x80,0x70,0x85,0x71,0x72,0x86"
fingerprint mfr:"0138", prod:"0001", model:"0001", deviceJoinName: "First Alert Smoke Detector"
fingerprint mfr:"0138", prod:"0001", model:"0002", deviceJoinName: "First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO)"
}
@@ -55,10 +57,6 @@ metadata {
def installed() {
// Device checks in every hour, this interval allows us to miss one check-in notification before marking offline
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
def cmds = []
createSmokeOrCOEvents("allClear", cmds) // allClear to set inital states for smoke and CO
cmds.each { cmd -> sendEvent(cmd) }
}
def updated() {
@@ -107,12 +105,6 @@ def createSmokeOrCOEvents(name, results) {
results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false)
name = "clear"
break
case "allClear":
text = "$device.displayName all clear"
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
results << createEvent(name: "carbonMonoxide", value: "clear", displayed: false)
name = "clear"
break
case "testClear":
text = "$device.displayName test cleared"
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)

View File

@@ -2,11 +2,26 @@ import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
/**
* OpenT2T SmartApp Test
*
* Copyright 2016 OpenT2T
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
definition(
name: "OpenT2T SmartApp Test",
namespace: "opent2t",
author: "Microsoft",
description: "SmartApp for end to end SmartThings scenarios via OpenT2T",
author: "OpenT2T",
description: "Test app to test end to end SmartThings scenarios via OpenT2T",
category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
@@ -40,16 +55,16 @@ definition(
//Device Inputs
preferences {
section("Allow Microsoft to control these things...") {
// input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false, hideWhenEmpty: true
// input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false, hideWhenEmpty: true
// input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true
// input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true
// input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true
// input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true
section("Allow OpenT2T to control these things...") {
input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false, hideWhenEmpty: true
input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false, hideWhenEmpty: true
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false, hideWhenEmpty: true
input "thermostats", "capability.thermostat", title: "Which Thermostat?", multiple: true, required: false, hideWhenEmpty: true
// input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false, hideWhenEmpty: true
input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false, hideWhenEmpty: true
}
}
@@ -67,32 +82,36 @@ def getInputs() {
return inputList
}
//API external Endpoints
mappings {
path("/devices") {
action: [
action:
[
GET: "getDevices"
]
}
path("/devices/:id") {
action: [
action:
[
GET: "getDevice"
]
}
path("/update/:id") {
action: [
action:
[
PUT: "updateDevice"
]
}
path("/deviceSubscription") {
action: [
action:
[
POST : "registerDeviceChange",
DELETE: "unregisterDeviceChange"
]
}
path("/locationSubscription") {
action: [
action:
[
POST : "registerDeviceGraph",
DELETE: "unregisterDeviceGraph"
]
@@ -177,7 +196,7 @@ def registerDeviceChange() {
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
} else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)) {
// state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
// For now, we will only have one subscription endpoint per device
// For now, we will only have one subscription endpoint per device
state.deviceSubscriptionMap.remove(deviceId)
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
@@ -292,16 +311,16 @@ def deviceEventHandler(evt) {
def evtDeviceType = getDeviceType(evtDevice)
def deviceData = [];
if (evt.data != null) {
def evtData = parseJson(evt.data)
log.info "Received event for ${evtDevice.displayName}, data: ${evtData}, description: ${evt.descriptionText}"
}
if (evtDeviceType == "thermostat") {
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationMode: getLocationModeInfo(), locationId: location.id]
} else {
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationId: location.id]
}
if(evt.data != null){
def evtData = parseJson(evt.data)
log.info "Received event for ${evtDevice.displayName}, data: ${evtData}, description: ${evt.descriptionText}"
}
def params = [body: deviceData]
@@ -311,10 +330,10 @@ def deviceEventHandler(evt) {
params.uri = "${it}"
if (state.verificationKeyMap[it] != null) {
def key = state.verificationKeyMap[it]
params.headers = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
}
log.trace "POST URI: ${params.uri}"
log.trace "Headers: ${params.headers}"
log.trace "Header: ${params.header}"
log.trace "Payload: ${params.body}"
try {
httpPostJson(params) { resp ->
@@ -344,10 +363,10 @@ def locationEventHandler(evt) {
params.uri = "${it}"
if (state.verificationKeyMap[it] != null) {
def key = state.verificationKeyMap[it]
params.headers = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
}
log.trace "POST URI: ${params.uri}"
log.trace "Headers: ${params.headers}"
log.trace "Header: ${params.header}"
log.trace "Payload: ${params.body}"
try {
httpPostJson(params) { resp ->
@@ -366,7 +385,6 @@ def locationEventHandler(evt) {
private ComputHMACValue(key, data) {
try {
log.debug "data hased: ${data}"
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1")
Mac mac = Mac.getInstance("HmacSHA1")
mac.init(secretKeySpec)
@@ -489,8 +507,7 @@ private getDeviceType(device) {
//Loop through the device capability list to determine the device type.
capabilities.each { capability ->
switch(capability.name.toLowerCase())
{
switch (capability.name.toLowerCase()) {
case "switch":
deviceType = "switch"
@@ -635,8 +652,7 @@ private mapDeviceCommands(command, value) {
if (value == 1 || value == "1" || value == "lock") {
resultCommand = "lock"
resultValue = ""
}
else if (value == 0 || value == "0" || value == "unlock") {
} else if (value == 0 || value == "0" || value == "unlock") {
resultCommand = "unlock"
resultValue = ""
}