/*
* Zooz ZSE70 Outdoor Sensor
* - Model: ZSE70 - ALL FIRMWARE
*
* For Support, Information, and Updates:
* https://community.hubitat.com/t/zooz-sensors/81074
* https://github.com/jtp10181/Hubitat/tree/main/Drivers/zooz
*
Changelog:
## [1.2.2] - 2024-06-28 (@jtp10181)
- Added ZSE70 Outdoor Sensor to package
- Fixed install sequence so device will fully configure at initial pairing
- Update library and common code
* Copyright 2022-2024 Jeff Page
*
* 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.
*
*/
import groovy.transform.Field
@Field static final String VERSION = "1.2.2"
@Field static final String DRIVER = "Zooz-Sensors"
@Field static final String COMM_LINK = "https://community.hubitat.com/t/zooz-sensors/81074"
@Field static final Map deviceModelNames = ["0004:0006":"ZSE70"]
metadata {
definition (
name: "Zooz ZSE70 Outdoor Motion Sensor",
namespace: "jtp10181",
author: "Jeff Page (@jtp10181)",
singleThreaded: true,
importUrl: "https://raw.githubusercontent.com/jtp10181/Hubitat/main/Drivers/zooz/zooz-zse70-outdoor-sensor.groovy"
) {
capability "Sensor"
capability "MotionSensor"
capability "IlluminanceMeasurement"
capability "TemperatureMeasurement"
capability "Battery"
capability "PowerSource"
capability "TamperAlert"
capability "Configuration"
capability "Refresh"
command "setPowerSource", [ [name:"Select Option*", description:"Force powerSource if detection does not work", type: "ENUM", constraints: ["battery","mains"]] ]
//DEBUGGING
//command "debugShowVars"
attribute "syncStatus", "string"
fingerprint mfr:"027A", prod:"0004", deviceId:"0006", inClusters:"0x00,0x00", controllerType: "ZWV" //Zooz ZSE70 Outdoor Sensor
}
preferences {
configParams.each { param ->
if (!param.hidden) {
if (param.options) {
BigDecimal paramVal = getParamValue(param)
input "configParam${param.num}", "enum",
title: fmtTitle("${param.title}"),
description: fmtDesc("• Parameter #${param.num}, Selected: ${paramVal}" + (param?.description ? "
• ${param?.description}" : '')),
defaultValue: param.defaultVal,
options: param.options,
required: false
}
else if (param.range) {
input "configParam${param.num}", "number",
title: fmtTitle("${param.title}"),
description: fmtDesc("• Parameter #${param.num}, Range: ${(param.range).toString()}, DEFAULT: ${param.defaultVal}" + (param?.description ? "
• ${param?.description}" : '')),
defaultValue: param.defaultVal,
range: param.range,
required: false
}
}
}
input "tempOffset", "decimal",
title: fmtTitle("Temperature Offset (Driver)"),
description: fmtDesc("Range: -25.0..25.0, DEFAULT: 0"),
defaultValue: 0, range: "-25..25", required: false
input "lightOffset", "decimal",
title: fmtTitle("Light/Lux Offset (Driver)"),
description: fmtDesc("Range: -100..100, DEFAULT: 0"),
defaultValue: 0, range: "-100..100", required: false
if (state.battery) {
input "wakeUpInt", "number", defaultValue: 12, range: "1..24",
title: fmtTitle("Wake-up Interval (hours)"),
description: fmtDesc("How often the device will wake up to receive commands from the hub")
}
}
}
void debugShowVars() {
log.warn "settings ${settings.hashCode()} ${settings}"
log.warn "paramsList ${paramsList.hashCode()} ${paramsList}"
log.warn "paramsMap ${paramsMap.hashCode()} ${paramsMap}"
}
//Association Settings
@Field static final int maxAssocGroups = 1
@Field static final int maxAssocNodes = 1
/*** Static Lists and Settings ***/
//Sensor Types
@Field static Short SENSOR_TYPE_TEMPERATURE = 0x01
@Field static Short SENSOR_TYPE_LUMINANCE = 0x03
@Field static Short SENSOR_TYPE_HUMIDITY = 0x05
//Notification Types
@Field static Short NOTIFICATION_TYPE_SECURITY = 0x07
@Field static Short NOTIFICATION_TYPE_POWER = 0x08
//Notification Events
@Field static Short EVENT_PARAM_IDLE = 0x00
@Field static Short EVENT_PARAM_TAMPER = 0x03
@Field static Short EVENT_PARAM_TAMPER_MOVED = 0x09
@Field static Short EVENT_PARAM_MOTION = 0x08
@Field static Short EVENT_MAINS_DISCONNECTED = 0x02
@Field static Short EVENT_MAINS_RECONNECTED = 0x03
//Main Parameters Listing
@Field static Map paramsMap =
[
motionSensitivity: [ num:1,
title: "Motion Sensitivity",
size: 1, defaultVal: 6,
options: [0:"Disabled", 1:"1 - Least Sensitive", 2:"2", 3:"3", 4:"4", 5:"5", 6:"6", 7:"7", 8:"8 - Most Sensitive"]
],
motionClear: [ num:2,
title: "Motion Clear Delay / Timeout (seconds)",
size: 2, defaultVal: 30,
range: "10..3600"
],
ledMode: [ num:6,
title: "LED Indicator Mode",
size: 1, defaultVal: 1,
options: [1:"Motion Flash LED", 0:"LED Disabled"],
],
duskDawnMode: [ num:16,
title: "Dusk to Dawn Mode",
description: "Send motion reports only when below this lux level, 0 = Disabled",
size: 2, defaultVal: 0,
range: "0..30000"
],
batteryAlert: [ num:7,
title: "Low Battery Report Level",
size: 1, defaultVal: 10,
range: "10..50"
],
batteryInterval: [ num:8,
title: "Battery Power Check Interval (hours)",
size: 2, defaultVal: 4,
range: "0..744"
],
batteryThreshold: [ num:11,
title: "Battery Change Report Trigger",
size: 1, defaultVal: 2,
range: "0..50"
],
//Temperature
tempVerification: [ num:9,
title: "Temperature Verification Interval (seconds)",
description: "How often the sensor checks the temperature",
size: 2, defaultVal: 30,
range: "0..600"
],
tempInterval: [ num:17,
title: "Temperature Reporting Interval (seconds)",
description: "How often to send temperature reports, 0 = Use Change Trigger",
size: 2, defaultVal: 0,
range: "0..43200"
],
tempThreshold: [ num:12,
title: "Temperature Change Report Trigger",
description: "Temperature in °F, 0 = Disabled",
size: 1, defaultVal: 1,
range: "0..144"
],
//Light / Lux
lightVerification: [ num:10,
title: "Light/Lux Verification Interval (seconds)",
description: "How often the sensor checks the lux",
size: 2, defaultVal: 10,
range: "0..600"
],
lightInterval: [ num:18,
title: "Light/Lux Reporting Interval (seconds)",
description: "How often to send lux reports, 0 = Use Change Trigger",
size: 2, defaultVal: 0,
range: "0..43200"
],
lightThreshold: [ num:13,
title: "Light Change Report Trigger",
description: "Set Lux, 0 = Disabled",
size: 2, defaultVal: 50,
range: "0..30000"
],
// //Offsets
tempOffsetHw: [ num:14,
title: "Temperature Offset (Hardware)",
size: 1, defaultVal: 0,
range: "-10.0..10.0"
],
lightOffsetHw: [ num:15,
title: "Light/Lux Offset (Hardware)",
size: 1, defaultVal: 0,
range: "-100..100"
],
//Temp Units
tempUnits: [ num:5,
title: "Temperature Units:",
size: 1, defaultVal: 1,
options: [0:"Celsius (°C)", 1:"Fahrenheit (°F)"]
],
//Hidden Settings
basicReports: [ num:3,
title: "Basic Set Reports on Motion",
size: 1, defaultVal: 0,
range: "0..7",
hidden: true
],
// basicMode: [ num:15,
// title: "Basic Set Value",
// size: 1, defaultVal: 0,
// options: [0:"Motion = On", 1:"Motion = 0ff"],
// hidden: true
// ],
// sensorReports: [ num:16,
// title: "Binary Sensor Reports on Motion",
// size: 1, defaultVal: 0,
// options: [0:"Disabled", 1:"Enabled"],
// firmVerM: [2:99,3:99,4:99],
// hidden: true
// ],
]
/* ZSE70
CommandClassReport
*/
//Set Command Class Versions
@Field static final Map commandClassVersions = [
0x31: 11, // Sensor Multilevel (sensormultilevelv11)
0x70: 1, // Configuration (configurationv1)
0x71: 8, // Notification (notificationv8)
0x80: 1, // Battery (batteryv1)
0x84: 2, // Wakeup (wakeupv2)
0x85: 2, // Association (associationv2)
0x86: 2, // Version (versionv2) (3)
]
/*******************************************************************
***** Core Functions
********************************************************************/
void installed() {
logWarn "installed..."
state.resyncAll = true
sendPowerEvent(EVENT_MAINS_RECONNECTED)
runIn(2, runWakeupCmds)
sendCommands(getRefreshCmds(),400)
}
void configure() {
logWarn "configure..."
if (device.currentValue("powerSource") == null && !state.resyncAll) {
sendPowerEvent(EVENT_MAINS_DISCONNECTED)
runIn(2, fullConfigure)
List cmds = getRefreshCmds()
if (cmds) sendCommands(cmds,300)
}
else {
fullConfigure()
}
}
void fullConfigure() {
logWarn "fullConfigure..."
if (!pendingChanges || state.resyncAll == null) {
logForceWakeupMessage "Full Re-Configure"
state.resyncAll = true
} else {
logForceWakeupMessage "Pending Configuration Changes"
}
updateSyncingStatus(2)
if (state.battery == false) runIn(1, runWakeupCmds)
}
void updated() {
logDebug "updated..."
checkLogLevel()
if (!firmwareVersion || !state.deviceModel) {
state.resyncAll = true
state.pendingRefresh = true
logForceWakeupMessage "Full Re-Configure and Refresh"
}
if (pendingChanges) {
logForceWakeupMessage "Pending Configuration Changes"
}
else if (!state.resyncAll && !state.pendingRefresh) {
state.remove("INFO")
}
setSubModel()
updateSyncingStatus(2)
if (state.battery == false) {
device.deleteCurrentState("battery")
runIn(1, runWakeupCmds)
}
}
void refresh() {
logDebug "refresh..."
forceRefresh()
}
void forceRefresh() {
logDebug "forceRefresh..."
state.pendingRefresh = true
logForceWakeupMessage "Sensor Info Refresh"
if (state.battery == false) runIn(1, runWakeupCmds)
}
/*******************************************************************
***** Driver Commands
********************************************************************/
/*** Capabilities ***/
/*** Custom Commands ***/
void setPowerSource(String source) {
sendPowerEvent(source == "mains" ? EVENT_MAINS_RECONNECTED : EVENT_MAINS_DISCONNECTED)
}
/*******************************************************************
***** Z-Wave Reports
********************************************************************/
void parse(String description) {
zwaveParse(description)
}
void zwaveEvent(hubitat.zwave.commands.supervisionv1.SupervisionGet cmd, ep=0) {
zwaveSupervision(cmd,ep)
}
void zwaveEvent(hubitat.zwave.commands.configurationv1.ConfigurationReport cmd) {
logTrace "${cmd}"
updateSyncingStatus()
Map param = getParam(cmd.parameterNumber)
Integer val = cmd.scaledConfigurationValue
if (param) {
//Convert scaled signed integer to unsigned
Long sizeFactor = Math.pow(256,param.size).round()
if (val < 0) { val += sizeFactor }
logDebug "${param.name} - ${param.title} (#${param.num}) = ${val.toString()}"
setParamStoredValue(param.num, val)
}
else {
logDebug "Parameter #${cmd.parameterNumber} = ${val.toString()}"
}
}
void zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd) {
logTrace "${cmd}"
updateSyncingStatus()
Integer grp = cmd.groupingIdentifier
if (grp == 1) {
logDebug "Lifeline Association: ${cmd.nodeId}"
state.group1Assoc = (cmd.nodeId == [zwaveHubNodeId]) ? true : false
}
else {
logDebug "Unhandled Group: $cmd"
}
}
void zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd, ep=0) {
logTrace "${cmd} (ep ${ep})"
Integer batLvl = cmd.batteryLevel
if (state.battery == false) {
if (batLvl == 0xFF) {
logDebug "Skipping ${cmd} | powerSource is mains"
return
} else {
sendPowerEvent(EVENT_MAINS_DISCONNECTED)
}
}
if (batLvl == 0xFF) {
batLvl = 1
logWarn "LOW BATTERY WARNING"
}
batLvl = validateRange(batLvl, 100, 1, 100)
String descText = "battery level is ${batLvl}%"
sendEventLog(name:"battery", value:batLvl, unit:"%", desc:descText, isStateChange:true)
}
void zwaveEvent(hubitat.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) {
logTrace "${cmd}"
BigDecimal wakeHrs = safeToDec(cmd.seconds/3600,0,2)
logDebug "WakeUp Interval is $cmd.seconds seconds ($wakeHrs hours)"
device.updateDataValue("zwWakeupInterval", "${cmd.seconds}")
}
void zwaveEvent(hubitat.zwave.commands.wakeupv2.WakeUpNotification cmd, ep=0) {
logTrace "${cmd} (ep ${ep})"
logDebug "WakeUp Notification Received"
runWakeupCmds()
}
void runWakeupCmds() {
List cmds = ["delay 0"]
cmds << batteryGetCmd()
//Refresh all if requested
if (state.pendingRefresh) { cmds += getRefreshCmds() }
//Any configuration needed
cmds += getConfigureCmds()
//This needs a longer delay
cmds << "delay 1400" << wakeUpNoMoreInfoCmd()
//Clear pending status
state.resyncAll = false
state.pendingRefresh = false
state.remove("INFO")
setSubModel()
sendCommands(cmds,300)
}
void zwaveEvent(hubitat.zwave.commands.basicv1.BasicSet cmd, ep=0) {
logTrace "${cmd} (ep ${ep})"
sendEventLog(name:"motion", value:(cmd.value ? "active":"inactive"))
}
void zwaveEvent(hubitat.zwave.commands.sensormultilevelv11.SensorMultilevelReport cmd, ep=0) {
logTrace "${cmd} (ep ${ep})"
switch (cmd.sensorType) {
case SENSOR_TYPE_TEMPERATURE: //0x01
String temp = convertTemperatureIfNeeded(cmd.scaledSensorValue, (cmd.scale ? "F" : "C"), cmd.precision)
BigDecimal offset = safeToDec(settings?.tempOffset,0)
BigDecimal tempOS = safeToDec(temp,0) + offset
logDebug "Temperature Offset by ${offset} from ${temp} to ${tempOS}"
sendEventLog(name:"temperature", value:(safeToDec(tempOS,0,Math.min(cmd.precision,1))), unit:"°${temperatureScale}")
break
case SENSOR_TYPE_LUMINANCE: //0x03
BigDecimal offset = safeToDec(settings?.lightOffset,0)
BigDecimal lightOS = safeToDec(cmd.scaledSensorValue,0) + offset
logDebug "Light/Lux Offset by ${offset} from ${cmd.scaledSensorValue} to ${lightOS}"
sendEventLog(name:"illuminance", value:(Math.round(lightOS)), unit:"lx")
break
default:
logDebug "Unhandled sensorType: ${cmd}"
}
}
void zwaveEvent(hubitat.zwave.commands.notificationv8.NotificationReport cmd, ep=0) {
logTrace "${cmd} (ep ${ep})"
switch (cmd.notificationType) {
case NOTIFICATION_TYPE_SECURITY:
sendSecurityEvent(cmd.event, cmd.eventParameter[0])
break
case NOTIFICATION_TYPE_POWER: //Power Management
sendPowerEvent(cmd.event, cmd.eventParameter[0])
break
default:
logDebug "Unhandled notificationType: ${cmd}"
}
}
/*******************************************************************
***** Event Senders
********************************************************************/
//evt = [name, value, type, unit, desc, isStateChange]
void sendEventLog(Map evt, Integer ep=0) {
//Set description if not passed in
evt.descriptionText = evt.desc ?: "${evt.name} set to ${evt.value}${evt.unit ?: ''}"
//Main Device Events
if (device.currentValue(evt.name).toString() != evt.value.toString() || evt.isStateChange) {
logInfo "${evt.descriptionText}"
} else {
logDebug "${evt.descriptionText} [NOT CHANGED]"
}
//Always send event to update last activity
sendEvent(evt)
}
void sendSecurityEvent(event, parameter) {
Boolean cleared
Integer eventAdj = event
//Idle Event the parameter is the event to clear
if (event == EVENT_PARAM_IDLE) {
eventAdj = parameter
cleared = true
}
switch (eventAdj) {
case EVENT_PARAM_TAMPER:
case EVENT_PARAM_TAMPER_MOVED:
sendEventLog(name:"tamper", value:(cleared ? "clear":"detected"))
break
case EVENT_PARAM_MOTION:
sendEventLog(name:"motion", value:(cleared ? "inactive":"active"))
break
default:
logDebug "Unhandled Security Event: ${event}, ${parameter}"
}
}
void sendPowerEvent(event, parameter=null) {
switch (event) {
case 0x00: break //Idle State - ignored
case 0x02: //AC mains disconnected
sendEventLog(name:"powerSource", value:"battery")
state.battery = true
break
case 0x03: //AC mains re-connected
sendEventLog(name:"powerSource", value:"mains")
device.deleteCurrentState("battery")
state.battery = false
break
default:
logDebug "Unhandled Power Management: ${event}, ${parameter}"
}
}
/*******************************************************************
***** Execute / Build Commands
********************************************************************/
List getConfigureCmds() {
logDebug "getConfigureCmds..."
List cmds = []
Integer wakeSeconds = wakeUpInt ? wakeUpInt*3600 : 43200
if (state.battery && (state.resyncAll || wakeSeconds != (device.getDataValue("zwWakeupInterval") as Integer))) {
logDebug "Settting WakeUp Interval to $wakeSeconds seconds"
cmds << wakeUpIntervalSetCmd(wakeSeconds)
cmds << wakeUpIntervalGetCmd()
}
if (state.resyncAll || !firmwareVersion || !state.deviceModel) {
cmds << mfgSpecificGetCmd()
cmds << versionGetCmd()
}
cmds += getConfigureAssocsCmds(true)
configParams.each { param ->
Integer paramVal = getParamValueAdj(param)
Integer storedVal = getParamStoredValue(param.num)
if ((paramVal != null) && (state.resyncAll || (storedVal != paramVal))) {
logDebug "Changing ${param.name} - ${param.title} (#${param.num}) from ${storedVal} to ${paramVal}"
cmds += configSetGetCmd(param, paramVal)
}
}
if (state.resyncAll) {
clearVariables()
state.battery = (device.currentValue("powerSource") == "battery")
}
state.resyncAll = false
if (cmds) updateSyncingStatus(6)
return cmds ?: []
}
List getRefreshCmds() {
List cmds = []
cmds << wakeUpIntervalGetCmd()
cmds << versionGetCmd()
//Sensors
cmds << sensorMultilevelGetCmd(SENSOR_TYPE_TEMPERATURE)
cmds << sensorMultilevelGetCmd(SENSOR_TYPE_LUMINANCE)
cmds << sensorMultilevelGetCmd(SENSOR_TYPE_HUMIDITY)
//These don't work
//cmds << notificationGetCmd(NOTIFICATION_TYPE_SECURITY, EVENT_PARAM_TAMPER)
//cmds << notificationGetCmd(NOTIFICATION_TYPE_SECURITY, EVENT_PARAM_MOTION)
//Power
//cmds << notificationGetCmd(NOTIFICATION_TYPE_POWER, EVENT_PARAM_IDLE) //Power Management - Idle
//cmds << notificationGetCmd(NOTIFICATION_TYPE_POWER, EVENT_MAINS_DISCONNECTED) //Power Management - Mains Disconnected
cmds << notificationGetCmd(NOTIFICATION_TYPE_POWER, EVENT_MAINS_RECONNECTED) //Power Management - Mains Reconnected
return cmds ?: []
}
List getConfigureAssocsCmds(Boolean logging=false) {
List cmds = []
if (!state.group1Assoc || state.resyncAll) {
if (logging) logDebug "Setting lifeline association..."
cmds << associationSetCmd(1, [zwaveHubNodeId])
cmds << associationGetCmd(1)
}
return cmds
}
private logForceWakeupMessage(msg) {
if (state.battery == false) return
String helpText = "You can force a wake up by clicking the Z-Wave button once."
logWarn "${msg} will execute the next time the device wakes up. ${helpText}"
state.INFO = "*** ${msg} *** Waiting for device to wake up. ${helpText}"
}
private setSubModel() {
}
/*******************************************************************
***** Required for Library
********************************************************************/
//These have to be added in after the fact or groovy complains
void fixParamsMap() {
paramsMap['settings'] = [fixed: true]
}
Integer getParamValueAdj(Map param) {
BigDecimal paramVal = getParamValue(param)
switch(param.name) {
case "tempOffsetHw":
case "humidOffsetHw":
//Convert -10.0..10.0 range to 0..200
paramVal = (paramVal * 10) + 100
paramVal = validateRange(paramVal, 100, 0, 200)
break
case "lightOffsetHw":
//Convert -100..100 range to 0..200
paramVal = paramVal + 100
paramVal = validateRange(paramVal, 100, 0, 200)
break
}
return (paramVal as Integer)
}
//#include jtp10181.zwaveDriverLibrary
/*******************************************************************
*******************************************************************
***** Z-Wave Driver Library by Jeff Page (@jtp10181)
*******************************************************************
********************************************************************
Changelog:
2023-05-10 - First version used in drivers
2023-05-12 - Adjustments to community links
2023-05-14 - Updates for power metering
2023-05-18 - Adding requirement for getParamValueAdj in driver
2023-05-24 - Fix for possible RuntimeException error due to bad cron string
2023-10-25 - Less saving to the configVals data, and some new functions
2023-10-26 - Added some battery shortcut functions
2023-11-08 - Added ability to adjust settings on firmware range
2024-01-28 - Adjusted logging settings for new / upgrade installs, added mfgSpecificReport
2024-06-15 - Added isLongRange function, convert range to string to prevent expansion
********************************************************************/
library (
author: "Jeff Page (@jtp10181)",
category: "zwave",
description: "Z-Wave Driver Library",
name: "zwaveDriverLibrary",
namespace: "jtp10181",
documentationLink: ""
)
/*******************************************************************
***** Z-Wave Reports (COMMON)
********************************************************************/
//Include these in Driver
//void parse(String description) {zwaveParse(description)}
//void zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {zwaveMultiChannel(cmd)}
//void zwaveEvent(hubitat.zwave.commands.supervisionv1.SupervisionGet cmd, ep=0) {zwaveSupervision(cmd,ep)}
void zwaveParse(String description) {
hubitat.zwave.Command cmd = zwave.parse(description, commandClassVersions)
if (cmd) {
logTrace "parse: ${description} --PARSED-- ${cmd}"
zwaveEvent(cmd)
} else {
logWarn "Unable to parse: ${description}"
}
//Update Last Activity
updateLastCheckIn()
}
//Decodes Multichannel Encapsulated Commands
void zwaveMultiChannel(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
hubitat.zwave.Command encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions)
logTrace "${cmd} --ENCAP-- ${encapsulatedCmd}"
if (encapsulatedCmd) {
zwaveEvent(encapsulatedCmd, cmd.sourceEndPoint as Integer)
} else {
logWarn "Unable to extract encapsulated cmd from $cmd"
}
}
//Decodes Supervision Encapsulated Commands (and replies to device)
void zwaveSupervision(hubitat.zwave.commands.supervisionv1.SupervisionGet cmd, ep=0) {
hubitat.zwave.Command encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions)
logTrace "${cmd} --ENCAP-- ${encapsulatedCmd}"
if (encapsulatedCmd) {
zwaveEvent(encapsulatedCmd, ep)
} else {
logWarn "Unable to extract encapsulated cmd from $cmd"
}
sendCommands(secureCmd(zwave.supervisionV1.supervisionReport(sessionID: cmd.sessionID, reserved: 0, moreStatusUpdates: false, status: 0xFF, duration: 0), ep))
}
void zwaveEvent(hubitat.zwave.commands.versionv2.VersionReport cmd) {
logTrace "${cmd}"
String fullVersion = String.format("%d.%02d",cmd.firmware0Version,cmd.firmware0SubVersion)
String zwaveVersion = String.format("%d.%02d",cmd.zWaveProtocolVersion,cmd.zWaveProtocolSubVersion)
device.updateDataValue("firmwareVersion", fullVersion)
device.updateDataValue("protocolVersion", zwaveVersion)
device.updateDataValue("hardwareVersion", "${cmd.hardwareVersion}")
logDebug "Received Version Report - Firmware: ${fullVersion}"
setDevModel(new BigDecimal(fullVersion))
}
void zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
logTrace "${cmd}"
device.updateDataValue("manufacturer",cmd.manufacturerId.toString())
device.updateDataValue("deviceType",cmd.productTypeId.toString())
device.updateDataValue("deviceId",cmd.productId.toString())
logDebug "fingerprint mfr:\"${hubitat.helper.HexUtils.integerToHexString(cmd.manufacturerId, 2)}\", "+
"prod:\"${hubitat.helper.HexUtils.integerToHexString(cmd.productTypeId, 2)}\", "+
"deviceId:\"${hubitat.helper.HexUtils.integerToHexString(cmd.productId, 2)}\", "+
"inClusters:\"${device.getDataValue("inClusters")}\""+
(device.getDataValue("secureInClusters") ? ", secureInClusters:\"${device.getDataValue("secureInClusters")}\"" : "")
}
void zwaveEvent(hubitat.zwave.Command cmd, ep=0) {
logDebug "Unhandled zwaveEvent: $cmd (ep ${ep})"
}
/*******************************************************************
***** Z-Wave Command Shortcuts
********************************************************************/
//These send commands to the device either a list or a single command
void sendCommands(List cmds, Long delay=200) {
sendHubCommand(new hubitat.device.HubMultiAction(delayBetween(cmds, delay), hubitat.device.Protocol.ZWAVE))
}
//Single Command
void sendCommands(String cmd) {
sendHubCommand(new hubitat.device.HubAction(cmd, hubitat.device.Protocol.ZWAVE))
}
//Consolidated zwave command functions so other code is easier to read
String associationSetCmd(Integer group, List nodes) {
return secureCmd(zwave.associationV2.associationSet(groupingIdentifier: group, nodeId: nodes))
}
String associationRemoveCmd(Integer group, List nodes) {
return secureCmd(zwave.associationV2.associationRemove(groupingIdentifier: group, nodeId: nodes))
}
String associationGetCmd(Integer group) {
return secureCmd(zwave.associationV2.associationGet(groupingIdentifier: group))
}
String mcAssociationGetCmd(Integer group) {
return secureCmd(zwave.multiChannelAssociationV3.multiChannelAssociationGet(groupingIdentifier: group))
}
String versionGetCmd() {
return secureCmd(zwave.versionV2.versionGet())
}
String mfgSpecificGetCmd() {
return secureCmd(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
}
String switchBinarySetCmd(Integer value, Integer ep=0) {
return secureCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: value), ep)
}
String switchBinaryGetCmd(Integer ep=0) {
return secureCmd(zwave.switchBinaryV1.switchBinaryGet(), ep)
}
String switchMultilevelSetCmd(Integer value, Integer duration, Integer ep=0) {
return secureCmd(zwave.switchMultilevelV4.switchMultilevelSet(dimmingDuration: duration, value: value), ep)
}
String switchMultilevelGetCmd(Integer ep=0) {
return secureCmd(zwave.switchMultilevelV4.switchMultilevelGet(), ep)
}
String switchMultilevelStartLvChCmd(Boolean upDown, Integer duration, Integer ep=0) {
//upDown: false=up, true=down
return secureCmd(zwave.switchMultilevelV4.switchMultilevelStartLevelChange(upDown: upDown, ignoreStartLevel:1, dimmingDuration: duration), ep)
}
String switchMultilevelStopLvChCmd(Integer ep=0) {
return secureCmd(zwave.switchMultilevelV4.switchMultilevelStopLevelChange(), ep)
}
String meterGetCmd(meter, Integer ep=0) {
return secureCmd(zwave.meterV3.meterGet(scale: meter.scale), ep)
}
String meterResetCmd(Integer ep=0) {
return secureCmd(zwave.meterV3.meterReset(), ep)
}
String wakeUpIntervalGetCmd() {
return secureCmd(zwave.wakeUpV2.wakeUpIntervalGet())
}
String wakeUpIntervalSetCmd(val) {
return secureCmd(zwave.wakeUpV2.wakeUpIntervalSet(seconds:val, nodeid:zwaveHubNodeId))
}
String wakeUpNoMoreInfoCmd() {
return secureCmd(zwave.wakeUpV2.wakeUpNoMoreInformation())
}
String batteryGetCmd() {
return secureCmd(zwave.batteryV1.batteryGet())
}
String sensorMultilevelGetCmd(sensorType) {
Integer scale = (temperatureScale == "F" ? 1 : 0)
return secureCmd(zwave.sensorMultilevelV11.sensorMultilevelGet(scale: scale, sensorType: sensorType))
}
String notificationGetCmd(notificationType, eventType, Integer ep=0) {
return secureCmd(zwave.notificationV3.notificationGet(notificationType: notificationType, v1AlarmType:0, event: eventType), ep)
}
String configSetCmd(Map param, Integer value) {
//Convert from unsigned to signed for scaledConfigurationValue
Long sizeFactor = Math.pow(256,param.size).round()
if (value >= sizeFactor/2) { value -= sizeFactor }
return secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: value))
}
String configGetCmd(Map param) {
return secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num))
}
List configSetGetCmd(Map param, Integer value) {
List cmds = []
cmds << configSetCmd(param, value)
cmds << configGetCmd(param)
return cmds
}
/*******************************************************************
***** Z-Wave Encapsulation
********************************************************************/
//Secure and MultiChannel Encapsulate
String secureCmd(String cmd) {
return zwaveSecureEncap(cmd)
}
String secureCmd(hubitat.zwave.Command cmd, ep=0) {
return zwaveSecureEncap(multiChannelEncap(cmd, ep))
}
//MultiChannel Encapsulate if needed
//This is called from secureCmd or supervisionEncap, do not call directly
String multiChannelEncap(hubitat.zwave.Command cmd, ep) {
//logTrace "multiChannelEncap: ${cmd} (ep ${ep})"
if (ep > 0) {
cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:ep).encapsulate(cmd)
}
return cmd.format()
}
/*******************************************************************
***** Common Functions
********************************************************************/
/*** Parameter Store Map Functions ***/
@Field static Map configsList = new java.util.concurrent.ConcurrentHashMap()
Integer getParamStoredValue(Integer paramNum) {
//Using Data (Map) instead of State Variables
Map configsMap = getParamStoredMap()
return safeToInt(configsMap[paramNum], null)
}
void setParamStoredValue(Integer paramNum, Integer value) {
//Using Data (Map) instead of State Variables
TreeMap configsMap = getParamStoredMap()
configsMap[paramNum] = value
configsList[device.id][paramNum] = value
//device.updateDataValue("configVals", configsMap.inspect())
}
Map getParamStoredMap() {
TreeMap configsMap = configsList[device.id]
if (configsMap == null) {
configsMap = [:]
if (device.getDataValue("configVals")) {
try {
configsMap = evaluate(device.getDataValue("configVals"))
}
catch(Exception e) {
logWarn("Clearing Invalid configVals: ${e}")
device.removeDataValue("configVals")
}
}
configsList[device.id] = configsMap
}
return configsMap
}
//Parameter List Functions
//This will rebuild the list for the current model and firmware only as needed
//paramsList Structure: MODEL:[FIRMWARE:PARAM_MAPS]
//PARAM_MAPS [num, name, title, description, size, defaultVal, options, firmVer]
@Field static Map> paramsList = new java.util.concurrent.ConcurrentHashMap()
void updateParamsList() {
logDebug "Update Params List"
String devModel = state.deviceModel
Short modelNum = deviceModelShort
Short modelSeries = Math.floor(modelNum/10)
BigDecimal firmware = firmwareVersion
List