Last active
June 12, 2021 18:39
-
-
Save jemenake/a25eb138dddf309c0eb13a3f9c6bd538 to your computer and use it in GitHub Desktop.
Example of implementing child devices on Smartthings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* Copyright 2021 Joseph Emenaker | |
* | |
* LOTS of credit to Eric Maycock for providing the starting code: | |
* https://community.smartthings.com/t/release-qubino-flush-1d-relay-flush-2-relays-flush-1-relay-flush-dimmer/79618 | |
* | |
* 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. | |
*/ | |
metadata { | |
definition (name: "TESTING123", namespace: "jemenake", author: "Joseph Emenaker", cstHandler: true | |
,ocfDeviceType: 'oic.d.switch', mnmn: 'SmartThingsCommunity', vid: 'generic-switch' | |
) { | |
capability "Actuator" | |
capability "Switch" | |
capability "Refresh" | |
// These define extra commands which are made available to the simulator and to other smartapps | |
command "on1" | |
command "on2" | |
command "deleteChildDevices" | |
command "createChildDevices" | |
command "getManufInfo" | |
fingerprint mfr: "0115", prod: "0110", model: "0001", deviceJoinName: "Device Join Name" | |
} | |
simulator { | |
// TODO: define status and reply messages here | |
} | |
tiles { | |
main(["switch"]) | |
details(["switch", | |
childDeviceTiles("all") | |
]) | |
// TODO: define your main and details tiles here | |
} | |
} | |
// parse events into attributes | |
def parse(String description) { | |
log.debug "parse description: $description" | |
def result = null | |
def cmd = zwave.parse(description) | |
if (cmd) { | |
result = zwaveEvent(cmd) | |
log.debug "Parsed ${cmd} to ${result.inspect()}" | |
} else { | |
log.debug "Non-parsed event: ${description}" | |
} | |
return result | |
} | |
// This gets called when you pull down on the details page in the mobile app | |
def refresh() { | |
log.debug("We got frefreshed... yay!") | |
def cmds = [] | |
cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), 1))) | |
cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), 2))) | |
cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), 3))) | |
cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), 4))) | |
sendHubCommand(cmds) | |
} | |
def getManufInfo() { | |
def cmds = [] | |
cmds << zwave.manufacturerSpecificV2.manufacturerSpecificGet() | |
sendHubCommand(cmds) | |
} | |
def installed() { | |
log.debug("parent installed"); | |
createChildDevices() | |
} | |
def updated() { | |
log.debug("Parent updated"); | |
} | |
void deleteChildDevices() { | |
log.debug("Before childDevices = $childDevices") | |
childDevices.each { deleteChildDevice(it.deviceNetworkId) } | |
log.debug("After childDevices = $childDevices") | |
} | |
void createChildDevices() { | |
for (i in 2..4) { | |
// When adding a child device from another namespace (i.e. developer), you need to add | |
// that namespace as the first parameter. If you just call: | |
// addChildDevice("Some Device Handler Name", ....) | |
// Smartthings will search for it in the namespace of the _parent_ | |
try { | |
addChildDevice("smartthings", "Child Switch", ChildDNIFromZwaveChannel(i), null, | |
[completedSetup: true, label: "${device.displayName} (R${i})", | |
isComponent: false]) | |
// TODO: Send a BasicReport or BasicGet to fetch the initial values | |
} catch (e) { | |
// runIn(2, "sendAlert") | |
log.debug("problem adding child device $i") | |
} | |
} | |
} | |
private String getChildDNIBase() { | |
return "${device.deviceNetworkId}-ep" | |
} | |
def zwaveChannelFromChildDNI(String dni) { | |
log.debug("Starting with DNI : $dni") | |
String remainder = dni.minus(getChildDNIBase()) | |
log.debug("Remainder is $remainder") | |
int channel = Integer.parseInt(remainder) | |
log.debug("Channel is $channel") | |
return channel | |
} | |
def ChildDNIFromZwaveChannel(int channel) { | |
return getChildDNIBase() + channel | |
} | |
private encap(cmd, endpoint) { | |
if (endpoint) { | |
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd) | |
} else { | |
cmd | |
} | |
} | |
private command(physicalgraph.zwave.Command cmd) { | |
if (state.sec || zwaveInfo?.zw?.contains("s") || ("0x98" in device.rawDescription?.split(" "))) { | |
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() | |
} else { | |
cmd.format() | |
} | |
} | |
def childOn(String id) { | |
log.debug("childOn($id)") | |
int channel = zwaveChannelFromChildDNI(id) | |
log.debug(" Looks like that's channel $channel") | |
def cmds = [] | |
cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF), channel))) | |
sendHubCommand(cmds) | |
log.debug("Commands are $cmds") | |
} | |
def childOff(String id) { | |
log.debug("childOff($id)") | |
int channel = zwaveChannelFromChildDNI(id) | |
log.debug(" Looks like that's channel $channel") | |
def cmds = [] | |
cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00), channel))) | |
sendHubCommand(cmds) | |
log.debug("Commands are $cmds") | |
} | |
private sendCommands(cmds) { | |
def actions = [] | |
cmds?.each { | |
actions << new physicalgraph.device.HubAction(it) | |
} | |
sendHubCommand(actions) | |
return [] | |
} | |
// handle commands | |
def on() { | |
log.debug "Executing 'on'" | |
createChildDevices() | |
if(childDevices) { | |
childDevices.each { theDevice -> log.debug("There's a child known as $theDevice ") } | |
} else { | |
log.debug("NO CHILD DEVICES") | |
} | |
on1() | |
// TODO: handle 'on' command | |
} | |
def off() { | |
log.debug "Executing 'off'" | |
off1() | |
// TODO: handle 'off' command | |
} | |
def on1() { | |
log.debug "on1()" | |
delayBetween([ | |
zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[255]).format(), | |
zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format() | |
], 1000) | |
} | |
def off1() { | |
log.debug "off1()" | |
delayBetween([ | |
zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[0]).format(), | |
zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format() | |
], 1000) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { | |
log.debug "Got a multichannel command: $cmd" | |
def encapsulatedCommand = cmd.encapsulatedCommand() | |
def source = cmd.sourceEndPoint | |
log.debug (" Command from endpoint ${source}: ${encapsulatedCommand}") | |
if (encapsulatedCommand) { | |
return zwaveEvent(encapsulatedCommand, source) | |
} | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, int channel) { | |
log.debug "Got a binary switch report (from channel $channel): $cmd" | |
log.debug "Value is ${cmd.value}" | |
if (channel == 1) { | |
return createEvent(name:"switch", value: cmd.value ? "on" : "off") | |
} else { | |
// All other channels are child devices, so we need to get the event to the child | |
def dni = ChildDNIFromZwaveChannel(channel) | |
log.debug("Hunting for DNI: $dni") | |
def child = childDevices.find { it.deviceNetworkId.equals(dni) } | |
if (child) { | |
child.sendEvent(name:"switch", value: cmd.value ? "on" : "off") | |
} | |
} | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport rpt) { | |
log.debug("ManufID : ${rpt.manufacturerId}") | |
log.debug("ProdTypeID : ${rpt.productTypeId}") | |
log.debug("ProdID : ${rpt.productId}") | |
log.debug("ManufName : ${rpt.manufacturerName}") | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { | |
log.debug "Got a binary switch report: $cmd" | |
log.debug "Value is ${cmd.value}" | |
createEvent(name:"switch", value: cmd.value ? "on" : "off") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment