Compare commits

..

33 Commits

Author SHA1 Message Date
Vinay Rao
01a36696d8 Merge pull request #291 from kwarodom/LiFXProd
LiFX - LiFX: update oauth/callback url using getApiServerUrl() for shard proxying and change shardUrl param to apiServerUrl
2015-11-17 15:00:30 -08:00
Yaima Valdivia
797def2935 Merge branch 'master' of github.com:SmartThingsCommunity/SmartThingsPublic 2015-11-17 10:40:04 -08:00
Yaima Valdivia
1034cd06e6 Var names reversed 2015-11-17 10:38:01 -08:00
Vinay Rao
c37729242e temporary changes to the lock DTH to account for the upcoming zigbee library changes 2015-11-17 10:38:01 -08:00
Vinay Rao
d29c3ec557 Cree DTH refresh 2015-11-17 10:38:01 -08:00
Vinay Rao
17be85b846 Moving re-certified device to generic zigbee DTH. 2015-11-17 10:38:01 -08:00
Vinay Rao
da06104563 Adding fingerprint for new bulbs and transition old color temperature osram bulbs to new one 2015-11-17 10:38:01 -08:00
Vinay Rao
5d37ac8515 new fingerprints for Yale. battery issue resolution for Yale 2015-11-17 10:38:01 -08:00
Vinay Rao
2a739fda07 adding fingerprints for osram and sengled dimmable bulbs 2015-11-17 10:38:01 -08:00
Juan Pablo Risso
f627fb4fac Reverted to previously working getBridgeIP() 2015-11-17 10:38:01 -08:00
Juan Pablo Risso
1d30a718b2 Replaced atomicState with State 2015-11-17 10:38:01 -08:00
Juan Pablo Risso
303ca7117c Added a "," 2015-11-17 10:38:01 -08:00
Juan Pablo Risso
1c96645b9f removed function ShardUrl() 2015-11-17 10:38:00 -08:00
Juan Pablo Risso
af4dc0640a added singleInstance: true 2015-11-17 10:38:00 -08:00
juano2310
80b46153dc Harmony Global Oauth 2015-11-17 10:38:00 -08:00
Mike Robinet
b92cd9c637 CREX-1094 Delete stale device subscriptions on IFTTT app update 2015-11-17 10:38:00 -08:00
Juan Pablo Risso
1039b65c81 Code Cleanup 2015-11-17 10:38:00 -08:00
juano2310
4e203e8e13 buildActionUrl("hookCallback") 2015-11-17 10:38:00 -08:00
juano2310
61398105d1 Jawbone Global Oauth 2015-11-17 10:38:00 -08:00
bflorian
505efc5463 Deleted lights on when door opens after sundown 2015-11-17 10:38:00 -08:00
bflorian
3ddc82f996 More name/namespace changes 2015-11-17 10:38:00 -08:00
Juan Pablo Risso
13a324069d Force Level = 1% to 1
This ensures that the bulb will be able to dim to it minimum
2015-11-17 10:38:00 -08:00
bflorian
2fd5859326 Misc filename and namespace changes. 2015-11-17 10:38:00 -08:00
bflorian
bbedbddf9d Filename corrections 2015-11-17 10:38:00 -08:00
bflorian
e424e7abdd Corrected filename of Z-Wave Device Multichannel 2015-11-17 10:38:00 -08:00
Tom Manley
47fbdabf6b Fix 'Low Battery Handler' exception caused by non-integer battery events
ZigBee locks report battery percentage remaining in .5% increments. However
the Low Battery Handler Smart App in Hello Home expects it to be an integer.
2015-11-17 10:37:59 -08:00
Mike Robinet
7defe1cc61 CREX-3129 Update parent and service manager apps to be singleton 2015-11-17 10:37:59 -08:00
Mike Cousins
a78459347b update keen home smart vent device handler 2015-11-17 10:37:59 -08:00
juano2310
c473745e47 Wemo refactor final (DVCSMP-1189)
https://smartthings.atlassian.net/browse/DVCSMP-1189

Detect and mark device offline within 5 minutes.
Show Device offline in device tile.
Show Device offline in Recent Activity.
Log the current IP address to Recent Activity.
Log the changed IP address to Recent Activity.
Support 'Turning on' and 'Turning off' (blindly changing the state of
device to ON or OFF without confirming bulb responded correctly)
Turn on / off through Wemo-App reflected timely in SmartThings
App/Ecosystem.
Manual turn on / off of device is reflected timely in SmartThings
App/Ecosystem.

Lower case createEvent

Bug Fixes

Bug fixes

setOffline

Minor cosmetic fixes
2015-11-17 10:37:59 -08:00
Juan Pablo Risso
fc587ef15a Fix to Hue reverts dimmer settings (DVCSMP-1227)
if you use the hue native app to adjust the dimmer setting, smartthings will reset the dimmer to previous value when toggling from ST app (and automations)
2015-11-17 10:37:59 -08:00
Warodom Khamphanchai
0f3b730f26 DVCSMP-668
- Show batteryStatus tile insteady of battery tile to be able to display both when sensor is USB powered or battery powered
- Remove background for illuminance. This can be added when we have best practice of showing colors for lux.
- Instead of using powerSupply:failed, configurationGet cmd is sent and then the configure() is triggered by configurationReport to determine powerSupply (USB Cable/Battery)
- Instead of querying battery level on wake up, battery report is put in association group 2 that is configured to report every 6 hours by default
- Update configure() to  send both unsecure and secure configuration commands when sensor is joined normally or securely
2015-11-17 10:37:59 -08:00
Warodom Khamphanchai
11df2f31b3 DVCSMP-668
The following changes has been made to the original Aeon Multisensor device type handler to improve and modernize it:
1. Add "powerSupply" attribute to be able to tell power source (USB Cable/Battery)
2. Add preference page for user to customize "motion delay time", "motion sensitivity", and "sensor report interval"
3. Add color backgroud of "illuminance" value tile
4. Add tile for "ultravioletIndex"
5. Add tile for "powerSupply"
6. Modify updated() to be able to send configuration commands to sensor whether it is powered by USB cable or battery
7. When battery operated, send command to get update battery level if it hasn't been reported for a while
8. Report MSR of the sensor
9. Add handle for ConfigurationReport command class to update the "powerSupply" tile and change opetion mode of the sensor accordingly
10. Update configure() to configure parameters changed by user in preference page
11. Take out the "Configure" tile and instead send the configuration commands on every wakeup (sensor is battey powered)
2015-11-17 10:37:59 -08:00
Yaima Valdivia
4d243bf44d Rename Sonos SmartApps
https://smartthings.atlassian.net/browse/DVCSMP-607
2015-10-27 12:29:05 -07:00
29 changed files with 1211 additions and 1174 deletions

View File

@@ -15,7 +15,6 @@
* Author: SmartThings * Author: SmartThings
* Date: 2013-12-04 * Date: 2013-12-04
*/ */
//DEPRECATED - Using the generic DTH for this device. Users need to be moved before deleting this DTH
metadata { metadata {
definition (name: "CentraLite Dimmer", namespace: "smartthings", author: "SmartThings") { definition (name: "CentraLite Dimmer", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level" capability "Switch Level"
@@ -26,6 +25,7 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B04,0B05", outClusters: "0019"
} }
// simulator metadata // simulator metadata

View File

@@ -1,71 +0,0 @@
/**
* Ecobee Sensor
*
* Copyright 2015 Juan Risso
*
* 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: "Ecobee Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Sensor"
capability "Temperature Measurement"
capability "Motion Sensor"
capability "Refresh"
capability "Polling"
}
simulator {
// TODO: define status and reply messages here
}
tiles {
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°', unit:"F",
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("motion", "device.motion") {
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main (["temperature","motion"])
details(["temperature","motion","refresh"])
}
}
def refresh() {
log.debug "refresh..."
poll()
}
void poll() {
log.debug "Executing 'poll' using parent SmartApp"
parent.pollChildren(this)
}
//generate custom mobile activity feeds event
def generateActivityFeedsEvent(notificationMessage) {
sendEvent(name: "notificationMessage", value: "$device.displayName $notificationMessage", descriptionText: "$device.displayName $notificationMessage", displayed: true)
}

View File

@@ -35,18 +35,18 @@ metadata {
simulator { } simulator { }
tiles { tiles {
valueTile("temperature", "device.temperature", width: 2, height: 2) { valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°', unit:"F", state("temperature", label:'${currentValue}°', unit:"F",
backgroundColors:[ backgroundColors:[
[value: 31, color: "#153591"], [value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"], [value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"], [value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"], [value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"], [value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"], [value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"] [value: 96, color: "#bc2323"]
] ]
) )
} }
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
@@ -55,7 +55,7 @@ metadata {
state "cool", action:"switchMode", nextState: "updating", icon: "st.thermostat.cool" state "cool", action:"switchMode", nextState: "updating", icon: "st.thermostat.cool"
state "auto", action:"switchMode", nextState: "updating", icon: "st.thermostat.auto" state "auto", action:"switchMode", nextState: "updating", icon: "st.thermostat.auto"
state "auxHeatOnly", action:"switchMode", icon: "st.thermostat.emergency-heat" state "auxHeatOnly", action:"switchMode", icon: "st.thermostat.emergency-heat"
state "updating", label:"Working", icon: "st.secondary.secondary" state "updating", label:"Working", icon: "st.secondary.secondary"
} }
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") { standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
state "auto", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "on" state "auto", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "on"
@@ -63,17 +63,17 @@ metadata {
state "off", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "circulate" state "off", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "circulate"
state "circulate", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "auto" state "circulate", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "auto"
} }
standardTile("upButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") { standardTile("upButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") {
state "setpoint", action:"raiseSetpoint", icon:"st.thermostat.thermostat-up" state "setpoint", action:"raiseSetpoint", backgroundColor:"#d04e00", icon:"st.thermostat.thermostat-up"
} }
valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") { valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") {
state "thermostatSetpoint", label:'${currentValue}°' state "thermostatSetpoint", label:'${currentValue}'
} }
valueTile("currentStatus", "device.thermostatStatus", height: 1, width: 2, decoration: "flat") { valueTile("currentStatus", "device.thermostatStatus", height: 1, width: 2, decoration: "flat") {
state "thermostatStatus", label:'${currentValue}', backgroundColor:"#ffffff" state "thermostatStatus", label:'${currentValue}', backgroundColor:"#ffffff"
} }
standardTile("downButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") { standardTile("downButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") {
state "setpoint", action:"lowerSetpoint", icon:"st.thermostat.thermostat-down" state "setpoint", action:"lowerSetpoint", backgroundColor:"#d04e00", icon:"st.thermostat.thermostat-down"
} }
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) { controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#d04e00" state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#d04e00"
@@ -91,19 +91,25 @@ metadata {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh" state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
standardTile("resumeProgram", "device.resumeProgram", inactiveLabel: false, decoration: "flat") { standardTile("resumeProgram", "device.resumeProgram", inactiveLabel: false, decoration: "flat") {
state "resume", action:"resumeProgram", nextState: "updating", label:'Resume Schedule', icon:"st.samsung.da.oven_ic_send" state "resume", label:'Resume Program', action:"device.resumeProgram", icon:"st.sonos.play-icon"
state "updating", label:"Working", icon: "st.secondary.secondary"
} }
main "temperature" main "temperature"
details(["temperature", "upButtonControl", "thermostatSetpoint", "currentStatus", "downButtonControl", "mode", "resumeProgram", "refresh"]) details(["temperature", "upButtonControl", "thermostatSetpoint", "currentStatus", "downButtonControl", "mode", "resumeProgram", "refresh"])
}
preferences {
input "holdType", "enum", title: "Hold Type", description: "When changing temperature, use Temporary or Permanent hold (default)", required: false, options:["Temporary", "Permanent"]
} }
} }
/*
preferences {
input "highTemperature", "number", title: "Auto Mode High Temperature:", defaultValue: 80
input "lowTemperature", "number", title: "Auto Mode Low Temperature:", defaultValue: 70
input name: "holdType", type: "enum", title: "Hold Type", description: "When changing temperature, use Temporary or Permanent hold", required: true, options:["Temporary", "Permanent"]
}
*/
// parse events into attributes // parse events into attributes
def parse(String description) { def parse(String description) {
log.debug "Parsing '${description}'" log.debug "Parsing '${description}'"
@@ -111,176 +117,192 @@ def parse(String description) {
} }
def refresh() { def refresh()
log.debug "refresh called" {
poll() log.debug "refresh called"
log.debug "refresh ended" poll()
log.debug "refresh ended"
}
def go()
{
log.debug "before:go tile tapped"
poll()
log.debug "after"
} }
void poll() { void poll() {
log.debug "Executing 'poll' using parent SmartApp" log.debug "Executing 'poll' using parent SmartApp"
def results = parent.pollChild(this) def results = parent.pollChild(this)
generateEvent(results) //parse received message from parent parseEventData(results)
generateStatusEvent()
} }
def generateEvent(Map results) { def parseEventData(Map results)
{
log.debug "parsing data $results" log.debug "parsing data $results"
if(results) { if(results)
{
results.each { name, value -> results.each { name, value ->
def linkText = getLinkText(device) def linkText = getLinkText(device)
def isChange = false def isChange = false
def isDisplayed = true def isDisplayed = true
def event = [name: name, linkText: linkText, descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name]
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint") { if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint") {
def sendValue = value? convertTemperatureIfNeeded(value.toDouble(), "F", 1): value //API return temperature value in F
isChange = isTemperatureStateChange(device, name, value.toString()) isChange = isTemperatureStateChange(device, name, value.toString())
isDisplayed = isChange isDisplayed = isChange
event << [value: sendValue, isStateChange: isChange, displayed: isDisplayed]
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){ sendEvent(
isChange = isStateChange(device, name, value.toString()) name: name,
event << [value: value.toString(), isStateChange: isChange, displayed: false] value: value,
} else { unit: "F",
isChange = isStateChange(device, name, value.toString()) linkText: linkText,
isDisplayed = isChange descriptionText: getThermostatDescriptionText(name, value, linkText),
event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed] handlerName: name,
} isStateChange: isChange,
sendEvent(event) displayed: isDisplayed)
}
else {
isChange = isStateChange(device, name, value.toString())
isDisplayed = isChange
sendEvent(
name: name,
value: value.toString(),
linkText: linkText,
descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name,
isStateChange: isChange,
displayed: isDisplayed)
}
} }
generateSetpointEvent () generateSetpointEvent ()
generateStatusEvent () generateStatusEvent ()
} }
} }
//return descriptionText to be shown on mobile activity feed void generateEvent(Map results)
private getThermostatDescriptionText(name, value, linkText) { {
if(name == "temperature") { log.debug "parsing data $results"
return "$linkText temperature is $value°F" if(results)
{
results.each { name, value ->
} else if(name == "heatingSetpoint") { def linkText = getLinkText(device)
return "heating setpoint is $value°F" def isChange = false
def isDisplayed = true
} else if(name == "coolingSetpoint"){ if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint") {
return "cooling setpoint is $value°F" isChange = isTemperatureStateChange(device, name, value.toString())
isDisplayed = isChange
} else if (name == "thermostatMode") { sendEvent(
return "thermostat mode is ${value}" name: name,
value: value,
unit: "F",
linkText: linkText,
descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name,
isStateChange: isChange,
displayed: isDisplayed)
}
else {
isChange = isStateChange(device, name, value.toString())
isDisplayed = isChange
} else if (name == "thermostatFanMode") { sendEvent(
return "thermostat fan mode is ${value}" name: name,
value: value.toString(),
linkText: linkText,
descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name,
isStateChange: isChange,
displayed: isDisplayed)
} else { }
return "${name} = ${value}" }
generateSetpointEvent ()
generateStatusEvent()
} }
} }
void setHeatingSetpoint(setpoint) { private getThermostatDescriptionText(name, value, linkText)
setHeatingSetpoint(setpoint.toDouble()) {
if(name == "temperature")
{
return "$linkText was $value°F"
}
else if(name == "heatingSetpoint")
{
return "latest heating setpoint was $value°F"
}
else if(name == "coolingSetpoint")
{
return "latest cooling setpoint was $value°F"
}
else if (name == "thermostatMode")
{
return "thermostat mode is ${value}"
}
else
{
return "${name} = ${value}"
}
} }
void setHeatingSetpoint(Double setpoint) {
// def mode = device.currentValue("thermostatMode")
def heatingSetpoint = setpoint
def coolingSetpoint = device.currentValue("coolingSetpoint").toDouble()
def deviceId = device.deviceNetworkId.split(/\./).last()
//enforce limits of heatingSetpoint void setHeatingSetpoint(degreesF) {
if (heatingSetpoint > 79) { setHeatingSetpoint(degreesF.toDouble())
heatingSetpoint = 79
} else if (heatingSetpoint < 45) {
heatingSetpoint = 45
}
//enforce limits of heatingSetpoint vs coolingSetpoint
if (heatingSetpoint >= coolingSetpoint) {
coolingSetpoint = heatingSetpoint
}
log.debug "Sending setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
if (parent.setHold (this, heatingSetpoint, coolingSetpoint, deviceId, sendHoldType)) {
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
log.debug "Done setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
generateSetpointEvent()
generateStatusEvent()
} else {
log.error "Error setHeatingSetpoint(setpoint)" //This error is handled by the connect app
}
} }
void setCoolingSetpoint(setpoint) { void setHeatingSetpoint(Double degreesF) {
setCoolingSetpoint(setpoint.toDouble()) log.debug "setHeatingSetpoint({$degreesF})"
sendEvent("name":"heatingSetpoint", "value":degreesF)
Double coolingSetpoint = device.currentValue("coolingSetpoint")
log.debug "coolingSetpoint: $coolingSetpoint"
parent.setHold(this, degreesF, coolingSetpoint)
} }
void setCoolingSetpoint(Double setpoint) { void setCoolingSetpoint(degreesF) {
// def mode = device.currentValue("thermostatMode") setCoolingSetpoint(degreesF.toDouble())
def heatingSetpoint = device.currentValue("heatingSetpoint").toDouble()
def coolingSetpoint = setpoint
def deviceId = device.deviceNetworkId.split(/\./).last()
if (coolingSetpoint > 92) {
coolingSetpoint = 92
} else if (coolingSetpoint < 65) {
coolingSetpoint = 65
}
//enforce limits of heatingSetpoint vs coolingSetpoint
if (heatingSetpoint >= coolingSetpoint) {
heatingSetpoint = coolingSetpoint
}
log.debug "Sending setCoolingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
if (parent.setHold (this, heatingSetpoint, coolingSetpoint, deviceId, sendHoldType)) {
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
log.debug "Done setCoolingSetpoint>> coolingSetpoint = ${coolingSetpoint}, heatingSetpoint = ${heatingSetpoint}"
generateSetpointEvent()
generateStatusEvent()
} else {
log.error "Error setCoolingSetpoint(setpoint)" //This error is handled by the connect app
}
} }
void resumeProgram() { void setCoolingSetpoint(Double degreesF) {
log.debug "setCoolingSetpoint({$degreesF})"
sendEvent("name":"coolingSetpoint", "value":degreesF)
Double heatingSetpoint = device.currentValue("heatingSetpoint")
parent.setHold(this, heatingSetpoint, degreesF)
}
log.debug "resumeProgram() is called" def configure() {
sendEvent("name":"thermostatStatus", "value":"resuming schedule", "description":statusText, displayed: false)
def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.resumeProgram(this, deviceId)) {
sendEvent("name":"thermostatStatus", "value":"setpoint is updating", "description":statusText, displayed: false)
runIn(5, "poll")
log.debug "resumeProgram() is done"
sendEvent("name":"resumeProgram", "value":"resume", descriptionText: "resumeProgram is done", displayed: false, isStateChange: true)
} else {
sendEvent("name":"thermostatStatus", "value":"failed resume click refresh", "description":statusText, displayed: false)
log.error "Error resumeProgram() check parent.resumeProgram(this, deviceId)"
}
} }
def resumeProgram() {
parent.resumeProgram(this)
}
def modes() { def modes() {
if (state.modes) { if (state.modes) {
log.debug "Modes = ${state.modes}" log.debug "Modes = ${state.modes}"
return state.modes return state.modes
} }
else { else {
state.modes = parent.availableModes(this) state.modes = parent.availableModes(this)
log.debug "Modes = ${state.modes}" log.debug "Modes = ${state.modes}"
return state.modes return state.modes
} }
} }
def fanModes() { def fanModes() {
["off", "on", "auto", "circulate"] ["off", "on", "auto", "circulate"]
} }
def switchMode() { def switchMode() {
log.debug "in switchMode" log.debug "in switchMode"
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
@@ -292,7 +314,7 @@ def switchMode() {
} }
def switchToMode(nextMode) { def switchToMode(nextMode) {
log.debug "In switchToMode = ${nextMode}" log.debug "In switchToMode = ${nextMode}"
if (nextMode in modes()) { if (nextMode in modes()) {
state.lastTriedMode = nextMode state.lastTriedMode = nextMode
"$nextMode"() "$nextMode"()
@@ -364,316 +386,290 @@ def setThermostatFanMode(String value) {
} }
def generateModeEvent(mode) { def generateModeEvent(mode) {
sendEvent(name: "thermostatMode", value: mode, descriptionText: "$device.displayName is in ${mode} mode", displayed: true)
sendEvent(name: "thermostatMode", value: mode, descriptionText: "$device.displayName is in ${mode} mode", displayed: true, isStateChange: true)
} }
def generateFanModeEvent(fanMode) { def generateFanModeEvent(fanMode) {
sendEvent(name: "thermostatFanMode", value: fanMode, descriptionText: "$device.displayName fan is in ${mode} mode", displayed: true)
sendEvent(name: "thermostatFanMode", value: fanMode, descriptionText: "$device.displayName fan is in ${mode} mode", displayed: true, isStateChange: true)
} }
def generateOperatingStateEvent(operatingState) { def generateOperatingStateEvent(operatingState) {
sendEvent(name: "thermostatOperatingState", value: operatingState, descriptionText: "$device.displayName is ${operatingState}", displayed: true)
sendEvent(name: "thermostatOperatingState", value: operatingState, descriptionText: "$device.displayName is ${operatingState}", displayed: true, isStateChange: true)
} }
def off() { def off() {
log.debug "off" log.debug "off"
def deviceId = device.deviceNetworkId.split(/\./).last() generateModeEvent("off")
if (parent.setMode (this,"off", deviceId)) if (parent.setMode (this,"off"))
generateModeEvent("off") generateModeEvent("off")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back generateModeEvent(currentMode) // reset the tile back
} }
generateSetpointEvent() generateSetpointEvent()
generateStatusEvent() generateStatusEvent()
} }
def heat() { def heat() {
log.debug "heat" log.debug "heat"
def deviceId = device.deviceNetworkId.split(/\./).last() generateModeEvent("heat")
if (parent.setMode (this,"heat", deviceId)) if (parent.setMode (this,"heat"))
generateModeEvent("heat") generateModeEvent("heat")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back generateModeEvent(currentMode) // reset the tile back
} }
generateSetpointEvent() generateSetpointEvent()
generateStatusEvent() generateStatusEvent()
} }
def auxHeatOnly() { def auxHeatOnly() {
log.debug "auxHeatOnly" log.debug "auxHeatOnly"
def deviceId = device.deviceNetworkId.split(/\./).last() generateModeEvent("auxHeatOnly")
if (parent.setMode (this,"auxHeatOnly", deviceId)) if (parent.setMode (this,"auxHeatOnly"))
generateModeEvent("auxHeatOnly") generateModeEvent("auxHeatOnly")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back generateModeEvent(currentMode) // reset the tile back
} }
generateSetpointEvent() generateSetpointEvent()
generateStatusEvent() generateStatusEvent()
} }
def cool() { def cool() {
log.debug "cool" log.debug "cool"
def deviceId = device.deviceNetworkId.split(/\./).last() generateModeEvent("cool")
if (parent.setMode (this,"cool", deviceId)) if (parent.setMode (this,"cool"))
generateModeEvent("cool") generateModeEvent("cool")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back generateModeEvent(currentMode) // reset the tile back
} }
generateSetpointEvent() generateSetpointEvent()
generateStatusEvent() generateStatusEvent()
} }
def auto() { def auto() {
log.debug "auto" log.debug "auto"
def deviceId = device.deviceNetworkId.split(/\./).last() generateModeEvent("auto")
if (parent.setMode (this,"auto", deviceId)) if (parent.setMode (this,"auto"))
generateModeEvent("auto") generateModeEvent("auto")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back generateModeEvent(currentMode) // reset the tile back
} }
generateSetpointEvent() generateSetpointEvent()
generateStatusEvent() generateStatusEvent()
} }
def fanOn() { def fanOn() {
log.debug "fanOn" log.debug "fanOn"
// parent.setFanMode (this,"on") parent.setFanMode (this,"on")
} }
def fanAuto() { def fanAuto() {
log.debug "fanAuto" log.debug "fanAuto"
// parent.setFanMode (this,"auto") parent.setFanMode (this,"auto")
} }
def fanCirculate() { def fanCirculate() {
log.debug "fanCirculate" log.debug "fanCirculate"
// parent.setFanMode (this,"circulate") parent.setFanMode (this,"circulate")
} }
def fanOff() { def fanOff() {
log.debug "fanOff" log.debug "fanOff"
// parent.setFanMode (this,"off") parent.setFanMode (this,"off")
} }
def generateSetpointEvent() { def generateSetpointEvent() {
log.debug "Generate SetPoint Event" log.debug "Generate SetPoint Event"
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
log.debug "Current Mode = ${mode}" log.debug "Current Mode = ${mode}"
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger() def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
log.debug "Heating Setpoint = ${heatingSetpoint}" log.debug "Heating Setpoint = ${heatingSetpoint}"
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger() def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
log.debug "Cooling Setpoint = ${coolingSetpoint}" log.debug "Cooling Setpoint = ${coolingSetpoint}"
if (mode == "heat") { if (mode == "heat") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()) sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()+"°")
} }
else if (mode == "cool") { else if (mode == "cool") {
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString()) sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString()+"°")
} else if (mode == "auto") { } else if (mode == "auto") {
sendEvent("name":"thermostatSetpoint", "value":"Auto") sendEvent("name":"thermostatSetpoint", "value":"Auto")
} else if (mode == "off") { } else if (mode == "off") {
sendEvent("name":"thermostatSetpoint", "value":"Off") sendEvent("name":"thermostatSetpoint", "value":"Off")
} else if (mode == "emergencyHeat") { } else if (mode == "emergencyHeat") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()) sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()+"°")
} }
} }
void raiseSetpoint() { void raiseSetpoint() {
log.debug "Raise SetPoint"
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
def targetvalue def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
if (mode == "off" || mode == "auto") { log.debug "Current Mode = ${mode}"
log.warn "this mode: $mode does not allow raiseSetpoint"
} else {
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def thermostatSetpoint = device.currentValue("thermostatSetpoint").toInteger()
log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
if (device.latestState('thermostatSetpoint')) { if (mode == "heat") {
targetvalue = device.latestState('thermostatSetpoint').value as Integer
} else {
targetvalue = 0
}
targetvalue = targetvalue + 1
if (mode == "heat" && targetvalue > 79) { heatingSetpoint++
targetvalue = 79
} else if (mode == "cool" && targetvalue > 92) {
targetvalue = 92
}
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false) if (heatingSetpoint > 99)
log.info "In mode $mode raiseSetpoint() to $targetvalue" heatingSetpoint = 99
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()+"°")
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
parent.setHold (this, heatingSetpoint, coolingSetpoint)
log.debug "New Heating Setpoint = ${heatingSetpoint}"
}
else if (mode == "cool") {
coolingSetpoint++
if (coolingSetpoint > 99)
coolingSetpoint = 99
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString()+"°")
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
parent.setHold (this, heatingSetpoint, coolingSetpoint)
log.debug "New Cooling Setpoint = ${coolingSetpoint}"
}
generateStatusEvent()
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
}
} }
//called by tile when user hit raise temperature button on UI
void lowerSetpoint() { void lowerSetpoint() {
def mode = device.currentValue("thermostatMode") log.debug "Lower SetPoint"
def targetvalue
if (mode == "off" || mode == "auto") {
log.warn "this mode: $mode does not allow lowerSetpoint"
} else {
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def thermostatSetpoint = device.currentValue("thermostatSetpoint").toInteger()
log.debug "lowerSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
if (device.latestState('thermostatSetpoint')) {
targetvalue = device.latestState('thermostatSetpoint').value as Integer
} else {
targetvalue = 0
}
targetvalue = targetvalue - 1
if (mode == "heat" && targetvalue.toInteger() < 45) {
targetvalue = 45
} else if (mode == "cool" && targetvalue.toInteger() < 65) {
targetvalue = 65
}
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
log.info "In mode $mode lowerSetpoint() to $targetvalue"
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
}
}
//called by raiseSetpoint() and lowerSetpoint()
void alterSetpoint(temp) {
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger() def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger() def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def deviceId = device.deviceNetworkId.split(/\./).last()
def targetHeatingSetpoint log.debug "Current Mode = ${mode}, Current Heating Setpoint = ${heatingSetpoint}, Current Cooling Setpoint = ${coolingSetpoint}"
def targetCoolingSetpoint
//step1: check thermostatMode, enforce limits before sending request to cloud if (mode == "heat" || mode == "emergencyHeat") {
if (mode == "heat"){
if (temp.value > coolingSetpoint){
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
} else {
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = coolingSetpoint
}
} else if (mode == "cool") {
//enforce limits before sending request to cloud
if (temp.value < heatingSetpoint){
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
} else {
targetHeatingSetpoint = heatingSetpoint
targetCoolingSetpoint = temp.value
}
}
log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to ${targetHeatingSetpoint} " + heatingSetpoint--
"coolingSetpoint to ${targetCoolingSetpoint} with holdType : ${holdType}"
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite" if (heatingSetpoint < 32)
//step2: call parent.setHold to send http request to 3rd party cloud heatingSetpoint = 32
if (parent.setHold(this, targetHeatingSetpoint, targetCoolingSetpoint, deviceId, sendHoldType)) {
sendEvent("name": "thermostatSetpoint", "value": temp.value.toString(), displayed: false) sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()+"°")
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint) sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint)
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}" parent.setHold (this, heatingSetpoint, coolingSetpoint)
} else {
log.error "Error alterSetpoint()" log.debug "New Heating Setpoint = ${heatingSetpoint}"
if (mode == "heat"){
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false) }
} else if (mode == "cool") { else if (mode == "cool") {
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
} coolingSetpoint--
}
generateStatusEvent() if (coolingSetpoint < 32)
coolingSetpoint = 32
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString()+"°")
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
parent.setHold (this, heatingSetpoint, coolingSetpoint)
log.debug "New Cooling Setpoint = ${coolingSetpoint}"
}
generateStatusEvent()
} }
def generateStatusEvent() { def generateStatusEvent() {
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger() def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger() def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def temperature = device.currentValue("temperature").toInteger() def temperature = device.currentValue("temperature").toInteger()
def statusText def statusText
log.debug "Generate Status Event for Mode = ${mode}" log.debug "Generate Status Event for Mode = ${mode}"
log.debug "Temperature = ${temperature}" log.debug "Temperature = ${temperature}"
log.debug "Heating set point = ${heatingSetpoint}" log.debug "Heating set point = ${heatingSetpoint}"
log.debug "Cooling set point = ${coolingSetpoint}" log.debug "Cooling set point = ${coolingSetpoint}"
log.debug "HVAC Mode = ${mode}" log.debug "HVAC Mode = ${mode}"
if (mode == "heat") { if (mode == "heat") {
if (temperature >= heatingSetpoint) if (temperature >= heatingSetpoint)
statusText = "Right Now: Idle" statusText = "Right Now: Idle"
else else
statusText = "Heating to ${heatingSetpoint}° F" statusText = "Heating to ${heatingSetpoint}° F"
} else if (mode == "cool") { } else if (mode == "cool") {
if (temperature <= coolingSetpoint) if (temperature <= coolingSetpoint)
statusText = "Right Now: Idle" statusText = "Right Now: Idle"
else else
statusText = "Cooling to ${coolingSetpoint}° F" statusText = "Cooling to ${coolingSetpoint}° F"
} else if (mode == "auto") { } else if (mode == "auto") {
statusText = "Right Now: Auto" statusText = "Right Now: Auto"
} else if (mode == "off") { } else if (mode == "off") {
statusText = "Right Now: Off" statusText = "Right Now: Off"
} else if (mode == "emergencyHeat") { } else if (mode == "emergencyHeat") {
statusText = "Emergency Heat" statusText = "Emergency Heat"
} else { } else {
statusText = "?" statusText = "?"
} }
log.debug "Generate Status Event = ${statusText}" log.debug "Generate Status Event = ${statusText}"
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true) sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true, isStateChange: true)
} }
//generate custom mobile activity feeds event
def generateActivityFeedsEvent(notificationMessage) {
sendEvent(name: "notificationMessage", value: "$device.displayName $notificationMessage", descriptionText: "$device.displayName $notificationMessage", displayed: true)
}

View File

@@ -14,7 +14,7 @@
* *
*/ */
metadata { metadata {
definition (name: "Harmony Activity", namespace: "smartthings", author: "Juan Risso") { definition (name: "Logitech Harmony Activity", namespace: "smartthings", author: "Juan Risso") {
capability "Switch" capability "Switch"
capability "Actuator" capability "Actuator"
capability "Refresh" capability "Refresh"

View File

@@ -79,8 +79,8 @@ metadata {
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
} }

View File

@@ -88,8 +88,8 @@ metadata {
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
} }

View File

@@ -43,8 +43,8 @@ metadata {
]) ])
} }
section { section {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
} }

View File

@@ -45,8 +45,8 @@ metadata {
]) ])
} }
section { section {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
} }

View File

@@ -37,8 +37,8 @@ metadata {
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
tiles(scale: 2) { tiles(scale: 2) {

View File

@@ -61,8 +61,8 @@
]) ])
} }
section { section {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
section { section {
input("garageSensor", "enum", title: "Do you want to use this sensor on a garage door?", options: ["Yes", "No"], defaultValue: "No", required: false, displayDuringSetup: false) input("garageSensor", "enum", title: "Do you want to use this sensor on a garage door?", options: ["Yes", "No"], defaultValue: "No", required: false, displayDuringSetup: false)
@@ -115,30 +115,31 @@
} }
} }
def parse(String description) { def parse(String description) {
Map map = [:]
if (description?.startsWith('catchall:')) { Map map = [:]
map = parseCatchAllMessage(description) if (description?.startsWith('catchall:')) {
} map = parseCatchAllMessage(description)
}
else if (description?.startsWith('read attr -')) {
map = parseReportAttributeMessage(description)
}
else if (description?.startsWith('temperature: ')) { else if (description?.startsWith('temperature: ')) {
map = parseCustomMessage(description) map = parseCustomMessage(description)
} }
else if (description?.startsWith('zone status')) { else if (description?.startsWith('zone status')) {
map = parseIasMessage(description) map = parseIasMessage(description)
} }
def result = map ? createEvent(map) : null def result = map ? createEvent(map) : null
if (description?.startsWith('enroll request')) { if (description?.startsWith('enroll request')) {
List cmds = enrollResponse() List cmds = enrollResponse()
log.debug "enroll response: ${cmds}" log.debug "enroll response: ${cmds}"
result = cmds?.collect { new physicalgraph.device.HubAction(it) } result = cmds?.collect { new physicalgraph.device.HubAction(it) }
} }
else if (description?.startsWith('read attr -')) { return result
result = parseReportAttributeMessage(description).each { createEvent(it) } }
}
return result
}
private Map parseCatchAllMessage(String description) { private Map parseCatchAllMessage(String description) {
Map resultMap = [:] Map resultMap = [:]
@@ -177,40 +178,28 @@ private boolean shouldProcessMessage(cluster) {
return !ignoredMessage return !ignoredMessage
} }
private List parseReportAttributeMessage(String description) { private Map parseReportAttributeMessage(String description) {
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param -> Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":") def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
} }
List result = [] Map resultMap = [:]
if (descMap.cluster == "0402" && descMap.attrId == "0000") { if (descMap.cluster == "0402" && descMap.attrId == "0000") {
def value = getTemperature(descMap.value) def value = getTemperature(descMap.value)
result << getTemperatureResult(value) resultMap = getTemperatureResult(value)
} }
else if (descMap.cluster == "FC02" && descMap.attrId == "0010") { else if (descMap.cluster == "FC02" && descMap.attrId == "0010") {
if (descMap.value.size() == 32) { resultMap = getAccelerationResult(descMap.value)
// value will look like 00ae29001403e2290013001629001201
// breaking this apart and swapping byte order where appropriate, this breaks down to:
// X (0x0012) = 0x0016
// Y (0x0013) = 0x03E2
// Z (0x0014) = 0x00AE
// note that there is a known bug in that the x,y,z attributes are interpreted in the wrong order
// this will be fixed in a future update
def threeAxisAttributes = descMap.value[0..-9]
result << parseAxis(threeAxisAttributes)
descMap.value = descMap.value[-2..-1]
}
result << getAccelerationResult(descMap.value)
} }
else if (descMap.cluster == "FC02" && descMap.attrId == "0012") { else if (descMap.cluster == "FC02" && descMap.attrId == "0012") {
result << parseAxis(descMap.value) resultMap = parseAxis(descMap.value)
} }
else if (descMap.cluster == "0001" && descMap.attrId == "0020") { else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
result << getBatteryResult(Integer.parseInt(descMap.value, 16)) resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
} }
return result return resultMap
} }
private Map parseCustomMessage(String description) { private Map parseCustomMessage(String description) {

View File

@@ -43,8 +43,8 @@ metadata {
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
tiles(scale: 2) { tiles(scale: 2) {

View File

@@ -32,8 +32,8 @@
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
tiles(scale: 2) { tiles(scale: 2) {

View File

@@ -34,8 +34,8 @@ metadata {
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
tiles(scale: 2) { tiles(scale: 2) {

View File

@@ -33,8 +33,8 @@ metadata {
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
tiles(scale: 2) { tiles(scale: 2) {

View File

@@ -45,8 +45,8 @@ metadata {
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
tiles { tiles {

View File

@@ -33,8 +33,8 @@ metadata {
} }
preferences { preferences {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph" input description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
tiles { tiles {

View File

@@ -33,14 +33,14 @@ metadata {
state "power", label: '${currentValue} W' state "power", label: '${currentValue} W'
} }
htmlTile(name: "powerContent", attribute: "powerContent", type: "HTML", whitelist: "www.wattvision.com" , url: '${currentValue}', width: 3, height: 2) tile(name: "powerChart", attribute: "powerContent", type: "HTML", url: '${currentValue}', width: 3, height: 2) { }
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh" state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
} }
main "power" main "power"
details(["powerContent", "power", "refresh"]) details(["powerChart", "power", "refresh"])
} }
} }
@@ -74,10 +74,10 @@ public addWattvisionData(json) {
log.trace "Adding data from Wattvision" log.trace "Adding data from Wattvision"
def data = parseJson(json.data.toString()) def data = json.data
def units = json.units ?: "watts" def units = json.units ?: "watts"
if (data.size() > 0) { if (data) {
def latestData = data[-1] def latestData = data[-1]
data.each { data.each {
sendPowerEvent(it.t, it.v, units, (latestData == it)) sendPowerEvent(it.t, it.v, units, (latestData == it))
@@ -103,7 +103,3 @@ private sendPowerEvent(time, value, units, isLatest = false) {
sendEvent(eventData) sendEvent(eventData)
} }
def parseJson(String s) {
new groovy.json.JsonSlurper().parseText(s)
}

View File

@@ -25,8 +25,8 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-CIA19NAE26", deviceJoinName: "Sengled Element touch" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-CIA19NAE26", deviceJoinName: "Sengled Element touch"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "000A, 0019", manufacturer: "Jasco Products", model: "45852", deviceJoinName: "GE Zigbee Plug-In Dimmer" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B05,0702", outClusters: "000A,0019", manufacturer: "Jasco Products", model: "45852", deviceJoinName: "GE Zigbee Plug-In Dimmer"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "000A, 0019", manufacturer: "Jasco Products", model: "45857", deviceJoinName: "GE Zigbee In-Wall Dimmer" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B05,0702", outClusters: "000A,0019", manufacturer: "Jasco Products", model: "45857", deviceJoinName: "GE Zigbee In-Wall Dimmer"
} }
tiles(scale: 2) { tiles(scale: 2) {

View File

@@ -17,15 +17,15 @@ metadata {
capability "Actuator" capability "Actuator"
capability "Configuration" capability "Configuration"
capability "Refresh" capability "Refresh"
capability "Sensor"
capability "Switch" capability "Switch"
capability "Switch Level" capability "Switch Level"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "OSRAM LIGHTIFY LED Smart Connected Light" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "OSRAM LIGHTIFY LED Smart Connected Light"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Bulb" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Bulb"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
fingerprint profileId: "0104", inClusters: "0001, 0006, 0008, 000E", outClusters: "0019", manufacturer: "Nanoleaf", model: "IvyBulbs", deviceJoinName: "Nanoleaf Smart Bulbs"
} }
tiles(scale: 2) { tiles(scale: 2) {

View File

@@ -23,8 +23,8 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B05", outClusters: "0003, 000A, 0019", manufacturer: "Jasco Products", model: "45853", deviceJoinName: "GE ZigBee Plug-In Switch" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B05,0702", outClusters: "0003, 000A,0019", manufacturer: "Jasco Products", model: "45853", deviceJoinName: "GE ZigBee Plug-In Switch"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B05", outClusters: "000A, 0019", manufacturer: "Jasco Products", model: "45856", deviceJoinName: "GE ZigBee In-Wall Switch" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B05,0702", outClusters: "000A,0019", manufacturer: "Jasco Products", model: "45856", deviceJoinName: "GE ZigBee In-Wall Switch"
} }
tiles(scale: 2) { tiles(scale: 2) {

View File

@@ -17,6 +17,7 @@ metadata {
capability "Actuator" capability "Actuator"
capability "Configuration" capability "Configuration"
capability "Refresh" capability "Refresh"
capability "Sensor"
capability "Switch" capability "Switch"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006"

View File

@@ -23,18 +23,18 @@ metadata {
capability "Color Temperature" capability "Color Temperature"
capability "Configuration" capability "Configuration"
capability "Refresh" capability "Refresh"
capability "Sensor"
capability "Switch" capability "Switch"
capability "Switch Level" capability "Switch Level"
attribute "colorName", "string" attribute "colorName", "string"
command "setGenericName" command "setGenericName"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04", outClusters: "0019"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04", outClusters: "0019" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Flood BR30 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Flood BR30 Tunable White" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Recessed Kit RT 5/6 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Recessed Kit RT 5/6 Tunable White" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
} }
// UI tile definitions // UI tile definitions

View File

@@ -66,20 +66,9 @@ metadata {
import physicalgraph.zwave.commands.doorlockv1.* import physicalgraph.zwave.commands.doorlockv1.*
import physicalgraph.zwave.commands.usercodev1.* import physicalgraph.zwave.commands.usercodev1.*
def updated() {
try {
if (!state.init) {
state.init = true
response(secureSequence([zwave.doorLockV1.doorLockOperationGet(), zwave.batteryV1.batteryGet()]))
}
} catch (e) {
log.warn "updated() threw $e"
}
}
def parse(String description) { def parse(String description) {
def result = null def result = null
if (description.startsWith("Err 106")) { if (description.startsWith("Err")) {
if (state.sec) { if (state.sec) {
result = createEvent(descriptionText:description, displayed:false) result = createEvent(descriptionText:description, displayed:false)
} else { } else {
@@ -91,8 +80,6 @@ def parse(String description) {
displayed: true, displayed: true,
) )
} }
} else if (description == "updated") {
return null
} else { } else {
def cmd = zwave.parse(description, [ 0x98: 1, 0x72: 2, 0x85: 2, 0x86: 1 ]) def cmd = zwave.parse(description, [ 0x98: 1, 0x72: 2, 0x85: 2, 0x86: 1 ])
if (cmd) { if (cmd) {
@@ -299,7 +286,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
} }
break break
case 167: case 167:
if (!state.lastbatt || now() - state.lastbatt > 12*60*60*1000) { if (!state.lastbatt || (new Date().time) - state.lastbatt > 12*60*60*1000) {
map = [ descriptionText: "$device.displayName: battery low", isStateChange: true ] map = [ descriptionText: "$device.displayName: battery low", isStateChange: true ]
result << response(secure(zwave.batteryV1.batteryGet())) result << response(secure(zwave.batteryV1.batteryGet()))
} else { } else {
@@ -444,7 +431,7 @@ def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
} else { } else {
map.value = cmd.batteryLevel map.value = cmd.batteryLevel
} }
state.lastbatt = now() state.lastbatt = new Date().time
createEvent(map) createEvent(map)
} }
@@ -512,14 +499,15 @@ def refresh() {
cmds << "delay 4200" cmds << "delay 4200"
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format() // old Schlage locks use group 2 and don't secure the Association CC cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format() // old Schlage locks use group 2 and don't secure the Association CC
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1)) cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.associationQuery = now() state.associationQuery = new Date().time
} else if (secondsPast(state.associationQuery, 9)) { } else if (new Date().time - state.associationQuery.toLong() > 9000) {
log.debug "setting association"
cmds << "delay 6000" cmds << "delay 6000"
cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format() cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)) cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format() cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1)) cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.associationQuery = now() state.associationQuery = new Date().time
} }
log.debug "refresh sending ${cmds.inspect()}" log.debug "refresh sending ${cmds.inspect()}"
cmds cmds
@@ -527,22 +515,55 @@ def refresh() {
def poll() { def poll() {
def cmds = [] def cmds = []
// Only check lock state if it changed recently or we haven't had an update in an hour if (state.assoc != zwaveHubNodeId && secondsPast(state.associationQuery, 19 * 60)) {
def latest = device.currentState("lock")?.date?.time log.debug "setting association"
if (!latest || !secondsPast(latest, 6 * 60) || secondsPast(state.lastPoll, 55 * 60)) { cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
cmds << secure(zwave.doorLockV1.doorLockOperationGet()) cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
state.lastPoll = now() cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
} else if (!state.lastbatt || now() - state.lastbatt > 53*60*60*1000) { cmds << "delay 6000"
cmds << secure(zwave.batteryV1.batteryGet()) cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.lastbatt = now() //inside-214 cmds << "delay 6000"
} state.associationQuery = new Date().time
if (cmds) {
log.debug "poll is sending ${cmds.inspect()}"
cmds
} else { } else {
// workaround to keep polling from stopping due to lack of activity // Only check lock state if it changed recently or we haven't had an update in an hour
sendEvent(descriptionText: "skipping poll", isStateChange: true, displayed: false) def latest = device.currentState("lock")?.date?.time
null if (!latest || !secondsPast(latest, 6 * 60) || secondsPast(state.lastPoll, 55 * 60)) {
cmds << secure(zwave.doorLockV1.doorLockOperationGet())
state.lastPoll = (new Date()).time
} else if (!state.MSR) {
cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
} else if (!state.fw) {
cmds << zwave.versionV1.versionGet().format()
} else if (!state.codes) {
state.pollCode = 1
cmds << secure(zwave.userCodeV1.usersNumberGet())
} else if (state.pollCode && state.pollCode <= state.codes) {
cmds << requestCode(state.pollCode)
} else if (!state.lastbatt || (new Date().time) - state.lastbatt > 53*60*60*1000) {
cmds << secure(zwave.batteryV1.batteryGet())
} else if (!state.enc) {
encryptCodes()
state.enc = 1
}
}
log.debug "poll is sending ${cmds.inspect()}"
device.activity()
cmds ?: null
}
private def encryptCodes() {
def keys = new ArrayList(state.keySet().findAll { it.startsWith("code") })
keys.each { key ->
def match = (key =~ /^code(\d+)$/)
if (match) try {
def keynum = match[0][1].toInteger()
if (keynum > 30 && !state[key]) {
state.remove(key)
} else if (state[key] && !state[key].startsWith("~")) {
log.debug "encrypting $key: ${state[key].inspect()}"
state[key] = encrypt(state[key])
}
} catch (java.lang.NumberFormatException e) { }
} }
} }
@@ -651,7 +672,7 @@ private Boolean secondsPast(timestamp, seconds) {
return true return true
} }
} }
return (now() - timestamp) > (seconds * 1000) return (new Date().time - timestamp) > (seconds * 1000)
} }
private allCodesDeleted() { private allCodesDeleted() {

View File

@@ -115,10 +115,6 @@ def strobe() {
] ]
} }
def both() {
on()
}
def refresh() { def refresh() {
log.debug "sending battery refresh command" log.debug "sending battery refresh command"
zwave.batteryV1.batteryGet().format() zwave.batteryV1.batteryGet().format()

View File

@@ -246,9 +246,6 @@ def toggle(devices) {
else if (devices*.currentValue('lock').contains('locked')) { else if (devices*.currentValue('lock').contains('locked')) {
devices.unlock() devices.unlock()
} }
else if (devices*.currentValue('lock').contains('unlocked')) {
devices.lock()
}
else if (devices*.currentValue('alarm').contains('off')) { else if (devices*.currentValue('alarm').contains('off')) {
devices.siren() devices.siren()
} }

File diff suppressed because it is too large Load Diff

View File

@@ -592,7 +592,7 @@ def updated() {
// log.debug "External Id=${app.id}:${member.id}" // log.debug "External Id=${app.id}:${member.id}"
// create the device // create the device
def childDevice = addChildDevice("smartthings", "Life360 User", "${app.id}.${member.id}",null,[name:member.firstName, completedSetup: true]) def childDevice = addChildDevice("smartthings", "life360-user", "${app.id}.${member.id}",null,[name:member.firstName, completedSetup: true])
// childDevice.setMemberId(member.id) // childDevice.setMemberId(member.id)
if (childDevice) if (childDevice)

View File

@@ -49,7 +49,6 @@ preferences {
section("Via a push notification and/or an SMS message"){ section("Via a push notification and/or an SMS message"){
input("recipients", "contact", title: "Send notifications to") { input("recipients", "contact", title: "Send notifications to") {
input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false
paragraph "If outside the US please make sure to enter the proper country code"
input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes", "No"] input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes", "No"]
} }
} }

View File

@@ -78,7 +78,7 @@ def firstPage()
def motionsDiscovered = motionsDiscovered() def motionsDiscovered = motionsDiscovered()
def lightSwitchesDiscovered = lightSwitchesDiscovered() def lightSwitchesDiscovered = lightSwitchesDiscovered()
return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: true) { return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: selectedSwitches != null || selectedMotions != null || selectedLightSwitches != null) {
section("Select a device...") { section("Select a device...") {
input "selectedSwitches", "enum", required:false, title:"Select Wemo Switches \n(${switchesDiscovered.size() ?: 0} found)", multiple:true, options:switchesDiscovered input "selectedSwitches", "enum", required:false, title:"Select Wemo Switches \n(${switchesDiscovered.size() ?: 0} found)", multiple:true, options:switchesDiscovered
input "selectedMotions", "enum", required:false, title:"Select Wemo Motions \n(${motionsDiscovered.size() ?: 0} found)", multiple:true, options:motionsDiscovered input "selectedMotions", "enum", required:false, title:"Select Wemo Motions \n(${motionsDiscovered.size() ?: 0} found)", multiple:true, options:motionsDiscovered