From 7def620f046644f4ede5aecead4d6967bcff1b5f Mon Sep 17 00:00:00 2001 From: Lee Joonmin Date: Wed, 23 Dec 2015 03:44:19 -0600 Subject: [PATCH 1/3] MSA-757: Smart Gas-Lock work with z-wave --- .../timevalve-smart.groovy | 281 ++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy diff --git a/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy b/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy new file mode 100644 index 0000000..15bb8ca --- /dev/null +++ b/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy @@ -0,0 +1,281 @@ +metadata { + definition (name: "Timevalve Smart", namespace: "timevalve.gaslock.t-08", author: "ruinnel") { + capability "Valve" + capability "Refresh" + capability "Battery" + capability "Notification" + capability "Configuration" + capability "Temperature Measurement" + + command "setRemaining" + command "setTimeout" + command "setTimeout10" + command "setTimeout20" + command "setTimeout30" + command "setTimeout40" + + command "remainingLevel" + + attribute "remaining", "number" + attribute "remainingText", "String" + attribute "timeout", "number" + } + + simulator { + } + + tiles (scale: 2) { + multiAttributeTile(name:"statusTile", type:"thermostat", width:6, height:4) { + tileAttribute("device.contact", key: "PRIMARY_CONTROL") { + attributeState "open", label: '${name}', action: "close", icon:"st.contact.contact.open", backgroundColor:"#ffa81e" + attributeState "closed", label:'${name}', action: "", icon:"st.contact.contact.closed", backgroundColor:"#79b821" + } + tileAttribute("device.remainingText", key: "SECONDARY_CONTROL") { + attributeState "open", label: '${currentValue}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e" + attributeState "closed", label:'', icon:"st.contact.contact.closed", backgroundColor:"#79b821" + } + } + + standardTile("refreshTile", "command.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" + } + + controlTile("remainingSliderTile", "device.remaining", "slider", inactiveLabel: false, range:"(0..590)", height: 2, width: 4) { + state "level", action:"remainingLevel" + } + valueTile("setRemaining", "device.remainingText", inactiveLabel: false, decoration: "flat", height: 2, width: 2){ + state "remainingText", label:'${currentValue}\nRemaining'//, action: "setRemaining"//, icon: "st.Office.office6" + } + + standardTile("setTimeout10", "device.remaining", inactiveLabel: false, decoration: "flat") { + state "default", label:'10Min', action: "setTimeout10", icon:"st.Health & Wellness.health7", defaultState: true + state "10", label:'10Min', action: "setTimeout10", icon:"st.Office.office13" + } + standardTile("setTimeout20", "device.remaining", inactiveLabel: false, decoration: "flat") { + state "default", label:'20Min', action: "setTimeout20", icon:"st.Health & Wellness.health7", defaultState: true + state "20", label:'20Min', action: "setTimeout20", icon:"st.Office.office13" + } + standardTile("setTimeout30", "device.remaining", inactiveLabel: false, decoration: "flat") { + state "default", label:'30Min', action: "setTimeout30", icon:"st.Health & Wellness.health7", defaultState: true + state "30", label:'30Min', action: "setTimeout30", icon:"st.Office.office13" + } + standardTile("setTimeout40", "device.remaining", inactiveLabel: false, decoration: "flat") { + state "default", label:'40Min', action: "setTimeout40", icon:"st.Health & Wellness.health7", defaultState: true + state "40", label:'40Min', action: "setTimeout40", icon:"st.Office.office13" + } + + valueTile("batteryTile", "device.battery", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + state "battery", label:'${currentValue}% battery', unit:"" + } + + main (["statusTile"]) +// details (["statusTile", "remainingSliderTile", "setRemaining", "setTimeout10", "setTimeout20", "batteryTile", "refreshTile", "setTimeout30", "setTimeout40"]) +// details (["statusTile", "batteryTile", "setRemaining", "refreshTile"]) + details (["statusTile", "batteryTile", "refreshTile"]) + } +} + +def parse(description) { +// log.debug "parse - " + description + def result = null + if (description.startsWith("Err 106")) { + state.sec = 0 + result = createEvent(descriptionText: description, isStateChange: true) + } else if (description != "updated") { + def cmd = zwave.parse(description, [0x20: 1, 0x25: 1, 0x70: 1, 0x71: 1, 0x98: 1]) + if (cmd) { + log.debug "parsed cmd = " + cmd + result = zwaveEvent(cmd) + //log.debug("'$description' parsed to $result") + } else { + log.debug("Couldn't zwave.parse '$description'") + } + } + return result +} + +// 복호화 후 zwaveEvent() 호출 +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + //log.debug "SecurityMessageEncapsulation - " + cmd + def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x25: 1, 0x70: 1, 0x71: 1, 0x98: 1]) + if (encapsulatedCommand) { + state.sec = 1 + log.debug "encapsulatedCommand = " + encapsulatedCommand + zwaveEvent(encapsulatedCommand) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { + //log.debug "switch status - " + cmd.value + createEvent(name:"contact", value: cmd.value ? "open" : "closed") +} + +def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + def map = [ name: "battery", unit: "%" ] + if (cmd.batteryLevel == 0xFF) { // Special value for low battery alert + map.value = 1 + map.descriptionText = "${device.displayName} has a low battery" + map.isStateChange = true + } else { + map.value = cmd.batteryLevel + } + + log.debug "battery - ${map.value}${map.unit}" + // Store time of last battery update so we don't ask every wakeup, see WakeUpNotification handler + state.lastbatt = new Date().time + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + //log.debug "zwaveEvent - ${device.displayName}: ${cmd}" + createEvent(descriptionText: "${device.displayName}: ${cmd}") +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + def result = [] + log.info "zwave.configurationV1.configurationGet - " + cmd + def array = cmd.configurationValue + def value = ( (array[0] * 0x1000000) + (array[1] * 0x10000) + (array[2] * 0x100) + array[3] ).intdiv(60) + if (device.currentValue("switch") == "on") { + value = ( (array[0] * 0x1000000) + (array[1] * 0x10000) + (array[2] * 0x100) + array[3] ).intdiv(60) + } else { + value = 0 + } + + if (device.currentValue('contact') == 'open') { + def hour = value.intdiv(60); + def min = (value % 60).toString().padLeft(2, '0'); + def text = "${hour}:${min}M" + + log.info "remain - " + text + result.add( createEvent(name: "remaining", value: value, displayed: false, isStateChange: true) ) + result.add( createEvent(name: "remainingText", value: text, displayed: false, isStateChange: true) ) + } else { + result.add( createEvent(name: "timeout", value: value, displayed: false, isStateChange: true) ) + } + + /* + if (cmd.parameterNumber == 0x01) { // timeout + result.add( createEvent(name: "timeout", value: value, displayed: false, isStateChange: true) ) + + + // timeout 설정 시에도 + def hour = value.intdiv(60); + def min = (value % 60).toString().padLeft(2, '0'); + def text = "${hour}:${min}M" + + result.add( createEvent(name: "remaining", value: value, displayed: false, isStateChange: true) ) + result.add( createEvent(name: "remainingText", value: text, displayed: false, isStateChange: true) ) + } else if (cmd.parameterNumber == 0x03) { // remaining + def hour = value.intdiv(60); + def min = (value % 60).toString().padLeft(2, '0'); + def text = "${hour}:${min}M" + + debug.info "remain - " + text + result.add( createEvent(name: "remaining", value: value, displayed: false, isStateChange: true) ) + result.add( createEvent(name: "remainingText", value: text, displayed: false, isStateChange: true) ) + } + */ + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + def type = cmd.notificationType + if (type == cmd.NOTIFICATION_TYPE_HEAT) { + log.info "NotificationReport - ${type}" + createEvent(name: "temperature", value: 80, descriptionText: "${device.displayName} is over heat!", displayed: true, isStateChange: true) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) { + def type = cmd.alarmType + def level = cmd.alarmLevel + + log.info "AlarmReport - type : ${type}, level : ${level}" + def msg = "${device.displayName} is over heat!" + def result = createEvent(name: "temperature", value: 80, descriptionText: msg, displayed: true, isStateChange: true) + if (sendPushMessage) { + sendPushMessage(msg) + } + return result +} + +// remote open not allow +def open() { +// log.debug 'cmd - open()' + commands([ + zwave.basicV1.basicSet(value: 0xFF).format(), + zwave.basicV1.basicGet().format() + ]) // 5 second delay for dimmers that change gradually, can be left out for immediate switches +} + +def close() { +// log.debug 'cmd - close()' + commands([ + zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00), + zwave.switchBinaryV1.switchBinaryGet() + ]) +} + +def setTimeout10() { setTimeout(10) } +def setTimeout20() { setTimeout(20) } +def setTimeout30() { setTimeout(30) } +def setTimeout40() { setTimeout(40) } + + +def setTimeout(value) { +// log.debug "setDefaultTime($value)" + commands([ + zwave.configurationV1.configurationSet(parameterNumber: 0x01, size: 4, scaledConfigurationValue: value * 60), + zwave.configurationV1.configurationGet(parameterNumber: 0x01) + ]); +} + +def remainingLevel(value) { +// log.debug "remainingLevel($value)" + def hour = value.intdiv(60); + def min = (value % 60).toString().padLeft(2, '0'); + def text = "${hour}:${min}M" + sendEvent(name: "remaining", value: value, displayed: false, isStateChange: true) + sendEvent(name: "remainingText", value: text, displayed: false, isStateChange: true) +} + +def setRemaining() { + def remaining = device.currentValue("remaining") +// log.debug "setConfiguration() - remaining : $remaining" + commands([ + zwave.configurationV1.configurationSet(parameterNumber: 0x03, size: 4, scaledConfigurationValue: remaining * 60), + zwave.configurationV1.configurationGet(parameterNumber: 0x03) + ]); +} + +private command(physicalgraph.zwave.Command cmd) { + if (state.sec != 0 && !(cmd instanceof physicalgraph.zwave.commands.batteryv1.BatteryGet)) { + log.debug "cmd = " + cmd + ", encapsulation" + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + log.debug "cmd = " + cmd + ", plain" + cmd.format() + } +} + +private commands(commands, delay=200) { + delayBetween(commands.collect{ command(it) }, delay) +} + +def refresh() { +// log.debug 'cmd - refresh()' + commands([ + zwave.batteryV1.batteryGet(), + zwave.switchBinaryV1.switchBinaryGet(), + zwave.configurationV1.configurationGet(parameterNumber: 0x01), + zwave.configurationV1.configurationGet(parameterNumber: 0x03) + ], 400) +} + + +// If you add the Configuration capability to your device type, this +// command will be called right after the device joins to set +// device-specific configuration commands. +def configure() { +} \ No newline at end of file From 3a7abd6169b1d940581963861b830c48cfebd063 Mon Sep 17 00:00:00 2001 From: Lee Joonmin Date: Tue, 12 Jan 2016 18:01:23 -0600 Subject: [PATCH 2/3] Modifying 'Timevalve Smart' --- .../timevalve-smart.groovy | 54 +++---------------- 1 file changed, 8 insertions(+), 46 deletions(-) diff --git a/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy b/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy index 15bb8ca..1a45cf1 100644 --- a/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy +++ b/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy @@ -3,8 +3,6 @@ metadata { capability "Valve" capability "Refresh" capability "Battery" - capability "Notification" - capability "Configuration" capability "Temperature Measurement" command "setRemaining" @@ -19,13 +17,13 @@ metadata { attribute "remaining", "number" attribute "remainingText", "String" attribute "timeout", "number" - } - - simulator { + + //raw desc : 0 0 0x1006 0 0 0 7 0x5E 0x86 0x72 0x5A 0x73 0x98 0x80 + fingerprint deviceId:"0x1006", inClusters:"0x5E, 0x86, 0x72, 0x5A, 0x73, 0x98, 0x80" } tiles (scale: 2) { - multiAttributeTile(name:"statusTile", type:"thermostat", width:6, height:4) { + multiAttributeTile(name:"statusTile", type:"generic", width:6, height:4) { tileAttribute("device.contact", key: "PRIMARY_CONTROL") { attributeState "open", label: '${name}', action: "close", icon:"st.contact.contact.open", backgroundColor:"#ffa81e" attributeState "closed", label:'${name}', action: "", icon:"st.contact.contact.closed", backgroundColor:"#79b821" @@ -136,7 +134,7 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport log.info "zwave.configurationV1.configurationGet - " + cmd def array = cmd.configurationValue def value = ( (array[0] * 0x1000000) + (array[1] * 0x10000) + (array[2] * 0x100) + array[3] ).intdiv(60) - if (device.currentValue("switch") == "on") { + if (device.currentValue("contact") == "open") { value = ( (array[0] * 0x1000000) + (array[1] * 0x10000) + (array[2] * 0x100) + array[3] ).intdiv(60) } else { value = 0 @@ -153,29 +151,6 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport } else { result.add( createEvent(name: "timeout", value: value, displayed: false, isStateChange: true) ) } - - /* - if (cmd.parameterNumber == 0x01) { // timeout - result.add( createEvent(name: "timeout", value: value, displayed: false, isStateChange: true) ) - - - // timeout 설정 시에도 - def hour = value.intdiv(60); - def min = (value % 60).toString().padLeft(2, '0'); - def text = "${hour}:${min}M" - - result.add( createEvent(name: "remaining", value: value, displayed: false, isStateChange: true) ) - result.add( createEvent(name: "remainingText", value: text, displayed: false, isStateChange: true) ) - } else if (cmd.parameterNumber == 0x03) { // remaining - def hour = value.intdiv(60); - def min = (value % 60).toString().padLeft(2, '0'); - def text = "${hour}:${min}M" - - debug.info "remain - " + text - result.add( createEvent(name: "remaining", value: value, displayed: false, isStateChange: true) ) - result.add( createEvent(name: "remainingText", value: text, displayed: false, isStateChange: true) ) - } - */ return result } @@ -183,7 +158,7 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm def type = cmd.notificationType if (type == cmd.NOTIFICATION_TYPE_HEAT) { log.info "NotificationReport - ${type}" - createEvent(name: "temperature", value: 80, descriptionText: "${device.displayName} is over heat!", displayed: true, isStateChange: true) + createEvent(name: "temperature", value: 999, unit: "C", descriptionText: "${device.displayName} is over heat!", displayed: true, isStateChange: true) } } @@ -193,7 +168,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) { log.info "AlarmReport - type : ${type}, level : ${level}" def msg = "${device.displayName} is over heat!" - def result = createEvent(name: "temperature", value: 80, descriptionText: msg, displayed: true, isStateChange: true) + def result = createEvent(name: "temperature", value: 999, unit: "C", descriptionText: msg, displayed: true, isStateChange: true) if (sendPushMessage) { sendPushMessage(msg) } @@ -201,13 +176,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) { } // remote open not allow -def open() { -// log.debug 'cmd - open()' - commands([ - zwave.basicV1.basicSet(value: 0xFF).format(), - zwave.basicV1.basicGet().format() - ]) // 5 second delay for dimmers that change gradually, can be left out for immediate switches -} +def open() {} def close() { // log.debug 'cmd - close()' @@ -271,11 +240,4 @@ def refresh() { zwave.configurationV1.configurationGet(parameterNumber: 0x01), zwave.configurationV1.configurationGet(parameterNumber: 0x03) ], 400) -} - - -// If you add the Configuration capability to your device type, this -// command will be called right after the device joins to set -// device-specific configuration commands. -def configure() { } \ No newline at end of file From 7669bec0bc9270bb765468e687ea08d14dae96c3 Mon Sep 17 00:00:00 2001 From: Jason Botello Date: Wed, 20 Jan 2016 16:17:32 -0800 Subject: [PATCH 3/3] Update timevalve-smart.groovy Commenting out fingerprint temporarily to avoid potential conflicts with other devices as this devices is specifically for a Korean deployment in AP01 - see DVCSMP-1425 --- .../timevalve-smart.src/timevalve-smart.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy b/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy index 1a45cf1..53df65a 100644 --- a/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy +++ b/devicetypes/timevalve-gaslock-t-08/timevalve-smart.src/timevalve-smart.groovy @@ -19,7 +19,7 @@ metadata { attribute "timeout", "number" //raw desc : 0 0 0x1006 0 0 0 7 0x5E 0x86 0x72 0x5A 0x73 0x98 0x80 - fingerprint deviceId:"0x1006", inClusters:"0x5E, 0x86, 0x72, 0x5A, 0x73, 0x98, 0x80" + //fingerprint deviceId:"0x1006", inClusters:"0x5E, 0x86, 0x72, 0x5A, 0x73, 0x98, 0x80" } tiles (scale: 2) { @@ -240,4 +240,4 @@ def refresh() { zwave.configurationV1.configurationGet(parameterNumber: 0x01), zwave.configurationV1.configurationGet(parameterNumber: 0x03) ], 400) -} \ No newline at end of file +}