mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 05:11:52 +00:00
Compare commits
31 Commits
MSA-1152-2
...
MSA-1181-3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffa95f668e | ||
|
|
d91fea89df | ||
|
|
d28d27c4ed | ||
|
|
e7c1d88285 | ||
|
|
74c334a0f7 | ||
|
|
6881f469f5 | ||
|
|
be8c84306a | ||
|
|
a6105188ea | ||
|
|
22f218c072 | ||
|
|
7c000dc61a | ||
|
|
8b543d399b | ||
|
|
bb21b9a612 | ||
|
|
e366a2686f | ||
|
|
820405a3ab | ||
|
|
69875becae | ||
|
|
5c90091e36 | ||
|
|
af19cee795 | ||
|
|
55ed08d5e7 | ||
|
|
2dec6f69c8 | ||
|
|
381fcfdd31 | ||
|
|
b23d7ccf2e | ||
|
|
fd29fe2b2a | ||
|
|
c259af4312 | ||
|
|
8a5f0af0e2 | ||
|
|
e2ab965e89 | ||
|
|
3824ccb5e1 | ||
|
|
1af43681a5 | ||
|
|
b211b298c0 | ||
|
|
23a76fa72b | ||
|
|
a46f09a84a | ||
|
|
aff8dec3ce |
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* Fibaro Door/Window Sensor ZW5
|
||||
*
|
||||
* Copyright 2016 Fibar Group S.A.
|
||||
*
|
||||
* 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: "Fibaro Door/Window Sensor ZW5 with Temperature", namespace: "fibargroup", author: "Fibar Group S.A.") {
|
||||
capability "Battery"
|
||||
capability "Contact Sensor"
|
||||
capability "Sensor"
|
||||
capability "Configuration"
|
||||
capability "Tamper Alert"
|
||||
|
||||
capability "Temperature Measurement"
|
||||
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x85, 0x59, 0x22, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x2B, 0x9C, 0x30, 0x31, 0x86", outClusters: ""
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x85, 0x59, 0x22, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x2B, 0x9C, 0x30, 0x31, 0x86, 0x84", outClusters: ""//actual NIF
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGK", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.contact", key:"PRIMARY_CONTROL") {
|
||||
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, , width: 2, height: 2, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
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"]
|
||||
]
|
||||
}
|
||||
|
||||
main "FGK"
|
||||
details(["FGK","battery", "temperature"])
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
log.debug "Parsing '${description}'"
|
||||
def result = []
|
||||
|
||||
if (description.startsWith("Err 106")) {
|
||||
if (state.sec) {
|
||||
result = createEvent(descriptionText:description, displayed:false)
|
||||
} else {
|
||||
result = createEvent(
|
||||
descriptionText: "FGK failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
|
||||
eventType: "ALERT",
|
||||
name: "secureInclusion",
|
||||
value: "failed",
|
||||
displayed: true,
|
||||
)
|
||||
}
|
||||
} else if (description == "updated") {
|
||||
return null
|
||||
} else {
|
||||
def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
|
||||
|
||||
if (cmd) {
|
||||
log.debug "Parsed '${cmd}'"
|
||||
zwaveEvent(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//security
|
||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x98: 1])
|
||||
if (encapsulatedCommand) {
|
||||
return zwaveEvent(encapsulatedCommand)
|
||||
} else {
|
||||
log.warn "Unable to extract encapsulated cmd from $cmd"
|
||||
createEvent(descriptionText: cmd.toString())
|
||||
}
|
||||
}
|
||||
|
||||
//crc16
|
||||
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
|
||||
{
|
||||
def versions = [0x31: 5, 0x72: 2, 0x80: 1, 0x86: 1]
|
||||
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) {
|
||||
log.debug "Could not extract command from $cmd"
|
||||
} else {
|
||||
zwaveEvent(encapsulatedCommand)
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
||||
//it is assumed that default notification events are used
|
||||
//(parameter 20 was not changed before device's re-inclusion)
|
||||
def map = [:]
|
||||
if (cmd.notificationType == 6) {
|
||||
switch (cmd.event) {
|
||||
case 22:
|
||||
map.name = "contact"
|
||||
map.value = "open"
|
||||
map.descriptionText = "${device.displayName}: is open"
|
||||
break
|
||||
|
||||
case 23:
|
||||
map.name = "contact"
|
||||
map.value = "closed"
|
||||
map.descriptionText = "${device.displayName}: is closed"
|
||||
break
|
||||
}
|
||||
} else if (cmd.notificationType == 7) {
|
||||
switch (cmd.event) {
|
||||
case 0:
|
||||
map.name = "tamper"
|
||||
map.value = "inactive"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
|
||||
break
|
||||
|
||||
case 3:
|
||||
map.name = "tamper"
|
||||
map.value = "active"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm activated"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [:]
|
||||
map.name = "battery"
|
||||
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
|
||||
map.unit = "%"
|
||||
map.displayed = true
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
|
||||
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
|
||||
def cmds = []
|
||||
cmds << encap(zwave.batteryV1.batteryGet())
|
||||
cmds << "delay 500"
|
||||
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
|
||||
cmds << "delay 1200"
|
||||
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
[event, response(cmds)]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
||||
log.debug "manufacturerId: ${cmd.manufacturerId}"
|
||||
log.debug "manufacturerName: ${cmd.manufacturerName}"
|
||||
log.debug "productId: ${cmd.productId}"
|
||||
log.debug "productTypeId: ${cmd.productTypeId}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
|
||||
log.debug "deviceIdData: ${cmd.deviceIdData}"
|
||||
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
|
||||
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
|
||||
log.debug "deviceIdType: ${cmd.deviceIdType}"
|
||||
|
||||
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
|
||||
String serialNumber = "h'"
|
||||
|
||||
cmd.deviceIdData.each{ data ->
|
||||
serialNumber += "${String.format("%02X", data)}"
|
||||
}
|
||||
|
||||
updateDataValue("serialNumber", serialNumber)
|
||||
log.debug "${device.displayName} - serial number: ${serialNumber}"
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
|
||||
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
|
||||
log.debug "applicationVersion: ${cmd.applicationVersion}"
|
||||
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
|
||||
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
|
||||
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
|
||||
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.sensorType == 1) {
|
||||
// temperature
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
|
||||
map.unit = getTemperatureScale()
|
||||
map.name = "temperature"
|
||||
map.displayed = true
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
|
||||
log.info "${device.displayName}: received command: $cmd - device has reset itself"
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Executing 'configure'"
|
||||
|
||||
def cmds = []
|
||||
|
||||
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGK's default wake up interval
|
||||
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
|
||||
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
|
||||
cmds += zwave.versionV1.versionGet()
|
||||
cmds += zwave.batteryV1.batteryGet()
|
||||
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)
|
||||
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
|
||||
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
|
||||
|
||||
encapSequence(cmds, 500)
|
||||
}
|
||||
|
||||
private secure(physicalgraph.zwave.Command cmd) {
|
||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
||||
}
|
||||
|
||||
private crc16(physicalgraph.zwave.Command cmd) {
|
||||
//zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
|
||||
"5601${cmd.format()}0000"
|
||||
}
|
||||
|
||||
private encapSequence(commands, delay=200) {
|
||||
delayBetween(commands.collect{ encap(it) }, delay)
|
||||
}
|
||||
|
||||
private encap(physicalgraph.zwave.Command cmd) {
|
||||
def secureClasses = [0x20, 0x2B, 0x30, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
|
||||
|
||||
//todo: check if secure inclusion was successful
|
||||
//if not do not send security-encapsulated command
|
||||
if (secureClasses.find{ it == cmd.commandClassId }) {
|
||||
secure(cmd)
|
||||
} else {
|
||||
crc16(cmd)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Fibaro Door/Window Sensor ZW5
|
||||
*
|
||||
* Copyright 2016 Fibar Group S.A.
|
||||
*
|
||||
* 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: "Fibaro Door/Window Sensor ZW5", namespace: "fibargroup", author: "Fibar Group S.A.") {
|
||||
capability "Battery"
|
||||
capability "Contact Sensor"
|
||||
capability "Sensor"
|
||||
capability "Configuration"
|
||||
capability "Tamper Alert"
|
||||
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x85, 0x59, 0x22, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x2B, 0x9C, 0x30, 0x86, 0x84", outClusters: ""
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGK", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.contact", key:"PRIMARY_CONTROL") {
|
||||
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, , width: 2, height: 2, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
main "FGK"
|
||||
details(["FGK","battery"])
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
log.debug "Parsing '${description}'"
|
||||
def result = []
|
||||
|
||||
if (description.startsWith("Err 106")) {
|
||||
if (state.sec) {
|
||||
result = createEvent(descriptionText:description, displayed:false)
|
||||
} else {
|
||||
result = createEvent(
|
||||
descriptionText: "FGK failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
|
||||
eventType: "ALERT",
|
||||
name: "secureInclusion",
|
||||
value: "failed",
|
||||
displayed: true,
|
||||
)
|
||||
}
|
||||
} else if (description == "updated") {
|
||||
return null
|
||||
} else {
|
||||
def cmd = zwave.parse(description, [0x56: 1, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
|
||||
|
||||
if (cmd) {
|
||||
log.debug "Parsed '${cmd}'"
|
||||
zwaveEvent(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//security
|
||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x98: 1])
|
||||
if (encapsulatedCommand) {
|
||||
return zwaveEvent(encapsulatedCommand)
|
||||
} else {
|
||||
log.warn "Unable to extract encapsulated cmd from $cmd"
|
||||
createEvent(descriptionText: cmd.toString())
|
||||
}
|
||||
}
|
||||
|
||||
//crc16
|
||||
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
|
||||
{
|
||||
def versions = [0x72: 2, 0x80: 1, 0x86: 1]
|
||||
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) {
|
||||
log.debug "Could not extract command from $cmd"
|
||||
} else {
|
||||
zwaveEvent(encapsulatedCommand)
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
||||
//it is assumed that default notification events are used
|
||||
//(parameter 20 was not changed before device's re-inclusion)
|
||||
def map = [:]
|
||||
if (cmd.notificationType == 6) {
|
||||
switch (cmd.event) {
|
||||
case 22:
|
||||
map.name = "contact"
|
||||
map.value = "open"
|
||||
map.descriptionText = "${device.displayName}: is open"
|
||||
break
|
||||
|
||||
case 23:
|
||||
map.name = "contact"
|
||||
map.value = "closed"
|
||||
map.descriptionText = "${device.displayName}: is closed"
|
||||
break
|
||||
}
|
||||
} else if (cmd.notificationType == 7) {
|
||||
switch (cmd.event) {
|
||||
case 0:
|
||||
map.name = "tamper"
|
||||
map.value = "inactive"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
|
||||
break
|
||||
|
||||
case 3:
|
||||
map.name = "tamper"
|
||||
map.value = "active"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm activated"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [:]
|
||||
map.name = "battery"
|
||||
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
|
||||
map.unit = "%"
|
||||
map.displayed = true
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
|
||||
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
|
||||
def cmds = []
|
||||
cmds << encap(zwave.batteryV1.batteryGet())
|
||||
cmds << "delay 1200"
|
||||
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
[event, response(cmds)]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
||||
log.debug "manufacturerId: ${cmd.manufacturerId}"
|
||||
log.debug "manufacturerName: ${cmd.manufacturerName}"
|
||||
log.debug "productId: ${cmd.productId}"
|
||||
log.debug "productTypeId: ${cmd.productTypeId}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
|
||||
log.debug "deviceIdData: ${cmd.deviceIdData}"
|
||||
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
|
||||
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
|
||||
log.debug "deviceIdType: ${cmd.deviceIdType}"
|
||||
|
||||
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
|
||||
String serialNumber = "h'"
|
||||
|
||||
cmd.deviceIdData.each{ data ->
|
||||
serialNumber += "${String.format("%02X", data)}"
|
||||
}
|
||||
|
||||
updateDataValue("serialNumber", serialNumber)
|
||||
log.debug "${device.displayName} - serial number: ${serialNumber}"
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
|
||||
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
|
||||
log.debug "applicationVersion: ${cmd.applicationVersion}"
|
||||
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
|
||||
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
|
||||
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
|
||||
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
|
||||
log.info "${device.displayName}: received command: $cmd - device has reset itself"
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Executing 'configure'"
|
||||
|
||||
def cmds = []
|
||||
|
||||
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGK's default wake up interval
|
||||
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
|
||||
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
|
||||
cmds += zwave.versionV1.versionGet()
|
||||
cmds += zwave.batteryV1.batteryGet()
|
||||
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
|
||||
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
|
||||
|
||||
encapSequence(cmds, 500)
|
||||
}
|
||||
|
||||
private secure(physicalgraph.zwave.Command cmd) {
|
||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
||||
}
|
||||
|
||||
private crc16(physicalgraph.zwave.Command cmd) {
|
||||
//zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
|
||||
"5601${cmd.format()}0000"
|
||||
}
|
||||
|
||||
private encapSequence(commands, delay=200) {
|
||||
delayBetween(commands.collect{ encap(it) }, delay)
|
||||
}
|
||||
|
||||
private encap(physicalgraph.zwave.Command cmd) {
|
||||
def secureClasses = [0x20, 0x2B, 0x30, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
|
||||
|
||||
//todo: check if secure inclusion was successful
|
||||
//if not do not send security-encapsulated command
|
||||
if (secureClasses.find{ it == cmd.commandClassId }) {
|
||||
secure(cmd)
|
||||
} else {
|
||||
crc16(cmd)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* Fibaro Flood Sensor ZW5
|
||||
*
|
||||
* Copyright 2016 Fibar Group S.A.
|
||||
*
|
||||
* 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: "Fibaro Flood Sensor ZW5", namespace: "fibargroup", author: "Fibar Group S.A.") {
|
||||
capability "Battery"
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Tamper Alert"
|
||||
capability "Temperature Measurement"
|
||||
capability "Water Sensor"
|
||||
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x9C, 0x31, 0x86", outClusters: ""
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGFS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.water", key:"PRIMARY_CONTROL") {
|
||||
attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#79b821")
|
||||
attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#ffa81e")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
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", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
main "FGFS"
|
||||
details(["FGFS","battery", "temperature"])
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
log.debug "Parsing '${description}'"
|
||||
def result = []
|
||||
|
||||
if (description.startsWith("Err 106")) {
|
||||
if (state.sec) {
|
||||
result = createEvent(descriptionText:description, displayed:false)
|
||||
} else {
|
||||
result = createEvent(
|
||||
descriptionText: "FGFS failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
|
||||
eventType: "ALERT",
|
||||
name: "secureInclusion",
|
||||
value: "failed",
|
||||
displayed: true,
|
||||
)
|
||||
}
|
||||
} else if (description == "updated") {
|
||||
return null
|
||||
} else {
|
||||
def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72:2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
|
||||
|
||||
if (cmd) {
|
||||
log.debug "Parsed '${cmd}'"
|
||||
zwaveEvent(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//security
|
||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
|
||||
if (encapsulatedCommand) {
|
||||
return zwaveEvent(encapsulatedCommand)
|
||||
} else {
|
||||
log.warn "Unable to extract encapsulated cmd from $cmd"
|
||||
createEvent(descriptionText: cmd.toString())
|
||||
}
|
||||
}
|
||||
|
||||
//crc16
|
||||
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
|
||||
{
|
||||
def versions = [0x31: 5, 0x72: 2, 0x80: 1]
|
||||
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) {
|
||||
log.debug "Could not extract command from $cmd"
|
||||
} else {
|
||||
zwaveEvent(encapsulatedCommand)
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
|
||||
{
|
||||
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
|
||||
def cmds = []
|
||||
cmds << encap(zwave.batteryV1.batteryGet())
|
||||
cmds << "delay 500"
|
||||
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
|
||||
cmds << "delay 1200"
|
||||
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
[event, response(cmds)]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
||||
log.debug "manufacturerId: ${cmd.manufacturerId}"
|
||||
log.debug "manufacturerName: ${cmd.manufacturerName}"
|
||||
log.debug "productId: ${cmd.productId}"
|
||||
log.debug "productTypeId: ${cmd.productTypeId}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
|
||||
log.debug "deviceIdData: ${cmd.deviceIdData}"
|
||||
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
|
||||
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
|
||||
log.debug "deviceIdType: ${cmd.deviceIdType}"
|
||||
|
||||
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
|
||||
String serialNumber = "h'"
|
||||
|
||||
cmd.deviceIdData.each{ data ->
|
||||
serialNumber += "${String.format("%02X", data)}"
|
||||
}
|
||||
|
||||
updateDataValue("serialNumber", serialNumber)
|
||||
log.debug "${device.displayName} - serial number: ${serialNumber}"
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
|
||||
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
|
||||
log.debug "applicationVersion: ${cmd.applicationVersion}"
|
||||
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
|
||||
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
|
||||
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
|
||||
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [:]
|
||||
map.name = "battery"
|
||||
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
|
||||
map.unit = "%"
|
||||
map.displayed = true
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.notificationType == 5) {
|
||||
switch (cmd.event) {
|
||||
case 2:
|
||||
map.name = "water"
|
||||
map.value = "wet"
|
||||
map.descriptionText = "${device.displayName} is ${map.value}"
|
||||
break
|
||||
|
||||
case 0:
|
||||
map.name = "water"
|
||||
map.value = "dry"
|
||||
map.descriptionText = "${device.displayName} is ${map.value}"
|
||||
break
|
||||
}
|
||||
} else if (cmd.notificationType == 7) {
|
||||
switch (cmd.event) {
|
||||
case 0:
|
||||
map.name = "tamper"
|
||||
map.value = "inactive"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
|
||||
break
|
||||
|
||||
case 3:
|
||||
map.name = "tamper"
|
||||
map.value = "active"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm activated"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.sensorType == 1) {
|
||||
// temperature
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
|
||||
map.unit = getTemperatureScale()
|
||||
map.name = "temperature"
|
||||
map.displayed = true
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
|
||||
log.info "${device.displayName}: received command: $cmd - device has reset itself"
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Executing 'configure'"
|
||||
|
||||
def cmds = []
|
||||
|
||||
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGFS' default wake up interval
|
||||
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
|
||||
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
|
||||
cmds += zwave.versionV1.versionGet()
|
||||
cmds += zwave.batteryV1.batteryGet()
|
||||
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)
|
||||
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
|
||||
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
|
||||
|
||||
encapSequence(cmds, 500)
|
||||
}
|
||||
|
||||
private secure(physicalgraph.zwave.Command cmd) {
|
||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
||||
}
|
||||
|
||||
private crc16(physicalgraph.zwave.Command cmd) {
|
||||
//zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
|
||||
"5601${cmd.format()}0000"
|
||||
}
|
||||
|
||||
private encapSequence(commands, delay=200) {
|
||||
delayBetween(commands.collect{ encap(it) }, delay)
|
||||
}
|
||||
|
||||
private encap(physicalgraph.zwave.Command cmd) {
|
||||
def secureClasses = [0x20, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
|
||||
|
||||
//todo: check if secure inclusion was successful
|
||||
//if not do not send security-encapsulated command
|
||||
if (secureClasses.find{ it == cmd.commandClassId }) {
|
||||
secure(cmd)
|
||||
} else {
|
||||
crc16(cmd)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* Fibaro Motion Sensor ZW5
|
||||
*
|
||||
* Copyright 2016 Fibar Group S.A.
|
||||
*
|
||||
* 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: "Fibaro Motion Sensor ZW5", namespace: "fibargroup", author: "Fibar Group S.A.") {
|
||||
capability "Battery"
|
||||
capability "Configuration"
|
||||
capability "Illuminance Measurement"
|
||||
capability "Motion Sensor"
|
||||
capability "Sensor"
|
||||
capability "Tamper Alert"
|
||||
capability "Temperature Measurement"
|
||||
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x20, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x30, 0x9C, 0x98, 0x7A", outClusters: ""
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGMS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.motion", key:"PRIMARY_CONTROL") {
|
||||
attributeState("inactive", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
|
||||
attributeState("active", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
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("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "luminosity", label:'${currentValue} ${unit}', unit:"lux"
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
main "FGMS"
|
||||
details(["FGMS","battery","temperature","illuminance"])
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
log.debug "Parsing '${description}'"
|
||||
def result = []
|
||||
|
||||
if (description.startsWith("Err 106")) {
|
||||
if (state.sec) {
|
||||
result = createEvent(descriptionText:description, displayed:false)
|
||||
} else {
|
||||
result = createEvent(
|
||||
descriptionText: "FGK failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
|
||||
eventType: "ALERT",
|
||||
name: "secureInclusion",
|
||||
value: "failed",
|
||||
displayed: true,
|
||||
)
|
||||
}
|
||||
} else if (description == "updated") {
|
||||
return null
|
||||
} else {
|
||||
def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
|
||||
|
||||
if (cmd) {
|
||||
log.debug "Parsed '${cmd}'"
|
||||
zwaveEvent(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//security
|
||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
|
||||
if (encapsulatedCommand) {
|
||||
return zwaveEvent(encapsulatedCommand)
|
||||
} else {
|
||||
log.warn "Unable to extract encapsulated cmd from $cmd"
|
||||
createEvent(descriptionText: cmd.toString())
|
||||
}
|
||||
}
|
||||
|
||||
//crc16
|
||||
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
|
||||
{
|
||||
def versions = [0x31: 5, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1]
|
||||
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) {
|
||||
log.debug "Could not extract command from $cmd"
|
||||
} else {
|
||||
zwaveEvent(encapsulatedCommand)
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
|
||||
def map = [ displayed: true ]
|
||||
switch (cmd.sensorType) {
|
||||
case 1:
|
||||
map.name = "temperature"
|
||||
map.unit = cmd.scale == 1 ? "F" : "C"
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, map.unit, cmd.precision)
|
||||
break
|
||||
case 3:
|
||||
map.name = "illuminance"
|
||||
map.value = cmd.scaledSensorValue.toInteger().toString()
|
||||
map.unit = "lux"
|
||||
break
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.notificationType == 7) {
|
||||
switch (cmd.event) {
|
||||
case 0:
|
||||
if (cmd.eventParameter[0] == 3) {
|
||||
map.name = "tamper"
|
||||
map.value = "inactive"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
|
||||
}
|
||||
if (cmd.eventParameter[0] == 8) {
|
||||
map.name = "motion"
|
||||
map.value = "inactive"
|
||||
map.descriptionText = "${device.displayName}: motion has stopped"
|
||||
}
|
||||
break
|
||||
|
||||
case 3:
|
||||
map.name = "tamper"
|
||||
map.value = "active"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm activated"
|
||||
break
|
||||
|
||||
case 8:
|
||||
map.name = "motion"
|
||||
map.value = "active"
|
||||
map.descriptionText = "${device.displayName}: motion detected"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [:]
|
||||
map.name = "battery"
|
||||
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
|
||||
map.unit = "%"
|
||||
map.displayed = true
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
|
||||
{
|
||||
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
|
||||
def cmds = []
|
||||
cmds << encap(zwave.batteryV1.batteryGet())
|
||||
cmds << "delay 500"
|
||||
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
|
||||
cmds << "delay 500"
|
||||
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 3, scale: 1))
|
||||
cmds << "delay 1200"
|
||||
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
[event, response(cmds)]
|
||||
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
||||
log.debug "manufacturerId: ${cmd.manufacturerId}"
|
||||
log.debug "manufacturerName: ${cmd.manufacturerName}"
|
||||
log.debug "productId: ${cmd.productId}"
|
||||
log.debug "productTypeId: ${cmd.productTypeId}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
|
||||
log.debug "deviceIdData: ${cmd.deviceIdData}"
|
||||
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
|
||||
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
|
||||
log.debug "deviceIdType: ${cmd.deviceIdType}"
|
||||
|
||||
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
|
||||
String serialNumber = "h'"
|
||||
|
||||
cmd.deviceIdData.each{ data ->
|
||||
serialNumber += "${String.format("%02X", data)}"
|
||||
}
|
||||
|
||||
updateDataValue("serialNumber", serialNumber)
|
||||
log.debug "${device.displayName} - serial number: ${serialNumber}"
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
|
||||
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
|
||||
log.debug "applicationVersion: ${cmd.applicationVersion}"
|
||||
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
|
||||
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
|
||||
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
|
||||
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
|
||||
log.info "${device.displayName}: received command: $cmd - device has reset itself"
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Executing 'configure'"
|
||||
|
||||
def cmds = []
|
||||
|
||||
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds: 7200, nodeid: zwaveHubNodeId)//FGMS' default wake up interval
|
||||
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
|
||||
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
|
||||
cmds += zwave.versionV1.versionGet()
|
||||
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId])
|
||||
cmds += zwave.batteryV1.batteryGet()
|
||||
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)
|
||||
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 3, scale: 1)
|
||||
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
|
||||
|
||||
encapSequence(cmds, 500)
|
||||
}
|
||||
|
||||
private secure(physicalgraph.zwave.Command cmd) {
|
||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
||||
}
|
||||
|
||||
private crc16(physicalgraph.zwave.Command cmd) {
|
||||
//zwave.crc16encapV1.crc16Encap().encapsulate(cmd).format()
|
||||
"5601${cmd.format()}0000"
|
||||
}
|
||||
|
||||
private encapSequence(commands, delay=200) {
|
||||
delayBetween(commands.collect{ encap(it) }, delay)
|
||||
}
|
||||
|
||||
private encap(physicalgraph.zwave.Command cmd) {
|
||||
def secureClasses = [0x20, 0x30, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
|
||||
|
||||
//todo: check if secure inclusion was successful
|
||||
//if not do not send security-encapsulated command
|
||||
if (secureClasses.find{ it == cmd.commandClassId }) {
|
||||
secure(cmd)
|
||||
} else {
|
||||
crc16(cmd)
|
||||
}
|
||||
}
|
||||
@@ -23,14 +23,14 @@
|
||||
#==============================================================================
|
||||
# Korean (ko)
|
||||
# Device Preferences
|
||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||
'''Give your device a name'''.ko=기기 이름 설정
|
||||
'''Set Device Image'''.ko=기기 이미지 설정
|
||||
'''Presence timeout (minutes)'''.ko=시간 초과. 스마트폰 위치 정보
|
||||
'''Presence timeout (minutes)'''.ko=알람 유예 시간 설정 (분)
|
||||
'''Tap to set'''.ko=눌러서 설정
|
||||
'''Arrival Sensor'''.ko=도착알림 센서
|
||||
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
|
||||
# Events / Notifications
|
||||
'''{{ linkText }} battery was {{ value }}'''.ko={{ linkText }}남아있는 배터리는 {{ value }}입니다.
|
||||
'''{{ linkText }} has arrived'''.ko={{ linkText }}집에 도착했습니다.
|
||||
'''{{ linkText }} has left'''.ko={{ linkText }}집을 나갔습니다.
|
||||
'''{{ linkText }} battery was {{ value }}'''.ko={{ linkText }}의 남은 배터리 {{ value }}
|
||||
'''{{ linkText }} has arrived'''.ko={{ linkText }} 귀가
|
||||
'''{{ linkText }} has left'''.ko={{ linkText }} 외출
|
||||
#==============================================================================
|
||||
|
||||
@@ -30,10 +30,10 @@ metadata {
|
||||
tiles (scale: 2){
|
||||
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
#==============================================================================
|
||||
# Korean (ko)
|
||||
# Device Preferences
|
||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||
'''Give your device a name'''.ko=기기 이름 설정
|
||||
'''Set Device Image'''.ko=기기 이미지 설정
|
||||
# Events / Notifications
|
||||
'''{{ linkText }} has left'''.ko={{ linkText }}집을 나갔습니다.
|
||||
'''{{ linkText }} has arrived'''.ko={{ linkText }}집에 도착했습니다.
|
||||
'''{{ linkText }} has left'''.ko={{ linkText }} 외출
|
||||
'''{{ linkText }} has arrived'''.ko={{ linkText }} 귀가
|
||||
'''present'''.ko=집안
|
||||
'''not present'''.ko=외출
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
#==============================================================================
|
||||
# Korean (ko)
|
||||
# Device Preferences
|
||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||
'''Outlet'''.ko=플러그
|
||||
'''Give your device a name'''.ko=기기 이름 설정
|
||||
'''Outlet'''.ko= 스마트 플러그
|
||||
# Events descriptionText
|
||||
'''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다.
|
||||
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다.
|
||||
'''{{ device.displayName }} power is {{ value }} Watts'''.ko={{ device.displayName }} 전원은 {{ value }}와트입니다
|
||||
'''On'''.ko=켜짐
|
||||
'''{{ device.displayName }} is On'''.ko={{ device.displayName }} 켜짐
|
||||
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }} 꺼짐
|
||||
'''{{ device.displayName }} power is {{ value }} Watts'''.ko={{ device.displayName }} 전력은 {{ value }}와트입니다.
|
||||
'''On'''.ko= 켜짐
|
||||
'''Off'''.ko=꺼짐
|
||||
'''Turning On'''.ko=켜기
|
||||
'''Turning Off'''.ko=끄기
|
||||
'''Turning On'''.ko=켜는 중
|
||||
'''Turning Off'''.ko=끄는 중
|
||||
#==============================================================================
|
||||
|
||||
@@ -31,14 +31,14 @@
|
||||
'''This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.'''.ko=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
|
||||
'''Degrees'''.ko=온도
|
||||
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
|
||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||
'''Water Leak Sensor'''.ko=누수센서
|
||||
'''Give your device a name'''.ko=기기 이름 설정
|
||||
'''Water Leak Sensor'''.ko=누수감지 센서
|
||||
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
|
||||
# Events descriptionText
|
||||
'''{{ device.displayName }} is dry'''.ko={{ device.displayName }}가 건조
|
||||
'''{{ device.displayName }} is wet'''.ko={{ device.displayName }}누수
|
||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
|
||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}에서 {{ value }}°C 감지
|
||||
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
||||
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
|
||||
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
|
||||
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}의 남은 배터리 {{ value }}%
|
||||
#==============================================================================
|
||||
|
||||
@@ -28,16 +28,16 @@
|
||||
'''This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.'''.ko=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
|
||||
'''Degrees'''.ko=온도
|
||||
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
|
||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||
'''Give your device a name'''.ko=기기 이름 설정
|
||||
'''Motion Sensor'''.ko=모션 센서
|
||||
'''motion'''.ko=동작 감지
|
||||
'''motion'''.ko= 동작 감지
|
||||
'''no motion'''.ko=동작 없음
|
||||
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
|
||||
# Events descriptionText
|
||||
'''{{ device.displayName }} detected motion'''.ko={{ device.displayName }} 가 움직임을 감지하였습니다.
|
||||
'''{{ device.displayName }} motion has stopped'''.ko={{ device.displayName }}움직임이 중단되었습니다
|
||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
|
||||
'''{{ device.displayName }} detected motion'''.ko={{ device.displayName }}에서 움직임이 감지되었습니다.
|
||||
'''{{ device.displayName }} motion has stopped'''.ko={{ device.displayName }}에서 움직임이 중단되었습니다.
|
||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}에서 {{ value }}°C 감지
|
||||
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
||||
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
|
||||
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
|
||||
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}의 남은 배터리 {{ value }}%
|
||||
#==============================================================================
|
||||
|
||||
@@ -31,20 +31,20 @@
|
||||
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
|
||||
'''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기
|
||||
'''Tap to set'''.ko=눌러서 설정
|
||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||
'''Multipurpose Sensor'''.ko=멀티 센서
|
||||
'''Give your device a name'''.ko=기기 이름 설정
|
||||
'''Multipurpose Sensor'''.ko=문 및 창 센서
|
||||
# Events descriptionText
|
||||
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}열림을 감지하였습니다.
|
||||
'''{{ device.displayName }} was closed'''.ko={{ device.displayName }}닫혔습니다.
|
||||
'''{{ device.displayName }} was active'''.ko={{ device.displayName }}활성화되었습니다.
|
||||
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }}비활성화되었습니다.
|
||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
|
||||
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}에서 열림이 감지되었습니다.
|
||||
'''{{ device.displayName }} was closed'''.ko={{ device.displayName }}에서 닫힘이 감지되었습니다.
|
||||
'''{{ device.displayName }} was active'''.ko={{ device.displayName }} 활성화
|
||||
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }} 비활성화
|
||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}에서 {{ value }}°C 감지
|
||||
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
||||
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
|
||||
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}의 남은 배터리 {{ value }}%
|
||||
'''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중
|
||||
'''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중
|
||||
'''Inactive'''.ko=비활성
|
||||
'''Active'''.ko=활성
|
||||
'''Open'''.ko=열림
|
||||
'''Inactive'''.ko=비활성 상태
|
||||
'''Active'''.ko=활성 상태
|
||||
'''Open'''.ko= 열림이 감지될 때
|
||||
'''Closed'''.ko=닫힘
|
||||
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "carouselDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Thermostat"
|
||||
capability "Relative Humidity Measurement"
|
||||
|
||||
command "tempUp"
|
||||
command "tempDown"
|
||||
command "heatUp"
|
||||
command "heatDown"
|
||||
command "coolUp"
|
||||
command "coolDown"
|
||||
command "setTemperature", ["number"]
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"thermostatMulti", type:"thermostat", width:6, height:4) {
|
||||
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
|
||||
attributeState("default", action: "setTemperature")
|
||||
}
|
||||
tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
|
||||
attributeState("default", label:'${currentValue}%', unit:"%")
|
||||
}
|
||||
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
|
||||
attributeState("idle", backgroundColor:"#44b621")
|
||||
attributeState("heating", backgroundColor:"#ffa81e")
|
||||
attributeState("cooling", backgroundColor:"#269bd2")
|
||||
}
|
||||
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
|
||||
attributeState("off", label:'${name}')
|
||||
attributeState("heat", label:'${name}')
|
||||
attributeState("cool", label:'${name}')
|
||||
attributeState("auto", label:'${name}')
|
||||
}
|
||||
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
}
|
||||
|
||||
main("thermostatMulti")
|
||||
details([
|
||||
"thermostatMulti"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
sendEvent(name: "temperature", value: 72, unit: "F")
|
||||
sendEvent(name: "heatingSetpoint", value: 70, unit: "F")
|
||||
sendEvent(name: "thermostatSetpoint", value: 70, unit: "F")
|
||||
sendEvent(name: "coolingSetpoint", value: 76, unit: "F")
|
||||
sendEvent(name: "thermostatMode", value: "off")
|
||||
sendEvent(name: "thermostatFanMode", value: "fanAuto")
|
||||
sendEvent(name: "thermostatOperatingState", value: "idle")
|
||||
sendEvent(name: "humidity", value: 53, unit: "%")
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def evaluate(temp, heatingSetpoint, coolingSetpoint) {
|
||||
log.debug "evaluate($temp, $heatingSetpoint, $coolingSetpoint"
|
||||
def threshold = 1.0
|
||||
def current = device.currentValue("thermostatOperatingState")
|
||||
def mode = device.currentValue("thermostatMode")
|
||||
|
||||
def heating = false
|
||||
def cooling = false
|
||||
def idle = false
|
||||
if (mode in ["heat","emergency heat","auto"]) {
|
||||
if (heatingSetpoint - temp >= threshold) {
|
||||
heating = true
|
||||
sendEvent(name: "thermostatOperatingState", value: "heating")
|
||||
}
|
||||
else if (temp - heatingSetpoint >= threshold) {
|
||||
idle = true
|
||||
}
|
||||
sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
|
||||
}
|
||||
if (mode in ["cool","auto"]) {
|
||||
if (temp - coolingSetpoint >= threshold) {
|
||||
cooling = true
|
||||
sendEvent(name: "thermostatOperatingState", value: "cooling")
|
||||
}
|
||||
else if (coolingSetpoint - temp >= threshold && !heating) {
|
||||
idle = true
|
||||
}
|
||||
sendEvent(name: "thermostatSetpoint", value: coolingSetpoint)
|
||||
}
|
||||
else {
|
||||
sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
|
||||
}
|
||||
if (idle && !heating && !cooling) {
|
||||
sendEvent(name: "thermostatOperatingState", value: "idle")
|
||||
}
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(Double degreesF) {
|
||||
log.debug "setHeatingSetpoint($degreesF)"
|
||||
sendEvent(name: "heatingSetpoint", value: degreesF)
|
||||
evaluate(device.currentValue("temperature"), degreesF, device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(Double degreesF) {
|
||||
log.debug "setCoolingSetpoint($degreesF)"
|
||||
sendEvent(name: "coolingSetpoint", value: degreesF)
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), degreesF)
|
||||
}
|
||||
|
||||
def setThermostatMode(String value) {
|
||||
sendEvent(name: "thermostatMode", value: value)
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def setThermostatFanMode(String value) {
|
||||
sendEvent(name: "thermostatFanMode", value: value)
|
||||
}
|
||||
|
||||
def off() {
|
||||
sendEvent(name: "thermostatMode", value: "off")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def heat() {
|
||||
sendEvent(name: "thermostatMode", value: "heat")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def auto() {
|
||||
sendEvent(name: "thermostatMode", value: "auto")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def emergencyHeat() {
|
||||
sendEvent(name: "thermostatMode", value: "emergency heat")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def cool() {
|
||||
sendEvent(name: "thermostatMode", value: "cool")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def fanOn() {
|
||||
sendEvent(name: "thermostatFanMode", value: "fanOn")
|
||||
}
|
||||
|
||||
def fanAuto() {
|
||||
sendEvent(name: "thermostatFanMode", value: "fanAuto")
|
||||
}
|
||||
|
||||
def fanCirculate() {
|
||||
sendEvent(name: "thermostatFanMode", value: "fanCirculate")
|
||||
}
|
||||
|
||||
def tempUp() {
|
||||
def ts = device.currentState("temperature")
|
||||
def value = ts ? ts.integerValue + 1 : 72
|
||||
sendEvent(name:"temperature", value: value)
|
||||
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def tempDown() {
|
||||
def ts = device.currentState("temperature")
|
||||
def value = ts ? ts.integerValue - 1 : 72
|
||||
sendEvent(name:"temperature", value: value)
|
||||
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def setTemperature(value) {
|
||||
def ts = device.currentState("temperature")
|
||||
sendEvent(name:"temperature", value: value)
|
||||
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def heatUp() {
|
||||
def ts = device.currentState("heatingSetpoint")
|
||||
def value = ts ? ts.integerValue + 1 : 68
|
||||
sendEvent(name:"heatingSetpoint", value: value)
|
||||
evaluate(device.currentValue("temperature"), value, device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def heatDown() {
|
||||
def ts = device.currentState("heatingSetpoint")
|
||||
def value = ts ? ts.integerValue - 1 : 68
|
||||
sendEvent(name:"heatingSetpoint", value: value)
|
||||
evaluate(device.currentValue("temperature"), value, device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
|
||||
def coolUp() {
|
||||
def ts = device.currentState("coolingSetpoint")
|
||||
def value = ts ? ts.integerValue + 1 : 76
|
||||
sendEvent(name:"coolingSetpoint", value: value)
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), value)
|
||||
}
|
||||
|
||||
def coolDown() {
|
||||
def ts = device.currentState("coolingSetpoint")
|
||||
def value = ts ? ts.integerValue - 1 : 76
|
||||
sendEvent(name:"coolingSetpoint", value: value)
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), value)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "colorWheelDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Color Control"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
valueTile("currentColor", "device.color") {
|
||||
state "default", label: '${currentValue}'
|
||||
}
|
||||
|
||||
controlTile("rgbSelector", "device.color", "color", height: 6, width: 6, inactiveLabel: false) {
|
||||
state "color", action: "color control.setColor"
|
||||
}
|
||||
|
||||
main("currentColor")
|
||||
details([
|
||||
"rgbSelector"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
log.debug "Parsing '${description}'"
|
||||
}
|
||||
|
||||
def setSaturation(percent) {
|
||||
log.debug "Executing 'setSaturation'"
|
||||
sendEvent(name: "saturation", value: percent)
|
||||
}
|
||||
|
||||
def setHue(percent) {
|
||||
log.debug "Executing 'setHue'"
|
||||
sendEvent(name: "hue", value: percent)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "presenceDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Presence Sensor"
|
||||
|
||||
command "arrived"
|
||||
command "departed"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
// You only get a presence tile view when the size is 3x3 otherwise it's a value tile
|
||||
standardTile("presence", "device.presence", width: 3, height: 3, canChangeBackground: true) {
|
||||
state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#53a7c0")
|
||||
state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ebeef2")
|
||||
}
|
||||
|
||||
standardTile("notPresentBtn", "device.fake", width: 3, height: 3, decoration: "flat") {
|
||||
state("not present", label:'not present', backgroundColor:"#ffffff", action:"departed")
|
||||
}
|
||||
|
||||
standardTile("presentBtn", "device.fake", width: 3, height: 3, decoration: "flat") {
|
||||
state("present", label:'present', backgroundColor:"#53a7c0", action:"arrived")
|
||||
}
|
||||
|
||||
main("presence")
|
||||
details([
|
||||
"presence", "presenceControl", "notPresentBtn", "presentBtn"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
sendEvent(name: "presence", value: "present")
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def arrived() {
|
||||
log.trace "Executing 'arrived'"
|
||||
sendEvent(name: "presence", value: "present")
|
||||
}
|
||||
|
||||
def departed() {
|
||||
log.trace "Executing 'arrived'"
|
||||
sendEvent(name: "presence", value: "not present")
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "sliderDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Switch Level"
|
||||
command "setRangedLevel", ["number"]
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
controlTile("tinySlider", "device.level", "slider", height: 2, width: 2, inactiveLabel: false) {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
|
||||
controlTile("mediumSlider", "device.level", "slider", height: 2, width: 4, inactiveLabel: false) {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
|
||||
controlTile("largeSlider", "device.level", "slider", decoration: "flat", height: 2, width: 6, inactiveLabel: false) {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
|
||||
controlTile("rangeSlider", "device.rangedLevel", "slider", height: 2, width: 4, range: "(20..80)") {
|
||||
state "level", action:"setRangedLevel"
|
||||
}
|
||||
|
||||
valueTile("rangeValue", "device.rangedLevel", height: 2, width: 2) {
|
||||
state "default", label:'${currentValue}'
|
||||
}
|
||||
|
||||
controlTile("rangeSliderConstrained", "device.rangedLevel", "slider", height: 2, width: 4, range: "(40..60)") {
|
||||
state "level", action:"setRangedLevel"
|
||||
}
|
||||
|
||||
main("rangeValue")
|
||||
details([
|
||||
"tinySlider", "mediumSlider",
|
||||
"largeSlider",
|
||||
"rangeSlider", "rangeValue",
|
||||
"rangeSliderConstrained"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
sendEvent(name: "level", value: 63)
|
||||
sendEvent(name: "rangedLevel", value: 47)
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
log.debug "setting level to $value"
|
||||
sendEvent(name:"level", value:value)
|
||||
}
|
||||
|
||||
def setRangedLevel(value) {
|
||||
log.debug "setting ranged level to $value"
|
||||
sendEvent(name:"rangedLevel", value:value)
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "standardDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Switch"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
// standard tile with actions
|
||||
standardTile("actionRings", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label: '${currentValue}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
state "on", label: '${currentValue}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
}
|
||||
|
||||
// standard flat tile with actions
|
||||
standardTile("actionFlat", "device.switch", width: 2, height: 2, canChangeIcon: true, decoration: "flat") {
|
||||
state "off", label: '${currentValue}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
state "on", label: '${currentValue}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
}
|
||||
|
||||
// standard flat tile without actions
|
||||
standardTile("noActionFlat", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label: '${currentValue}',icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
state "on", label: '${currentValue}', icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
}
|
||||
|
||||
// standard flat tile with only a label
|
||||
standardTile("flatLabel", "device.switch", width: 2, height: 2, decoration: "flat") {
|
||||
state "default", label: 'On Action', action: "switch.on", backgroundColor: "#ffffff"
|
||||
}
|
||||
|
||||
// standard flat tile with icon and label
|
||||
standardTile("flatIconLabel", "device.switch", width: 2, height: 2, decoration: "flat") {
|
||||
state "default", label: 'Off Action', action: "switch.off", icon:"st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
|
||||
// standard flat tile with only icon (Refreh text is IN the icon file)
|
||||
standardTile("flatIcon", "device.switch", width: 2, height: 2, decoration: "flat") {
|
||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
// standard with defaultState = true
|
||||
standardTile("flatDefaultState", "null", width: 2, height: 2, decoration: "flat") {
|
||||
state "off", label: 'Fail!', icon: "st.switches.switch.off"
|
||||
state "on", label: 'Pass!', icon: "st.switches.switch.on", defaultState: true
|
||||
}
|
||||
|
||||
// standard with implicit defaultState based on order (0 index is selected)
|
||||
standardTile("flatImplicitDefaultState1", "null", width: 2, height: 2, decoration: "flat") {
|
||||
state "on", label: 'Pass!', icon: "st.switches.switch.on"
|
||||
state "off", label: 'Fail!', icon: "st.switches.switch.off"
|
||||
}
|
||||
|
||||
// standard with implicit defaultState based on state.name == default
|
||||
standardTile("flatImplicitDefaultState2", "null", width: 2, height: 2, decoration: "flat") {
|
||||
state "off", label: 'Fail!', icon: "st.switches.switch.off"
|
||||
state "default", label: 'Pass!', icon: "st.switches.switch.on"
|
||||
}
|
||||
|
||||
// utility tiles to fill the spaces
|
||||
standardTile("empty2x2", "null", width: 2, height: 2, decoration: "flat") {
|
||||
state "default", label:''
|
||||
}
|
||||
standardTile("empty4x2", "null", width: 4, height: 2, decoration: "flat") {
|
||||
state "default", label:''
|
||||
}
|
||||
|
||||
main("standard1")
|
||||
details([
|
||||
"actionRings", "actionFlat", "noActionFlat",
|
||||
|
||||
"flatLabel", "flatIconLabel", "flatIcon",
|
||||
|
||||
"flatDefaultState", "flatImplicitDefaultState1", "flatImplicitDefaultState2",
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.debug "on()"
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "off()"
|
||||
sendEvent(name: "switch", value: "off")
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "valueDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Sensor"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
valueTile("text", "device.text", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}'
|
||||
}
|
||||
|
||||
valueTile("longText", "device.longText", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}'
|
||||
}
|
||||
|
||||
valueTile("integer", "device.integer", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}'
|
||||
}
|
||||
|
||||
valueTile("integerFloat", "device.integerFloat", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}'
|
||||
}
|
||||
|
||||
valueTile("pi", "device.pi", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}'
|
||||
}
|
||||
|
||||
valueTile("floatAsText", "device.floatAsText", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}'
|
||||
}
|
||||
|
||||
valueTile("bgColor", "device.integer", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}', backgroundColor: "#e86d13"
|
||||
}
|
||||
|
||||
valueTile("bgColorRange", "device.integer", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}', backgroundColors: [
|
||||
[value: 10, color: "#ff0000"],
|
||||
[value: 90, color: "#0000ff"]
|
||||
]
|
||||
}
|
||||
|
||||
valueTile("bgColorRangeSingleItem", "device.integer", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}', backgroundColors: [
|
||||
[value: 10, color: "#333333"]
|
||||
]
|
||||
}
|
||||
|
||||
valueTile("bgColorRangeConflict", "device.integer", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}', backgroundColors: [
|
||||
[value: 10, color: "#990000"],
|
||||
[value: 10, color: "#000099"]
|
||||
]
|
||||
}
|
||||
|
||||
valueTile("noValue", "device.nada", width: 2, height: 2) {
|
||||
state "default", label:'${currentValue}'
|
||||
}
|
||||
|
||||
main("text")
|
||||
details([
|
||||
"text", "longText", "integer",
|
||||
"integerFloat", "pi", "floatAsText",
|
||||
"bgColor", "bgColorRange", "bgColorRangeSingleItem",
|
||||
"bgColorRangeConflict", "noValue"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
sendEvent(name: "text", value: "Test")
|
||||
sendEvent(name: "longText", value: "The Longer The Text, The Better The Test")
|
||||
sendEvent(name: "integer", value: 47)
|
||||
sendEvent(name: "integerFloat", value: 47.0)
|
||||
sendEvent(name: "pi", value: 3.14159)
|
||||
sendEvent(name: "floatAsText", value: "3.14159")
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "genericDeviceTile", namespace: "smartthings/tile-ux", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
|
||||
command "levelUp"
|
||||
command "levelDown"
|
||||
command "randomizeLevel"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"basicTile", type:"generic", width:6, height:4) {
|
||||
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", 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:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
}
|
||||
multiAttributeTile(name:"sliderTile", type:"generic", width:6, height:4) {
|
||||
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute("device.level", key: "SECONDARY_CONTROL") {
|
||||
attributeState "default", icon: 'st.Weather.weather1', action:"randomizeLevel"
|
||||
}
|
||||
tileAttribute("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "default", action:"switch level.setLevel"
|
||||
}
|
||||
}
|
||||
multiAttributeTile(name:"valueTile", type:"generic", width:6, height:4) {
|
||||
tileAttribute("device.level", key: "PRIMARY_CONTROL") {
|
||||
attributeState "default", label:'${currentValue}', backgroundColors:[
|
||||
[value: 0, color: "#ff0000"],
|
||||
[value: 20, color: "#ffff00"],
|
||||
[value: 40, color: "#00ff00"],
|
||||
[value: 60, color: "#00ffff"],
|
||||
[value: 80, color: "#0000ff"],
|
||||
[value: 100, color: "#ff00ff"]
|
||||
]
|
||||
}
|
||||
tileAttribute("device.switch", key: "SECONDARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'…', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'…', action:"switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute("device.level", key: "VALUE_CONTROL") {
|
||||
attributeState "VALUE_UP", action: "levelUp"
|
||||
attributeState "VALUE_DOWN", action: "levelDown"
|
||||
}
|
||||
}
|
||||
|
||||
main(["basicTile"])
|
||||
details(["basicTile", "sliderTile", "valueTile"])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
|
||||
}
|
||||
|
||||
def parse() {
|
||||
// This is a simulated device. No incoming data to parse.
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.debug "turningOn"
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "turningOff"
|
||||
sendEvent(name: "switch", value: "off")
|
||||
}
|
||||
|
||||
def setLevel(percent) {
|
||||
log.debug "setLevel: ${percent}, this"
|
||||
sendEvent(name: "level", value: percent)
|
||||
}
|
||||
|
||||
def randomizeLevel() {
|
||||
def level = Math.round(Math.random() * 100)
|
||||
setLevel(level)
|
||||
}
|
||||
|
||||
def levelUp() {
|
||||
def level = device.latestValue("level") as Integer ?: 0
|
||||
if (level < 100) {
|
||||
level = level + 1
|
||||
}
|
||||
setLevel(level)
|
||||
}
|
||||
|
||||
def levelDown() {
|
||||
def level = device.latestValue("level") as Integer ?: 0
|
||||
if (level > 0) {
|
||||
level = level - 1
|
||||
}
|
||||
setLevel(level)
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "lightingDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Switch Level"
|
||||
capability "Actuator"
|
||||
capability "Color Control"
|
||||
capability "Power Meter"
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
command "setAdjustedColor"
|
||||
command "reset"
|
||||
command "refresh"
|
||||
}
|
||||
|
||||
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.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.power", key: "SECONDARY_CONTROL") {
|
||||
attributeState "power", label:'Power level: ${currentValue}W', icon: "st.Appliances.appliances17"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
tileAttribute ("device.color", key: "COLOR_CONTROL") {
|
||||
attributeState "color", action:"setAdjustedColor"
|
||||
}
|
||||
}
|
||||
|
||||
multiAttributeTile(name:"switchNoPower", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
tileAttribute ("device.color", key: "COLOR_CONTROL") {
|
||||
attributeState "color", action:"setAdjustedColor"
|
||||
}
|
||||
}
|
||||
|
||||
multiAttributeTile(name:"switchNoSlider", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.power", key: "SECONDARY_CONTROL") {
|
||||
attributeState "power", label:'The power level is currently: ${currentValue}W', icon: "st.Appliances.appliances17"
|
||||
}
|
||||
tileAttribute ("device.color", key: "COLOR_CONTROL") {
|
||||
attributeState "color", action:"setAdjustedColor"
|
||||
}
|
||||
}
|
||||
|
||||
multiAttributeTile(name:"switchNoSliderOrColor", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.power", key: "SECONDARY_CONTROL") {
|
||||
attributeState "power", label:'The light is currently consuming this amount of power: ${currentValue}W', icon: "st.Appliances.appliances17"
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("color", "device.color", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "color", label: '${currentValue}'
|
||||
}
|
||||
|
||||
standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main(["switch"])
|
||||
details(["switch", "switchNoPower", "switchNoSlider", "switchNoSliderOrColor", "color", "refresh", "reset"])
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(description) {
|
||||
log.debug "parse() - $description"
|
||||
def results = []
|
||||
def map = description
|
||||
if (description instanceof String) {
|
||||
log.debug "Hue Bulb stringToMap - ${map}"
|
||||
map = stringToMap(description)
|
||||
}
|
||||
if (map?.name && map?.value) {
|
||||
results << createEvent(name: "${map?.name}", value: "${map?.value}")
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
// handle commands
|
||||
def on() {
|
||||
//log.trace parent.on(this)
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
|
||||
def off() {
|
||||
//log.trace parent.off(this)
|
||||
sendEvent(name: "switch", value: "off")
|
||||
}
|
||||
|
||||
def nextLevel() {
|
||||
def level = device.latestValue("level") as Integer ?: 0
|
||||
if (level <= 100) {
|
||||
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
|
||||
}
|
||||
else {
|
||||
level = 25
|
||||
}
|
||||
setLevel(level)
|
||||
}
|
||||
|
||||
def setLevel(percent) {
|
||||
log.debug "setLevel: ${percent}, this"
|
||||
sendEvent(name: "level", value: percent)
|
||||
def power = Math.round(percent / 1.175) * 0.1
|
||||
sendEvent(name: "power", value: power)
|
||||
}
|
||||
|
||||
def setSaturation(percent) {
|
||||
log.debug "setSaturation: ${percent}, $this"
|
||||
sendEvent(name: "saturation", value: percent)
|
||||
}
|
||||
|
||||
def setHue(percent) {
|
||||
log.debug "setHue: ${percent}, $this"
|
||||
sendEvent(name: "hue", value: percent)
|
||||
}
|
||||
|
||||
def setColor(value) {
|
||||
log.debug "setColor: ${value}, $this"
|
||||
if (value.hue) { sendEvent(name: "hue", value: value.hue)}
|
||||
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation)}
|
||||
if (value.hex) { sendEvent(name: "color", value: value.hex)}
|
||||
if (value.level) { sendEvent(name: "level", value: value.level)}
|
||||
if (value.switch) { sendEvent(name: "switch", value: value.switch)}
|
||||
}
|
||||
|
||||
def reset() {
|
||||
log.debug "Executing 'reset'"
|
||||
setAdjustedColor([level:100, hex:"#90C638", saturation:56, hue:23])
|
||||
//parent.poll()
|
||||
}
|
||||
|
||||
def setAdjustedColor(value) {
|
||||
if (value) {
|
||||
log.trace "setAdjustedColor: ${value}"
|
||||
def adjusted = value + [:]
|
||||
adjusted.hue = adjustOutgoingHue(value.hue)
|
||||
// Needed because color picker always sends 100
|
||||
adjusted.level = null
|
||||
setColor(adjusted)
|
||||
}
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
//parent.manualRefresh()
|
||||
}
|
||||
|
||||
def adjustOutgoingHue(percent) {
|
||||
def adjusted = percent
|
||||
if (percent > 31) {
|
||||
if (percent < 63.0) {
|
||||
adjusted = percent + (7 * (percent -30 ) / 32)
|
||||
}
|
||||
else if (percent < 73.0) {
|
||||
adjusted = 69 + (5 * (percent - 62) / 10)
|
||||
}
|
||||
else {
|
||||
adjusted = percent + (2 * (100 - percent) / 28)
|
||||
}
|
||||
}
|
||||
log.info "percent: $percent, adjusted: $adjusted"
|
||||
adjusted
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "mediaPlayerDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Music Player"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name: "mediaMulti", type:"mediaPlayer", width:6, height:4) {
|
||||
tileAttribute("device.status", key: "PRIMARY_CONTROL") {
|
||||
attributeState("paused", label:"Paused",)
|
||||
attributeState("playing", label:"Playing")
|
||||
attributeState("stopped", label:"Stopped")
|
||||
}
|
||||
tileAttribute("device.status", key: "MEDIA_STATUS") {
|
||||
attributeState("paused", label:"Paused", action:"music Player.play", nextState: "playing")
|
||||
attributeState("playing", label:"Playing", action:"music Player.pause", nextState: "paused")
|
||||
attributeState("stopped", label:"Stopped", action:"music Player.play", nextState: "playing")
|
||||
}
|
||||
tileAttribute("device.status", key: "PREVIOUS_TRACK") {
|
||||
attributeState("default", action:"music Player.previousTrack")
|
||||
}
|
||||
tileAttribute("device.status", key: "NEXT_TRACK") {
|
||||
attributeState("default", action:"music Player.nextTrack")
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState("level", action:"music Player.setLevel")
|
||||
}
|
||||
tileAttribute ("device.mute", key: "MEDIA_MUTED") {
|
||||
attributeState("unmuted", action:"music Player.mute", nextState: "muted")
|
||||
attributeState("muted", action:"music Player.unmute", nextState: "unmuted")
|
||||
}
|
||||
tileAttribute("device.trackDescription", key: "MARQUEE") {
|
||||
attributeState("default", label:"${currentValue}")
|
||||
}
|
||||
}
|
||||
|
||||
main "mediaMulti"
|
||||
details(["mediaMulti"])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
state.tracks = [
|
||||
"Gangnam Style (강남스타일)\nPSY\nPsy 6 (Six Rules), Part 1",
|
||||
"Careless Whisper\nWham!\nMake It Big",
|
||||
"Never Gonna Give You Up\nRick Astley\nWhenever You Need Somebody",
|
||||
"Shake It Off\nTaylor Swift\n1989",
|
||||
"Ironic\nAlanis Morissette\nJagged Little Pill",
|
||||
"Hotline Bling\nDrake\nHotline Bling - Single"
|
||||
]
|
||||
state.currentTrack = 0
|
||||
|
||||
sendEvent(name: "level", value: 72)
|
||||
sendEvent(name: "mute", value: "unmuted")
|
||||
sendEvent(name: "status", value: "stopped")
|
||||
}
|
||||
|
||||
def parse(description) {
|
||||
// No parsing will happen with this simulated device.
|
||||
}
|
||||
|
||||
def play() {
|
||||
sendEvent(name: "status", value: "playing")
|
||||
sendEvent(name: "trackDescription", value: state.tracks[state.currentTrack])
|
||||
}
|
||||
|
||||
def pause() {
|
||||
sendEvent(name: "status", value: "paused")
|
||||
sendEvent(name: "trackDescription", value: state.tracks[state.currentTrack])
|
||||
}
|
||||
|
||||
def stop() {
|
||||
sendEvent(name: "status", value: "stopped")
|
||||
}
|
||||
|
||||
def previousTrack() {
|
||||
state.currentTrack = state.currentTrack - 1
|
||||
if (state.currentTrack < 0)
|
||||
state.currentTrack = state.tracks.size()-1
|
||||
|
||||
sendEvent(name: "trackDescription", value: state.tracks[state.currentTrack])
|
||||
}
|
||||
|
||||
def nextTrack() {
|
||||
state.currentTrack = state.currentTrack + 1
|
||||
if (state.currentTrack == state.tracks.size())
|
||||
state.currentTrack = 0
|
||||
|
||||
sendEvent(name: "trackDescription", value: state.tracks[state.currentTrack])
|
||||
}
|
||||
|
||||
def mute() {
|
||||
sendEvent(name: "mute", value: "muted")
|
||||
}
|
||||
|
||||
def unmute() {
|
||||
sendEvent(name: "mute", value: "unmuted")
|
||||
}
|
||||
|
||||
def setLevel(level) {
|
||||
sendEvent(name: "level", value: level)
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "thermostatDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Thermostat"
|
||||
capability "Relative Humidity Measurement"
|
||||
|
||||
command "tempUp"
|
||||
command "tempDown"
|
||||
command "heatUp"
|
||||
command "heatDown"
|
||||
command "coolUp"
|
||||
command "coolDown"
|
||||
command "setTemperature", ["number"]
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"thermostatFull", type:"thermostat", width:6, height:4) {
|
||||
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
|
||||
attributeState("VALUE_UP", action: "tempUp")
|
||||
attributeState("VALUE_DOWN", action: "tempDown")
|
||||
}
|
||||
tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
|
||||
attributeState("default", label:'${currentValue}%', unit:"%")
|
||||
}
|
||||
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
|
||||
attributeState("idle", backgroundColor:"#44b621")
|
||||
attributeState("heating", backgroundColor:"#ffa81e")
|
||||
attributeState("cooling", backgroundColor:"#269bd2")
|
||||
}
|
||||
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
|
||||
attributeState("off", label:'${name}')
|
||||
attributeState("heat", label:'${name}')
|
||||
attributeState("cool", label:'${name}')
|
||||
attributeState("auto", label:'${name}')
|
||||
}
|
||||
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
}
|
||||
multiAttributeTile(name:"thermostatNoHumidity", type:"thermostat", width:6, height:4) {
|
||||
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
|
||||
attributeState("VALUE_UP", action: "tempUp")
|
||||
attributeState("VALUE_DOWN", action: "tempDown")
|
||||
}
|
||||
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
|
||||
attributeState("idle", backgroundColor:"#44b621")
|
||||
attributeState("heating", backgroundColor:"#ffa81e")
|
||||
attributeState("cooling", backgroundColor:"#269bd2")
|
||||
}
|
||||
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
|
||||
attributeState("off", label:'${name}')
|
||||
attributeState("heat", label:'${name}')
|
||||
attributeState("cool", label:'${name}')
|
||||
attributeState("auto", label:'${name}')
|
||||
}
|
||||
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
||||
}
|
||||
}
|
||||
multiAttributeTile(name:"thermostatBasic", type:"thermostat", width:6, height:4) {
|
||||
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
||||
attributeState("default", label:'${currentValue}', unit:"dF",
|
||||
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"]
|
||||
])
|
||||
}
|
||||
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
|
||||
attributeState("VALUE_UP", action: "tempUp")
|
||||
attributeState("VALUE_DOWN", action: "tempDown")
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||
state("temperature", label:'${currentValue}', unit:"dF",
|
||||
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"]
|
||||
]
|
||||
)
|
||||
}
|
||||
standardTile("tempDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'down', action:"tempDown"
|
||||
}
|
||||
standardTile("tempUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'up', action:"tempUp"
|
||||
}
|
||||
|
||||
valueTile("heatingSetpoint", "device.heatingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "heat", label:'${currentValue} heat', unit: "F", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("heatDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'down', action:"heatDown"
|
||||
}
|
||||
standardTile("heatUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'up', action:"heatUp"
|
||||
}
|
||||
|
||||
valueTile("coolingSetpoint", "device.coolingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "cool", label:'${currentValue} cool', unit:"F", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("coolDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'down', action:"coolDown"
|
||||
}
|
||||
standardTile("coolUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'up', action:"coolUp"
|
||||
}
|
||||
|
||||
standardTile("mode", "device.thermostatMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "off", label:'${name}', action:"thermostat.heat", backgroundColor:"#ffffff"
|
||||
state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#ffa81e"
|
||||
state "cool", label:'${name}', action:"thermostat.auto", backgroundColor:"#269bd2"
|
||||
state "auto", label:'${name}', action:"thermostat.off", backgroundColor:"#79b821"
|
||||
}
|
||||
standardTile("fanMode", "device.thermostatFanMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "fanAuto", label:'${name}', action:"thermostat.fanOn", backgroundColor:"#ffffff"
|
||||
state "fanOn", label:'${name}', action:"thermostat.fanCirculate", backgroundColor:"#ffffff"
|
||||
state "fanCirculate", label:'${name}', action:"thermostat.fanAuto", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("operatingState", "device.thermostatOperatingState", width: 2, height: 2) {
|
||||
state "idle", label:'${name}', backgroundColor:"#ffffff"
|
||||
state "heating", label:'${name}', backgroundColor:"#ffa81e"
|
||||
state "cooling", label:'${name}', backgroundColor:"#269bd2"
|
||||
}
|
||||
|
||||
|
||||
main("thermostatFull")
|
||||
details([
|
||||
"thermostatFull", "thermostatNoHumidity", "thermostatBasic",
|
||||
"temperature","tempDown","tempUp",
|
||||
"mode", "fanMode", "operatingState",
|
||||
"heatingSetpoint", "heatDown", "heatUp",
|
||||
"coolingSetpoint", "coolDown", "coolUp"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
sendEvent(name: "temperature", value: 72, unit: "F")
|
||||
sendEvent(name: "heatingSetpoint", value: 70, unit: "F")
|
||||
sendEvent(name: "thermostatSetpoint", value: 70, unit: "F")
|
||||
sendEvent(name: "coolingSetpoint", value: 76, unit: "F")
|
||||
sendEvent(name: "thermostatMode", value: "off")
|
||||
sendEvent(name: "thermostatFanMode", value: "fanAuto")
|
||||
sendEvent(name: "thermostatOperatingState", value: "idle")
|
||||
sendEvent(name: "humidity", value: 53, unit: "%")
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def evaluate(temp, heatingSetpoint, coolingSetpoint) {
|
||||
log.debug "evaluate($temp, $heatingSetpoint, $coolingSetpoint"
|
||||
def threshold = 1.0
|
||||
def current = device.currentValue("thermostatOperatingState")
|
||||
def mode = device.currentValue("thermostatMode")
|
||||
|
||||
def heating = false
|
||||
def cooling = false
|
||||
def idle = false
|
||||
if (mode in ["heat","emergency heat","auto"]) {
|
||||
if (heatingSetpoint - temp >= threshold) {
|
||||
heating = true
|
||||
sendEvent(name: "thermostatOperatingState", value: "heating")
|
||||
}
|
||||
else if (temp - heatingSetpoint >= threshold) {
|
||||
idle = true
|
||||
}
|
||||
sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
|
||||
}
|
||||
if (mode in ["cool","auto"]) {
|
||||
if (temp - coolingSetpoint >= threshold) {
|
||||
cooling = true
|
||||
sendEvent(name: "thermostatOperatingState", value: "cooling")
|
||||
}
|
||||
else if (coolingSetpoint - temp >= threshold && !heating) {
|
||||
idle = true
|
||||
}
|
||||
sendEvent(name: "thermostatSetpoint", value: coolingSetpoint)
|
||||
}
|
||||
else {
|
||||
sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
|
||||
}
|
||||
|
||||
if (mode == "off") {
|
||||
idle = true
|
||||
}
|
||||
|
||||
if (idle && !heating && !cooling) {
|
||||
sendEvent(name: "thermostatOperatingState", value: "idle")
|
||||
}
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(Double degreesF) {
|
||||
log.debug "setHeatingSetpoint($degreesF)"
|
||||
sendEvent(name: "heatingSetpoint", value: degreesF)
|
||||
evaluate(device.currentValue("temperature"), degreesF, device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(Double degreesF) {
|
||||
log.debug "setCoolingSetpoint($degreesF)"
|
||||
sendEvent(name: "coolingSetpoint", value: degreesF)
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), degreesF)
|
||||
}
|
||||
|
||||
def setThermostatMode(String value) {
|
||||
sendEvent(name: "thermostatMode", value: value)
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def setThermostatFanMode(String value) {
|
||||
sendEvent(name: "thermostatFanMode", value: value)
|
||||
}
|
||||
|
||||
def off() {
|
||||
sendEvent(name: "thermostatMode", value: "off")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def heat() {
|
||||
sendEvent(name: "thermostatMode", value: "heat")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def auto() {
|
||||
sendEvent(name: "thermostatMode", value: "auto")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def emergencyHeat() {
|
||||
sendEvent(name: "thermostatMode", value: "emergency heat")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def cool() {
|
||||
sendEvent(name: "thermostatMode", value: "cool")
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def fanOn() {
|
||||
sendEvent(name: "thermostatFanMode", value: "fanOn")
|
||||
}
|
||||
|
||||
def fanAuto() {
|
||||
sendEvent(name: "thermostatFanMode", value: "fanAuto")
|
||||
}
|
||||
|
||||
def fanCirculate() {
|
||||
sendEvent(name: "thermostatFanMode", value: "fanCirculate")
|
||||
}
|
||||
|
||||
def poll() {
|
||||
null
|
||||
}
|
||||
|
||||
def tempUp() {
|
||||
def ts = device.currentState("temperature")
|
||||
def value = ts ? ts.integerValue + 1 : 72
|
||||
sendEvent(name:"temperature", value: value)
|
||||
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def tempDown() {
|
||||
def ts = device.currentState("temperature")
|
||||
def value = ts ? ts.integerValue - 1 : 72
|
||||
sendEvent(name:"temperature", value: value)
|
||||
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def setTemperature(value) {
|
||||
def ts = device.currentState("temperature")
|
||||
sendEvent(name:"temperature", value: value)
|
||||
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def heatUp() {
|
||||
def ts = device.currentState("heatingSetpoint")
|
||||
def value = ts ? ts.integerValue + 1 : 68
|
||||
sendEvent(name:"heatingSetpoint", value: value)
|
||||
evaluate(device.currentValue("temperature"), value, device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
def heatDown() {
|
||||
def ts = device.currentState("heatingSetpoint")
|
||||
def value = ts ? ts.integerValue - 1 : 68
|
||||
sendEvent(name:"heatingSetpoint", value: value)
|
||||
evaluate(device.currentValue("temperature"), value, device.currentValue("coolingSetpoint"))
|
||||
}
|
||||
|
||||
|
||||
def coolUp() {
|
||||
def ts = device.currentState("coolingSetpoint")
|
||||
def value = ts ? ts.integerValue + 1 : 76
|
||||
sendEvent(name:"coolingSetpoint", value: value)
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), value)
|
||||
}
|
||||
|
||||
def coolDown() {
|
||||
def ts = device.currentState("coolingSetpoint")
|
||||
def value = ts ? ts.integerValue - 1 : 76
|
||||
sendEvent(name:"coolingSetpoint", value: value)
|
||||
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), value)
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings, Inc.
|
||||
*
|
||||
* 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: "videoPlayerDeviceTile",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings") {
|
||||
|
||||
capability "Configuration"
|
||||
capability "Video Camera"
|
||||
capability "Video Capture"
|
||||
capability "Refresh"
|
||||
capability "Switch"
|
||||
|
||||
// custom commands
|
||||
command "start"
|
||||
command "stop"
|
||||
command "setProfileHD"
|
||||
command "setProfileSDH"
|
||||
command "setProfileSDL"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name: "videoPlayer", type: "videoPlayer", width: 6, height: 4) {
|
||||
tileAttribute("device.switch", key: "CAMERA_STATUS") {
|
||||
attributeState("on", label: "Active", icon: "st.camera.dlink-indoor", action: "switch.off", backgroundColor: "#79b821", defaultState: true)
|
||||
attributeState("off", label: "Inactive", icon: "st.camera.dlink-indoor", action: "switch.on", backgroundColor: "#ffffff")
|
||||
attributeState("restarting", label: "Connecting", icon: "st.camera.dlink-indoor", backgroundColor: "#53a7c0")
|
||||
attributeState("unavailable", label: "Unavailable", icon: "st.camera.dlink-indoor", action: "refresh.refresh", backgroundColor: "#F22000")
|
||||
}
|
||||
|
||||
tileAttribute("device.errorMessage", key: "CAMERA_ERROR_MESSAGE") {
|
||||
attributeState("errorMessage", label: "", value: "", defaultState: true)
|
||||
}
|
||||
|
||||
tileAttribute("device.camera", key: "PRIMARY_CONTROL") {
|
||||
attributeState("on", label: "Active", icon: "st.camera.dlink-indoor", backgroundColor: "#79b821", defaultState: true)
|
||||
attributeState("off", label: "Inactive", icon: "st.camera.dlink-indoor", backgroundColor: "#ffffff")
|
||||
attributeState("restarting", label: "Connecting", icon: "st.camera.dlink-indoor", backgroundColor: "#53a7c0")
|
||||
attributeState("unavailable", label: "Unavailable", icon: "st.camera.dlink-indoor", backgroundColor: "#F22000")
|
||||
}
|
||||
|
||||
tileAttribute("device.startLive", key: "START_LIVE") {
|
||||
attributeState("live", action: "start", defaultState: true)
|
||||
}
|
||||
|
||||
tileAttribute("device.stream", key: "STREAM_URL") {
|
||||
attributeState("activeURL", defaultState: true)
|
||||
}
|
||||
|
||||
tileAttribute("device.profile", key: "STREAM_QUALITY") {
|
||||
attributeState("1", label: "720p", action: "setProfileHD", defaultState: true)
|
||||
attributeState("2", label: "h360p", action: "setProfileSDH", defaultState: true)
|
||||
attributeState("3", label: "l360p", action: "setProfileSDL", defaultState: true)
|
||||
}
|
||||
|
||||
tileAttribute("device.betaLogo", key: "BETA_LOGO") {
|
||||
attributeState("betaLogo", label: "", value: "", defaultState: true)
|
||||
}
|
||||
}
|
||||
|
||||
multiAttributeTile(name: "videoPlayerMin", type: "videoPlayer", width: 6, height: 4) {
|
||||
tileAttribute("device.switch", key: "CAMERA_STATUS") {
|
||||
attributeState("on", label: "Active", icon: "st.camera.dlink-indoor", action: "switch.off", backgroundColor: "#79b821", defaultState: true)
|
||||
attributeState("off", label: "Inactive", icon: "st.camera.dlink-indoor", action: "switch.on", backgroundColor: "#ffffff")
|
||||
attributeState("restarting", label: "Connecting", icon: "st.camera.dlink-indoor", backgroundColor: "#53a7c0")
|
||||
attributeState("unavailable", label: "Unavailable", icon: "st.camera.dlink-indoor", action: "refresh.refresh", backgroundColor: "#F22000")
|
||||
}
|
||||
|
||||
tileAttribute("device.errorMessage", key: "CAMERA_ERROR_MESSAGE") {
|
||||
attributeState("errorMessage", label: "", value: "", defaultState: true)
|
||||
}
|
||||
|
||||
tileAttribute("device.camera", key: "PRIMARY_CONTROL") {
|
||||
attributeState("on", label: "Active", icon: "st.camera.dlink-indoor", backgroundColor: "#79b821", defaultState: true)
|
||||
attributeState("off", label: "Inactive", icon: "st.camera.dlink-indoor", backgroundColor: "#ffffff")
|
||||
attributeState("restarting", label: "Connecting", icon: "st.camera.dlink-indoor", backgroundColor: "#53a7c0")
|
||||
attributeState("unavailable", label: "Unavailable", icon: "st.camera.dlink-indoor", backgroundColor: "#F22000")
|
||||
}
|
||||
|
||||
tileAttribute("device.startLive", key: "START_LIVE") {
|
||||
attributeState("live", action: "start", defaultState: true)
|
||||
}
|
||||
|
||||
tileAttribute("device.stream", key: "STREAM_URL") {
|
||||
attributeState("activeURL", defaultState: true)
|
||||
}
|
||||
}
|
||||
|
||||
main("videoPlayer")
|
||||
details([
|
||||
"videoPlayer", "videoPlayerMin"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.trace "refresh()"
|
||||
// no-op
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.trace "on()"
|
||||
// no-op
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.trace "off()"
|
||||
// no-op
|
||||
}
|
||||
|
||||
def setProfile(profile) {
|
||||
log.trace "setProfile(): ${profile}"
|
||||
sendEvent(name: "profile", value: profile, displayed: false)
|
||||
}
|
||||
|
||||
def setProfileHD() {
|
||||
setProfile(1)
|
||||
}
|
||||
|
||||
def setProfileSDH() {
|
||||
setProfile(2)
|
||||
}
|
||||
|
||||
def setProfileSDL() {
|
||||
setProfile(3)
|
||||
}
|
||||
|
||||
def start() {
|
||||
log.trace "start()"
|
||||
def dataLiveVideo = [
|
||||
OutHomeURL : "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8",
|
||||
InHomeURL : "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8",
|
||||
ThumbnailURL: "http://cdn.device-icons.smartthings.com/camera/dlink-indoor@2x.png",
|
||||
cookie : [key: "key", value: "value"]
|
||||
]
|
||||
|
||||
def event = [
|
||||
name : "stream",
|
||||
value : groovy.json.JsonOutput.toJson(dataLiveVideo).toString(),
|
||||
data : groovy.json.JsonOutput.toJson(dataLiveVideo),
|
||||
descriptionText: "Starting the livestream",
|
||||
eventType : "VIDEO",
|
||||
displayed : false,
|
||||
isStateChange : true
|
||||
]
|
||||
sendEvent(event)
|
||||
}
|
||||
|
||||
def stop() {
|
||||
log.trace "stop()"
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Iris Smart Fob", namespace: "mitchpond", author: "Mitch Pond") {
|
||||
definition (name: "ZigBee Button", namespace: "smartthings", author: "Mitch Pond") {
|
||||
capability "Battery"
|
||||
capability "Button"
|
||||
capability "Configuration"
|
||||
268
devicetypes/swarmx/swarmx2.src/swarmx2.groovy
Normal file
268
devicetypes/swarmx/swarmx2.src/swarmx2.groovy
Normal file
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* swarmx2
|
||||
*
|
||||
* Copyright 2016 Badrinarayanan Rangarajan
|
||||
*
|
||||
* 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: "swarmx2", namespace: "swarmx", author: "Badrinarayanan Rangarajan") {
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
capability "Image Capture"
|
||||
|
||||
command "setAuthToken"
|
||||
command "removeAuthToken"
|
||||
}
|
||||
|
||||
preferences {
|
||||
input("CameraIP", "string", title:"Camera IP Address", description: "Please enter your camera's IP Address", required: true, displayDuringSetup: true)
|
||||
input("CameraPort", "string", title:"Camera Port", description: "Please enter your camera's Port", defaultValue: 80 , required: true, displayDuringSetup: true)
|
||||
input("CameraPath", "string", title:"Camera Path to Image", description: "Please enter the path to the image (default: /cgi-bin/video.cgi?msubmenu=jpg&resolution=2)", defaultValue: "/cgi-bin/video.cgi?msubmenu=jpg&resolution=2", required: true, displayDuringSetup: true)
|
||||
input("CameraPostGet", "string", title:"Does Camera use a Post or Get, normally Get?", description: "Please choose if the camera uses a POST or a GET command to retreive the image", defaultValue: "GET", displayDuringSetup: true)
|
||||
input("CameraUser", "string", title:"Camera User", description: "Please enter your camera's username (default: admin)", defaultValue: "admin", required: false, displayDuringSetup: true)
|
||||
input("CameraPassword", "string", title:"Camera Password", description: "Please enter your camera's password", required: false, displayDuringSetup: true)
|
||||
}
|
||||
|
||||
simulator {
|
||||
}
|
||||
|
||||
tiles {
|
||||
standardTile("camera", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: true) {
|
||||
state "default", label: "", action: "", icon: "st.camera.dropcam-centered", backgroundColor: "#FFFFFF"
|
||||
}
|
||||
|
||||
carouselTile("cameraDetails", "device.image", width: 3, height: 2) { }
|
||||
|
||||
standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
|
||||
state "take", label: "Take", action: "Image Capture.take", icon: "st.camera.camera", backgroundColor: "#FFFFFF", nextState:"taking"
|
||||
state "taking", label:'Taking', action: "", icon: "st.camera.take-photo", backgroundColor: "#53a7c0"
|
||||
state "image", label: "Take", action: "Image Capture.take", icon: "st.camera.camera", backgroundColor: "#FFFFFF", nextState:"taking"
|
||||
}
|
||||
|
||||
standardTile("authenticate", "device.button", width: 1, height: 1, canChangeIcon: true) {
|
||||
state "DeAuth", label: '${name}', action: "removeAuthToken", icon: "st.switches.light.on", backgroundColor: "#79b821"
|
||||
state "Auth", label: '${name}', action: "setAuthToken", icon: "st.switches.light.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
|
||||
main "camera"
|
||||
details(["cameraDetails", "take", "error", "authenticate"])
|
||||
}
|
||||
}
|
||||
|
||||
// method to set digest token
|
||||
def setAuthToken() {
|
||||
trace("setAuth")
|
||||
state.auth = "empty"
|
||||
take()
|
||||
}
|
||||
|
||||
// method to set remove token (a.k.a. logout)
|
||||
def removeAuthToken() {
|
||||
trace("removeAuth")
|
||||
sendEvent(name: "authenticate", value: "Auth")
|
||||
state.auth = "empty"
|
||||
}
|
||||
|
||||
// method called after touching the take button
|
||||
def take() {
|
||||
def porthex = convertPortToHex(CameraPort)
|
||||
def hosthex = convertIPtoHex(CameraIP)
|
||||
def path = CameraPath.trim()
|
||||
def request = ""
|
||||
|
||||
// set a proper network Id of the device
|
||||
device.deviceNetworkId = "$hosthex:$porthex"
|
||||
|
||||
trace("The device id configured is: $device.deviceNetworkId")
|
||||
trace("state: " + state)
|
||||
|
||||
if (!state.auth || state.auth == "empty") {
|
||||
// empty request to get nonce token
|
||||
request = """GET ${path} HTTP/1.1\r\nAccept: */*\r\nHost: ${getHostAddress()}\r\n\r\n"""
|
||||
} else {
|
||||
// got nonce token, parsing headers and calculating digest header
|
||||
def auth_headers = calcDigestAuth(state.auth)
|
||||
request = """GET ${path} HTTP/1.1\r\nAccept: */*\r\nHost: ${getHostAddress()}\r\nAuthorization: ${auth_headers}\r\n\r\n"""
|
||||
}
|
||||
|
||||
try {
|
||||
def hubAction = new physicalgraph.device.HubAction(request, physicalgraph.device.Protocol.LAN, "${device.deviceNetworkId}")
|
||||
if (state.auth && state.auth != "empty") {
|
||||
// upload image/jpg output to S3
|
||||
hubAction.options = [outputMsgToS3: true]
|
||||
}
|
||||
return hubAction
|
||||
} catch (Exception e) {
|
||||
trace("Hit Exception $e on $hubAction")
|
||||
}
|
||||
}
|
||||
|
||||
// method to parse output from the camera
|
||||
def parse(String output) {
|
||||
trace("Parsing output: '${output}'")
|
||||
def headers = ""
|
||||
def parsedHeaders = ""
|
||||
def map = stringToMap(output)
|
||||
|
||||
if (map.headers) {
|
||||
headers = new String(map.headers.decodeBase64())
|
||||
parsedHeaders = parseHttpHeaders(headers)
|
||||
|
||||
if (parsedHeaders.auth) {
|
||||
// set required tokens in the special state variable (see description above)
|
||||
state.auth = parsedHeaders.auth
|
||||
trace("Got 401, send request again (click on 'take' one more time): " + state.auth)
|
||||
sendEvent(name: "authenticate", value: "DeAuth")
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
if (map.body != null) {
|
||||
def bodyString = new String(map.body.decodeBase64())
|
||||
trace(bodyString)
|
||||
}
|
||||
|
||||
if (map.bucket && map.key) {
|
||||
trace("Uploading the picture to amazon S3")
|
||||
putImageInS3(map)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
// parse headers that are returned from the camera
|
||||
private parseHttpHeaders(String headers) {
|
||||
def lines = headers.readLines()
|
||||
def status = lines[0].split()
|
||||
|
||||
def result = [
|
||||
protocol: status[0],
|
||||
status: status[1].toInteger(),
|
||||
reason: status[2]
|
||||
]
|
||||
|
||||
if (result.status == 401) {
|
||||
result.auth = stringToMap(lines[1].replaceAll("WWW-Authenticate: Digest ", "").replaceAll("=", ":").replaceAll("\"", ""))
|
||||
trace("It's ok. Press take again" + result.auth)
|
||||
}
|
||||
|
||||
if (result.status == 200) {
|
||||
trace("Authentication successful! :" + result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
// calculate digest token, more details: http://en.wikipedia.org/wiki/Digest_access_authentication#Overview
|
||||
private String calcDigestAuth(headers) {
|
||||
def HA1 = new String("${CameraUser}:" + headers.realm.trim() + ":${CameraPassword}").trim().encodeAsMD5()
|
||||
def HA2 = new String("${CameraPostGet}:${CameraPath}").trim().encodeAsMD5()
|
||||
|
||||
// increase nc every request by one
|
||||
if (!state.nc) {
|
||||
state.nc = 1
|
||||
} else {
|
||||
state.nc = state.nc + 1
|
||||
}
|
||||
|
||||
def cnonce = java.util.UUID.randomUUID().toString().replaceAll('-', '').substring(0, 8)
|
||||
def response = new String("${HA1}:" + headers.nonce.trim() + ":" + state.nc + ":" + cnonce + ":" + "auth" + ":${HA2}")
|
||||
def response_enc = response.encodeAsMD5()
|
||||
|
||||
trace("HA1: " + HA1 + " ===== org:" + "${CameraUser}:" + headers.realm.trim() + ":${CameraPassword}")
|
||||
trace("HA2: " + HA2 + " ===== org:" + "${CameraPostGet}:${CameraPath}")
|
||||
trace("Response: " + response_enc + " ===== org:" + response)
|
||||
|
||||
def eol = " "
|
||||
|
||||
return 'Digest username="' + CameraUser.trim() + '",' + eol +
|
||||
'realm="' + headers.realm.trim() + '",' + eol +
|
||||
'qop="' + headers.qop.trim() + '",' + eol +
|
||||
'algorithm="MD5",' + eol +
|
||||
'uri="'+ CameraPath.trim() + '",' + eol +
|
||||
'nonce="' + headers.nonce.trim() + '",' + eol +
|
||||
'cnonce="' + cnonce.trim() + '",'.trim() + eol +
|
||||
'opaque="",' + eol +
|
||||
'nc=' + state.nc + ',' + eol +
|
||||
'response="' + response_enc.trim() + '"'
|
||||
}
|
||||
|
||||
private getPictureName() {
|
||||
def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')
|
||||
return device.deviceNetworkId + "_$pictureUuid" + ".jpg"
|
||||
}
|
||||
|
||||
private String convertIPtoHex(ipAddress) {
|
||||
String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
|
||||
trace("IP address entered is $ipAddress and the converted hex code is $hex")
|
||||
return hex
|
||||
}
|
||||
|
||||
private String convertPortToHex(port) {
|
||||
String hexport = port.toString().format( '%04x', port.toInteger() )
|
||||
return hexport
|
||||
}
|
||||
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex, 16)
|
||||
}
|
||||
|
||||
|
||||
private String convertHexToIP(hex) {
|
||||
[convertHexToInt(hex[0..1]), convertHexToInt(hex[2..3]), convertHexToInt(hex[4..5]), convertHexToInt(hex[6..7])].join(".")
|
||||
}
|
||||
|
||||
private getHostAddress() {
|
||||
def parts = device.deviceNetworkId.split(":")
|
||||
def ip = convertHexToIP(parts[0])
|
||||
def port = convertHexToInt(parts[1])
|
||||
return ip + ":" + port
|
||||
}
|
||||
|
||||
private hashMD5(String somethingToHash) {
|
||||
java.security.MessageDigest.getInstance("MD5").digest(somethingToHash.getBytes("UTF-8")).encodeHex().toString()
|
||||
}
|
||||
|
||||
// store image on S3. Hint: if you use your bucket and key maybe you can upload it to your cloud? Never tested, but possible it will work.
|
||||
def putImageInS3(map) {
|
||||
def s3ObjectContent
|
||||
|
||||
try {
|
||||
def imageBytes = getS3Object(map.bucket, map.key + ".jpg")
|
||||
|
||||
if (imageBytes) {
|
||||
s3ObjectContent = imageBytes.getObjectContent()
|
||||
def bytes = new ByteArrayInputStream(s3ObjectContent.bytes)
|
||||
storeImage(getPictureName(), bytes)
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error e
|
||||
} finally {
|
||||
if (s3ObjectContent) {
|
||||
s3ObjectContent.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def delayHubAction(ms) {
|
||||
return new physicalgraph.device.HubAction("delay ${ms}")
|
||||
}
|
||||
|
||||
private getCallBackAddress() {
|
||||
device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
|
||||
}
|
||||
|
||||
private trace(message) {
|
||||
log.debug message
|
||||
}
|
||||
@@ -23,9 +23,9 @@
|
||||
'''Minimum time between messages (optional, defaults to every message)'''.ko=메시지 전송 간격 설정
|
||||
'''If outside the US please make sure to enter the proper country code'''.ko=미국 이외 국가에 거주한다면 국가 코드와 함께 입력하여 주세요.
|
||||
'''Water Sensor Wet'''.ko=누수가 감지되었을 때
|
||||
'''{{ triggerEvent.linkText }} has arrived at the {{ location.name }}'''.ko={{ triggerEvent.linkText }}님이 {{ location.name }}에 도착했습니다
|
||||
'''{{ triggerEvent.linkText }} has arrived at {{ location.name }}'''.ko={{ triggerEvent.linkText }}님이 {{ location.name }}에 도착했습니다
|
||||
'''{{ triggerEvent.linkText }} has left the {{ location.name }}'''.ko={{ triggerEvent.linkText }}님이 {{ location.name }}을(를) 떠났습니다
|
||||
'''{{ triggerEvent.linkText }} has left {{ location.name }}'''.ko={{ triggerEvent.linkText }}님이 {{ location.name }}을(를) 떠났습니다
|
||||
'''{{ triggerEvent.linkText }} has arrived at the {{ location.name }}'''.ko={{ location.name }}에 {{ triggerEvent.linkText }} 귀가
|
||||
'''{{ triggerEvent.linkText }} has arrived at {{ location.name }}'''.ko={{ location.name }}에 {{ triggerEvent.linkText }} 귀가
|
||||
'''{{ triggerEvent.linkText }} has left the {{ location.name }}'''.ko={{ location.name }}에서 {{ triggerEvent.linkText }} 외출
|
||||
'''{{ triggerEvent.linkText }} has left {{ location.name }}'''.ko={{ location.name }}에서 {{ triggerEvent.linkText }} 외출
|
||||
'''Assign a name'''.ko=이름 설정
|
||||
'''Choose Modes'''.ko=상태 선택
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Device Tile Controller
|
||||
*
|
||||
* Copyright 2016 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.
|
||||
*
|
||||
*/
|
||||
definition(
|
||||
name: "Device Tile Controller",
|
||||
namespace: "smartthings/tile-ux",
|
||||
author: "SmartThings",
|
||||
description: "A controller SmartApp to install virtual devices into your location in order to simulate various native Device Tiles.",
|
||||
category: "SmartThings Internal",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||
singleInstance: true)
|
||||
|
||||
|
||||
preferences {
|
||||
// landing page
|
||||
page(name: "defaultPage")
|
||||
}
|
||||
|
||||
def defaultPage() {
|
||||
dynamicPage(name: "defaultPage", install: true, uninstall: true) {
|
||||
section {
|
||||
paragraph "Select on Unselect the devices that you want to install"
|
||||
}
|
||||
section(title: "Multi Attribute Tile Types") {
|
||||
input(type: "bool", name: "genericDeviceTile", title: "generic", description: "A device that showcases the various use of generic multi-attribute-tiles.", defaultValue: "false")
|
||||
input(type: "bool", name: "lightingDeviceTile", title: "lighting", description: "A device that showcases the various use of lighting multi-attribute-tiles.", defaultValue: "false")
|
||||
input(type: "bool", name: "thermostatDeviceTile", title: "thermostat", description: "A device that showcases the various use of thermostat multi-attribute-tiles.", defaultValue: "true")
|
||||
input(type: "bool", name: "mediaPlayerDeviceTile", title: "media player", description: "A device that showcases the various use of mediaPlayer multi-attribute-tiles.", defaultValue: "false")
|
||||
input(type: "bool", name: "videoPlayerDeviceTile", title: "video player", description: "A device that showcases the various use of videoPlayer multi-attribute-tiles.", defaultValue: "false")
|
||||
}
|
||||
section(title: "Device Tile Types") {
|
||||
input(type: "bool", name: "standardDeviceTile", title: "standard device tiles", description: "A device that showcases the various use of standard device tiles.", defaultValue: "false")
|
||||
input(type: "bool", name: "valueDeviceTile", title: "value device tiles", description: "A device that showcases the various use of value device tiles.", defaultValue: "false")
|
||||
input(type: "bool", name: "presenceDeviceTile", title: "presence device tiles", description: "A device that showcases the various use of color control device tile.", defaultValue: "false")
|
||||
}
|
||||
section(title: "Other Tile Types") {
|
||||
input(type: "bool", name: "carouselDeviceTile", title: "image carousel", description: "A device that showcases the various use of carousel device tile.", defaultValue: "false")
|
||||
input(type: "bool", name: "sliderDeviceTile", title: "slider", description: "A device that showcases the various use of slider device tile.", defaultValue: "false")
|
||||
input(type: "bool", name: "colorWheelDeviceTile", title: "color wheel", description: "A device that showcases the various use of color wheel device tile.", defaultValue: "false")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
}
|
||||
|
||||
def uninstalled() {
|
||||
getChildDevices().each {
|
||||
deleteChildDevice(it.deviceNetworkId)
|
||||
}
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
initializeDevices()
|
||||
}
|
||||
|
||||
def initializeDevices() {
|
||||
settings.each { key, value ->
|
||||
log.debug "$key : $value"
|
||||
def existingDevice = getChildDevices().find { it.name == key }
|
||||
log.debug "$existingDevice"
|
||||
if (existingDevice && !value) {
|
||||
deleteChildDevice(existingDevice.deviceNetworkId)
|
||||
} else if (!existingDevice && value) {
|
||||
String dni = UUID.randomUUID()
|
||||
log.debug "$dni"
|
||||
addChildDevice(app.namespace, key, dni, null, [
|
||||
label: labelMap()[key] ?: key,
|
||||
completedSetup: true
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map the name of the Device to a proper Label
|
||||
def labelMap() {
|
||||
[
|
||||
genericDeviceTile: "Tile Multiattribute Generic",
|
||||
lightingDeviceTile: "Tile Multiattribute Lighting",
|
||||
thermostatDeviceTile: "Tile Multiattribute Thermostat",
|
||||
mediaPlayerDeviceTile: "Tile Multiattribute Media Player",
|
||||
videoPlayerDeviceTile: "Tile Multiattribute Video Player",
|
||||
standardDeviceTile: "Tile Device Standard",
|
||||
valueDeviceTile: "Tile Device Value",
|
||||
presenceDeviceTile: "Tile Device Presence",
|
||||
carouselDeviceTile: "Tile Device Carousel",
|
||||
sliderDeviceTile: "Tile Device Slider",
|
||||
colorWheelDeviceTile: "Tile Device Color Wheel"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user