├── CrockPot.v010.app.groovy ├── MimoPowerIsOut.groovy ├── NotifyMeWhen.groovy ├── README.md ├── ResetColorOnClose.groovy ├── ST Sample OAuth2.groovy ├── SocketPowerIsOut.app.groovy ├── Trigger-Fibaro-RGBW-Action-From-Contact-Sensor.groovy ├── Turn-on-Police-Light-When-It-Opens.groovy ├── Turn-on-Police-Light-When-Switch-Is-On.groovy ├── Water Off When Wet.groovy ├── dim-with-me.app.groovy └── scene-machine.app.groovy /CrockPot.v010.app.groovy: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Device: CrockPot.v010.app.groovy 3 | Author: twack@wackware.net 4 | Version: 1.0 5 | Date: 2013-04-06 6 | Purpose: To control a crockpot using a switch. This app was written using 7 | GE outdoor lighting controller so there is functionality to see if 8 | the controller or power strip is running/plugged in. Typically the 9 | user will have a Zwave outlet. You need to use a dumb crockpot that 10 | only has a off/low/med/high physical switch. Set the crockpot to the 11 | desired level and plug into the switch. 12 | 13 | Use License: Non-Profit Open Software License version 3.0 (NPOSL-3.0) 14 | http://opensource.org/licenses/NPOSL-3.0 15 | 16 | ****************************************************************************** 17 | Change Log 18 | 19 | Version: 1.0 20 | Date: 20130406 21 | Change1: Initial Release 22 | 23 | ****************************************************************************** 24 | 25 | Device Types Supported: Switch 26 | 27 | To-Do's: Create a customer companion device that has tile with minutes 28 | left to cook. 29 | 30 | Other Info: This written to demo at San Francisco with SmartThings. 31 | 32 | ******************************************************************************/ 33 | 34 | 35 | // Automatically generated. Make future change here. 36 | definition( 37 | name: "SmartThings CrockPot Controller 1.0", 38 | namespace: "wackware", 39 | author: "todd@wackford.net", 40 | description: "CrockPots Will Win the Machine -v- Man Wars!", 41 | category: "My Apps", 42 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 43 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png") 44 | 45 | preferences { 46 | section("Crockpot Cooking Info..."){ 47 | input "startTime", "time", title: "Start time...", required: true 48 | input "onDuration", "decimal", title: "For how long...", required: true 49 | input "meal", "text", title: "Meal name (optional)...", required: false 50 | } 51 | section("CrockPot Notifications") { 52 | input "phone", "phone", title: "Text me at (optional)...", multiple: false, required: false 53 | } 54 | section("CrockPot Controller Switch") { 55 | input "switch1", "capability.switch", multiple: false, required: true 56 | } 57 | } 58 | 59 | def installed() { 60 | log.debug "Installed with settings: ${settings}" 61 | startUp() 62 | } 63 | 64 | def updated() { 65 | log.debug "Updated with settings: ${settings}" 66 | unschedule() 67 | unsubscribe() 68 | startUp() 69 | } 70 | 71 | def startUp() { 72 | 73 | def usrMilliTime = timeToday(startTime) 74 | if(now() > usrMilliTime.time) { 75 | log.debug "start time is not ok ${startTime}" 76 | communicateError("CROCKPOT ERROR: Your Starting Time is in the Past. Please update Starting Time") 77 | return 78 | } else { 79 | log.debug "start time is ok ${startTime}" 80 | } 81 | 82 | state.onTimer = onDuration?.toDouble() * 60 83 | state.errorCount = 0 84 | log.debug "The user running time is: ${state.onTimer}" 85 | schedule(startTime, "turnOnDevice") 86 | subscribe(switch1, "switch.on", communicateOn) 87 | subscribe(switch1, "switch.off", communicateOff) 88 | } 89 | 90 | def shutdown(){ 91 | unschedule() 92 | unsubscribe() 93 | switch1.off() 94 | } 95 | 96 | def checkStatus() { 97 | if (state.onTimer > 0) { 98 | log.debug "Timer = ${state.onTimer}" 99 | if (switchOK("on") == true) { 100 | state.onTimer = state.onTimer - 1 101 | state.errorCount = 0 102 | //here's where someday we'll update the display on the app/device for minutes left 103 | } else { 104 | state.errorCount = state.errorCount + 1 105 | switch1.on() 106 | } 107 | } else { 108 | // we should not get here as the off event handler will kill this schedule 109 | log.debug "Timer = ${state.onTimer}" 110 | switch1.off() 111 | } 112 | log.debug "We had ${state.errorCount - 1} errors calling device on" 113 | if(state.errorCount > 3) { 114 | communicateCantStart() 115 | shutdown() 116 | } 117 | } 118 | 119 | def switchOK(value) 120 | { 121 | def result = false 122 | for (it in (switch1 ?: [])) { 123 | log.debug "SwitchState = ${it.currentSwitch}" 124 | if (it.currentSwitch == value) { 125 | result = true 126 | break 127 | } 128 | } 129 | result 130 | } 131 | 132 | def turnOnDevice() { 133 | schedule("0 * * * * ?", "checkStatus") // used to verify on and stay on for duration 134 | checkStatus() 135 | } 136 | 137 | 138 | def communicateError(msg) { 139 | if (phone != "") { 140 | sendSms(phone, msg) 141 | } 142 | 143 | def push = true 144 | if(push == true) { 145 | sendPush(msg) 146 | } 147 | } 148 | 149 | def communicate(msg){ 150 | log.debug "COMMUNICATING" 151 | 152 | if (phone != "") { 153 | sendSms(phone, msg) 154 | } 155 | 156 | def push = true //code like this so we can toggle it on or off during testing in program 157 | if(push == true) { 158 | sendPush(msg) 159 | } 160 | } 161 | 162 | def communicateOn(evt){ 163 | if (settings['meal'] == '""') { 164 | settings['meal'] = "Meal" 165 | } 166 | 167 | def msg = "A Smart CrockPot Says: I have started cooking the ${meal}. " 168 | msg+= "I will be finished in ${onDuration} hours." 169 | communicate(msg) 170 | } 171 | 172 | def communicateOff(evt) { 173 | if (settings['meal'] == '""') { 174 | settings['meal'] = "Meal" 175 | } 176 | 177 | def msg = "A Smart CrockPot Says: I have finished cooking the ${meal}. " 178 | msg+= "Bon Appetit!" 179 | communicate(msg) 180 | shutdown() 181 | } 182 | 183 | def communicateCantStart() { 184 | def msg = "Unable to talk to the Cloud. " 185 | msg+= "Please contact your CrockPot System Administrator. " 186 | msg+= "Or, check that I am plugged in." 187 | communicate(msg) 188 | } 189 | -------------------------------------------------------------------------------- /MimoPowerIsOut.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * MimoPowerIsOut.groovy 3 | * 4 | * Copyright 2016 SmartThings 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 | definition( 18 | name: "MimoLite Power Is Out", 19 | namespace: "smartthings", 20 | author: "Todd Wackford", 21 | description: "Alert me of a power loss on MimoLite. SmartThings hub and internet connection must be working so this does not work if whole house power is lost. The app works great for like a GFI or breaker trips in part of the house.", 22 | category: "Safety & Security", 23 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 24 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", 25 | iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 26 | 27 | preferences { 28 | section("When there's AC power loss on..."){ 29 | input "myDevice", "capability.contactSensor", title: "Where?" 30 | } 31 | section("Via a push notification and a text message(optional)"){ 32 | input "pushAndPhone", "enum", title: "Send Text?", required: false, metadata: [values: ["Yes","No"]] 33 | input "phone1", "phone", title: "Phone Number (for Text, optional)", required: false 34 | 35 | } 36 | } 37 | 38 | def installed() 39 | { 40 | subscribe(myDevice, "powered.powerOff", lostPowerHandler) 41 | } 42 | 43 | def updated() 44 | { 45 | unsubscribe() 46 | subscribe(myDevice, "powered.powerOff", lostPowerHandler) 47 | } 48 | 49 | def lostPowerHandler(evt) { 50 | log.trace "$evt.value: $evt, $settings" 51 | def msg = "${myDevice.label ?: myDevice.name} detected loss of input power" 52 | 53 | log.debug "sending push" 54 | sendPush(msg) 55 | 56 | if ( phone1 && pushAndPhone ) { 57 | log.debug "sending SMS to ${phone1}" 58 | sendSms(phone1, msg) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /NotifyMeWhen.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Notify Me When 3 | * 4 | * Author: SmartThings 5 | * Date: 2013-03-20 6 | * 7 | * Change Log: 8 | * 1. Todd Wackford 9 | * 2014-10-03: Added capability.button device picker and button.pushed event subscription. For Doorbell. 10 | */ 11 | definition( 12 | name: "Notify Me When", 13 | namespace: "smartthings", 14 | author: "SmartThings", 15 | description: "Get a push notification or text message when any of a variety of SmartThings is activated. Supports button push, motion, contact, acceleration, moisture and presence sensors as well as switches.", 16 | category: "Convenience", 17 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact.png", 18 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact@2x.png" 19 | ) 20 | 21 | preferences { 22 | section("Choose one or more, when..."){ 23 | input "button", "capability.button", title: "Button Pushed", required: false, multiple: true //tw 24 | input "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true 25 | input "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true 26 | input "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true 27 | input "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true 28 | input "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true 29 | input "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true 30 | input "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true 31 | input "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true 32 | input "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true 33 | input "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true 34 | } 35 | section("Send this message (optional, sends standard status message if not specified)"){ 36 | input "messageText", "text", title: "Message Text", required: false 37 | } 38 | section("Via a push notification and/or an SMS message"){ 39 | input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false 40 | input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes","No"] 41 | } 42 | section("Minimum time between messages (optional, defaults to every message)") { 43 | input "frequency", "decimal", title: "Minutes", required: false 44 | } 45 | } 46 | 47 | def installed() { 48 | log.debug "Installed with settings: ${settings}" 49 | subscribeToEvents() 50 | } 51 | 52 | def updated() { 53 | log.debug "Updated with settings: ${settings}" 54 | unsubscribe() 55 | subscribeToEvents() 56 | } 57 | 58 | def subscribeToEvents() { 59 | subscribe(button, "button.pushed", eventHandler) //tw 60 | subscribe(contact, "contact.open", eventHandler) 61 | subscribe(contactClosed, "contact.closed", eventHandler) 62 | subscribe(acceleration, "acceleration.active", eventHandler) 63 | subscribe(motion, "motion.active", eventHandler) 64 | subscribe(mySwitch, "switch.on", eventHandler) 65 | subscribe(mySwitchOff, "switch.off", eventHandler) 66 | subscribe(arrivalPresence, "presence.present", eventHandler) 67 | subscribe(departurePresence, "presence.not present", eventHandler) 68 | subscribe(smoke, "smoke.detected", eventHandler) 69 | subscribe(smoke, "smoke.tested", eventHandler) 70 | subscribe(smoke, "carbonMonoxide.detected", eventHandler) 71 | subscribe(water, "water.wet", eventHandler) 72 | } 73 | 74 | def eventHandler(evt) { 75 | log.debug "Notify got evt ${evt}" 76 | if (frequency) { 77 | def lastTime = state[evt.deviceId] 78 | if (lastTime == null || now() - lastTime >= frequency * 60000) { 79 | sendMessage(evt) 80 | } 81 | } 82 | else { 83 | sendMessage(evt) 84 | } 85 | } 86 | 87 | private sendMessage(evt) { 88 | def msg = messageText ?: defaultText(evt) 89 | log.debug "$evt.name:$evt.value, pushAndPhone:$pushAndPhone, '$msg'" 90 | 91 | if (!phone || pushAndPhone != "No") { 92 | log.debug "sending push" 93 | sendPush(msg) 94 | } 95 | if (phone) { 96 | log.debug "sending SMS" 97 | sendSms(phone, msg) 98 | } 99 | if (frequency) { 100 | state[evt.deviceId] = now() 101 | } 102 | } 103 | 104 | private defaultText(evt) { 105 | if (evt.name == "presence") { 106 | if (evt.value == "present") { 107 | if (includeArticle) { 108 | "$evt.linkText has arrived at the $location.name" 109 | } 110 | else { 111 | "$evt.linkText has arrived at $location.name" 112 | } 113 | } 114 | else { 115 | if (includeArticle) { 116 | "$evt.linkText has left the $location.name" 117 | } 118 | else { 119 | "$evt.linkText has left $location.name" 120 | } 121 | } 122 | } 123 | else { 124 | evt.descriptionText 125 | } 126 | } 127 | 128 | private getIncludeArticle() { 129 | def name = location.name.toLowerCase() 130 | def segs = name.split(" ") 131 | !(["work","home"].contains(name) || (segs.size() > 1 && (["the","my","a","an"].contains(segs[0]) || segs[0].endsWith("'s")))) 132 | } 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | smarthings-apps 2 | =============== 3 | 4 | Files for SmartThings Apps 5 | -------------------------------------------------------------------------------- /ResetColorOnClose.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Reset Color on Close 3 | * 4 | * Copyright 2015 smartthings 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 | definition ( 18 | name: "Reset Color on Close", 19 | namespace: "smartthings", 20 | author: "smartthings", 21 | description: "Return color bulbs to previous setting on closure of contact sensor(s).", 22 | category: "Convenience", 23 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 24 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", 25 | iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png" 26 | ) 27 | 28 | preferences { 29 | section("When this/these contact(s) close...") { 30 | input "contacts", "capability.contactSensor", multiple: true 31 | } 32 | section("Return this light to the color at contact open...") { 33 | input "bulb", "capability.colorControl" 34 | } 35 | } 36 | 37 | def installed() { 38 | subscribe(contacts, "contact.open", contactOpenHandler) 39 | subscribe(contacts, "contact.closed", contactClosedHandler) 40 | } 41 | 42 | def updated() { 43 | unsubscribe() 44 | subscribe(contacts, "contact.open", contactOpenHandler) 45 | subscribe(contacts, "contact.closed", contactclosedHandler) 46 | } 47 | 48 | def contactOpenHandler(evt) { 49 | def values = [:] 50 | values = [ level: bulb.latestValue("level") as Integer, 51 | hex: bulb.latestValue("color"), 52 | saturation: bulb.latestValue("saturation"), 53 | hue: bulb.latestValue("hue")] 54 | 55 | atomicState.previousValues = values 56 | log.info "Previous values are: ${atomicState.previousValues}" 57 | } 58 | 59 | def contactclosedHandler(evt) { 60 | bulb.setColor(atomicState.previousValues) 61 | } 62 | -------------------------------------------------------------------------------- /ST Sample OAuth2.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * OAuth2 Credentials Service Manager 3 | * 4 | * Author: todd@wackford.net 5 | * Date: 2014-03-05 6 | */ 7 | 8 | // Automatically generated. Make future change here. 9 | definition( 10 | name: "OAuth2 Credentials Service Manager", 11 | namespace: "YourNameSpaceGoesHere", 12 | author: "barney@rubble.com", 13 | description: "Connect your Cloud Devices to SmartThings using OAuth2 credential methods.", 14 | category: "My Apps", 15 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 16 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png", 17 | oauth: true) 18 | 19 | preferences { 20 | page(name: "Credentials", title: "Fetch OAuth2 Credentials", content: "authPage", install: false) 21 | } 22 | 23 | mappings { 24 | path("/receivedToken") { action: [ POST: "receivedToken", GET: "receivedToken"] } 25 | path("/receiveToken") { action: [ POST: "receiveToken", GET: "receiveToken"] } 26 | 27 | // This is where you get call backs and process posted events from the vendor. This code will get errors until 28 | // you define a legit endpoint (below). Error example "Service Manager DOES NOT RESPOND TO UPDATED HANDLER" 29 | 30 | //path("/vendorEvents") { action: [ POST: "vendorPostEventsHandler", GET: "vendorGetEventsHandler"] } 31 | } 32 | 33 | private getVendorName() { "Super Widgets" } 34 | private getVendorAuthPath() { "https://superapi.superwidgets.com/oauth2/authorize?" } 35 | private getVendorTokenPath(){ "https://superapi.superwidgets.com/oauth2/token?" } 36 | private getVendorIcon() { "https://s3.amazonaws.com/smartthings-device-icons/custom/super-widgets/beertap@2x.png" } 37 | private getClientId() { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } 38 | private getClientSecret() { "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" } 39 | 40 | private getServerUrl() { return "https://graph.api.smartthings.com" } 41 | 42 | 43 | def authPage() { 44 | log.debug "In authPage" 45 | 46 | def description = null 47 | 48 | if (state.vendorAccessToken == null) { 49 | log.debug "About to create access token." 50 | 51 | createAccessToken() 52 | description = "Tap to enter Credentials." 53 | 54 | def redirectUrl = oauthInitUrl() 55 | 56 | 57 | return dynamicPage(name: "Credentials", title: "Authorize Connection", nextPage: null, uninstall: false, install:false) { 58 | section { href url:redirectUrl, style:"embedded", required:false, title:"Connect to ${getVendorName()}:", description:description } 59 | } 60 | } else { 61 | description = "Press 'Done' to proceed" 62 | 63 | return dynamicPage(name: "Credentials", title: "Credentials Accepted!", nextPage: null, uninstall: true, install:true) { 64 | section { href url: buildRedirectUrl("receivedToken"), style:"embedded", required:false, title:"${getVendorName()} is now connected to SmartThings!", description:description } 65 | } 66 | } 67 | } 68 | 69 | def oauthInitUrl() { 70 | log.debug "In oauthInitUrl" 71 | 72 | /* OAuth Step 1: Request access code with our client ID */ 73 | 74 | state.oauthInitState = UUID.randomUUID().toString() 75 | 76 | def oauthParams = [ response_type: "code", 77 | client_id: getClientId(), 78 | state: state.oauthInitState, 79 | redirect_uri: buildRedirectUrl("receiveToken") ] 80 | 81 | return getVendorAuthPath() + toQueryString(oauthParams) 82 | } 83 | 84 | def buildRedirectUrl(endPoint) { 85 | log.debug "In buildRedirectUrl" 86 | 87 | return getServerUrl() + "/api/token/${state.accessToken}/smartapps/installations/${app.id}/${endPoint}" 88 | } 89 | 90 | def receiveToken() { 91 | log.debug "In receiveToken" 92 | 93 | def oauthParams = [ client_secret: getClientSecret(), 94 | grant_type: "authorization_code", 95 | code: params.code ] 96 | 97 | def tokenUrl = getVendorTokenPath() + toQueryString(oauthParams) 98 | def params = [ 99 | uri: tokenUrl, 100 | ] 101 | 102 | /* OAuth Step 2: Request access token with our client Secret and OAuth "Code" */ 103 | httpPost(params) { response -> 104 | 105 | def data = response.data.data 106 | 107 | state.vendorRefreshToken = data.refresh_token //these may need to be adjusted depending on depth of returned data 108 | state.vendorAccessToken = data.access_token 109 | } 110 | 111 | if ( !state.vendorAccessToken ) { //We didn't get an access token, bail on install 112 | return 113 | } 114 | 115 | /* OAuth Step 3: Use the access token to call into the vendor API throughout your code using state.vendorAccessToken. */ 116 | 117 | def html = """ 118 | 119 | 120 | 121 | 122 | ${getVendorName()} Connection 123 | 173 | 174 | 175 |
176 | Vendor icon 177 | connected device icon 178 | SmartThings logo 179 |

We have located your """ + getVendorName() + """ account.

180 |

Tap 'Done' to process your credentials.

181 |
182 | 183 | 184 | """ 185 | render contentType: 'text/html', data: html 186 | } 187 | 188 | def receivedToken() { 189 | log.debug "In receivedToken" 190 | 191 | def html = """ 192 | 193 | 194 | 195 | 196 | Withings Connection 197 | 247 | 248 | 249 |
250 | Vendor icon 251 | connected device icon 252 | SmartThings logo 253 |

Your Quirky account is now connected to SmartThings. Tap 'Done' to continue to choose devices.

254 |
255 | 256 | 257 | """ 258 | render contentType: 'text/html', data: html 259 | } 260 | 261 | String toQueryString(Map m) { 262 | return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&") 263 | } 264 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /SocketPowerIsOut.app.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * SocketPowerIsOut.groovy 3 | * 4 | * Copyright 2015 Todd Wackford 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 | definition( 18 | name: "Socket Power Is Out", 19 | namespace: "wackware", 20 | author: "Todd Wackford", 21 | description: "Alert me of an ac power loss on motion detector by detecting change from powered to battery. SmartThings hub and internet connection must be working so this does not work if whole house power is lost. The app works great for like a GFI or breaker trips in part of the house.", 22 | category: "Safety & Security", 23 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 24 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", 25 | iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 26 | 27 | preferences { 28 | section("When there's AC power loss on..."){ 29 | input "myDevice", "capability.battery", title: "Where?" 30 | } 31 | section("Via a push notification and a text message(optional)"){ 32 | input "pushAndPhone", "enum", title: "Send Text?", required: false, metadata: [values: ["Yes","No"]] 33 | input "phone1", "phone", title: "Phone Number (for Text, optional)", required: false 34 | 35 | } 36 | } 37 | 38 | def installed() 39 | { 40 | subscribe(myDevice, "powerSource.battery", onBatteryPowerHandler) 41 | } 42 | 43 | def updated() 44 | { 45 | unsubscribe() 46 | subscribe(myDevice, "powerSource.battery", onBatteryPowerHandler) 47 | } 48 | 49 | def onBatteryPowerHandler(evt) { 50 | log.trace "$evt.value: $evt, $settings" 51 | def msg = "${myDevice.label ?: myDevice.name} detected going to battery power" 52 | 53 | log.debug "sending push" 54 | sendPush(msg) 55 | 56 | if ( phone1 && pushAndPhone ) { 57 | log.debug "sending SMS to ${phone1}" 58 | sendSms(phone1, msg) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Trigger-Fibaro-RGBW-Action-From-Contact-Sensor.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Trigger a Fibaro RGBW action when a contact sensor opens. 3 | * User can optionaly pick what action is taken by the controller 4 | * such as; turn green, blue or run the fireplace program. If the 5 | * user does not pick an action, this app will run the next 6 | * sequential action from the list (see below). The controller 7 | * will be turned off whne the contact sensor closes. 8 | * 9 | * Author: Todd Wackford 10 | */ 11 | definition( 12 | name: "Trigger Fibaro RGBW action from contact sensor", 13 | namespace: "smartthings", 14 | author: "Todd Wackford", 15 | description: "Trigger Fibaro RGBW when an contact open/close sensor opens...", 16 | category: "My Apps", 17 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/light_contact-outlet.png", 18 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/light_contact-outlet@2x.png" 19 | ) 20 | 21 | preferences { 22 | section(""){ 23 | input "contact1", "capability.contactSensor", title: "When this door opens..." 24 | input "switches", "capability.switch", multiple: true, title: "Turn on this/these Fibaro Controller(s)..." 25 | input(name: "switchAction", required: false, type: "enum", title: "And run this action (optional)", 26 | options: ["red", "green", "blue", "white", "cyan", "magenta", "orange", "purple", "yellow", 27 | "pink", "coldWhite", "warmWhite", "fireplace", "storm", "deepfade", "litefade", "police"]) 28 | } 29 | } 30 | 31 | def installed() { 32 | log.info "installed with $settings" 33 | subscribe(contact1, "contact.open", contactOpenHandler) 34 | subscribe(contact1, "contact.closed", contactClosedHandler) 35 | initialize() 36 | } 37 | 38 | def updated() { 39 | log.info "updated with $settings" 40 | unsubscribe() 41 | subscribe(contact1, "contact.open", contactOpenHandler) 42 | subscribe(contact1, "contact.closed", contactclosedHandler) 43 | initialize() 44 | } 45 | 46 | def initialize() { 47 | state.actionList = ["red", "green", "blue", "white", "cyan", "magenta", "orange", "purple", "yellow", 48 | "pink", "coldWhite", "warmWhite", "fireplace", "storm", "deepfade", "litefade", "police"] 49 | } 50 | 51 | def contactOpenHandler(evt) { 52 | if ( switchAction ) { 53 | switches."${switchAction}"() 54 | } else { 55 | def cnt = counterHandler() 56 | log.trace "Turning on Controllers $switches with: ${state.actionList.get(cnt)}" 57 | switches."${state.actionList.get(cnt)}"() 58 | } 59 | } 60 | 61 | def contactclosedHandler(evt) { 62 | log.trace "Turning off Controllers: $switches" 63 | switches.off() 64 | } 65 | 66 | def counterHandler() { 67 | if ( (state.actionCounter == null) || (state.actionCounter >= (state.actionList.size() - 1)) ) 68 | state.actionCounter = 0 69 | else 70 | state.actionCounter = state.actionCounter + 1 71 | return state.actionCounter 72 | } 73 | -------------------------------------------------------------------------------- /Turn-on-Police-Light-When-It-Opens.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Turn on Police Light When It Opens 3 | * 4 | * Author: SmartThings 5 | */ 6 | definition( 7 | name: "Turn on Police Light When It Opens", 8 | namespace: "smartthings", 9 | author: "SmartThings", 10 | description: "Turn something on when an open/close sensor opens.", 11 | category: "My Apps", 12 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/light_contact-outlet.png", 13 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/light_contact-outlet@2x.png" 14 | ) 15 | 16 | preferences { 17 | section("When the door opens..."){ 18 | input "contact1", "capability.contactSensor", title: "Where?" 19 | } 20 | section("Turn on the Fibaro Police Light..."){ 21 | input "switches", "capability.switch", multiple: false 22 | } 23 | } 24 | 25 | 26 | def installed() 27 | { 28 | subscribe(contact1, "contact.open", contactOpenHandler) 29 | subscribe(contact1, "contact.closed", contactClosedHandler) 30 | } 31 | 32 | def updated() 33 | { 34 | unsubscribe() 35 | subscribe(contact1, "contact.open", contactOpenHandler) 36 | subscribe(contact1, "contact.closed", contactclosedHandler) 37 | } 38 | 39 | def contactOpenHandler(evt) { 40 | log.debug "$evt.value: $evt, $settings" 41 | log.trace "Turning on switches: $switches" 42 | switches.police() 43 | } 44 | 45 | def contactclosedHandler(evt) { 46 | log.debug "$evt.value: $evt, $settings" 47 | log.trace "Turning on switches: $switches" 48 | switches.off() 49 | } 50 | -------------------------------------------------------------------------------- /Turn-on-Police-Light-When-Switch-Is-On.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Turn on Police Light When Switch Is On 3 | * 4 | * Author: Todd Wackford 5 | */ 6 | definition( 7 | name: "Turn on Police Light When Switch Is On", 8 | namespace: "smartthings", 9 | author: "twack", 10 | description: "Turn Fibaro Controller to Police lights program when a switch, real or virtual, is turned on.", 11 | category: "My Apps", 12 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/light_contact-outlet.png", 13 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/light_contact-outlet@2x.png" 14 | ) 15 | preferences { 16 | section("When a Switch is turned on..."){ 17 | input "switch", "capability.switch", title: "Which?" 18 | } 19 | section("Turn on this/these Fibaro Police Light(s)..."){ 20 | input "switches", "capability.switch", multiple: true 21 | } 22 | } 23 | def installed() { 24 | subscribe(switch, "switch.on", switchOnHandler) 25 | subscribe(switch, "switch.off", switchOffHandler) 26 | } 27 | def updated() { 28 | unsubscribe() 29 | subscribe(switch, "switch.on", switchOnHandler) 30 | subscribe(switch, "switch.off", switchOffHandler) 31 | } 32 | def switchOnHandler(evt) { 33 | log.trace "Turning on switches: $switches" 34 | switches.police() 35 | } 36 | def switchOffHandler(evt) { 37 | log.trace "Turning on switches: $switches" 38 | switches.off() 39 | } 40 | -------------------------------------------------------------------------------- /Water Off When Wet.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Water Off When Wet 4 | * 5 | * 6 | * DO NOT USE THIS APP TO SHUT OFF HOUSE MAIN WATER IF YOU HAVE 7 | * A SPRINKLER SYSTEM IN YOUR HOME! 8 | * 9 | * 10 | * If you have a sprinkler system in your home, only install this app to control local valves like for your wash machine, 11 | * sink or toilet. 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * Copyright 2015 Todd Wackford 18 | * 19 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 20 | * in compliance with the License. You may obtain a copy of the License at: 21 | * 22 | * http://www.apache.org/licenses/LICENSE-2.0 23 | * 24 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 25 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 26 | * for the specific language governing permissions and limitations under the License. 27 | * 28 | */ 29 | definition( 30 | name: "Water Off When Wet", 31 | namespace: "wackware", 32 | author: "Todd Wackford", 33 | description: "Shut a water valve off when moisture sensor senses water. Also send notifications.", 34 | category: "Safety & Security", 35 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/water_moisture.png", 36 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/water_moisture@2x.png" 37 | ) 38 | 39 | preferences { 40 | section("When there's water detected by...") { 41 | input "waterSensor", "capability.waterSensor", title: "Where?", multiple: true 42 | } 43 | section("Shut off this/these water valve(s)...") { 44 | input "waterValve", "capability.valve", title: "Which?", multiple: true 45 | } 46 | section("Send a notification to...") { 47 | input("recipients", "contact", title: "Recipients", description: "Send notifications to") { 48 | input "phone", "phone", title: "Phone number?", required: false 49 | } 50 | } 51 | } 52 | 53 | def installed() { 54 | subscribe(waterSensor, "water.wet", waterWetHandler) 55 | } 56 | 57 | def updated() { 58 | unsubscribe() 59 | subscribe(waterSensor, "water.wet", waterWetHandler) 60 | } 61 | 62 | def waterWetHandler(evt) { 63 | //shut off the water 64 | waterValve.close() 65 | 66 | def deltaSeconds = 60 67 | 68 | def timeAgo = new Date(now() - (1000 * deltaSeconds)) 69 | def recentEvents = waterSensor.eventsSince(timeAgo) 70 | log.debug "Found ${recentEvents?.size() ?: 0} events in the last $deltaSeconds seconds" 71 | 72 | def alreadySentSms = recentEvents.count { it.value && it.value == "wet" } > 1 73 | 74 | if (alreadySentSms) { 75 | log.debug "SMS already sent to $phone within the last $deltaSeconds seconds" 76 | } else { 77 | def msg = "${waterSensor.displayName} is wet! Shutting valve(s) ${waterValve.displayName}" 78 | log.debug "$waterSensor is wet, shuting valve $waterValve, texting $phone" 79 | 80 | if (location.contactBookEnabled) { 81 | sendNotificationToContacts(msg, recipients) 82 | } 83 | else { 84 | sendPush(msg) 85 | if (phone) { 86 | sendSms(phone, msg) 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /dim-with-me.app.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * dim-with-me.app.groovy 3 | * Dim With Me 4 | * 5 | * Author: todd@wackford.net 6 | * Date: 2013-11-12 7 | */ 8 | /** 9 | * App Name: Dim With Me 10 | * 11 | * Author: Todd Wackford 12 | * twack@wackware.net 13 | * Date: 2013-11-12 14 | * Version: 0.2 15 | * 16 | * Use this program with a virtual dimmer as the master for best results. 17 | * 18 | * This app lets the user select from a list of dimmers to act as a triggering 19 | * master for other dimmers or regular switches. Regular switches come on 20 | * anytime the master dimmer is on or dimmer level is set to more than 0%. 21 | * of the master dimmer. 22 | * 23 | ****************************************************************************** 24 | * Changes 25 | ****************************************************************************** 26 | * 27 | * Change 1: 2014-10-22 (wackford) 28 | * Fixed bug in setlevelwhen on/off was coming in 29 | * 30 | * Change 2: 2014-11-01 (wackford) 31 | * added subscription to switch.level event. Shouldn't change much 32 | * but some devices only sending level event and not setLevel. 33 | * 34 | ****************************************************************************** 35 | 36 | Other Info: Special thanks to Danny Kleinman at ST for helping me get the 37 | state stuff figured out. The Android state filtering had me 38 | stumped. 39 | * 40 | ****************************************************************************** 41 | */ 42 | 43 | 44 | // Automatically generated. Make future change here. 45 | definition( 46 | name: "Dim With Me", 47 | namespace: "wackware", 48 | author: "todd@wackford.net", 49 | description: "Follows the dimmer level of another dimmer", 50 | category: "My Apps", 51 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 52 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png" 53 | ) 54 | 55 | preferences { 56 | section("When this...") { 57 | input "masters", "capability.switchLevel", 58 | multiple: false, 59 | title: "Master Dimmer Switch...", 60 | required: true 61 | } 62 | 63 | section("Then these will follow with on/off...") { 64 | input "slaves2", "capability.switch", 65 | multiple: true, 66 | title: "Slave On/Off Switch(es)...", 67 | required: false 68 | } 69 | 70 | section("And these will follow with dimming level...") { 71 | input "slaves", "capability.switchLevel", 72 | multiple: true, 73 | title: "Slave Dimmer Switch(es)...", 74 | required: true 75 | } 76 | } 77 | 78 | def installed() 79 | { 80 | subscribe(masters, "switch.on", switchOnHandler) 81 | subscribe(masters, "switch.off", switchOffHandler) 82 | subscribe(masters, "switch.setLevel", switchSetLevelHandler) 83 | subscribe(masters, "switch", switchSetLevelHandler) 84 | } 85 | 86 | def updated() 87 | { 88 | unsubscribe() 89 | subscribe(masters, "switch.on", switchOnHandler) 90 | subscribe(masters, "switch.off", switchOffHandler) 91 | subscribe(masters, "switch.setLevel", switchSetLevelHandler) 92 | subscribe(masters, "switch", switchSetLevelHandler) 93 | log.info "subscribed to all of switches events" 94 | } 95 | 96 | def switchSetLevelHandler(evt) 97 | { 98 | 99 | if ((evt.value == "on") || (evt.value == "off" )) 100 | return 101 | def level = evt.value.toFloat() 102 | level = level.toInteger() 103 | log.info "switchSetLevelHandler Event: ${level}" 104 | slaves?.setLevel(level) 105 | } 106 | 107 | def switchOffHandler(evt) { 108 | log.info "switchoffHandler Event: ${evt.value}" 109 | slaves?.off() 110 | slaves2?.off() 111 | } 112 | 113 | def switchOnHandler(evt) { 114 | log.info "switchOnHandler Event: ${evt.value}" 115 | def dimmerValue = masters.latestValue("level") //can be turned on by setting the level 116 | slaves?.on() 117 | slaves2?.on() 118 | } 119 | -------------------------------------------------------------------------------- /scene-machine.app.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * App Name: Scene Machine 3 | * 4 | * Author: Todd Wackford 5 | * twack@wackware.net 6 | * Date: 2013-06-14 7 | * Version: 1.1 8 | * 9 | * Updated: 2013-07-25 10 | * 11 | * Change #1 Fixed bug where string null was being returned for non-dimmers and 12 | * was trying to assign to variable. 13 | * 14 | * Change #2 Updated setLevel setion to work with bulbs that were not defined as type "Dimmer Switch" 15 | * 16 | * 17 | * This app lets the user select from a list of switches or dimmers and record 18 | * their currents states as a Scene. It is suggested that you name the app 19 | * during install something like "Scene - Romantic Dinner" or 20 | * "Scene - Movie Time". Switches can be added, removed or set at new levels 21 | * by editing and updating the app from the smartphone interface. 22 | * 23 | * Usage Note: GE-Jasco dimmers with ST is real buggy right now. Sometimes the levels 24 | * get correctly, sometimes not. 25 | * On/Off is OK. 26 | * Other dimmers should be OK. 27 | * 28 | * Use License: Non-Profit Open Software License version 3.0 (NPOSL-3.0) 29 | * http://opensource.org/licenses/NPOSL-3.0 30 | */ 31 | // Automatically generated. Make future change here. 32 | definition( 33 | name: "Scene Machine Orig", 34 | namespace: "wackware", 35 | author: "todd@wackford.net", 36 | description: "This app lets the user select from a list of switches or dimmers and record their currents states as a Scene. It is suggested that you name the app during install something like 'Scene - Romantic Dinner' or 'Scene - Movie Time'. Switches can be added removed or set at new levels by editing and updating the app from the smartphone interface.", 37 | category: "My Apps", 38 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 39 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png" 40 | ) 41 | 42 | preferences { 43 | section("Select switches ...") { 44 | input "switches", "capability.switch", multiple: true 45 | } 46 | 47 | //Uncomment this section below to test/change on the IDE. Smartphone does 48 | //not need it. 49 | 50 | //section("Record New Scene?") { 51 | // input "record", "enum", title: "New Scene...", multiple: false, 52 | // required: true, metadata:[values:['No','Yes']] 53 | //} 54 | } 55 | 56 | def installed() { 57 | log.debug "Installed with settings: ${settings}" 58 | subscribe(app, appTouch) 59 | getDeviceSettings() 60 | } 61 | 62 | def updated() { 63 | log.debug "Updated with settings: ${settings}" 64 | unsubscribe() 65 | subscribe(app, appTouch) 66 | 67 | //if(record == "Yes") //uncomment this line to test/change stuff on the IDE 68 | getDeviceSettings() 69 | } 70 | 71 | def appTouch(evt) { 72 | log.debug "appTouch: $evt" 73 | setScene() 74 | } 75 | 76 | private setScene() { 77 | 78 | def i = 0 79 | def switchName = "" 80 | def switchType = "" 81 | def switchState = "" 82 | def dimmerValue = "" 83 | 84 | for(myData in state.lastSwitchData) { 85 | 86 | switchName = myData.switchName 87 | switchType = myData.switchType 88 | switchState = myData.switchState 89 | if(myData.dimmerValue != "null") // 90 | dimmerValue = myData.dimmerValue.toInteger() //BF #1 91 | else // 92 | dimmerValue = 0 // 93 | 94 | log.info "switchName: $switchName" 95 | log.info "switchType: $switchType" 96 | log.info "switchState: $switchState" 97 | log.info "dimmerValue: $dimmerValue" 98 | 99 | if(switchState == "on") 100 | switches[i].on() 101 | 102 | if(dimmerValue > 0) 103 | switches[i].setLevel(dimmerValue) 104 | 105 | if(switchState == "off") 106 | switches[i].off() 107 | 108 | i++ 109 | log.info "Device setting is Done-------------------" 110 | } 111 | } 112 | 113 | private getDeviceSettings() { 114 | 115 | def cnt = 0 116 | for(myCounter in switches) { 117 | switches[cnt].refresh() //this was a try to get dimmer values (bug) 118 | //switches[cnt].poll() 119 | cnt++ 120 | } 121 | 122 | state.lastSwitchData = [cnt] 123 | 124 | def i = 0 125 | def switchName = "" 126 | def switchType = "" 127 | def switchState = "" 128 | def dimmerValue = "" 129 | 130 | for(mySwitch in switches) { 131 | switchName = mySwitch.device.toString() 132 | switchType = mySwitch.name.toString() 133 | switchState = mySwitch.latestValue("switch").toString() 134 | //dimmerValue below returns null if it is not a dimmer 135 | dimmerValue = mySwitch.latestValue("level").toString() 136 | 137 | state.lastSwitchData[i] = [switchName: switchName, 138 | switchType: switchType, 139 | switchState: switchState, 140 | dimmerValue: dimmerValue] 141 | 142 | log.debug "SwitchData: ${state.lastSwitchData[i]}" 143 | i++ 144 | } 145 | } 146 | --------------------------------------------------------------------------------