/* * * Salus SP600 Smart Plug Driver v2 (20th Jan 2021) * */ metadata { definition (name: "Salus SP600 Smart Plug", namespace: "Salus", author: "Andrew Davison", importUrl: "https://raw.githubusercontent.com/birdslikewires/hubitat/master/drivers/salus_sp600.groovy") { capability "Actuator" capability "Configuration" capability "Initialize" capability "Outlet" capability "PowerMeter" capability "Refresh" capability "Switch" fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0004, 0005, 0006, 0402, 0702, FC01", outClusters: "0019", manufacturer: "Computime", model: "SP600", deviceJoinName: "Salus SP600 Smart Plug" } } preferences { input name: "RepW", type: "number", title: "power reporting level in W", required: true input name: "RepMin", type: "number", title: "power reporting min (seconds), if a change above reporting level wait this long befor sending report", required: true, defaultValue: 15 input name: "RepMax", type: "number", title: "power reporting max (seconds), if no reports sent in this window send report after this time", required: true , defaultValue: 1200 input name: "infoLogging", type: "bool", title: "Enable info logging", defaultValue: true input name: "debugLogging", type: "bool", title: "Enable debug logging", defaultValue: false input name: "traceLogging", type: "bool", title: "Enable trace logging", defaultValue: false } def installed() { device.updateSetting("infoLogging",[value:"true",type:"bool"]) device.updateSetting("debugLogging",[value:"false",type:"bool"]) device.updateSetting("traceLogging",[value:"false",type:"bool"]) device.updateSetting("RepMin",[value:15,type:"number"]) device.updateSetting("RepW",[value:50,type:"number"]) device.updateSetting("RepMax",[value:1201,type:"number"]) log.info "${device} Paired!" //configure is automaticly called after } def initialize() { // Runs on reboot, or can be triggered manually. int randomSixty = 1 + (Math.random() * 58) // Stagger our device init refreshes or we run the risk of DDoS attacking our hub on reboot! runIn(randomSixty, sendZigbeeCommands, [data:zigbee.onOffConfig()]) runIn(randomSixty*2,refresh) //schedule("${randomSixty} ${randomSixty} ${randomTwentyFour}/${checkEveryHours} * * ? *", refresh) log.info "${device} Initialised zigbee config in $randomSixty seconds" } def configure() { // Runs after installed() when a device is paired or rejoined when, or can be triggered manually. unschedule() if (debugLogging == true){ runIn(3600,debugLogOff)} if (traceLogging == true){ runIn(1800,traceLogOff)} /* state.remove("checkPhase") state.remove("bin") state.remove("presenceUpdated") */ log.info "${device} configured, debug logging=$debugLogging , trace logging=$traceLogging , info logging=$infoLogging , " powerMeteringConfig() initialize() } def updated() { // Runs whenever preferences are saved. log.info "${device} updated" configure() } void traceLogOff(){ device.updateSetting("traceLogging",[value:"false",type:"bool"]) log.trace "${device} : Trace Logging : Automatically Disabled" } void debugLogOff(){ device.updateSetting("debugLogging",[value:"false",type:"bool"]) log.trace "${device} : Debug Logging : Automatically Disabled" } void reportToDev(map) { String receivedData = map.data if (infoLogging == true || debugLogging == true || traceLogging == true) log.warn "${device} : UNKNOWN DATA!Received : cluster: ${map.cluster}, clusterId: ${map.clusterId}, attrId: ${map.attrId}, command: ${map.command} with value: ${map.value} and data: ${receivedData}" } def powerMeteringConfig() { if(RepMin == null || RepW == null || RepMax == null){ log.warn "${device} : null value detected max=$maxReportTime s, min=$minReportTime s, change=$reportableChange w" installed() } else{ int minReportTime = settings.RepMin int reportableChange = settings.RepW int maxReportTime = settings.RepMax log.trace "${device} : Power config max=$maxReportTime s, min=$minReportTime s, change=$reportableChange w" sendZigbeeCommands(zigbee.configureReporting(0x0702, 0x0400, DataType.INT24, minReportTime, maxReportTime, reportableChange)) } } def off() { sendZigbeeCommands(zigbee.off()) } def on() { sendZigbeeCommands(zigbee.on()) } def refresh() { if(infoLogging == true || traceLogging == true || debugLogging == true) log.trace "${device} :Sending Refresh command" sendZigbeeCommands(zigbee.readAttribute(0x0702, 0x0400)) sendZigbeeCommands(zigbee.onOffRefresh()) } def parse(String description) { // Primary parse routine. if (debugLogging == true) log.debug "${device} : Parse : $description" Map descriptionMap = zigbee.parseDescriptionAsMap(description) if (descriptionMap) processMap(descriptionMap) else reportToDev(description) } void processMap(map) { if (debugLogging == true) log.debug "${device} : processMap() : ${map}" // Relay configuration and response handling. if (map.cluster == '0006' || map.clusterId == '0006') { if (map.command == '01' || map.command == '0A') { // 01 - Prompted Refresh 0A - Automated Refresh //Relay refresh if (map.value == '01') { sendEvent(name: 'switch', value: 'on') if (infoLogging == true) log.info "${device} : Switch : On Relay Report ${map.command} ${map.value}" } else { sendEvent(name: 'switch', value: 'off') if (infoLogging == true) log.info "${device} : Switch : Off Relay Report ${map.command} ${map.value}" } } // Relay Configuration else if (map.command == '07') { if (infoLogging == true) log.info "${device} : Relay Configuration : Successful" } // Relay State Confirmations - digiatl control else if (map.command == '0B') { if (map.data[0] == '01') { sendEvent(name: 'switch', value: 'on', descriptionText:'Digital command') if (infoLogging == true) log.info "${device} : Switch : On Digital" } else { sendEvent(name: 'switch', value: 'off', descriptionText:'Digital command') if (infoLogging == true) log.info "${device} : Switch : Off Digital" } } else if (map.command == '00') { if (debugLogging == true) log.debug "${device} : skipping state counter message : ${map}" } else reportToDev(map) } // Power configuration and response handling. else if (map.cluster == '0702' || map.clusterId == '0702') { if (map.command == '07') { if (infoLogging == true) log.info "${device} : Power Reporting Configuration : Successful" // Power Configuration } else if (map.command == '01' || map.command == '0A') { // Power Usage //01 - Prompted Refresh ,0A - Automated Refresh int powerValue = zigbee.convertHexToInt(map.value) sendEvent(name: 'power', value: powerValue, unit: 'W') if (debugLogging == true) log.debug "${device} : power hex value : ${map.value}" if (infoLogging == true) log.info "${device} : power sensor reports : ${powerValue} W" } else reportToDev(map) } else if (map.cluster == '8021' || map.clusterId == '8021') if (traceLogging == true) log.trace "${device} : skipping discovery message : ${map}" else if (map.cluster == '8032' || map.clusterId == '8032') if (traceLogging == true) log.trace "${device} : skipping management routing response message : ${map}" else if (map.cluster == '8038' || map.clusterId == '8038') if (traceLogging == true) log.trace "${device} : skipping management network update notify message : ${map}" else reportToDev(map) } void sendZigbeeCommands(List cmds) { // All hub commands go through here for immediate transmission and to avoid some method() weirdness. if (traceLogging == true) log.trace "${device} : sendZigbeeCommands to transmit : ${cmds}" sendHubCommand(new hubitat.device.HubMultiAction(cmds, hubitat.device.Protocol.ZIGBEE)) }