Compare commits

...

25 Commits

Author SHA1 Message Date
Kyle Morgan
ca42ee5526 MSA-2099: Want access to 5-2 Day Thermostat on my Smartthings app 2017-07-15 12:19:15 -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
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
13 changed files with 607 additions and 27 deletions

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

@@ -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')
}
@@ -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") {
@@ -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

@@ -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

@@ -0,0 +1,470 @@
/* **DISCLAIMER**
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Without limitation of the foregoing, Contributors/Regents expressly does not warrant that:
* 1. the software will meet your requirements or expectations;
* 2. the software or the software content will be free of bugs, errors, viruses or other defects;
* 3. any results, output, or data provided through or generated by the software will be accurate, up-to-date, complete or reliable;
* 4. the software will be compatible with third party software;
* 5. any errors in the software will be corrected.
* The user assumes all responsibility for selecting the software and for the results obtained from the use of the software. The user shall bear the entire risk as to the quality and the performance of the software.
*/
/**
* 5-2 Day Thermostat
*
* Base code from mwoodengr@hotmail.com, bugfixed and enhanced by RBoy
* Changes Copyright RBoy, redistribution of any changes or modified code is not allowed without permission
* Version 2.1.0
* 2015-10-3 - Fixed an issue with selecting multiple thermostats and for recent platform changes
* 2015-5-17 - Added ability to select mutiple thermostats simultaneously
* 2015-2-11 - Fixed issue with fan mode
*
*/
definition(
name: "5-2 Day Thermostat",
namespace: "rboy",
author: "RBoy",
description: "Weekday and Weekend Thermostat",
category: "Green Living",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/GreenLiving/Cat-GreenLiving.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/GreenLiving/Cat-GreenLiving@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/GreenLiving/Cat-GreenLiving@3x.png")
preferences {
section("Choose thermostat (s)") {
input "thermostats", "capability.thermostat", required: true, multiple:true
}
section("Switch HVAC mode (auto to cool/heat) based on the outside temperature (optional)") {
input "temperatureSensor", "capability.temperatureMeasurement", required: false
input "temperatureH", "number", title: "Switch to heating temperature", required: false, description: "Temperature below which switch to heat mode"
input "temperatureC", "number", title: "Switch to cooling temperature", required: false, description: "Temperature above which switch to cool mode"
}
section("Monday to Friday Schedule") {
input "time1", "time", title: "Wake Time", required: true
input "tempSetpoint1", "number", title: "Wake Heat Temp", required: true
input "tempSetpointA", "number", title: "Wake Cool Temp", required: true
input "time2", "time", title: "Leave Time", required: true
input "tempSetpoint2", "number", title: "Leave Heat Temp", required: true
input "tempSetpointB", "number", title: "Leave Cool Temp", required: true
input "time3", "time", title: "Return Time", required: true
input "tempSetpoint3", "number", title: "Return Heat Temp", required: true
input "tempSetpointC", "number", title: "Return Cool Temp", required: true
input "time4", "time", title: "Sleep Time", required: true
input "tempSetpoint4", "number", title: "Sleep Heat Temp", required: true
input "tempSetpointD", "number", title: "Sleep Cool Temp", required: true
}
section("Saturday and Sunday Schedule") {
input "time11", "time", title: "Wake Time", required: true
input "tempSetpoint11", "number", title: "Wake Heat Temp", required: true
input "tempSetpointAA", "number", title: "Wake Cool Temp", required: true
input "time21", "time", title: "Leave Time", required: true
input "tempSetpoint21", "number", title: "Leave Heat Temp", required: true
input "tempSetpointBB", "number", title: "Leave Cool Temp", required: true
input "time31", "time", title: "Return Time", required: true
input "tempSetpoint31", "number", title: "Return Heat Temp", required: true
input "tempSetpointCC", "number", title: "Return Cool Temp", required: true
input "time41", "time", title: "Sleep Time", required: true
input "tempSetpoint41", "number", title: "Sleep Heat Temp", required: true
input "tempSetpointDD", "number", title: "Sleep Cool Temp", required: true
}
}
def installed()
{
subscribeToEvents()
}
def updated()
{
unsubscribe()
subscribeToEvents()
}
def subscribeToEvents() {
subscribe(temperatureSensor, "temperature", temperatureHandler)
subscribe(location, modeChangeHandler)
initialize()
}
// Handle mode changes, reinitialize the current temperature and timers after a mode change, this is to workaround the issue of the last timer firing while in a non running mode, resume operations when supported modes are set
def modeChangeHandler(evt) {
log.debug "Reinitializing thermostats on mode change notification, new mode $evt.value"
//sendNotificationEvent("$thermostats Reinitializing on mode change notification, new mode $evt.value")
initialize()
}
// This section sets the HVAC mode based outside temperature. HVAC fan mode is set to "auto".
def temperatureHandler(evt) {
log.debug "Heat mode switch temperature $temperatureH, cool mode switch temperature $temperatureC"
if (temperatureH == null || temperatureC == null) { // We are in Auto mode or user doesn't want us to switch modes
return
}
thermostats.each { thermostat ->
def extTemp = temperatureSensor.currentTemperature
log.debug "External temperature is: $extTemp"
def thermostatState = thermostat.currentThermostatMode
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "HVAC current mode $thermostatState"
log.debug "HVAC Fan current mode $thermostatFan"
if (extTemp < temperatureH) {
if (thermostatState == "cool") {
def hvacmode = "heat"
thermostat.setThermostatMode(hvacmode)
log.debug "HVAC mode set to $hvacmode"
}
}
else if (extTemp > temperatureC) {
if (thermostatState == "heat") {
def hvacmode = "cool"
thermostat.setThermostatMode(hvacmode)
log.debug "HVAC mode set to $hvacmode"
}
}
if (thermostatFan != "fanAuto") {
thermostat.setThermostatFanMode("auto")
log.debug "HVAC fan mode set to auto"
}
}
}
// This section determines which day it is.
def initialize() {
log.trace "Initialized with $settings"
unschedule()
def calendar = Calendar.getInstance()
calendar.setTimeZone(location.timeZone)
def today = calendar.get(Calendar.DAY_OF_WEEK)
def timeNow = now()
def midnightToday = timeToday("2000-01-01T23:59:59.999-0000", location.timeZone)
log.debug("Current time is ${(new Date(timeNow)).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
log.debug("Midnight today is ${midnightToday.format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
log.trace("Weekday schedule1 ${timeToday(time1, location.timeZone).format("HH:mm z", location.timeZone)}")
log.trace("Weekday schedule2 ${timeToday(time2, location.timeZone).format("HH:mm z", location.timeZone)}")
log.trace("Weekday schedule3 ${timeToday(time3, location.timeZone).format("HH:mm z", location.timeZone)}")
log.trace("Weekday schedule4 ${timeToday(time4, location.timeZone).format("HH:mm z", location.timeZone)}")
log.trace("Weekend schedule1 ${timeToday(time11, location.timeZone).format("HH:mm z", location.timeZone)}")
log.trace("Weekend schedule2 ${timeToday(time21, location.timeZone).format("HH:mm z", location.timeZone)}")
log.trace("Weekend schedule3 ${timeToday(time31, location.timeZone).format("HH:mm z", location.timeZone)}")
log.trace("Weekend schedule4 ${timeToday(time41, location.timeZone).format("HH:mm z", location.timeZone)}")
// This section is where the time/temperature schedule is set
switch (today) {
case Calendar.MONDAY:
case Calendar.TUESDAY:
case Calendar.WEDNESDAY:
case Calendar.THURSDAY:
if (timeNow >= timeToday(time1, location.timeZone).time && timeNow < timeToday(time2, location.timeZone).time) { // Are we between 1st time and 2nd time
changeTemp1()
schedule(timeToday(time2, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time2, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time2, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time2, location.timeZone).time && timeNow < timeToday(time3, location.timeZone).time) { // Are we between 2nd time and 3rd time
changeTemp2()
schedule(timeToday(time3, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time3, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time3, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time3, location.timeZone).time && timeNow < timeToday(time4, location.timeZone).time) { // Are we between 3rd time and 4th time
changeTemp3()
schedule(timeToday(time4, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time4, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time4, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time4, location.timeZone).time && timeNow < midnightToday.time) { // Are we between 4th time and midnight, schedule next day
changeTemp4()
schedule(timeToday(time1, location.timeZone) + 1, initialize)
log.info("$thermostats Scheduled next adjustment for ${(timeToday(time1, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${(timeToday(time1, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= (midnightToday - 1).time && timeNow < timeToday(time1, location.timeZone).time) { // Are we between midnight yesterday and 1st time, schedule today
changeTemp4()
schedule(timeToday(time1, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time1, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time1, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
break
case Calendar.FRIDAY:
if (timeNow >= timeToday(time1, location.timeZone).time && timeNow < timeToday(time2, location.timeZone).time) { // Are we between 1st time and 2nd time
changeTemp1()
schedule(timeToday(time2, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time2, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time2, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time2, location.timeZone).time && timeNow < timeToday(time3, location.timeZone).time) { // Are we between 2nd time and 3rd time
changeTemp2()
schedule(timeToday(time3, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time3, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time3, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time3, location.timeZone).time && timeNow < timeToday(time4, location.timeZone).time) { // Are we between 3rd time and 4th time
changeTemp3()
schedule(timeToday(time4, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time4, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time4, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time4, location.timeZone).time && timeNow < midnightToday.time) { // Are we between 4th time Friday and midnight, we schedule Saturday
changeTemp4()
schedule(timeToday(time11, location.timeZone) + 1, initialize)
log.info("$thermostats Scheduled next adjustment for ${(timeToday(time11, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${(timeToday(time11, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= (midnightToday - 1).time && timeNow < timeToday(time11, location.timeZone).time) { // Are we between midnight Friday and 1st time on Saturday, we schedule Saturday
changeTemp4()
schedule(timeToday(time11, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time11, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time11, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
break
case Calendar.SATURDAY:
if (timeNow >= timeToday(time11, location.timeZone).time && timeNow < timeToday(time21, location.timeZone).time) { // Are we between 1st time and 2nd time
changeTemp11()
schedule(timeToday(time21, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time21, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time21, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time21, location.timeZone).time && timeNow < timeToday(time31, location.timeZone).time) { // Are we between 2nd time and 3rd time
changeTemp21()
schedule(timeToday(time31, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time31, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time31, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time31, location.timeZone).time && timeNow < timeToday(time41, location.timeZone).time) { // Are we between 3rd time and 4th time
changeTemp31()
schedule(timeToday(time41, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time41, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time41, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time41, location.timeZone).time && timeNow < midnightToday.time) { // Are we between 4th time and midnight, schedule the next day
changeTemp41()
schedule(timeToday(time11, location.timeZone) + 1, initialize)
log.info("$thermostats Scheduled next adjustment for ${(timeToday(time11, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${(timeToday(time11, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= (midnightToday - 1).time && timeNow < timeToday(time11, location.timeZone).time) { // Are we between midnight yesterday and 1st time, schedule today
changeTemp41()
schedule(timeToday(time11, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time11, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time11, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
break
case Calendar.SUNDAY:
if (timeNow >= timeToday(time11, location.timeZone).time && timeNow < timeToday(time21, location.timeZone).time) { // Are we between 1st time and 2nd time
changeTemp11()
schedule(timeToday(time21, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time21, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time21, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time21, location.timeZone).time && timeNow < timeToday(time31, location.timeZone).time) { // Are we between 2nd time and 3rd time
changeTemp21()
schedule(timeToday(time31, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time31, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time31, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time31, location.timeZone).time && timeNow < timeToday(time41, location.timeZone).time) { // Are we between 3rd time and 4th time
changeTemp31()
schedule(timeToday(time41, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time41, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time41, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= timeToday(time41, location.timeZone).time && timeNow < midnightToday.time) { // Are we between 4th time Sunday and midnight, we schedule Monday
changeTemp41()
schedule(timeToday(time1, location.timeZone) + 1, initialize)
log.info("$thermostats Scheduled next adjustment for ${(timeToday(time1, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${(timeToday(time1, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
else if (timeNow >= (midnightToday - 1).time && timeNow < timeToday(time1, location.timeZone).time) { // Are we between midnight Sunday and 1st time on Monday, we schedule Monday
changeTemp41()
schedule(timeToday(time1, location.timeZone), initialize)
log.info("$thermostats Scheduled next adjustment for ${timeToday(time1, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time1, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
}
break
}
}
// This section is where the thermostat temperature settings are set.
def changeTemp1() {
thermostats.each { thermostat ->
def thermostatState = thermostat.currentThermostatMode
log.debug "Thermostat mode = $thermostatState"
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "Thermostat fan = $thermostatFan"
if (thermostatState == "auto") {
thermostat.setHeatingSetpoint(tempSetpoint1)
thermostat.setCoolingSetpoint(tempSetpointA)
log.info "Set $thermostat Heat $tempSetpoint1°, Cool $tempSetpointA°"
}
else if (thermostatState == "heat") {
thermostat.setHeatingSetpoint(tempSetpoint1)
log.info "Set $thermostat Heat $tempSetpoint1°"
}
else {
thermostat.setCoolingSetpoint(tempSetpointA)
log.info "Set $thermostat Cool $tempSetpointA°"
}
}
}
def changeTemp2() {
thermostats.each { thermostat ->
def thermostatState = thermostat.currentThermostatMode
log.debug "Thermostat mode = $thermostatState"
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "Thermostat fan = $thermostatFan"
if (thermostatState == "auto") {
thermostat.setHeatingSetpoint(tempSetpoint2)
thermostat.setCoolingSetpoint(tempSetpointB)
log.info "Set $thermostat Heat $tempSetpoint2°, Cool $tempSetpointB°"
}
else if (thermostatState == "heat") {
thermostat.setHeatingSetpoint(tempSetpoint2)
}
else {
thermostat.setCoolingSetpoint(tempSetpointB)
}
}
}
def changeTemp3() {
thermostats.each { thermostat ->
def thermostatState = thermostat.currentThermostatMode
log.debug "Thermostat mode = $thermostatState"
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "Thermostat fan = $thermostatFan"
if (thermostatState == "auto") {
thermostat.setHeatingSetpoint(tempSetpoint3)
thermostat.setCoolingSetpoint(tempSetpointC)
log.info "Set $thermostat Heat $tempSetpoint3°, Cool $tempSetpointC°"
}
else if (thermostatState == "heat") {
thermostat.setHeatingSetpoint(tempSetpoint3)
log.info "Set $thermostat Heat $tempSetpoint3°"
}
else {
thermostat.setCoolingSetpoint(tempSetpointC)
log.info "Set $thermostat Cool $tempSetpointC°"
}
}
}
def changeTemp4() {
thermostats.each { thermostat ->
def thermostatState = thermostat.currentThermostatMode
log.debug "Thermostat mode = $thermostatState"
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "Thermostat fan = $thermostatFan"
if (thermostatState == "auto") {
thermostat.setHeatingSetpoint(tempSetpoint4)
thermostat.setCoolingSetpoint(tempSetpointD)
log.info "Set $thermostat Heat $tempSetpoint4°, Cool $tempSetpointD°"
}
else if (thermostatState == "heat") {
thermostat.setHeatingSetpoint(tempSetpoint4)
log.info "Set $thermostat Heat $tempSetpoint4°"
}
else {
thermostat.setCoolingSetpoint(tempSetpointD)
log.info "Set $thermostat Cool $tempSetpointD°"
}
}
}
def changeTemp11() {
thermostats.each { thermostat ->
def thermostatState = thermostat.currentThermostatMode
log.debug "Thermostat mode = $thermostatState"
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "Thermostat fan = $thermostatFan"
if (thermostatState == "auto") {
thermostat.setHeatingSetpoint(tempSetpoint11)
thermostat.setCoolingSetpoint(tempSetpointAA)
log.info "Set $thermostat Heat $tempSetpoint11°, Cool $tempSetpointAA°"
}
else if (thermostatState == "heat") {
thermostat.setHeatingSetpoint(tempSetpoint11)
log.info "Set $thermostat Heat $tempSetpoint11°"
}
else {
thermostat.setCoolingSetpoint(tempSetpointAA)
log.info "Set $thermostat Cool $tempSetpointAA°"
}
}
}
def changeTemp21() {
thermostats.each { thermostat ->
def thermostatState = thermostat.currentThermostatMode
log.debug "Thermostat mode = $thermostatState"
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "Thermostat fan = $thermostatFan"
if (thermostatState == "auto") {
thermostat.setHeatingSetpoint(tempSetpoint21)
thermostat.setCoolingSetpoint(tempSetpointBB)
log.info "Set $thermostat Heat $tempSetpoint21°, Cool $tempSetpointBB°"
}
else if (thermostatState == "heat") {
thermostat.setHeatingSetpoint(tempSetpoint21)
log.info "Set $thermostat Heat $tempSetpoint21°"
}
else {
thermostat.setCoolingSetpoint(tempSetpointBB)
log.info "Set $thermostat Cool $tempSetpointBB°"
}
}
}
def changeTemp31() {
thermostats.each { thermostat ->
def thermostatState = thermostat.currentThermostatMode
log.debug "Thermostat mode = $thermostatState"
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "Thermostat fan = $thermostatFan"
if (thermostatState == "auto") {
thermostat.setHeatingSetpoint(tempSetpoint31)
thermostat.setCoolingSetpoint(tempSetpointCC)
log.info "Set $thermostat Heat $tempSetpoint31°, Cool $tempSetpointCC°"
}
else if (thermostatState == "heat") {
thermostat.setHeatingSetpoint(tempSetpoint31)
log.info "Set $thermostat Heat $tempSetpoint31°"
}
else {
thermostat.setCoolingSetpoint(tempSetpointCC)
log.info "Set $thermostat Cool $tempSetpointCC°"
}
}
}
def changeTemp41() {
thermostats.each { thermostat ->
def thermostatState = thermostat.currentThermostatMode
log.debug "Thermostat mode = $thermostatState"
def thermostatFan = thermostat.currentThermostatFanMode
log.debug "Thermostat fan = $thermostatFan"
if (thermostatState == "auto") {
thermostat.setHeatingSetpoint(tempSetpoint41)
thermostat.setCoolingSetpoint(tempSetpointDD)
log.info "Set $thermostat Heat $tempSetpoint41°, Cool $tempSetpointDD°"
}
else if (thermostatState == "heat") {
thermostat.setHeatingSetpoint(tempSetpoint41)
log.info "Set $thermostat Heat $tempSetpoint41°"
}
else {
thermostat.setCoolingSetpoint(tempSetpointDD)
log.info "Set $thermostat Cool $tempSetpointDD°"
}
}
}

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") {