Compare commits

..

33 Commits

Author SHA1 Message Date
Fortrezz
f7515fab78 MSA-2113: Multi-Input Multi-Output device with 2 low voltage signal inputs and 2 low voltage relay outputs. Device handler allows user to see all 4 device endpoints and control each relay independently and view signal status as a voltage or (open/closed) status, depending on the signal type that is connected. There's two devices type handlers (a-side & b-side). These are for users to be able to create separate devices for each of the relays and inputs, so they can control them with rules and scenes in smart-home monitor or other smart-apps. 2017-07-20 08:05:31 -07:00
Vinay Rao
de5f0683d3 Merge pull request #2168 from SmartThingsCommunity/staging
Rolling down staging to master
2017-07-18 12:24:01 -07:00
Vinay Rao
36e63133fc Merge pull request #2146 from marstorp/icp1148HoneywellZwave
ICP-1148 Support Thermostat Dynamic data
2017-07-17 15:45:19 -07:00
Vinay Rao
838c466312 Merge pull request #2158 from varzac/smartsense-battery-updates
[DVCSMP-2811] Update ranges for centralite battery values
2017-07-14 14:37:45 -07:00
Zach Varberg
97bfe61baa Update ranges for centralite battery values
This just updates the range to be more conservative as well as match the
battery curve better.
2017-07-14 11:39:48 -05:00
Vinay Rao
34df40d5b4 Merge pull request #2155 from jackchi/health-aeon6
[DHF-24] Fix Aeon MultiSensor6 OFFLINE issue
2017-07-12 14:29:03 -07:00
jackchi
545be046f0 [DHF-24] Update Aeon Multi6 to 2hr2min 2017-07-11 17:05:42 -07:00
Vinay Rao
a5041e0fcb Merge pull request #2154 from SmartThingsCommunity/master
Rolling up staging to master
2017-07-11 14:03:19 -07:00
Vinay Rao
5beae1d9fc Merge pull request #2150 from SmartThingsCommunity/staging
Rolling down staging to master
2017-07-07 16:06:50 -07:00
Vinay Rao
e5738978b0 Merge pull request #850 from workingmonk/feature/new_button_methods
DVCSMP-1741 Changes to button controller to read number of buttons from the dth
2017-07-07 14:38:39 -07:00
Jack Chi
07064eb8cc Merge pull request #2148 from natec007/MSA-2084-5
MSA-2084: Spruce Sensor DTH new model and health check
2017-07-07 12:14:09 -07:00
natec007
f519a2d828 Update spruce-sensor.groovy
Removed checkInterval attribute
sendEvent(name: "checkInterval" already in updated and installed
2017-07-06 14:27:33 -07:00
Nathan Cauffman
4da49283bf MSA-2084: Updated with new model number and health check 2017-07-06 13:22:12 -07:00
Bob Florian
7389edf795 Merge pull request #2140 from juano2310/lifx_not_null
ICP-507 - Replace location count null with 0
2017-07-06 08:29:58 -07:00
Brian Steere
23154372d2 Merge pull request #2141 from SmartThingsCommunity/every-element-update
Update Every Element with more accurate enum settings
2017-07-06 09:02:42 -05:00
juano2310
1b0437c633 ICP-507 - Replace location count null with 0
Updated with def count = options.size().toString()
2017-07-06 09:43:47 -04:00
Vinay Rao
b3d9578140 Merge pull request #2147 from dkirker/CSING-26
CSING-26 Send IAS Zone update request when refreshing for 1st party zigbee sensors
2017-07-05 22:43:38 -07:00
Donald Kirker
7549979be5 CSING-26 Send IAS Zone update request when refreshing for 1st party zigbee sensors. 2017-07-05 20:35:18 -07:00
marstorp
771926c337 ICP-1148 Support Thermostat Dynamic data
Adding support for dynamic thermostat and fan modes to TCC DTH.
Also replaced capability "Polling" with "Refresh" and runEvery5Minutes("refresh") as polling capability is unreliable.
Also removed capability "Relative Humidity Measurement" as Honeywell Z-Wave Thermostat (YTH8320ZW1007/U) doesn't support humidity.
2017-07-05 16:50:30 -07:00
Vinay Rao
f647be3a62 Merge pull request #2144 from SmartThingsCommunity/staging
Rolling down staging to master
2017-07-05 14:22:13 -07:00
Vinay Rao
d2f981fd34 Merge pull request #2142 from SmartThingsCommunity/production
Rolling down production to staging
2017-07-05 14:13:37 -07:00
Brian Steere
2536c69083 Update Every Element with more accurate enum settings
Added a couple of extra enum inputs to demonstrate additional states
2017-07-05 16:01:16 -05:00
Vinay Rao
a58dd2094d Merge pull request #2137 from twack/update-gopher-water-valve-initail-state
(ICP-1050) Added check gopher valve initial state in installed()
2017-07-05 13:57:44 -07:00
twack
c549a5bed0 Added check valve initial state in installed() 2017-07-04 08:10:09 -07:00
Vinay Rao
ef5fffc4bc Merge pull request #2133 from jackchi/health-dhf-16
[DHF-16] ZigBee Button Device Health Update
2017-06-30 11:17:31 -07:00
jackchi
c370e88a6b [DHF-16] ZigBee Button Device Health Update 2017-06-30 10:44:20 -07:00
Vinay Rao
ce8c50c630 Merge pull request #2128 from marstorp/icp1148EcobeeHotfix
ICP-1148 Support Thermostat Dynamic data
2017-06-27 16:29:03 -07:00
marstorp
b069669c11 ICP-1148 Support Thermostat Dynamic data
Adding default "off" thermostat mode as it is not provided by the thermostat.
2017-06-27 16:19:49 -07:00
Vinay Rao
4d61d28b42 Merge pull request #2127 from SmartThingsCommunity/master
Rolling up master to staging
2017-06-27 16:05:32 -07:00
Vinay Rao
4547d0543b Merge pull request #2126 from SmartThingsCommunity/staging
Rolling down staging to master
2017-06-27 16:03:00 -07:00
Jack Chi
59e6f1251b Merge pull request #2097 from skt123/zigbee_button
[DHF-16] Added Health Check Implementation for Zigbee Button.
2017-06-27 10:28:47 -07:00
sushant.k1
e8939a77d7 [DHF-16]
Added Health Check Implementation for Zigbee Button.
2017-06-27 19:12:35 +05:30
Vinay Rao
45663ffb86 changes to button controller to read number of buttons from the dth 2016-04-01 16:17:03 -07:00
17 changed files with 673 additions and 90 deletions

View File

@@ -0,0 +1,102 @@
/**
* FortrezZ MIMO2+ B-Side
*
* 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 MIMO2+ B-Side", namespace: "fortrezz", author: "FortrezZ, LLC") {
capability "Contact Sensor"
capability "Relay Switch"
capability "Switch"
capability "Voltage Measurement"
capability "Refresh"
}
tiles {
standardTile("switch", "device.switch", width: 2, height: 2) {
state "on", label: "Relay 2 On", action: "off", icon: "http://swiftlet.technology/wp-content/uploads/2016/06/Switch-On-104-edit.png", backgroundColor: "#53a7c0"
state "off", label: "Relay 2 Off", action: "on", icon: "http://swiftlet.technology/wp-content/uploads/2016/06/Switch-Off-104-edit.png", backgroundColor: "#ffffff"
}
standardTile("anaDig1", "device.anaDig1", inactiveLabel: false) {
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
state "val", label:'${currentValue}v', unit:"", defaultState: true
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("powered", "device.powered", inactiveLabel: false) {
state "powerOn", label: "Power On", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "powerOff", label: "Power Off", icon: "st.switches.switch.off", backgroundColor: "#ffa81e"
}
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
standardTile("blank", "device.blank", inactiveLabel: true, decoration: "flat") {
state("blank", label: '')
}
main (["switch"])
details(["switch", "anaDig1", "blank", "blank", "refresh", "powered"])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle 'contact' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'voltage' attribute
}
def eventParse(evt) {
log.debug("Event: ${evt.name}=${evt.value}")
switch(evt.name) {
case "powered":
sendEvent(name: evt.name, value: evt.value)
break
case "switch2":
sendEvent(name: "switch", value: evt.value)
break
case "contact2":
sendEvent(name: "contact", value: evt.value)
break
case "voltage2":
sendEvent(name: "voltage", value: evt.value)
break
case "relay2":
sendEvent(name: evt.name, value: evt.value)
break
case "anaDig2":
sendEvent(name: "anaDig1", value: evt.value)
break
}
}
// handle commands
def on() {
parent.on2(device.id)
log.debug("Executing 'on'")
// TODO: Send Event to parent device for "on2"
}
def off() {
parent.off2(device.id)
log.debug("Executing 'off'")
// TODO: Send Event to parent device for "off2"
}
def refresh() {
parent.refresh2(device.id)
log.debug("Executing 'refresh'")
}

View File

@@ -0,0 +1,379 @@
/**
* MIMO2 Device Handler
*
* 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 MIMO2+", namespace: "fortrezz", author: "FortrezZ, LLC") {
capability "Alarm"
capability "Contact Sensor"
capability "Switch"
capability "Voltage Measurement"
capability "Configuration"
capability "Refresh"
attribute "powered", "string"
attribute "relay", "string"
attribute "relay2", "string"
attribute "contact2", "string"
attribute "voltage2", "string"
command "on"
command "off"
command "on2"
command "off2"
fingerprint deviceId: "0x2100", inClusters: "0x5E,0x86,0x72,0x5A,0x59,0x71,0x98,0x7A"
}
preferences {
input ("RelaySwitchDelay", "decimal", title: "Delay between relay switch on and off in seconds. Only Numbers 0 to 3 allowed. 0 value will remove delay and allow relay to function as a standard switch:\nRelay 1", description: "Numbers 0 to 3.1 allowed.", defaultValue: 0, required: false, displayDuringSetup: true)
input ("RelaySwitchDelay2", "decimal", title: "Relay 2", description: "Numbers 0 to 3.1 allowed.", defaultValue: 0, required: false, displayDuringSetup: true)
input ("Sig1AD", "bool", title: "Switch off for digital, on for analog:\nSIG1", required: false, displayDuringSetup: true)
input ("Sig2AD", "bool", title: "SIG2", required: false, displayDuringSetup: true)
} // the range would be 0 to 3.1, but the range value would not accept 3.1, only whole numbers (i tried paranthesis and fractions too. :( )
tiles {
standardTile("switch", "device.switch", width: 2, height: 2) {
state "on", label: "Relay 1 On", action: "off", icon: "http://swiftlet.technology/wp-content/uploads/2016/06/Switch-On-104-edit.png", backgroundColor: "#53a7c0"
state "off", label: "Relay 1 Off", action: "on", icon: "http://swiftlet.technology/wp-content/uploads/2016/06/Switch-Off-104-edit.png", backgroundColor: "#ffffff"
}
standardTile("switch2", "device.switch2", width: 2, height: 2, inactiveLabel: false) {
state "on", label: "Relay 2 On", action: "off2", icon: "http://swiftlet.technology/wp-content/uploads/2016/06/Switch-On-104-edit.png", backgroundColor: "#53a7c0"
state "off", label: 'Relay 2 Off', action: "on2", icon: "http://swiftlet.technology/wp-content/uploads/2016/06/Switch-Off-104-edit.png", backgroundColor: "#ffffff"
}
standardTile("anaDig1", "device.anaDig1", inactiveLabel: false) {
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
state "val", label:'${currentValue}v', unit:"", defaultState: true
}
standardTile("anaDig2", "device.anaDig2", inactiveLabel: false) {
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
state "val", label:'${currentValue}v', unit:"", defaultState: true
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("powered", "device.powered", inactiveLabel: false) {
state "powerOn", label: "Power On", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "powerOff", label: "Power Off", icon: "st.switches.switch.off", backgroundColor: "#ffa81e"
}
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
standardTile("blank", "device.blank", inactiveLabel: true, decoration: "flat") {
state("blank", label: '')
}
main (["switch"])
details(["switch", "anaDig1", "blank", "switch2", "anaDig2", "blank", "configure", "refresh", "powered"])
}
}
// parse events into attributes
def parse(String description) {
def result = null
def cmd = zwave.parse(description)
if (cmd.CMD == "7105") { //Mimo sent a power loss report
log.debug "Device lost power"
sendEvent(name: "powered", value: "powerOff", descriptionText: "$device.displayName lost power")
} else {
sendEvent(name: "powered", value: "powerOn", descriptionText: "$device.displayName regained power")
}
if (cmd) {
def eventReturn = zwaveEvent(cmd)
if(eventReturn in physicalgraph.device.HubMultiAction) {
result = eventReturn
}
else {
result = createEvent(eventReturn)
}
}
log.debug "Parse returned ${result} $cmd.CMD"
return result
}
def updated() { // neat built-in smartThings function which automatically runs whenever any setting inputs are changed in the preferences menu of the device handler
if (state.count == 1) // this bit with state keeps the function from running twice ( which it always seems to want to do) (( oh, and state.count is a variable which is nonVolatile and doesn't change per every parse request.
{
state.count = 0
log.debug "Settings Updated..."
return response(delayBetween([
configure(), // the response() function is used for sending commands in reponse to an event, without it, no zWave commands will work for contained function
refresh()
], 200))
}
else {state.count = 1}
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) // basic set is essentially our digital sensor for SIG1 and SIG2 - it doesn't use an endpoint so we are having it send a multilevelGet() for SIG1 and SIG2 to see which one triggered.
{
log.debug "sent a BasicSet command"
return response(refresh())
}
def zwaveEvent(int endPoint, physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd) // event to get the state of the digital sensor SIG1 and SIG2
{
log.debug "sent a sensorBinaryReport command"
return response(refresh())
}
def zwaveEvent(int endPoint, physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) // event for seeing the states of relay 1 and relay 2
{
def map = [:] // map for containing the name and state fo the specified relay
if (endPoint == 3)
{
if (cmd.value) // possible values are 255 and 0 (0 is false)
{map.value = "on"}
else
{map.value = "off"}
map.name = "switch"
log.debug "sent a SwitchBinary command $map.name $map.value" // the map is only used for debug messages. not for the return command to the device
return [name: "switch", value: cmd.value ? "on" : "off"]
}
else if (endPoint == 4)
{
if (cmd.value)
{map.value = "on"}
else
{map.value = "off"}
map.name = "switch2"
sendEvent(name: "relay2", value: "$map.value")
log.debug "sent a SwitchBinary command $map.name $map.value" // the map is only used for debug messages. not for the return command to the device
return [name: "switch2", value: cmd.value ? "on" : "off"]
}
}
def zwaveEvent (int endPoint, physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) // sensorMultilevelReport is used to report the value of the analog voltage for SIG1
{
def map = [:]
def stdEvent = [:]
def voltageVal = CalculateVoltage(cmd.scaledSensorValue) // saving the scaled Sensor Value used to enter into a large formula to determine actual voltage value
if (endPoint == 1) //endPoint 1 is for SIG1
{
if (state.AD1 == false) // state.AD1 is to determine which state the anaDig1 tile should be in (either analogue or digital mode)
{
map.name = "anaDig1"
stdEvent.name = "contact"
if (voltageVal < 2) { // DK changed to 2v to follow LED behavior
map.value = "closed"
stdEvent.value = "closed"
}
else
{
map.value = "open"
stdEvent.value = "open"
}
}
else //or state.AD1 is true for analogue mode
{
map.name = "anaDig1"
stdEvent.name = "voltage"
map.value = voltageVal
stdEvent.value = voltageVal
map.unit = "v"
stdEvent.unit = "v"
}
}
else if (endPoint == 2) // endpoint 2 is for SIG2
{
if (state.AD2 == false)
{
map.name = "anaDig2"
stdEvent.name = "contact2"
if (voltageVal < 2) {
map.value = "closed"
stdEvent.value = "closed"
}
else
{
map.value = "open"
stdEvent.value = "open"
}
}
else
{
map.name = "anaDig2"
stdEvent.name = "voltage2"
map.value = voltageVal
stdEvent.value = voltageVal
map.unit = "v"
stdEvent.unit = "v"
}
}
log.debug "sent a SensorMultilevelReport $map.name $map.value"
sendEvent(stdEvent)
return map
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { //standard security encapsulation event code (should be the same on all device handlers)
def encapsulatedCommand = cmd.encapsulatedCommand()
// can specify command class versions here like in zwave.parse
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
}
}
// MultiChannelCmdEncap and MultiInstanceCmdEncap are ways that devices
// can indicate that a message is coming from one of multiple subdevices
// or "endpoints" that would otherwise be indistinguishable
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand()
log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}")
if (encapsulatedCommand) {
return zwaveEvent(cmd.sourceEndPoint, encapsulatedCommand)
}
}
def zwaveEvent(int endPoint, physicalgraph.zwave.commands.multichannelassociationv2.MultiChannelAssociationReport cmd) {
log.debug "sent an Association Report"
log.debug " ${cmd.groupingIdentifier}"
//return [:]
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
// Handles all Z-Wave commands we aren't interested in
log.debug("Un-parsed Z-Wave message ${cmd}")
return [:]
}
def CalculateVoltage(ADCvalue) // used to calculate the voltage based on the collected Scaled sensor value of the multilevel sensor event
{
def volt = (((2.396*(10**-17))*(ADCvalue**5)) - ((1.817*(10**-13))*(ADCvalue**4)) + ((5.087*(10**-10))*(ADCvalue**3)) - ((5.868*(10**-7))*(ADCvalue**2)) + ((9.967*(10**-4))*(ADCvalue)) - (1.367*(10**-2)))
return volt.round(1)
}
def configure() {
log.debug "Configuring...."
def sig1
def sig2
if (Sig1AD == true)
{ sig1 = 0x01
state.AD1 = true}
else if (Sig1AD == false)
{ sig1 = 0x40
state.AD1 = false}
if (Sig2AD == true)
{ sig2 = 0x01
state.AD2 = true}
else if (Sig2AD == false)
{ sig2 = 0x40
state.AD2 = false}
def delay = (RelaySwitchDelay*10).toInteger() // the input which we get from the user is a string and is in seconds while the MIMO2 configuration requires it in 100ms so - change to integer and multiply by 10
def delay2 = (RelaySwitchDelay2*10).toInteger() // the input which we get from the user is a string and is in seconds while the MIMO2 configuration requires it in 100ms so - change to integer and multiply by 10
if (delay > 31)
{
log.debug "Relay 1 input ${delay / 10} set too high. Max value is 3.1"
delay = 31
}
if (delay < 0)
{
log.debug "Relay 1 input ${delay / 10} set too low. Min value is 0"
delay = 0
}
if (delay2 > 31)
{
log.debug "Relay 2 input ${delay2 / 10} set too high. Max value is 3.1"
delay2 = 31
}
if (delay2 < 0)
{
log.debug "Relay 2 input ${delay2 / 10} set too low. Min value is 0"
delay = 0
}
return delayBetween([
encap(zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]), 0),
encap(zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]), 0),
encap(zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]), 1),
encap(zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]), 2),
encap(zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]), 3),
encap(zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]), 4),
secure(zwave.configurationV1.configurationSet(configurationValue: [sig1], parameterNumber: 3, size: 1)), // sends a multiLevelSensor report every 30 seconds for SIG1
secure(zwave.configurationV1.configurationSet(configurationValue: [sig2], parameterNumber: 9, size: 1)), // sends a multiLevelSensor report every 30 seconds for SIG2
secure(zwave.configurationV1.configurationSet(configurationValue: [delay], parameterNumber: 1, size: 1)), // configurationValue for parameterNumber means how many 100ms do you want the relay
// to wait before it cycles again / size should just be 1 (for 1 byte.)
secure(zwave.configurationV1.configurationSet(configurationValue: [delay2], parameterNumber: 2, size: 1)),
], 200)
}
def on() {
return encap(zwave.basicV1.basicSet(value: 0xff), 3) // physically changes the relay from on to off and requests a report of the relay
// oddly, smartThings automatically sends a switchBinaryGet() command whenever the above basicSet command is sent, so we don't need to send one here.
}
def off() {
return encap(zwave.basicV1.basicSet(value: 0x00), 3) // physically changes the relay from on to off and requests a report of the relay
// oddly, smartThings automatically sends a switchBinaryGet() command whenever the above basicSet command is sent, so we don't need to send one here.
}
def on2() {
return encap(zwave.basicV1.basicSet(value: 0xff), 4)
// oddly, smartThings automatically sends a switchBinaryGet() command whenever the above basicSet command is sent, so we don't need to send one here.
}
def off2() {
return encap(zwave.basicV1.basicSet(value: 0x00), 4)
// oddly, smartThings automatically sends a switchBinaryGet() command whenever the above basicSet command is sent, so we don't need to send one here.
}
def refresh() {
log.debug "Refresh"
return delayBetween([
encap(zwave.sensorMultilevelV5.sensorMultilevelGet(), 1),// requests a report of the anologue input voltage for SIG1
encap(zwave.sensorMultilevelV5.sensorMultilevelGet(), 2),// requests a report of the anologue input voltage for SIG2
encap(zwave.switchBinaryV1.switchBinaryGet(), 3), //requests a report of the relay to make sure that it changed for Relay 1
encap(zwave.switchBinaryV1.switchBinaryGet(), 4), //requests a report of the relay to make sure that it changed for Relay 2
],200)
}
def refreshZWave() {
log.debug "Refresh (Z-Wave Response)"
return delayBetween([
encap(zwave.sensorMultilevelV5.sensorMultilevelGet(), 1),// requests a report of the anologue input voltage for SIG1
encap(zwave.sensorMultilevelV5.sensorMultilevelGet(), 2),// requests a report of the anologue input voltage for SIG2
encap(zwave.switchBinaryV1.switchBinaryGet(), 3), //requests a report of the relay to make sure that it changed for Relay 1
encap(zwave.switchBinaryV1.switchBinaryGet(), 4) //requests a report of the relay to make sure that it changed for Relay 2
],200)
}
private secureSequence(commands, delay=200) { // decided not to use this
return delayBetween(commands.collect{ secure(it) }, delay)
}
private secure(physicalgraph.zwave.Command cmd) { //take multiChannel message and securely encrypts the message so the device can read it
return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private encap(cmd, endpoint) { // takes desired command and encapsulates it by multiChannel and then sends it to secure() to be wrapped with another encapsulation for secure encryption
if (endpoint) {
return secure(zwave.multiChannelV3.multiChannelCmdEncap(bitAddress: false, sourceEndPoint:0, destinationEndPoint: endpoint).encapsulate(cmd))
} else {
return secure(cmd)
}
}

View File

@@ -1,5 +1,5 @@
/**
* Spruce Sensor -Pre-release V2 10/8/2015
* Spruce Sensor -updated with SLP model number 5/2017
*
* Copyright 2014 Plaid Systems
*
@@ -14,25 +14,33 @@
*
-------10/20/2015 Updates--------
-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 {
definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "NCauffman") {
definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "Plaid Systems") {
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"
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"
}
preferences {
@@ -293,6 +301,11 @@ 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"
@@ -303,6 +316,8 @@ 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
@@ -395,4 +410,4 @@ private byte[] reverseArray(byte[] array) {
i++;
}
return array
}
}

View File

@@ -27,13 +27,9 @@ Works with:
## Device Health
Aeon Labs MultiSensor 6 is polled by the hub.
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
Aeon MultiSensor reports in once every hour.
* __32min__ checkInterval
* __122min__ checkInterval
## Troubleshooting

View File

@@ -130,13 +130,13 @@ metadata {
}
def installed(){
// 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])
// 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])
}
def updated() {
// 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])
// 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])
log.debug "Updated with settings: ${settings}"
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"

View File

@@ -158,7 +158,7 @@ def generateEvent(Map results) {
if(results) {
def linkText = getLinkText(device)
def supportedThermostatModes = []
def supportedThermostatModes = ["off"]
def thermostatMode = null
results.each { name, value ->

View File

@@ -105,6 +105,8 @@ def parse(String description) {
} else {
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") {
@@ -129,6 +131,10 @@ def parse(String description) {
private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description)
translateZoneStatus(zs)
}
private Map translateZoneStatus(ZoneStatus zs) {
return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry')
}
@@ -197,7 +203,8 @@ def ping() {
def refresh() {
log.debug "Refreshing Temperature and Battery"
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()
}

View File

@@ -111,6 +111,8 @@ def parse(String description) {
def value = descMap.value.endsWith("01") ? "active" : "inactive"
log.debug "Doing a read attr motion event"
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") {
@@ -135,6 +137,10 @@ def parse(String description) {
private Map parseIasMessage(String 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
return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive')
}
@@ -165,8 +171,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts]
result.value = pct
} else {
def minVolts = 2.1
def maxVolts = 3.0
def minVolts = 2.4
def maxVolts = 2.7
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0)
@@ -200,7 +206,8 @@ def refresh() {
log.debug "refresh called"
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()
}

View File

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

View File

@@ -0,0 +1,2 @@
.st-ignore
README.md

View File

@@ -0,0 +1,42 @@
# ZigBee Button
Cloud Execution
Works with:
* [OSRAM LIGHTIFY Dimming Switch](https://support.smartthings.com/hc/en-us/articles/115000236823-SYLVANIA-Dimming-Switch)
* [Iris Smart Button](https://support.smartthings.com/hc/en-us/articles/115000190186-Iris-Smart-Button)
* [Iris KeyFob](https://support.smartthings.com/hc/en-us/articles/217409686-Iris-Smart-Fob)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Troubleshooting](#troubleshooting)
## Capabilities
* **Actuator** - It represents that a device has commands.
* **Battery** - It defines that the device has a battery
* **Button** - It defines that a device has one or more buttons
* **Holdable Button** - It defines that a device has one or more holdable buttons
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Refresh** - _refresh()_ command for status updates
* **Sensor** - it represents that a Device has attributes.
* **Health Check** - indicates ability to get device health notifications
## Device Health
ZigBee Button is marked offline only in the case when Hub is offline.
## Troubleshooting
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
Pairing needs to be tried again by placing the device closer to the hub.
It may also happen that you need to reset the device.
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
* [OSRAM LIGHTIFY Dimming Switch Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/115000236823-SYLVANIA-Dimming-Switch)
* [Iris Smart Button Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/115000190186-Iris-Smart-Button)
* [Iris KeyFob Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/217409686-Iris-Smart-Fob)

View File

@@ -13,6 +13,8 @@
* for the specific language governing permissions and limitations under the License.
*
*/
import groovy.json.JsonOutput
import physicalgraph.zigbee.zcl.DataType
metadata {
@@ -24,6 +26,7 @@ metadata {
capability "Configuration"
capability "Refresh"
capability "Sensor"
capability "Health Check"
command "enrollResponse"
@@ -249,6 +252,8 @@ def updated() {
}
def initialize() {
// Arrival sensors only goes OFFLINE when Hub is off
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
if ((device.getDataValue("manufacturer") == "OSRAM") && (device.getDataValue("model") == "LIGHTIFY Dimming Switch")) {
sendEvent(name: "numberOfButtons", value: 2)
}

View File

@@ -15,10 +15,9 @@ metadata {
definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Thermostat"
capability "Configuration"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Health Check"
@@ -117,7 +116,7 @@ metadata {
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
}
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
state "default", action:"polling.poll", icon:"st.secondary.refresh"
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "temperature"
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"])
@@ -125,13 +124,20 @@ metadata {
}
def installed(){
// 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])
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()))
initialize()
}
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)
@@ -149,6 +155,7 @@ 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}"
@@ -172,6 +179,7 @@ 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
@@ -305,26 +313,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.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
if(cmd.cool) { supportedModes += "cool " }
if(cmd.auto) { supportedModes += "auto " }
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" }
state.supportedModes = supportedModes
// No events to be generated, return empty map
state.supportedThermostatModes = supportedModes
sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false)
return [:]
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
def supportedFanModes = ""
if(cmd.auto) { supportedFanModes += "auto " } // "fanAuto "
if(cmd.low) { supportedFanModes += "on " } // "fanOn"
if(cmd.circulation) { supportedFanModes += "circulate " } // "fanCirculate"
def supportedFanModes = []
if(cmd.auto) { supportedFanModes << "auto" } // "fanAuto "
if(cmd.circulation) { supportedFanModes << "circulate" } // "fanCirculate"
if(cmd.low) { supportedFanModes << "on" } // "fanOn"
state.supportedFanModes = supportedFanModes
// No events to be generated, return empty map
state.supportedThermostatFanModes = supportedFanModes
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false)
return [:]
}
@@ -337,15 +345,17 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
}
// Command Implementations
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 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 quickSetHeat(degrees) {
@@ -416,28 +426,14 @@ 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() {
["off", "heat", "cool", "auto", "emergency heat"]
return state.supportedThermostatModes
}
def switchMode() {
def currentMode = device.currentState("thermostatMode")?.value
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
def supportedModes = getDataByName("supportedModes")
def lastTriedMode = state.lastTriedMode ?: currentMode ?: ["off"]
def supportedModes = getDataByName("supportedThermostatModes")
def modeOrder = modes()
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode)
@@ -454,7 +450,7 @@ def switchMode() {
}
def switchToMode(nextMode) {
def supportedModes = getDataByName("supportedModes")
def supportedModes = getDataByName("supportedThermostatModes")
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
if (nextMode in modes()) {
state.lastTriedMode = nextMode
@@ -466,9 +462,9 @@ def switchToMode(nextMode) {
def switchFanMode() {
def currentMode = device.currentState("thermostatFanMode")?.value
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off"
def supportedModes = getDataByName("supportedFanModes") ?: "auto on" // "fanAuto fanOn"
def modeOrder = ["auto", "circulate", "on"] // "fanAuto", "fanCirculate", "fanOn"
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: ["off"]
def supportedModes = getDataByName("supportedThermostatFanModes") ?: ["auto", "on"]
def modeOrder = state.supportedThermostatFanModes
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode)
while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto"
@@ -478,7 +474,7 @@ def switchFanMode() {
}
def switchToFanMode(nextMode) {
def supportedFanModes = getDataByName("supportedFanModes")
def supportedFanModes = getDataByName("supportedThermostatFanModes")
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
def returnCommand

View File

@@ -58,6 +58,7 @@ metadata {
def installed() {
// 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])
response(refresh())
}
def updated() {

View File

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

View File

@@ -202,7 +202,8 @@ 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", groupedOptions: addGroup(englishOptions + spanishOptions))
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: "selectionGrouped", title: "Grouped options", description: "separate groups of options with headers", groupedOptions: groupedOptions)
}
@@ -214,15 +215,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", groupedOptions: addGroup(["One"]))
input(type: "enum", name: "selectionSegmented4", style: "segmented", title: "4 options", groupedOptions: addGroup(["One", "Two", "Three", "Four"]))
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"])
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", title: "defaulted to 'two'", groupedOptions: addGroup(["One", "Two", "Three"]), defaultValue: "Two")
input(type: "enum", name: "selectionSegmentedWithDefault", style: "segmented", title: "defaulted to 'two'", options: ["One", "Two", "Three"], defaultValue: "Two")
}
section("required: true") {
@@ -231,6 +232,8 @@ 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()
def count = options.size().toString()
return dynamicPage(name:"Credentials", title:"", nextPage:"", install:true, uninstall: true) {
section("Select your location") {