Compare commits

..

1 Commits

Author SHA1 Message Date
Kuldip
7cd07e7ab2 MSA-1371: Its a prototype for connecting the largest T (Car) in the IoT eco-system 2016-06-24 00:34:42 -05:00
7 changed files with 387 additions and 634 deletions

View File

@@ -15,13 +15,12 @@
*/
metadata {
definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Battery"
capability "Configuration"
capability "Contact Sensor"
capability "Refresh"
capability "Temperature Measurement"
capability "Health Check"
command "enrollResponse"
@@ -274,8 +273,7 @@ def refresh() {
}
def configure() {
sendEvent(name: "checkInterval", value: 7200, displayed: false)
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
log.debug "Configuring Reporting, IAS CIE, and Bindings."
def configCmds = [

View File

@@ -28,6 +28,8 @@ metadata {
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x98"
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82"
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x80,0x71,0x85,0x70,0x72,0x86,0x30,0x31,0x84,0x59,0x73,0x5A,0x8F,0x98,0x7A", outClusters:"0x20" // Philio multi+
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x72,0x5A,0x80,0x73,0x86,0x84,0x85,0x59,0x71,0x70,0x7A,0x98" // Vision door/window
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x98,0x86,0x72,0x5A,0x85,0x59,0x73,0x80,0x71,0x31,0x70,0x84,0x7A" // Vision Motion
}
// simulator metadata
@@ -80,7 +82,7 @@ def updated() {
def cmds = []
if (!state.MSR) {
cmds = [
command(zwave.manufacturerSpecificV2.manufacturerSpecificGet()),
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
"delay 1200",
zwave.wakeUpV1.wakeUpNoMoreInformation().format()
]
@@ -93,9 +95,9 @@ def updated() {
}
def configure() {
commands([
zwave.manufacturerSpecificV2.manufacturerSpecificGet(),
zwave.batteryV1.batteryGet()
delayBetween([
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
batteryGetCommand()
], 6000)
}
@@ -146,11 +148,12 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm
result << sensorValueEvent(1)
} else if (cmd.event == 0x03) {
result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
if(!state.MSR) result << response(command(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId))
if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
} else if (cmd.event == 0x05 || cmd.event == 0x06) {
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
} else if (cmd.event == 0x07) {
if(!state.MSR) result << response(command(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
result << createEvent(name: "motion", value: "active", descriptionText:"$device.displayName detected motion")
}
} else if (cmd.notificationType) {
@@ -168,13 +171,14 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd)
def event = createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
def cmds = []
if (!state.MSR) {
cmds << command(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
cmds << zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId).format()
cmds << zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()
cmds << "delay 1200"
}
if (!state.lastbat || now() - state.lastbat > 53*60*60*1000) {
cmds << command(zwave.batteryV1.batteryGet())
cmds << batteryGetCommand()
} else {
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation()
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
}
[event, response(cmds)]
}
@@ -209,7 +213,7 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
if (msr == "0086-0102-0059") {
result << response(zwave.securityV1.securityMessageEncapsulation().encapsulate(zwave.batteryV1.batteryGet()).format())
} else {
result << response(command(zwave.batteryV1.batteryGet()))
result << response(batteryGetCommand())
}
}
@@ -217,7 +221,7 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x25: 1, 0x30: 1, 0x31: 5, 0x80: 1, 0x84: 1, 0x71: 3, 0x9C: 1])
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1])
// log.debug "encapsulated: $encapsulatedCommand"
if (encapsulatedCommand) {
state.sec = 1
@@ -229,16 +233,12 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
}
private command(physicalgraph.zwave.Command cmd) {
if (state.sec == 1) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
} else {
cmd.format()
def batteryGetCommand() {
def cmd = zwave.batteryV1.batteryGet()
if (state.sec) {
cmd = zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd)
}
}
private commands(commands, delay=200) {
delayBetween(commands.collect{ command(it) }, delay)
cmd.format()
}
def retypeBasedOnMSR() {
@@ -261,12 +261,12 @@ def retypeBasedOnMSR() {
setDeviceType("3-in-1 Multisensor Plus (SG)")
break
case "0109-2001-0106": // Vision door/window
log.debug "Changing device type to Z-Wave Plus Door/Window Sensor"
setDeviceType("Z-Wave Plus Door/Window Sensor")
log.debug "Changing device type to Door / Window Sensor Plus (SG)"
setDeviceType("Door / Window Sensor Plus (SG)")
break
case "0109-2002-0205": // Vision Motion
log.debug "Changing device type to Z-Wave Plus Motion/Temp Sensor"
setDeviceType("Z-Wave Plus Motion/Temp Sensor")
log.debug "Changing device type to Vision Motion Sensor Plus (SG)"
setDeviceType("Vision Motion Sensor Plus (SG)")
break
}
}

View File

@@ -1,270 +0,0 @@
/**
* Copyright 2016 SmartThings
* Copyright 2015 AstraLink
*
* 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.
*
* Z-Wave Plus Door/Window Sensor, ZD2102*-5
*
*/
metadata {
definition (name: "Z-Wave Plus Door/Window Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Contact Sensor"
capability "Configuration"
capability "Battery"
capability "Sensor"
// for Astralink
attribute "ManufacturerCode", "string"
attribute "ProduceTypeCode", "string"
attribute "ProductCode", "string"
attribute "WakeUp", "string"
attribute "WirelessConfig", "string"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x98, 0x86, 0x72, 0x5A, 0x85, 0x59, 0x73, 0x80, 0x71, 0x70, 0x84, 0x7A"
fingerprint type:"8C07", inClusters: "5E,98,86,72,5A,71"
fingerprint mfr:"0109", prod:"2001", model:"0106" // not using deviceJoinName because it's sold under different brand names
}
tiles(scale: 2) {
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4){
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
}
}
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
main (["contact"])
details(["contact","battery"])
}
simulator {
// messages the device returns in response to commands it receives
status "open (basic)" : "command: 9881, payload: 00 20 01 FF"
status "closed (basic)" : "command: 9881 payload: 00 20 01 00"
status "open (notification)" : "command: 9881, payload: 00 71 05 06 FF 00 FF 06 16 00 00"
status "closed (notification)" : "command: 9881, payload: 00 71 05 06 00 00 FF 06 17 00 00"
status "tamper: enclosure opened" : "command: 9881, payload: 00 71 05 07 FF 00 FF 07 03 00 00"
status "tamper: enclosure replaced" : "command: 9881, payload: 00 71 05 07 00 00 FF 07 00 00 00"
status "wake up" : "command: 9881, payload: 00 84 07"
status "battery (100%)" : "command: 9881, payload: 00 80 03 64"
status "battery low" : "command: 9881, payload: 00 80 03 FF"
}
}
def configure() {
log.debug "configure()"
def cmds = []
if (state.sec != 1) {
// secure inclusion may not be complete yet
cmds << "delay 1000"
}
cmds += secureSequence([
zwave.manufacturerSpecificV2.manufacturerSpecificGet(),
zwave.batteryV1.batteryGet(),
], 500)
cmds << "delay 8000"
cmds << secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
return cmds
}
private getCommandClassVersions() {
[
0x71: 3, // Notification
0x5E: 2, // ZwaveplusInfo
0x59: 1, // AssociationGrpInfo
0x85: 2, // Association
0x20: 1, // Basic
0x80: 1, // Battery
0x70: 1, // Configuration
0x5A: 1, // DeviceResetLocally
0x7A: 2, // FirmwareUpdateMd
0x72: 2, // ManufacturerSpecific
0x73: 1, // Powerlevel
0x98: 1, // Security
0x84: 2, // WakeUp
0x86: 1, // Version
]
}
// Parse incoming device messages to generate events
def parse(String description) {
def result = []
def cmd
if (description.startsWith("Err 106")) {
state.sec = 0
result = createEvent( name: "secureInclusion", value: "failed", eventType: "ALERT",
descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.")
} else if (description.startsWith("Err")) {
result = createEvent(descriptionText: "$device.displayName $description", isStateChange: true)
} else {
cmd = zwave.parse(description, commandClassVersions)
if (cmd) {
result = zwaveEvent(cmd)
}
}
if (result instanceof List) {
result = result.flatten()
}
log.debug "Parsed '$description' to $result"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions)
log.debug "encapsulated: $encapsulatedCommand"
if (encapsulatedCommand) {
state.sec = 1
return zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
return [createEvent(descriptionText: cmd.toString())]
}
}
def sensorValueEvent(value) {
if (value) {
createEvent(name: "contact", value: "open", descriptionText: "$device.displayName is open")
} else {
createEvent(name: "contact", value: "closed", descriptionText: "$device.displayName is closed")
}
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
return sensorValueEvent(cmd.value)
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
return sensorValueEvent(cmd.value)
}
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) {
return sensorValueEvent(cmd.sensorValue)
}
def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) {
return sensorValueEvent(cmd.sensorState)
}
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
def result = []
if (cmd.notificationType == 0x06 && cmd.event == 0x16) {
result << sensorValueEvent(1)
} else if (cmd.notificationType == 0x06 && cmd.event == 0x17) {
result << sensorValueEvent(0)
} else if (cmd.notificationType == 0x07) {
if (cmd.event == 0x00) {
if (cmd.eventParametersLength == 0 || cmd.eventParameter[0] != 3) {
result << createEvent(descriptionText: "$device.displayName covering replaced", isStateChange: true, displayed: false)
} else {
result << sensorValueEvent(0)
}
} else if (cmd.event == 0x01 || cmd.event == 0x02) {
result << sensorValueEvent(1)
} else if (cmd.event == 0x03) {
result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
if (!device.currentState("ManufacturerCode")) {
result << response(secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
}
} else if (cmd.event == 0x05 || cmd.event == 0x06) {
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
} else {
result << createEvent(descriptionText: "$device.displayName event $cmd.event ${cmd.eventParameter.inspect()}", isStateChange: true, displayed: false)
}
} else if (cmd.notificationType) {
result << createEvent(descriptionText: "$device.displayName notification $cmd.notificationType event $cmd.event ${cmd.eventParameter.inspect()}", isStateChange: true, displayed: false)
} else {
def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive"
result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, isStateChange: true, displayed: false)
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
def event = createEvent(name: "WakeUp", value: "wakeup", descriptionText: "${device.displayName} woke up", isStateChange: true, displayed: false) // for Astralink
def cmds = []
if (!device.currentState("ManufacturerCode")) {
cmds << secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
cmds << "delay 2000"
}
if (!state.lastbat || now() - state.lastbat > 10*60*60*1000) {
event.descriptionText += ", requesting battery"
cmds << secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1))
cmds << "delay 800"
cmds << secure(zwave.batteryV1.batteryGet())
cmds << "delay 2000"
} else {
log.debug "not checking battery, was updated ${(now() - state.lastbat)/60000 as int} min ago"
}
cmds << secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
return [event, response(cmds)]
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "${device.displayName} has a low battery"
map.isStateChange = true
} else {
map.value = cmd.batteryLevel
}
def event = createEvent(map)
// Save at least one battery report in events list every few days
if (!event.isStateChange && (now() - 3*24*60*60*1000) > device.latestState("battery")?.date?.time) {
map.isStateChange = true
}
state.lastbat = now()
return [event]
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def result = []
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
def productTypeCode = String.format("%04X", cmd.productTypeId)
def productCode = String.format("%04X", cmd.productId)
def wirelessConfig = "ZWP"
log.debug "MSR ${manufacturerCode} ${productTypeCode} ${productCode}"
result << createEvent(name: "ManufacturerCode", value: manufacturerCode)
result << createEvent(name: "ProduceTypeCode", value: productTypeCode)
result << createEvent(name: "ProductCode", value: productCode)
result << createEvent(name: "WirelessConfig", value: wirelessConfig)
return result
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
return [createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)]
}
private secure(physicalgraph.zwave.Command cmd) {
if (state.sec == 0) { // default to secure
cmd.format()
} else {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
}
private secureSequence(commands, delay=200) {
delayBetween(commands.collect{ secure(it) }, delay)
}

View File

@@ -1,327 +0,0 @@
/**
* Copyright 2016 SmartThings
* Copyright 2015 AstraLink
*
* 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.
*
* Z-Wave Plus Motion Sensor with Temperature Measurement, ZP3102*-5
*
*/
metadata {
definition (name: "Z-Wave Plus Motion/Temp Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Motion Sensor"
capability "Temperature Measurement"
capability "Configuration"
capability "Battery"
capability "Sensor"
// for Astralink
attribute "ManufacturerCode", "string"
attribute "ProduceTypeCode", "string"
attribute "ProductCode", "string"
attribute "WakeUp", "string"
attribute "WirelessConfig", "string"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x98, 0x86, 0x72, 0x5A, 0x85, 0x59, 0x73, 0x80, 0x71, 0x31, 0x70, 0x84, 0x7A"
fingerprint type:"8C07", inClusters: "5E,98,86,72,5A,31,71"
fingerprint mfr:"0109", prod:"2002", model:"0205" // not using deviceJoinName because it's sold under different brand names
}
tiles {
standardTile("motion", "device.motion", width: 3, height: 2) {
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
}
valueTile("temperature", "device.temperature", inactiveLabel: false) {
state "temperature", label:'${currentValue}°',
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:"%"
}
main(["motion", "temperature"])
details(["motion", "temperature", "battery"])
}
}
def updated() {
if (!device.currentState("ManufacturerCode")) {
response(secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
}
}
def configure() {
log.debug "configure()"
def cmds = []
if (state.sec != 1) {
// secure inclusion may not be complete yet
cmds << "delay 1000"
}
cmds += secureSequence([
zwave.manufacturerSpecificV2.manufacturerSpecificGet(),
zwave.batteryV1.batteryGet(),
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1)
], 500)
cmds << "delay 8000"
cmds << secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
return cmds
}
private getCommandClassVersions() {
[
0x71: 3, // Notification
0x5E: 2, // ZwaveplusInfo
0x59: 1, // AssociationGrpInfo
0x85: 2, // Association
0x20: 1, // Basic
0x80: 1, // Battery
0x70: 1, // Configuration
0x5A: 1, // DeviceResetLocally
0x7A: 2, // FirmwareUpdateMd
0x72: 2, // ManufacturerSpecific
0x73: 1, // Powerlevel
0x98: 1, // Security
0x31: 5, // SensorMultilevel
0x84: 2 // WakeUp
]
}
// Parse incoming device messages to generate events
def parse(String description) {
def result = []
def cmd
if (description.startsWith("Err 106")) {
state.sec = 0
result = createEvent( name: "secureInclusion", value: "failed", eventType: "ALERT",
descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.")
} else if (description.startsWith("Err")) {
result = createEvent(descriptionText: "$device.displayName $description", isStateChange: true)
} else {
cmd = zwave.parse(description, commandClassVersions)
if (cmd) {
result = zwaveEvent(cmd)
}
}
if (result instanceof List) {
result = result.flatten()
}
log.debug "Parsed '$description' to $result"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions)
log.debug "encapsulated: $encapsulatedCommand"
if (encapsulatedCommand) {
state.sec = 1
return zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
return [createEvent(descriptionText: cmd.toString())]
}
}
def sensorValueEvent(value) {
def result = []
if (value) {
log.debug "sensorValueEvent($value) : active"
result << createEvent(name: "motion", value: "active", descriptionText: "$device.displayName detected motion")
} else {
log.debug "sensorValueEvent($value) : inactive"
result << createEvent(name: "motion", value: "inactive", descriptionText: "$device.displayName motion has stopped")
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
return sensorValueEvent(cmd.value)
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
return sensorValueEvent(cmd.value)
}
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
return sensorValueEvent(cmd.value)
}
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) {
return sensorValueEvent(cmd.sensorValue)
}
def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) {
return sensorValueEvent(cmd.sensorState)
}
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
def result = []
if (cmd.notificationType == 0x07) {
if (cmd.event == 0x01 || cmd.event == 0x02) {
result << sensorValueEvent(1)
} else if (cmd.event == 0x03) {
result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
result << response(secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
} else if (cmd.event == 0x05 || cmd.event == 0x06) {
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
} else if (cmd.event == 0x07) {
result << sensorValueEvent(1)
} else if (cmd.event == 0x08) {
result << sensorValueEvent(1)
} else if (cmd.event == 0x00) {
if (cmd.eventParametersLength && cmd.eventParameter[0] == 3) {
result << createEvent(descriptionText: "$device.displayName covering replaced", isStateChange: true, displayed: false)
} else {
result << sensorValueEvent(0)
}
} else if (cmd.event == 0xFF) {
result << sensorValueEvent(1)
} else {
result << createEvent(descriptionText: "$device.displayName sent event $cmd.event")
}
} else if (cmd.notificationType) {
def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}"
result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, displayed: false)
} else {
def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive"
result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, displayed: false)
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
def event = createEvent(name: "WakeUp", value: "wakeup", descriptionText: "${device.displayName} woke up", isStateChange: true, displayed: false) // for Astralink
def cmds = []
if (!device.currentState("ManufacturerCode")) {
cmds << secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
cmds << "delay 2000"
}
if (!state.lastbat || now() - state.lastbat > 10*60*60*1000) {
event.descriptionText += ", requesting battery"
cmds << secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1))
cmds << "delay 800"
cmds << secure(zwave.batteryV1.batteryGet())
cmds << "delay 2000"
} else {
log.debug "not checking battery, was updated ${(now() - state.lastbat)/60000 as int} min ago"
}
cmds << secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
return [event, response(cmds)]
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def result = []
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "${device.displayName} has a low battery"
map.isStateChange = true
} else {
map.value = cmd.batteryLevel
}
def event = createEvent(map)
// Save at least one battery report in events list every few days
if (!event.isStateChange && (now() - 3*24*60*60*1000) > device.latestState("battery")?.date?.time) {
map.isStateChange = true
}
state.lastbat = now()
return [event]
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
def result = []
def map = [:]
switch (cmd.sensorType) {
case 1:
def cmdScale = cmd.scale == 1 ? "F" : "C"
map.name = "temperature"
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
map.unit = getTemperatureScale()
break;
case 3:
map.name = "illuminance"
map.value = cmd.scaledSensorValue.toInteger().toString()
map.unit = "lux"
break;
case 5:
map.name = "humidity"
map.value = cmd.scaledSensorValue.toInteger().toString()
map.unit = cmd.scale == 0 ? "%" : ""
break;
case 0x1E:
map.name = "loudness"
map.unit = cmd.scale == 1 ? "dBA" : "dB"
map.value = cmd.scaledSensorValue.toString()
break;
default:
map.descriptionText = cmd.toString()
}
result << createEvent(map)
return result
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def result = []
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
def productTypeCode = String.format("%04X", cmd.productTypeId)
def productCode = String.format("%04X", cmd.productId)
def wirelessConfig = "ZWP"
log.debug "MSR ${manufacturerCode} ${productTypeCode} ${productCode}"
result << createEvent(name: "ManufacturerCode", value: manufacturerCode)
result << createEvent(name: "ProduceTypeCode", value: productTypeCode)
result << createEvent(name: "ProductCode", value: productCode)
result << createEvent(name: "WirelessConfig", value: wirelessConfig)
if (manufacturerCode == "0109" && productTypeCode == "2002") {
result << response(secureSequence([
// Change re-trigger duration to 1 minute
zwave.configurationV1.configurationSet(parameterNumber: 1, configurationValue: [1], size: 1),
zwave.batteryV1.batteryGet(),
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1)
], 400))
}
return result
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
return [createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)]
}
private secure(physicalgraph.zwave.Command cmd) {
if (state.sec == 0) { // default to secure
cmd.format()
} else {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
}
private secureSequence(commands, delay=200) {
delayBetween(commands.collect{ secure(it) }, delay)
}

View File

@@ -0,0 +1,333 @@
/**
* CCTest
*
* Copyright 2016 Kuldip
*
* 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.
*
*/
definition(
name: "CloudCar",
namespace: "CloudCar",
author: "Kuldip@CloudCar",
description: "Connect Car and Home",
category: "Convenience",
iconUrl: "http://justdrive.net/wp-content/themes/justdrive/assets/img/jd-logo-sans.png",
iconX2Url: "http://justdrive.net/wp-content/themes/justdrive/assets/img/jd-logo-sans.png",
iconX3Url: "http://justdrive.net/wp-content/themes/justdrive/assets/img/jd-logo-sans.png",
oauth: [displayName: "CloudCar", displayLink: "https://CloudCar.com"]
)
preferences {
section ("Control your home while driving your car") {
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false
input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors?", multiple: true, required: false
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors?", multiple: true, required: false
input "temperatureSensors", "capability.temperatureMeasurement", title: "Which Temperature Sensors?", multiple: true, required: false
input "accelerationSensors", "capability.accelerationSensor", title: "Which Vibration Sensors?", multiple: true, required: false
input "waterSensors", "capability.waterSensor", title: "Which Water Sensors?", multiple: true, required: false
input "lightSensors", "capability.illuminanceMeasurement", title: "Which Light Sensors?", multiple: true, required: false
input "humiditySensors", "capability.relativeHumidityMeasurement", title: "Which Relative Humidity Sensors?", multiple: true, required: false
input "alarms", "capability.alarm", title: "Which Sirens?", multiple: true, required: false
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
}
}
mappings {
path("/scan") {
action: [
GET: "scanDevices"
]
}
path("/control/:command") {
action: [
GET: "updateSwitches"
]
}
path("/control/:deviceType/:deviceId/:command") {
action: [
GET: "updateDevice"
]
}
}
def installed() {
log.debug "[installed]: state ${state}"
initialize ()
}
def uninstalled() {
log.debug "[uninstalled]: ${settings}"
}
def updated () {
log.debug "[updated]: ${settings}"
unsubscribe()
initialize ()
}
def initialize () {
state.clear() // clear previous state - state is loaded from persistant store
addSubscription()
}
def addSubscription() {
def callbackUrl = 'http://ec2-54-67-21-37.us-west-1.compute.amazonaws.com/notify' //data.callbackUrl
log.debug "[addSubscription] state ${state}"
log.debug "[addSubscription] settings ${settings}"
settings.each {
log.debug "[addSubscription] settings ${it}"
}
def currentDeviceIds = settings.collect { k, devices -> devices }.flatten().collect()// { it.id }.unique()
log.debug "[addSubscription] currentDeviceIds ${currentDeviceIds}"
currentDeviceIds.each {
log.debug "[addSubscription] device ids ${it} - ${it.id}"
log.debug "Adding switch subscription " + callbackUrl
state[it.id] = [callbackUrl: callbackUrl]
}
subscribe(switches, "switch.on", mydeviceHandler)
subscribe(contactSensors, "contact.open", mydeviceHandler)
subscribe(motionSensors, "motion.active", mydeviceHandler)
log.debug "[addSubscription] COMPLETED ${state}"
}
def mydeviceHandler(evt) {
def callbackUrl = 'http://ec2-54-67-21-37.us-west-1.compute.amazonaws.com/notify' //data.callbackUrl
//def callbackUrl = 'https://testing.justdrive.cloudcar.com/notify/5c5dca5f-1b98-fff9-f111-8072b41c5fb3'
log.debug "[deviceHandler] $evt.displayName"
def deviceInfo = state[evt.deviceId]
log.debug "[deviceHandler] deviceInfo ${deviceInfo}"
if (deviceInfo) {
try {
httpPostJson(uri: callbackUrl, path: '', body: [deviceId: evt.deviceId, deviceType: evt.name, deviceName: evt.displayName, value: evt.value]) {
log.debug "[deviceHandler] Event data successfully posted"
}
} catch (groovyx.net.http.ResponseParseException e) {
log.debug("Error parsing payload ${e}")
}
} else {
log.debug "[deviceHandler] No subscribed device found"
}
}
def scanDevices() {
log.debug "[scandevices] ${type}"
[
switches: switches.collect{KPdevice(it,"switches")},
contactSensors: contacts.collect{KPdevice(it,"contactSensors")},
temperatures: temperatures.collect{KPdevice(it,"temperature")},
]
}
private KPdevice(it, type) {
def device_state = null
log.debug "KPDevice it: ${it}"
if (it.currentValue("switch") == "on" ) {
device_state = [label:it.label, type:type, id:it.id]
for (attribute in it.supportedAttributes) {
device_state."${attribute}" = it.currentValue("${attribute}")
}
}
log.debug "KPdevice: ${device_state}"
device_state ? device_state : null
}
def void updateSwitches() {
// use the built-in request object to get the command parameter
def command = params.command
log.debug "updateSwitches: ${params.command}"
// all switches have the command
// execute the command on all switches
// (note we can do this on the array - the command will be invoked on every element
switch(command) {
case "on":
switches.on()
break
case "off":
switches.off()
break
default:
httpError(400, "$command is not a valid command for all switches specified")
}
}
def void updateDevice() {
// use the built-in request object to get the command parameter
def command = params.command
def deviceId = params.deviceId
def devices = settings[params.deviceType]
log.debug "[updateDevice] ${deviceId} - ${command} - ${devices}"
// all switches have the command
// execute the command on all switches
// (note we can do this on the array - the command will be invoked on every element
def device = devices.find { it.id == deviceId }
if (!device) {
httpError(404, "Device not found")
} else {
device."$command"()
}
}
def list() {
log.debug "[PROD] list, params: ${params}"
def type = params.deviceType
settings[type]?.collect{deviceItem(it)} ?: []
}
def listStates() {
log.debug "[PROD] liststates, params: ${params}"
def type = params.deviceType
def attributeName = attributeFor(type)
settings[type]?.collect{deviceState(it, it.currentState(attributeName))} ?: []
}
def listSubscriptions() {
log.debug "[PROD] listSubscription, params: ${params}"
state
}
def update() {
def type = params.deviceType
def data = request.JSON
def devices = settings[type]
def device = settings[type]?.find { it.id == params.id }
def command = data.command
log.debug "[PROD] update, params: ${params}, request: ${data}, devices: ${devices*.id}"
if (!device) {
httpError(404, "Device not found")
}
if (validateCommand(device, type, command)) {
device."$command"()
} else {
httpError(403, "Access denied. This command is not supported by current capability.")
}
}
/**
* Validating the command passed by the user based on capability.
* @return boolean
*/
def validateCommand(device, deviceType, command) {
log.debug "[validatecommand] ${device} ${deviceType} ${command}"
def capabilityCommands = getDeviceCapabilityCommands(device.capabilities)
def currentDeviceCapability = getCapabilityName(deviceType)
if (capabilityCommands[currentDeviceCapability]) {
return command in capabilityCommands[currentDeviceCapability] ? true : false
} else {
// Handling other device types here, which don't accept commands
httpError(400, "Bad request.")
}
}
/**
* Need to get the attribute name to do the lookup. Only
* doing it for the device types which accept commands
* @return attribute name of the device type
*/
def getCapabilityName(type) {
log.debug "[getCapabilityName] ${type}"
switch(type) {
case "switches":
return "Switch"
case "alarms":
return "Alarm"
case "locks":
return "Lock"
default:
return type
}
}
/**
* Constructing the map over here of
* supported commands by device capability
* @return a map of device capability -> supported commands
*/
def getDeviceCapabilityCommands(deviceCapabilities) {
log.debug "[getDeviceCapabilities] ${deviceCapabilities}"
def map = [:]
deviceCapabilities.collect {
map[it.name] = it.commands.collect{ it.name.toString() }
}
return map
}
def show() {
def type = params.deviceType
def devices = settings[type]
def device = devices.find { it.id == params.id }
log.debug "[PROD] show, params: ${params}, devices: ${devices*.id}"
if (!device) {
httpError(404, "Device not found")
}
else {
def attributeName = attributeFor(type)
def s = device.currentState(attributeName)
deviceState(device, s)
}
}
def removeSubscription() {
def type = params.deviceType
def devices = settings[type]
def deviceId = params.id
def device = devices.find { it.id == deviceId }
log.debug "[PROD] removeSubscription, params: ${params}, request: ${data}, device: ${device}"
if (device) {
log.debug "Removing $device.displayName subscription"
state.remove(device.id)
unsubscribe(device)
}
log.info state
}
private deviceItem(it) {
log.debug "[deviceItem] ${it}"
it ? [id: it.id, label: it.displayName] : null
}
private deviceState(device, s) {
log.debug "[deviceState] ${device}: ${s}"
device && s ? [id: device.id, label: device.displayName, name: s.name, value: s.value, unixTime: s.date.time] : null
}
private attributeFor(type) {
log.debug "[attributeFor] ${type}"
switch (type) {
case "switches":
log.debug "[PROD] switch type"
return "switch"
case "locks":
log.debug "[PROD] lock type"
return "lock"
case "alarms":
log.debug "[PROD] alarm type"
return "alarm"
case "lightSensors":
log.debug "[PROD] illuminance type"
return "illuminance"
default:
log.debug "[PROD] other sensor type"
return "Sensors"
}
}

View File

@@ -689,7 +689,7 @@ def parse(childDevice, description) {
log.warn "Parsing Body failed - trying again..."
poll()
}
if (body instanceof java.util.Map) {
if (body instanceof java.util.HashMap) {
//poll response
def bulbs = getChildDevices()
for (bulb in body) {
@@ -830,22 +830,22 @@ def setColorTemperature(childDevice, huesettings) {
def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'"
def value = [:]
def hue = null
def sat = null
def xy = null
if (huesettings.hex != null) {
value.xy = getHextoXY(huesettings.hex)
} else {
if (huesettings.hue != null)
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
if (huesettings.saturation != null)
if (huesettings.saturation != null)
value.sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
}
// Default behavior is to turn light on
// Default behavior is to turn light on
value.on = true
if (huesettings.level != null) {
@@ -853,7 +853,7 @@ def setColor(childDevice, huesettings) {
value.on = false
else if (huesettings.level == 1)
value.bri = 1
else
else
value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
}
value.alert = huesettings.alert ? huesettings.alert : "none"

View File

@@ -823,8 +823,8 @@ def deviceHandler(evt) {
}
def sendToHarmony(evt, String callbackUrl) {
def callback = new URI(callbackUrl)
if (callback.port != -1) {
def callback = new URI(callbackUrl)
if(isIP(callback.host)){
def host = callback.port != -1 ? "${callback.host}:${callback.port}" : callback.host
def path = callback.query ? "${callback.path}?${callback.query}".toString() : callback.path
sendHubCommand(new physicalgraph.device.HubAction(
@@ -852,6 +852,25 @@ def sendToHarmony(evt, String callbackUrl) {
}
}
public static boolean isIP(String str) {
try {
String[] parts = str.split("\\.");
if (parts.length != 4) return false;
for (int i = 0; i < 4; ++i) {
int p
try {
p = Integer.parseInt(parts[i]);
} catch (Exception e) {
return false;
}
if (p > 255 || p < 0) return false;
}
return true;
} catch (Exception e) {
return false;
}
}
def listHubs() {
location.hubs?.findAll { it.type.toString() == "PHYSICAL" }?.collect { hubItem(it) }
}