/* * 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 } } }