Compare commits

..

2 Commits

Author SHA1 Message Date
Vinay Rao
6996a07969 Merge pull request #2153 from SmartThingsCommunity/staging
Rolling up staging to production
2017-07-11 13:59:29 -07:00
Vinay Rao
728b169a08 Merge pull request #2143 from SmartThingsCommunity/staging
Rolling up staging to production
2017-07-05 14:16:16 -07:00
9 changed files with 73 additions and 522 deletions

View File

@@ -1,439 +0,0 @@
/**
* FortrezZ Flow Meter Interface
*
* Copyright 2016 FortrezZ, LLC
*
* 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: "FortrezZ Flow Meter Interface", namespace: "fortrezz", author: "Daniel Kurin") {
capability "Battery"
capability "Energy Meter"
capability "Image Capture"
capability "Temperature Measurement"
capability "Sensor"
capability "Water Sensor"
attribute "gpm", "number"
attribute "cumulative", "number"
attribute "alarmState", "string"
attribute "chartMode", "string"
attribute "lastThreshhold", "number"
command "chartMode"
command "zero"
command "setHighFlowLevel", ["number"]
fingerprint deviceId: "0x2101", inClusters: "0x5E, 0x86, 0x72, 0x5A, 0x73, 0x71, 0x85, 0x59, 0x32, 0x31, 0x70, 0x80, 0x7A"
}
simulator {
// TODO: define status and reply messages here
}
preferences {
input "gallonThreshhold", "decimal", title: "High Flow Rate Threshhold", description: "Flow rate (in gpm) that will trigger a notification.", defaultValue: 5, required: false, displayDuringSetup: true
input("registerEmail", type: "email", required: false, title: "Email Address", description: "Register your device with FortrezZ", displayDuringSetup: true)
}
tiles(scale: 2) {
carouselTile("flowHistory", "device.image", width: 6, height: 3) { }
valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2) {
state "battery", label:'${currentValue}%\nBattery', unit:""
}
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°',
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
)
}
valueTile("gpm", "device.gpm", inactiveLabel: false, width: 2, height: 2) {
state "gpm", label:'${currentValue}gpm', unit:""
}
standardTile("powerState", "device.powerState", width: 2, height: 2) {
state "reconnected", icon:"http://swiftlet.technology/wp-content/uploads/2016/02/Connected-64.png", backgroundColor:"#cccccc"
state "disconnected", icon:"http://swiftlet.technology/wp-content/uploads/2016/02/Disconnected-64.png", backgroundColor:"#cc0000"
state "batteryReplaced", icon:"http://swiftlet.technology/wp-content/uploads/2016/04/Full-Battery-96.png", backgroundColor:"#cccccc"
state "noBattery", icon:"http://swiftlet.technology/wp-content/uploads/2016/04/No-Battery-96.png", backgroundColor:"#cc0000"
}
standardTile("waterState", "device.waterState", width: 2, height: 2, canChangeIcon: true) {
state "none", icon:"http://cdn.device-icons.smartthings.com/Weather/weather12-icn@2x.png", backgroundColor:"#cccccc", label: "No Flow"
state "flow", icon:"http://cdn.device-icons.smartthings.com/Weather/weather12-icn@2x.png", backgroundColor:"#53a7c0", label: "Flow"
state "overflow", icon:"http://cdn.device-icons.smartthings.com/Weather/weather12-icn@2x.png", backgroundColor:"#cc0000", label: "High Flow"
}
standardTile("heatState", "device.heatState", width: 2, height: 2) {
state "normal", label:'Normal', icon:"st.alarm.temperature.normal", backgroundColor:"#ffffff"
state "freezing", label:'Freezing', icon:"st.alarm.temperature.freeze", backgroundColor:"#2eb82e"
state "overheated", label:'Overheated', icon:"st.alarm.temperature.overheat", backgroundColor:"#F80000"
}
standardTile("take1", "device.image", width: 2, height: 2, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false, decoration: "flat") {
state "take", label: "", action: "Image Capture.take", nextState:"taking", icon: "st.secondary.refresh"
}
standardTile("chartMode", "device.chartMode", width: 2, height: 2, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
state "day", label:'24 Hours\n(press to change)', nextState: "week", action: 'chartMode'
state "week", label:'7 Days\n(press to change)', nextState: "month", action: 'chartMode'
state "month", label:'4 Weeks\n(press to change)', nextState: "day", action: 'chartMode'
}
valueTile("zeroTile", "device.zero", width: 2, height: 2, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
state "zero", label:'Zero', action: 'zero'
}
main (["waterState"])
details(["flowHistory", "chartMode", "take1", "temperature", "gpm", "waterState", "battery"])
}
}
// parse events into attributes
def parse(String description) {
def results = []
if (description.startsWith("Err")) {
results << createEvent(descriptionText:description, displayed:true)
} else {
def cmd = zwave.parse(description, [ 0x80: 1, 0x84: 1, 0x71: 2, 0x72: 1 ])
if (cmd) {
results << createEvent( zwaveEvent(cmd) )
}
}
//log.debug "\"$description\" parsed to ${results.inspect()}"
if(gallonThreshhold != device.currentValue("lastThreshhold"))
{
results << setThreshhold(gallonThreshhold)
}
log.debug "zwave parsed to ${results.inspect()}"
return results
}
def updated()
{
log.debug("Updated")
}
def setHighFlowLevel(level)
{
setThreshhold(level)
}
def take() {
def mode = device.currentValue("chartMode")
if(mode == "day")
{
take1()
}
else if(mode == "week")
{
take7()
}
else if(mode == "month")
{
take28()
}
}
def chartMode(string) {
log.debug("ChartMode")
def state = device.currentValue("chartMode")
def tempValue = ""
switch(state)
{
case "day":
tempValue = "week"
break
case "week":
tempValue = "month"
break
case "month":
tempValue = "day"
break
default:
tempValue = "day"
break
}
sendEvent(name: "chartMode", value: tempValue)
take()
}
def take1() {
api("24hrs", "") {
log.debug("Image captured")
if(it.headers.'Content-Type'.contains("image/png")) {
if(it.data) {
storeImage(getPictureName("24hrs"), it.data)
}
}
}
}
def take7() {
api("7days", "") {
log.debug("Image captured")
if(it.headers.'Content-Type'.contains("image/png")) {
if(it.data) {
storeImage(getPictureName("7days"), it.data)
}
}
}
}
def take28() {
api("4weeks", "") {
log.debug("Image captured")
if(it.headers.'Content-Type'.contains("image/png")) {
if(it.data) {
storeImage(getPictureName("4weeks"), it.data)
}
}
}
}
def zero()
{
delayBetween([
zwave.meterV3.meterReset().format(),
zwave.meterV3.meterGet().format(),
zwave.firmwareUpdateMdV2.firmwareMdGet().format(),
], 100)
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd)
{
log.debug cmd
def map = [:]
if(cmd.sensorType == 1) {
map = [name: "temperature"]
if(cmd.scale == 0) {
map.value = getTemperature(cmd.scaledSensorValue)
} else {
map.value = cmd.scaledSensorValue
}
map.unit = location.temperatureScale
} /* else if(cmd.sensorType == 2) {
map = [name: "waterState"]
if(cmd.sensorValue[0] == 0x80) {
map.value = "flow"
sendEvent(name: "water", value: "dry")
} else if(cmd.sensorValue[0] == 0x00) {
map.value = "none"
sendEvent(name: "water", value: "dry")
} else if(cmd.sensorValue[0] == 0xFF) {
map.value = "overflow"
sendEvent(name: "water", value: "wet")
sendAlarm("waterOverflow")
}
} */
return map
}
def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd)
{
def map = [:]
map.name = "gpm"
def delta = cmd.scaledMeterValue - cmd.scaledPreviousMeterValue
if (delta < 0 || delta > 10000) {
log.error(cmd)
delta = 0
}
map.value = delta
map.unit = "gpm"
sendDataToCloud(delta)
sendEvent(name: "cumulative", value: cmd.scaledMeterValue, displayed: false, unit: "gal")
return map
}
def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd)
{
def map = [:]
if (cmd.zwaveAlarmType == 8) // Power Alarm
{
map.name = "powerState" // For Tile (shows in "Recently")
if (cmd.zwaveAlarmEvent == 2) // AC Mains Disconnected
{
map.value = "disconnected"
sendAlarm("acMainsDisconnected")
}
else if (cmd.zwaveAlarmEvent == 3) // AC Mains Reconnected
{
map.value = "reconnected"
sendAlarm("acMainsReconnected")
}
else if (cmd.zwaveAlarmEvent == 0x0B) // Replace Battery Now
{
map.value = "noBattery"
sendAlarm("replaceBatteryNow")
}
else if (cmd.zwaveAlarmEvent == 0x00) // Battery Replaced
{
map.value = "batteryReplaced"
sendAlarm("batteryReplaced")
}
}
else if (cmd.zwaveAlarmType == 4) // Heat Alarm
{
map.name = "heatState"
if (cmd.zwaveAlarmEvent == 0) // Normal
{
map.value = "normal"
}
else if (cmd.zwaveAlarmEvent == 1) // Overheat
{
map.value = "overheated"
sendAlarm("tempOverheated")
}
else if (cmd.zwaveAlarmEvent == 5) // Underheat
{
map.value = "freezing"
sendAlarm("tempFreezing")
}
}
else if (cmd.zwaveAlarmType == 5) // Water Alarm
{
map.name = "waterState"
if (cmd.zwaveAlarmEvent == 0) // Normal
{
map.value = "none"
sendEvent(name: "water", value: "dry")
}
else if (cmd.zwaveAlarmEvent == 6) // Flow Detected
{
if(cmd.eventParameter[0] == 2)
{
map.value = "flow"
sendEvent(name: "water", value: "dry")
}
else if(cmd.eventParameter[0] == 3)
{
map.value = "overflow"
sendAlarm("waterOverflow")
sendEvent(name: "water", value: "wet")
}
}
}
//log.debug "alarmV2: $cmd"
return map
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [:]
if(cmd.batteryLevel == 0xFF) {
map.name = "battery"
map.value = 1
map.descriptionText = "${device.displayName} has a low battery"
map.displayed = true
} else {
map.name = "battery"
map.value = cmd.batteryLevel > 0 ? cmd.batteryLevel.toString() : 1
map.unit = "%"
map.displayed = false
}
return map
}
def zwaveEvent(physicalgraph.zwave.Command cmd)
{
log.debug "COMMAND CLASS: $cmd"
}
def sendDataToCloud(double data)
{
def params = [
uri: "https://iot.swiftlet.technology",
path: "/fortrezz/post.php",
body: [
id: device.id,
value: data,
email: registerEmail
]
]
//log.debug("POST parameters: ${params}")
try {
httpPostJson(params) { resp ->
resp.headers.each {
//log.debug "${it.name} : ${it.value}"
}
log.debug "sendDataToCloud query response: ${resp.data}"
}
} catch (e) {
log.debug "something went wrong: $e"
}
}
def getTemperature(value) {
if(location.temperatureScale == "C"){
return value
} else {
return Math.round(celsiusToFahrenheit(value))
}
}
private getPictureName(category) {
//def pictureUuid = device.id.toString().replaceAll('-', '')
def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')
def name = "image" + "_$pictureUuid" + "_" + category + ".png"
return name
}
def api(method, args = [], success = {}) {
def methods = [
//"snapshot": [uri: "http://${ip}:${port}/snapshot.cgi${login()}&${args}", type: "post"],
"24hrs": [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=1", type: "get"],
"7days": [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=2", type: "get"],
"4weeks": [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=3", type: "get"],
]
def request = methods.getAt(method)
return doRequest(request.uri, request.type, success)
}
private doRequest(uri, type, success) {
log.debug(uri)
if(type == "post") {
httpPost(uri , "", success)
}
else if(type == "get") {
httpGet(uri, success)
}
}
def sendAlarm(text)
{
sendEvent(name: "alarmState", value: text, descriptionText: text, displayed: false)
}
def setThreshhold(rate)
{
log.debug "Setting Threshhold to ${rate}"
def event = createEvent(name: "lastThreshhold", value: rate, displayed: false)
def cmds = []
cmds << zwave.configurationV2.configurationSet(configurationValue: [(int)Math.round(rate*10)], parameterNumber: 5, size: 1).format()
sendEvent(event)
return response(cmds) // return a list containing the event and the result of response()
}

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
*
@@ -14,33 +14,25 @@
*
-------10/20/2015 Updates--------
-Fix/add battery reporting interval to update
-remove polling and/or refresh
-------5/2017 Updates--------
-Add fingerprints for SLP
-add device health, check every 60mins + 2mins
-remove polling and/or refresh(?)
*/
metadata {
definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "Plaid Systems") {
definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "NCauffman") {
capability "Configuration"
capability "Battery"
capability "Relative Humidity Measurement"
capability "Temperature Measurement"
capability "Sensor"
capability "Health Check"
//capability "Polling"
attribute "maxHum", "string"
attribute "minHum", "string"
command "resetHumidity"
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-SLP1", deviceJoinName: "Spruce Sensor"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01"
}
preferences {
@@ -301,11 +293,6 @@ def setConfig(){
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
def 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")
}
}
//check every 1 hour + 2mins
sendEvent(name: "checkInterval", value: 1 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
//poll
@@ -410,4 +395,4 @@ private byte[] reverseArray(byte[] array) {
i++;
}
return array
}
}

View File

@@ -27,9 +27,13 @@ Works with:
## Device Health
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

View File

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

View File

@@ -171,8 +171,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts]
result.value = pct
} else {
def minVolts = 2.4
def maxVolts = 2.7
def minVolts = 2.1
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0)

View File

@@ -274,7 +274,7 @@ private Map getBatteryResult(rawValue) {
result.value = pct
} else {
def minVolts = 2.1
def maxVolts = 2.7
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0)

View File

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

View File

@@ -202,8 +202,7 @@ def inputSelectionPage() {
section("options variations") {
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: "selectionSimpleGrouped", title: "Simple (Grouped) options", description: "no separators in the selectable options", groupedOptions: addGroup(englishOptions + spanishOptions))
input(type: "enum", name: "selectionSimple", title: "Simple 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)
}
@@ -215,15 +214,15 @@ def inputSelectionPage() {
section("segmented") {
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: "selectionSegmented4", style: "segmented", title: "4 options", options: ["One", "Two", "Three", "Four"])
input(type: "enum", name: "selectionSegmented1", style: "segmented", title: "1 option", groupedOptions: addGroup(["One"]))
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"
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"])
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") {
@@ -232,8 +231,6 @@ def inputSelectionPage() {
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: "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") {

View File

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