/** * * Inovelli 2-Channel Smart Plug * * github: Eric Maycock (erocm123) * Date: 2018-06-13 * Copyright 2018 Eric Maycock * * Includes all configuration parameters and ease of advanced configuration. * * 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. * * 2018-06-13: Modified tile layout. Update firmware version reporting. * */ metadata { definition(name: "Inovelli 2-Channel Smart Plug (Alternate)", namespace: "erocm123", author: "Eric Maycock", vid: "generic-switch") { capability "Actuator" capability "Sensor" capability "Switch" capability "Polling" capability "Refresh" //capability "Health Check" attribute "lastActivity", "String" attribute "lastEvent", "String" attribute "firmware", "String" fingerprint mfr: "015D", prod: "0221", model: "251C" fingerprint mfr: "0312", prod: "B221", model: "251C" fingerprint deviceId: "0x1001", inClusters: "0x5E,0x85,0x59,0x5A,0x72,0x60,0x8E,0x73,0x27,0x25,0x86" } simulator {} preferences {} tiles { multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) { tileAttribute("device.switch", key: "PRIMARY_CONTROL") { attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff" attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff" } } childDeviceTiles("all") valueTile("lastActivity", "device.lastActivity", inactiveLabel: false, decoration: "flat", width: 4, height: 1) { state "default", label: 'Last Activity: ${currentValue}',icon: "st.Health & Wellness.health9" } valueTile("firmware", "device.firmware", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { state "default", label: 'fw: ${currentValue}', icon: "" } valueTile("icon", "device.icon", inactiveLabel: false, decoration: "flat", width: 5, height: 1) { state "default", label: '', icon: "https://inovelli.com/wp-content/uploads/Device-Handler/Inovelli-Device-Handler-Logo.png" } standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { state "default", label: "", action: "refresh.refresh", icon: "st.secondary.refresh" } } } def parse(String description) { def result = [] def cmd = zwave.parse(description) if (cmd) { result += zwaveEvent(cmd) logging("Parsed ${cmd} to ${result.inspect()}", 1) } else { logging("Non-parsed event: ${description}", 2) } def now if(location.timeZone) now = new Date().format("yyyy MMM dd EEE h:mm:ss a", location.timeZone) else now = new Date().format("yyyy MMM dd EEE h:mm:ss a") sendEvent(name: "lastActivity", value: now, displayed:false) return result } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { logging("BasicReport ${cmd} - ep ${ep}", 2) if (ep) { def event childDevices.each { childDevice -> if (childDevice.deviceNetworkId == "$device.deviceNetworkId-ep$ep") { childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off") } } if (cmd.value) { event = [createEvent([name: "switch", value: "on"])] } else { def allOff = true childDevices.each { n -> if (n.currentState("switch").value != "off") allOff = false } if (allOff) { event = [createEvent([name: "switch", value: "off"])] } else { event = [createEvent([name: "switch", value: "on"])] } } return event } } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { logging("BasicSet ${cmd}", 2) def result = createEvent(name: "switch", value: cmd.value ? "on" : "off", type: "digital") def cmds = [] cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1) cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2) return [result, response(commands(cmds))] // returns the result of reponse() } def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) { logging("SwitchBinaryReport ${cmd} - ep ${ep}", 2) if (ep) { def event def childDevice = childDevices.find { it.deviceNetworkId == "$device.deviceNetworkId-ep$ep" } if (childDevice) childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off") if (cmd.value) { event = [createEvent([name: "switch", value: "on"])] } else { def allOff = true childDevices.each { n-> if (n.deviceNetworkId != "$device.deviceNetworkId-ep$ep" && n.currentState("switch").value != "off") allOff = false } if (allOff) { event = [createEvent([name: "switch", value: "off"])] } else { event = [createEvent([name: "switch", value: "on"])] } } return event } else { def result = createEvent(name: "switch", value: cmd.value ? "on" : "off", type: "digital") def cmds = [] cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1) cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2) return [result, response(commands(cmds))] // returns the result of reponse() } } def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { logging("MultiChannelCmdEncap ${cmd}", 2) def encapsulatedCommand = cmd.encapsulatedCommand([0x32: 3, 0x25: 1, 0x20: 1]) if (encapsulatedCommand) { zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) } } def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { logging("ManufacturerSpecificReport ${cmd}", 2) def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) logging("msr: $msr", 2) updateDataValue("MSR", msr) } def zwaveEvent(physicalgraph.zwave.Command cmd) { // This will capture any commands not handled by other instances of zwaveEvent // and is recommended for development so you can see every command the device sends logging("Unhandled Event: ${cmd}", 2) } def on() { logging("on()", 1) commands([ zwave.switchAllV1.switchAllOn(), encap(zwave.switchBinaryV1.switchBinaryGet(), 1), encap(zwave.switchBinaryV1.switchBinaryGet(), 2) ]) } def off() { logging("off()", 1) commands([ zwave.switchAllV1.switchAllOff(), encap(zwave.switchBinaryV1.switchBinaryGet(), 1), encap(zwave.switchBinaryV1.switchBinaryGet(), 2) ]) } void childOn(String dni) { logging("childOn($dni)", 1) def cmds = [] cmds << new physicalgraph.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0xFF), channelNumber(dni)))) cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni)))) sendHubCommand(cmds, 1000) } void childOff(String dni) { logging("childOff($dni)", 1) def cmds = [] cmds << new physicalgraph.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0x00), channelNumber(dni)))) cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni)))) sendHubCommand(cmds, 1000) } void childRefresh(String dni) { logging("childRefresh($dni)", 1) def cmds = [] cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni)))) sendHubCommand(cmds, 1000) } def poll() { logging("poll()", 1) commands([ encap(zwave.switchBinaryV1.switchBinaryGet(), 1), encap(zwave.switchBinaryV1.switchBinaryGet(), 2), ]) } def refresh() { logging("refresh()", 1) commands([ encap(zwave.switchBinaryV1.switchBinaryGet(), 1), encap(zwave.switchBinaryV1.switchBinaryGet(), 2), ]) } def ping() { logging("ping()", 1) refresh() } def installed() { logging("installed()", 1) command(zwave.manufacturerSpecificV1.manufacturerSpecificGet()) createChildDevices() } def updated() { logging("updated()", 1) if (!childDevices) { createChildDevices() } else if (device.label != state.oldLabel) { childDevices.each { if (it.label == "${state.oldLabel} (CH${channelNumber(it.deviceNetworkId)})") { def newLabel = "${device.displayName} (CH${channelNumber(it.deviceNetworkId)})" it.setLabel(newLabel) } } state.oldLabel = device.label } sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "needUpdate", value: device.currentValue("needUpdate"), displayed: false, isStateChange: true) return command(zwave.versionV1.versionGet()) } def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { logging("${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd2Integer(cmd.configurationValue)}'", 2) } private encap(cmd, endpoint) { if (endpoint) { zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) } else { cmd } } private command(physicalgraph.zwave.Command cmd) { if (state.sec) { zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() } else { cmd.format() } } private commands(commands, delay = 1000) { delayBetween(commands.collect { command(it) }, delay) } private channelNumber(String dni) { dni.split("-ep")[-1] as Integer } private void createChildDevices() { state.oldLabel = device.label try { for (i in 1..2) { addChildDevice("Switch Child Device", "${device.deviceNetworkId}-ep${i}", null, [completedSetup: true, label: "${device.displayName} (CH${i})", isComponent: false ]) } } catch (e) { runIn(2, "sendAlert") } } private sendAlert() { sendEvent(descriptionText: "Child device creation failed. Please make sure that the \"Switch Child Device\" is installed and published.", eventType: "ALERT", name: "childDeviceCreation", value: "failed", displayed: true, ) } private def logging(message, level) { if (logLevel != "0") { switch (logLevel) { case "1": if (level > 1) log.debug "$message" break case "99": log.debug "$message" break } } } def configuration_model() { ''' ''' }