Skip to content

Commit

Permalink
Merge pull request SmartThingsCommunity#2995 from SmartThingsCommunit…
Browse files Browse the repository at this point in the history
…y/master

Rolling up master to staging
  • Loading branch information
workingmonk authored Apr 10, 2018
2 parents eae6139 + 933cb38 commit 0aa89bf
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ metadata {
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3328-G", deviceJoinName: "Centralite Micro Motion Sensor"
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor"
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv5", deviceJoinName: "Motion Sensor"
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "Bosch", model: "RFDL-ZB-MS", deviceJoinName: "Bosch Motion Sensor"
}

simulator {
Expand Down Expand Up @@ -174,7 +175,13 @@ private Map getBatteryResult(rawValue) {
volts = maxVolts
def pct = batteryMap[volts]
result.value = pct
} else {
} else if (device.getDataValue("manufacturer") == "Bosch") {
def minValue = 21
def maxValue = 30
def pct = Math.round((rawValue - minValue) * 100 / (maxValue - minValue))
pct = pct > 0 ? pct : 1
result.value = Math.min(100, pct)
} else { // Centralite
def useOldBatt = shouldUseOldBatteryReporting()
def minVolts = useOldBatt ? 2.1 : 2.4
def maxVolts = useOldBatt ? 3.0 : 2.7
Expand All @@ -189,7 +196,7 @@ private Map getBatteryResult(rawValue) {
// OR we don't currently have a battery reading
// OR the value we just received is at least 2 steps off from the last reported value
// OR the device's firmware is older than 1.15.7
if(useOldBatt || state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
if (useOldBatt || state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ metadata {
fingerprint mfr: "001A", prod: "4449", model: "0101", deviceJoinName: "Eaton RF Master Dimmer"
fingerprint mfr: "001A", prod: "4449", model: "0003", deviceJoinName: "Eaton RF Dimming Plug-In Module"
fingerprint mfr: "0086", prod: "0103", model: "0063", deviceJoinName: "Aeotec Smart Dimmer 6"
fingerprint mfr: "014F", prod: "5744", model: "3530", deviceJoinName: "GoControl In-Wall Dimmer"
}

simulator {
Expand Down
182 changes: 182 additions & 0 deletions devicetypes/smartthings/zwave-dual-switch.src/zwave-dual-switch.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/**
* Copyright 2018 SmartThings
*
* 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: "Z-Wave Dual Switch", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Health Check"
capability "Light"
capability "Refresh"
capability "Sensor"
capability "Switch"

// This DTH uses 2 switch endpoints. Parent DTH controlls endpoint 1 so please use '1' at the end of deviceJoinName
// Child device (isComponent : false) representing endpoint 2 will substitude 1 with 2 for easier identification.
fingerprint mfr: "0258", prod: "0003", model: "008B", deviceJoinName: "NEO Coolcam Light Switch 1"
}

// tile definitions
tiles(scale: 2) {
multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
}
}

standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
}

main "switch"
details(["switch", "refresh"])
}
}

def installed() {
def componentLabel
if (device.displayName.endsWith('1')) {
componentLabel = "${device.displayName[0..-2]}2"
} else {
// no '1' at the end of deviceJoinName - use 2 to indicate second switch anyway
componentLabel = "$device.displayName 2"
}
try {
String dni = "${device.deviceNetworkId}-ep2"
addChildDevice("Binary Switch Endpoint", dni, device.hub.id,
[completedSetup: true, label: "${componentLabel}",
isComponent : false, componentName: "ch2", componentLabel: "${componentLabel}"])
log.debug "Endpoint 2 (Binary Switch Endpoint) added as $componentLabel"
} catch (e) {
log.warn "Failed to add endpoint 2 ($desc) as Binary Switch Endpoint - $e"
}
configure()
}

def updated() {
configure()
}

def configure() {
// Device-Watch simply pings if no device events received for checkInterval duration of 32min = 2 * 15min + 2min lag time
sendEvent(name: "checkInterval", value: 30 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
def commands = []
if (zwaveInfo.mfr.equals("0258")) {
commands << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, configurationValue: [0]).format()
commands << "delay 100"
}
commands << zwave.basicV1.basicGet().format()
response(commands)
}

def parse(String description) {
def result = null
def cmd = zwave.parse(description, [0x20: 1, 0x84: 1, 0x98: 1, 0x56: 1, 0x60: 3])
if (cmd) {
result = zwaveEvent(cmd)
}
log.debug("'$description' parsed to $result")
return createEvent(result)
}

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
[name: "switch", value: cmd.value ? "on" : "off"]
}

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
[name: "switch", value: cmd.value ? "on" : "off"]
}

def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
[name: "switch", value: cmd.value ? "on" : "off"]
}

def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x32: 3, 0x25: 1, 0x20: 1])
if (cmd.sourceEndPoint == 1) {
zwaveEvent(encapsulatedCommand)
} else { // sourceEndPoint == 2
childDevices[0]?.handleZWave(encapsulatedCommand)
[:]
}
}

def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) {
def versions = [0x31: 2, 0x30: 1, 0x84: 1, 0x9C: 1, 0x70: 2]
def version = versions[cmd.commandClass as Integer]
def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass)
def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data)
if (encapsulatedCommand) {
zwaveEvent(encapsulatedCommand)
}
[:]
}

def zwaveEvent(physicalgraph.zwave.Command cmd) {
[descriptionText: "$device.displayName: $cmd", isStateChange: true]
}

def on() {
// parent DTH conrols endpoint 1
def endpointNumber = 1
delayBetween([
encap(endpointNumber, zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF)),
encap(endpointNumber, zwave.switchBinaryV1.switchBinaryGet())
])
}

def off() {
// parent DTH conrols endpoint 1
def endpointNumber = 1
delayBetween([
encap(endpointNumber, zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00)),
encap(endpointNumber, zwave.switchBinaryV1.switchBinaryGet())
])
}

/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
refresh()
}

def refresh() {
// parent DTH conrols endpoint 1
def endpointNumber = 1
encap(endpointNumber, zwave.switchBinaryV1.switchBinaryGet())
}

// sendCommand is called by endpoint 2 child device handler
def sendCommand(endpointDevice, commands) {
//There is only 1 child device - endpoint 2
def endpointNumber = 2
def result
if (commands instanceof String) {
commands = commands.split(',') as List
}
result = commands.collect { encap(endpointNumber, it) }
sendHubCommand(result, 100)
}

def encap(endpointNumber, cmd) {
if (cmd instanceof physicalgraph.zwave.Command) {
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpointNumber).encapsulate(cmd).format()
} else if (cmd.startsWith("delay")) {
cmd
} else {
def header = "600D00"
String.format("%s%02X%s", header, endpointNumber, cmd)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Copyright 2018 SmartThings
*
* 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: "Binary Switch Endpoint", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Health Check"
capability "Refresh"
capability "Sensor"
capability "Switch"
}

simulator {
}

// tile definitions
tiles(scale: 2) {
multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
}
}

standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
}

main "switch"
details(["switch", "refresh"])
}
}

def installed() {
configure()
}

def updated() {
configure()
}

def configure() {
// Device-Watch simply pings if no device events received for checkInterval duration of 32min
sendEvent(name: "checkInterval", value: 30 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
refresh()
}

def handleZWave(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
switchEvents(cmd)
}

def handleZWave(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
switchEvents(cmd)
}

def handleZWave(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
switchEvents(cmd)
}

def switchEvents(physicalgraph.zwave.Command cmd) {
def value = (cmd.value ? "on" : "off")
sendEvent(name: "switch", value: value, descriptionText: "$device.displayName was turned $value")
}

def handleZWave(physicalgraph.zwave.Command cmd) {
sendEvent(descriptionText: "$device.displayName: $cmd", isStateChange: true, displayed: false)
}

def on() {
// We do not use delayBetween, as delay required may be different for each parent device
parent.sendCommand(device, [zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF),
zwave.switchBinaryV1.switchBinaryGet()])
}

def off() {
// We do not use delayBetween, as delay required may be different for each parent device
parent.sendCommand(device, [zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00),
zwave.switchBinaryV1.switchBinaryGet()])
}

/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
refresh()
}

def refresh() {
parent.sendCommand(device, zwave.switchBinaryV1.switchBinaryGet())
}

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ metadata {
fingerprint inClusters: "0x26,0x32"
fingerprint mfr:"0086", prod:"0003", model:"001B", deviceJoinName: "Aeotec Micro Smart Dimmer 2E"
fingerprint mfr:"0086", prod:"0103", model:"0063", deviceJoinName: "Aeotec Smart Dimmer 6"
fingerprint mfr:"014F", prod:"5044", model:"3533", deviceJoinName: "GoControl Plug-in Dimmer"
}

simulator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ metadata {
fingerprint mfr: "0086", prod: "0003", model: "0012", deviceJoinName: "Aeotec Micro Smart Switch"
fingerprint mfr: "021F", prod: "0003", model: "0087", deviceJoinName: "Dome On/Off Plug-in Switch"
fingerprint mfr: "0086", prod: "0103", model: "0060", deviceJoinName: "Aeotec Smart Switch 6"
fingerprint mfr: "014F", prod: "574F", model: "3535", deviceJoinName: "GoControl Wall-Mounted Outlet"
fingerprint mfr: "014F", prod: "5053", model: "3531", deviceJoinName: "GoControl Plug-in Switch"
}

// simulator metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def configure() {
log.debug "configure()"
def cmds = []

if (state.sec != 1) {
if (!isSecured()) {
// secure inclusion may not be complete yet
cmds << "delay 1000"
}
Expand Down Expand Up @@ -317,7 +317,7 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
}

private secure(physicalgraph.zwave.Command cmd) {
if (state.sec == 0) { // default to secure
if (!isSecured()) { // default to secure
cmd.format()
} else {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
Expand All @@ -327,3 +327,11 @@ private secure(physicalgraph.zwave.Command cmd) {
private secureSequence(commands, delay=200) {
delayBetween(commands.collect{ secure(it) }, delay)
}

private isSecured() {
if (zwaveInfo && zwaveInfo.zw) {
return zwaveInfo.zw.endsWith("s")
} else {
return state.sec == 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ metadata {
fingerprint mfr: "001A", prod: "534C", model: "0000", deviceJoinName: "Eaton RF Master Switch"
fingerprint mfr: "001A", prod: "5354", model: "0003", deviceJoinName: "Eaton RF Appliance Plug-In Module"
fingerprint mfr: "001A", prod: "5352", model: "0000", deviceJoinName: "Eaton RF Accessory Switch"
fingerprint mfr: "014F", prod: "5753", model: "3535", deviceJoinName: "GoControl Smart In-Wall Switch"
fingerprint mfr: "014F", prod: "5257", model: "3033", deviceJoinName: "GoControl Wall Relay Switch"
}

// simulator metadata
Expand Down

0 comments on commit 0aa89bf

Please sign in to comment.