Compare commits

..

9 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
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
8 changed files with 536 additions and 533 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

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

@@ -171,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)

View File

@@ -274,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)

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

@@ -1,470 +0,0 @@
/* **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°"
}
}
}