Compare commits

..

1 Commits

Author SHA1 Message Date
Vinay Rao
728b169a08 Merge pull request #2143 from SmartThingsCommunity/staging
Rolling up staging to production
2017-07-05 14:16:16 -07:00
11 changed files with 89 additions and 324 deletions

View File

@@ -1,183 +0,0 @@
/**
* Z-Wave Garage Door Opener
*
* Copyright 2014 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Telguard GDC1", namespace: "fortrezz", author: "FortrezZ, LLC") {
capability "Actuator"
capability "Door Control"
capability "Garage Door Control"
capability "Contact Sensor"
capability "Refresh"
capability "Sensor"
capability "Switch"
fingerprint deviceId: "0x1000", inClusters: "0x72, 0x86, 0x25"
}
simulator {
status "closed": "command: 9881, payload: 00 66 03 00"
status "opening": "command: 9881, payload: 00 66 03 FE"
status "open": "command: 9881, payload: 00 66 03 FF"
status "closing": "command: 9881, payload: 00 66 03 FC"
status "unknown": "command: 9881, payload: 00 66 03 FD"
reply "988100660100": "command: 9881, payload: 00 66 03 FC"
reply "9881006601FF": "command: 9881, payload: 00 66 03 FE"
}
tiles {
standardTile("toggle", "device.door", width: 2, height: 2) {
state("unknown", label:'${name}', action:"refresh.refresh", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e")
state("closed", label:'${name}', action:"door control.open", icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821", nextState:"opening")
state("open", label:'${name}', action:"door control.close", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e", nextState:"closing")
state("opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
state("closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
}
standardTile("open", "device.door", inactiveLabel: false, decoration: "flat") {
state "default", label:'open', action:"door control.open", icon:"st.doors.garage.garage-opening"
}
standardTile("close", "device.door", inactiveLabel: false, decoration: "flat") {
state "default", label:'close', action:"door control.close", icon:"st.doors.garage.garage-closing"
}
standardTile("refresh", "device.door", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
state "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ff0000"
}
main "toggle"
details(["toggle", "open", "close", "refresh"])
}
}
def parse(String description) {
def results = []
log.debug(description)
if (description.startsWith("Err")) {
results << createEvent(descriptionText:description, displayed:true)
} else {
def cmd = zwave.parse(description)
if (cmd) {
results = zwaveEvent(cmd)
}
}
log.debug "\"$description\" parsed to ${results.inspect()}"
return results
}
def open() {
sendEvent(name: "door", value: "opening", descriptionText: "$device.displayName: opening")
delayBetween([
zwave.basicV1.basicSet(value: 0xFF).format(),
zwave.basicV1.basicGet().format()
], 15000)
}
def close() {
sendEvent(name: "door", value: "closing", descriptionText: "$device.displayName: closing")
zwave.basicV1.basicSet(value: 0x00).format()
}
def on() {
open()
}
def off() {
close()
}
def refresh() {
zwave.basicV1.basicGet().format()
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
def map = [name: "door"]
def map2 = [name: "switch"]
if (cmd.value == 0xFF)
{
map.value = "open"
map2.value = "on"
sendEvent(map2)
}
else if (cmd.value == 0x00)
{
map.value = "closed"
map2.value = "off"
sendEvent(map2)
}
else
{
map.value = "unknown"
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
def map = [name: "door"]
def map2 = [name: "switch"]
if (cmd.value == 0xFF)
{
map.value = "open"
map2.value = "on"
sendEvent(map2)
}
else if (cmd.value == 0x00)
{
map.value = "closed"
map2.value = "off"
sendEvent(map2)
}
else
{
map.value = "unknown"
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def result = []
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
log.debug "msr: $msr"
updateDataValue("MSR", msr)
result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)
result
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
def fw = "${cmd.applicationVersion}.${cmd.applicationSubVersion}"
updateDataValue("fw", fw)
def text = "$device.displayName: firmware version: $fw, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
createEvent(descriptionText: text, isStateChange: false)
}
def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationBusy cmd) {
def msg = cmd.status == 0 ? "try again later" :
cmd.status == 1 ? "try again in $cmd.waitTime seconds" :
cmd.status == 2 ? "request queued" : "sorry"
createEvent(displayed: true, descriptionText: "$device.displayName is busy, $msg")
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
createEvent(displayed: false, descriptionText: "$device.displayName: $cmd")
}

View File

@@ -1,5 +1,5 @@
/** /**
* Spruce Sensor -updated with SLP model number 5/2017 * Spruce Sensor -Pre-release V2 10/8/2015
* *
* Copyright 2014 Plaid Systems * Copyright 2014 Plaid Systems
* *
@@ -14,33 +14,25 @@
* *
-------10/20/2015 Updates-------- -------10/20/2015 Updates--------
-Fix/add battery reporting interval to update -Fix/add battery reporting interval to update
-remove polling and/or refresh -remove polling and/or refresh(?)
-------5/2017 Updates--------
-Add fingerprints for SLP
-add device health, check every 60mins + 2mins
*/ */
metadata { metadata {
definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "Plaid Systems") { definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "NCauffman") {
capability "Configuration" capability "Configuration"
capability "Battery" capability "Battery"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Sensor" capability "Sensor"
capability "Health Check"
//capability "Polling" //capability "Polling"
attribute "maxHum", "string" attribute "maxHum", "string"
attribute "minHum", "string" attribute "minHum", "string"
command "resetHumidity" command "resetHumidity"
command "refresh" command "refresh"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01", deviceJoinName: "Spruce Sensor" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP1", deviceJoinName: "Spruce Sensor"
} }
preferences { preferences {
@@ -301,11 +293,6 @@ def setConfig(){
sendEvent(name: 'configuration',value: configInterval, descriptionText: "Configuration initialized") sendEvent(name: 'configuration',value: configInterval, descriptionText: "Configuration initialized")
} }
def installed(){
//check every 1 hour + 2mins
sendEvent(name: "checkInterval", value: 1 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
//when device preferences are changed //when device preferences are changed
def updated(){ def updated(){
log.debug "device updated" log.debug "device updated"
@@ -316,8 +303,6 @@ def updated(){
sendEvent(name: 'configuration',value: 0, descriptionText: "Settings changed and will update at next report. Measure interval set to ${interval} mins") sendEvent(name: 'configuration',value: 0, descriptionText: "Settings changed and will update at next report. Measure interval set to ${interval} mins")
} }
} }
//check every 1 hour + 2mins
sendEvent(name: "checkInterval", value: 1 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
} }
//poll //poll
@@ -410,4 +395,4 @@ private byte[] reverseArray(byte[] array) {
i++; i++;
} }
return array return array
} }

View File

@@ -27,9 +27,13 @@ Works with:
## Device Health ## Device Health
Aeon Labs MultiSensor 6 is polled by the hub. Aeon Labs MultiSensor 6 is polled by the hub.
Aeon MultiSensor reports in once every hour. 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.
* __122min__ checkInterval * __32min__ checkInterval
## Troubleshooting ## Troubleshooting

View File

@@ -130,13 +130,13 @@ metadata {
} }
def installed(){ def installed(){
// Device-Watch simply pings if no device events received for 122min(checkInterval) // Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
} }
def updated() { def updated() {
// Device-Watch simply pings if no device events received for 122min(checkInterval) // Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
log.debug "Updated with settings: ${settings}" log.debug "Updated with settings: ${settings}"
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}" log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"

View File

@@ -105,8 +105,6 @@ def parse(String description) {
} else { } else {
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}" log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
} }
} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) {
map = translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value)))
} }
} }
} else if (map.name == "temperature") { } else if (map.name == "temperature") {
@@ -131,10 +129,6 @@ def parse(String description) {
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) ZoneStatus zs = zigbee.parseZoneStatus(description)
translateZoneStatus(zs)
}
private Map translateZoneStatus(ZoneStatus zs) {
return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry') return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry')
} }
@@ -203,8 +197,7 @@ def ping() {
def refresh() { def refresh() {
log.debug "Refreshing Temperature and Battery" log.debug "Refreshing Temperature and Battery"
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
return refreshCmds + zigbee.enrollResponse() return refreshCmds + zigbee.enrollResponse()
} }

View File

@@ -111,8 +111,6 @@ def parse(String description) {
def value = descMap.value.endsWith("01") ? "active" : "inactive" def value = descMap.value.endsWith("01") ? "active" : "inactive"
log.debug "Doing a read attr motion event" log.debug "Doing a read attr motion event"
map = getMotionResult(value) map = getMotionResult(value)
} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) {
map = translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value)))
} }
} }
} else if (map.name == "temperature") { } else if (map.name == "temperature") {
@@ -137,10 +135,6 @@ def parse(String description) {
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) ZoneStatus zs = zigbee.parseZoneStatus(description)
translateZoneStatus(zs)
}
private Map translateZoneStatus(ZoneStatus zs) {
// Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion // Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion
return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive') return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive')
} }
@@ -171,8 +165,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts] def pct = batteryMap[volts]
result.value = pct result.value = pct
} else { } else {
def minVolts = 2.4 def minVolts = 2.1
def maxVolts = 2.7 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0) if (roundedPct <= 0)
@@ -206,8 +200,7 @@ def refresh() {
log.debug "refresh called" log.debug "refresh called"
def refreshCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + def refreshCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000)
zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
return refreshCmds + zigbee.enrollResponse() return refreshCmds + zigbee.enrollResponse()
} }

View File

@@ -134,9 +134,8 @@ def parse(String description) {
} else { } else {
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}" log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
} }
} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) {
maps += translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value)))
} else { } else {
maps += handleAcceleration(descMap) maps += handleAcceleration(descMap)
} }
} }
@@ -230,11 +229,6 @@ private List<Map> parseAxis(List<Map> attrData) {
private List<Map> parseIasMessage(String description) { private List<Map> parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) ZoneStatus zs = zigbee.parseZoneStatus(description)
translateZoneStatus(zs)
}
private List<Map> translateZoneStatus(ZoneStatus zs) {
List<Map> results = [] List<Map> results = []
if (garageSensor != "Yes") { if (garageSensor != "Yes") {
@@ -274,7 +268,7 @@ private Map getBatteryResult(rawValue) {
result.value = pct result.value = pct
} else { } else {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 2.7 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0) if (roundedPct <= 0)
@@ -319,7 +313,7 @@ def refresh() {
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode]) + zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode]) +
zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + zigbee.enrollResponse() zigbee.enrollResponse()
return refreshCmds return refreshCmds
} }

View File

@@ -15,9 +15,10 @@ metadata {
definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") { definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") {
capability "Actuator" capability "Actuator"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Thermostat" capability "Thermostat"
capability "Configuration" capability "Configuration"
capability "Refresh" capability "Polling"
capability "Sensor" capability "Sensor"
capability "Health Check" capability "Health Check"
@@ -116,7 +117,7 @@ metadata {
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff" state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
} }
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh" state "default", action:"polling.poll", icon:"st.secondary.refresh"
} }
main "temperature" main "temperature"
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"]) details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"])
@@ -124,20 +125,13 @@ metadata {
} }
def installed(){ def installed(){
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())) // Device-Watch simply pings if no device events received for 32min(checkInterval)
initialize() sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
} }
def updated(){ def updated(){
initialize()
}
def initialize() {
// Device-Watch simply pings if no device events received for 32min(checkInterval) // Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
unschedule()
runEvery5Minutes("refresh")
refresh()
} }
def parse(String description) def parse(String description)
@@ -155,7 +149,6 @@ def parse(String description)
] ]
if (map.name == "thermostatMode") { if (map.name == "thermostatMode") {
state.lastTriedMode = map.value state.lastTriedMode = map.value
map.data = [supportedThermostatModes:state.supportedThermostatModes]
if (map.value == "cool") { if (map.value == "cool") {
map2.value = device.latestValue("coolingSetpoint") map2.value = device.latestValue("coolingSetpoint")
log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}" log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}"
@@ -179,7 +172,6 @@ def parse(String description)
} }
} else if (map.name == "thermostatFanMode" && map.isStateChange) { } else if (map.name == "thermostatFanMode" && map.isStateChange) {
state.lastTriedFanMode = map.value state.lastTriedFanMode = map.value
map.data = [supportedThermostatFanModes: state.supportedThermostatFanModes]
} }
log.debug "Parse returned $result" log.debug "Parse returned $result"
result result
@@ -313,26 +305,26 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod
} }
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
def supportedModes = [] def supportedModes = ""
if(cmd.off) { supportedModes << "off" } if(cmd.off) { supportedModes += "off " }
if(cmd.heat) { supportedModes << "heat" } if(cmd.heat) { supportedModes += "heat " }
if(cmd.cool) { supportedModes << "cool" } if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
if(cmd.auto) { supportedModes << "auto" } if(cmd.cool) { supportedModes += "cool " }
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" } if(cmd.auto) { supportedModes += "auto " }
state.supportedThermostatModes = supportedModes state.supportedModes = supportedModes
sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false) // No events to be generated, return empty map
return [:] return [:]
} }
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
def supportedFanModes = [] def supportedFanModes = ""
if(cmd.auto) { supportedFanModes << "auto" } // "fanAuto " if(cmd.auto) { supportedFanModes += "auto " } // "fanAuto "
if(cmd.circulation) { supportedFanModes << "circulate" } // "fanCirculate" if(cmd.low) { supportedFanModes += "on " } // "fanOn"
if(cmd.low) { supportedFanModes << "on" } // "fanOn" if(cmd.circulation) { supportedFanModes += "circulate " } // "fanCirculate"
state.supportedThermostatFanModes = supportedFanModes state.supportedFanModes = supportedFanModes
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false) // No events to be generated, return empty map
return [:] return [:]
} }
@@ -345,17 +337,15 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
} }
// Command Implementations // Command Implementations
def refresh() { def poll() {
def cmds = [] delayBetween([
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()) zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format()) zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format()) zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format()) zwave.thermostatModeV2.thermostatModeGet().format(),
cmds << new physicalgraph.device.HubAction(zwave.sensorMultilevelV2.sensorMultilevelGet().format()) // current temperature zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()) zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()) ], 2300)
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
sendHubCommand(cmds)
} }
def quickSetHeat(degrees) { def quickSetHeat(degrees) {
@@ -426,14 +416,28 @@ def ping() {
poll() poll()
} }
def configure() {
delayBetween([
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format(),
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
zwave.thermostatModeV2.thermostatModeGet().format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
], 2300)
}
def modes() { def modes() {
return state.supportedThermostatModes ["off", "heat", "cool", "auto", "emergency heat"]
} }
def switchMode() { def switchMode() {
def currentMode = device.currentState("thermostatMode")?.value def currentMode = device.currentState("thermostatMode")?.value
def lastTriedMode = state.lastTriedMode ?: currentMode ?: ["off"] def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
def supportedModes = getDataByName("supportedThermostatModes") def supportedModes = getDataByName("supportedModes")
def modeOrder = modes() def modeOrder = modes()
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode) def nextMode = next(lastTriedMode)
@@ -450,7 +454,7 @@ def switchMode() {
} }
def switchToMode(nextMode) { def switchToMode(nextMode) {
def supportedModes = getDataByName("supportedThermostatModes") def supportedModes = getDataByName("supportedModes")
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
if (nextMode in modes()) { if (nextMode in modes()) {
state.lastTriedMode = nextMode state.lastTriedMode = nextMode
@@ -462,9 +466,9 @@ def switchToMode(nextMode) {
def switchFanMode() { def switchFanMode() {
def currentMode = device.currentState("thermostatFanMode")?.value def currentMode = device.currentState("thermostatFanMode")?.value
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: ["off"] def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off"
def supportedModes = getDataByName("supportedThermostatFanModes") ?: ["auto", "on"] def supportedModes = getDataByName("supportedFanModes") ?: "auto on" // "fanAuto fanOn"
def modeOrder = state.supportedThermostatFanModes def modeOrder = ["auto", "circulate", "on"] // "fanAuto", "fanCirculate", "fanOn"
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode) def nextMode = next(lastTriedMode)
while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto" while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto"
@@ -474,7 +478,7 @@ def switchFanMode() {
} }
def switchToFanMode(nextMode) { def switchToFanMode(nextMode) {
def supportedFanModes = getDataByName("supportedThermostatFanModes") def supportedFanModes = getDataByName("supportedFanModes")
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
def returnCommand def returnCommand

View File

@@ -27,9 +27,10 @@ definition(
preferences { preferences {
page(name: "selectButton") page(name: "selectButton")
for (def i=1; i<=8; i++) { page(name: "configureButton1")
page(name: "configureButton$i") page(name: "configureButton2")
} page(name: "configureButton3")
page(name: "configureButton4")
page(name: "timeIntervalInput", title: "Only during a certain time") { page(name: "timeIntervalInput", title: "Only during a certain time") {
section { section {
@@ -59,45 +60,22 @@ def selectButton() {
} }
} }
def createPage(pageNum) {
if ((state.numButton == pageNum) || (pageNum == 8))
state.installCondition = true
dynamicPage(name: "configureButton$pageNum", title: "Set up button $pageNum here",
nextPage: "configureButton${pageNum+1}", install: state.installCondition, uninstall: configured(), getButtonSections(pageNum))
}
def configureButton1() { def configureButton1() {
state.numButton = buttonDevice.currentState("numberOfButtons")?.longValue ?: 4 dynamicPage(name: "configureButton1", title: "Now let's decide how to use the first button",
log.debug "state variable numButton: ${state.numButton}" nextPage: "configureButton2", uninstall: configured(), getButtonSections(1))
state.installCondition = false
createPage(1)
} }
def configureButton2() { def configureButton2() {
createPage(2) dynamicPage(name: "configureButton2", title: "If you have a second button, set it up here",
nextPage: "configureButton3", uninstall: configured(), getButtonSections(2))
} }
def configureButton3() { def configureButton3() {
createPage(3) dynamicPage(name: "configureButton3", title: "If you have a third button, you can do even more here",
nextPage: "configureButton4", uninstall: configured(), getButtonSections(3))
} }
def configureButton4() { def configureButton4() {
createPage(4) dynamicPage(name: "configureButton4", title: "If you have a fourth button, you rule, and can set it up here",
} install: true, uninstall: true, getButtonSections(4))
def configureButton5() {
createPage(5)
}
def configureButton6() {
createPage(6)
}
def configureButton7() {
createPage(7)
}
def configureButton8() {
createPage(8)
} }
def getButtonSections(buttonNumber) { def getButtonSections(buttonNumber) {

View File

@@ -202,8 +202,7 @@ def inputSelectionPage() {
section("options variations") { section("options variations") {
paragraph "tap these elements and look at the differences when selecting an option" paragraph "tap these elements and look at the differences when selecting an option"
input(type: "enum", name: "selectionSimple", title: "Simple options", description: "no separators in the selectable options", options: ["Thing 1", "Thing 2", "(Complicated) Thing 3"]) input(type: "enum", name: "selectionSimple", title: "Simple options", description: "no separators in the selectable options", groupedOptions: addGroup(englishOptions + spanishOptions))
input(type: "enum", name: "selectionSimpleGrouped", title: "Simple (Grouped) options", description: "no separators in the selectable options", groupedOptions: addGroup(englishOptions + spanishOptions))
input(type: "enum", name: "selectionGrouped", title: "Grouped options", description: "separate groups of options with headers", groupedOptions: groupedOptions) input(type: "enum", name: "selectionGrouped", title: "Grouped options", description: "separate groups of options with headers", groupedOptions: groupedOptions)
} }
@@ -215,15 +214,15 @@ def inputSelectionPage() {
section("segmented") { section("segmented") {
paragraph "segmented should only work if there are either 2 or 3 options to choose from" paragraph "segmented should only work if there are either 2 or 3 options to choose from"
input(type: "enum", name: "selectionSegmented1", style: "segmented", title: "1 option", options: ["One"]) input(type: "enum", name: "selectionSegmented1", style: "segmented", title: "1 option", groupedOptions: addGroup(["One"]))
input(type: "enum", name: "selectionSegmented4", style: "segmented", title: "4 options", options: ["One", "Two", "Three", "Four"]) input(type: "enum", name: "selectionSegmented4", style: "segmented", title: "4 options", groupedOptions: addGroup(["One", "Two", "Three", "Four"]))
paragraph "multiple and required will have no effect on segmented selection elements. There will always be exactly 1 option selected" paragraph "multiple and required will have no effect on segmented selection elements. There will always be exactly 1 option selected"
input(type: "enum", name: "selectionSegmented2", style: "segmented", title: "2 options", options: ["One", "Two"]) input(type: "enum", name: "selectionSegmented2", style: "segmented", title: "2 options", options: ["One", "Two"])
input(type: "enum", name: "selectionSegmented3", style: "segmented", title: "3 options", options: ["One", "Two", "Three"]) input(type: "enum", name: "selectionSegmented3", style: "segmented", title: "3 options", options: ["One", "Two", "Three"])
paragraph "specifying defaultValue still works with segmented selection elements" paragraph "specifying defaultValue still works with segmented selection elements"
input(type: "enum", name: "selectionSegmentedWithDefault", style: "segmented", title: "defaulted to 'two'", options: ["One", "Two", "Three"], defaultValue: "Two") input(type: "enum", name: "selectionSegmentedWithDefault", title: "defaulted to 'two'", groupedOptions: addGroup(["One", "Two", "Three"]), defaultValue: "Two")
} }
section("required: true") { section("required: true") {
@@ -232,8 +231,6 @@ def inputSelectionPage() {
section("multiple: true") { section("multiple: true") {
input(type: "enum", name: "selectionMultiple", title: "This allows multiple selections", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true) input(type: "enum", name: "selectionMultiple", title: "This allows multiple selections", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true)
input(type: "enum", name: "selectionMultipleDefault1", title: "This allows multiple selections with a single default", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true, defaultValue: "an option")
input(type: "enum", name: "selectionMultipleDefault2", title: "This allows multiple selections with multiple defaults", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true, defaultValue: ["an option", "another option"])
} }
section("with image") { section("with image") {

View File

@@ -72,7 +72,7 @@ def authPage() {
log.debug "have LIFX access token" log.debug "have LIFX access token"
def options = locationOptions() ?: [] def options = locationOptions() ?: []
def count = options.size().toString() def count = options.size()
return dynamicPage(name:"Credentials", title:"", nextPage:"", install:true, uninstall: true) { return dynamicPage(name:"Credentials", title:"", nextPage:"", install:true, uninstall: true) {
section("Select your location") { section("Select your location") {