mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-23 13:14:11 +00:00
Compare commits
9 Commits
PROD_2016.
...
MSA-1570-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f29521237c | ||
|
|
40c4520d08 | ||
|
|
1f69a42341 | ||
|
|
969852602c | ||
|
|
492315b78a | ||
|
|
40acb36009 | ||
|
|
8986c4f5d6 | ||
|
|
3a377ba147 | ||
|
|
8a66742bb5 |
@@ -23,6 +23,7 @@ metadata {
|
|||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Relative Humidity Measurement"
|
capability "Relative Humidity Measurement"
|
||||||
|
capability "Health Check"
|
||||||
|
|
||||||
command "generateEvent"
|
command "generateEvent"
|
||||||
command "raiseSetpoint"
|
command "raiseSetpoint"
|
||||||
@@ -38,6 +39,7 @@ metadata {
|
|||||||
attribute "maxCoolingSetpoint", "number"
|
attribute "maxCoolingSetpoint", "number"
|
||||||
attribute "minCoolingSetpoint", "number"
|
attribute "minCoolingSetpoint", "number"
|
||||||
attribute "deviceTemperatureUnit", "string"
|
attribute "deviceTemperatureUnit", "string"
|
||||||
|
attribute "deviceAlive", "enum", ["true", "false"]
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles {
|
tiles {
|
||||||
@@ -120,6 +122,21 @@ metadata {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void installed() {
|
||||||
|
// The device refreshes every 5 minutes by default so if we miss 2 refreshes we can consider it offline
|
||||||
|
// Using 12 minutes because in testing, device health team found that there could be "jitter"
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "cloud", hubHardwareId: device.hub.hardwareID], displayed: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device Watch will ping the device to proactively determine if the device has gone offline
|
||||||
|
// If the device was online the last time we refreshed, trigger another refresh as part of the ping.
|
||||||
|
def ping() {
|
||||||
|
def isAlive = device.currentValue("deviceAlive") == "true" ? true : false
|
||||||
|
if (isAlive) {
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "Parsing '${description}'"
|
log.debug "Parsing '${description}'"
|
||||||
@@ -164,7 +181,11 @@ def generateEvent(Map results) {
|
|||||||
} else if (name=="humidity") {
|
} else if (name=="humidity") {
|
||||||
isChange = isStateChange(device, name, value.toString())
|
isChange = isStateChange(device, name, value.toString())
|
||||||
event << [value: value.toString(), isStateChange: isChange, displayed: false, unit: "%"]
|
event << [value: value.toString(), isStateChange: isChange, displayed: false, unit: "%"]
|
||||||
} else {
|
} else if (name == "deviceAlive") {
|
||||||
|
isChange = isStateChange(device, name, value.toString())
|
||||||
|
event['isStateChange'] = isChange
|
||||||
|
event['displayed'] = false
|
||||||
|
} else {
|
||||||
isChange = isStateChange(device, name, value.toString())
|
isChange = isStateChange(device, name, value.toString())
|
||||||
isDisplayed = isChange
|
isDisplayed = isChange
|
||||||
event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed]
|
event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed]
|
||||||
|
|||||||
@@ -83,24 +83,105 @@ def parse(String description) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
def cluster = zigbee.parse(description)
|
Map bindingTable = parseBindingTableResponse(description)
|
||||||
|
if (bindingTable) {
|
||||||
|
List<String> cmds = []
|
||||||
|
bindingTable.table_entries.inject(cmds) { acc, entry ->
|
||||||
|
// The binding entry is not for our hub and should be deleted
|
||||||
|
if (entry["dstAddr"] != zigbeeEui) {
|
||||||
|
acc.addAll(removeBinding(entry.clusterId, entry.srcAddr, entry.srcEndpoint, entry.dstAddr, entry.dstEndpoint))
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
// There are more entries that we haven't examined yet
|
||||||
|
if (bindingTable.numTableEntries > bindingTable.startIndex + bindingTable.numEntriesReturned) {
|
||||||
|
def startPos
|
||||||
|
if (cmds) {
|
||||||
|
log.warn "Removing binding entries for other devices: $cmds"
|
||||||
|
// Since we are removing some entries, we should start in the same spot as we just read since values
|
||||||
|
// will fill in the newly vacated spots
|
||||||
|
startPos = bindingTable.startIndex
|
||||||
|
} else {
|
||||||
|
// Since we aren't removing anything we move forward to the next set of table entries
|
||||||
|
startPos = bindingTable.startIndex + bindingTable.numEntriesReturned
|
||||||
|
}
|
||||||
|
cmds.addAll(requestBindingTable(startPos))
|
||||||
|
}
|
||||||
|
sendHubCommand(cmds.collect { it ->
|
||||||
|
new physicalgraph.device.HubAction(it)
|
||||||
|
}, 2000)
|
||||||
|
} else {
|
||||||
|
def cluster = zigbee.parse(description)
|
||||||
|
|
||||||
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) {
|
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) {
|
||||||
if (cluster.data[0] == 0x00) {
|
if (cluster.data[0] == 0x00) {
|
||||||
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
|
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||||
|
log.debug "${cluster}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
|
||||||
log.debug "${cluster}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def parseBindingTableResponse(description) {
|
||||||
|
Map descMap = zigbee.parseDescriptionAsMap(description)
|
||||||
|
if (descMap["clusterInt"] == 0x8033) {
|
||||||
|
def header_field_lengths = ["transactionSeqNo": 1, "status": 1, "numTableEntries": 1, "startIndex": 1, "numEntriesReturned": 1]
|
||||||
|
def field_values = [:]
|
||||||
|
def data = descMap["data"]
|
||||||
|
header_field_lengths.each { k, v ->
|
||||||
|
field_values[k] = Integer.parseInt(data.take(v).join(""), 16);
|
||||||
|
data = data.drop(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map> table = []
|
||||||
|
if (field_values.numEntriesReturned) {
|
||||||
|
def table_entry_lengths = ["srcAddr": 8, "srcEndpoint": 1, "clusterId": 2, "dstAddrMode": 1]
|
||||||
|
for (def i : 0..(field_values.numEntriesReturned - 1)) {
|
||||||
|
def entryMap = [:]
|
||||||
|
table_entry_lengths.each { k, v ->
|
||||||
|
def val = data.take(v).reverse().join("")
|
||||||
|
entryMap[k] = val.length() < 8 ? Integer.parseInt(val, 16) : val
|
||||||
|
data = data.drop(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (entryMap.dstAddrMode) {
|
||||||
|
case 0x01:
|
||||||
|
entryMap["dstAddr"] = data.take(2).reverse().join("")
|
||||||
|
data = data.drop(2)
|
||||||
|
break
|
||||||
|
case 0x03:
|
||||||
|
entryMap["dstAddr"] = data.take(8).reverse().join("")
|
||||||
|
data = data.drop(8)
|
||||||
|
entryMap["dstEndpoint"] = Integer.parseInt(data.take(1).join(""), 16)
|
||||||
|
data = data.drop(1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
table << entryMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field_values["table_entries"] = table
|
||||||
|
return field_values
|
||||||
|
}
|
||||||
|
return [:]
|
||||||
|
}
|
||||||
|
|
||||||
|
def requestBindingTable(startPos=0) {
|
||||||
|
return ["zdo mgmt-bind 0x${zigbee.deviceNetworkId} $startPos"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def removeBinding(cluster, srcAddr, srcEndpoint, destAddr, destEndpoint) {
|
||||||
|
return ["zdo unbind unicast 0x${zigbee.deviceNetworkId} {${srcAddr}} $srcEndpoint $cluster {${destAddr}} $destEndpoint"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def off() {
|
def off() {
|
||||||
zigbee.off()
|
zigbee.off()
|
||||||
}
|
}
|
||||||
@@ -130,8 +211,7 @@ def configure() {
|
|||||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||||
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|
||||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
refresh() + requestBindingTable(0) + ["delay 2000"]
|
||||||
refresh()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def setColorTemperature(value) {
|
def setColorTemperature(value) {
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
.st-ignore
|
||||||
|
README.md
|
||||||
39
devicetypes/smartthings/zwave-switch-generic.src/README.md
Normal file
39
devicetypes/smartthings/zwave-switch-generic.src/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Z-wave Switch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Works with:
|
||||||
|
|
||||||
|
* [Leviton Appliance Module (DZPA1-1LW)](https://support.smartthings.com/hc/en-us/articles/205881176-Leviton-Appliance-Module-DZPA1-1LW-)
|
||||||
|
* [GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave)](https://support.smartthings.com/hc/en-us/articles/200903080-GE-Plug-In-Outdoor-Smart-Switch-GE-12720-Z-Wave-)
|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
|
||||||
|
* [Capabilities](#capabilities)
|
||||||
|
* [Health](#device-health)
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
* **Actuator** - represents that a Device has commands
|
||||||
|
* **Health Check** - indicates ability to get device health notifications
|
||||||
|
* **Switch** - can detect state (possible values: on/off)
|
||||||
|
* **Polling** - represents that poll() can be implemented for the device
|
||||||
|
* **Refresh** - _refresh()_ command for status updates
|
||||||
|
* **Sensor** - detects sensor events
|
||||||
|
|
||||||
|
## Device Health
|
||||||
|
|
||||||
|
A Category C5 Leviton Appliance Module (DZPA1-1LW) and GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave) polled by the hub.
|
||||||
|
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||||
|
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||||
|
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||||
|
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||||
|
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||||
|
Pairing needs to be tried again by placing the device closer to the hub.
|
||||||
|
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||||
|
* [Leviton Appliance Module (DZPA1-1LW) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205881176-Leviton-Appliance-Module-DZPA1-1LW-)
|
||||||
|
* [GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200903080-GE-Plug-In-Outdoor-Smart-Switch-GE-12720-Z-Wave-)
|
||||||
@@ -14,12 +14,15 @@
|
|||||||
metadata {
|
metadata {
|
||||||
definition (name: "Z-Wave Switch Generic", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "Z-Wave Switch Generic", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
|
capability "Health Check"
|
||||||
capability "Switch"
|
capability "Switch"
|
||||||
capability "Polling"
|
capability "Polling"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
|
|
||||||
fingerprint inClusters: "0x25", deviceJoinName: "Z-Wave Switch"
|
fingerprint inClusters: "0x25", deviceJoinName: "Z-Wave Switch"
|
||||||
|
fingerprint mfr:"001D", prod:"1A02", model:"0334", deviceJoinName: "Leviton Appliance Module"
|
||||||
|
fingerprint mfr:"0063", prod:"4F50", model:"3031", deviceJoinName: "GE Plug-in Outdoor Switch"
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulator metadata
|
// simulator metadata
|
||||||
@@ -50,6 +53,11 @@ metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updated(){
|
||||||
|
// Device-Watch simply pings if no device events received for checkInterval duration of 32min = 2 * 15min + 2min lag time
|
||||||
|
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
|
}
|
||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
def result = null
|
def result = null
|
||||||
def cmd = zwave.parse(description, [0x20: 1, 0x70: 1])
|
def cmd = zwave.parse(description, [0x20: 1, 0x70: 1])
|
||||||
@@ -126,6 +134,13 @@ def poll() {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
|
* */
|
||||||
|
def ping() {
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
delayBetween([
|
delayBetween([
|
||||||
zwave.switchBinaryV1.switchBinaryGet().format(),
|
zwave.switchBinaryV1.switchBinaryGet().format(),
|
||||||
|
|||||||
238
smartapps/js/switch-timer.src/switch-timer.groovy
Normal file
238
smartapps/js/switch-timer.src/switch-timer.groovy
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
/**
|
||||||
|
* Switch Timer
|
||||||
|
*
|
||||||
|
* Copyright 2016 Joe Saporito
|
||||||
|
*
|
||||||
|
* 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: "Switch Timer",
|
||||||
|
namespace: "js",
|
||||||
|
author: "Joe Saporito",
|
||||||
|
description: "Turns on switches that are not already on for a determined amount of time after an event. ",
|
||||||
|
category: "Convenience",
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
|
preferences {
|
||||||
|
|
||||||
|
page(name: "settings")
|
||||||
|
page(name: "certainTime")
|
||||||
|
page(name: "renameLabel")
|
||||||
|
}
|
||||||
|
|
||||||
|
def settings() {
|
||||||
|
dynamicPage(name: "settings", title: "Turn switches off after some minutes, unless already on", uninstall: true, install: true) {
|
||||||
|
section("Choose one or more, when..."){
|
||||||
|
input "people", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||||
|
input "doors", "capability.contactSensor", title: "Doors", required: false, multiple: true
|
||||||
|
input "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||||
|
}
|
||||||
|
section("Turn on switches"){
|
||||||
|
input "switches", "capability.switch", multiple: true, required: true
|
||||||
|
}
|
||||||
|
section("Turn off after") {
|
||||||
|
input "waitTime", "decimal", title: "Minutes", required: true
|
||||||
|
}
|
||||||
|
section(title: "Additional Options") {
|
||||||
|
def timeLabel = timeIntervalLabel()
|
||||||
|
href "certainTime", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
|
||||||
|
def appLabel = getDefaultLabel()
|
||||||
|
href "renameLabel", title: "Rename '" + appLabel + "'", description: ""
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def certainTime() {
|
||||||
|
dynamicPage(name:"certainTime",title: "Only during a certain time", uninstall: false) {
|
||||||
|
section() {
|
||||||
|
input "startEnum", "enum", title: "Starting at", options: ["A specific time", "Sunrise", "Sunset"], defaultValue: "A specific time", submitOnChange: true
|
||||||
|
if(startEnum in [null, "A specific time"]) input "startTime", "time", title: "Start time", required: false
|
||||||
|
else {
|
||||||
|
if(startEnum == "Sunrise") input "startSunriseOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
|
||||||
|
else if(startEnum == "Sunset") input "startSunsetOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section() {
|
||||||
|
input "endEnum", "enum", title: "Ending at", options: ["A specific time", "Sunrise", "Sunset"], defaultValue: "A specific time", submitOnChange: true
|
||||||
|
if(endEnum in [null, "A specific time"]) input "endTime", "time", title: "End time", required: false
|
||||||
|
else {
|
||||||
|
if(endEnum == "Sunrise") input "endSunriseOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
|
||||||
|
else if(endEnum == "Sunset") input "endSunsetOffset", "number", range: "*..*", title: "Offset in minutes (+/-)", required: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def renameLabel() {
|
||||||
|
dynamicPage(name:"renameLabel",title: "Rename", uninstall: false) {
|
||||||
|
section() {
|
||||||
|
input "appLabelText", "text", title: "Rename to", defaultValue: (appLabelText ? appLabelText : getDefaultLabel()), required: false
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def installed() {
|
||||||
|
log.debug "Installed with settings: ${settings}"
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updated() {
|
||||||
|
log.debug "Updated with settings: ${settings}"
|
||||||
|
unsubscribe()
|
||||||
|
initialize()
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
|
||||||
|
def initialize() {
|
||||||
|
subscribeToEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
def subscribeToEvents() {
|
||||||
|
subscribe(doors, "contact.open", activateHandler)
|
||||||
|
subscribe(people, "presence.present", activateHandler)
|
||||||
|
subscribe(motion, "motion.active", activateHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement event handlers
|
||||||
|
def activateHandler(evt) {
|
||||||
|
if (checkTime()) {
|
||||||
|
log.trace("$evt.name triggered")
|
||||||
|
turnOn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def turnOn() {
|
||||||
|
if(state.offSwitches) {
|
||||||
|
runIn(waitTime * 60, turnOff)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
def offSwitches = getOffSwitches()
|
||||||
|
for (s in offSwitches) {
|
||||||
|
s.on()
|
||||||
|
}
|
||||||
|
if (offSwitches.size() > 0) {
|
||||||
|
state.offSwitches = offSwitches.displayName
|
||||||
|
runIn(waitTime * 60, turnOff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def turnOff() {
|
||||||
|
if (state.offSwitches) {
|
||||||
|
def offSwitches = switches.findAll {
|
||||||
|
it.displayName in state.offSwitches
|
||||||
|
}
|
||||||
|
for (s in offSwitches) {
|
||||||
|
s.off()
|
||||||
|
}
|
||||||
|
state.offSwitches = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateLabel() {
|
||||||
|
if (appLabelText) {
|
||||||
|
if (appLabelText != app.label) {
|
||||||
|
log.trace("Renamed to $appLabelText")
|
||||||
|
app.updateLabel(appLabelText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
def label = getDefaultLabel()
|
||||||
|
log.trace("Renamed to $label")
|
||||||
|
app.updateLabel(label)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDefaultLabel() {
|
||||||
|
def label = "Switch Timer"
|
||||||
|
|
||||||
|
if (switches) {
|
||||||
|
label = "Timer for " + switches[0].displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOffSwitches() {
|
||||||
|
def offSwitches = switches.findAll {
|
||||||
|
it.currentSwitch == "off" ||
|
||||||
|
it.currentSwitch == null
|
||||||
|
}
|
||||||
|
return offSwitches
|
||||||
|
}
|
||||||
|
private checkTime() {
|
||||||
|
def result = true
|
||||||
|
|
||||||
|
if ((startTime && endTime) ||
|
||||||
|
(startTime && endEnum in ["Sunrise", "Sunset"]) ||
|
||||||
|
(startEnum in ["Sunrise", "Sunset"] && endTime) ||
|
||||||
|
(startEnum in ["Sunrise", "Sunset"] && endEnum in ["Sunrise", "Sunset"])) {
|
||||||
|
def currentTime = now()
|
||||||
|
def start = null
|
||||||
|
def stop = null
|
||||||
|
def sun = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: startSunriseOffset, sunsetOffset: startSunsetOffset)
|
||||||
|
|
||||||
|
if (startEnum == "Sunrise")
|
||||||
|
start = sun.sunrise.time
|
||||||
|
else if (startEnum == "Sunset")
|
||||||
|
start = sun.sunset.time
|
||||||
|
else if (startTime)
|
||||||
|
start = timeToday(startTime,location.timeZone).time
|
||||||
|
|
||||||
|
sun = getSunriseAndSunset(zipCode: zipCode, sunriseOffset: endSunriseOffset, sunsetOffset: endSunsetOffset)
|
||||||
|
if (endEnum == "Sunrise")
|
||||||
|
stop = sun.sunrise.time
|
||||||
|
else if (endEnum == "Sunset")
|
||||||
|
stop = sun.sunset.time
|
||||||
|
else if (endTime)
|
||||||
|
stop = timeToday(endTime,location.timeZone).time
|
||||||
|
|
||||||
|
result = start < stop ? currentTime >= start && currentTime <= stop : currentTime <= stop || currentTime >= start
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private hideOptionsSection() {
|
||||||
|
(startTime || endTime || startEnum || endEnum) ? false : true
|
||||||
|
}
|
||||||
|
|
||||||
|
private offset(value) {
|
||||||
|
def result = value ? ((value > 0 ? "+" : "") + value + " min") : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private hhmm(time, fmt = "h:mm a") {
|
||||||
|
def t = timeToday(time, location.timeZone)
|
||||||
|
def f = new java.text.SimpleDateFormat(fmt)
|
||||||
|
f.setTimeZone(location.timeZone ?: timeZone(time))
|
||||||
|
f.format(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
private timeIntervalLabel() {
|
||||||
|
def result = ""
|
||||||
|
if (startEnum == "Sunrise" && endEnum == "Sunrise") result = "Sunrise" + offset(startSunriseOffset) + " to " + "Sunrise" + offset(endSunriseOffset)
|
||||||
|
else if (startEnum == "Sunrise" && endEnum == "Sunset") result = "Sunrise" + offset(startSunriseOffset) + " to " + "Sunset" + offset(endSunsetOffset)
|
||||||
|
else if (startEnum == "Sunset" && endEnum == "Sunrise") result = "Sunset" + offset(startSunsetOffset) + " to " + "Sunrise" + offset(endSunriseOffset)
|
||||||
|
else if (startEnum == "Sunset" && endEnum == "Sunset") result = "Sunset" + offset(startSunsetOffset) + " to " + "Sunset" + offset(endSunsetOffset)
|
||||||
|
else if (startEnum == "Sunrise" && endTime) result = "Sunrise" + offset(startSunriseOffset) + " to " + hhmm(endTime, "h:mm a z")
|
||||||
|
else if (startEnum == "Sunset" && endTime) result = "Sunset" + offset(startSunsetOffset) + " to " + hhmm(endTime, "h:mm a z")
|
||||||
|
else if (startTime && endEnum == "Sunrise") result = hhmm(startTime) + " to " + "Sunrise" + offset(endSunriseOffset)
|
||||||
|
else if (startTime && endEnum == "Sunset") result = hhmm(startTime) + " to " + "Sunset" + offset(endSunsetOffset)
|
||||||
|
else if (startTime && endTime) result = hhmm(startTime) + " to " + hhmm(endTime, "h:mm a z")
|
||||||
|
}
|
||||||
@@ -842,6 +842,7 @@ private void storeThermostatData(thermostats) {
|
|||||||
minCoolingSetpoint: (stat.settings.coolRangeLow / 10),
|
minCoolingSetpoint: (stat.settings.coolRangeLow / 10),
|
||||||
maxCoolingSetpoint: (stat.settings.coolRangeHigh / 10),
|
maxCoolingSetpoint: (stat.settings.coolRangeHigh / 10),
|
||||||
autoMode: stat.settings.autoHeatCoolFeatureEnabled,
|
autoMode: stat.settings.autoHeatCoolFeatureEnabled,
|
||||||
|
deviceAlive: stat.runtime.connected == true ? "true" : "false",
|
||||||
auxHeatMode: (stat.settings.hasHeatPump) && (stat.settings.hasForcedAir || stat.settings.hasElectric || stat.settings.hasBoiler),
|
auxHeatMode: (stat.settings.hasHeatPump) && (stat.settings.hasForcedAir || stat.settings.hasElectric || stat.settings.hasBoiler),
|
||||||
temperature: (stat.runtime.actualTemperature / 10),
|
temperature: (stat.runtime.actualTemperature / 10),
|
||||||
heatingSetpoint: stat.runtime.desiredHeat / 10,
|
heatingSetpoint: stat.runtime.desiredHeat / 10,
|
||||||
|
|||||||
Reference in New Issue
Block a user