├── README.md
├── device-types
├── README.md
├── momentary-button-tile
│ ├── README.md
│ └── momentary-button-tile.device.groovy
├── raspberry-pi
│ ├── README.md
│ ├── raspberry-pi.device.groovy
│ └── raspberrypi.py
├── spark-core-illuminance-sensor
│ ├── README.md
│ ├── spark-core-illuminance-sensor.device.groovy
│ └── spark-core-illuminance-sensor.ino
├── spark-core-relative-humidity-sensor
│ ├── README.md
│ ├── spark-core-relative-humidity-sensor.device.groovy
│ └── spark-core-relative-humidity-sensor.ino
└── spark-core-temperature-sensor
│ ├── README.md
│ ├── spark-core-temperature-sensor.device.groovy
│ └── spark-core-temperature-sensor.ino
├── devicetypes
└── nicholaswilde
│ └── plug-in-zwave-dimmer.src
│ └── plug-in-zwave-dimmer.groovy
└── smartapps
├── README.md
├── device-poll
├── README.md
└── device-poll.app.groovy
├── nicholaswilde
└── samsung-tv-remote.src
│ ├── Readme.md
│ └── samsung-tv-remote.groovy
├── notify-when-on-for
├── README.md
└── notify-when-on-for.app.groovy
└── thingspeak-logger-ii
├── README.md
├── thingspeak-logger-ii.app.groovy
├── thingspeak.png
└── thingspeak@2x.png
/README.md:
--------------------------------------------------------------------------------
1 | SmartThings
2 | ===========
3 |
4 | This is a repository of all of my custom [SmartThings](http://www.smartthings.com/) programs, which include both [Device Types](http://docs.smartthings.com/en/latest/device-type-developers-guide/index.html) and [SmartApps](http://docs.smartthings.com/en/latest/smartapp-developers-guide/index.html).
5 |
--------------------------------------------------------------------------------
/device-types/README.md:
--------------------------------------------------------------------------------
1 | Device Types
2 | ============
3 |
4 | All of my custom [Device Types](http://docs.smartthings.com/en/latest/device-type-developers-guide/index.html).
5 |
6 | ## Sark Core Installation Instructions
7 | 1. For Spark Core Device Types, first install the Spark Core sketch (*.ino file) using [these instructions](http://docs.spark.io/start/#flash-apps-with-spark-build-flashing-your-first-app).
8 | 2. Install the
9 |
--------------------------------------------------------------------------------
/device-types/momentary-button-tile/README.md:
--------------------------------------------------------------------------------
1 | Momentary Button Tile
2 | ====================
3 | Creates a virtual momentary button tile which can be used to trigger events in SmartApps.
4 |
5 | One example is to use it with [Samsung TV Remote SmartApp to trigger](https://github.com/nicholaswilde/smartthings/blob/master/smartapps/samsung-tv-remote/samsung-tv-remote.app.groovy) a command on a Samsung TV.
6 |
--------------------------------------------------------------------------------
/device-types/momentary-button-tile/momentary-button-tile.device.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Momentary Button Tile
3 | *
4 | * Author: SmartThings
5 | *
6 | * Date: 2013-05-01
7 | */
8 | metadata {
9 | // Automatically generated. Make future change here.
10 | definition (name: "Momentary Button Tile", namespace: "smartthings", author: "SmartThings") {
11 | capability "Actuator"
12 | capability "Switch"
13 | capability "Momentary"
14 | capability "Sensor"
15 | }
16 |
17 | // simulator metadata
18 | simulator {
19 | }
20 |
21 | // UI tile definitions
22 | tiles {
23 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
24 | state "off", label: 'Push', action: "momentary.push", backgroundColor: "#ffffff", nextState: "on"
25 | state "on", label: 'Push', action: "momentary.push", backgroundColor: "#53a7c0"
26 | }
27 | main "switch"
28 | details "switch"
29 | }
30 | }
31 |
32 | def parse(String description) {
33 | }
34 |
35 | def push() {
36 | sendEvent(name: "switch", value: "on", isStateChange: true, display: false)
37 | sendEvent(name: "switch", value: "off", isStateChange: true, display: false)
38 | sendEvent(name: "momentary", value: "pushed", isStateChange: true)
39 | }
40 |
41 | def on() {
42 | push()
43 | }
44 |
45 | def off() {
46 | push()
47 | }
48 |
--------------------------------------------------------------------------------
/device-types/raspberry-pi/README.md:
--------------------------------------------------------------------------------
1 | ## Deprecated
2 | Try using [berryio-smartthings](https://github.com/nicholaswilde/berryio-smartthings).
3 |
4 | ## Raspberry Pi Device Type
5 | Monitor your [Raspberry Pi](http://www.raspberrypi.org/) with [SmartThings](http://www.smartthings.com/) and [WebIOPi](https://code.google.com/p/webiopi/).
6 |
7 | The setup requires three things; the SmartThings Raspberry Pi Device Type, WebIOPi, and the WebIOPi python script macro (raspberrypi.py).
8 |
9 | **Note:** If you're using a [Raspberry Pi 2](https://www.raspberrypi.org/products/raspberry-pi-2-model-b/), you might have to use [berryio](https://github.com/NeonHorizon/berryio). See [Ledridge's](https://github.com/Ledridge) [RaspberryPi2](https://github.com/Ledridge/SmartThings/blob/master/RaspberryPi2).
10 |
11 | 
12 |
13 | ### Check your SmartThings Hub firmware version
14 | Your firmware version needs to be greater than `000.010.00246`. Has been proven to work with `000.011.00603` and not with `000.010.00246`.
15 | - Check your hub firmware version [here](https://graph.api.smartthings.com/hub/list).
16 |
17 | ### WebIOPi Installation Instructions
18 | - Following [these instructions](https://code.google.com/p/webiopi/wiki/INSTALL?tm=6) for installing WebIOPi.
19 |
20 | ### WebIOPi Python Script Marco Installation Instructions
21 | The python script macro is used by WebIOPi to pipe out the information from the Raspberry Pi to SmartThings using a REST API. The script requires psutil for Python3 which also requires pip-3.2.
22 | - Install [pip-3.2](https://github.com/pypa/pip)
23 | ```
24 | $ sudo apt-get install python3-pip
25 | ```
26 | - Install [psutil](https://github.com/giampaolo/psutil)
27 | ```
28 | $ sudo pip-3.2 install psutil
29 | ```
30 | - Check that the install was successful
31 | ```
32 | $ python3
33 | >>> import psutil
34 | >>> exit()
35 | ```
36 | - Reboot your Raspberry Pi.
37 | ```
38 | $ sudo reboot
39 | ```
40 | - Create a directory and download the python script. See [this for reference](https://code.google.com/p/webiopi/wiki/Tutorial_Basis).
41 | ```
42 | $ mkdir -p ~/myproject/python
43 | $ cd ~/myproject/python/
44 | $ wget https://raw.githubusercontent.com/nicholaswilde/smartthings/master/device-types/raspberry-pi/raspberrypi.py
45 | $ sudo chmod +x raspberrypi.py
46 | ```
47 | - Add the script to the WebIOPi configuration file. See [this for reference](https://code.google.com/p/webiopi/wiki/Tutorial_Basis).
48 | ```
49 | $ sudo nano /etc/webiopi/config
50 | ```
51 | ```
52 | ...
53 | [SCRIPTS]
54 | myproject = /home/pi/myproject/python/raspberrypi.py
55 | ...
56 | ```
57 | - Save the configuration file and restart the `webiopi` service
58 | ```
59 | $ sudo service webiopi restart
60 | ```
61 |
62 | ### Rasberry Pi SmartThings Device Type Installation Instructions
63 | - Create and account or log into the [SmartThings Web IDE](https://graph.api.smartthings.com/login/auth).
64 | - Click on `My Device Types` from the navigation menu.
65 | - Click on `+ New SmartDevice` button.
66 | - Fill in the `Name` field and click on the `Create` button. Don't worry about filling out everything else.
67 | - Paste the code from the [raspberry-pi.device.groovy](https://github.com/nicholaswilde/smartthings/blob/master/device-types/raspberry-pi/raspberry-pi.device.groovy) file.
68 | - Click on `Save` in the IDE.
69 | - Click on `Publish -> For Me` in the IDE.
70 |
71 | ### Raspberry Pi SmartThings Device Installation Instructions
72 | The device type is a generic template for a specific type of device. The Raspberry Pi device itself needs to be added to SmartThings.
73 | - Click on `My Devices` in the SmartThings Web IDE navigation menu.
74 | - Click on `+ New Device` button.
75 | - Specify the following fields:
76 | - Name: `Raspberry Pi`
77 | - Device Network Id: `123` (can be anything at the moment because the Raspberry Pi device type will change it for you).
78 | - Type: `Raspberry Pi` (Name of your new device type)
79 | - Version: `Published`
80 | - Location: your location (e.g. `Home`)
81 | - Hub: your hub (This is a must or else the Raspberry Pi device won't work!)
82 | - Group: Can leave blank.
83 | - Click on the `Create` button.
84 | - Click on the `Raspberry Pi` display name from the `My Devices` list.
85 | - Go to `Preference` and click `edit`
86 | - Enter in your settings;
87 | - IP address: RPi IP address
88 | - Port: `8000`(default for WebIOPi)
89 | - WebIOPi username: `webiopi` (default for WebIOPi)
90 | - WebIOPi password: `raspberry` (default for WebIOPi)
91 | - Click the `Save` button.
92 |
93 | **Congratulations. Your Raspberry Pi should now be listed in the `Things` category of your SmartThings mobile app!**
94 |
95 | ### Troubleshooting
96 | - Ensure that your SmartThings hub is up to date (see above).
97 | - [Forum topic](http://community.smartthings.com/t/raspberry-pi-device-type/4969)
98 | - Ensure that the `Device Network Id` and `Hub` fields are filled in under your Raspberry Pi device in your [Device List](https://graph.api.smartthings.com/device/list)
99 |
--------------------------------------------------------------------------------
/device-types/raspberry-pi/raspberry-pi.device.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Raspberry Pi
3 | *
4 | * Copyright 2014 Nicholas Wilde
5 | *
6 | * Monitor your Raspberry Pi using SmartThings and WebIOPi
7 | *
8 | * Companion WebIOPi python script can be found here:
9 | *
10 | *
11 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
12 | * in compliance with the License. You may obtain a copy of the License at:
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
17 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
18 | * for the specific language governing permissions and limitations under the License.
19 | *
20 | */
21 |
22 | import groovy.json.JsonSlurper
23 |
24 | preferences {
25 | input("ip", "string", title:"IP Address", description: "192.168.1.150", required: true, displayDuringSetup: true)
26 | input("port", "string", title:"Port", description: "8000", defaultValue: 8000 , required: true, displayDuringSetup: true)
27 | input("username", "string", title:"Username", description: "webiopi", required: true, displayDuringSetup: true)
28 | input("password", "password", title:"Password", description: "Password", required: true, displayDuringSetup: true)
29 | }
30 |
31 | metadata {
32 | definition (name: "Raspberry Pi", namespace: "nicholaswilde/smartthings", author: "Nicholas Wilde") {
33 | capability "Polling"
34 | capability "Refresh"
35 | capability "Temperature Measurement"
36 | capability "Switch"
37 | capability "Sensor"
38 | capability "Actuator"
39 |
40 | attribute "cpuPercentage", "string"
41 | attribute "memory", "string"
42 | attribute "diskUsage", "string"
43 |
44 | command "restart"
45 | }
46 |
47 | simulator {
48 | // TODO: define status and reply messages here
49 | }
50 |
51 | tiles {
52 | valueTile("temperature", "device.temperature", width: 1, height: 1) {
53 | state "temperature", label:'${currentValue}° CPU', unit: "F",
54 | backgroundColors:[
55 | [value: 25, color: "#153591"],
56 | [value: 35, color: "#1e9cbb"],
57 | [value: 47, color: "#90d2a7"],
58 | [value: 59, color: "#44b621"],
59 | [value: 67, color: "#f1d801"],
60 | [value: 76, color: "#d04e00"],
61 | [value: 77, color: "#bc2323"]
62 | ]
63 | }
64 | standardTile("button", "device.switch", width: 1, height: 1, canChangeIcon: true) {
65 | state "off", label: 'Off', icon: "st.Electronics.electronics18", backgroundColor: "#ffffff", nextState: "on"
66 | state "on", label: 'On', icon: "st.Electronics.electronics18", backgroundColor: "#79b821", nextState: "off"
67 | }
68 | valueTile("cpuPercentage", "device.cpuPercentage", inactiveLabel: false) {
69 | state "default", label:'${currentValue}% CPU', unit:"Percentage",
70 | backgroundColors:[
71 | [value: 31, color: "#153591"],
72 | [value: 44, color: "#1e9cbb"],
73 | [value: 59, color: "#90d2a7"],
74 | [value: 74, color: "#44b621"],
75 | [value: 84, color: "#f1d801"],
76 | [value: 95, color: "#d04e00"],
77 | [value: 96, color: "#bc2323"]
78 | ]
79 | }
80 | valueTile("memory", "device.memory", width: 1, height: 1) {
81 | state "default", label:'${currentValue} MB', unit:"MB",
82 | backgroundColors:[
83 | [value: 353, color: "#153591"],
84 | [value: 287, color: "#1e9cbb"],
85 | [value: 210, color: "#90d2a7"],
86 | [value: 133, color: "#44b621"],
87 | [value: 82, color: "#f1d801"],
88 | [value: 26, color: "#d04e00"],
89 | [value: 20, color: "#bc2323"]
90 | ]
91 | }
92 | valueTile("diskUsage", "device.diskUsage", width: 1, height: 1) {
93 | state "default", label:'${currentValue}% Disk', unit:"Percent",
94 | backgroundColors:[
95 | [value: 31, color: "#153591"],
96 | [value: 44, color: "#1e9cbb"],
97 | [value: 59, color: "#90d2a7"],
98 | [value: 74, color: "#44b621"],
99 | [value: 84, color: "#f1d801"],
100 | [value: 95, color: "#d04e00"],
101 | [value: 96, color: "#bc2323"]
102 | ]
103 | }
104 | standardTile("restart", "device.restart", inactiveLabel: false, decoration: "flat") {
105 | state "default", action:"restart", label: "Restart", displayName: "Restart"
106 | }
107 | standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
108 | state "default", action:"refresh.refresh", icon: "st.secondary.refresh"
109 | }
110 | main "button"
111 | details(["button", "temperature", "cpuPercentage", "memory" , "diskUsage", "restart", "refresh"])
112 | }
113 | }
114 |
115 | // ------------------------------------------------------------------
116 |
117 | // parse events into attributes
118 | def parse(String description) {
119 | def map = [:]
120 | def descMap = parseDescriptionAsMap(description)
121 | //log.debug descMap
122 | def body = new String(descMap["body"].decodeBase64())
123 | //log.debug "body: ${body}"
124 | def slurper = new JsonSlurper()
125 | def result = slurper.parseText(body)
126 | log.debug "result: ${result}"
127 | if (result){
128 | log.debug "Computer is up"
129 | sendEvent(name: "switch", value: "on")
130 | }
131 |
132 |
133 | if (result.containsKey("cpu_temp")) {
134 | sendEvent(name: "temperature", value: result.cpu_temp)
135 | }
136 |
137 | if (result.containsKey("cpu_perc")) {
138 | sendEvent(name: "cpuPercentage", value: result.cpu_perc)
139 | }
140 |
141 | if (result.containsKey("mem_avail")) {
142 | log.debug "mem_avail: ${result.mem_avail}"
143 | sendEvent(name: "memory", value: result.mem_avail)
144 | }
145 | if (result.containsKey("disk_usage")) {
146 | log.debug "disk_usage: ${result.disk_usage}"
147 | sendEvent(name: "diskUsage", value: result.disk_usage)
148 | }
149 |
150 | }
151 |
152 | // handle commands
153 | def poll() {
154 | log.debug "Executing 'poll'"
155 | sendEvent(name: "switch", value: "off")
156 | getRPiData()
157 | }
158 |
159 | def refresh() {
160 | sendEvent(name: "switch", value: "off")
161 | log.debug "Executing 'refresh'"
162 | getRPiData()
163 | }
164 |
165 | def restart(){
166 | log.debug "Restart was pressed"
167 | sendEvent(name: "switch", value: "off")
168 | def uri = "/macros/reboot"
169 | postAction(uri)
170 | }
171 |
172 | // Get CPU percentage reading
173 | private getRPiData() {
174 | def uri = "/macros/getData"
175 | postAction(uri)
176 | }
177 |
178 | // ------------------------------------------------------------------
179 |
180 | private postAction(uri){
181 | setDeviceNetworkId(ip,port)
182 |
183 | def userpass = encodeCredentials(username, password)
184 |
185 | def headers = getHeader(userpass)
186 |
187 | def hubAction = new physicalgraph.device.HubAction(
188 | method: "POST",
189 | path: uri,
190 | headers: headers
191 | )//,delayAction(1000), refresh()]
192 | log.debug("Executing hubAction on " + getHostAddress())
193 | //log.debug hubAction
194 | hubAction
195 | }
196 |
197 | // ------------------------------------------------------------------
198 | // Helper methods
199 | // ------------------------------------------------------------------
200 |
201 | def parseDescriptionAsMap(description) {
202 | description.split(",").inject([:]) { map, param ->
203 | def nameAndValue = param.split(":")
204 | map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
205 | }
206 | }
207 |
208 | private encodeCredentials(username, password){
209 | log.debug "Encoding credentials"
210 | def userpassascii = "${username}:${password}"
211 | def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
212 | //log.debug "ASCII credentials are ${userpassascii}"
213 | //log.debug "Credentials are ${userpass}"
214 | return userpass
215 | }
216 |
217 | private getHeader(userpass){
218 | log.debug "Getting headers"
219 | def headers = [:]
220 | headers.put("HOST", getHostAddress())
221 | headers.put("Authorization", userpass)
222 | //log.debug "Headers are ${headers}"
223 | return headers
224 | }
225 |
226 | private delayAction(long time) {
227 | new physicalgraph.device.HubAction("delay $time")
228 | }
229 |
230 | private setDeviceNetworkId(ip,port){
231 | def iphex = convertIPtoHex(ip)
232 | def porthex = convertPortToHex(port)
233 | device.deviceNetworkId = "$iphex:$porthex"
234 | log.debug "Device Network Id set to ${iphex}:${porthex}"
235 | }
236 |
237 | private getHostAddress() {
238 | return "${ip}:${port}"
239 | }
240 |
241 | private String convertIPtoHex(ipAddress) {
242 | String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
243 | return hex
244 |
245 | }
246 |
247 | private String convertPortToHex(port) {
248 | String hexport = port.toString().format( '%04x', port.toInteger() )
249 | return hexport
250 | }
251 |
--------------------------------------------------------------------------------
/device-types/raspberry-pi/raspberrypi.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # To be used with WebIOPi
4 |
5 | import webiopi
6 | import psutil
7 | import os
8 | import json
9 | from threading import Timer
10 | from time import sleep
11 |
12 | GPIO = webiopi.GPIO
13 |
14 | # setup function is automatically called at WebIOPi startup
15 | def setup():
16 | # Do nothing
17 | pass
18 |
19 | # loop function is repeatedly called by WebIOPi
20 | def loop():
21 | # Do Nothing
22 | sleep(0.1) # Prevent the CPU from running at 100%
23 | pass
24 |
25 | # destroy function is called at WebIOPi shutdown
26 | def destroy():
27 | # do nothing
28 | pass
29 |
30 | @webiopi.macro
31 | def reboot(t=1):
32 | Timer(int(t)*60,os.system("sudo reboot")).start()
33 | return "The system is going DOWN for reboot in %d minute" % t
34 |
35 | @webiopi.macro
36 | def getData():
37 | temp = round(int(open('/sys/class/thermal/thermal_zone0/temp').read()) / 1e3,1)
38 | perc = psutil.cpu_percent()
39 | memAvail = round(psutil.avail_phymem()/1000000,1)
40 | diskUsage = psutil.disk_usage('/').percent
41 | j = {'cpu_temp': temp, 'cpu_perc': perc, 'mem_avail': memAvail, 'disk_usage': diskUsage}
42 | return json.dumps(j,indent=4, separators=(',', ': '))
43 |
--------------------------------------------------------------------------------
/device-types/spark-core-illuminance-sensor/README.md:
--------------------------------------------------------------------------------
1 | Spark Core Illuminance Sensor
2 | =============================
3 |
4 | Read an illuminance measurement from a [Spark Core](https://www.spark.io/).
5 |
6 | 
7 | 
8 |
--------------------------------------------------------------------------------
/device-types/spark-core-illuminance-sensor/spark-core-illuminance-sensor.device.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Spark Core Illuminance Sensor
3 | *
4 | * Copyright 2014 Nicholas Wilde
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | */
16 |
17 | preferences {
18 | input "deviceId", "text", title: "Device ID", required: true
19 | input "token", "password", title: "Access Token", required: true
20 | input "sparkVar", "text", title: "Spark Variable", required: true
21 | }
22 |
23 | metadata {
24 | definition (name: "Spark Core Illuminance Sensor", namespace: "nicholaswilde/smartthings", author: "Nicholas Wilde") {
25 | capability "Polling"
26 | capability "Refresh"
27 | capability "Illuminance Measurement"
28 | }
29 |
30 | simulator {
31 | // TODO: define status and reply messages here
32 | }
33 |
34 | tiles {
35 | valueTile("illuminance", "device.illuminance", width: 2, height: 2) {
36 | state "illuminance", label:'${currentValue}', unit: "LUX",
37 | backgroundColors:[
38 | [value: 21, color: "#153591"],
39 | [value: 51, color: "#1e9cbb"],
40 | [value: 101, color: "#90d2a7"],
41 | [value: 401, color: "#44b621"],
42 | [value: 1001, color: "#f1d801"],
43 | [value: 10001, color: "#d04e00"],
44 | [value: 25001, color: "#bc2323"]
45 | ]
46 | }
47 | standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
48 | state "default", action:"refresh.refresh", icon: "st.secondary.refresh"
49 | }
50 | main "illuminance"
51 | details(["illuminance", "refresh"])
52 | }
53 | }
54 |
55 | // parse events into attributes
56 | def parse(String description) {
57 | log.debug "Parsing '${description}'"
58 | }
59 |
60 | // handle commands
61 | def poll() {
62 | log.debug "Executing 'poll'"
63 | getReading()
64 | }
65 |
66 | def refresh() {
67 | log.debug "Executing 'refresh'"
68 | getReading()
69 | }
70 |
71 | // Get the sensor reading
72 | private getReading() {
73 | //Spark Core API Call
74 | def readingClosure = { response ->
75 | log.debug "Reading request was successful, $response.data.result"
76 | sendEvent(name: "illuminance", value: response.data.result)
77 | }
78 |
79 | httpGet("https://api.spark.io/v1/devices/${deviceId}/${sparkVar}?access_token=${token}", readingClosure)
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/device-types/spark-core-illuminance-sensor/spark-core-illuminance-sensor.ino:
--------------------------------------------------------------------------------
1 | // Create a variable that will store the illuminance value
2 | int illuminance = 0;
3 |
4 | void setup()
5 | {
6 | // Register a Spark variable here
7 | Spark.variable("illuminance", &illuminance, INT);
8 |
9 | // Connect the illuminance sensor to A7 and configure it
10 | // to be an input
11 | pinMode(A0, INPUT);
12 | }
13 |
14 | void loop()
15 | {
16 | // Keep reading the temperature so when we make an API
17 | // call to read its value, we have the latest one
18 | illuminance = map(analogRead(A0), 0, 4095, 0, 99);
19 | }
20 |
--------------------------------------------------------------------------------
/device-types/spark-core-relative-humidity-sensor/README.md:
--------------------------------------------------------------------------------
1 | Spark Core Relative Humidity Sensor
2 | =============================
3 |
4 | Read an relative humidity measurement from a [Spark Core](https://www.spark.io/).
5 |
--------------------------------------------------------------------------------
/device-types/spark-core-relative-humidity-sensor/spark-core-relative-humidity-sensor.device.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Spark Core Relative Humidity Sensor
3 | *
4 | * Copyright 2014 Nicholas Wilde
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | */
16 |
17 | preferences {
18 | input "deviceId", "text", title: "Device ID", required: true
19 | input "token", "password", title: "Access Token", required: true
20 | input "sparkVar", "text", title: "Spark Variable", required: true
21 | }
22 |
23 | metadata {
24 | definition (name: "Spark Core Relative Humidity Sensor", namespace: "nicholaswilde/smartthings", author: "Nicholas Wilde") {
25 | capability "Polling"
26 | capability "Refresh"
27 | capability "Relative Humidity Measurement"
28 | }
29 |
30 | simulator {
31 | // TODO: define status and reply messages here
32 | }
33 |
34 | tiles {
35 | valueTile("humidity", "device.humidity", width: 2, height: 2) {
36 | state "humidity", label:'${currentValue}%', unit: "",
37 | backgroundColors:[
38 | [value: 16, color: "#5600A3"],
39 | [value: 31, color: "#153591"],
40 | [value: 44, color: "#1e9cbb"],
41 | [value: 59, color: "#90d2a7"],
42 | [value: 74, color: "#44b621"],
43 | [value: 84, color: "#f1d801"],
44 | [value: 95, color: "#d04e00"],
45 | [value: 96, color: "#bc2323"]
46 | ]
47 | }
48 | standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
49 | state "default", action:"refresh.refresh", icon: "st.secondary.refresh"
50 | }
51 | main "humidity"
52 | details(["humidity", "refresh"])
53 | }
54 | }
55 |
56 | // parse events into attributes
57 | def parse(String description) {
58 | log.debug "Parsing '${description}'"
59 | }
60 |
61 | // handle commands
62 | def poll() {
63 | log.debug "Executing 'poll'"
64 | getReading()
65 | }
66 |
67 | def refresh() {
68 | log.debug "Executing 'refresh'"
69 | getReading()
70 | }
71 |
72 | // Get the sensor reading
73 | private getReading() {
74 | //Spark Core API Call
75 | def readingClosure = { response ->
76 | log.debug "Reading request was successful, $response.data.result"
77 | sendEvent(name: "humidity", value: response.data.result)
78 | }
79 |
80 | httpGet("https://api.spark.io/v1/devices/${deviceId}/${sparkVar}?access_token=${token}", readingClosure)
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/device-types/spark-core-relative-humidity-sensor/spark-core-relative-humidity-sensor.ino:
--------------------------------------------------------------------------------
1 | // Create a variable that will store the humidity value
2 | int humidity = 0;
3 |
4 | void setup()
5 | {
6 | // Register a Spark variable here
7 | Spark.variable("humidity", &humidity, INT);
8 |
9 | // Connect the humidity sensor to A7 and configure it
10 | // to be an input
11 | pinMode(A0, INPUT);
12 | }
13 |
14 | void loop()
15 | {
16 | // Keep reading the humidity so when we make an API
17 | // call to read its value, we have the latest one
18 | humidity = map(analogRead(A0), 0, 4095, 0, 99);
19 | }
20 |
--------------------------------------------------------------------------------
/device-types/spark-core-temperature-sensor/README.md:
--------------------------------------------------------------------------------
1 | Spark Core Temperature Sensor
2 | =============================
3 |
4 | Read an temperature measurement from a [Spark Core](https://www.spark.io/).
5 |
--------------------------------------------------------------------------------
/device-types/spark-core-temperature-sensor/spark-core-temperature-sensor.device.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Spark Core Sensor
3 | *
4 | * Copyright 2014 Nicholas Wilde
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | */
16 |
17 | preferences {
18 | input "deviceId", "text", title: "Device ID", required: true
19 | input "token", "password", title: "Access Token", required: true
20 | input "sparkVar", "text", title: "Spark Variable", required: true
21 | }
22 |
23 | metadata {
24 | definition (name: "Spark Core Temperature Sensor", namespace: "nicholaswilde/smartthings", author: "Nicholas Wilde") {
25 | capability "Polling"
26 | capability "Refresh"
27 | capability "Temperature Measurement"
28 | }
29 |
30 | simulator {
31 | // TODO: define status and reply messages here
32 | }
33 |
34 | tiles {
35 | valueTile("temperature", "device.temperature", width: 2, height: 2) {
36 | state "temperature", label:'${currentValue}°', unit: "F",
37 | backgroundColors:[
38 | [value: 31, color: "#153591"],
39 | [value: 44, color: "#1e9cbb"],
40 | [value: 59, color: "#90d2a7"],
41 | [value: 74, color: "#44b621"],
42 | [value: 84, color: "#f1d801"],
43 | [value: 95, color: "#d04e00"],
44 | [value: 96, color: "#bc2323"]
45 | ]
46 | }
47 | standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
48 | state "default", action:"refresh.refresh", icon: "st.secondary.refresh"
49 | }
50 | main "temperature"
51 | details(["temperature", "refresh"])
52 | }
53 | }
54 |
55 | // parse events into attributes
56 | def parse(String description) {
57 | log.debug "Parsing '${description}'"
58 | }
59 |
60 | // handle commands
61 | def poll() {
62 | log.debug "Executing 'poll'"
63 | getReading()
64 | }
65 |
66 | def refresh() {
67 | log.debug "Executing 'refresh'"
68 | getReading()
69 | }
70 |
71 | // Get the sensor reading
72 | private getReading() {
73 | //Spark Core API Call
74 | def readingClosure = { response ->
75 | log.debug "Reading request was successful, $response.data.result"
76 | sendEvent(name: "temperature", value: response.data.result)
77 | }
78 |
79 | httpGet("https://api.spark.io/v1/devices/${deviceId}/${sparkVar}?access_token=${token}", readingClosure)
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/device-types/spark-core-temperature-sensor/spark-core-temperature-sensor.ino:
--------------------------------------------------------------------------------
1 | // Create a variable that will store the temperature value
2 | int temperature = 0;
3 |
4 | void setup()
5 | {
6 | // Register a Spark variable here
7 | Spark.variable("temperature", &temperature, INT);
8 |
9 | // Connect the temperature sensor to A7 and configure it
10 | // to be an input
11 | pinMode(A0, INPUT);
12 | }
13 |
14 | void loop()
15 | {
16 | // Keep reading the temperature so when we make an API
17 | // call to read its value, we have the latest one
18 | temperature = map(analogRead(A0), 0, 4095, 0, 99);
19 | }
20 |
--------------------------------------------------------------------------------
/devicetypes/nicholaswilde/plug-in-zwave-dimmer.src/plug-in-zwave-dimmer.groovy:
--------------------------------------------------------------------------------
1 | /* Plug-In ZWave Dimmer
2 | *
3 | * Variation of the pmjoen's "Dimmer-Switch-Levels"
4 | *
5 | * Device type adds set levels low, medium, and High for preset values (Moods).
6 | * Adds increment up and down buttons with ability to set value.
7 | * Removes indicator from plug-in ZWave dimmer device
8 | *
9 | * Nicholas Wilde
10 | * 20160814
11 | *
12 | * v1.0 - Initial release of device handler
13 | *
14 | */
15 |
16 | metadata {
17 | // Automatically generated. Make future change here.
18 | definition (name: "Plug-In ZWave Dimmer", namespace: "nicholaswilde", author: "SmartThings") {
19 | capability "Switch Level"
20 | capability "Actuator"
21 | //capability "Indicator"
22 | capability "Switch"
23 | capability "Polling"
24 | capability "Refresh"
25 | capability "Sensor"
26 |
27 |
28 | command "low"
29 | command "med"
30 | command "high"
31 | command "levelUp"
32 | command "levelDown"
33 |
34 |
35 | attribute "currentState", "string"
36 | attribute "switch", "string"
37 |
38 | //fingerprint deviceId: "0x1101", inClusters: "0x26, 0x27, 0x70, 0x86, 0x72"
39 | }
40 |
41 |
42 | tiles(scale: 2) {
43 | multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
44 | tileAttribute ("device.currentState", key: "PRIMARY_CONTROL") {
45 | attributeState "default", label:'ADJUSTING', action:"refresh.refresh",icon:"st.Lighting.light13", backgroundColor:"#2179b8", nextState: "turningOff"
46 | attributeState "HIGH", label:'HIGH', action:"switch.off", icon:"st.illuminance.illuminance.bright", backgroundColor:"#79b821", nextState:"turningOff"
47 | attributeState "MED", label:'MED', action:"switch.off", icon:"st.illuminance.illuminance.light", backgroundColor:"#79b821", nextState:"turningOff"
48 | attributeState "LOW", label:'LOW', action:"switch.off", icon:"st.Weather.weather4", backgroundColor:"#79b821", nextState:"turningOff"
49 | attributeState "OFF", label:'OFF', action:"switch.on", icon:"st.Lighting.light13", backgroundColor:"#ffffff", nextState: "turningOn"
50 | attributeState "on", label:'ON', action:"switch.off", icon:"st.Lighting.light11", backgroundColor:"#79b821", nextState:"turningOff"
51 | attributeState "turningOn", action:"switch.on", label:'TURNINGON', icon:"st.Lighting.light11", backgroundColor:"#2179b8", nextState: "turningOn"
52 | attributeState "turningOff", action:"switch.off", label:'TURNINGOFF', icon:"st.Lighting.light13", backgroundColor:"#2179b8", nextState: "turningOff"
53 | }
54 |
55 | tileAttribute("device.level", key: "VALUE_CONTROL") {
56 | attributeState("VALUE_UP", action: "levelUp")
57 | attributeState("VALUE_DOWN", action: "levelDown")
58 | }
59 | tileAttribute ("device.level", key: "SLIDER_CONTROL") {
60 | attributeState "level", action:"switch level.setLevel"
61 | }
62 | }
63 |
64 |
65 | /*standardTile("indicator", "device.indicatorStatus", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
66 | state "when off", action:"indicator.indicatorWhenOn", icon:"st.indicators.lit-when-off"
67 | state "when on", action:"indicator.indicatorNever", icon:"st.indicators.lit-when-on"
68 | state "never", action:"indicator.indicatorWhenOff", icon:"st.indicators.never-lit"
69 | }*/
70 |
71 |
72 | standardTile("refresh", "device.switch", width: 3, height: 2, inactiveLabel: false, decoration: "flat") {
73 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
74 | }
75 |
76 |
77 | valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 3, height: 2) {
78 | state "level", label:'${currentValue} %', unit:"%", backgroundColor:"#ffffff"
79 | }
80 |
81 |
82 | standardTile("low", "device.currentState", inactiveLabel: false, width: 2, height: 2, canChangeBackground: false) {
83 | state "default", label: 'LOW', action: "low", icon:"st.Weather.weather4", backgroundColor: "#ffffff"
84 | state "LOW", label:'LOW', action: "low", icon:"st.Weather.weather4", backgroundColor: "#79b821"
85 | state "ADJUSTING.LOW", label:'LOW', action: "low", icon:"st.Weather.weather4", backgroundColor: "#2179b8"
86 | }
87 |
88 | standardTile("med", "device.currentState", inactiveLabel: false, width: 2, height: 2, canChangeBackground: false) {
89 | state "default", label: 'MED', action: "med", icon:"st.illuminance.illuminance.light", backgroundColor: "#ffffff"
90 | state "MED", label: 'MED', action: "med", icon:"st.illuminance.illuminance.light", backgroundColor: "#79b821"
91 | state "ADJUSTING.MED", label:'MED', action: "med", icon:"st.illuminance.illuminance.light", backgroundColor: "#2179b8"
92 | }
93 |
94 | standardTile("high", "device.currentState", inactiveLabel: false, width: 2, height: 2, canChangeBackground: false) {
95 | state "default", label: 'HIGH', action: "high", icon:"st.illuminance.illuminance.bright", backgroundColor: "#ffffff"
96 | state "HIGH", label: 'HIGH', action: "high", icon:"st.illuminance.illuminance.bright", backgroundColor: "#79b821"
97 | state "ADJUSTING.HIGH", label:'HIGH', action: "high", icon:"st.illuminance.illuminance.bright", backgroundColor: "#2179b8"
98 | }
99 |
100 |
101 | main(["switch"])
102 | //details(["switch", "low", "med", "high", "level", "indicator", "refresh"])
103 | details(["switch", "low", "med", "high", "level", "refresh"])
104 |
105 | }
106 | preferences {
107 | section("Light Level Values") {
108 | input "lowThreshold", "number", title: "Low Button Light Value", range: "1..99"
109 | input "medThreshold", "number", title: "Medium Button Light Value", range: "1..99"
110 | input "highThreshold", "number", title: "High Button Light Value", range: "1..99"
111 | }
112 | section ("Interval Selection") {
113 | input "stepper", "enum", title: "Up/Down Light Interval Value", defaultValue: "10", options: ["5","10","20"]
114 | }
115 | }
116 | }
117 |
118 |
119 | def parse(String description) {
120 | def item1 = [
121 | canBeCurrentState: false,
122 | linkText: getLinkText(device),
123 | isStateChange: false,
124 | displayed: false,
125 | descriptionText: description,
126 | value: description
127 | ]
128 | def result
129 | def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1])
130 | if (cmd) {
131 | result = createEvent(cmd, item1)
132 | }
133 | else {
134 | item1.displayed = displayed(description, item1.isStateChange)
135 | result = [item1]
136 | }
137 | log.debug "Parse returned ${result?.descriptionText}"
138 | result
139 | }
140 |
141 |
142 | def createEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, Map item1) {
143 | def result = doCreateEvent(cmd, item1)
144 | for (int i = 0; i < result.size(); i++) {
145 | result[i].type = "physical"
146 | }
147 | log.trace "BasicReport"
148 | result
149 | }
150 |
151 |
152 | def createEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd, Map item1) {
153 | def result = doCreateEvent(cmd, item1)
154 | for (int i = 0; i < result.size(); i++) {
155 | result[i].type = "physical"
156 | }
157 | log.trace "BasicSet"
158 | result
159 | }
160 |
161 |
162 | def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStartLevelChange cmd, Map item1) {
163 | []
164 | log.trace "StartLevel"
165 | }
166 |
167 |
168 | def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd, Map item1) {
169 | [response(zwave.basicV1.basicGet())]
170 | }
171 |
172 |
173 | def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd, Map item1) {
174 | def result = doCreateEvent(cmd, item1)
175 | for (int i = 0; i < result.size(); i++) {
176 | result[i].type = "physical"
177 | }
178 | log.trace "SwitchMultiLevelSet"
179 | result
180 | }
181 |
182 |
183 | def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd, Map item1) {
184 | def result = doCreateEvent(cmd, item1)
185 | result[0].descriptionText = "${item1.linkText} is ${item1.value}"
186 | result[0].handlerName = cmd.value ? "statusOn" : "statusOff"
187 | for (int i = 0; i < result.size(); i++) {
188 | result[i].type = "digital"
189 | }
190 | log.trace "SwitchMultilevelReport"
191 | result
192 | }
193 |
194 |
195 | def doCreateEvent(physicalgraph.zwave.Command cmd, Map item1) {
196 | def result = [item1]
197 | def lowThresholdvalue = (settings.lowThreshold != null && settings.lowThreshold != "") ? settings.lowThreshold.toInteger() : 33
198 | def medThresholdvalue = (settings.medThreshold != null && settings.medThreshold != "") ? settings.medThreshold.toInteger() : 67
199 | def highThresholdvalue = (settings.highThreshold != null && settings.highThreshold != "") ? settings.highThreshold.toInteger() : 99
200 |
201 |
202 | item1.name = "switch"
203 | item1.value = cmd.value ? "on" : "off"
204 | if (item1.value == "off") {
205 | sendEvent(name: "currentState", value: "OFF" as String)
206 | }
207 | item1.handlerName = item1.value
208 | item1.descriptionText = "${item1.linkText} was turned ${item1.value}"
209 | item1.canBeCurrentState = true
210 | item1.isStateChange = isStateChange(device, item1.name, item1.value)
211 | item1.displayed = false
212 |
213 |
214 | if (cmd.value) {
215 | def item2 = new LinkedHashMap(item1)
216 | item2.name = "level"
217 | item2.value = cmd.value as String
218 | item2.unit = "%"
219 | item2.descriptionText = "${item1.linkText} dimmed ${item2.value} %"
220 | item2.canBeCurrentState = true
221 | item2.isStateChange = isStateChange(device, item2.name, item2.value)
222 | item2.displayed = false
223 |
224 | if (item2.value.toInteger() == lowThresholdvalue) { sendEvent(name: "currentState", value: "LOW" as String) }
225 | else if (item2.value.toInteger() == medThresholdvalue) { sendEvent(name: "currentState", value: "MED" as String) }
226 | else if (item2.value.toInteger() == highThresholdvalue) { sendEvent(name: "currentState", value: "HIGH" as String) }
227 | else if (item2.value.toInteger() == 0) { sendEvent(name: "currentState", value: "OFF" as String) }
228 | else { sendEvent(name: "currentState", value: "on" as String) }
229 |
230 |
231 | result << item2
232 | }
233 | log.trace "doCreateEvent"
234 | result
235 | }
236 |
237 |
238 | /*def createEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd, Map map) {
239 | //def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
240 | def value = "when off"
241 | log.trace "ConfigurationReport"
242 | if (cmd.configurationValue[0] == 1) {value = "when on"}
243 | if (cmd.configurationValue[0] == 2) {value = "never"}
244 | [name: "indicatorStatus", value: value, display: false]
245 | }*/
246 |
247 |
248 | def createEvent(physicalgraph.zwave.Command cmd, Map map) {
249 | // Handles any Z-Wave commands we aren't interested in
250 | log.debug "UNHANDLED COMMAND $cmd"
251 | }
252 |
253 |
254 | def on() {
255 | log.info "on"
256 | delayBetween([zwave.basicV1.basicSet(value: 0xFF).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
257 | }
258 |
259 |
260 | def off() {
261 | log.info "off"
262 | delayBetween ([zwave.basicV1.basicSet(value: 0x00).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
263 | }
264 |
265 |
266 | def setLevel(value) {
267 | def lowThresholdvalue = (settings.lowThreshold != null && settings.lowThreshold != "") ? settings.lowThreshold.toInteger() : 33
268 | def medThresholdvalue = (settings.medThreshold != null && settings.medThreshold != "") ? settings.medThreshold.toInteger() : 67
269 | def highThresholdvalue = (settings.highThreshold != null && settings.highThreshold != "") ? settings.highThreshold.toInteger() : 99
270 |
271 | if (value == "LOW") { value = lowThresholdvalue }
272 | if (value == "MED") { value = medThresholdvalue }
273 | if (value == "HIGH") { value = highThresholdvalue }
274 |
275 |
276 | def level = Math.min(value as Integer, 99)
277 |
278 | if (level == lowThresholdvalue) { sendEvent(name: "currentState", value: "ADJUSTING.LOW" as String, displayed: false) }
279 | else if (level == medThresholdvalue) { sendEvent(name: "currentState", value: "ADJUSTING.MED" as String, displayed: false) }
280 | else if (level == highThresholdvalue) { sendEvent(name: "currentState", value: "ADJUSTING.HIGH" as String, displayed: false) }
281 | else if (level == 0) { sendEvent(name: "currentState", value: "OFF" as String, displayed: false) }
282 | else { sendEvent(name: "currentState", value: "on" as String, displayed: false) }
283 |
284 | sendEvent(name: "level", value: level, unit: "%")
285 |
286 | log.trace "setLevelValue: ${value}"
287 |
288 | delayBetween ([zwave.basicV1.basicSet(value: level as Integer).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 1000)
289 | }
290 |
291 |
292 | def resetLevel(value) {
293 | def level = Math.min(value as Integer, 99)
294 | delayBetween ([
295 | delayBetween ([ zwave.basicV1.basicSet(value: level).format(),
296 | zwave.basicV1.basicSet(value: 0x00).format()], 10),
297 | zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
298 | }
299 |
300 |
301 | def setLevel(value, duration) {
302 | def lowThresholdvalue = (settings.lowThreshold != null && settings.lowThreshold != "") ? settings.lowThreshold.toInteger() : 33
303 | def medThresholdvalue = (settings.medThreshold != null && settings.medThreshold != "") ? settings.medThreshold.toInteger() : 67
304 | def highThresholdvalue = (settings.highThreshold != null && settings.highThreshold != "") ? settings.highThreshold.toInteger() : 99
305 |
306 |
307 | if (value == "LOW") { value = lowThresholdvalue }
308 | if (value == "MED") { value = medThresholdvalue }
309 | if (value == "HIGH") { value = highThresholdvalue }
310 |
311 |
312 | def level = Math.min(value as Integer, 99)
313 | def dimmingDuration = duration < 128 ? duration : 128 + Math.round(duration / 60)
314 |
315 | if (level == lowThresholdvalue) { sendEvent(name: "currentState", value: "ADJUSTING.LOW" as String, displayed: false) }
316 | else if (level == medThresholdvalue) { sendEvent(name: "currentState", value: "ADJUSTING.MED" as String, displayed: false) }
317 | else if (level == highThresholdvalue) { sendEvent(name: "currentState", value: "ADJUSTING.HIGH" as String, displayed: false) }
318 | else if (level == 0) { sendEvent(name: "currentState", value: "OFF" as String, displayed: false) }
319 | else { sendEvent(name: "currentState", value: "on" as String, displayed: false) }
320 |
321 |
322 | log.trace "setLevelValueDuration: ${value}"
323 |
324 | zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format()
325 | }
326 |
327 |
328 | def levelUp(){
329 |
330 |
331 | def steppervalue = (settings.stepper != null && settings.stepper != "") ? settings.stepper.toInteger() : 10
332 | int nextLevel = device.currentValue("level") + steppervalue
333 | sendEvent(name: "currentState", value: "default" as String, displayed: false)
334 |
335 | if( nextLevel > 100){
336 | nextLevel = 100
337 | }
338 |
339 | log.trace "setLevelUp(value): ${level}"
340 | log.debug "Setting dimmer level up to: ${nextLevel}"
341 | delayBetween ([zwave.basicV1.basicSet(value: nextLevel).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 1500)
342 | }
343 |
344 | def levelDown(){
345 |
346 |
347 | def steppervalue = (settings.stepper != null && settings.stepper != "") ? settings.stepper.toInteger() : 10
348 | int nextLevel = device.currentValue("level") - steppervalue
349 | sendEvent(name: "currentState", value: "default" as String, displayed: false)
350 |
351 | if (nextLevel < 0){
352 | nextLevel = 0
353 | }
354 |
355 | if (nextLevel == 0){
356 | off()
357 | }
358 |
359 | else
360 | {
361 | log.trace "setLevelDown(value): ${level}"
362 | log.debug "Setting dimmer level down to: ${nextLevel}"
363 | delayBetween ([zwave.basicV1.basicSet(value: nextLevel).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 1500)
364 | }
365 | }
366 |
367 |
368 | def low() {
369 | log.trace "setLow"
370 | setLevel("LOW")
371 | }
372 |
373 |
374 | def med() {
375 | log.trace "setMed"
376 | setLevel("MED")
377 | }
378 |
379 |
380 | def high() {
381 | log.trace "setHigh"
382 | setLevel("HIGH")
383 | }
384 |
385 |
386 | def poll() {
387 | zwave.switchMultilevelV1.switchMultilevelGet().format()
388 | }
389 |
390 |
391 | def refresh() {
392 | zwave.switchMultilevelV1.switchMultilevelGet().format()
393 | }
394 |
395 |
396 | /*def indicatorWhenOn() {
397 | sendEvent(name: "indicatorStatus", value: "when on", display: false)
398 | zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format()
399 | }*/
400 |
401 |
402 | /*def indicatorWhenOff() {
403 | sendEvent(name: "indicatorStatus", value: "when off", display: false)
404 | zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format()
405 | }*/
406 |
407 |
408 | /*def indicatorNever() {
409 | sendEvent(name: "indicatorStatus", value: "never", display: false)
410 | zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format()
411 | }*/
--------------------------------------------------------------------------------
/smartapps/README.md:
--------------------------------------------------------------------------------
1 | SmartApps
2 | =========
3 |
4 | All of my [SmartThings](http://www.smartthings.com/) [SmartApps](http://docs.smartthings.com/en/latest/smartapp-developers-guide/index.html).
5 |
--------------------------------------------------------------------------------
/smartapps/device-poll/README.md:
--------------------------------------------------------------------------------
1 | Device Poll
2 | ===========
3 |
4 | Poll particular devices to make sure readings are up to date.
5 |
6 | This is taken directly from [Stever Herrell's](mailto:steve.herrell@gmail.com) [post](http://community.smartthings.com/t/polling-not-working-for-custom-device-type/2787/14).
7 |
8 |
--------------------------------------------------------------------------------
/smartapps/device-poll/device-poll.app.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Device Poll
3 | *
4 | * Copyright 2014 Steve Herrell
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | */
16 | definition(
17 | name: "Device Poll",
18 | namespace: "steve.herrell@gmail.com",
19 | author: "Steve Herrell",
20 | description: "Poll particular devices to make sure readings are up to date.",
21 | category: "Convenience",
22 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact.png",
23 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact@2x.png"
24 | )
25 |
26 | preferences {
27 | section("Devices To Poll") {
28 | input "pollingDevices", "capability.polling", multiple: true, title: "Pollable Devices"
29 | }
30 | section("Polling Interval (defaults to 15 min)") {
31 | input "pollingInterval", "decimal", title: "Number of minutes", required: false
32 | }
33 | }
34 |
35 | def installed() {
36 | log.debug "Installed with settings: ${settings}"
37 |
38 | doPoll()
39 | }
40 |
41 | def updated() {
42 | log.debug "Updated with settings: ${settings}"
43 |
44 | doPoll()
45 | }
46 |
47 | def doPoll() {
48 |
49 | log.debug "running doPoll"
50 |
51 | pollingDevices.poll()
52 |
53 | def timeOut = (pollingInterval != null && pollingInterval != "") ? pollingInterval : 15
54 |
55 | runIn( timeOut * 60, "doPoll",)
56 | }
57 |
--------------------------------------------------------------------------------
/smartapps/nicholaswilde/samsung-tv-remote.src/Readme.md:
--------------------------------------------------------------------------------
1 | Samsung TV Remote
2 | =================
3 |
4 | Control Samsung Smart TVs using SmartThings
5 |
6 | Based on [Brad Butner's](https://github.com/bradmb/) [SmartThings Samsung TV Remote](https://github.com/bradmb/SmartThings-Samsung-TV-Remote)
7 |
8 | Commands taken from http://forum.samygo.tv/viewtopic.php?f=12&t=1792
9 |
10 | This currently only allows you to trigger actions on your TV if you press the app or trigger it
11 | with a virtual [momentary button tile](https://github.com/nicholaswilde/smartthings/blob/master/device-types/momentary-button-tile/momentary-button-tile.device.groovy).
12 |
--------------------------------------------------------------------------------
/smartapps/nicholaswilde/samsung-tv-remote.src/samsung-tv-remote.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Samsung TV Remote
3 | *
4 | * Author: Nicholas Wilde
5 | * Date: 2014-08-31
6 | *
7 | * Based on Brad Butner's SmartThings Samsung TV Remote
8 | *
9 | * Commands taken from http://forum.samygo.tv/viewtopic.php?f=12&t=1792
10 | *
11 | * This currently only allows you to trigger actions on your TV if you press the app or trigger it
12 | * with a virtual button
13 | *
14 | */
15 |
16 | // Automatically generated. Make future change here.
17 | definition(
18 | name: "Samsung TV Remote",
19 | namespace: "nicholaswilde",
20 | author: "Nicholas Wilde",
21 | description: "Control Samsung Smart TVs using SmartThings",
22 | categorhy: "SmartThings Labs",
23 | iconUrl: "http://icons.iconarchive.com/icons/icons8/windows-8/512/Network-Remote-Control-icon.png",
24 | iconX2Url: "http://icons.iconarchive.com/icons/icons8/windows-8/512/Network-Remote-Control-icon.png")
25 |
26 | preferences {
27 | section("Samsung TV Settings") {
28 | def commands = [
29 | "UP",
30 | "DOWN",
31 | "LEFT",
32 | "RIGHT",
33 | "3",
34 | "VOLUP",
35 | "4",
36 | "5",
37 | "6",
38 | "VOLDOWN",
39 | "7",
40 | "8",
41 | "9",
42 | "MUTE",
43 | "CHDOWN",
44 | "0",
45 | "CHUP",
46 | "PRECH",
47 | "GREEN",
48 | "YELLOW",
49 | "CYAN",
50 | "ADDDEL",
51 | "SOURCE",
52 | "INFO",
53 | "PIP_ONOFF",
54 | "PIP_SWAP",
55 | "PLUS100",
56 | "CAPTION",
57 | "PMODE",
58 | "TTX_MIX",
59 | "TV",
60 | "PICTURE_SIZE",
61 | "AD",
62 | "PIP_SIZE",
63 | "MAGIC_CHANNEL",
64 | "PIP_SCAN",
65 | "PIP_CHUP",
66 | "PIP_CHDOWN",
67 | "DEVICE_CONNECT",
68 | "HELP",
69 | "ANTENA",
70 | "CONVERGENCE",
71 | "11",
72 | "12",
73 | "AUTO_PROGRAM",
74 | "FACTORY",
75 | "3SPEED",
76 | "RSURF",
77 | "ASPECT",
78 | "TOPMENU",
79 | "GAME",
80 | "QUICK_REPLAY",
81 | "STILL_PICTURE",
82 | "DTV",
83 | "FAVCH",
84 | "REWIND",
85 | "STOP",
86 | "PLAY",
87 | "FF",
88 | "REC",
89 | "PAUSE",
90 | "TOOLS",
91 | "INSTANT_REPLAY",
92 | "LINK",
93 | "FF_",
94 | "GUIDE",
95 | "REWIND_",
96 | "ANGLE",
97 | "RESERVED1",
98 | "ZOOM1",
99 | "PROGRAM",
100 | "BOOKMARK",
101 | "DISC_MENU",
102 | "PRINT",
103 | "RETURN",
104 | "SUB_TITLE",
105 | "CLEAR",
106 | "VCHIP",
107 | "REPEAT",
108 | "DOOR",
109 | "OPEN",
110 | "WHEEL_LEFT",
111 | "POWER",
112 | "SLEEP",
113 | "2",
114 | "DMA",
115 | "TURBO",
116 | "1",
117 | "FM_RADIO",
118 | "DVR_MENU",
119 | "MTS",
120 | "PCMODE",
121 | "TTX_SUBFACE",
122 | "CH_LIST",
123 | "RED",
124 | "DNIe",
125 | "SRS",
126 | "CONVERT_AUDIO_MAINSUB",
127 | "MDC",
128 | "SEFFECT",
129 | "DVR",
130 | "DTV_SIGNAL",
131 | "LIVE",
132 | "PERPECT_FOCUS",
133 | "HOME",
134 | "ESAVING",
135 | "WHEEL_RIGHT",
136 | "CONTENTS",
137 | "VCR_MODE",
138 | "CATV_MODE",
139 | "DSS_MODE",
140 | "TV_MODE",
141 | "DVD_MODE",
142 | "STB_MODE",
143 | "CALLER_ID",
144 | "SCALE",
145 | "ZOOM_MOVE",
146 | "CLOCK_DISPLAY",
147 | "AV1",
148 | "SVIDEO1",
149 | "COMPONENT1",
150 | "SETUP_CLOCK_TIMER",
151 | "COMPONENT2",
152 | "MAGIC_BRIGHT",
153 | "DVI",
154 | "HDMI",
155 | "W_LINK",
156 | "DTV_LINK",
157 | "APP_LIST",
158 | "BACK_MHP",
159 | "ALT_MHP",
160 | "DNSe",
161 | "RSS",
162 | "ENTERTAINMENT",
163 | "ID_INPUT",
164 | "ID_SETUP",
165 | "ANYNET",
166 | "POWEROFF",
167 | "POWERON",
168 | "ANYVIEW",
169 | "MS",
170 | "MORE",
171 | "PANNEL_POWER",
172 | "PANNEL_CHUP",
173 | "PANNEL_CHDOWN",
174 | "PANNEL_VOLUP",
175 | "PANNEL_VOLDOW",
176 | "PANNEL_ENTER",
177 | "PANNEL_MENU",
178 | "PANNEL_SOURCE",
179 | "AV2",
180 | "AV3",
181 | "SVIDEO2",
182 | "SVIDEO3",
183 | "ZOOM2",
184 | "PANORAMA",
185 | "4_3",
186 | "16_9",
187 | "DYNAMIC",
188 | "STANDARD",
189 | "MOVIE1",
190 | "CUSTOM",
191 | "AUTO_ARC_RESET",
192 | "AUTO_ARC_LNA_ON",
193 | "AUTO_ARC_LNA_OFF",
194 | "AUTO_ARC_ANYNET_MODE_OK",
195 | "AUTO_ARC_ANYNET_AUTO_START",
196 | "AUTO_FORMAT",
197 | "DNET",
198 | "HDMI1",
199 | "AUTO_ARC_CAPTION_ON",
200 | "AUTO_ARC_CAPTION_OFF",
201 | "AUTO_ARC_PIP_DOUBLE",
202 | "AUTO_ARC_PIP_LARGE",
203 | "AUTO_ARC_PIP_SMALL",
204 | "AUTO_ARC_PIP_WIDE",
205 | "AUTO_ARC_PIP_LEFT_TOP",
206 | "AUTO_ARC_PIP_RIGHT_TOP",
207 | "AUTO_ARC_PIP_LEFT_BOTTOM",
208 | "AUTO_ARC_PIP_RIGHT_BOTTOM",
209 | "AUTO_ARC_PIP_CH_CHANGE",
210 | "AUTO_ARC_AUTOCOLOR_SUCCESS",
211 | "AUTO_ARC_AUTOCOLOR_FAIL",
212 | "AUTO_ARC_C_FORCE_AGING",
213 | "AUTO_ARC_USBJACK_INSPECT",
214 | "AUTO_ARC_JACK_IDENT",
215 | "NINE_SEPERATE",
216 | "ZOOM_IN",
217 | "ZOOM_OUT",
218 | "MIC",
219 | "HDMI2",
220 | "HDMI3",
221 | "AUTO_ARC_CAPTION_KOR",
222 | "AUTO_ARC_CAPTION_ENG",
223 | "AUTO_ARC_PIP_SOURCE_CHANGE",
224 | "HDMI4",
225 | "AUTO_ARC_ANTENNA_AIR",
226 | "AUTO_ARC_ANTENNA_CABLE",
227 | "AUTO_ARC_ANTENNA_SATELLITE",
228 | "EXT1",
229 | "EXT2",
230 | "EXT3",
231 | "EXT4",
232 | "EXT5",
233 | "EXT6",
234 | "EXT7",
235 | "EXT8",
236 | "EXT9",
237 | "EXT10",
238 | "EXT11",
239 | "EXT12",
240 | "EXT13",
241 | "EXT14",
242 | "EXT15",
243 | "EXT16",
244 | "EXT17",
245 | "EXT18",
246 | "EXT19",
247 | "EXT20",
248 | "EXT21",
249 | "EXT22",
250 | "EXT23",
251 | "EXT24",
252 | "EXT25",
253 | "EXT26",
254 | "EXT27",
255 | "EXT28",
256 | "EXT29",
257 | "EXT30",
258 | "EXT31",
259 | "EXT32",
260 | "EXT33",
261 | "EXT34",
262 | "EXT35",
263 | "EXT36",
264 | "EXT37",
265 | "EXT38",
266 | "EXT39",
267 | "EXT40",
268 | "EXT41"
269 | ]
270 | commands.sort()
271 | input "settingIpAddress", "text", title: "IP Address", description: "192.168.1.100", required: true
272 | input "settingMacAddress", "text", title: "MAC Address",description: "01:23:45:67:89:ab", required: true
273 | input "tvCommand", "enum", title: "Perform This Command", metadata:[values: commands], required: true
274 | }
275 | section("When this switch is turned pushed") {
276 | input "master", "capability.switch", title: "Which?", required: true
277 | }
278 | }
279 |
280 | def installed() {
281 | log.debug "Installed with settings: ${settings}"
282 | initialize()
283 | }
284 |
285 | def updated() {
286 | log.debug "Updated with settings: ${settings}"
287 | unsubscribe()
288 | initialize()
289 | }
290 |
291 | def initialize() {
292 | subscribe(app, appTouch)
293 | subscribe(master, "switch.on", switchHandler)
294 | tvAction("AUTHENTICATE")
295 | }
296 |
297 | def appTouch(evt) {
298 | log.debug "appTouch: $evt"
299 | tvAction(tvCommand)
300 | }
301 |
302 | def switchHandler(evt) {
303 | log.debug "${master} switch turned ${evt}"
304 | tvAction(tvCommand)
305 | }
306 |
307 | private tvAction(key) {
308 | log.debug "Executing ${tvCommand}"
309 |
310 | // Standard Connection Data
311 | def appString = "iphone..iapp.samsung"
312 | def appStringLength = appString.getBytes().size()
313 |
314 | def tvAppString = "iphone.UN60ES8000.iapp.samsung"
315 | def tvAppStringLength = tvAppString.getBytes().size()
316 |
317 | def remoteName = "SmartThings".encodeAsBase64().toString()
318 | def remoteNameLength = remoteName.getBytes().size()
319 |
320 | // Device Connection Data
321 | def ipAddress = settingIpAddress.encodeAsBase64().toString()
322 | def ipAddressLength = ipAddress.getBytes().size()
323 | def ipAddressHex = settingIpAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
324 | log.debug "IP Address (HEX): ${ipAddressHex}"
325 |
326 | def macAddress = settingMacAddress.replaceAll(":","").encodeAsBase64().toString()
327 | def macAddressLength = macAddress.getBytes().size()
328 |
329 | // The Authentication Message
330 | def authenticationMessage = "${(char)0x64}${(char)0x00}${(char)ipAddressLength}${(char)0x00}${ipAddress}${(char)macAddressLength}${(char)0x00}${macAddress}${(char)remoteNameLength}${(char)0x00}${remoteName}"
331 | def authenticationMessageLength = authenticationMessage.getBytes().size()
332 |
333 | def authenticationPacket = "${(char)0x00}${(char)appStringLength}${(char)0x00}${appString}${(char)authenticationMessageLength}${(char)0x00}${authenticationMessage}"
334 |
335 | // If our initial run, just send the authentication packet so the prompt appears on screen
336 | if (key == "AUTHENTICATE") {
337 | sendHubCommand(new physicalgraph.device.HubAction(authenticationPacket, physicalgraph.device.Protocol.LAN, "${ipAddressHex}:D6D8"))
338 | } else {
339 | // Build the command we will send to the Samsung TV
340 | def command = "KEY_${key}".encodeAsBase64().toString()
341 | def commandLength = command.getBytes().size()
342 |
343 | def actionMessage = "${(char)0x00}${(char)0x00}${(char)0x00}${(char)commandLength}${(char)0x00}${command}"
344 | def actionMessageLength = actionMessage.getBytes().size()
345 |
346 | def actionPacket = "${(char)0x00}${(char)tvAppStringLength}${(char)0x00}${tvAppString}${(char)actionMessageLength}${(char)0x00}${actionMessage}"
347 |
348 | // Send both the authentication and action at the same time
349 | sendHubCommand(new physicalgraph.device.HubAction(authenticationPacket + actionPacket, physicalgraph.device.Protocol.LAN, "${ipAddressHex}:D6D8"))
350 | }
351 | }
--------------------------------------------------------------------------------
/smartapps/notify-when-on-for/README.md:
--------------------------------------------------------------------------------
1 | Notify When On For
2 | ==================
3 |
4 | Notify after switch has been on for X minutes. If the switch/outlet is powered off prior to the 'time out,' then the scheduled time off is cancelled.
5 |
--------------------------------------------------------------------------------
/smartapps/notify-when-on-for/notify-when-on-for.app.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Notify When On For
3 | *
4 | * Author: Nicholas Wilde
5 | *
6 | * Based on Improved Power Allowance by chrisb
7 | *
8 | * This program sends a notification instead of turning off a switch.
9 | */
10 |
11 | // Automatically generated. Make future change here.
12 | definition(
13 | name: "Notify When On For",
14 | namespace: "nicholaswilde/smartthings",
15 | author: "Nicholas Wilde",
16 | description: "Notify after switch has been on for X minutes. If the switch/outlet is powered off prior to the 'time out,' then the scheduled time off is cancelled.",
17 | category: "Green Living",
18 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact.png",
19 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact@2x.png"
20 | )
21 |
22 | preferences {
23 | section("When a switch turns on...") {
24 | input "theSwitch", "capability.switch"
25 | }
26 | section("Notify how many minutes later? (optional, default 15 minutes)") {
27 | input "minutesLater", "number", title: "When?", required: false
28 | }
29 | section("Send this message (optional, sends standard status message if not specified)"){
30 | input "messageText", "text", title: "Message Text", required: false
31 | }
32 | section("Via a push notification and/or an SMS message"){
33 | input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false
34 | input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, metadata: [values: ["Yes","No"]]
35 | }
36 | }
37 |
38 | def installed() {
39 | log.debug "Installed with settings: ${settings}"
40 | subscribe(theSwitch, "switch.on", switchOnHandler, [filterEvents: true])
41 | }
42 |
43 | def updated() {
44 | log.debug "Updated with settings: ${settings}"
45 | unsubscribe()
46 | subscribe(theSwitch, "switch.on", switchOnHandler, [filterEvents: true])
47 | }
48 |
49 | def switchOnHandler(evt) {
50 | log.debug "Switch ${theSwitch} turned: ${evt.value}"
51 | def minutesLater = minutesLater ? minutesLater: 15
52 | def delay = minutesLater * 60
53 | log.debug "Sending notification in ${minutesLater} minutes (${delay}s)"
54 | runIn(delay, sendMessage)
55 | }
56 |
57 | def sendMessage() {
58 | def msg = messageText ?: "${theSwitch} has been on for ${minutesLater} minutes."
59 | if (theSwitch.currentValue("switch") == "on"){
60 | if (!phone || pushAndPhone != "No") {
61 | log.debug "sending push"
62 | sendPush(msg)
63 | }
64 | if (phone) {
65 | log.debug "sending SMS"
66 | sendSms(phone, msg)
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/smartapps/thingspeak-logger-ii/README.md:
--------------------------------------------------------------------------------
1 | ThingSpeak Logger II
2 | ====================
3 |
4 | Log events to [ThingSpeak](https://thingspeak.com/).
5 |
6 | Based on *ThingSpeak Logger* by [florianz](http://community.smartthings.com/users/florianz/activity).
7 |
--------------------------------------------------------------------------------
/smartapps/thingspeak-logger-ii/thingspeak-logger-ii.app.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * ThingSpeak Logger II
3 | *
4 | * Based on "ThingSpeak Logger by florianz
5 | *
6 | * Author: Nicholas Wilde
7 | * Date: 2014-07-08
8 | *
9 | * Changes from florianz's ThingSpeakLogger:
10 | * - Added illuminance & relative humidity sensors
11 | * - Convert device names and map values to lowercase before looking for field number.
12 | *
13 | * Create a ThingSpeak channel with a write key. The app must be given the channel id and key.
14 | * Then, create a field for each device and name the field according to the label given to the
15 | * device in SmartThings.
16 | *
17 | */
18 |
19 | // Automatically generated. Make future change here.
20 | definition(
21 | name: "ThingSpeak Logger II",
22 | namespace: "nicholaswilde/smartthings",
23 | author: "Nicholas Wilde",
24 | description: "Log events to ThingSpeak",
25 | category: "Convenience",
26 | iconUrl: "https://lh6.googleusercontent.com/-LCt2nG3Npbo/AAAAAAAAAAI/AAAAAAAAAB8/O6sWrun4q6w/s50-c/photo.jpg",
27 | iconX2Url: "https://lh6.googleusercontent.com/-LCt2nG3Npbo/AAAAAAAAAAI/AAAAAAAAAB8/O6sWrun4q6w/s100-c/photo.jpg")
28 |
29 | preferences {
30 | section("Log devices...") {
31 | input "illuminants", "capability.illuminanceMeasurement", title: "Illuminants", required:false, multiple: true
32 | input "humidities", "capability.relativeHumidityMeasurement", title: "Relative Humidities", required:false, multiple: true
33 | input "temperatures", "capability.temperatureMeasurement", title: "Temperatures", required:false, multiple: true
34 | input "contacts", "capability.contactSensor", title: "Contacts", required: false, multiple: true
35 | input "accelerations", "capability.accelerationSensor", title: "Accelerations", required: false, multiple: true
36 | input "motions", "capability.motionSensor", title: "Motions", required: false, multiple: true
37 | input "switches", "capability.switch", title: "Switches", required: false, multiple: true
38 | }
39 |
40 | section ("ThinkSpeak channel id...") {
41 | input "channelId", "number", title: "Channel ID"
42 | }
43 |
44 | section ("ThinkSpeak write key...") {
45 | input "channelKey", "password", title: "Channel Key"
46 | }
47 | }
48 |
49 | def installed() {
50 | initialize()
51 | }
52 |
53 | def updated() {
54 | unsubscribe()
55 | initialize()
56 | }
57 |
58 | def initialize() {
59 | subscribe(illuminants, "illuminance", handleIlluminanceEvent)
60 | subscribe(humidities, "humidity", handleHumidityEvent)
61 | subscribe(temperatures, "temperature", handleTemperatureEvent)
62 | subscribe(contacts, "contact", handleContactEvent)
63 | subscribe(accelerations, "acceleration", handleAccelerationEvent)
64 | subscribe(motions, "motion", handleMotionEvent)
65 | subscribe(switches, "switch", handleSwitchEvent)
66 |
67 | updateChannelInfo()
68 | log.debug state.fieldMap
69 | }
70 |
71 | def handleIlluminanceEvent(evt) {
72 | logField(evt) { it.toString() }
73 | }
74 |
75 | def handleHumidityEvent(evt) {
76 | logField(evt) { it.toString() }
77 | }
78 |
79 | def handleTemperatureEvent(evt) {
80 | logField(evt) { it.toString() }
81 | }
82 |
83 | def handleContactEvent(evt) {
84 | logField(evt) { it == "open" ? "1" : "0" }
85 | }
86 |
87 | def handleAccelerationEvent(evt) {
88 | logField(evt) { it == "active" ? "1" : "0" }
89 | }
90 |
91 | def handleMotionEvent(evt) {
92 | logField(evt) { it == "active" ? "1" : "0" }
93 | }
94 |
95 | def handleSwitchEvent(evt) {
96 | logField(evt) { it == "on" ? "1" : "0" }
97 | }
98 |
99 | private getFieldMap(channelInfo) {
100 | def fieldMap = [:]
101 | channelInfo?.findAll { it.key?.startsWith("field") }.each { fieldMap[it.value?.trim().toLowerCase()] = it.key }
102 | return fieldMap
103 | }
104 |
105 | private updateChannelInfo() {
106 | log.debug "Retrieving channel info for ${channelId}"
107 |
108 | def url = "http://api.thingspeak.com/channels/${channelId}/feed.json?key=${channelKey}&results=0"
109 | httpGet(url) {
110 | response ->
111 | if (response.status != 200 ) {
112 | log.debug "ThingSpeak data retrieval failed, status = ${response.status}"
113 | } else {
114 | log.debug "ThingSpeak data retrieval successful, status = ${response.status}"
115 | state.channelInfo = response.data?.channel
116 | }
117 | }
118 |
119 | state.fieldMap = getFieldMap(state.channelInfo)
120 | }
121 |
122 | private logField(evt, Closure c) {
123 | def deviceName = evt.displayName.trim().toLowerCase()
124 | log.debug state.fieldMap
125 | def fieldNum = state.fieldMap[deviceName]
126 | if (!fieldNum) {
127 | log.debug "Device '${deviceName}' has no field"
128 | return
129 | }
130 |
131 | def value = c(evt.value)
132 | log.debug "Logging to channel ${channelId}, ${fieldNum}, value ${value}"
133 |
134 | def url = "http://api.thingspeak.com/update?key=${channelKey}&${fieldNum}=${value}"
135 | httpGet(url) {
136 | response ->
137 | if (response.status != 200 ) {
138 | log.debug "ThingSpeak logging failed, status = ${response.status}"
139 | } else {
140 | log.debug "ThingSpeak logging successful, status = ${response.status}"
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/smartapps/thingspeak-logger-ii/thingspeak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicholaswilde/smartthings/250cbde85d63c7819a0cebed48bb538e0955b7e9/smartapps/thingspeak-logger-ii/thingspeak.png
--------------------------------------------------------------------------------
/smartapps/thingspeak-logger-ii/thingspeak@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicholaswilde/smartthings/250cbde85d63c7819a0cebed48bb538e0955b7e9/smartapps/thingspeak-logger-ii/thingspeak@2x.png
--------------------------------------------------------------------------------