/*
* Securifi Sensors
*
* Description:
* This Hubitat driver is designed for use with the following Securifi/Sercomm sensors:
* SZ-PIR04 Motion Sensor - WORKING
* SZ-PIR02 Motion Sensor - WORKING
* SZ-PIR02_SF Motion Sensor - WORKING
* SZ-DWS08 Door/Window Sensor - WORKING
* SZ-DWS04 Door/Window Sensor - WORKING
* SZ-DWS04N_SF Door/Window Sensor - WORKING
* SZ-DWS02 Door/Window Sensor - WORKING
* SZ-WTD01 Flood Sensor - NOT WORKING (Do not have one)
*
* Features List:
* Ability to detect motion
* Ability to determine contact switch state
* Ability to show tampered state and reset tampering
* Ability to determine sensor type based on model
* Ability to return battery state
* Ability to return temperature
* Ability to check a website (mine) if there is a newer version of the driver available
*
* Licensing:
* Copyright 2024 David Snell
* 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.
*
* Version Control:
* 0.98.3 - Removal of old driver-specific attributes when Preferences are saved and changes to driver update checking
* 0.98.2 - Update for motion sensor reporting motion in different zone data
* 0.98.1 - Update for motion sensor reporting motion in different zone data
* 0.98.0 - Rework to semver versioning and other updates, added changes for SZ-PIR02_SF
* 0.97 - Trying to handle ZDO Device Announcements a bit cleaner
* 0.96 - Rework configuration and reading of ZigBee
* 0.95 - Further additions to reporting & device identification, plus tweaking for SZ-DWS04N_SF
* 0.94 - Massive rework of reporting and starting work on SZ-DWS04N_SF
* 0.93 - Update to driver version checking section
* 0.92 - Major cleanup of temperature and battery reporting as well as general ZigBee reporting
* 0.91 - Corrected fingerprints and ClusterId detection
* 0.9 - Additional fingerprint for SZ-PIR02_SF
* 0.8 - Removed buttons now that they have been separated to their own driver
* 0.7 - Changes to update notification code
* 0.6 - Additional ZigBee parsing recognition
* 0.5 - Additional ZigBee parsing recognition and support for SZ-KFB01 (Note: * button does not appear to report) and SZ-DWS02
* 0.4 - Added support for SZ-DWS08 and fixed mistakes in the ZigBee configuration section
* 0.3 - Added support for ZB2-BU01
* 0.2 - Added support for SZ-PIR04 and SZ-DWS04
* 0.1 - Initial inspiration/learning from Hubitat's Environmental Sensor for SZ-PIR02
*
* Thank you(s):
* I would like to thank @Cobra his contributions to the community. Parts based on Cobra's driver update code
* have been included at the bottom of the driver and are noted as such.
*/
// Returns the driver name
def DriverName(){
return "Securifi Sensors"
}
// Returns the driver version
def DriverVersion(){
return "0.98.3"
}
import hubitat.zigbee.zcl.DataType
metadata{
definition( name: "Securifi Sensors", namespace: "Snell", author: "David Snell", importUrl: "https://www.drdsnell.com/projects/hubitat/drivers/SecurifiSensors.groovy" ){
capability "Configuration"
capability "Refresh"
capability "Sensor"
capability "Battery"
capability "Temperature Measurement"
capability "Motion Sensor"
capability "Contact Sensor"
capability "Tamper Alert"
// Commands that have been implemented
//command "Identify"
command "ResetTamper"
// Attributes - Driver Related
attribute "DriverName", "string" // Identifies the driver being used for update purposes
attribute "DriverVersion", "string" // Handles version for driver
attribute "DriverStatus", "string" // Handles version notices for driver
// SensorType is used to determine what type of sensor is actually being used
attribute "SensorType", "string"
// Message is used to provide a message to the user of the device
attribute "Message", "string"
fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0402, 0020, 0500, 0B05", outClusters: "0003", manufacturer: "Sercomm Corp.", model: "SZ-DWS08", deviceJoinName: "Door/Window Sensor"
fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0402, 0020, 0500, 0B05", outClusters: "0003", manufacturer: "Sercomm Corp.", model: "SZ-DWS04", deviceJoinName: "Door/Window Sensor"
fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0402, 0500, 0020, 0B05", outClusters: "0003, 0019", manufacturer: "Sercomm Corp.", model: "SZ-DWS04N_SF", deviceJoinName: "Door/Window Sensor"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0500", outClusters: "0003", manufacturer: "Sercomm Corp.", model: "SZ-DWS02", deviceJoinName: "Door/Window Sensor"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0500", outClusters: "0003", manufacturer: "Sercomm Corp.", model: "SZ-PIR02", deviceJoinName: "Motion Sensor"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0500", outClusters: "0003", manufacturer: "Sercomm Corp.", model: "SZ-PIR02_SF", deviceJoinName: "Motion Sensor"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0402, 0500", outClusters: "0003", manufacturer: "Sercomm Corp.", model: "SZ-PIR04N", deviceJoinName: "Motion Sensor"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0500", outClusters: "0003", manufacturer: "Sercomm Corp.", model: "SZ-WTD01", deviceJoinName: "Flood Sensor"
}
preferences{
section{
input( type: "enum", name: "ReportInterval", title: "Temperature reporting interval?", description: "Only for SZ-DWS04, SZ-DWS08, or SZ-PIR04", required: false, multiple: false, options: [ "1 minute" ,"5 minutes", "10 minutes", "1 hour", "1 day", "Disabled" ], defaultValue: "Disabled" )
input( type: "enum", name: "LogType", title: "Enable Logging?", required: true, multiple: false, options: [ "None", "Info", "Debug", "Trace" ], defaultValue: "Info" )
}
}
}
// Called when preferences are saved
// This clears state variables then sets some basic information as well as does a reconfig of the device
def updated(){
Logging( "Saved preferences", 2 )
state.clear()
// Set the driver name and version before update checking is scheduled
if( state."Driver Name" != null ){
state.remove( "Driver Name" )
state.remove( "Driver Version" )
device.deleteCurrentState( "Driver Name" )
device.deleteCurrentState( "Driver Version" )
}
ProcessState( "DriverName", "${ DriverName() }" )
ProcessState( "DriverVersion", "${ DriverVersion() }" )
// DetermineDeviceType called to identify device, really only needed after driver is changed
DetermineDeviceType()
// Set up the regular checks for driver version
unschedule()
schedule( new Date(), CheckForUpdate )
// Configure the regular ZigBee reporting
configure()
}
// Check the model information to determine what the device is
private DetermineDeviceType(){
if( device.data.model.startsWith( "SZ-DWS" ) ){
Logging( "model ${ device.data.model } = contact sensor", 4 )
state.SensorType = "Contact"
ResetTamper()
} else if( device.data.model.startsWith( "SZ-PIR" ) ){
Logging( "model ${ device.data.model } = motion sensor", 4 )
state.SensorType = "Motion"
ResetTamper()
} else if( device.data.model.startsWith( "SZ-WTD" ) ){
Logging( "model ${ device.data.model } = flood sensor", 4 )
state.SensorType = "Flood"
} else {
state.SensorType = "Unknown"
Logging( "model ${ device.data.model } = unknown sensor", 3 )
}
}
// Parse incoming device messages to generate events
def parse( String description ){
if( description?.startsWith( "zone status" ) ){
parseZoneMessage( description )
} else {
parseReport( description )
}
}
// parses Zone Messaging ( security related, such as motion, contacts, and tampering )
private Map parseZoneMessage( String description ){
def ZoneStatus = null as String
Logging( "Zone Status Desc = ${ description }", 4 )
ZoneStatus = description.substring( 14, 18 )
switch( ZoneStatus ){
case "0020": // Motion Inactive
if( state.SensorType == "Motion" ){
Logging( "Motion inactive", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
} else if( state.SensorType == "Contact" ){
Logging( "Contact closed", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
}
break
case "0021": // Motion Active
if( state.SensorType == "Motion" ){
Logging( "Motion active", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
} else if( state.SensorType == "Contact" ){
Logging( "Contact open", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
}
break
case "0022": //Tampered
Logging( "Tampered", 4 )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
break
case "0023": //Low Battery
Logging( "Low battery notice", 4 )
state.Message = "Low battery"
break
case "0024": // Tampered and closed
if( state.SensorType == "Contact" ){
Logging( "Contact closed and tampered", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion inactive and tampered", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
}
break
case "0025": // Tampered and opened
if( state.SensorType == "Contact" ){
Logging( "Contact open and tampered", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion active and tampered", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
}
break
case "0026": // Trouble
Logging( "Trouble notice", 3 )
break
case "0027": // AC Mains fault
Logging( "AC Mains fault notice", 3 )
break
case "0028": // Battery defect with contact closed or motion inactive
if( state.SensorType == "Contact" ){
Logging( "Battery defect and contact closed", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Battery defect and motion inactive", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
} else {
Logging( "0028 notice", 3 )
}
break
case "0029": // Battery defect with contact open or motion active
if( state.SensorType == "Contact" ){
Logging( "Battery defect and contact open", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Battery defect and motion active", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
} else {
Logging( "0029 notice", 3 )
}
break
case "0030": // Contact Closed and Motion Inactive
if( state.SensorType == "Contact" ){
Logging( "Contact closed", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion inactive", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
}
break
case "0031": // Contact Open and Motion Active
if( state.SensorType == "Contact" ){
Logging( "Contact open", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion active", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
}
break
case "0034": // Contact Closed and Tampered
if( state.SensorType == "Contact" ){
Logging( "Contact closed and tampered", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion inactive and tampered", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
}
break
case "0035": // Contact Open and Tampered
if( state.SensorType == "Contact" ){
Logging( "Contact open and tampered", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion active and tampered", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
}
break
case "0038": // Motion Inactive
if( state.SensorType == "Motion" ){
Logging( "Motion inactive", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
}
break
case "0039": // Motion Active
if( state.SensorType == "Motion" ){
Logging( "Motion active", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
}
break
case "0228": // Contact Open
if( state.SensorType == "Contact" ){
Logging( "Contact open", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
}
break
case "0229": // Contact Closed
if( state.SensorType == "Contact" ){
Logging( "Contact closed", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
}
break
default:
Logging( "${ ZoneStatus }", 3 )
break
}
}
/*
* This parses ZigBee reports. I am including as many clusters as possible in here, even ones this
* driver will never handle, so that it can be a useful reference for future work or others.
*/
private parseReport( String description ){
def descMap = zigbee.parseDescriptionAsMap( description )
def cluster = descMap.cluster ?: descMap.clusterId
def attrId = descMap.attrId
def ClusterIdentified = "Unknown" as String // Meant for logging with class data came in for
def Handled = false as boolean // Meant to help identify if a report handler existed for this data
switch( cluster ){
// General Clusters
case "0000": // Basic
ClusterIdentified = "Basic"
BasicReport( descMap )
Handled = true
break
case "0001": // Power configuration
ClusterIdentified = "Power config"
BatteryReport( descMap )
Handled = true
break
case "0002": // Device temperature config
ClusterIdentified = "Device temperature config"
break
case "0003": // Identify
ClusterIdentified = "Identify"
IdentifyReport( descMap )
Handled = true
break
case "0004": // Groups
ClusterIdentified = "Groups"
break
case "0005": // Scenes
ClusterIdentified = "Scenes"
break
case "0006": // On/Off
ClusterIdentified = "On/Off"
break
case "0007": // On/Off switch configuration
ClusterIdentified = "On/Off switch configuration"
break
case "0008": // Level control
ClusterIdentified = "Level control"
break
case "0009": // Alarms
ClusterIdentified = "Alarms"
break
case "000A": // Time
ClusterIdentified = "Time"
break
case "000B": // RSSI Location
ClusterIdentified = "RSSI Location"
break
case "000C": // Analog Input
ClusterIdentified = "Analog Input"
break
case "000D": // Analog Output
ClusterIdentified = "Analog Output"
break
case "000E": // Analog Value
ClusterIdentified = "Analog Value"
break
case "000F": // Binary Input
ClusterIdentified = "Binary Input"
break
case "0010": // Binary Output
ClusterIdentified = "Binary Output"
break
case "0011": // Binary Value
ClusterIdentified = "Binary Value"
break
case "0012": // Multistate Input
ClusterIdentified = "Multistate Input"
break
case "0013": // Multistate Output & ZDO Device Announce
ClusterIdentified = "Multistate Output & ZDO Device Announce"
AnnounceReport( descMap )
Handled = true
break
case "0014": // Multistate Value
ClusterIdentified = "Multistate Value"
break
case "0015": // Commissioning
ClusterIdentified = "Commissioning"
break
case "0016": // Partition Cluster
ClusterIdentified = "Partition Cluster"
break
case "0017": // Node_Desc_Store_req
ClusterIdentified = "Node_Desc_Store_req"
break
case "0018": // Power_Desc_Store_req
ClusterIdentified = "Power_Desc_Store_req"
break
case "0019": // OTA Upgrades
ClusterIdentified = "OTA Upgrades"
break
case "001A": // Power Profile
ClusterIdentified = "Power Profile"
break
case "001B": // Appliance Control
ClusterIdentified = "Appliance Control"
break
case "001C": // Find_Node_Cache_req
ClusterIdentified = "Find_Node_Cache_req"
break
case "0020": // Poll Control
ClusterIdentified = "Poll Control"
break
case "0021": // Bind_req
ClusterIdentified = "Bind_req"
break
case "0022": // Unbind_req
ClusterIdentified = "Unbind_req"
break
case "0023": // Bind_Register_req
ClusterIdentified = "Bind_Register_req"
break
case "0024": // Replace_Device_req
ClusterIdentified = "Replace_Device_req"
break
case "0025": // Store_Bkup_Bind_Entry_req
ClusterIdentified = "Store_Bkup_Bind_Entry_req"
break
case "0026": // Rm_Bkup_Bind_Entry_req
ClusterIdentified = "Rm_Bkup_Bind_Entry_req"
break
case "0027": // Backup_Bind_Table_req
ClusterIdentified = "Backup_Bind_Table_req"
break
case "0028": // Recover_Bind_Table_req
ClusterIdentified = "Recover_Bind_Table_req"
break
case "0029": // Backup_Source_Bind_req
ClusterIdentified = "Backup_Source_Bind_req"
break
case "002A": // Recover_Source_Bind_req
ClusterIdentified = "Recover_Source_Bind_req"
break
// Network Management
case "0030": // Mgmt_NWK_Disc_req
ClusterIdentified = "Mgmt_NWK_Disc_req"
break
case "0031": // Mgmt_LQI_req
ClusterIdentified = "Mgmt_LQI_req"
break
case "0032": // Mgmt_Rtg_req
ClusterIdentified = "Mgmt_Rtg_req"
break
case "0033": // Mgmt_Bind_req
ClusterIdentified = "Mgmt_Bind_req"
break
case "0034": // Mgmt_Leave_req
ClusterIdentified = "Mgmt_Leave_req"
break
case "0035": // Mgmt_Direct_Join_req
ClusterIdentified = "Mgmt_Direct_Join_req"
break
case "0036": // Mgmt_Permit_Join_req
ClusterIdentified = "Mgmt_Permit_Join_req"
break
case "0037": // Mgmt_Cache_req
ClusterIdentified = "Mgmt_Cache_req"
break
case "0038": // Management Network Update Request
ClusterIdentified = "Management Network Update Request"
break
// Closures Clusters
case "0100": // Shade configuration
ClusterIdentified = "Shade configuration"
break
case "0101": // Door Lock
ClusterIdentified = "Door Lock"
break
case "0102": // Window Covering
ClusterIdentified = "Window Covering"
break
case "0103": // Barrier Control
ClusterIdentified = "Barrier Control"
break
// HVAC Clusters
case "0200": // Pump config and control
ClusterIdentified = "Pump config and control"
break
case "0201": // Thermostat
ClusterIdentified = "Thermostat"
break
case "0202": // Fan Control
ClusterIdentified = "Fan control"
break
case "0203": // Dehumidifier control
ClusterIdentified = "Dehumidifier control"
break
case "0204": // Thermostat UI config
ClusterIdentified = "Thermostat UI configur"
break
// Lighting Clusters
case "0300": // Color control
ClusterIdentified = "Color control"
break
case "0301": // Ballast configuration
ClusterIdentified = "Ballast configuration"
break
// Measurement and Sensing Clusters
case "0400": // Luminance measurement
ClusterIdentified = "Luminance measurement"
break
case "0401": // Luminance level sensing
ClusterIdentified = "Luminance level sensing"
break
case "0402": // Temperature measurement
ClusterIdentified = "Temperature measurement"
TemperatureReport( descMap )
Handled = true
break
case "0403": // Pressure measurement
ClusterIdentified = "Pressure measurement"
break
case "0404": // Flow measurement
ClusterIdentified = "Flow measurement"
break
case "0405": // Relative humidity measurement
ClusterIdentified = "Relative humidity measurement"
break
case "0406": // Occupancy sensing
ClusterIdentified = "Occupancy sensing"
break
case "040C": // CARBON_MONOXIDE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "CARBON_MONOXIDE_CONCENTRATION_MEASUREMENT"
break
case "040D": // CARBON_DIOXIDE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "CARBON_DIOXIDE_CONCENTRATION_MEASUREMENT"
break
case "040E": // ETHYLENE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "ETHYLENE_CONCENTRATION_MEASUREMENT"
break
case "040F": // ETHYLENE_OXIDE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "ETHYLENE_OXIDE_CONCENTRATION_MEASUREMENT"
break
case "0410": // HYDROGEN_CONCENTRATION_MEASUREMENT
ClusterIdentified = "HYDROGEN_CONCENTRATION_MEASUREMENT"
break
case "0411": // HYDROGEN_SULPHIDE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "HYDROGEN_SULPHIDE_CONCENTRATION_MEASUREMENT"
break
case "0412": // NITRIC_OXIDE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "NITRIC_OXIDE_CONCENTRATION_MEASUREMENT"
break
case "0413": // NITROGEN_DIOXIDE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "NITROGEN_DIOXIDE_CONCENTRATION_MEASUREMENT"
break
case "0414": // OXYGEN_CONCENTRATION_MEASUREMENT
ClusterIdentified = "OXYGEN_CONCENTRATION_MEASUREMENT"
break
case "0415": // OZONE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "OZONE_CONCENTRATION_MEASUREMENT"
break
case "0416": // SULFUR_DIOXIDE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "SULFUR_DIOXIDE_CONCENTRATION_MEASUREMENT"
break
case "0417": // DISSOLVED_OXYGEN_CONCENTRATION_MEASUREMENT
ClusterIdentified = "DISSOLVED_OXYGEN_CONCENTRATION_MEASUREMENT"
break
case "0418": // BROMATE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "BROMATE_CONCENTRATION_MEASUREMENT"
break
case "0419": // CHLORAMINES_CONCENTRATION_MEASUREMENT
ClusterIdentified = "CHLORAMINES_CONCENTRATION_MEASUREMENT"
break
case "041A": // CHLORINE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "CHLORINE_CONCENTRATION_MEASUREMENT"
break
case "041B": // FECAL_COLIFORM_AND_E_COLI_CONCENTRATION_MEASUREMENT
ClusterIdentified = "FECAL_COLIFORM_AND_E_COLI_CONCENTRATION_MEASUREMENT"
break
case "041C": // FLUORIDE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "FLUORIDE_CONCENTRATION_MEASUREMENT"
break
case "041D": // HALOACETIC_ACIDS_CONCENTRATION_MEASUREMENT
ClusterIdentified = "HALOACETIC_ACIDS_CONCENTRATION_MEASUREMENT"
break
case "041E": // TOTAL_TRIHALOMETHANES_CONCENTRATION_MEASUREMENT
ClusterIdentified = "TOTAL_TRIHALOMETHANES_CONCENTRATION_MEASUREMENT"
break
case "041F": // TOTAL_COLIFORM_BACTERIA_CONCENTRATION_MEASUREMENT
ClusterIdentified = "TOTAL_COLIFORM_BACTERIA_CONCENTRATION_MEASUREMENT"
break
case "0420": // TURBIDITY_CONCENTRATION_MEASUREMENT
ClusterIdentified = "TURBIDITY_CONCENTRATION_MEASUREMENT"
break
case "0421": // COPPER_CONCENTRATION_MEASUREMENT
ClusterIdentified = "COPPER_CONCENTRATION_MEASUREMENT"
break
case "0422": // LEAD_CONCENTRATION_MEASUREMENT
ClusterIdentified = "LEAD_CONCENTRATION_MEASUREMENT"
break
case "0423": // MANGANESE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "MANGANESE_CONCENTRATION_MEASUREMENT"
break
case "0424": // SULFATE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "SULFATE_CONCENTRATION_MEASUREMENT"
break
case "0425": // BROMODICHLOROMETHANE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "BROMODICHLOROMETHANE_CONCENTRATION_MEASUREMENT"
break
case "0426": // BROMOFORM_CONCENTRATION_MEASUREMENT
ClusterIdentified = "BROMOFORM_CONCENTRATION_MEASUREMENT"
break
case "0427": // CHLORODIBROMOMETHANE_CONCENTRATION_MEASUREMENT
ClusterIdentified = "CHLORODIBROMOMETHANE_CONCENTRATION_MEASUREMENT"
break
case "0428": // CHLOROFORM_CONCENTRATION_MEASUREMENT
ClusterIdentified = "CHLOROFORM_CONCENTRATION_MEASUREMENT"
break
case "0429": // SODIUM_CONCENTRATION_MEASUREMENT
ClusterIdentified = "SODIUM_CONCENTRATION_MEASUREMENT"
break
// Security & Safety Clusters
case "0500": // IAS Zone
ClusterIdentified = "IAS Zone"
IASZoneReport( descMap )
Handled = true
break
case "0501": // IAS ACE "Ancillary Control"
ClusterIdentified = "IAS ACE Ancillary Control"
break
case "0502": // IAS WD "Warning Devices"
ClusterIdentified = "IAS WD Warning Devices"
break
// Protocol Interface Clusters
case "0600": // Generic Tunnel
ClusterIdentified = "Interface Clusters"
break
case "0601": // BACnet Protocol Tunnel
ClusterIdentified = "BACnet Protocol Tunnel"
break
case "0602": // Analog Input (BACnet Regular)
ClusterIdentified = "Analog Input (BACnet Regular)"
break
case "0603": // Analog Input (BACnet Extended)
ClusterIdentified = "Analog Input (BACnet Extended)"
break
case "0604": // Analog Output (BACnet Regular)
ClusterIdentified = "Analog Output (BACnet Regular)"
break
case "0605": // Analog Output (BACnet Extended)
ClusterIdentified = "Analog Output (BACnet Extended)"
break
case "0606": // Analog Value (BACnet Regular)
ClusterIdentified = "Analog Value (BACnet Regular)"
break
case "0607": // Analog Value (BACnet Extended)
ClusterIdentified = "Analog Value (BACnet Extended)"
break
case "0608": // Binary Input (BACnet Regular)
ClusterIdentified = "Binary Input (BACnet Regular)"
break
case "0609": // Binary Input (BACnet Extended)
ClusterIdentified = "Binary Input (BACnet Extended)"
break
case "060A": // Binary Output (BACnet Regular)
ClusterIdentified = "Binary Output (BACnet Regular)"
break
case "060B": // Binary Output (BACnet Extended)
ClusterIdentified = "Binary Output (BACnet Extended)"
break
case "060C": // Binary Value (BACnet Regular)
ClusterIdentified = "Binary Value (BACnet Regular)"
break
case "060D": // Binary Value (BACnet Extended)
ClusterIdentified = "Binary Value (BACnet Extended)"
break
case "060E": // Multistate Input (BACnet Regular)
ClusterIdentified = "Multistate Input (BACnet Regular)"
break
case "060F": // Multistate Input (BACnet Extended)
ClusterIdentified = "Multistate Input (BACnet Extended)"
break
case "0610": // Multistate Output (BACnet Regular)
ClusterIdentified = "Multistate Output (BACnet Regular)"
break
case "0611": // Multistate Output (BACnet Extended)
ClusterIdentified = "Multistate Output (BACnet Extended)"
break
case "0612": // Multistate Value (BACnet Regular)
ClusterIdentified = "Multistate Value (BACnet Regular)"
break
case "0613": // Multistate Value (BACnet Extended)
ClusterIdentified = "Multistate Value (BACnet Extended)"
break
case "0614": // Protocol Tunnel Cluster
ClusterIdentified = "Protocol Tunnel Cluster"
break
case "0615": // Protocol Tunnel
ClusterIdentified = "Protocol Tunnel"
break
case "0700": // Price
ClusterIdentified = "Price"
break
case "0701": // Demand Response and Load Control
ClusterIdentified = "Demand Response and Load Control"
break
case "0702": // Metering
ClusterIdentified = "Metering"
break
case "0703": // Messaging
ClusterIdentified = "Messaging"
break
case "0704": // Tunneling Cluster
ClusterIdentified = "Tunneling Cluster"
break
case "0705": // PREPAYMENT
ClusterIdentified = "PREPAYMENT"
break
case "0706": // ENERGY_MANAGEMENT
ClusterIdentified = "ENERGY_MANAGEMENT"
break
case "0707": // CALENDAR
ClusterIdentified = "CALENDAR"
break
case "0708": // DEVICE_MANAGEMENT
ClusterIdentified = "DEVICE_MANAGEMENT"
break
case "0709": // EVENTS
ClusterIdentified = "EVENTS"
break
case "070A": // MDU_PAIRING
ClusterIdentified = "MDU_PAIRING"
break
case "070B": // SUB_GHZ
ClusterIdentified = "SUB_GHZ"
break
case "0800": // Key Establishment
ClusterIdentified = "Key Establishment"
break
case "0900": // INFORMATION
ClusterIdentified = "INFORMATION"
break
case "0901": // DATA_SHARING
ClusterIdentified = "DATA_SHARING"
break
case "0902": // GAMING
ClusterIdentified = "GAMING"
break
case "0903": // DATA_RATE_CONTROL
ClusterIdentified = "DATA_RATE_CONTROL"
break
case "0904": // VOICE_OVER_ZIGBEE
ClusterIdentified = "VOICE_OVER_ZIGBEE"
break
case "0905": // CHATTING
ClusterIdentified = "CHATTING"
break
case "0A00": // PAYMENT
ClusterIdentified = "PAYMENT"
break
case "0A01": // BILLING
ClusterIdentified = "BILLING"
break
case "0B00": // APPLIANCE_IDENTIFICATION
ClusterIdentified = "APPLIANCE_IDENTIFICATION"
break
case "0B01": // Meter Identification
ClusterIdentified = "Meter Identification"
break
case "0B02": // Appliance Events and Alerts
ClusterIdentified = "Appliance Events and Alerts"
break
case "0B03": // Appliance Statistics
ClusterIdentified = "Appliance Statistics"
break
case "0B04": // Electrical Measurement
ClusterIdentified = "Electrical Measurement"
break
case "0B05": // Diagnostic
ClusterIdentified = "Diagnostic"
break
case "1000": // ZLL_COMMISSIONING
ClusterIdentified = "ZLL_COMMISSIONING"
break
case "8000": // NWK_ADDR_RSP
ClusterIdentified = "NWK_ADDR_RSP"
break
case "8001": // IEEE_ADDR_RSP
ClusterIdentified = "IEEE_ADDR_RSP"
break
case "8002": // NODE_DESC_RSP
ClusterIdentified = "NODE_DESC_RSP"
break
case "8003": // POWER_DESC_RSP
ClusterIdentified = "POWER_DESC_RSP"
break
case "8004": // SIMPLE_DESC_RSP
ClusterIdentified = "SIMPLE_DESC_RSP"
break
case "8005": // ACTIVE_EP_RSP
ClusterIdentified = "ACTIVE_EP_RSP"
break
case "8006": // MATCH_DESC_RSP
ClusterIdentified = "MATCH_DESC_RSP"
break
case "8010": // Complex_Desc_rsp
ClusterIdentified = "Complex_Desc_rsp"
break
case "8011": // User_Desc_rsp
ClusterIdentified = "User_Desc_rsp"
break
case "8012": // Discovery_Cache_rsp
ClusterIdentified = "Discovery_Cache_rsp"
break
case "8014": // User_Desc_Conf
ClusterIdentified = "User_Desc_Conf"
break
case "8015": // System_Server_Discover_rsp
ClusterIdentified = "System_Server_Discover_rsp"
break
case "8016": // Discovery_Store_rsp
ClusterIdentified = "Discovery_Store_rsp"
break
case "8017": // Node_Desc_Store_rso
ClusterIdentified = "Node_Desc_Store_rso"
break
case "8018": // Power_Desc_Store_rsp
ClusterIdentified = "Power_Desc_Store_rsp"
break
case "8019": // Active_EP_Store_rsp
ClusterIdentified = "Active_EP_Store_rsp"
break
case "801A": // Simple_Desc_Store_rsp
ClusterIdentified = "Simple_Desc_Store_rsp"
break
case "801B": // Remove_Node_Cache_rsp
ClusterIdentified = "Remove_Node_Cache_rsp"
break
case "801C": // Find_Node_Cache_rsp
ClusterIdentified = "Find_Node_Cache_rsp"
break
// End Device Bind, Unbind and Bind Management
case "8020": // End_Dev_Bind_rsp
ClusterIdentified = "End_Dev_Bind_rsp"
break
case "8021": // Bind_rsp
ClusterIdentified = "Bind_rsp"
break
case "8022": // Unbind_rsp
ClusterIdentified = "Unbind_rsp"
break
case "8023": // Bind_Register_rsp
ClusterIdentified = "Bind_Register_rsp"
break
case "8024": // Replace_Device_rsp
ClusterIdentified = "Replace_Device_rsp"
break
case "8025": // Store_Bkup_Bind_Entry_rsp
ClusterIdentified = "Store_Bkup_Bind_Entry_rsp"
break
case "8026": // Rm_Bkup_Bind_Entry_rsp
ClusterIdentified = "Rm_Bkup_Bind_Entry_rsp"
break
case "8027": // Backup_Bind_Table_rsp
ClusterIdentified = "Backup_Bind_Table_rsp"
break
case "8028": // Recover_Bind_Table_rsp
ClusterIdentified = "Recover_Bind_Table_rsp"
break
case "8029": // Backup_Source_Bind_rsp
ClusterIdentified = "Backup_Source_Bind_rsp"
break
case "802A": // Recover_Source_Bind_rsp
ClusterIdentified = "Recover_Source_Bind_rsp"
break
// Network Management
case "8030": // Mgmt_NWK_Disc_rsp
ClusterIdentified = "Mgmt_NWK_Disc_rsp"
break
case "8031": // Mgmt_LQI_rsp
ClusterIdentified = "Mgmt_LQI_rsp"
break
case "8032": // Mgmt_Rtg_rsp
ClusterIdentified = "Mgmt_Rtg_rsp"
break
case "8033": // Mgmt_Bind_rsp
ClusterIdentified = "Mgmt_Bind_rsp"
break
case "8034": // Mgmt_Leave_rsp
ClusterIdentified = "Mgmt_Leave_rsp"
break
case "8035": // Mgmt_Direct_Join_rsp
ClusterIdentified = "Mgmt_Direct_Join_rsp"
break
case "8036": // Mgmt_Permit_Join_rsp
ClusterIdentified = "Mgmt_Permit_Join_rsp"
break
case "8037": // Mgmt_Cache_rsp
ClusterIdentified = "Mgmt_Cache_rsp"
break
case "8038": // Management Network Update Notify
ClusterIdentified = "Management Network Update Notify"
break
case "FC00": // SAMPLE_MFG_SPECIFIC
ClusterIdentified = "SAMPLE_MFG_SPECIFIC"
break
case "FC01": // OTA_CONFIGURATION
ClusterIdentified = "OTA_CONFIGURATION"
break
case "FC02": // MFGLIB
ClusterIdentified = "MFGLIB"
break
case "FC57": // SL_WWAH
ClusterIdentified = "SL_WWAH"
break
case "FD00": // Halo Smoke Detector Only ???
ClusterIdentified = "Halo Smoke Detector Only ???"
break
case "FD01": // Halo Smoke Detector Only ???
ClusterIdentified = "Halo Smoke Detector Only ???"
break
case "FD02": // Halo Smoke Detector Only ???
ClusterIdentified = "Halo Smoke Detector Only ???"
break
default:
ClusterIdentified = "Unknown"
break
}
if( ClusterIdentified == "Unknown" ){
Logging( "Unknown cluster. descMap = ${ descMap }", 3 )
} else {
if( !Handled ){
Logging( "Unhandled cluster. ${ cluster } is ${ ClusterIdentified }, descMap = ${ descMap }", 3 )
}
}
}
// Handles Identify reports
def IdentifyReport( descMap ){
switch( descMap.command ){
case "04":
Logging( "Identify command in progress", 4 )
break;
case "0B":
Logging( "Identify command received", 4 )
break;
default:
Logging( "Identify Report Unknown ${ descMap }", 3 )
break;
}
}
// Attempts to trigger any Identification function the device has
def Identify(){
Logging( "Sending identify command for 30 seconds", 4 )
zigbee.writeAttribute( 0x0003, 0x0000, 0x21, 0x001E )
}
// Processes temperature related reports
private TemperatureReport( descMap ){
if( descMap.value == null) return
def valueRaw = hexStrToSignedInt( descMap.value )
valueRaw = valueRaw / 100
def value = convertTemperatureIfNeeded( valueRaw.toFloat(),"c", 1 )
def unit = "°${ location.temperatureScale }"
Logging( "Temperature is ${ value }${ unit }", 4 )
sendEvent( name: "temperature", value: value, unit: unit, isStateChange: true )
}
// Processes battery related reports
private BatteryReport( descMap ){
if( descMap.command == "07" ){
def BatteryPercent
BatteryPercent = descMap.data[ 0 ] as int
Logging( "Battery at ${ BatteryPercent }%", 4 )
sendEvent( name: "battery", value: BatteryPercent, unit: "%", isStateChange: true )
}
}
// Used to clear a tamper alert on the device
def ResetTamper() {
Logging( "Reset tamper notification", 4 )
sendEvent( name: "tamper", value: "clear", isStateChange: true )
}
// Processes reporting for the Basic class
def BasicReport( descMap ){
switch( descMap.attrId ){
case "0000":
Logging( "ZCLVersion = ${ descMap.value }", 3 )
break
case "0001":
Logging( "ApplicationVersion = ${ descMap.value }", 3 )
break
case "0002":
Logging( "StackVersion = ${ descMap.value }", 3 )
break
case "0003":
Logging( "HardwareVersion = ${ descMap.value }", 3 )
break
case "0004":
Logging( "ManufacturerName = ${ descMap.value }", 3 )
break
case "0005":
Logging( "ModelIdentifier = ${ descMap.value }", 3 )
break
case "0006":
Logging( "DateCode = ${ descMap.value }", 3 )
break
case "0007":
switch( descMap.value ){
case "00":
Logging( "PowerSource = Unknown", 3 )
break
case "01":
Logging( "PowerSource = Mains (single phase)", 3 )
break
case "02":
Logging( "PowerSource = Mains (3 phase)", 3 )
break
case "03":
Logging( "PowerSource = Battery", 3 )
break
case "04":
Logging( "PowerSource = DC source", 3 )
break
case "05":
Logging( "PowerSource = Emergency mains constantly powered", 3 )
break
case "06":
Logging( "PowerSource = Emergency mains and transfer switch", 3 )
break
default:
Logging( "PowerSource is unlisted", 3 )
break
}
break
case "0010":
Logging( "LocationDescription = ${ descMap.value }", 3 )
break
case "0011":
Logging( "PhysicalEnvironment = ${ descMap.value }", 3 )
break
case "0012":
Logging( "DeviceEnabled = ${ descMap.value }", 3 )
break
case "0013":
Logging( "AlarmMask = ${ descMap.value }", 3 )
break
case "0014":
Logging( "DisableLocalConfig = ${ descMap.value }", 3 )
break
case "4000":
Logging( "SWBuildID = ${ descMap.value }", 3 )
break
default:
Logging( "Basic descMap = ${ descMap }", 3 )
break
}
}
// Processes any IASZone reporting received
def IASZoneReport( descMap ){
if( descMap.attrId == "0001" ){ // Zone Types
def ZoneType = "Unknown" as String
switch( descMap.value ){
case "0000": // Standard CIE
ZoneType = "Standard CIE"
break
case "000D": // Motion sensor
ZoneType = "Motion sensor"
break
case "0015": // Contact switch
ZoneType = "Contact switch"
break
case "0028": // Fire sensor
ZoneType = "Fire sensor"
break
case "002A": // Water sensor
ZoneType = "Water sensor"
break
case "002B": // Gas sensor
ZoneType = "Gas sensor"
break
case "002C": // Personal emergency device
ZoneType = "Personal emergency device"
break
case "002D": // Vibration/Movement sensor
ZoneType = "Vibration/Movement sensor"
break
case "010F": // Remote Control
ZoneType = "Remote Control"
break
case "0115": // Key fob
ZoneType = "Key fob"
break
case "021D": // Keypad
ZoneType = "Keypad"
break
case "0225": // Standard Warning Device
ZoneType = "Standard Warning Device "
break
}
Logging( "ZoneType = ${ ZoneType }", 3 )
} else if( descMap.attrId == "0002" ){
switch( descMap.value ){
case "0020": // Motion Inactive
if( state.SensorType == "Motion" ){
Logging( "Motion inactive", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
} else if( state.SensorType == "Contact" ){
Logging( "Contact closed", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
}
break
case "0021": // Motion Active
if( state.SensorType == "Motion" ){
Logging( "Motion active", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
} else if( state.SensorType == "Contact" ){
Logging( "Contact open", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
}
break
case "0022": //Tampered
Logging( "Tampered", 4 )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
break
case "0023": //Low Battery
Logging( "Low battery notice", 4 )
state.Message = "Low battery"
break
case "0024": // Tampered and closed
if( state.SensorType == "Contact" ){
Logging( "Contact closed and tampered", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion inactive and tampered", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
}
break
case "0025": // Tampered and opened
if( state.SensorType == "Contact" ){
Logging( "Contact open and tampered", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion active and tampered", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
}
break
case "0026": // Trouble
Logging( "Trouble notice", 3 )
break
case "0027": // AC Mains fault
Logging( "AC Mains fault notice", 3 )
break
case "0028": // Battery defect with contact closed or motion inactive
if( state.SensorType == "Contact" ){
Logging( "Battery defect and contact closed", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Battery defect and motion inactive", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
} else {
Logging( "0028 notice", 3 )
}
break
case "0029": // Battery defect with contact open or motion active
if( state.SensorType == "Contact" ){
Logging( "Battery defect and contact open", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Battery defect and motion active", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
} else {
Logging( "0029 notice", 3 )
}
break
case "002D": // Unknown??? and Tamper on a SZ-DWS04N_SF
if( device.data.model.startsWith( "SZ-DWS04N_SF" ) ){
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else {
Logging( "Unknown IASZone notice", 3 )
}
break
case "0030": // Contact Closed
if( state.SensorType == "Contact" ){
Logging( "Contact closed", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
}
break
case "0031": // Contact Open
if( state.SensorType == "Contact" ){
Logging( "Contact open", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
}
break
case "0034": // Contact Closed and Tampered
if( state.SensorType == "Contact" ){
Logging( "Contact closed and tampered", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion inactive and tampered", 4 )
sendEvent( name: "motion", value: "inactive", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
}
break
case "0035": // Contact Open and Tampered
if( state.SensorType == "Contact" ){
Logging( "Contact open and tampered", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
} else if( state.SensorType == "Motion" ){
Logging( "Motion active and tampered", 4 )
sendEvent( name: "motion", value: "active", isStateChange: true )
sendEvent( name: "tamper", value: "tampered", isStateChange: true )
}
break
case "0228": // Contact Open
if( state.SensorType == "Contact" ){
Logging( "Contact open", 4 )
sendEvent( name: "contact", value: "open", isStateChange: true )
}
break
case "0229": // Contact Closed
if( state.SensorType == "Contact" ){
Logging( "Contact closed", 4 )
sendEvent( name: "contact", value: "closed", isStateChange: true )
}
break
default:
Logging( "IASZone descMap = ${ descMap }", 3 )
break
}
}
}
// Handles ZDO Announce reports
def AnnounceReport( descMap ){
switch( descMap.command ){
case "00":
Logging( "Device alive/awake", 4 )
break
default:
Logging( "Announce Report ${ descMap }", 3 )
break
}
}
// ReadDevice is meant to check all the device basic information
def ReadDevice(){
// Not clear if any of these sensors respond to these commands.
// DWS04N_FL does not respond to any
def cmds = zigbee.readAttribute( 0x0000, 0x0000, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0001, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0002, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0003, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0004, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0005, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0006, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0007, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0010, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0011, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0012, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0013, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x0014, [:], 200 )
cmds = cmds + zigbee.readAttribute( 0x0000, 0x4000, [:], 200 )
// Read the type of sensor (contact or motion, etc...)
cmds = cmds + zigbee.readAttribute( 0x0500, 0x0001, [:], 200 )
Logging( "Sending request for Basic cluster data", 4 )
return cmds
}
// Sets the list of read commands
def ZigBeeReadCommands(){
//def cmds = zigbee.readAttribute( 0x0001, 0x0021, [:], 200 )
//cmds = cmds + zigbee.readAttribute( 0x0001, 0x0020, [:], 200 )
def cmds = zigbee.readAttribute( zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020 )
cmds += zigbee.readAttribute( zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021 )
//cmds += zigbee.readAttribute( zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS )
cmds += zigbee.readAttribute( 0x0500, 0x0002, [:], 200 )
if( device.data.model.startsWith( "SZ-DWS04" ) || device.data.model.startsWith( "SZ-DWS08" ) || device.data.model.startsWith( "SZ-PIR04" ) ){
cmds += zigbee.readAttribute( 0x0402, 0x0000, [:], 200 )
cmds += zigbee.readAttribute( zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000 )
//cmds = cmds + zigbee.readAttribute( 0x0500, 0x0002, [:], 200 )
}
diagAttributes.each{ it ->
cmds += zigbee.readAttribute( 0x0B05, it.value.val, [:], 200 )
}
cmds += zigbee.enrollResponse()
Logging( "Sending request to read device attributes", 4 )
return cmds
}
// Gets the list of commands
def ZigBeeReportingCommands(){
//def cmds = zigbee.configureReporting( 0x0001, 0x0021, DataType.UINT8, 1, 86400, 1 )
def cmds = zigbee.configureReporting( 0x0001, 0x0020, DataType.UINT8, 1, 86400, 1 )
cmds += zigbee.configureReporting( zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 1, 3600, 0x10 )
cmds += zigbee.configureReporting( 0x0500, 0x0002, DataType.BITMAP16, 1, 86400, 1 )
if( device.data.model.startsWith( "SZ-DWS04" ) || device.data.model.startsWith( "SZ-DWS08" ) || device.data.model.startsWith( "SZ-PIR04" ) ){
switch( ReportInterval ){
case "1 minute":
cmds += zigbee.configureReporting( 0x0402, 0x0000, DataType.INT16, 1, 60, 1 )
cmds += zigbee.temperatureConfig( 30, 60 )
break
case "5 minutes":
cmds += zigbee.configureReporting( 0x0402, 0x0000, DataType.INT16, 1, 300, 1 )
cmds += zigbee.temperatureConfig( 30, 300 )
break
case "10 minutes":
cmds += zigbee.configureReporting( 0x0402, 0x0000, DataType.INT16, 1, 600, 1 )
cmds += zigbee.temperatureConfig( 30, 600 )
break
case "1 hour":
cmds += zigbee.configureReporting( 0x0402, 0x0000, DataType.INT16, 1, 3600, 1 )
cmds += zigbee.temperatureConfig( 30, 3600 )
break
case "1 day":
cmds += zigbee.configureReporting( 0x0402, 0x0000, DataType.INT16, 1, 86400, 1 )
cmds += zigbee.temperatureConfig( 30, 86400 )
break
case "Disabled":
Logging( "Temperature reporting disabled", 4 )
break
}
}
cmds += ZigBeeReadCommands()
Logging( "Sending request to set ZigBee reporting", 4 )
return cmds
}
// refresh command
def refresh(){
Logging( "Refreshing device...", 2 )
return ZigBeeReadCommands()
}
// Configures the device, typically at install or when preferences are saved
def configure(){
Logging( "Configuring device...", 2 )
return ZigBeeReportingCommands()
}
// installed is called when the device is installed, all it really does is run updated
def installed(){
Logging( "Installed", 2 )
updated()
}
// initialize is called when the device is initialized, all it really does is run updated
def initialize(){
Logging( "Initialized", 2 )
updated()
}
// Handles whether logging is enabled and thus what to put there.
def Logging( LogMessage, LogLevel ){
// Add all messages as info logging
if( ( LogLevel == 2 ) && ( LogType != "None" ) ){
log.info( "${ device.displayName } - ${ LogMessage }" )
} else if( ( LogLevel == 3 ) && ( ( LogType == "Debug" ) || ( LogType == "Trace" ) ) ){
log.debug( "${ device.displayName } - ${ LogMessage }" )
} else if( ( LogLevel == 4 ) && ( LogType == "Trace" ) ){
log.trace( "${ device.displayName } - ${ LogMessage }" )
} else if( LogLevel == 5 ){
log.error( "${ device.displayName } - ${ LogMessage }" )
}
}
// Checks drdsnell.com for the latest version of the driver
// Original inspiration from @cobra's version checking
def CheckForUpdate(){
ProcessEvent( "DriverName", DriverName() )
ProcessEvent( "DriverVersion", DriverVersion() )
httpGet( uri: "https://www.drdsnell.com/projects/hubitat/drivers/versions.json", contentType: "application/json" ){ resp ->
switch( resp.status ){
case 200:
if( resp.data."${ DriverName() }" ){
CurrentVersion = DriverVersion().split( /\./ )
if( resp.data."${ DriverName() }".version == "REPLACED" ){
ProcessEvent( "DriverStatus", "Driver replaced, please use ${ resp.data."${ state.DriverName }".file }" )
} else if( resp.data."${ DriverName() }".version == "REMOVED" ){
ProcessEvent( "DriverStatus", "Driver removed and no longer supported." )
} else {
SiteVersion = resp.data."${ DriverName() }".version.split( /\./ )
if( CurrentVersion == SiteVersion ){
Logging( "Driver version up to date", 3 )
ProcessEvent( "DriverStatus", "Up to date" )
} else if( ( CurrentVersion[ 0 ] as int ) > ( SiteVersion [ 0 ] as int ) ){
Logging( "Major development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 )
ProcessEvent( "DriverStatus", "Major development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version" )
} else if( ( CurrentVersion[ 1 ] as int ) > ( SiteVersion [ 1 ] as int ) ){
Logging( "Minor development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 )
ProcessEvent( "DriverStatus", "Minor development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version" )
} else if( ( CurrentVersion[ 2 ] as int ) > ( SiteVersion [ 2 ] as int ) ){
Logging( "Patch development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 )
ProcessEvent( "DriverStatus", "Patch development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version" )
} else if( ( SiteVersion[ 0 ] as int ) > ( CurrentVersion[ 0 ] as int ) ){
Logging( "New major release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 )
ProcessEvent( "DriverStatus", "New major release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available" )
} else if( ( SiteVersion[ 1 ] as int ) > ( CurrentVersion[ 1 ] as int ) ){
Logging( "New minor release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 )
ProcessEvent( "DriverStatus", "New minor release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available" )
} else if( ( SiteVersion[ 2 ] as int ) > ( CurrentVersion[ 2 ] as int ) ){
Logging( "New patch ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 )
ProcessEvent( "DriverStatus", "New patch ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available" )
}
}
} else {
Logging( "${ DriverName() } is not published on drdsnell.com", 2 )
ProcessEvent( "DriverStatus", "${ DriverName() } is not published on drdsnell.com" )
}
break
default:
Logging( "Unable to check drdsnell.com for ${ DriverName() } driver updates.", 2 )
break
}
}
}