Compare commits

..

67 Commits

Author SHA1 Message Date
Fortrezz
f7515fab78 MSA-2113: Multi-Input Multi-Output device with 2 low voltage signal inputs and 2 low voltage relay outputs. Device handler allows user to see all 4 device endpoints and control each relay independently and view signal status as a voltage or (open/closed) status, depending on the signal type that is connected. There's two devices type handlers (a-side & b-side). These are for users to be able to create separate devices for each of the relays and inputs, so they can control them with rules and scenes in smart-home monitor or other smart-apps. 2017-07-20 08:05:31 -07:00
Vinay Rao
de5f0683d3 Merge pull request #2168 from SmartThingsCommunity/staging
Rolling down staging to master
2017-07-18 12:24:01 -07:00
Vinay Rao
36e63133fc Merge pull request #2146 from marstorp/icp1148HoneywellZwave
ICP-1148 Support Thermostat Dynamic data
2017-07-17 15:45:19 -07:00
Vinay Rao
838c466312 Merge pull request #2158 from varzac/smartsense-battery-updates
[DVCSMP-2811] Update ranges for centralite battery values
2017-07-14 14:37:45 -07:00
Zach Varberg
97bfe61baa Update ranges for centralite battery values
This just updates the range to be more conservative as well as match the
battery curve better.
2017-07-14 11:39:48 -05:00
Vinay Rao
34df40d5b4 Merge pull request #2155 from jackchi/health-aeon6
[DHF-24] Fix Aeon MultiSensor6 OFFLINE issue
2017-07-12 14:29:03 -07:00
jackchi
545be046f0 [DHF-24] Update Aeon Multi6 to 2hr2min 2017-07-11 17:05:42 -07:00
Vinay Rao
a5041e0fcb Merge pull request #2154 from SmartThingsCommunity/master
Rolling up staging to master
2017-07-11 14:03:19 -07:00
Vinay Rao
5beae1d9fc Merge pull request #2150 from SmartThingsCommunity/staging
Rolling down staging to master
2017-07-07 16:06:50 -07:00
Vinay Rao
e5738978b0 Merge pull request #850 from workingmonk/feature/new_button_methods
DVCSMP-1741 Changes to button controller to read number of buttons from the dth
2017-07-07 14:38:39 -07:00
Jack Chi
07064eb8cc Merge pull request #2148 from natec007/MSA-2084-5
MSA-2084: Spruce Sensor DTH new model and health check
2017-07-07 12:14:09 -07:00
natec007
f519a2d828 Update spruce-sensor.groovy
Removed checkInterval attribute
sendEvent(name: "checkInterval" already in updated and installed
2017-07-06 14:27:33 -07:00
Nathan Cauffman
4da49283bf MSA-2084: Updated with new model number and health check 2017-07-06 13:22:12 -07:00
Bob Florian
7389edf795 Merge pull request #2140 from juano2310/lifx_not_null
ICP-507 - Replace location count null with 0
2017-07-06 08:29:58 -07:00
Brian Steere
23154372d2 Merge pull request #2141 from SmartThingsCommunity/every-element-update
Update Every Element with more accurate enum settings
2017-07-06 09:02:42 -05:00
juano2310
1b0437c633 ICP-507 - Replace location count null with 0
Updated with def count = options.size().toString()
2017-07-06 09:43:47 -04:00
Vinay Rao
b3d9578140 Merge pull request #2147 from dkirker/CSING-26
CSING-26 Send IAS Zone update request when refreshing for 1st party zigbee sensors
2017-07-05 22:43:38 -07:00
Donald Kirker
7549979be5 CSING-26 Send IAS Zone update request when refreshing for 1st party zigbee sensors. 2017-07-05 20:35:18 -07:00
marstorp
771926c337 ICP-1148 Support Thermostat Dynamic data
Adding support for dynamic thermostat and fan modes to TCC DTH.
Also replaced capability "Polling" with "Refresh" and runEvery5Minutes("refresh") as polling capability is unreliable.
Also removed capability "Relative Humidity Measurement" as Honeywell Z-Wave Thermostat (YTH8320ZW1007/U) doesn't support humidity.
2017-07-05 16:50:30 -07:00
Vinay Rao
f647be3a62 Merge pull request #2144 from SmartThingsCommunity/staging
Rolling down staging to master
2017-07-05 14:22:13 -07:00
Vinay Rao
d2f981fd34 Merge pull request #2142 from SmartThingsCommunity/production
Rolling down production to staging
2017-07-05 14:13:37 -07:00
Brian Steere
2536c69083 Update Every Element with more accurate enum settings
Added a couple of extra enum inputs to demonstrate additional states
2017-07-05 16:01:16 -05:00
Vinay Rao
a58dd2094d Merge pull request #2137 from twack/update-gopher-water-valve-initail-state
(ICP-1050) Added check gopher valve initial state in installed()
2017-07-05 13:57:44 -07:00
twack
c549a5bed0 Added check valve initial state in installed() 2017-07-04 08:10:09 -07:00
Vinay Rao
ef5fffc4bc Merge pull request #2133 from jackchi/health-dhf-16
[DHF-16] ZigBee Button Device Health Update
2017-06-30 11:17:31 -07:00
jackchi
c370e88a6b [DHF-16] ZigBee Button Device Health Update 2017-06-30 10:44:20 -07:00
Vinay Rao
ce8c50c630 Merge pull request #2128 from marstorp/icp1148EcobeeHotfix
ICP-1148 Support Thermostat Dynamic data
2017-06-27 16:29:03 -07:00
marstorp
b069669c11 ICP-1148 Support Thermostat Dynamic data
Adding default "off" thermostat mode as it is not provided by the thermostat.
2017-06-27 16:19:49 -07:00
Vinay Rao
4d61d28b42 Merge pull request #2127 from SmartThingsCommunity/master
Rolling up master to staging
2017-06-27 16:05:32 -07:00
Vinay Rao
4547d0543b Merge pull request #2126 from SmartThingsCommunity/staging
Rolling down staging to master
2017-06-27 16:03:00 -07:00
Vinay Rao
6b1e41198c Merge pull request #2125 from SmartThingsCommunity/staging
Rolling up staging to production
2017-06-27 15:07:38 -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
5aee3a1861 Merge pull request #2114 from marstorp/icp1148dynamicModeSupport
ICP-1148 Support Thermostat Dynamic data
2017-06-23 14:15:56 -07:00
marstorp
17ae692aa0 ICP-1148 Support Thermostat Dynamic data
Adding support for dynamic thermostat and fan modes to ecobee
2017-06-23 13:24:03 -07:00
Vinay Rao
f02428b99f Merge pull request #2113 from larsfinander/DVCSMP-2702_smartpower-outlet_remove_light_staging
DVCSMP-2702 smartpower-outlet DTH incorrectly has capability.light
2017-06-22 12:25:26 -07:00
Lars Finander
34174b730c DVCSMP-2702 smartpower-outlet DTH incorrectly has capability.light 2017-06-22 13:13:08 -06:00
Vinay Rao
12f874408a Merge pull request #2110 from SmartThingsCommunity/master
Rolling up master to staging
2017-06-20 11:46:47 -07:00
Vinay Rao
513e44912c Merge pull request #2109 from SmartThingsCommunity/staging
Rolling down staging to master
2017-06-20 11:38:53 -07:00
Vinay Rao
f752a01906 Merge pull request #2108 from SmartThingsCommunity/staging
Rolling up staging to production for deploy
2017-06-20 11:29:13 -07:00
Vinay Rao
505ec4ff0f Merge pull request #2107 from larsfinander/DVCSMP-2703_OpenT2T_Update_6_20_submission_2_staging
DVCSMP-2703 OpenT2T: Update to 6/20 submission
2017-06-20 09:19:50 -07:00
Lars Finander
fe0f555f10 DVCSMP-2703 OpenT2T: Update to 6/20 submission 2017-06-20 10:17:49 -06:00
Vinay Rao
6e1701f955 Merge pull request #2106 from larsfinander/DVCSMP-2703_OpenT2T_Update_6_20_submission_staging
DVCSMP-2703 OpenT2T: Update to 6/20 submission
2017-06-20 09:09:52 -07:00
Lars Finander
9f5378c2b6 DVCSMP-2703 OpenT2T: Update to 6/20 submission 2017-06-20 10:06:23 -06:00
Vinay Rao
39e828b16d Merge pull request #2105 from SmartThingsCommunity/staging
Rolling down staging to master
2017-06-19 18:32:03 -07:00
Vinay Rao
42353044e6 Merge pull request #2104 from SmartThingsCommunity/production
Rolling down production to staging
2017-06-19 18:31:41 -07:00
Vinay Rao
78f06a0b9d Merge pull request #2103 from workingmonk/bug/appengine_version
Fix circle ci issues with new artifactory server
2017-06-19 16:25:32 -07:00
Vinay Rao
7f13dd356d Fix circle ci issues with new artifactory server 2017-06-19 16:24:58 -07:00
Vinay Rao
4042bbbf98 Merge pull request #2102 from SmartThingsCommunity/bug/deploy_script2
DVCSMP-2701 Update Deployment Jobs to new Artifactory.
2017-06-19 14:24:34 -07:00
Vinay Rao
20407441d9 DVCSMP-2701 Update Deployment Jobs to new Artifactory. set build gradle to new artifactory server 2017-06-19 14:23:44 -07:00
Vinay Rao
52925d6d99 Merge pull request #2093 from dkirker/DVCSMP-2698
DVCSMP-2698 Temporary bandage to properly show valve states in the device tile of local running devices
2017-06-19 12:15:01 -07:00
Vinay Rao
e6c17131af Merge pull request #2101 from SmartThingsCommunity/bug/deploy_script
DVCSMP-2701 Update Deployment Jobs to new Artifactory
2017-06-19 11:27:21 -07:00
Vinay Rao
cc70865fbf DVCSMP-2701 Update Deployment Jobs to new Artifactory 2017-06-19 11:21:05 -07:00
Jack Chi
91a77afb2d Merge pull request #2099 from pchomal/aeon_keyfob_hc
[DHF-14] Health Check Aeon Key Fob
2017-06-19 11:19:17 -07:00
Jack Chi
006ffa8a0b Merge pull request #2098 from pchomal/aeon_minimote_hc
[DHF-15] Health Check Aeon Minimote
2017-06-19 11:19:01 -07:00
piyush.c
17465c87c0 [DHF-14] Health Check Aeon Key Fob 2017-06-19 18:12:28 +05:30
piyush.c
370b435874 [DHF-15] Health Check Aeon Minimote 2017-06-19 18:02:29 +05:30
David Hastings
be9bdae4cc Merge pull request #2094 from unixbeast/iotevent_786
IOTEVENT-786 check for null/empty values before sending events
2017-06-16 14:49:19 -05:00
Dave Hastings
c278e035f6 IOTEVENT-786 check for null/empty values before sending events 2017-06-15 14:44:14 -05:00
Donald Kirker
e53d9c910c DVCSMP-2698 Temporary bandage to properly show valve states in the device tile of local running devices 2017-06-14 17:35:11 -07:00
Vinay Rao
c13014936b Merge pull request #2090 from SmartThingsCommunity/master
Rolling up master to staging
2017-06-13 15:25:30 -07:00
Vinay Rao
d6b0f6a8ed Merge pull request #2089 from SmartThingsCommunity/staging
Rolling down staging to master
2017-06-13 15:24:44 -07:00
Vinay Rao
62c810ba90 Merge pull request #2078 from dkirker/ICP-1110
ICP-1110 Read valve state during pairing
2017-06-13 15:23:07 -07:00
Vinay Rao
ea3abb26c0 Merge pull request #2088 from SmartThingsCommunity/staging
Rolling up staging to production
2017-06-13 14:10:07 -07:00
Donald Kirker
fb8e4a2416 ICP-1110 Read valve state during pairing 2017-06-07 23:47:13 -07:00
Vinay Rao
c4c2a3ffd7 Merge pull request #2072 from SmartThingsCommunity/staging
Rolling up staging to production
2017-06-06 11:35:06 -07:00
Vinay Rao
45663ffb86 changes to button controller to read number of buttons from the dth 2016-04-01 16:17:03 -07:00
30 changed files with 838 additions and 577 deletions

View File

@@ -9,7 +9,7 @@ apply plugin: 'smartthings-slack'
buildscript {
dependencies {
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.11"
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.12"
}
repositories {
mavenLocal()
@@ -19,7 +19,7 @@ buildscript {
username smartThingsArtifactoryUserName
password smartThingsArtifactoryPassword
}
url "https://artifactory.smartthings.com/libs-release-local"
url "https://smartthings.jfrog.io/smartthings/libs-release-local"
}
}
}
@@ -32,7 +32,7 @@ repositories {
username smartThingsArtifactoryUserName
password smartThingsArtifactoryPassword
}
url "https://artifactory.smartthings.com/libs-release-local"
url "https://smartthings.jfrog.io/smartthings/libs-release-local"
}
}
@@ -51,10 +51,10 @@ sourceSets {
dependencies {
devicetypesCompile 'org.codehaus.groovy:groovy-all:2.4.7'
devicetypesCompile 'smartthings:appengine-z-wave:0.1.2'
devicetypesCompile 'smartthings:appengine-zigbee:0.1.11'
devicetypesCompile 'smartthings:appengine-z-wave:0.1.3'
devicetypesCompile 'smartthings:appengine-zigbee:0.1.12'
smartappsCompile 'org.codehaus.groovy:groovy-all:2.4.7'
smartappsCompile 'smartthings:appengine-common:0.1.8'
smartappsCompile 'smartthings:appengine-common:0.1.9'
smartappsCompile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.1'
smartappsCompile 'org.grails:grails-web:2.3.11'
smartappsCompile 'org.json:json:20140107'
@@ -74,19 +74,19 @@ slackSendMessage {
String username
switch (branch) {
case 'master':
username = 'Hickory'
username = 'DEV'
iconUrl = wolverine
color = '#35D0F2'
messageText = 'Began deployment of _SmartThingsPublic[master]_ branch to the _Dev_ environments.'
break
case 'staging':
username = 'Dickory'
username = 'STG'
iconUrl = beach
color = '#FFDE20'
messageText = 'Began deployment of _SmartThingsPublic[staging]_ branch to the _Staging_ environments.'
break
case 'production':
username = 'Dock'
username = 'PRD'
iconUrl = drinks
color = '#FF1D23'
messageText = 'Began deployment of _SmartThingsPublic[production]_ branch to the _Prod_ environments.'

View File

@@ -0,0 +1,102 @@
/**
* FortrezZ MIMO2+ B-Side
*
* Copyright 2016 FortrezZ, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "FortrezZ MIMO2+ B-Side", namespace: "fortrezz", author: "FortrezZ, LLC") {
capability "Contact Sensor"
capability "Relay Switch"
capability "Switch"
capability "Voltage Measurement"
capability "Refresh"
}
tiles {
standardTile("switch", "device.switch", width: 2, height: 2) {
state "on", label: "Relay 2 On", action: "off", icon: "http://swiftlet.technology/wp-content/uploads/2016/06/Switch-On-104-edit.png", backgroundColor: "#53a7c0"
state "off", label: "Relay 2 Off", action: "on", icon: "http://swiftlet.technology/wp-content/uploads/2016/06/Switch-Off-104-edit.png", backgroundColor: "#ffffff"
}
standardTile("anaDig1", "device.anaDig1", inactiveLabel: false) {
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
state "val", label:'${currentValue}v', unit:"", defaultState: true
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("powered", "device.powered", inactiveLabel: false) {
state "powerOn", label: "Power On", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "powerOff", label: "Power Off", icon: "st.switches.switch.off", backgroundColor: "#ffa81e"
}
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
standardTile("blank", "device.blank", inactiveLabel: true, decoration: "flat") {
state("blank", label: '')
}
main (["switch"])
details(["switch", "anaDig1", "blank", "blank", "refresh", "powered"])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle 'contact' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'voltage' attribute
}
def eventParse(evt) {
log.debug("Event: ${evt.name}=${evt.value}")
switch(evt.name) {
case "powered":
sendEvent(name: evt.name, value: evt.value)
break
case "switch2":
sendEvent(name: "switch", value: evt.value)
break
case "contact2":
sendEvent(name: "contact", value: evt.value)
break
case "voltage2":
sendEvent(name: "voltage", value: evt.value)
break
case "relay2":
sendEvent(name: evt.name, value: evt.value)
break
case "anaDig2":
sendEvent(name: "anaDig1", value: evt.value)
break
}
}
// handle commands
def on() {
parent.on2(device.id)
log.debug("Executing 'on'")
// TODO: Send Event to parent device for "on2"
}
def off() {
parent.off2(device.id)
log.debug("Executing 'off'")
// TODO: Send Event to parent device for "off2"
}
def refresh() {
parent.refresh2(device.id)
log.debug("Executing 'refresh'")
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,34 @@
# Aeon Labs Key Fob
Cloud Execution
Works with:
* [Aeon Labs Key Fob](http://aeotec.com/z-wave-key-fob-remote-control)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Troubleshooting](#troubleshooting)
## Capabilities
* **Actuator** - represents device has commands
* **Button** - represents a device with one or more buttons
* **Holdable Button** - represents a device with one or more holdable buttons
* **Configuration** - allows for configuration of devices
* **Sensor** - detects sensor events
* **Battery** - defines device uses a battery
* **Health Check** - indicates ability to get device health notifications
## Device Health
Aeon Key Fob is a ZWave totally sleepy device and 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 sensor is out of range.
Pairing needs to be tried again by placing the sensor closer to the hub.
Instructions related to pairing, resetting and removing the Aeon Labs Key Fob from SmartThings can be found in the following link:
* [Aeotec Key Fob Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202294120-Aeon-Labs-Key-Fob)

View File

@@ -1,3 +1,4 @@
import groovy.json.JsonOutput
/**
* Copyright 2015 SmartThings
*
@@ -19,6 +20,7 @@ metadata {
capability "Configuration"
capability "Sensor"
capability "Battery"
capability "Health Check"
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x80,0x84,0x85"
fingerprint mfr: "0086", prod: "0001", model: "0026", deviceJoinName: "Aeon Panic Button"
@@ -131,6 +133,9 @@ 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)
def zwMap = getZwaveInfo()
def buttons = 4 // Default for Key Fob

View File

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

View File

@@ -0,0 +1,33 @@
# Aeon Minimote
Cloud Execution
Works with:
* [Aeotec Minimote](http://aeotec.com/small-z-wave-remote-control)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Troubleshooting](#troubleshooting)
## Capabilities
* **Actuator** - represents device has commands
* **Button** - represents a device with one or more buttons
* **Holdable Button** - represents a device with one or more holdable buttons
* **Configuration** - allows for configuration of devices
* **Sensor** - detects sensor events
* **Health Check** - indicates ability to get device health notifications
## Device Health
Aeon Minimote is a ZWave totally sleepy device and 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 sensor is out of range.
Pairing needs to be tried again by placing the sensor closer to the hub.
Instructions related to pairing, resetting and removing the Aeotec Minimote from SmartThings can be found in the following link:
* [Aeotec Minimote Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202087904-Aeotec-Minimote)

View File

@@ -1,3 +1,4 @@
import groovy.json.JsonOutput
/**
* Copyright 2015 SmartThings
*
@@ -18,6 +19,7 @@ metadata {
capability "Holdable Button"
capability "Configuration"
capability "Sensor"
capability "Health Check"
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B", outClusters: "0x26,0x2B"
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B,0x85,0x84", outClusters: "0x26" // old style with numbered buttons
@@ -119,5 +121,7 @@ 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)
sendEvent(name: "numberOfButtons", value: 4)
}

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

@@ -70,7 +70,7 @@ metadata {
state "heat", action:"switchMode", nextState: "updating", icon: "st.thermostat.heat"
state "cool", action:"switchMode", nextState: "updating", icon: "st.thermostat.cool"
state "auto", action:"switchMode", nextState: "updating", icon: "st.thermostat.auto"
state "emergency heat", action:"switchMode", icon: "st.thermostat.emergency-heat" // emergency heat = auxHeatOnly
state "auxheatonly", action:"switchMode", icon: "st.thermostat.emergency-heat"
state "updating", label:"Working", icon: "st.secondary.secondary"
}
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
@@ -156,47 +156,49 @@ void poll() {
def generateEvent(Map results) {
log.debug "parsing data $results"
if(results) {
results.each { name, value ->
def linkText = getLinkText(device)
def isChange = false
def isDisplayed = true
def supportedThermostatModes = ["off"]
def thermostatMode = null
results.each { name, value ->
def event = [name: name, linkText: linkText, descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name]
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) {
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger()
isChange = isTemperatureStateChange(device, name, value.toString())
isDisplayed = isChange
event << [value: sendValue, unit: temperatureScale, isStateChange: isChange, displayed: isDisplayed]
event << [value: sendValue, unit: temperatureScale]
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger()
event << [value: sendValue, unit: temperatureScale, displayed: false]
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
isChange = isStateChange(device, name, value.toString())
event << [value: value.toString(), isStateChange: isChange, displayed: false]
if (value == true) {
supportedThermostatModes << ((name == "auxHeatMode") ? "auxheatonly" : name - "Mode")
}
return // as we don't want to send this event here, proceed to next name/value pair
} else if (name=="thermostatFanMode"){
isChange = isStateChange(device, name, value.toString())
event << [value: value.toString(), isStateChange: isChange, displayed: false]
sendEvent(name: "supportedThermostatFanModes", value: fanModes(), displayed: false)
event << [value: value.toString(), data:[supportedThermostatFanModes: fanModes()]]
} else if (name=="humidity") {
isChange = isStateChange(device, name, value.toString())
event << [value: value.toString(), isStateChange: isChange, displayed: false, unit: "%"]
event << [value: value.toString(), displayed: false, unit: "%"]
} else if (name == "deviceAlive") {
isChange = isStateChange(device, name, value.toString())
event['isStateChange'] = isChange
event['displayed'] = false
} else if (name == "thermostatMode") {
def mode = value.toString()
mode = (mode == "auxHeatOnly") ? "emergency heat" : mode
isChange = isStateChange(device, name, mode)
event << [value: mode, isStateChange: isChange, displayed: isDisplayed]
thermostatMode = value.toLowerCase()
return // as we don't want to send this event here, proceed to next name/value pair
} else {
isChange = isStateChange(device, name, value.toString())
isDisplayed = isChange
event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed]
event << [value: value.toString()]
}
sendEvent(event)
}
if (state.supportedThermostatModes != supportedThermostatModes) {
state.supportedThermostatModes = supportedThermostatModes
sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: false)
}
if (thermostatMode) {
sendEvent(name: "thermostatMode", value: thermostatMode, data:[supportedThermostatModes:state.supportedThermostatModes], linkText: linkText,
descriptionText: getThermostatDescriptionText("thermostatMode", thermostatMode, linkText), handlerName: "thermostatMode")
}
generateSetpointEvent ()
generateStatusEvent ()
}
@@ -322,15 +324,7 @@ void resumeProgram() {
}
def modes() {
if (state.modes) {
log.debug "Modes = ${state.modes}"
return state.modes
}
else {
state.modes = parent.availableModes(this)
log.debug "Modes = ${state.modes}"
return state.modes
}
return state.supportedThermostatModes
}
def fanModes() {
@@ -413,11 +407,13 @@ def setThermostatFanMode(String mode) {
}
def generateModeEvent(mode) {
sendEvent(name: "thermostatMode", value: mode, descriptionText: "$device.displayName is in ${mode} mode", displayed: true)
sendEvent(name: "thermostatMode", value: mode, data:[supportedThermostatModes: state.supportedThermostatModes],
descriptionText: "$device.displayName is in ${mode} mode")
}
def generateFanModeEvent(fanMode) {
sendEvent(name: "thermostatFanMode", value: fanMode, descriptionText: "$device.displayName fan is in ${fanMode} mode", displayed: true)
sendEvent(name: "thermostatFanMode", value: fanMode, data:[supportedThermostatFanModes: fanModes()],
descriptionText: "$device.displayName fan is in ${fanMode} mode")
}
def generateOperatingStateEvent(operatingState) {
@@ -453,14 +449,14 @@ def heat() {
}
def emergencyHeat() {
auxHeatOnly()
auxheatonly()
}
def auxHeatOnly() {
log.debug "auxHeatOnly = emergency heat"
def auxheatonly() {
log.debug "auxheatonly()"
def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode ("auxHeatOnly", deviceId))
generateModeEvent("emergency heat") // emergency heat = auxHeatOnly
generateModeEvent("auxheatonly")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
@@ -593,7 +589,7 @@ def generateSetpointEvent() {
} else if (mode == "off") {
sendEvent("name":"thermostatSetpoint", "value":averageSetpoint, "unit":location.temperatureScale)
sendEvent("name":"displayThermostatSetpoint", "value":"Off", displayed: false)
} else if (mode == "emergency heat") { // emergency heat = auxHeatOnly
} else if (mode == "auxheatonly") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"displayThermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale, displayed: false)
}
@@ -632,7 +628,7 @@ void raiseSetpoint() {
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0
targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5
if ((mode == "heat" || mode == "emergency heat") && targetvalue > maxHeatingSetpoint) { // emergency heat = auxHeatOnly
if ((mode == "heat" || mode == "auxheatonly") && targetvalue > maxHeatingSetpoint) {
targetvalue = maxHeatingSetpoint
} else if (mode == "cool" && targetvalue > maxCoolingSetpoint) {
targetvalue = maxCoolingSetpoint
@@ -678,7 +674,7 @@ void lowerSetpoint() {
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0
targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5
if ((mode == "heat" || mode == "emergency heat") && targetvalue < minHeatingSetpoint) { // emergency heat = auxHeatOnly
if ((mode == "heat" || mode == "auxheatonly") && targetvalue < minHeatingSetpoint) {
targetvalue = minHeatingSetpoint
} else if (mode == "cool" && targetvalue < minCoolingSetpoint) {
targetvalue = minCoolingSetpoint
@@ -719,7 +715,7 @@ void alterSetpoint(temp) {
}
//step1: check thermostatMode, enforce limits before sending request to cloud
if (mode == "heat" || mode == "emergency heat"){ // emergency heat = auxHeatOnly
if (mode == "heat" || mode == "auxheatonly"){
if (temp.value > coolingSetpoint){
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
@@ -754,7 +750,7 @@ void alterSetpoint(temp) {
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
} else {
log.error "Error alterSetpoint()"
if (mode == "heat" || mode == "emergency heat"){ // emergency heat = auxHeatOnly
if (mode == "heat" || mode == "auxheatonly"){
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
sendEvent("name": "displayThermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
} else if (mode == "cool") {
@@ -783,7 +779,7 @@ def generateStatusEvent() {
log.debug "Cooling set point = ${coolingSetpoint}"
log.debug "HVAC Mode = ${mode}"
if (mode == "heat") {
if (mode == "heat" || mode == "auxheatonly") {
if (temperature >= heatingSetpoint) {
statusText = "Right Now: Idle"
} else {
@@ -806,8 +802,6 @@ def generateStatusEvent() {
}
} else if (mode == "off") {
statusText = "Right Now: Off"
} else if (mode == "emergency heat") { // emergency heat = auxHeatOnly
statusText = "Emergency Heat"
} else {
statusText = "?"
}

View File

@@ -300,15 +300,21 @@ def setColor(value) {
value.hex = "#${hex(value.red)}${hex(value.green)}${hex(value.blue)}"
}
sendEvent(name: "hue", value: value.hue, displayed: false)
sendEvent(name: "saturation", value: value.saturation, displayed: false)
sendEvent(name: "color", value: value.hex, displayed: false)
if (value.level) {
sendEvent(name: "level", value: value.level)
}
if (value.switch) {
sendEvent(name: "switch", value: value.switch)
}
if(value.hue) {
sendEvent(name: "hue", value: value.hue, displayed: false)
}
if(value.saturation) {
sendEvent(name: "saturation", value: value.saturation, displayed: false)
}
if(value.hex?.trim()) {
sendEvent(name: "color", value: value.hex, displayed: false)
}
if (value.level) {
sendEvent(name: "level", value: value.level)
}
if (value.switch?.trim()) {
sendEvent(name: "switch", value: value.switch)
}
sendRGB(value.rh, value.gh, value.bh)
}

View File

@@ -35,8 +35,8 @@ metadata {
// tile definitions
tiles(scale: 2) {
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
@@ -48,14 +48,16 @@ metadata {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "valve"
details(["valve","refresh"])
main "contact"
details(["contact","refresh"])
}
}
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(){
@@ -85,11 +87,17 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
}
def open() {
zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00).format()
delayBetween([
zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00).format(),
zwave.switchBinaryV1.switchBinaryGet().format()
], 500)
}
def close() {
zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF).format()
delayBetween([
zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF).format(),
zwave.switchBinaryV1.switchBinaryGet().format()
], 500)
}
/**
@@ -105,6 +113,6 @@ def refresh() {
def createEventWithDebug(eventMap) {
def event = createEvent(eventMap)
log.debug "Event created with ${event?.descriptionText}"
log.debug "Event created with ${event?.name}:${event?.value} - ${event?.descriptionText}"
return event
}

View File

@@ -23,7 +23,6 @@ metadata {
capability "Refresh"
capability "Sensor"
capability "Health Check"
capability "Light"
capability "Outlet"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,8 +38,8 @@ metadata {
}
tiles(scale: 2) {
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
@@ -58,8 +58,8 @@ metadata {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["valve"])
details(["valve", "battery", "refresh"])
main(["contact"])
details(["contact", "battery", "refresh"])
}
}

View File

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

View File

@@ -58,6 +58,7 @@ metadata {
def installed() {
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
response(refresh())
}
def updated() {

View File

@@ -1,412 +0,0 @@
/**
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
definition(
name: "Tcp Bulbs (Connect)",
namespace: "mujica",
author: "SmartThings-Ule",
description: "Connect your TCP bulbs to SmartThings using local integration. You must have a geteway with firmware ver 2",
category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/tcp.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/tcp@2x.png",
singleInstance: true
)
preferences {
page(name: "iniSettings", title: "Connect Your TCP Lights to SmartThings", content: "iniSettings")
page(name: "chooseBulbs", title: "Choose Bulbs to Control With SmartThings", content: "bulbDiscovery")
}
def iniSettings(){
state.loadStatus = "Inactive"
log.trace "state.loadStatus ${state.loadStatus}"
return dynamicPage(name:"iniSettings", title:"Connect Your TCP Lights to SmartThings", nextPage:"chooseBulbs", install:false, uninstall: true) {
section("TCP Connected Remote Credentials") {
input "ipGateway", "text", title: "Enter TCP Gateway IP", required: true
paragraph "Tap 'Next' after you have entered the ip of your TCP Connected Gateway.\r\n\r\nOnce your ip are accepted, SmartThings will scan your TCP installation for Bulbs."
}
}
}
def bulbDiscovery() {
debugOut "bulbDiscovery()"
//getToken()
state.token = "1234567890"
if (state.loadStatus == "Inactive"){
state.count = 0
state.loadStatus = "Loading"
log.trace "state.loadStatus ${state.loadStatus}"
deviceDiscovery()
}
log.trace "state.count ${state.count}"
state.count = state.count + 1
log.trace "state.count ${state.count}"
if(state.loadStatus == "Loaded" ){
def options = devicesDiscovered() ?: []
log.trace "state.loadStatus ${state.loadStatus}"
return dynamicPage(name:"chooseBulbs", title:"", nextPage:"", install:true, uninstall: true) {
section("Tap Below to View Device List") {
input "selectedBulbs", "enum", required:false, title:"Select Bulb", multiple:true, options:options
paragraph """Tap 'Done' after you have selected the desired devices."""
}
}
}else{
if (state.count)
log.trace "state.loadStatus ${state.loadStatus}"
def msg = state.count >= 3 ? "The TCP Gateway is not responding, please verify the ip address" : "Please wait while we discover your devices. Discovery can take some minutes or more, so sit back and relax! Select your device below once discovered."
return dynamicPage(name:"chooseBulbs", title:"", nextPage:"", refreshInterval:5) {
section(msg) {}
}
}
}
def installed() {
debugOut "Installed with settings: ${settings}"
unschedule()
unsubscribe()
setupBulbs()
def cron = "0 0/1 * * * ?"
log.debug "schedule('$cron', syncronizeDevices)"
schedule(cron, syncronizeDevices)
}
def updated() {
debugOut "Updated with settings: ${settings}"
unschedule()
setupBulbs()
def cron = "0 0/1 * * * ?"
log.debug "schedule('$cron', syncronizeDevices)"
schedule(cron, syncronizeDevices)
}
def uninstalled()
{
unschedule() //in case we have hanging runIn()'s
}
private removeChildDevices(delete)
{
debugOut "deleting ${delete.size()} bulbs"
debugOut "deleting ${delete}"
delete.each {
deleteChildDevice(it.device.deviceNetworkId)
}
}
def uninstallFromChildDevice(childDevice)
{
def errorMsg = "uninstallFromChildDevice was called and "
if (!settings.selectedBulbs) {
debugOut errorMsg += "had empty list passed in"
return
}
def dni = childDevice.device.deviceNetworkId
if ( !dni ) {
debugOut errorMsg += "could not find dni of device"
return
}
def newDeviceList = settings.selectedBulbs - dni
app.updateSetting("selectedBulbs", newDeviceList)
debugOut errorMsg += "completed succesfully"
}
def setupBulbs() {
debugOut "setupBulbs()"
def bulbs = state.devices
def deviceFile = "TCP Bulb"
selectedBulbs.each { did ->
//see if this is a selected bulb and install it if not already
def d = getChildDevice(did)
if(!d) {
def newBulb = bulbs.find { (it.did) == did }
d = addChildDevice("mujica", deviceFile, did, null, [name: "${newBulb?.name}", label: "${newBulb?.name}", completedSetup: true,"data":["model":newBulb?.model,"nodetype":newBulb?.nodetype,"node":newBulb?.node,"dni":did]])
} else {
infoOut "Avoid add existent device ${did}"
}
}
def delete = getChildDevices().findAll { !selectedBulbs?.contains(it.deviceNetworkId) }
removeChildDevices(delete)
}
def deviceDiscovery() {
log.trace "deviceDiscovery()"
def data = "<gip><version>1</version><token>${state.token}</token></gip>"
def Params = [
cmd: "RoomGetCarousel",
data: "${data}",
fmt: "json"
]
def cmd = toQueryString(Params)
debugOut "deviceDiscovery()"
apiGet(cmd,"RoomGetCarouselHandler")
}
def apiGet(String data, String calledBackHandler) {
debugOut "apiGet($data, $calledBackHandler) $ipGateway"
sendHubCommand(new physicalgraph.device.HubAction([
method: "GET",
path: "/gwr/gop.php?$data",
headers: [
HOST: "$ipGateway:80"
]], getNetworkId("$ipGateway","80"), [callback: calledBackHandler]))
}
void SendCommandHandler(physicalgraph.device.HubResponse hubResponse){
debugOut "SendCommandHandler($hubResponse)"
debugOut "hubResponse.body ${hubResponse.body}"
}
void RoomGetCarouselHandler(physicalgraph.device.HubResponse hubResponse){
debugOut "RoomGetCarouselHandler($hubResponse)"
def bodyXml
if (hubResponse?.body?.contains("<gip>"))
{ // description.xml response (application/xml)
debugOut "body contains xml"
bodyXml = new XmlSlurper().parseText(hubResponse.body)
debugOut "bodyXml $bodyXml"
}
def rooms = ""
def devices = []
def deviceList = []
rooms = bodyXml.room
debugOut "rooms ${rooms[1]}"
rooms.each({
devices = it.device
debugOut "it.device ${it.device}"
def roomName = it.name
debugOut "roomName = ${it.name}"
debugOut "devices[1] ${devices[1]}"
debugOut "devices[1] != null"
def roomId = it?.rid
debugOut "Room Device Data: did:${roomId} roomName:${roomName}"
devices.each({
debugOut "Bulb Device Data: did:${it?.did} room:${roomName} BulbName:${it?.name}"
deviceList += ["name" : "${roomName} ${it?.name}", "did" : "${it?.did}", "type" : "${devices?.type}", "node" : "${devices?.node}", "nodetype" : "${devices?.nodetype}", "model" : "${devices?.prodmodel}"]
})
})
devices = ["devices" : deviceList]
debugOut "devices $devices"
state.devices = devices.devices
state.loadStatus = "Loaded"
}
def getDevices()
{
state.devices = state.devices ?: [:]
}
void RoomGetCarouselUpdateHandler(physicalgraph.device.HubResponse hubResponse){
debugOut "RoomGetCarouselUpdateHandler($hubResponse)"
debugOut "msg ${parseLanMessage(hubResponse.body)}"
def bodyXml
if (hubResponse?.body?.contains("<gip>"))
{
debugOut "body contains xml"
bodyXml = new XmlSlurper().parseText(hubResponse.body)
}
def rooms = ""
def devices = []
def deviceList = []
rooms = bodyXml.room
rooms.each({
devices = it.device
devices.each({
def dni = it.did.text()
def bulb = getChildDevice(dni)
if ( bulb ){
def power = it.power ? it.power.text() as float :0
sendEvent( dni, [name: "power", value: power*1000] )
if (( it.state.text() == "1" ) && ( bulb?.currentValue("switch") != "on" ))
sendEvent( dni, [name: "switch",value:"on"] )
if (( it.state.text() == "0" ) && ( bulb?.currentValue("switch") != "off" ))
sendEvent( dni, [name: "switch",value:"off"] )
if ( it.level.text() != bulb?.currentValue("level")) {
sendEvent( dni, [name: "level",value: "${it.level.text()}"] )
sendEvent( dni, [name: "setLevel",value: "${it.level.text()}"] )
}
}
})
})
}
Map devicesDiscovered() {
def devices = state.devices
def map = [:]
if (devices instanceof java.util.Map) {
devices.each {
def value = "${it?.name}"
def key = it?.did
map["${key}"] = value
}
} else { //backwards compatable
devices.each {
def value = "${it?.name}"
def key = it?.did
map["${key}"] = value
}
}
map
}
def getToken() {
state.token = "1234567890"
}
String toQueryString(Map m) {
return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&")
}
def syncronizeDevices() {
poll(null)
}
def getNetworkId(ipaddr, port) {
"${ipaddr.tokenize('.').collect {String.format('%02X', it.toInteger())}.join()}:${String.format('%04X', port.toInteger())}"
}
/**************************************************************************
Child Device Call In Methods
**************************************************************************/
def on(childDevice) {
def dni = childDevice.device.deviceNetworkId
def data = ""
def cmd = ""
data = "<gip><version>1</version><token>$state.token</token><did>${dni}</did><type>power</type><value>1</value></gip>"
cmd = "DeviceSendCommand"
def qParams = [
cmd: cmd,
data: "${data}",
fmt: "json"
]
cmd = toQueryString(qParams)
apiGet(cmd,"SendCommandHandler" )
}
def off(childDevice) {
def dni = childDevice.device.deviceNetworkId
def data = ""
def cmd = ""
data = "<gip><version>1</version><token>$state.token</token><did>${dni}</did><type>power</type><value>0</value></gip>"
cmd = "DeviceSendCommand"
def qParams = [
cmd: cmd,
data: "${data}",
fmt: "json"
]
cmd = toQueryString(qParams)
apiGet(cmd,"SendCommandHandler")
}
def setLevel(childDevice, value) {
debugOut "setLevel request from child device"
def dni = childDevice.device.deviceNetworkId
def data = ""
def cmd = ""
data = "<gip><version>1</version><token>${state.token}</token><did>${dni}</did><type>level</type><value>${value}</value></gip>"
cmd = "DeviceSendCommand"
def qParams = [
cmd: cmd,
data: "${data}",
fmt: "json"
]
cmd = toQueryString(qParams)
apiGet(cmd,"SendCommandHandler")
}
def poll(childDevice) {
infoOut "poll()"
def eventTime = new Date().time
if ((state.lastPollTime ?:0) + 10000 <= eventTime ){
state.lastPollTime = new Date().time
def Params = [
cmd: "RoomGetCarousel",
data: "<gip><version>1</version><token>${state.token}</token></gip>",
fmt: "json"
]
def cmd = toQueryString(Params)
apiGet(cmd,"RoomGetCarouselUpdateHandler")
}else{
infoOut "Multiple poll requests avoided"
}
}
/**************************************************************************
Msg Methods
**************************************************************************/
def debugOut(msg) {
//log.debug msg
}
def traceOut(msg) {
log.trace msg
}
def infoOut(msg) {
log.info msg
}

View File

@@ -195,7 +195,10 @@ def registerDeviceChange() {
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
} else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)) {
state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
// state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
// For now, we will only have one subscription endpoint per device
state.deviceSubscriptionMap.remove(deviceId)
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
}

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