├── .gitattributes ├── .gitignore ├── DeviceTypes └── fuzzysb │ ├── Fibaro Dual Relay │ └── FibaroDualRelay.groovy │ ├── Fibaro Motion Sensor v3.2 │ └── Fibaro_Motion_Sensor_v3.2.groovy │ ├── Fibaro Universal Sensor │ └── Fibaro Universal Sensor.groovy │ ├── Homeseer-HS3 │ └── Homeseer-HS3.groovy │ ├── Jasco_GE_Plug_In_Dimmer_Module │ └── Jasco_GE_Plug_In_Dimmer_Module.groovy │ ├── MCOHome MH-S412 │ └── MCOHome_MH-S412.groovy │ ├── Popp_Dimmer_123603 │ ├── Popp_dimmer_123603.groovy │ └── Popp_dimmer_As_Switch_123603.groovy │ ├── TV_Bed │ └── TV-Bed.groovy │ ├── Tado-Heating-User-Presence │ └── Tado-Heating-User-Presence.groovy │ ├── tado-Cooling-AC.src │ └── Images │ │ ├── auto_mode_icon.png │ │ ├── cool_arrow_down.png │ │ ├── cool_arrow_up.png │ │ ├── cool_mode_icon.png │ │ ├── dry_mode_icon.png │ │ ├── emergencyHeat.png │ │ ├── endManual.png │ │ ├── fan_auto_icon.png │ │ ├── fan_high_icon.png │ │ ├── fan_low_icon.png │ │ ├── fan_med_icon.png │ │ ├── fan_mode_icon.png │ │ ├── fan_mode_icononly.png │ │ ├── fan_off_icon.png │ │ ├── fan_on_icon.png │ │ ├── heat_arrow_down.png │ │ ├── heat_arrow_up.png │ │ ├── heat_mode_icon.png │ │ ├── hvac_auto.png │ │ ├── hvac_cool.png │ │ ├── hvac_dry.png │ │ ├── hvac_heat.png │ │ ├── hvac_off.png │ │ ├── hvac_on.png │ │ └── off_mode_icon.png │ ├── tado.Heating.src │ └── Images │ │ ├── emergencyHeat.png │ │ ├── endManual.png │ │ ├── heat_arrow_down.png │ │ ├── heat_arrow_up.png │ │ ├── heat_mode_icon.png │ │ ├── hvac_auto.png │ │ ├── hvac_heat.png │ │ ├── hvac_off.png │ │ ├── hvac_on.png │ │ └── off_mode_icon.png │ └── tado.Hot.Water.src │ └── Images │ ├── emergencyHeat.png │ ├── endManual.png │ ├── heat_arrow_down.png │ ├── heat_arrow_up.png │ ├── heat_mode_icon.png │ ├── hvac_heat.png │ ├── hvac_off.png │ ├── hvac_on.png │ ├── off_mode_icon.png │ └── tap_icon.png ├── SmartApps └── fuzzysb │ ├── Button Controller Plus │ └── ButtonControllerPlus.groovy │ ├── Fibaro Dual Relay Secondary Switch Binder │ └── FibaroDualRelaySecondarySwitchBinder.groovy │ ├── HomeSeer Link │ ├── ContactSensortoHS3.groovy │ ├── MotionSensortoHS3.groovy │ ├── PresenceSensortoHS3.groovy │ ├── SwitchSyncWithHS3.groovy │ └── SwitchtoHS3.groovy │ ├── Netatmo Sound Sensor │ └── NetatmoSoundSensor.groovy │ ├── Rule Machine │ ├── Rule-Machine.groovy │ ├── RuleMachine@2x.png │ ├── rule.groovy │ └── rulemachine.png │ ├── Sonos Door Knocker │ └── SonosDoorKnocker.groovy │ ├── TV-Bed-Connect │ └── TV-Bed-Connect.groovy │ └── VoxCommando │ └── Recieve VoxCommando Events.groovy ├── devicetypes └── fuzzysb │ ├── aeon-dsd37-repeater.src │ └── aeon-dsd37-repeater.groovy │ ├── aeon-water-sensor.src │ └── aeon-water-sensor.groovy │ ├── axis-gate-opener.src │ └── axis-gate-opener.groovy │ ├── dlink-dch-z510.src │ └── dlink-dch-z510.groovy │ ├── everspring-st812-flood-detector.src │ └── everspring-st812-flood-detector.groovy │ ├── fibaro-door-window-temp-sensor.src │ └── fibaro-door-window-temp-sensor.groovy │ ├── fibaro-dual-relay.src │ └── fibaro-dual-relay.groovy │ ├── fibaro-universal-sensor-contact-motion.src │ └── fibaro-universal-sensor-contact-motion.groovy │ ├── fibaro-universal-sensor-dual-contact.src │ └── fibaro-universal-sensor-dual-contact.groovy │ ├── tkb-home-tz65s.src │ └── tkb-home-tz65s.groovy │ ├── vera-plus.src │ └── vera-plus.groovy │ ├── virtual-presence-sensor.src │ └── virtual-presence-sensor.groovy │ ├── voxcommando-tts-announcer.src │ └── voxcommando-tts-announcer.groovy │ ├── zipato-ph-pse02-eu.src │ └── zipato-ph-pse02-eu.groovy │ ├── zwave-me-key-fob.src │ └── zwave-me-key-fob.groovy │ └── zwave-me-zwave-controller.src │ └── zwave-me-zwave-controller.groovy └── smartapps └── fuzzysb ├── asuswrt-tv-on-off-check.src ├── CheckTVon ├── asuswrt-tv-checker.groovy └── init-start ├── asuswrt-wifi-presence.src ├── CheckIfHome ├── asuswrt-wifi-presence.groovy └── init-start └── dummy-connect.src └── dummy-connect.groovy /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/Fibaro Dual Relay/FibaroDualRelay.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Fibaro Dual Relay 3 | * 4 | * Author Stuart Buchanan based on work of Matt Frank, chrisb for AEON Power Strip 5 | * 6 | * Date Created: 12/22/2015 7 | * Last Modified: 12/22/2015 8 | * 9 | */ 10 | // for the UI 11 | metadata { 12 | definition (name: "Fibaro Dual Relay", namespace: "fuzzysb", author: "Stuart Buchanan") { 13 | capability "Switch" 14 | capability "Polling" 15 | capability "Configuration" 16 | capability "Refresh" 17 | capability "Zw Multichannel" 18 | 19 | attribute "switch", "string" 20 | attribute "switch2", "string" 21 | 22 | command "on" 23 | command "off" 24 | command "on2" 25 | command "off2" 26 | 27 | fingerprint deviceId: "0x1001", inClusters:"0x60, 0x25, 0x27, 0x85, 0x72, 0x86" 28 | } 29 | 30 | simulator { 31 | status "on": "command: 2003, payload: FF" 32 | status "off": "command: 2003, payload: 00" 33 | reply "8E010101,delay 800,6007": "command: 6008, payload: 4004" 34 | reply "8505": "command: 8506, payload: 02" 35 | reply "59034002": "command: 5904, payload: 8102003101000000" 36 | reply "6007": "command: 6008, payload: 0002" 37 | reply "600901": "command: 600A, payload: 10002532" 38 | reply "600902": "command: 600A, payload: 210031" 39 | } 40 | 41 | tiles { 42 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { 43 | state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821" 44 | state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" 45 | } 46 | standardTile("switch2", "device.switch2",canChangeIcon: true) { 47 | state "on", label: "switch2", action: "off2", icon: "st.switches.switch.on", backgroundColor: "#79b821" 48 | state "off", label: "switch2", action: "on2", icon: "st.switches.switch.off", backgroundColor: "#ffffff" 49 | } 50 | standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { 51 | state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" 52 | } 53 | 54 | standardTile("configure", "device.switch", inactiveLabel: false, decoration: "flat") { 55 | state "default", label:"", action:"configure", icon:"st.secondary.configure" 56 | } 57 | 58 | main("switch") 59 | details(["switch","switch2","refresh","configure"]) 60 | } 61 | } 62 | 63 | def parse(String description) { 64 | def result = null 65 | def cmd = zwave.parse(description, [0x60:3, 0x25:1, 0x70:1, 0x32:1, 0x72:1]) 66 | if (cmd) { 67 | result = zwaveEvent(cmd) 68 | log.debug "Parsed ${description} to ${cmd} to ${result.inspect()}" 69 | } else { 70 | log.debug "Non-parsed event: ${description}" 71 | } 72 | return result 73 | } 74 | 75 | 76 | //Reports 77 | 78 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 79 | // This will capture any commands not handled by other instances of zwaveEvent 80 | // and is recommended for development so you can see every command the device sends 81 | return createEvent(descriptionText: "${device.displayName}: ${cmd}") 82 | } 83 | 84 | def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { 85 | log.debug "SwitchBinaryReport $cmd" 86 | def result = [] 87 | result << response( 88 | delayBetween([ 89 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(), 90 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 91 | ]) 92 | ) 93 | result 94 | } 95 | 96 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { 97 | log.debug "MultiChannelCmdEncap $cmd" 98 | def name = "switch$cmd.sourceEndPoint" 99 | if (cmd.sourceEndPoint == 1) name = "switch" 100 | def map = [ name: name ] 101 | if (cmd.commandClass == 37) { 102 | if (cmd.parameter == [0]) { 103 | map.value = "off" 104 | } 105 | if (cmd.parameter == [255]) { 106 | map.value = "on" 107 | } 108 | return createEvent(map) 109 | } 110 | } 111 | 112 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) { 113 | log.debug "MultiChannelCapabilityReport $cmd" 114 | } 115 | 116 | def refresh() { 117 | log.debug "refresh" 118 | delayBetween([ 119 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(), 120 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 121 | ]) 122 | } 123 | 124 | 125 | def poll() { 126 | log.debug "poll" 127 | delayBetween([ 128 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(), 129 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 130 | ]) 131 | } 132 | 133 | def configure() { 134 | log.debug "configure" 135 | delayBetween([ 136 | zwave.configurationV1.configurationSet(parameterNumber:4, configurationValue: [0]).format() // Report reguarly 137 | ]) 138 | } 139 | 140 | def on() { 141 | delayBetween([ 142 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[255]).format(), 143 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format() 144 | ]) 145 | } 146 | 147 | def off() { 148 | delayBetween([ 149 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[0]).format(), 150 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format() 151 | ]) 152 | } 153 | 154 | def on2() { 155 | delayBetween([ 156 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:1, parameter:[255]).format(), 157 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 158 | ]) 159 | } 160 | 161 | def off2() { 162 | delayBetween([ 163 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:1, parameter:[0]).format(), 164 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 165 | ]) 166 | } -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/Fibaro Universal Sensor/Fibaro Universal Sensor.groovy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/Fibaro Universal Sensor/Fibaro Universal Sensor.groovy -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/Homeseer-HS3/Homeseer-HS3.groovy: -------------------------------------------------------------------------------- 1 | import groovy.json.JsonSlurper 2 | metadata { 3 | definition (name: "HomeSeer HS3", namespace: "fuzzysb", author: "Stuart Buchanan") { 4 | capability "Sensor" 5 | capability "Actuator" 6 | command "api" 7 | } 8 | 9 | preferences { 10 | input("ip", "text", title: "IP Address", description: "Your HS3 IP Address", required: true, displayDuringSetup: true) 11 | input("port", "number", title: "Port Number", description: "Your HS3 Port Number (Default:80)", defaultValue: "80", required: true, displayDuringSetup: true) 12 | } 13 | 14 | tiles (scale: 2){ 15 | valueTile("hubInfo", "device.hubInfo", decoration: "flat", height: 2, width: 6, inactiveLabel: false, canChangeIcon: false) { 16 | state "hubInfo", label:'${currentValue}' 17 | } 18 | } 19 | main("hubInfo") 20 | details(["hubInfo"]) 21 | } 22 | 23 | def parse(description) { 24 | log.debug description 25 | def events = [] 26 | def result 27 | def descMap = parseDescriptionAsMap(description) 28 | def body = new String(descMap["body"]) 29 | body = new String(descMap["body"].decodeBase64()) 30 | def slurper = new JsonSlurper() 31 | result = slurper.parseText(body) 32 | log.debug result 33 | events << createEvent(name:"hubInfo", value:result) 34 | return events 35 | } 36 | 37 | def parseDescriptionAsMap(description) { 38 | description.split(",").inject([:]) { map, param -> 39 | def nameAndValue = param.split(":") 40 | 41 | if (nameAndValue.length == 2) map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] 42 | else map += [(nameAndValue[0].trim()):""] 43 | } 44 | } 45 | 46 | def installed() { 47 | log.debug "Installed with settings: ${settings}" 48 | initialize() 49 | } 50 | 51 | def updated() { 52 | log.debug "Updated with settings: ${settings}" 53 | initialize() 54 | } 55 | 56 | def initialize() { 57 | ipSetup() 58 | } 59 | 60 | def api(String HS3Command, String HS3DevId) { 61 | ipSetup() 62 | def cmdPath 63 | def hubAction 64 | switch (HS3Command) { 65 | case "on": 66 | cmdPath = "/JSON?request=controldevicebyvalue&ref=${HS3DevId}&value=100" 67 | log.debug "The Switch On Command was sent to HS3 Device ${HS3DevId}" 68 | break; 69 | case "off": 70 | cmdPath = "/JSON?request=controldevicebyvalue&ref=${HS3DevId}&value=0" 71 | log.debug "The Switch Off Command was sent to HS3 Device ID: ${HS3DevId}" 72 | break; 73 | case "home": 74 | cmdPath = "/JSON?request=controldevicebyvalue&ref=${HS3DevId}&value=1" 75 | log.debug "The Switch On Command was sent to HS3 Device ${HS3DevId}" 76 | break; 77 | case "away": 78 | cmdPath = "/JSON?request=controldevicebyvalue&ref=${HS3DevId}&value=0" 79 | log.debug "The Switch Off Command was sent to HS3 Device ID: ${HS3DevId}" 80 | break; 81 | case "open": 82 | cmdPath = "/JSON?request=controldevicebyvalue&ref=${HS3DevId}&value=1" 83 | log.debug "The Contact Open Command was sent to HS3 Device ID: ${HS3DevId}" 84 | break; 85 | case "closed": 86 | cmdPath = "/JSON?request=controldevicebyvalue&ref=${HS3DevId}&value=0" 87 | log.debug "The Contact Closed Command was sent to HS3 Device ID: ${HS3DevId}" 88 | break; 89 | case "active": 90 | cmdPath = "/JSON?request=controldevicebyvalue&ref=${HS3DevId}&value=255" 91 | log.debug "The Motion active Command was sent to HS3 Device ID: ${HS3DevId}" 92 | break; 93 | case "inactive": 94 | cmdPath = "/JSON?request=controldevicebyvalue&ref=${HS3DevId}&value=0" 95 | log.debug "The Motion inactive Command was sent to HS3 Device ID: ${HS3DevId}" 96 | break; 97 | } 98 | 99 | switch (HS3Command) { 100 | default: 101 | try { 102 | hubAction = [new physicalgraph.device.HubAction( 103 | method: "GET", 104 | path: cmdPath, 105 | headers: [HOST: "${settings.ip}:${settings.port}", Accept: "application/json"] 106 | )] 107 | } 108 | catch (Exception e) { 109 | log.debug "Hit Exception $e on $hubAction" 110 | } 111 | break; 112 | } 113 | return hubAction 114 | } 115 | 116 | def ipSetup() { 117 | log.debug "In IPSetup Area" 118 | def hosthex 119 | def porthex 120 | if (settings.ip) { 121 | hosthex = convertIPtoHex(settings.ip).toUpperCase() 122 | } 123 | if (settings.port) { 124 | porthex = convertPortToHex(settings.port).toUpperCase() 125 | } 126 | if (settings.ip && settings.port) { 127 | log.debug "updating Network ID to ${hosthex}:${porthex}" 128 | device.deviceNetworkId = "${hosthex}:${porthex}" 129 | } 130 | } 131 | 132 | private String convertIPtoHex(ip) { 133 | String hexip = ip.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join() 134 | return hexip 135 | } 136 | private String convertPortToHex(port) { 137 | String hexport = port.toString().format( '%04x', port.toInteger() ) 138 | return hexport 139 | } -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/Jasco_GE_Plug_In_Dimmer_Module/Jasco_GE_Plug_In_Dimmer_Module.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 SmartThings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | */ 14 | metadata { 15 | definition (name: "Jasco GE Plug In Dimmer", namespace: "fuzzysb", author: "Stuart Buchanan") { 16 | capability "Switch Level" 17 | capability "Actuator" 18 | capability "Indicator" 19 | capability "Switch" 20 | capability "Polling" 21 | capability "Refresh" 22 | capability "Sensor" 23 | 24 | command "resetParams2StDefaults" 25 | command "listCurrentParams" 26 | 27 | fingerprint inClusters: "0x26" 28 | } 29 | 30 | simulator { 31 | status "on": "command: 2003, payload: FF" 32 | status "off": "command: 2003, payload: 00" 33 | status "09%": "command: 2003, payload: 09" 34 | status "10%": "command: 2003, payload: 0A" 35 | status "33%": "command: 2003, payload: 21" 36 | status "66%": "command: 2003, payload: 42" 37 | status "99%": "command: 2003, payload: 63" 38 | 39 | // reply messages 40 | reply "2001FF,delay 5000,2602": "command: 2603, payload: FF" 41 | reply "200100,delay 5000,2602": "command: 2603, payload: 00" 42 | reply "200119,delay 5000,2602": "command: 2603, payload: 19" 43 | reply "200132,delay 5000,2602": "command: 2603, payload: 32" 44 | reply "20014B,delay 5000,2602": "command: 2603, payload: 4B" 45 | reply "200163,delay 5000,2602": "command: 2603, payload: 63" 46 | } 47 | 48 | tiles(scale: 2) { 49 | multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ 50 | tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { 51 | attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" 52 | attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" 53 | attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" 54 | attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" 55 | } 56 | tileAttribute ("device.level", key: "SLIDER_CONTROL") { 57 | attributeState "level", action:"switch level.setLevel" 58 | } 59 | } 60 | 61 | standardTile("indicator", "device.indicatorStatus", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { 62 | state "when off", action:"indicator.indicatorWhenOn", icon:"st.indicators.lit-when-off" 63 | state "when on", action:"indicator.indicatorNever", icon:"st.indicators.lit-when-on" 64 | state "never", action:"indicator.indicatorWhenOff", icon:"st.indicators.never-lit" 65 | } 66 | 67 | standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { 68 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" 69 | } 70 | 71 | valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { 72 | state "level", label:'${currentValue} %', unit:"%", backgroundColor:"#ffffff" 73 | } 74 | 75 | main(["switch"]) 76 | details(["switch", "level", "indicator", "refresh"]) 77 | 78 | } 79 | } 80 | 81 | def parse(String description) { 82 | def result = null 83 | if (description != "updated") { 84 | log.debug "parse() >> zwave.parse($description)" 85 | def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1]) 86 | if (cmd) { 87 | result = zwaveEvent(cmd) 88 | } 89 | } 90 | if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) { 91 | result = [result, response(zwave.basicV1.basicGet())] 92 | log.debug "Was hailed: requesting state update" 93 | } else { 94 | log.debug "Parse returned ${result?.descriptionText}" 95 | } 96 | return result 97 | } 98 | 99 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { 100 | dimmerEvents(cmd) 101 | } 102 | 103 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { 104 | dimmerEvents(cmd) 105 | } 106 | 107 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd) { 108 | dimmerEvents(cmd) 109 | } 110 | 111 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd) { 112 | dimmerEvents(cmd) 113 | } 114 | 115 | private dimmerEvents(physicalgraph.zwave.Command cmd) { 116 | def value = (cmd.value ? "on" : "off") 117 | def result = [createEvent(name: "switch", value: value)] 118 | if (cmd.value && cmd.value <= 100) { 119 | result << createEvent(name: "level", value: cmd.value, unit: "%") 120 | } 121 | return result 122 | } 123 | 124 | 125 | def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { 126 | log.debug "ConfigurationReport $cmd" 127 | def value = "when off" 128 | if (cmd.configurationValue[0] == 1) {value = "when on"} 129 | if (cmd.configurationValue[0] == 2) {value = "never"} 130 | createEvent([name: "indicatorStatus", value: value]) 131 | } 132 | 133 | def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) { 134 | createEvent([name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false]) 135 | } 136 | 137 | def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { 138 | log.debug "manufacturerId: ${cmd.manufacturerId}" 139 | log.debug "manufacturerName: ${cmd.manufacturerName}" 140 | log.debug "productId: ${cmd.productId}" 141 | log.debug "productTypeId: ${cmd.productTypeId}" 142 | def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) 143 | updateDataValue("MSR", msr) 144 | createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false]) 145 | } 146 | 147 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd) { 148 | [createEvent(name:"switch", value:"on"), response(zwave.switchMultilevelV1.switchMultilevelGet().format())] 149 | } 150 | 151 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 152 | // Handles all Z-Wave commands we aren't interested in 153 | [:] 154 | } 155 | 156 | def on() { 157 | delayBetween([ 158 | zwave.basicV1.basicSet(value: 0xFF).format(), 159 | zwave.switchMultilevelV1.switchMultilevelGet().format() 160 | ],5000) 161 | } 162 | 163 | def off() { 164 | delayBetween([ 165 | zwave.basicV1.basicSet(value: 0x00).format(), 166 | zwave.switchMultilevelV1.switchMultilevelGet().format() 167 | ],5000) 168 | } 169 | 170 | def setLevel(value) { 171 | log.debug "setLevel >> value: $value" 172 | def valueaux = value as Integer 173 | def level = Math.max(Math.min(valueaux, 99), 0) 174 | if (level > 0) { 175 | sendEvent(name: "switch", value: "on") 176 | } else { 177 | sendEvent(name: "switch", value: "off") 178 | } 179 | sendEvent(name: "level", value: level, unit: "%") 180 | delayBetween ([zwave.basicV1.basicSet(value: level).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000) 181 | } 182 | 183 | def setLevel(value, duration) { 184 | log.debug "setLevel >> value: $value, duration: $duration" 185 | def valueaux = value as Integer 186 | def level = Math.max(Math.min(valueaux, 99), 0) 187 | def dimmingDuration = duration < 128 ? duration : 128 + Math.round(duration / 60) 188 | def getStatusDelay = duration < 128 ? (duration*1000)+2000 : (Math.round(duration / 60)*60*1000)+2000 189 | delayBetween ([zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format(), 190 | zwave.switchMultilevelV1.switchMultilevelGet().format()], getStatusDelay) 191 | } 192 | 193 | def poll() { 194 | zwave.switchMultilevelV1.switchMultilevelGet().format() 195 | } 196 | 197 | def refresh() { 198 | log.debug "refresh() is called" 199 | def commands = [] 200 | commands << zwave.switchMultilevelV1.switchMultilevelGet().format() 201 | if (getDataValue("MSR") == null) { 202 | commands << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() 203 | } 204 | delayBetween(commands,100) 205 | } 206 | 207 | def indicatorWhenOn() { 208 | sendEvent(name: "indicatorStatus", value: "when on") 209 | zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format() 210 | } 211 | 212 | def indicatorWhenOff() { 213 | sendEvent(name: "indicatorStatus", value: "when off") 214 | zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format() 215 | } 216 | 217 | def indicatorNever() { 218 | sendEvent(name: "indicatorStatus", value: "never") 219 | zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format() 220 | } 221 | 222 | def invertSwitch(invert=true) { 223 | if (invert) { 224 | zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format() 225 | } 226 | else { 227 | zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format() 228 | } 229 | } 230 | 231 | def resetParams2StDefaults() { 232 | log.debug "Resetting Sensor Parameters to SmartThings Compatible Defaults" 233 | def cmds = [] 234 | cmds << zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format() 235 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 5, size: 1).format() 236 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 7, size: 1).format() 237 | cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 8, size: 1).format() 238 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 11, size: 1).format() 239 | cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 12, size: 1).format() 240 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 29, size: 1).format() 241 | 242 | delayBetween(cmds, 500) 243 | } 244 | 245 | def listCurrentParams() { 246 | log.debug "Listing of current parameter settings of ${device.displayName}" 247 | def cmds = [] 248 | cmds << zwave.associationV1.associationGet(groupingIdentifier: 1).format() 249 | cmds << zwave.associationV1.associationGet(groupingIdentifier: 2).format() 250 | cmds << zwave.associationV1.associationGet(groupingIdentifier: 3).format() 251 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 5).format() 252 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format() 253 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 8).format() 254 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 11).format() 255 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format() 256 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 29).format() 257 | 258 | delayBetween(cmds, 500) 259 | } 260 | -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/MCOHome MH-S412/MCOHome_MH-S412.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * MCOHome MH-S412 3 | * 4 | * Author Stuart Buchanan based on work of Matt Frank, chrisb for AEON Power Strip 5 | * 6 | * Date Created: 12/22/2015 7 | * Last Modified: 12/22/2015 8 | * 9 | */ 10 | // for the UI 11 | metadata { 12 | definition (name: "MCOHome MH-S412 Dual Relay", namespace: "fuzzysb", author: "Stuart Buchanan") { 13 | capability "Switch" 14 | capability "Polling" 15 | capability "Configuration" 16 | capability "Refresh" 17 | capability "Zw Multichannel" 18 | 19 | attribute "switch", "string" 20 | attribute "switch2", "string" 21 | 22 | command "on" 23 | command "off" 24 | command "on2" 25 | command "off2" 26 | 27 | fingerprint deviceId: "0x1001", inClusters:"0x25 0x27 0x85 0x60 0x8E 0x72 0x86 0xEF 0x20 0x60" 28 | } 29 | 30 | simulator { 31 | } 32 | 33 | tiles { 34 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { 35 | state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821" 36 | state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" 37 | } 38 | standardTile("switch2", "device.switch2",canChangeIcon: true) { 39 | state "on", label: "switch2", action: "off2", icon: "st.switches.switch.on", backgroundColor: "#79b821" 40 | state "off", label: "switch2", action: "on2", icon: "st.switches.switch.off", backgroundColor: "#ffffff" 41 | } 42 | standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { 43 | state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" 44 | } 45 | 46 | standardTile("configure", "device.switch", inactiveLabel: false, decoration: "flat") { 47 | state "default", label:"", action:"configure", icon:"st.secondary.configure" 48 | } 49 | 50 | main("switch") 51 | details(["switch","switch2","refresh","configure"]) 52 | } 53 | } 54 | 55 | def parse(String description) { 56 | def result = null 57 | def cmd = zwave.parse(description, [0x20:1, 0x60:3, 0x25:1, 0x70:1, 0x32:1, 0x72:1]) 58 | if (cmd) { 59 | result = zwaveEvent(cmd) 60 | log.debug "Parsed ${description} to ${cmd} to ${result.inspect()}" 61 | } else { 62 | log.debug "Non-parsed event: ${description}" 63 | } 64 | return result 65 | } 66 | 67 | 68 | //Reports 69 | 70 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 71 | // This will capture any commands not handled by other instances of zwaveEvent 72 | // and is recommended for development so you can see every command the device sends 73 | return createEvent(descriptionText: "${device.displayName}: ${cmd}") 74 | } 75 | 76 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { 77 | log.debug "MultiChannelCmdEncap $cmd" 78 | def name = "switch$cmd.sourceEndPoint" 79 | if (cmd.sourceEndPoint == 1) name = "switch" 80 | def map = [ name: name ] 81 | if (cmd.commandClass == 37) { 82 | if (cmd.parameter == [0]) { 83 | map.value = "off" 84 | } 85 | if (cmd.parameter == [255]) { 86 | map.value = "on" 87 | } 88 | return createEvent(map) 89 | } 90 | } 91 | 92 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) { 93 | log.debug "MultiChannelCapabilityReport $cmd" 94 | } 95 | 96 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) 97 | { 98 | def result = [] 99 | result << createEvent(name:"switch", value: cmd.value ? "on" : "off") 100 | 101 | // For a multilevel switch, cmd.value can be from 1-99 to represent dimming levels 102 | result << createEvent(name:"level", value: cmd.value, unit:"%", descriptionText:"${device.displayName} dimmed ${cmd.value==255 ? 100 : cmd.value}%") 103 | 104 | result 105 | } 106 | 107 | def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) 108 | { 109 | createEvent(name:"switch", value: cmd.value ? "on" : "off") 110 | } 111 | 112 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) 113 | { 114 | def result = [] 115 | result << createEvent(name:"switch", value: cmd.value ? "on" : "off") 116 | result << createEvent(name:"level", value: cmd.value, unit:"%", descriptionText:"${device.displayName} dimmed ${cmd.value==255 ? 100 : cmd.value}%") 117 | result 118 | } 119 | 120 | 121 | def refresh() { 122 | log.debug "refresh" 123 | delayBetween([ 124 | getendpointreport(1), 125 | getendpointreport(2) 126 | ]) 127 | } 128 | 129 | 130 | def poll() { 131 | log.debug "poll" 132 | refresh() 133 | } 134 | 135 | def configure() { 136 | log.debug "configure" 137 | delayBetween([ 138 | zwave.associationV1.associationSet(groupingIdentifier: 1, nodeId: zwaveHubNodeId).format(), 139 | zwave.associationV1.associationSet(groupingIdentifier: 2, nodeId: zwaveHubNodeId).format(), 140 | zwave.associationV1.associationSet(groupingIdentifier: 3, nodeId: zwaveHubNodeId).format() 141 | ]) 142 | } 143 | 144 | def on() { 145 | delayBetween([ 146 | endpointon(1), 147 | getendpointreport(1) 148 | ]) 149 | } 150 | 151 | def off() { 152 | delayBetween([ 153 | endpointoff(1), 154 | getendpointreport(1) 155 | ]) 156 | } 157 | 158 | def on2() { 159 | delayBetween([ 160 | endpointon(2), 161 | getendpointreport(2) 162 | ]) 163 | } 164 | 165 | def off2() { 166 | delayBetween([ 167 | endpointoff(2), 168 | getendpointreport(2) 169 | ]) 170 | } 171 | 172 | 173 | def endpointon(endpoint) { 174 | log.debug "MCO2-on $endpoint" 175 | encap(zwave.basicV1.basicSet(value: 0xFF), endpoint).format() 176 | } 177 | 178 | def endpointoff(endpoint) { 179 | log.debug "MCO2-off $endpoint" 180 | encap(zwave.basicV1.basicSet(value: 0x00), endpoint).format() 181 | } 182 | 183 | def getendpointreport(endpoint) { 184 | log.debug "MCO2-off $endpoint" 185 | encap(zwave.basicV1.basicGet(), endpoint).format() 186 | } 187 | 188 | private encap(cmd, endpoint) { 189 | log.debug "MCO2-encap $endpoint {$cmd}" 190 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:endpoint, destinationEndPoint:endpoint).encapsulate(cmd) 191 | } 192 | -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/Popp_Dimmer_123603/Popp_dimmer_123603.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 SmartThings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | */ 14 | metadata { 15 | definition (name: "Popp Dimmer 123603", namespace: "fuzzysb", author: "Stuart Buchanan") { 16 | capability "Switch Level" 17 | capability "Actuator" 18 | capability "Indicator" 19 | capability "Switch" 20 | capability "Polling" 21 | capability "Refresh" 22 | capability "Sensor" 23 | 24 | command "resetParams2StDefaults" 25 | command "listCurrentParams" 26 | 27 | fingerprint inClusters: "0x26" 28 | } 29 | 30 | simulator { 31 | status "on": "command: 2003, payload: FF" 32 | status "off": "command: 2003, payload: 00" 33 | status "09%": "command: 2003, payload: 09" 34 | status "10%": "command: 2003, payload: 0A" 35 | status "33%": "command: 2003, payload: 21" 36 | status "66%": "command: 2003, payload: 42" 37 | status "99%": "command: 2003, payload: 63" 38 | 39 | // reply messages 40 | reply "2001FF,delay 5000,2602": "command: 2603, payload: FF" 41 | reply "200100,delay 5000,2602": "command: 2603, payload: 00" 42 | reply "200119,delay 5000,2602": "command: 2603, payload: 19" 43 | reply "200132,delay 5000,2602": "command: 2603, payload: 32" 44 | reply "20014B,delay 5000,2602": "command: 2603, payload: 4B" 45 | reply "200163,delay 5000,2602": "command: 2603, payload: 63" 46 | } 47 | 48 | tiles(scale: 2) { 49 | multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ 50 | tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { 51 | attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" 52 | attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" 53 | attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" 54 | attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" 55 | } 56 | tileAttribute ("device.level", key: "SLIDER_CONTROL") { 57 | attributeState "level", action:"switch level.setLevel" 58 | } 59 | } 60 | 61 | standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { 62 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" 63 | } 64 | 65 | valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { 66 | state "level", label:'${currentValue} %', unit:"%", backgroundColor:"#ffffff" 67 | } 68 | 69 | main(["switch"]) 70 | details(["switch", "level", "refresh"]) 71 | 72 | } 73 | } 74 | 75 | def parse(String description) { 76 | def result = null 77 | if (description != "updated") { 78 | log.debug "parse() >> zwave.parse($description)" 79 | def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1]) 80 | if (cmd) { 81 | result = zwaveEvent(cmd) 82 | } 83 | } 84 | if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) { 85 | result = [result, response(zwave.basicV1.basicGet())] 86 | log.debug "Was hailed: requesting state update" 87 | } else { 88 | log.debug "Parse returned ${result?.descriptionText}" 89 | } 90 | return result 91 | } 92 | 93 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { 94 | dimmerEvents(cmd) 95 | } 96 | 97 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { 98 | dimmerEvents(cmd) 99 | } 100 | 101 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd) { 102 | dimmerEvents(cmd) 103 | } 104 | 105 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd) { 106 | dimmerEvents(cmd) 107 | } 108 | 109 | private dimmerEvents(physicalgraph.zwave.Command cmd) { 110 | def value = (cmd.value ? "on" : "off") 111 | def result = [createEvent(name: "switch", value: value)] 112 | if (cmd.value && cmd.value <= 100) { 113 | result << createEvent(name: "level", value: cmd.value, unit: "%") 114 | } 115 | return result 116 | } 117 | 118 | 119 | def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { 120 | log.debug "ConfigurationReport $cmd" 121 | def value = "when off" 122 | if (cmd.configurationValue[0] == 1) {value = "when on"} 123 | if (cmd.configurationValue[0] == 2) {value = "never"} 124 | createEvent([name: "indicatorStatus", value: value]) 125 | } 126 | 127 | def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) { 128 | createEvent([name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false]) 129 | } 130 | 131 | def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { 132 | log.debug "manufacturerId: ${cmd.manufacturerId}" 133 | log.debug "manufacturerName: ${cmd.manufacturerName}" 134 | log.debug "productId: ${cmd.productId}" 135 | log.debug "productTypeId: ${cmd.productTypeId}" 136 | def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) 137 | updateDataValue("MSR", msr) 138 | createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false]) 139 | } 140 | 141 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd) { 142 | [createEvent(name:"switch", value:"on"), response(zwave.switchMultilevelV1.switchMultilevelGet().format())] 143 | } 144 | 145 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 146 | // Handles all Z-Wave commands we aren't interested in 147 | [:] 148 | } 149 | 150 | def on() { 151 | delayBetween([ 152 | zwave.basicV1.basicSet(value: 0xFF).format(), 153 | zwave.switchMultilevelV1.switchMultilevelGet().format() 154 | ],5000) 155 | } 156 | 157 | def off() { 158 | delayBetween([ 159 | zwave.basicV1.basicSet(value: 0x00).format(), 160 | zwave.switchMultilevelV1.switchMultilevelGet().format() 161 | ],5000) 162 | } 163 | 164 | def setLevel(value) { 165 | log.debug "setLevel >> value: $value" 166 | def valueaux = value as Integer 167 | def level = Math.max(Math.min(valueaux, 99), 0) 168 | if (level > 0) { 169 | sendEvent(name: "switch", value: "on") 170 | } else { 171 | sendEvent(name: "switch", value: "off") 172 | } 173 | sendEvent(name: "level", value: level, unit: "%") 174 | delayBetween ([zwave.basicV1.basicSet(value: level).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000) 175 | } 176 | 177 | def setLevel(value, duration) { 178 | log.debug "setLevel >> value: $value, duration: $duration" 179 | def valueaux = value as Integer 180 | def level = Math.max(Math.min(valueaux, 99), 0) 181 | def dimmingDuration = duration < 128 ? duration : 128 + Math.round(duration / 60) 182 | def getStatusDelay = duration < 128 ? (duration*1000)+2000 : (Math.round(duration / 60)*60*1000)+2000 183 | delayBetween ([zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format(), 184 | zwave.switchMultilevelV1.switchMultilevelGet().format()], getStatusDelay) 185 | } 186 | 187 | def poll() { 188 | zwave.switchMultilevelV1.switchMultilevelGet().format() 189 | } 190 | 191 | def refresh() { 192 | log.debug "refresh() is called" 193 | def commands = [] 194 | commands << zwave.switchMultilevelV1.switchMultilevelGet().format() 195 | if (getDataValue("MSR") == null) { 196 | commands << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() 197 | } 198 | delayBetween(commands,100) 199 | } 200 | 201 | def invertSwitch(invert=true) { 202 | if (invert) { 203 | zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format() 204 | } 205 | else { 206 | zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format() 207 | } 208 | } 209 | 210 | def resetParams2StDefaults() { 211 | log.debug "Resetting Sensor Parameters to SmartThings Compatible Defaults" 212 | def cmds = [] 213 | cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 1, size: 1).format() 214 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 2, size: 1).format() 215 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format() 216 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format() 217 | cmds << zwave.configurationV1.configurationSet(configurationValue: [30], parameterNumber: 5, size: 1).format() 218 | cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 6, size: 1).format() 219 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 7, size: 1).format() 220 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 8, size: 1).format() 221 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 9, size: 1).format() 222 | cmds << zwave.configurationV1.configurationSet(configurationValue: [50], parameterNumber: 10, size: 1).format() 223 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 17, size: 1).format() 224 | cmds << zwave.configurationV1.configurationSet(configurationValue: [99], parameterNumber: 18, size: 1).format() 225 | cmds << zwave.configurationV1.configurationSet(configurationValue: [99], parameterNumber: 19, size: 1).format() 226 | cmds << zwave.configurationV1.configurationSet(configurationValue: [28], parameterNumber: 51, size: 1).format() 227 | cmds << zwave.configurationV1.configurationSet(configurationValue: [28], parameterNumber: 52, size: 1).format() 228 | cmds << zwave.configurationV1.configurationSet(configurationValue: [10], parameterNumber: 53, size: 1).format() 229 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 54, size: 1).format() 230 | 231 | delayBetween(cmds, 500) 232 | } 233 | 234 | def listCurrentParams() { 235 | log.debug "Listing of current parameter settings of ${device.displayName}" 236 | def cmds = [] 237 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 1).format() 238 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 2).format() 239 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 3).format() 240 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 4).format() 241 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 5).format() 242 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 6).format() 243 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format() 244 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 8).format() 245 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 9).format() 246 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format() 247 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 17).format() 248 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 18).format() 249 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 19).format() 250 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 20).format() 251 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 51).format() 252 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 52).format() 253 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 53).format() 254 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 54).format() 255 | 256 | delayBetween(cmds, 500) 257 | } 258 | -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/Popp_Dimmer_123603/Popp_dimmer_As_Switch_123603.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 SmartThings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | */ 14 | metadata { 15 | definition (name: "Popp Dimmer 123603", namespace: "fuzzysb", author: "Stuart Buchanan") { 16 | capability "Switch Level" 17 | capability "Actuator" 18 | capability "Indicator" 19 | capability "Switch" 20 | capability "Polling" 21 | capability "Refresh" 22 | capability "Sensor" 23 | 24 | command "resetParams2StDefaults" 25 | command "listCurrentParams" 26 | 27 | fingerprint inClusters: "0x26" 28 | } 29 | 30 | simulator { 31 | status "on": "command: 2003, payload: FF" 32 | status "off": "command: 2003, payload: 00" 33 | status "09%": "command: 2003, payload: 09" 34 | status "10%": "command: 2003, payload: 0A" 35 | status "33%": "command: 2003, payload: 21" 36 | status "66%": "command: 2003, payload: 42" 37 | status "99%": "command: 2003, payload: 63" 38 | 39 | // reply messages 40 | reply "2001FF,delay 5000,2602": "command: 2603, payload: FF" 41 | reply "200100,delay 5000,2602": "command: 2603, payload: 00" 42 | reply "200119,delay 5000,2602": "command: 2603, payload: 19" 43 | reply "200132,delay 5000,2602": "command: 2603, payload: 32" 44 | reply "20014B,delay 5000,2602": "command: 2603, payload: 4B" 45 | reply "200163,delay 5000,2602": "command: 2603, payload: 63" 46 | } 47 | 48 | tiles(scale: 2) { 49 | multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ 50 | tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { 51 | attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" 52 | attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" 53 | attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" 54 | attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" 55 | } 56 | } 57 | 58 | standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { 59 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" 60 | } 61 | 62 | valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { 63 | state "level", label:'${currentValue} %', unit:"%", backgroundColor:"#ffffff" 64 | } 65 | 66 | main(["switch"]) 67 | details(["switch", "refresh"]) 68 | 69 | } 70 | } 71 | 72 | def parse(String description) { 73 | def result = null 74 | if (description != "updated") { 75 | log.debug "parse() >> zwave.parse($description)" 76 | def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1]) 77 | if (cmd) { 78 | result = zwaveEvent(cmd) 79 | } 80 | } 81 | if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) { 82 | result = [result, response(zwave.basicV1.basicGet())] 83 | log.debug "Was hailed: requesting state update" 84 | } else { 85 | log.debug "Parse returned ${result?.descriptionText}" 86 | } 87 | return result 88 | } 89 | 90 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { 91 | dimmerEvents(cmd) 92 | } 93 | 94 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { 95 | dimmerEvents(cmd) 96 | } 97 | 98 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd) { 99 | dimmerEvents(cmd) 100 | } 101 | 102 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd) { 103 | dimmerEvents(cmd) 104 | } 105 | 106 | private dimmerEvents(physicalgraph.zwave.Command cmd) { 107 | def value = (cmd.value ? "on" : "off") 108 | def result = [createEvent(name: "switch", value: value)] 109 | if (cmd.value && cmd.value <= 100) { 110 | result << createEvent(name: "level", value: cmd.value, unit: "%") 111 | } 112 | return result 113 | } 114 | 115 | 116 | def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { 117 | log.debug "ConfigurationReport $cmd" 118 | def value = "when off" 119 | if (cmd.configurationValue[0] == 1) {value = "when on"} 120 | if (cmd.configurationValue[0] == 2) {value = "never"} 121 | createEvent([name: "indicatorStatus", value: value]) 122 | } 123 | 124 | def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) { 125 | createEvent([name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false]) 126 | } 127 | 128 | def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { 129 | log.debug "manufacturerId: ${cmd.manufacturerId}" 130 | log.debug "manufacturerName: ${cmd.manufacturerName}" 131 | log.debug "productId: ${cmd.productId}" 132 | log.debug "productTypeId: ${cmd.productTypeId}" 133 | def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) 134 | updateDataValue("MSR", msr) 135 | createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false]) 136 | } 137 | 138 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd) { 139 | [createEvent(name:"switch", value:"on"), response(zwave.switchMultilevelV1.switchMultilevelGet().format())] 140 | } 141 | 142 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 143 | // Handles all Z-Wave commands we aren't interested in 144 | [:] 145 | } 146 | 147 | def on() { 148 | delayBetween([ 149 | setLevel(100), 150 | zwave.basicV1.basicSet(value: 0xFF).format(), 151 | zwave.switchMultilevelV1.switchMultilevelGet().format() 152 | ],5000) 153 | } 154 | 155 | def off() { 156 | delayBetween([ 157 | zwave.basicV1.basicSet(value: 0x00).format(), 158 | zwave.switchMultilevelV1.switchMultilevelGet().format() 159 | ],5000) 160 | } 161 | 162 | def setLevel(value) { 163 | log.debug "setLevel >> value: $value" 164 | def valueaux = value as Integer 165 | def level = Math.max(Math.min(valueaux, 99), 0) 166 | if (level > 0) { 167 | sendEvent(name: "switch", value: "on") 168 | } else { 169 | sendEvent(name: "switch", value: "off") 170 | } 171 | sendEvent(name: "level", value: level, unit: "%") 172 | delayBetween ([zwave.basicV1.basicSet(value: level).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000) 173 | } 174 | 175 | def setLevel(value, duration) { 176 | log.debug "setLevel >> value: $value, duration: $duration" 177 | def valueaux = value as Integer 178 | def level = Math.max(Math.min(valueaux, 99), 0) 179 | def dimmingDuration = duration < 128 ? duration : 128 + Math.round(duration / 60) 180 | def getStatusDelay = duration < 128 ? (duration*1000)+2000 : (Math.round(duration / 60)*60*1000)+2000 181 | delayBetween ([zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format(), 182 | zwave.switchMultilevelV1.switchMultilevelGet().format()], getStatusDelay) 183 | } 184 | 185 | def poll() { 186 | zwave.switchMultilevelV1.switchMultilevelGet().format() 187 | } 188 | 189 | def refresh() { 190 | log.debug "refresh() is called" 191 | def commands = [] 192 | commands << zwave.switchMultilevelV1.switchMultilevelGet().format() 193 | if (getDataValue("MSR") == null) { 194 | commands << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() 195 | } 196 | delayBetween(commands,100) 197 | } 198 | 199 | def invertSwitch(invert=true) { 200 | if (invert) { 201 | zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format() 202 | } 203 | else { 204 | zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format() 205 | } 206 | } 207 | 208 | def resetParams2StDefaults() { 209 | log.debug "Resetting Sensor Parameters to SmartThings Compatible Defaults" 210 | def cmds = [] 211 | cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 1, size: 1).format() 212 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 2, size: 1).format() 213 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format() 214 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format() 215 | cmds << zwave.configurationV1.configurationSet(configurationValue: [30], parameterNumber: 5, size: 1).format() 216 | cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 6, size: 1).format() 217 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 7, size: 1).format() 218 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 8, size: 1).format() 219 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 9, size: 1).format() 220 | cmds << zwave.configurationV1.configurationSet(configurationValue: [50], parameterNumber: 10, size: 1).format() 221 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 17, size: 1).format() 222 | cmds << zwave.configurationV1.configurationSet(configurationValue: [99], parameterNumber: 18, size: 1).format() 223 | cmds << zwave.configurationV1.configurationSet(configurationValue: [99], parameterNumber: 19, size: 1).format() 224 | cmds << zwave.configurationV1.configurationSet(configurationValue: [28], parameterNumber: 51, size: 1).format() 225 | cmds << zwave.configurationV1.configurationSet(configurationValue: [28], parameterNumber: 52, size: 1).format() 226 | cmds << zwave.configurationV1.configurationSet(configurationValue: [10], parameterNumber: 53, size: 1).format() 227 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 54, size: 1).format() 228 | 229 | delayBetween(cmds, 500) 230 | } 231 | 232 | def listCurrentParams() { 233 | log.debug "Listing of current parameter settings of ${device.displayName}" 234 | def cmds = [] 235 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 1).format() 236 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 2).format() 237 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 3).format() 238 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 4).format() 239 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 5).format() 240 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 6).format() 241 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format() 242 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 8).format() 243 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 9).format() 244 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format() 245 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 17).format() 246 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 18).format() 247 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 19).format() 248 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 20).format() 249 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 51).format() 250 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 52).format() 251 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 53).format() 252 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 54).format() 253 | 254 | delayBetween(cmds, 500) 255 | } 256 | -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/TV_Bed/TV-Bed.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * TV-Bed Device Handler 3 | * 4 | * Copyright 2016 Stuart Buchanan 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 | * 03/04/2016 V1.0 Initial Release 16 | */ 17 | 18 | 19 | preferences { 20 | 21 | } 22 | 23 | metadata { 24 | definition (name: "TV-Bed", namespace: "fuzzysb", author: "Stuart Buchanan") { 25 | 26 | capability "Switch" 27 | capability "Actuator" 28 | capability "Refresh" 29 | capability "Polling" 30 | } 31 | 32 | simulator { 33 | 34 | } 35 | 36 | tiles(scale: 2) { 37 | multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4){ 38 | tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { 39 | attributeState "on", label:'${name}', action:"switch.off", icon:"st.Bedroom.bedroom7", backgroundColor:"#79b821" 40 | attributeState "off", label:'${name}', action:"switch.on", icon:"st.Bedroom.bedroom7", backgroundColor:"#c0c0c0" 41 | } 42 | } 43 | main "switch" 44 | details(["switch"]) 45 | } 46 | } 47 | 48 | 49 | private parseResponse(resp) { 50 | log.debug("Executing parseResponse: "+resp.data) 51 | log.debug("Output status: "+resp.status) 52 | if(resp.status == 200) { 53 | log.debug("Executing parseResponse.successTrue") 54 | def id = resp.data.id 55 | def name = resp.data.name 56 | def connected = resp.data.connected 57 | def returnValue = resp.data.return_value 58 | }else if(resp.status == 201){ 59 | log.debug("Something was created/updated") 60 | } 61 | } 62 | 63 | private getDeviceDetails() { 64 | def fullDni = device.deviceNetworkId 65 | return fullDni 66 | } 67 | 68 | private sendCommand(method, args = []) { 69 | def DefaultUri = "https://api.particle.io" 70 | def cdni = getDeviceDetails().tokenize(':') 71 | def deviceId = cdni[0] 72 | def token = cdni[1] 73 | def methods = [ 74 | 'startTVUp': [ 75 | uri: "${DefaultUri}", 76 | path: "/v1/devices/${deviceId}/startTVUp", 77 | requestContentType: "application/json", 78 | query: [access_token: token], 79 | body: args[0] 80 | ], 81 | 'startTVDown': [ 82 | uri: "${DefaultUri}", 83 | path: "/v1/devices/${deviceId}/startTVDown", 84 | requestContentType: "application/json", 85 | query: [access_token: token], 86 | body: args[0] 87 | ], 88 | ] 89 | 90 | def request = methods.getAt(method) 91 | 92 | log.debug "Http Params ("+request+")" 93 | 94 | try{ 95 | log.debug "Executing 'sendCommand'" 96 | 97 | if (method == "startTVUp"){ 98 | log.debug "calling startTVUp Method" 99 | httpPost(request) { resp -> 100 | parseResponse(resp) 101 | } 102 | }else if (method == "startTVDown"){ 103 | log.debug "calling startTVDown Method" 104 | httpPost(request) { resp -> 105 | parseResponse(resp) 106 | } 107 | }else{ 108 | httpGet(request) 109 | } 110 | } catch(Exception e){ 111 | log.debug("___exception: " + e) 112 | } 113 | } 114 | 115 | 116 | def on() { 117 | log.debug "Executing 'on'" 118 | upCommand() 119 | } 120 | 121 | def off() { 122 | log.debug "Executing 'off'" 123 | downCommand() 124 | } 125 | 126 | def upCommand(){ 127 | log.debug "Executing 'sendCommand.setState'" 128 | def jsonbody = new groovy.json.JsonOutput().toJson(arg:"on") 129 | sendCommand("startTVUp",[jsonbody]) 130 | createEvent(name: "switch", value: "on", descriptionText: "$device.displayName is on") 131 | } 132 | 133 | def downCommand(){ 134 | log.debug "Executing 'sendCommand.setState'" 135 | def jsonbody = new groovy.json.JsonOutput().toJson(arg:"off") 136 | sendCommand("startTVDown",[jsonbody]) 137 | createEvent(name: "switch", value: "off", descriptionText: "$device.displayName is off") 138 | } 139 | 140 | 141 | -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/Tado-Heating-User-Presence/Tado-Heating-User-Presence.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Tado Thermostat 14 | * 15 | * Author: Stuart Buchanan, Based on original work by Ian M with thanks 16 | * Date: 2015-04-28 v1.3 changed Presence tile as this was reporting a bug 17 | * Date: 2015-04-28 v1.2 updated API call found issue where session was closed and nothing else was returned, now add number generator to input noCache statement in the query 18 | * Date: 2015-04-27 v1.1 updated API call and added refresh function 19 | * Date: 2015-12-04 v1.0 Initial Release 20 | */ 21 | 22 | import groovy.json.JsonOutput 23 | import groovy.json.JsonSlurper 24 | import java.util.Random 25 | 26 | preferences { 27 | input("username", "text", title: "Username", description: "Your Tado username") 28 | input("password", "password", title: "Password", description: "Your Tado password") 29 | input("tadouser", "text", title: "Tado User", description: "Your Tado User") 30 | } 31 | 32 | metadata { 33 | definition (name: "Tado Heating User Presence", namespace: "fuzzysb", author: "Stuart Buchanan") { 34 | capability "Presence Sensor" 35 | capability "Sensor" 36 | capability "Polling" 37 | capability "Refresh" 38 | 39 | command "arrived" 40 | command "departed" 41 | 42 | } 43 | 44 | // simulator metadata 45 | simulator { 46 | status "present": "presence: present" 47 | status "not present": "presence: not present" 48 | } 49 | 50 | tiles { 51 | standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { 52 | state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#53a7c0") 53 | state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ffffff") 54 | } 55 | standardTile("refresh", "device.refresh", width: 2, height: 1, decoration: "flat") { 56 | state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" 57 | } 58 | main "presence" 59 | details (["presence","refresh"]) 60 | } 61 | } 62 | 63 | // Parse incoming device messages to generate events 64 | private parseResponse(resp) { 65 | def result 66 | log.debug("Executing parseResponse: "+resp.data) 67 | log.debug("Output status: "+resp.status) 68 | if(resp.status == 200) { 69 | log.debug("Executing parseResponse.successTrue") 70 | def evtJson = new groovy.json.JsonOutput().toJson(resp.data) 71 | def json = new JsonSlurper().parseText(evtJson) 72 | def appuserarray = new groovy.json.JsonOutput().toJson(json.appUsers) 73 | def list = new JsonSlurper().parseText(appuserarray) 74 | list.each { 75 | if ((it.nickname).capitalize() == (settings.tadouser).capitalize()) { 76 | log.debug("Found Tado User : " + it.nickname) 77 | if (it.geoTrackingEnabled == true) { 78 | log.debug("Users GeoTracking is Enabled") 79 | if (it.geolocationIsStale == false){ 80 | log.debug("Users Current Relative Position is : " + it.relativePosition ) 81 | if (it.relativePosition == 0) { 82 | result = arrived() 83 | }else{ 84 | result = departed() 85 | } 86 | }else{ 87 | log.debug("Geolocation is Stale Skipping") 88 | } 89 | }else{ 90 | log.debug("Users GeoTracking Not Enabled") 91 | } 92 | } 93 | } 94 | }else if(resp.status == 201){ 95 | log.debug("Something was created/updated") 96 | } 97 | return result 98 | } 99 | 100 | def installed() { 101 | log.debug "Executing 'installed'" 102 | statusCommand() 103 | } 104 | 105 | def updated() { 106 | log.debug "Executing 'updated'" 107 | statusCommand() 108 | } 109 | 110 | def poll() { 111 | log.debug "Executing 'poll'" 112 | refresh() 113 | } 114 | 115 | def refresh() { 116 | log.debug "Executing 'refresh'" 117 | statusCommand() 118 | } 119 | 120 | 121 | private sendCommand(method, args = []) { 122 | def methods = [ 123 | 'status': [ 124 | uri: "https://my.tado.com", 125 | path: "/mobile/1.6/getAppUsersRelativePositions", 126 | requestContentType: "application/json", 127 | query: [username:settings.username, password:settings.password, noCache:args[0], webapp:1] 128 | ], 129 | ] 130 | 131 | def request = methods.getAt(method) 132 | 133 | log.debug "Http Params ("+request+")" 134 | 135 | try{ 136 | log.debug "Executing 'sendCommand'" 137 | 138 | if (method == "status"){ 139 | httpGet(request) { resp -> 140 | parseResponse(resp) 141 | } 142 | }else{ 143 | httpGet(request) 144 | } 145 | } catch(Exception e){ 146 | log.debug("___exception: " + e) 147 | } 148 | } 149 | 150 | 151 | 152 | // Commands 153 | def statusCommand(){ 154 | log.debug "Executing 'sendCommand.statusCommand'" 155 | Random rand = new Random() 156 | int min = 10000 157 | int max = 99999 158 | int randomNum = rand.nextInt((max - min) + 1) + min 159 | sendCommand("status",[randomNum]) 160 | } 161 | 162 | def arrived() { 163 | log.trace "Executing 'arrived'" 164 | def result = sendEvent(name: "presence", value: "present") 165 | return result 166 | } 167 | 168 | 169 | def departed() { 170 | log.trace "Executing 'departed'" 171 | def result = sendEvent(name: "presence", value: "not present") 172 | return result 173 | } 174 | 175 | -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/auto_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/auto_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/cool_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/cool_arrow_down.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/cool_arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/cool_arrow_up.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/cool_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/cool_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/dry_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/dry_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/emergencyHeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/emergencyHeat.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/endManual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/endManual.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_auto_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_auto_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_high_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_high_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_low_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_low_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_med_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_med_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_mode_icononly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_mode_icononly.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_off_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_off_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_on_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/fan_on_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/heat_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/heat_arrow_down.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/heat_arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/heat_arrow_up.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/heat_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/heat_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_auto.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_cool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_cool.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_dry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_dry.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_heat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_heat.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_off.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/hvac_on.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/off_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado-Cooling-AC.src/Images/off_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/emergencyHeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/emergencyHeat.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/endManual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/endManual.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/heat_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/heat_arrow_down.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/heat_arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/heat_arrow_up.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/heat_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/heat_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/hvac_auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/hvac_auto.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/hvac_heat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/hvac_heat.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/hvac_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/hvac_off.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/hvac_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/hvac_on.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Heating.src/Images/off_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Heating.src/Images/off_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/emergencyHeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/emergencyHeat.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/endManual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/endManual.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/heat_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/heat_arrow_down.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/heat_arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/heat_arrow_up.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/heat_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/heat_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/hvac_heat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/hvac_heat.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/hvac_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/hvac_off.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/hvac_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/hvac_on.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/off_mode_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/off_mode_icon.png -------------------------------------------------------------------------------- /DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/tap_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/DeviceTypes/fuzzysb/tado.Hot.Water.src/Images/tap_icon.png -------------------------------------------------------------------------------- /SmartApps/fuzzysb/Fibaro Dual Relay Secondary Switch Binder/FibaroDualRelaySecondarySwitchBinder.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * FGS-221 Fibaro Dual Relay Secondary Binder 3 | * 4 | * Author Stuart Buchanan based on work of Matt Frank 5 | * 6 | * Date Created: 12/22/2015 7 | * Last Modified: 12/22/2015 8 | * 9 | */ 10 | 11 | definition( 12 | name: "Fibaro Dual Relay Secondary Switch Binder", 13 | namespace: "fuzzysb", 14 | author: "Stuart Buchanan", 15 | description: "You can use this to bind the secondary relay to a virtual switch", 16 | category: "My Apps", 17 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 18 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 19 | 20 | preferences { 21 | section("Which Dual Relay"){ 22 | input "dualrelay", "capability.switch", multiple: true, icon: "st.switches.switch.on" 23 | } 24 | section("Select a virtual switch to bind to secondary relay"){ 25 | input "switch2", "capability.switch" 26 | } 27 | } 28 | 29 | def installed(settings) { 30 | log.debug "Installed with settings: ${settings}" 31 | subscribe(switch2, "switch", switchHandler) 32 | subscribe(dualrelay, "switch2", dualrelayHandler) 33 | } 34 | 35 | def updated(settings) { 36 | unsubscribe() 37 | installed() 38 | } 39 | 40 | def switchHandler(evt) { 41 | switch (evt.value) { 42 | case 'on': 43 | dualrelay.on2() 44 | break 45 | case 'off': 46 | dualrelay.off2() 47 | break 48 | } 49 | } 50 | 51 | def dualrelayHandler(evt) { 52 | switch (evt.value) { 53 | case 'on': 54 | switch2.on() 55 | break 56 | case 'off': 57 | switch2.off() 58 | break 59 | } 60 | } -------------------------------------------------------------------------------- /SmartApps/fuzzysb/HomeSeer Link/ContactSensortoHS3.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Send ST Contact Sensor to HomeSeer HS3 SecuritySensor 14 | * 15 | * Author: Stuart Buchanan 16 | /* 17 | * Date: 2016-09-015 v1.0 Initial Release 18 | */ 19 | definition( 20 | name: "ST to HomeSeer HS3 Contact Sensor", 21 | namespace: "fuzzysb", 22 | author: "Stuart Buchanan", 23 | description: "Triggers Virtual Contact Sensor on HomeSeer HS3 when ST Contact Sensor status changes", 24 | category: "My Apps", 25 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 26 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 27 | 28 | 29 | preferences { 30 | section(title: "Select Devices") { 31 | input "ContactSensor", "capability.contactSensor", title: "Select ST Contact Sensor", required: true, multiple:false 32 | } 33 | section("Select HomeSeer Device"){ 34 | input "HomeSeer", "capability.actuator", title: "HomeSeer", required: true, multiple: false, defaultValue: false, submitOnChange: true 35 | } 36 | section(title: "Enter Corresponding HomeSeer Device ID") { 37 | input name: "HS3Id", type: "text", title: "HomeSeer Device ID", required: true 38 | } 39 | } 40 | 41 | def installed() { 42 | log.debug "Installed with settings: ${settings}" 43 | init() 44 | } 45 | 46 | def updated() { 47 | log.debug "Updated with settings: ${settings}" 48 | unsubscribe() 49 | init() 50 | } 51 | 52 | def init() { 53 | subscribe(ContactSensor, "contact", handleEvent) 54 | } 55 | 56 | 57 | def lanResponseHandler(evt) { 58 | log.debug "In response handler" 59 | log.debug "I got back ${evt.description}" 60 | } 61 | 62 | def handleEvent(evt) { 63 | log.debug "Event Value is: ${evt.value}" 64 | def result 65 | if (evt.value == "open") 66 | { 67 | sendOpenCommand() 68 | } 69 | else 70 | { 71 | sendClosedCommand() 72 | } 73 | 74 | } 75 | 76 | 77 | def sendOpenCommand() { 78 | log.debug "Sending open Web Request to HS3 Id ${settings.HS3Id}" 79 | HomeSeer.api("open", settings.HS3Id) 80 | } 81 | 82 | def sendClosedCommand() { 83 | log.debug "Sending closed Web Request to HS3 Id ${settings.HS3Id}" 84 | HomeSeer.api("closed", settings.HS3Id) 85 | } -------------------------------------------------------------------------------- /SmartApps/fuzzysb/HomeSeer Link/MotionSensortoHS3.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Send ST Motion Sensor to HomeSeer HS3 Motion Sensor 14 | * 15 | * Author: Stuart Buchanan 16 | /* 17 | * Date: 2016-09-015 v1.0 Initial Release 18 | */ 19 | definition( 20 | name: "ST to HomeSeer HS3 Motion Sensor", 21 | namespace: "fuzzysb", 22 | author: "Stuart Buchanan", 23 | description: "Triggers Virtual Motion Sensor on HomeSeer HS3 when ST Motion Sensor status changes", 24 | category: "My Apps", 25 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 26 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 27 | 28 | 29 | preferences { 30 | section(title: "Select Devices") { 31 | input "MotionSensor", "capability.motionSensor", title: "Select ST Motion Sensor", required: true, multiple:false 32 | } 33 | section("Select HomeSeer Device"){ 34 | input "HomeSeer", "capability.actuator", title: "HomeSeer", required: true, multiple: false, defaultValue: false, submitOnChange: true 35 | } 36 | section(title: "Enter Corresponding HomeSeer Device ID") { 37 | input name: "HS3Id", type: "text", title: "HomeSeer Device ID", required: true 38 | } 39 | } 40 | 41 | def installed() { 42 | log.debug "Installed with settings: ${settings}" 43 | init() 44 | } 45 | 46 | def updated() { 47 | log.debug "Updated with settings: ${settings}" 48 | unsubscribe() 49 | init() 50 | } 51 | 52 | def init() { 53 | subscribe(MotionSensor, "motion", handleEvent) 54 | } 55 | 56 | 57 | def lanResponseHandler(evt) { 58 | log.debug "In response handler" 59 | log.debug "I got back ${evt.description}" 60 | } 61 | 62 | def handleEvent(evt) { 63 | log.debug "Event Value is: ${evt.value}" 64 | def result 65 | if (evt.value == "active") 66 | { 67 | sendActiveCommand() 68 | } 69 | else 70 | { 71 | sendInactiveCommand() 72 | } 73 | 74 | } 75 | 76 | 77 | def sendActiveCommand() { 78 | log.debug "Sending Active Web Request to HS3 Id ${settings.HS3Id}" 79 | HomeSeer.api("active", settings.HS3Id) 80 | } 81 | 82 | def sendInactiveCommand() { 83 | log.debug "Sending Inactive Web Request to HS3 Id ${settings.HS3Id}" 84 | HomeSeer.api("inactive", settings.HS3Id) 85 | } -------------------------------------------------------------------------------- /SmartApps/fuzzysb/HomeSeer Link/PresenceSensortoHS3.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Send ST Presence to Vera Plus Presence 14 | * 15 | * Author: Stuart Buchanan 16 | /* 17 | * Date: 2016-09-015 v1.0 Initial Release 18 | */ 19 | definition( 20 | name: "ST to HomeSeer HS3 Presence", 21 | namespace: "fuzzysb", 22 | author: "Stuart Buchanan", 23 | description: "Triggers Virtual Switch on HS3 when ST Presence status changes", 24 | category: "My Apps", 25 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 26 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 27 | 28 | 29 | preferences { 30 | section(title: "Select Devices") { 31 | input "PresenceSensor", "capability.presenceSensor", title: "Select ST Presence Sensor", required: true, multiple:false 32 | } 33 | section("Select HomeSeer Device"){ 34 | input "HomeSeer", "capability.actuator", title: "HomeSeer", required: true, multiple: false, defaultValue: false, submitOnChange: true 35 | } 36 | section(title: "Enter Corresponding HS3 Device ID") { 37 | input name: "HS3Id", type: "text", title: "HS3 Device ID", required: true 38 | } 39 | } 40 | 41 | def installed() { 42 | log.debug "Installed with settings: ${settings}" 43 | init() 44 | } 45 | 46 | def updated() { 47 | log.debug "Updated with settings: ${settings}" 48 | unsubscribe() 49 | init() 50 | } 51 | 52 | def init() { 53 | subscribe(PresenceSensor, "presence", handleEvent) 54 | } 55 | 56 | 57 | def lanResponseHandler(evt) { 58 | log.debug "In response handler" 59 | log.debug "I got back ${evt.description}" 60 | } 61 | 62 | def handleEvent(evt) { 63 | log.debug "Event Value is: ${evt.value}" 64 | def result 65 | if (evt.value == "present") 66 | { 67 | sendHomeCommand() 68 | } 69 | else 70 | { 71 | sendAwayCommand() 72 | } 73 | 74 | } 75 | 76 | 77 | def sendHomeCommand() { 78 | log.debug "Sending home Web Request to Vera Id ${settings.HS3Id}" 79 | HomeSeer.api("home", settings.HS3Id) 80 | } 81 | 82 | def sendAwayCommand() { 83 | log.debug "Sending Away Web Request to Vera Id ${settings.HS3Id}" 84 | HomeSeer.api("away", settings.HS3Id) 85 | } -------------------------------------------------------------------------------- /SmartApps/fuzzysb/HomeSeer Link/SwitchSyncWithHS3.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * 14 | * Author: Stuart Buchanan, Based on original work by midyear66 with thanks 15 | * Date: 2016-02-03 v1.0 Initial Release 16 | */ 17 | definition( 18 | name: "Air Conditioning Power", 19 | namespace: "fuzzysb", 20 | author: "Stuart Buchanan", 21 | description: "Triggers Switch when HTTP GET Request is received", 22 | category: "My Apps", 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 | 26 | 27 | preferences { 28 | section(title: "Select Devices") { 29 | input "virtualSwitch", "capability.Switch", title: "Select Switch", required: true, multiple:false 30 | } 31 | section("Select HS3 Device"){ 32 | input "HomeSeer", "capability.actuator", title: "HomeSeer", required: true, multiple: false, defaultValue: false, submitOnChange: true 33 | } 34 | section(title: "Enter Corresponding HomeSeer Device ID") { 35 | input name: "HomeSeerId", type: "text", title: "HomeSeer Device ID", required: true 36 | } 37 | 38 | } 39 | 40 | def installed() { 41 | createAccessToken() 42 | getToken() 43 | DEBUG("Installed with Application Id: $app.id") 44 | DEBUG("Installed with token: $state.accessToken") 45 | DEBUG("Installed with settings: $virtualSwitch.name") 46 | init() 47 | } 48 | def updated() { 49 | DEBUG("Updated with Application Id: $app.id") 50 | DEBUG("Updated with token: $state.accessToken") 51 | unsubscribe() 52 | init() 53 | } 54 | 55 | def init() { 56 | state.lastOnChanged = 0 57 | state.lastOffChanged = 0 58 | subscribe(virtualSwitch, "switch", handleEvent) 59 | } 60 | 61 | mappings { 62 | path("/ST/on") { 63 | action: [ 64 | GET: "switchon" 65 | ] 66 | } 67 | path("/ST/off") { 68 | action: [ 69 | GET:"switchoff" 70 | ] 71 | } 72 | } 73 | 74 | 75 | // Callback functions 76 | def getSwitch() { 77 | // This returns the current state of the switch in JSON 78 | return virtualSwitch.currentState("switch") 79 | } 80 | 81 | def switchon() { 82 | if(now() - (3 * 1000) > state.lastOnChanged){ 83 | DEBUG("on") 84 | state.lastOnChanged = now() 85 | virtualSwitch.on(); 86 | } 87 | else{ 88 | DEBUG("Not Turning On as State Changed too Recently.") 89 | } 90 | 91 | } 92 | 93 | def switchoff() { 94 | if(now() - (3 * 1000) > state.lastOffChanged){ 95 | DEBUG("off") 96 | state.lastOffChanged = now() 97 | virtualSwitch.off(); 98 | } 99 | else{ 100 | DEBUG("Not Turning Off as State Changed too Recently.") 101 | } 102 | } 103 | 104 | def sendOnCommand() { 105 | log.debug "Sending home Web Request to HomeSeer Id ${settings.HomeSeerId}" 106 | HomeSeer.api("on", settings.HomeSeerId) 107 | } 108 | 109 | def sendOffCommand() { 110 | log.debug "Sending Off Web Request to HomeSeer Id ${settings.HomeSeerId}" 111 | HomeSeer.api("off", settings.HomeSeerId) 112 | } 113 | 114 | def lanResponseHandler(evt) { 115 | log.debug "In response handler" 116 | log.debug "I got back ${evt.description}" 117 | } 118 | 119 | def handleEvent(evt) { 120 | 121 | log.debug "Event Value is: ${evt.value}" 122 | def result 123 | if (evt.value == "on") 124 | { 125 | sendOnCommand() 126 | } 127 | else 128 | { 129 | sendOffCommand() 130 | } 131 | 132 | 133 | } 134 | 135 | def getToken(){ 136 | if (!state.accessToken) { 137 | try { 138 | getAccessToken() 139 | DEBUG("Creating new Access Token: $state.accessToken") 140 | } catch (ex) { 141 | DEBUG("Did you forget to enable OAuth in SmartApp IDE settings") 142 | DEBUG(ex) 143 | } 144 | } 145 | } 146 | 147 | private def DEBUG(message){ 148 | log.debug message 149 | } -------------------------------------------------------------------------------- /SmartApps/fuzzysb/HomeSeer Link/SwitchtoHS3.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Send ST Switch to HomeSeer HS3 Switch 14 | * 15 | * Author: Stuart Buchanan 16 | /* 17 | * Date: 2016-09-015 v1.0 Initial Release 18 | */ 19 | definition( 20 | name: "ST to HomeSeer HS3 Switch", 21 | namespace: "fuzzysb", 22 | author: "Stuart Buchanan", 23 | description: "Triggers Virtual Switch on HS3 when ST Switch status changes", 24 | category: "My Apps", 25 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 26 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 27 | 28 | 29 | preferences { 30 | section(title: "Select Devices") { 31 | input "Switch", "capability.switch", title: "Select ST Switch", required: true, multiple:false 32 | } 33 | section("Select Homeseer HS3 Device"){ 34 | input "HomeSeer", "capability.actuator", title: "HomeSeer", required: true, multiple: false, defaultValue: false, submitOnChange: true 35 | } 36 | section(title: "Enter Corresponding HomeSeer Device ID") { 37 | input name: "HomeSeerId", type: "text", title: "HomeSeer Device ID", required: true 38 | } 39 | } 40 | 41 | def installed() { 42 | log.debug "Installed with settings: ${settings}" 43 | init() 44 | } 45 | 46 | def updated() { 47 | log.debug "Updated with settings: ${settings}" 48 | unsubscribe() 49 | init() 50 | } 51 | 52 | def init() { 53 | subscribe(Switch, "switch", handleEvent) 54 | } 55 | 56 | 57 | def lanResponseHandler(evt) { 58 | log.debug "In response handler" 59 | log.debug "I got back ${evt.description}" 60 | } 61 | 62 | def handleEvent(evt) { 63 | log.debug "Event Value is: ${evt.value}" 64 | def result 65 | if (evt.value == "on") 66 | { 67 | sendOnCommand() 68 | } 69 | else 70 | { 71 | sendOffCommand() 72 | } 73 | 74 | } 75 | 76 | 77 | def sendOnCommand() { 78 | log.debug "Sending home Web Request to HomeSeer Id ${settings.HomeSeerId}" 79 | HomeSeer.api("on", settings.HomeSeerId) 80 | } 81 | 82 | def sendOffCommand() { 83 | log.debug "Sending Away Web Request to HomeSeer Id ${settings.HomeSeerId}" 84 | HomeSeer.api("off", settings.HomeSeerId) 85 | } -------------------------------------------------------------------------------- /SmartApps/fuzzysb/Netatmo Sound Sensor/NetatmoSoundSensor.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Netatmo Sound Sensor Custom Device Attribute Detection 3 | * 4 | * Author: stuart@broadbandtap.co.uk 5 | * Date: 29/01/2016 6 | * 7 | * Allows me to utilise a devices custom attribute to set a Virtual Switch for example i use the Sound Sensor on my Netatmo indoor module to turn on a virtual Switch if the noise in db is above a threshold and switch it off when below. 8 | * I am using this as an extra detection to see if my main living room is occupied and i will use the switch status along with motion sensors to assess if its correct to turn off lights off. 9 | * this is used as i get false positives just from motion as it seems i sit very still sometimes when watching TV. this smartapp is ideal to incorporate your custom attribute into Rule machine for example. 10 | */ 11 | 12 | definition( 13 | name: "Netatmo Sound Level Detection", 14 | namespace: "fuzzysb ", 15 | author: "Stuart Buchanan", 16 | description: "switch on a virtual switch if the value of a custom attribute is above or equals the specified threshold", 17 | category: "Convenience", 18 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 19 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png" 20 | ) 21 | 22 | preferences { 23 | section("This Netatmo Base Station") { 24 | input name: "netatmo", type: "device.netatmoBasestation", title: "This Netatmo Base Station" 25 | } 26 | 27 | section("Threshold") { 28 | input name: "threshold", type: "number", title: "Threshold in db", required: false 29 | } 30 | 31 | section("This Virtual Switch") { 32 | input name: "virtualSwitch", type: "capability.switch", title: "switch this virtual switch" 33 | } 34 | 35 | } 36 | 37 | def installed() { 38 | log.debug "Installed with settings: ${settings}" 39 | init() 40 | } 41 | 42 | def updated() { 43 | log.debug "Updated with settings: ${settings}" 44 | unsubscribe() 45 | init() 46 | } 47 | 48 | def init() { 49 | subscribe(netatmo, "noise", handleEvent) 50 | } 51 | 52 | def handleEvent(evt) { 53 | def dbthold = threshold ?: 50 54 | log.debug "entering handle event method, evaluating against a threshold of ${dbthold}" 55 | if(netatmo.currentValue("noise").toInteger() >= dbthold.toInteger()){ 56 | log.debug "evaluated as true" 57 | log.debug("${netatmo.label ?: netatmo.name} is greater than " + dbthold + " Turning on Virtual Switch") 58 | virtualSwitch.on() 59 | log.debug "end of handleEvent" 60 | } 61 | else { 62 | log.debug("${netatmo.label ?: netatmo.name} is below the threshold turning off the Virtual switch.") 63 | virtualSwitch.off() 64 | } 65 | } -------------------------------------------------------------------------------- /SmartApps/fuzzysb/Rule Machine/RuleMachine@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/SmartApps/fuzzysb/Rule Machine/RuleMachine@2x.png -------------------------------------------------------------------------------- /SmartApps/fuzzysb/Rule Machine/rulemachine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzzysb/SmartThings/e293839bafde1588732dd2ebe81b8d6287540afd/SmartApps/fuzzysb/Rule Machine/rulemachine.png -------------------------------------------------------------------------------- /SmartApps/fuzzysb/Sonos Door Knocker/SonosDoorKnocker.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Door Knocker 3 | * 4 | * Author: stuart@broadbandtap.co.uk 5 | * Date: 05/12/15 6 | * 7 | * Let me know when someone knocks on the door via sonos custom message, but ignore 8 | * when someone is opening the door. 9 | */ 10 | 11 | definition( 12 | name: "Sonos Door Knocker", 13 | namespace: "fuzzysb ", 14 | author: "Stuart Buchanan", 15 | description: "Alert if door is knocked, but not opened.", 16 | category: "Convenience", 17 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 18 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png" 19 | ) 20 | 21 | preferences { 22 | section("When Someone Knocks?") { 23 | input name: "knockSensor", type: "capability.accelerationSensor", title: "Where?" 24 | } 25 | 26 | section("But not when they open this door?") { 27 | input name: "openSensor", type: "capability.contactSensor", title: "Where?" 28 | } 29 | 30 | section("Knock Delay (defaults to 5s)?") { 31 | input name: "knockDelay", type: "number", title: "How Long?", required: false 32 | } 33 | 34 | section("Notifications") { 35 | input "sendPushMessage", "enum", title: "Send a push notification?", metadata: [values: ["Yes", "No"]], required: false 36 | input "phone", "phone", title: "Send a Text Message?", required: false 37 | } 38 | 39 | section("Minimum time between messages (optional, defaults to every message)") { 40 | input "frequency", "number", title: "Minutes", required: false 41 | } 42 | 43 | section("Speaker to Play Sound") { 44 | input "sonos", "capability.musicPlayer", title: "Sonos Device", required: true 45 | input "volume", "number", title: "Temporarily change volume", description: "0-100%", required: false 46 | } 47 | 48 | section("What message to you want to say?") { 49 | input "textHere", "text", title: "Type in the message" 50 | } 51 | 52 | } 53 | 54 | def installed() { 55 | log.debug "Installed with settings: ${settings}" 56 | init() 57 | } 58 | 59 | def updated() { 60 | log.debug "Updated with settings: ${settings}" 61 | unsubscribe() 62 | init() 63 | } 64 | 65 | def init() { 66 | state.lastClosed = 0 67 | subscribe(knockSensor, "acceleration.active", handleEvent) 68 | subscribe(openSensor, "contact.closed", doorClosed) 69 | } 70 | 71 | def doorClosed(evt) { 72 | state.lastClosed = now() 73 | } 74 | 75 | 76 | def handleEvent(evt) { 77 | def delay = knockDelay ?: 5 78 | runIn(delay, "doorKnock") 79 | } 80 | 81 | def doorKnock() { 82 | def frequency = frequency ?: 1 83 | log.debug "entering doorknock method, frequency is $frequency " 84 | if((openSensor.latestValue("contact") == "closed") && (now() - (60 * 1000) > state.lastClosed)) { 85 | log.debug("${knockSensor.label ?: knockSensor.name} detected a knock.") 86 | /* send("${knockSensor.label ?: knockSensor.name} detected a knock.") */ 87 | sonos.setLevel(volume) 88 | sonos.playText(textHere) 89 | log.debug "end of doorknock method" 90 | } 91 | else { 92 | log.debug("${knockSensor.label ?: knockSensor.name} knocked, but looks like it was just someone opening the door.") 93 | } 94 | } 95 | 96 | 97 | 98 | private send(msg) { 99 | if(sendPushMessage != "No") { 100 | log.debug("Sending push message") 101 | sendPush(msg) 102 | } 103 | 104 | if(phone) { 105 | log.debug("Sending text message") 106 | sendSms(phone, msg) 107 | } 108 | 109 | log.debug(msg) 110 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/aeon-dsd37-repeater.src/aeon-dsd37-repeater.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | */ 14 | metadata { 15 | definition (name: "Aeon DSD37 Repeater", namespace: "fuzzysb", author: "Stuart Buchanan") { 16 | capability "Actuator" 17 | capability "Refresh" 18 | capability "Configuration" 19 | 20 | /* Capability notes 21 | 0x20 COMMAND_CLASS_BASIC 1 22 | 0x70 COMMAND_CLASS_CONFIGURATION 1 23 | 0x72 COMMAND_CLASS_MANUFACTURER_SPECIFIC 2 24 | 0x85 COMMAND_CLASS_ASSOCIATION 2 25 | 0x73 COMMAND_CLASS_POWERLEVEL 1 26 | 0x86 COMMAND_CLASS_VERSION 2 27 | */ 28 | 29 | fingerprint deviceId: "0x0F01", inClusters: "0x20 0x70 0x72 0x85 0x73 0x86" 30 | } 31 | 32 | // simulator metadata 33 | simulator { 34 | } 35 | 36 | // tile definitions 37 | tiles { 38 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { 39 | state "on", label: '${name}', icon: "st.switches.switch.on", backgroundColor: "#79b821" 40 | state "off", label: '${name}', icon: "st.switches.switch.off", backgroundColor: "#ffffff" 41 | } 42 | standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { 43 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" 44 | } 45 | 46 | main "switch" 47 | details(["switch","refresh"]) 48 | } 49 | } 50 | def parse(String description) { 51 | def result = null 52 | def cmd = zwave.parse(description, [0x20: 1, 0x70: 1, 0x72: 2, 0x85: 2, 0x73: 1, 0x86: 1]) 53 | if (cmd) { 54 | result = createEvent(zwaveEvent(cmd)) 55 | } 56 | if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) { 57 | result = [result, response(zwave.basicV1.basicGet())] 58 | log.debug "Was hailed: requesting state update" 59 | } else { 60 | log.debug "Parse returned ${result?.descriptionText}" 61 | } 62 | return result 63 | } 64 | 65 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) 66 | { 67 | createEvent([ 68 | name: "switch", value: cmd.value ? "on" : "off", type: "physical" 69 | ]) 70 | } 71 | 72 | 73 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 74 | log.debug "No handler for $cmd" 75 | // Handles all Z-Wave commands we aren't interested in 76 | createEvent(descriptionText: cmd.toString(), isStateChange: false) 77 | } 78 | 79 | 80 | def poll() { 81 | zwave.basicV1.basicGet().format() 82 | } 83 | 84 | def refresh() { 85 | zwave.basicV1.basicGet().format() 86 | } 87 | 88 | def configure() { 89 | log.debug "configure()" 90 | } 91 | 92 | def updated() { 93 | 94 | refresh() 95 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/aeon-water-sensor.src/aeon-water-sensor.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Aeon Water Sensor (DSB45-ZWEU/DSB45-ZWUS) 14 | * 15 | * Author: Stuart Buchanan, Based on original work by Tosa with thanks 16 | * Date: 2016-03-07 17 | */ 18 | 19 | metadata { 20 | definition (name: "Aeon Water Sensor", namespace: "fuzzysb", author: "Stuart Buchanan") { 21 | capability "Water Sensor" 22 | capability "Battery" 23 | capability "Configuration" 24 | 25 | fingerprint deviceId: "0x2001", inClusters: "0x30,0x80,0x84,0x71,0x70,0x85,0x86,0x72" 26 | } 27 | 28 | simulator { 29 | status "dry": "command: 2001, payload: 00" 30 | status "wet": "command: 2001, payload: FF" 31 | } 32 | 33 | 34 | tiles { 35 | standardTile("water", "device.water", width: 2, height: 2) { 36 | state "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff" 37 | state "wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0" 38 | } 39 | valueTile("battery", "device.battery", inactiveLabel: false, canChangeBackground: true) { 40 | state "battery", label:'${currentValue}% Battery', unit:"", 41 | backgroundColors:[ 42 | [value: 19, color: "#BC2323"], 43 | [value: 20, color: "#D04E00"], 44 | [value: 30, color: "#D04E00"], 45 | [value: 40, color: "#DAC400"], 46 | [value: 41, color: "#79b821"] 47 | ] 48 | } 49 | standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") { 50 | state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure" 51 | } 52 | main "water" 53 | details(["water", "battery", "configure"]) 54 | } 55 | } 56 | 57 | def parse(String description) { 58 | def result = null 59 | if (description.startsWith("Err 106")) { 60 | if (state.sec) { 61 | log.debug description 62 | } else { 63 | log.debug "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again." 64 | result = createEvent( 65 | descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.", 66 | eventType: "ALERT", 67 | name: "secureInclusion", 68 | value: "failed", 69 | isStateChange: true, 70 | ) 71 | } 72 | } else if (description != "updated") { 73 | def cmd = zwave.parse(description, [0x20: 1, 0x25: 1, 0x30: 1, 0x31: 5, 0x80: 1, 0x84: 1, 0x71: 3, 0x9C: 1]) 74 | if (cmd) { 75 | result = zwaveEvent(cmd) 76 | } 77 | } 78 | log.debug "parsed '${description}' to ${result}" 79 | return result 80 | } 81 | 82 | def updated() { 83 | def cmds = [] 84 | if (!state.MSR) { 85 | cmds = [ 86 | zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(), 87 | "delay 1200", 88 | zwave.wakeUpV1.wakeUpNoMoreInformation().format() 89 | ] 90 | } else if (!state.lastbat) { 91 | cmds = [] 92 | } else { 93 | cmds = [zwave.wakeUpV1.wakeUpNoMoreInformation().format()] 94 | } 95 | response(cmds) 96 | } 97 | 98 | def configure() { 99 | delayBetween([ 100 | zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(), 101 | batteryGetCommand() 102 | ], 6000) 103 | } 104 | 105 | def sensorValueEvent(value) { 106 | if (value) { 107 | log.debug "Sensor it Wet" 108 | createEvent(name: "water", value: "wet", descriptionText: "$device.displayName is wet") 109 | } else { 110 | log.debug "Sensor it Dry" 111 | createEvent(name: "water", value: "dry", descriptionText: "$device.displayName is dry") 112 | } 113 | } 114 | 115 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) 116 | { 117 | sensorValueEvent(cmd.value) 118 | } 119 | 120 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) 121 | { 122 | sensorValueEvent(cmd.value) 123 | } 124 | 125 | def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) 126 | { 127 | sensorValueEvent(cmd.value) 128 | } 129 | 130 | def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd) 131 | { 132 | sensorValueEvent(cmd.sensorValue) 133 | } 134 | 135 | def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) 136 | { 137 | sensorValueEvent(cmd.sensorState) 138 | } 139 | 140 | def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) 141 | { 142 | def result = [] 143 | if (cmd.notificationType == 0x06 && cmd.event == 0x16) { 144 | result << sensorValueEvent(1) 145 | } else if (cmd.notificationType == 0x06 && cmd.event == 0x17) { 146 | result << sensorValueEvent(0) 147 | } else if (cmd.notificationType == 0x07) { 148 | if (cmd.v1AlarmType == 0x07) { // special case for nonstandard messages from Monoprice door/window sensors 149 | result << sensorValueEvent(cmd.v1AlarmLevel) 150 | } else if (cmd.event == 0x01 || cmd.event == 0x02) { 151 | result << sensorValueEvent(1) 152 | } else if (cmd.event == 0x03) { 153 | result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true) 154 | result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId)) 155 | if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet()) 156 | } else if (cmd.event == 0x05 || cmd.event == 0x06) { 157 | result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true) 158 | } else if (cmd.event == 0x07) { 159 | if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet()) 160 | result << createEvent(name: "motion", value: "active", descriptionText:"$device.displayName detected motion") 161 | } 162 | } else if (cmd.notificationType) { 163 | def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}" 164 | result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, displayed: false) 165 | } else { 166 | def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive" 167 | result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, displayed: false) 168 | } 169 | result 170 | } 171 | 172 | def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) 173 | { 174 | def event = createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false) 175 | def cmds = [] 176 | if (!state.MSR) { 177 | cmds << zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId).format() 178 | cmds << zwave.manufacturerSpecificV2.manufacturerSpecificGet().format() 179 | cmds << "delay 1200" 180 | } 181 | if (!state.lastbat || now() - state.lastbat > 53*60*60*1000) { 182 | cmds << batteryGetCommand() 183 | } else { 184 | cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format() 185 | } 186 | [event, response(cmds)] 187 | } 188 | 189 | def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { 190 | def map = [ name: "battery", unit: "%" ] 191 | if (cmd.batteryLevel == 0xFF) { 192 | map.value = 1 193 | map.descriptionText = "${device.displayName} has a low battery" 194 | map.isStateChange = true 195 | } else { 196 | map.value = cmd.batteryLevel 197 | } 198 | state.lastbat = now() 199 | [createEvent(map), response(zwave.wakeUpV1.wakeUpNoMoreInformation())] 200 | } 201 | 202 | def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { 203 | def result = [] 204 | 205 | def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) 206 | log.debug "msr: $msr" 207 | updateDataValue("MSR", msr) 208 | 209 | result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false) 210 | 211 | if (msr == "011A-0601-0901") { // Enerwave motion doesn't always get the associationSet that the hub sends on join 212 | result << response(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)) 213 | } else if (!device.currentState("battery")) { 214 | if (msr == "0086-0102-0059") { 215 | result << response(zwave.securityV1.securityMessageEncapsulation().encapsulate(zwave.batteryV1.batteryGet()).format()) 216 | } else { 217 | result << response(batteryGetCommand()) 218 | } 219 | } 220 | 221 | result 222 | } 223 | 224 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { 225 | def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1]) 226 | // log.debug "encapsulated: $encapsulatedCommand" 227 | if (encapsulatedCommand) { 228 | state.sec = 1 229 | zwaveEvent(encapsulatedCommand) 230 | } 231 | } 232 | 233 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 234 | createEvent(descriptionText: "$device.displayName: $cmd", displayed: false) 235 | } 236 | 237 | def batteryGetCommand() { 238 | def cmd = zwave.batteryV1.batteryGet() 239 | if (state.sec) { 240 | cmd = zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd) 241 | } 242 | cmd.format() 243 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/axis-gate-opener.src/axis-gate-opener.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the License); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * httpwww.apache.orglicensesLICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Axis Gate Opener 14 | * 15 | * Author Stuart Buchanan 16 | * Date 2016-04-06 v1.1 Added User Authentication, added logging of response parsed http headers. 17 | * Date 2016-04-05 v1.0 Initial Release 18 | **/ 19 | preferences { 20 | input("ServerIP", "text", title: "ServerIP", description: "Enter the IP Address of the Axis Server") 21 | input("Port", "text", title: "Port", description: "Enter the TCP Port of the Axis Server") 22 | input("username", "text", title: "username", description: "Enter the username for the Axis Server") 23 | input("password", "password", title: "password", description: "Enter the Password for the Axis Server") 24 | } 25 | 26 | metadata { 27 | definition (name: "Axis Gate Opener", namespace: "fuzzysb", author: "Stuart Buchanan") { 28 | capability "Switch" 29 | capability "Actuator" 30 | capability "Refresh" 31 | capability "Polling" 32 | command "open" 33 | command "close" 34 | command "getopen" 35 | command "getclose" 36 | } 37 | 38 | 39 | // simulator metadata 40 | simulator { 41 | // status messages 42 | 43 | // reply messages 44 | } 45 | 46 | tiles(scale: 2) { 47 | multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4){ 48 | tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { 49 | attributeState "on", label:'Open', action:"switch.off", icon:"https://raw.githubusercontent.com/fuzzysb/SmartThings/master/DeviceTypes/fuzzysb/Axis%20Gate%20Opener/GateOpen.png", backgroundColor:"#ffa81e" 50 | attributeState "off", label:'Closed', action:"switch.on", icon:"https://raw.githubusercontent.com/fuzzysb/SmartThings/master/DeviceTypes/fuzzysb/Axis%20Gate%20Opener/GateClosed.png", backgroundColor:"#79b821" 51 | } 52 | } 53 | standardTile("open", "device.switch", width: 2, height: 2, decoration: "flat") { 54 | state "default", label:"Open", action:"switch.on", icon:"https://raw.githubusercontent.com/fuzzysb/SmartThings/master/DeviceTypes/fuzzysb/Axis%20Gate%20Opener/GateOpen.png", backgroundColor:"#ffa81e" 55 | } 56 | standardTile("close", "device.switch", width: 2, height: 2, decoration: "flat") { 57 | state "default", label:"Close", action:"switch.off", icon:"https://raw.githubusercontent.com/fuzzysb/SmartThings/master/DeviceTypes/fuzzysb/Axis%20Gate%20Opener/GateClosed.png", backgroundColor:"#79b821" 58 | } 59 | standardTile("refresh", "device.switch", width: 2, height: 2, decoration: "flat") { 60 | state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" 61 | } 62 | main "switch" 63 | details(["switch","open","close","refresh"]) 64 | } 65 | } 66 | 67 | def updated() { 68 | log.info "Axis Gate Opener Updated" 69 | state.dni = createDNI(settings.ServerIP, settings.Port) 70 | state.hostAddress = "${settings.ServerIP}, ${settings.Port}" 71 | refresh() 72 | } 73 | 74 | def installed() { 75 | log.info "Axis Gate Opener Updated" 76 | state.dni = createDNI(settings.ServerIP, settings.Port) 77 | state.hostAddress = "${settings.ServerIP}, ${settings.Port}" 78 | refresh() 79 | } 80 | 81 | def parse(String message) { 82 | def msg = stringToMap(message) 83 | 84 | if (!msg.containsKey("headers")) { 85 | log.error "No HTTP headers found in '${message}'" 86 | return null 87 | } 88 | 89 | // parse HTTP response headers 90 | def headers = new String(msg.headers.decodeBase64()) 91 | def parsedHeaders = parseHttpHeaders(headers) 92 | log.debug "parsedHeaders: ${parsedHeaders}" 93 | if (parsedHeaders.status != 200) { 94 | log.error "Return Code: ${parsedHeaders.status} Server error: ${parsedHeaders.reason}" 95 | return null 96 | } 97 | 98 | // parse HTTP response body 99 | if (!msg.body) { 100 | log.error "No HTTP body found in '${message}'" 101 | return null 102 | } else { 103 | def body = new String(msg.body.decodeBase64()) 104 | parseHttpResponse(body) 105 | log.debug "body: ${body}" 106 | } 107 | } 108 | 109 | private parseHttpHeaders(String headers) { 110 | def lines = headers.readLines() 111 | def status = lines.remove(0).split() 112 | 113 | def result = [ 114 | protocol: status[0], 115 | status: status[1].toInteger(), 116 | reason: status[2] 117 | ] 118 | 119 | return result 120 | } 121 | 122 | private def parseHttpResponse(String data) { 123 | log.debug("parseHttpResponse(${data})") 124 | def splitresponse = data.split("=") 125 | def port = splitresponse[0] 126 | def status = splitresponse[1] 127 | if (status == "active"){ 128 | createEvent(name: "switch", value: "open", descriptionText: "$device.displayName is open", isStateChange: "true") 129 | } else if (status == "inactive"){ 130 | createEvent(name: "switch", value: "close", descriptionText: "$device.displayName is closed", isStateChange: "true") 131 | } 132 | return status 133 | } 134 | 135 | def poll() { 136 | log.debug "Executing poll Command" 137 | refresh() 138 | } 139 | 140 | def refresh() { 141 | log.debug "Executing Refresh Command" 142 | getstatus() 143 | } 144 | 145 | def on() { 146 | log.debug "Executing Open Command" 147 | open() 148 | } 149 | 150 | def off() { 151 | log.debug "Executing Close Command" 152 | close() 153 | } 154 | 155 | def open(){ 156 | log.debug "Checking DNI" 157 | updateDNI() 158 | try { 159 | log.debug "Executing Open" 160 | def openResult = getopen() 161 | log.debug "${openResult}" 162 | sendHubCommand(openResult) 163 | } 164 | catch (Exception e) 165 | { 166 | log.debug "Hit Exception $e" 167 | } 168 | createEvent(name: "switch", value: "open", descriptionText: "$device.displayName is open", isStateChange: "true") 169 | } 170 | 171 | def close(){ 172 | log.debug "Checking DNI" 173 | updateDNI() 174 | try { 175 | log.debug "Executing Close" 176 | def closeResult = getclose() 177 | log.debug "${closeResult}" 178 | sendHubCommand(closeResult) 179 | } 180 | catch (Exception e) 181 | { 182 | log.debug "Hit Exception $e" 183 | } 184 | createEvent(name: "switch", value: "close", descriptionText: "$device.displayName is closed", isStateChange: "true") 185 | } 186 | 187 | def getopen(){ 188 | def userpassascii = "${settings.username}:${settings.password}" 189 | def userpass = "Basic " + userpassascii.encodeAsBase64().toString() 190 | def headers = [:] 191 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 192 | headers.put("Authorization","${userpass}") 193 | def result = new physicalgraph.device.HubAction( 194 | method: "GET", 195 | path: "/axis-cgi/io/port.cgi?action=2:/", 196 | headers: headers 197 | ) 198 | return result 199 | } 200 | 201 | def getclose(){ 202 | def userpassascii = "${settings.username}:${settings.password}" 203 | def userpass = "Basic " + userpassascii.encodeAsBase64().toString() 204 | def headers = [:] 205 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 206 | headers.put("Authorization","${userpass}") 207 | def result = new physicalgraph.device.HubAction( 208 | method: "GET", 209 | path: "/axis-cgi/io/port.cgi?action=2:\\", 210 | headers: headers 211 | ) 212 | return result 213 | } 214 | 215 | def getstatus(){ 216 | def userpassascii = "${settings.username}:${settings.password}" 217 | def userpass = "Basic " + userpassascii.encodeAsBase64().toString() 218 | def headers = [:] 219 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 220 | headers.put("Authorization","${userpass}") 221 | def result = new physicalgraph.device.HubAction( 222 | method: "GET", 223 | path: "/axis-cgi/io/port.cgi?checkactive=2", 224 | headers: headers 225 | ) 226 | return result 227 | } 228 | 229 | private String createDNI(ipaddr, port) { 230 | log.debug("createDNI(${ipaddr}, ${port})") 231 | 232 | def hexIp = ipaddr.tokenize('.').collect { 233 | String.format('%02X', it.toInteger()) 234 | }.join() 235 | 236 | def hexPort = String.format('%04X', port.toInteger()) 237 | log.debug "Hex IP:Port: ${hexIp}:${hexPort}" 238 | return "${hexIp}:${hexPort}" 239 | } 240 | 241 | private def delayHubAction(ms) { 242 | log.debug("delayHubAction(${ms})") 243 | return new physicalgraph.device.HubAction("delay ${ms}") 244 | } 245 | 246 | private updateDNI() { 247 | if (device.deviceNetworkId != state.dni) { 248 | device.deviceNetworkId = state.dni 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /devicetypes/fuzzysb/dlink-dch-z510.src/dlink-dch-z510.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Dlink DCH-Z510 14 | * 15 | * Author: fuzzysb 16 | * Date: 2015-01-05 17 | */ 18 | 19 | 20 | 21 | preferences { 22 | input "defaultSound", "enum", title: "Default Sound to use for the Siren?", options: ["Emergency","FireAlarm","Ambulance","PoliceCar","DoorChime"], required: false, defaultValue: "Emergency" 23 | } 24 | 25 | metadata { 26 | definition (name: "Dlink DCH-Z510", namespace: "fuzzysb", author: "Stuart Buchanan") { 27 | capability "Actuator" 28 | capability "Alarm" 29 | capability "Switch" 30 | capability "Configuration" 31 | 32 | command "Emergency" 33 | command "FireAlarm" 34 | command "Ambulance" 35 | command "PoliceCar" 36 | command "DoorChime" 37 | 38 | fingerprint deviceId: "0x1005", inClusters: "0x5E,0x71,0x20,0x25,0x85,0x70,0x72,0x86,0x30,0x59,0x73,0x5A,0x98,0x7A" 39 | } 40 | 41 | simulator { 42 | 43 | } 44 | 45 | tiles(scale: 2) { 46 | multiAttributeTile(name:"alarm", type: "generic", width: 6, height: 4){ 47 | tileAttribute ("device.alarm", key: "PRIMARY_CONTROL") { 48 | attributeState "off", label:'off', action:'alarm.siren', icon:"st.alarm.alarm.alarm", backgroundColor:"#ffffff" 49 | attributeState "both", label:'alarm!', action:'alarm.off', icon:"st.alarm.alarm.alarm", backgroundColor:"#e86d13" 50 | } 51 | } 52 | standardTile("Emergency", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 53 | state "default", label:'Emergency', action:"Emergency", icon:"st.Weather.weather1" 54 | } 55 | standardTile("FireAlarm", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 56 | state "default", label:'Fire Alarm', action:"FireAlarm", icon:"st.Outdoor.outdoor10" 57 | } 58 | standardTile("Ambulance", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 59 | state "default", label:'Ambulance', action:"Ambulance", icon:"st.Transportation.transportation2" 60 | } 61 | standardTile("PoliceCar", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 62 | state "default", label:'Police Car', action:"PoliceCar", icon:"st.Transportation.transportation8" 63 | } 64 | standardTile("DoorChime", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 65 | state "default", label:'Door Chime', action:"DoorChime", icon:"st.Home.home30" 66 | } 67 | standardTile("off", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 68 | state "default", label:'', action:"alarm.off", icon:"st.secondary.off" 69 | } 70 | main "alarm" 71 | details(["alarm", "Emergency", "FireAlarm", "Ambulance", "PoliceCar", "DoorChime", "off"]) 72 | } 73 | } 74 | 75 | def parse(String description) 76 | { 77 | def result = null 78 | if (description.startsWith("Err 106")) { 79 | state.sec = 0 80 | result = createEvent( name: "secureInclusion", value: "failed", isStateChange: true, 81 | descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.") 82 | } else if (description != "updated") { 83 | def cmd = zwave.parse(description, [0x20: 1, 0x25: 1, 0x26: 1, 0x70: 1, 0x80: 1]) 84 | if (cmd) { 85 | result = zwaveEvent(cmd) 86 | log.debug "Parse returned ${result?.inspect()}" 87 | } else { 88 | log.debug("Couldn't zwave.parse '$description'") 89 | null 90 | } 91 | 92 | } 93 | log.debug "Parsed '${description}' to ${result.inspect()}" 94 | return result 95 | } 96 | 97 | 98 | 99 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { 100 | def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x25: 3, 0x26: 3, 0x70: 1, 0x80: 1]) 101 | state.sec = 1 102 | log.debug "encapsulated: ${encapsulatedCommand}" 103 | if (encapsulatedCommand) { 104 | zwaveEvent(encapsulatedCommand) 105 | } else { 106 | log.warn "Unable to extract encapsulated cmd from $cmd" 107 | createEvent(descriptionText: cmd.toString()) 108 | } 109 | } 110 | 111 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) { 112 | response(configure()) 113 | } 114 | 115 | def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { 116 | log.debug "---CONFIGURATION REPORT V2--- ${device.displayName} parameter ${cmd.parameterNumber} with a byte size of ${cmd.size} is set to ${cmd.configurationValue}" 117 | } 118 | 119 | def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) 120 | { 121 | [name: "switch", value: cmd.value ? "on" : "off", type: "digital", displayed: true, isStateChange: true] 122 | } 123 | 124 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { 125 | log.debug "rx $cmd" 126 | [ 127 | createEvent([name: "switch", value: cmd.value ? "on" : "off", displayed: false]), 128 | createEvent([name: "alarm", value: cmd.value ? "both" : "off"]) 129 | ] 130 | } 131 | 132 | def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) { 133 | createEvent(name:"Alarm", cmd.sensorValue ? "on" : "off") 134 | } 135 | 136 | def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { 137 | if (cmd.notificationType == 6 && cmd.event == 22) { 138 | log.debug "Playing Door Chime" 139 | } else if (cmd.notificationType == 10 && cmd.event == 1) { 140 | log.debug "Playing Police Car" 141 | } else if (cmd.notificationType == 10 && cmd.event == 3) { 142 | log.debug "Playing Ambulance" 143 | } else if (cmd.notificationType == 10 && cmd.event == 2) { 144 | log.debug "Playing Fire Alarm" 145 | } else if (cmd.notificationType == 7 && cmd.event == 1) { 146 | log.debug "Playing Emergency" 147 | } 148 | } 149 | 150 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 151 | log.debug "Unhandled: $cmd" 152 | createEvent(descriptionText: cmd.toString(), isStateChange: false) 153 | } 154 | 155 | def strobe() { 156 | on() 157 | } 158 | 159 | def siren() { 160 | on() 161 | } 162 | 163 | def both() { 164 | on() 165 | } 166 | 167 | def on() { 168 | log.debug("Sounding Siren") 169 | switch ( settings.defaultSound ) { 170 | case "Emergency": 171 | Emergency() 172 | break 173 | 174 | case "FireAlarm": 175 | FireAlarm() 176 | break 177 | 178 | case "Ambulance": 179 | Ambulance() 180 | break 181 | 182 | case "PoliceCar": 183 | PoliceCar() 184 | break 185 | 186 | case "DoorChime": 187 | DoorChime() 188 | break 189 | 190 | default: 191 | Emergency() 192 | break 193 | } 194 | } 195 | 196 | def off() { 197 | log.debug "sending off" 198 | [ 199 | secure(zwave.basicV1.basicSet(value: 0x00)), 200 | secure(zwave.basicV1.basicGet()) 201 | ] 202 | } 203 | 204 | def Emergency() { 205 | log.debug "Sounding Siren With Emergency" 206 | [ 207 | secure(zwave.notificationV3.notificationReport(event: 0x01, notificationType: 0x07)), 208 | secure(zwave.basicV1.basicGet()) 209 | ] 210 | } 211 | 212 | def FireAlarm() { 213 | log.debug "Sounding Siren With Fire Alarm" 214 | [ 215 | secure(zwave.notificationV3.notificationReport(event: 0x02, notificationType: 0x0A)), 216 | secure(zwave.basicV1.basicGet()) 217 | ] 218 | } 219 | 220 | def Ambulance() { 221 | log.debug "Sounding Siren With Ambulance" 222 | [ 223 | secure(zwave.notificationV3.notificationReport(event: 0x03, notificationType: 0x0A)), 224 | secure(zwave.basicV1.basicGet()) 225 | ] 226 | } 227 | 228 | def PoliceCar() { 229 | log.debug "Sounding Siren With Police Car" 230 | [ 231 | secure(zwave.notificationV3.notificationReport(event: 0x01, notificationType: 0x0A)), 232 | secure(zwave.basicV1.basicGet()) 233 | ] 234 | } 235 | 236 | def DoorChime() { 237 | log.debug "Sounding Siren With Door Chime" 238 | [ 239 | secure(zwave.notificationV3.notificationReport(event: 0x16, notificationType: 0x06)), 240 | secure(zwave.basicV1.basicGet()) 241 | ] 242 | } 243 | 244 | def configure() { 245 | log.debug "Resetting Siren Parameters to SmartThings Compatible Defaults" 246 | def cmds = [] 247 | 248 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1) 249 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 29, size: 1) 250 | cmds << zwave.configurationV1.configurationSet(configurationValue: [6], parameterNumber: 31, size: 1) 251 | 252 | delayBetween(cmds, 500) 253 | } 254 | 255 | private secure(physicalgraph.zwave.Command cmd) { 256 | if (state.sec) { 257 | log.debug "Sending Secure Command $cmd" 258 | zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() 259 | } else { 260 | log.debug "Sending Insecure Command $cmd" 261 | cmd.format() 262 | } 263 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/everspring-st812-flood-detector.src/everspring-st812-flood-detector.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Everspring ST812 Flood Detector 14 | * 15 | * Author: Stuart Buchanan, Based on original work by Tosa with thanks 16 | * Date: 2016-01-31 17 | */ 18 | 19 | preferences { 20 | // manufacturer default wake up is every hour; optionally increase for better battery life 21 | input "userWakeUpInterval", "number", title: "Wake Up Interval (seconds)", description: "Default 3600 sec (60 sec - 194 days)", defaultValue: '3600', required: false, displayDuringSetup: true 22 | } 23 | 24 | 25 | metadata { 26 | definition (name: "Everspring ST812 Flood Detector", namespace: "fuzzysb", author: "Stuart Buchanan") { 27 | capability "Water Sensor" 28 | capability "Battery" 29 | capability "Configuration" 30 | 31 | fingerprint deviceId: "0xA102", inClusters: "0x86,0x72,0x85,0x84,0x80,0x70,0x9C,0x20,0x71" 32 | } 33 | 34 | simulator { 35 | status "dry": "command: 9C02, payload: 00 05 00 00 00" 36 | status "wet": "command: 9C02, payload: 00 05 FF 00 00" 37 | status "wakeup": "command: 8407, payload: " 38 | status "low batt alarm": "command: 7105, payload: 01 FF" 39 | status "battery <20%": new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: 0xFF).incomingMessage() 40 | for (int i = 20; i <= 100; i += 10) { 41 | status "battery ${i}%": new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: i).incomingMessage() 42 | } 43 | } 44 | 45 | tiles { 46 | standardTile("water", "device.water", width: 2, height: 2) { 47 | state "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff" 48 | state "wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0" 49 | } 50 | valueTile("battery", "device.battery", inactiveLabel: false, canChangeBackground: true) { 51 | state "battery", label:'${currentValue}% Battery', unit:"", 52 | backgroundColors:[ 53 | [value: 19, color: "#BC2323"], 54 | [value: 20, color: "#D04E00"], 55 | [value: 30, color: "#D04E00"], 56 | [value: 40, color: "#DAC400"], 57 | [value: 41, color: "#79b821"] 58 | ] 59 | } 60 | standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") { 61 | state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure" 62 | } 63 | main "water" 64 | details(["water", "battery", "configure"]) 65 | } 66 | } 67 | 68 | def parse(String description) { 69 | log.debug "parse: $description" 70 | 71 | def parsedZwEvent = zwave.parse(description, [0x9C: 1, 0x71: 1, 0x84: 2, 0x30: 1, 0x70: 1, 0x71: 1]) 72 | def result = [] 73 | 74 | if (parsedZwEvent) { 75 | result = zwaveEvent(parsedZwEvent) 76 | log.debug "Parsed ${parsedZwEvent} to ${result.inspect()}" 77 | } else { 78 | log.debug "Non-parsed event: ${description}" 79 | } 80 | 81 | return result 82 | } 83 | 84 | def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { 85 | 86 | // Appears that the Everspring/Utilitech water sensor responds to batteryGet, but not wakeUpNoMoreInformation(?) 87 | 88 | def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)] 89 | result << response(zwave.batteryV1.batteryGet()) 90 | 91 | // If user has changed userWakeUpInterval, send the new interval to the device 92 | def userWake = getUserWakeUp(userWakeUpInterval) 93 | if (state.wakeUpInterval != userWake) { 94 | state.wakeUpInterval = userWake 95 | result << response("delay 200") 96 | result << response(zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId)) 97 | result << response("delay 200") 98 | result << response(zwave.wakeUpV2.wakeUpIntervalGet()) 99 | } 100 | 101 | return result 102 | } 103 | 104 | def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) { 105 | 106 | def map = [:] 107 | if (cmd.sensorType == 0x05) { 108 | map.name = "water" 109 | map.value = cmd.sensorState ? "wet" : "dry" 110 | map.descriptionText = "${device.displayName} is ${map.value}" 111 | } 112 | 113 | createEvent(map) 114 | } 115 | 116 | def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd) { 117 | 118 | def map = [:] 119 | map.name = "water" 120 | map.value = cmd.sensorValue ? "wet" : "dry" 121 | map.descriptionText = "${device.displayName} is ${map.value}" 122 | 123 | createEvent(map) 124 | } 125 | 126 | def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) { 127 | 128 | def map = [:] 129 | def result 130 | if (cmd.alarmType == 1) { 131 | if (cmd.alarmLevel == 0xFF) { 132 | map.descriptionText = "${device.displayName} has a low battery alarm" 133 | map.displayed = true 134 | } else if (cmd.alarmLevel == 1) { 135 | map.descriptionText = "${device.displayName} battery alarm level 1" // device sometimes returns alarmLevel 1, 136 | map.displayed = false // but this appears to be an undocumented alarmLevel(?) 137 | } 138 | result = [createEvent(map)] 139 | result << response(zwave.batteryV1.batteryGet()) // try to update battery status, but device doesn't seem to always respond 140 | } else if (cmd.alarmType == 2 && cmd.alarmLevel == 1) { 141 | map.descriptionText = "${device.displayName} powered up" 142 | map.displayed = false 143 | result = [createEvent(map)] 144 | } else { 145 | log.debug cmd 146 | } 147 | 148 | return result 149 | } 150 | 151 | def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { 152 | 153 | def map = [ name: "battery", unit: "%" ] 154 | if (cmd.batteryLevel == 0xFF) { // Special value for low battery alert 155 | map.value = 10 // will display (and alarm in mobile app) as 10% battery remaining, even though it's really 1%-19% remaining 156 | map.descriptionText = "${device.displayName} reports a low battery" 157 | map.isStateChange = true 158 | map.displayed = true 159 | } else { 160 | map.value = cmd.batteryLevel 161 | map.displayed = false 162 | } 163 | 164 | createEvent(map) 165 | } 166 | 167 | def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalCapabilitiesReport cmd) { 168 | 169 | def map = [ name: "defaultWakeUpInterval", unit: "seconds" ] 170 | map.value = cmd.defaultWakeUpIntervalSeconds 171 | map.displayed = false 172 | 173 | state.defaultWakeUpInterval = cmd.defaultWakeUpIntervalSeconds 174 | createEvent(map) 175 | } 176 | 177 | def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) { 178 | 179 | def map = [ name: "reportedWakeUpInterval", unit: "seconds" ] 180 | map.value = cmd.seconds 181 | map.displayed = false 182 | 183 | createEvent(map) 184 | } 185 | 186 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 187 | 188 | log.debug "COMMAND CLASS: ${cmd}" 189 | createEvent(descriptionText: "Command not handled: ${cmd}") 190 | } 191 | 192 | def configure() { 193 | 194 | state.wakeUpInterval = getUserWakeUp(userWakeUpInterval) 195 | 196 | delayBetween([ 197 | zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId).format(), 198 | zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId).format(), 199 | zwave.wakeUpV2.wakeUpIntervalGet().format(), 200 | zwave.batteryV1.batteryGet().format() 201 | ], 200) 202 | 203 | } 204 | 205 | private getUserWakeUp(userWake) { 206 | 207 | if (!userWake) { userWake = '3600' } // set default 1 hr if no user preference 208 | 209 | // make sure user setting is within valid range for device 210 | if (userWake.toInteger() < 60) { userWake = '60' } // 60 sec min 211 | if (userWake.toInteger() > 16761600) { userWake = '16761600' } // 194 days max 212 | 213 | /* 214 | * Ideally, would like to reassign userWakeUpInterval to min or max when needed, 215 | * so it more obviously reflects in 'preferences' in the IDE and mobile app 216 | * for the device. Referencing the preference on the RH side is ok, but 217 | * haven't figured out how to reassign it on the LH side? 218 | * 219 | */ 220 | //userWakeUpInterval = userWake 221 | 222 | return userWake.toInteger() 223 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/fibaro-dual-relay.src/fibaro-dual-relay.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Fibaro Dual Relay 3 | * 4 | * Author Stuart Buchanan based on work of Matt Frank, chrisb for AEON Power Strip 5 | * 6 | * Date Created: 12/22/2015 7 | * Last Modified: 12/22/2015 8 | * 9 | */ 10 | // for the UI 11 | metadata { 12 | definition (name: "Fibaro Dual Relay", namespace: "fuzzysb", author: "Stuart Buchanan") { 13 | capability "Switch" 14 | capability "Polling" 15 | capability "Configuration" 16 | capability "Refresh" 17 | capability "Zw Multichannel" 18 | 19 | attribute "switch", "string" 20 | attribute "switch2", "string" 21 | 22 | command "on" 23 | command "off" 24 | command "on2" 25 | command "off2" 26 | 27 | fingerprint deviceId: "0x1001", inClusters:"0x60, 0x25, 0x27, 0x85, 0x72, 0x86" 28 | } 29 | 30 | simulator { 31 | status "on": "command: 2003, payload: FF" 32 | status "off": "command: 2003, payload: 00" 33 | reply "8E010101,delay 800,6007": "command: 6008, payload: 4004" 34 | reply "8505": "command: 8506, payload: 02" 35 | reply "59034002": "command: 5904, payload: 8102003101000000" 36 | reply "6007": "command: 6008, payload: 0002" 37 | reply "600901": "command: 600A, payload: 10002532" 38 | reply "600902": "command: 600A, payload: 210031" 39 | } 40 | 41 | tiles { 42 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { 43 | state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821" 44 | state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" 45 | } 46 | standardTile("switch2", "device.switch2",canChangeIcon: true) { 47 | state "on", label: "switch2", action: "off2", icon: "st.switches.switch.on", backgroundColor: "#79b821" 48 | state "off", label: "switch2", action: "on2", icon: "st.switches.switch.off", backgroundColor: "#ffffff" 49 | } 50 | standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { 51 | state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" 52 | } 53 | 54 | standardTile("configure", "device.switch", inactiveLabel: false, decoration: "flat") { 55 | state "default", label:"", action:"configure", icon:"st.secondary.configure" 56 | } 57 | 58 | main("switch") 59 | details(["switch","switch2","refresh","configure"]) 60 | } 61 | } 62 | 63 | def parse(String description) { 64 | def result = null 65 | def cmd = zwave.parse(description, [0x60:3, 0x25:1, 0x70:1, 0x32:1, 0x72:1]) 66 | if (cmd) { 67 | result = zwaveEvent(cmd) 68 | log.debug "Parsed ${description} to ${cmd} to ${result.inspect()}" 69 | } else { 70 | log.debug "Non-parsed event: ${description}" 71 | } 72 | return result 73 | } 74 | 75 | 76 | //Reports 77 | 78 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 79 | // This will capture any commands not handled by other instances of zwaveEvent 80 | // and is recommended for development so you can see every command the device sends 81 | return createEvent(descriptionText: "${device.displayName}: ${cmd}") 82 | } 83 | 84 | def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { 85 | log.debug "SwitchBinaryReport $cmd" 86 | def result = [] 87 | result << response( 88 | delayBetween([ 89 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(), 90 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 91 | ]) 92 | ) 93 | result 94 | } 95 | 96 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { 97 | log.debug "MultiChannelCmdEncap $cmd" 98 | def name = "switch$cmd.sourceEndPoint" 99 | if (cmd.sourceEndPoint == 1) name = "switch" 100 | def map = [ name: name ] 101 | if (cmd.commandClass == 37) { 102 | if (cmd.parameter == [0]) { 103 | map.value = "off" 104 | } 105 | if (cmd.parameter == [255]) { 106 | map.value = "on" 107 | } 108 | return createEvent(map) 109 | } 110 | } 111 | 112 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) { 113 | log.debug "MultiChannelCapabilityReport $cmd" 114 | } 115 | 116 | def refresh() { 117 | log.debug "refresh" 118 | delayBetween([ 119 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(), 120 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 121 | ]) 122 | } 123 | 124 | 125 | def poll() { 126 | log.debug "poll" 127 | delayBetween([ 128 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(), 129 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 130 | ]) 131 | } 132 | 133 | def configure() { 134 | log.debug "configure" 135 | delayBetween([ 136 | zwave.configurationV1.configurationSet(parameterNumber:4, configurationValue: [0]).format() // Report reguarly 137 | ]) 138 | } 139 | 140 | def on() { 141 | delayBetween([ 142 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[255]).format(), 143 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format() 144 | ]) 145 | } 146 | 147 | def off() { 148 | delayBetween([ 149 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[0]).format(), 150 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format() 151 | ]) 152 | } 153 | 154 | def on2() { 155 | delayBetween([ 156 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:1, parameter:[255]).format(), 157 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 158 | ]) 159 | } 160 | 161 | def off2() { 162 | delayBetween([ 163 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:1, parameter:[0]).format(), 164 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format() 165 | ]) 166 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/fibaro-universal-sensor-dual-contact.src/fibaro-universal-sensor-dual-contact.groovy: -------------------------------------------------------------------------------- 1 | metadata { 2 | definition (name: "Fibaro Universal Sensor - Dual Contact", namespace: "fuzzysb", author: "Stuart Buchanan") { 3 | capability "Contact Sensor" 4 | capability "Temperature Measurement" 5 | capability "Configuration" 6 | capability "Sensor" 7 | 8 | command "updateCurrentParams" 9 | command "listCurrentParams" 10 | 11 | /* Capability notes 12 | 0x30 Sensor Binary V1 V2 13 | 0x60 Multi Channel V3 14 | 0x85 Association V1 V2 15 | 0x8E Multi Instance Association V1 16 | 0x72 Manufacturer Specific V1 V2 17 | 0x70 Configuration V1 V2 18 | 0x86 Version V1 19 | 0x7A Firmware Update Md V1 V2 20 | 0xEF Mark V1 21 | 0x2B Scene Activation V1 22 | */ 23 | 24 | fingerprint deviceId: "0x2001", inClusters: "0x20 0x30 0x31 0x60 0x85 0x8E 0x72 0x70 0x86 0x7A 0xEF 0x2B" 25 | } 26 | 27 | simulator { 28 | } 29 | 30 | tiles(scale: 2) { 31 | multiAttributeTile(name: "thermostat", type:"thermostat", width:6, height:4) { 32 | tileAttribute("device.temperature", key:"PRIMARY_CONTROL", canChangeIcon: true, canChangeBackground: true){ 33 | attributeState "default", label:'${currentValue}°', unit:"C", backgroundColor:"#fab907", icon:"st.Weather.weather2" 34 | } 35 | tileAttribute ("device.contact", key: "SECONDARY_CONTROL") { 36 | attributeState "open", label: '${name}' 37 | attributeState "closed", label: '${name}' 38 | } 39 | } 40 | standardTile("contact", "device.contact", width: 2, height: 2) { 41 | state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e" 42 | state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821" 43 | } 44 | standardTile("contact2", "device.contact2", width: 2, height: 2) { 45 | state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e" 46 | state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821" 47 | } 48 | standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 2, height: 2 ) { 49 | state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure" 50 | } 51 | 52 | main(["thermostat"]) 53 | details(["thermostat", "contact" ,"contact2", "configure"]) 54 | } 55 | } 56 | 57 | def parse(String description) { 58 | def result = null 59 | def cmd = zwave.parse(description, [ 0x20: 1, 0x30: 1, 0x31: 2, 0x60: 3, 0x85: 2, 0x8E: 2, 0x72: 1, 0x70: 1, 0x86: 1, 0x7A: 1, 0xEF: 1, 0x2B: 1 ]) 60 | if (cmd) { 61 | result = zwaveEvent(cmd) 62 | } 63 | log.debug "parsed '$description' to result: ${result}" 64 | result 65 | } 66 | 67 | def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv1.ManufacturerSpecificReport cmd) { 68 | log.debug("ManufacturerSpecificReport ${cmd.inspect()}") 69 | } 70 | 71 | def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { 72 | log.debug("ConfigurationReport ${cmd.inspect()}") 73 | } 74 | 75 | def configure() { 76 | log.debug "Updating Configuration of ${device.displayName}" 77 | def cmds = [] 78 | cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]).format() 79 | cmds << zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format() 80 | cmds << zwave.associationV2.associationRemove(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format() 81 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format() 82 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format() 83 | cmds << zwave.configurationV1.configurationSet(configurationValue: [20], parameterNumber: 10, size: 1).format() 84 | cmds << zwave.configurationV1.configurationSet(configurationValue: [60], parameterNumber: 11, size: 1).format() 85 | cmds << zwave.configurationV1.configurationSet(configurationValue: [8], parameterNumber: 12, size: 1).format() 86 | 87 | delayBetween(cmds, 500) 88 | } 89 | 90 | def createEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd, Map item1) { 91 | log.debug "manufacturerId: ${cmd.manufacturerId}" 92 | log.debug "manufacturerName: ${cmd.manufacturerName}" 93 | log.debug "productId: ${cmd.productId}" 94 | log.debug "productTypeId: ${cmd.productTypeId}" 95 | } 96 | 97 | def createEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd, Map item1) { 98 | updateDataValue("applicationVersion", "${cmd.applicationVersion}") 99 | log.debug "applicationVersion: ${cmd.applicationVersion}" 100 | log.debug "applicationSubVersion: ${cmd.applicationSubVersion}" 101 | log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}" 102 | log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}" 103 | log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}" 104 | } 105 | 106 | 107 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { 108 | /* log.debug "BasicSet V1 ${cmd.inspect()}" 109 | if (cmd.value) { 110 | createEvent(name: "contact", value: "open", descriptionText: "$device.displayName is open") 111 | } else { 112 | createEvent(name: "contact", value: "closed", descriptionText: "$device.displayName is closed") 113 | } 114 | */ 115 | } 116 | 117 | def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd) { 118 | log.debug "Sensor Binary report" 119 | def map = [:] 120 | map.value = cmd.sensorValue ? "active" : "inactive" 121 | map.name = "motion" 122 | if (map.value == "active") { 123 | map.descriptionText = "$device.displayName detected motion" 124 | } 125 | else { 126 | map.descriptionText = "$device.displayName motion has stopped" 127 | } 128 | map 129 | } 130 | 131 | def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) 132 | { 133 | log.debug "Sensor MultiLevel Report" 134 | def map = [:] 135 | switch (cmd.sensorType) { 136 | case 1: 137 | // temperature 138 | def cmdScale = cmd.scale == 1 ? "F" : "C" 139 | map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision).toDouble().round(1) 140 | log.debug("tempvalue: " + map.value) 141 | map.unit = getTemperatureScale() 142 | log.debug("tempunit: " + map.unit) 143 | map.name = "temperature" 144 | break; 145 | } 146 | createEvent(map) 147 | } 148 | 149 | def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv1.SensorMultilevelReport cmd) { 150 | log.debug "SensorMultilevelReport $cmd" 151 | } 152 | 153 | 154 | def createEvent(physicalgraph.zwave.commands.firmwareupdatemdv1.FirmwareMdReport cmd, Map item1) { 155 | log.debug "checksum: ${cmd.checksum}" 156 | log.debug "firmwareId: ${cmd.firmwareId}" 157 | log.debug "manufacturerId: ${cmd.manufacturerId}" 158 | } 159 | 160 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { 161 | log.debug "ZWaveEvent V3 ${cmd.inspect()}" 162 | def result 163 | if (cmd.commandClass == 32) { 164 | if (cmd.parameter == [0]) { 165 | if (cmd.sourceEndPoint == 1) { 166 | result = createEvent(name: "contact", value: "closed", descriptionText: "$device.displayName is closed") 167 | log.debug "Contact is closed" 168 | } 169 | else 170 | if (cmd.sourceEndPoint == 2) { 171 | result = createEvent(name: "contact2", value: "closed", descriptionText: "$device.displayName is closed") 172 | log.debug "Contact2 is closed" 173 | } 174 | } 175 | if (cmd.parameter == [255]) { 176 | if (cmd.sourceEndPoint == 1) { 177 | result = createEvent(name: "contact", value: "open", descriptionText: "$device.displayName is open") 178 | log.debug "Contact is open" 179 | } 180 | else 181 | if (cmd.sourceEndPoint == 2) { 182 | result = createEvent(name: "contact2", value: "open", descriptionText: "$device.displayName is open") 183 | log.debug "Contact2 is open" 184 | } 185 | } 186 | return result 187 | }else{ 188 | def encapsulatedCommand = cmd.encapsulatedCommand([ 0x20: 1, 0x30: 1, 0x31: 2, 0x60: 3, 0x85: 2, 0x8E: 2, 0x72: 1, 0x70: 1, 0x86: 1, 0x7A: 1, 0xEF: 1, 0x2B: 1]) // can specify command class versions here like in zwave.parse 189 | log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}") 190 | if (encapsulatedCommand) { 191 | return zwaveEvent(encapsulatedCommand) 192 | } 193 | } 194 | } 195 | 196 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 197 | log.debug "Catchall reached for cmd: ${cmd.toString()}}" 198 | [:] 199 | } 200 | 201 | 202 | def listCurrentParams() { 203 | log.debug "Listing of current parameter settings of ${device.displayName}" 204 | def cmds = [] 205 | cmds << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier:2).format() 206 | cmds << zwave.associationV2.associationGet(groupingIdentifier: 3).format() 207 | cmds << zwave.associationV1.associationGet(groupingIdentifier: 1).format() 208 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 1).format() 209 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 2).format() 210 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 3).format() 211 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 4).format() 212 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 5).format() 213 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 6).format() 214 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format() 215 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 8).format() 216 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 9).format() 217 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format() 218 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 11).format() 219 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format() 220 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 13).format() 221 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 14).format() 222 | 223 | delayBetween(cmds, 500) 224 | } 225 | 226 | def updateCurrentParams() { 227 | log.debug "Updating current parameter settings of ${device.displayName}" 228 | def cmds = [] 229 | cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]).format() 230 | cmds << zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format() 231 | cmds << zwave.associationV2.associationRemove(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format() 232 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0, 0], parameterNumber: 1, size: 2).format() 233 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0, 0], parameterNumber: 2, size: 2).format() 234 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format() 235 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format() 236 | cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format() 237 | cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 6, size: 1).format() 238 | cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 7, size: 1).format() 239 | cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 8, size: 1).format() 240 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 9, size: 1).format() 241 | cmds << zwave.configurationV1.configurationSet(configurationValue: [20], parameterNumber: 10, size: 1).format() 242 | cmds << zwave.configurationV1.configurationSet(configurationValue: [60], parameterNumber: 11, size: 1).format() 243 | cmds << zwave.configurationV1.configurationSet(configurationValue: [8], parameterNumber: 12, size: 1).format() 244 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 13, size: 1).format() 245 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 14, size: 1).format() 246 | 247 | delayBetween(cmds, 500) 248 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/tkb-home-tz65s.src/tkb-home-tz65s.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 SmartThings 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | */ 14 | metadata { 15 | definition (name: "TKB Home TZ65S", namespace: "fuzzysb", author: "Stuart Buchanan") { 16 | capability "Switch Level" 17 | capability "Actuator" 18 | capability "Indicator" 19 | capability "Switch" 20 | capability "Polling" 21 | capability "Refresh" 22 | capability "Sensor" 23 | 24 | command "resetParams2StDefaults" 25 | command "listCurrentParams" 26 | 27 | /* Capability notes 28 | 0x26 COMMAND_CLASS_SWITCH_MULTILEVEL 2 29 | 0x85 COMMAND_CLASS_ASSOCIATION 2 30 | 0x70 COMMAND_CLASS_CONFIGURATION 1 31 | 0x27 COMMAND_CLASS_SWITCH_ALL 1 32 | 0x86 COMMAND_CLASS_VERSION 2 33 | 0x72 COMMAND_CLASS_MANUFACTURER_SPECIFIC 2 34 | */ 35 | fingerprint deviceId: "0x1101", inClusters: "0x26 0x85 0x70 0x27 0x86 0x72" 36 | 37 | } 38 | 39 | simulator { 40 | status "on": "command: 2003, payload: FF" 41 | status "off": "command: 2003, payload: 00" 42 | status "09%": "command: 2003, payload: 09" 43 | status "10%": "command: 2003, payload: 0A" 44 | status "33%": "command: 2003, payload: 21" 45 | status "66%": "command: 2003, payload: 42" 46 | status "99%": "command: 2003, payload: 63" 47 | 48 | // reply messages 49 | reply "2001FF,delay 5000,2602": "command: 2603, payload: FF" 50 | reply "200100,delay 5000,2602": "command: 2603, payload: 00" 51 | reply "200119,delay 5000,2602": "command: 2603, payload: 19" 52 | reply "200132,delay 5000,2602": "command: 2603, payload: 32" 53 | reply "20014B,delay 5000,2602": "command: 2603, payload: 4B" 54 | reply "200163,delay 5000,2602": "command: 2603, payload: 63" 55 | } 56 | 57 | tiles(scale: 2) { 58 | multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ 59 | tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { 60 | attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" 61 | attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" 62 | attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" 63 | attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" 64 | } 65 | tileAttribute ("device.level", key: "SLIDER_CONTROL") { 66 | attributeState "level", action:"switch level.setLevel" 67 | } 68 | } 69 | 70 | standardTile("indicator", "device.indicatorStatus", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { 71 | state "when off", action:"indicator.indicatorWhenOn", icon:"st.indicators.lit-when-off" 72 | state "when on", action:"indicator.indicatorNever", icon:"st.indicators.lit-when-on" 73 | state "never", action:"indicator.indicatorWhenOff", icon:"st.indicators.never-lit" 74 | } 75 | 76 | standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { 77 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" 78 | } 79 | 80 | valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { 81 | state "level", label:'${currentValue} %', unit:"%", backgroundColor:"#ffffff" 82 | } 83 | 84 | main(["switch"]) 85 | details(["switch", "level", "indicator", "refresh"]) 86 | 87 | } 88 | } 89 | 90 | def parse(String description) { 91 | def result = null 92 | if (description != "updated") { 93 | log.debug "parse() >> zwave.parse($description)" 94 | def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x27: 1, 0x70: 1, 0x86: 2, 0x72: 2,]) 95 | if (cmd) { 96 | result = zwaveEvent(cmd) 97 | } 98 | } 99 | if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) { 100 | result = [result, response(zwave.basicV1.basicGet())] 101 | log.debug "Was hailed: requesting state update" 102 | } else { 103 | log.debug "Parse returned ${result?.descriptionText}" 104 | } 105 | return result 106 | } 107 | 108 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { 109 | dimmerEvents(cmd) 110 | } 111 | 112 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { 113 | dimmerEvents(cmd) 114 | } 115 | 116 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd) { 117 | dimmerEvents(cmd) 118 | } 119 | 120 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd) { 121 | dimmerEvents(cmd) 122 | } 123 | 124 | private dimmerEvents(physicalgraph.zwave.Command cmd) { 125 | def value = (cmd.value ? "on" : "off") 126 | def result = [createEvent(name: "switch", value: value)] 127 | if (cmd.value && cmd.value <= 100) { 128 | result << createEvent(name: "level", value: cmd.value, unit: "%") 129 | } 130 | return result 131 | } 132 | 133 | 134 | def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { 135 | log.debug "ConfigurationReport $cmd" 136 | def value = "when off" 137 | if (cmd.configurationValue[0] == 1) {value = "when on"} 138 | if (cmd.configurationValue[0] == 2) {value = "never"} 139 | createEvent([name: "indicatorStatus", value: value]) 140 | } 141 | 142 | def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) { 143 | createEvent([name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false]) 144 | } 145 | 146 | def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { 147 | log.debug "manufacturerId: ${cmd.manufacturerId}" 148 | log.debug "manufacturerName: ${cmd.manufacturerName}" 149 | log.debug "productId: ${cmd.productId}" 150 | log.debug "productTypeId: ${cmd.productTypeId}" 151 | def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) 152 | updateDataValue("MSR", msr) 153 | createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false]) 154 | } 155 | 156 | def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd) { 157 | [createEvent(name:"switch", value:"on"), response(zwave.switchMultilevelV1.switchMultilevelGet().format())] 158 | } 159 | 160 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 161 | // Handles all Z-Wave commands we aren't interested in 162 | [:] 163 | } 164 | 165 | def on() { 166 | delayBetween([ 167 | zwave.basicV1.basicSet(value: 0xFF).format(), 168 | zwave.switchMultilevelV1.switchMultilevelGet().format() 169 | ],5000) 170 | } 171 | 172 | def off() { 173 | delayBetween([ 174 | zwave.basicV1.basicSet(value: 0x00).format(), 175 | zwave.switchMultilevelV1.switchMultilevelGet().format() 176 | ],5000) 177 | } 178 | 179 | def setLevel(value) { 180 | log.debug "setLevel >> value: $value" 181 | def valueaux = value as Integer 182 | def level = Math.max(Math.min(valueaux, 99), 0) 183 | if (level > 0) { 184 | sendEvent(name: "switch", value: "on") 185 | } else { 186 | sendEvent(name: "switch", value: "off") 187 | } 188 | sendEvent(name: "level", value: level, unit: "%") 189 | delayBetween ([zwave.basicV1.basicSet(value: level).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000) 190 | } 191 | 192 | def setLevel(value, duration) { 193 | log.debug "setLevel >> value: $value, duration: $duration" 194 | def valueaux = value as Integer 195 | def level = Math.max(Math.min(valueaux, 99), 0) 196 | def dimmingDuration = duration < 128 ? duration : 128 + Math.round(duration / 60) 197 | def getStatusDelay = duration < 128 ? (duration*1000)+2000 : (Math.round(duration / 60)*60*1000)+2000 198 | delayBetween ([zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format(), 199 | zwave.switchMultilevelV1.switchMultilevelGet().format()], getStatusDelay) 200 | } 201 | 202 | def poll() { 203 | zwave.switchMultilevelV1.switchMultilevelGet().format() 204 | } 205 | 206 | def refresh() { 207 | log.debug "refresh() is called" 208 | def commands = [] 209 | commands << zwave.switchMultilevelV1.switchMultilevelGet().format() 210 | if (getDataValue("MSR") == null) { 211 | commands << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() 212 | } 213 | delayBetween(commands,100) 214 | } 215 | 216 | def invertSwitch(invert=true) { 217 | if (invert) { 218 | zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format() 219 | } 220 | else { 221 | zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format() 222 | } 223 | } 224 | 225 | def resetParams2StDefaults() { 226 | log.debug "Resetting Sensor Parameters to SmartThings Compatible Defaults" 227 | def cmds = [] 228 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 1, size: 1).format() 229 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 2, size: 1).format() 230 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format() 231 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format() 232 | cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 20, size: 1).format() 233 | cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 22, size: 1).format() 234 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 14, size: 1).format() 235 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 15, size: 1).format() 236 | cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 19, size: 1).format() 237 | 238 | delayBetween(cmds, 500) 239 | } 240 | 241 | def listCurrentParams() { 242 | log.debug "Listing of current parameter settings of ${device.displayName}" 243 | def cmds = [] 244 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 1).format() 245 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 2).format() 246 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 3).format() 247 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 4).format() 248 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 20).format() 249 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 22).format() 250 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 14).format() 251 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 15).format() 252 | cmds << zwave.configurationV1.configurationGet(parameterNumber: 19).format() 253 | 254 | delayBetween(cmds, 500) 255 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/vera-plus.src/vera-plus.groovy: -------------------------------------------------------------------------------- 1 | import groovy.json.JsonSlurper 2 | metadata { 3 | definition (name: "Vera Plus", namespace: "fuzzysb", author: "Stuart Buchanan") { 4 | capability "Sensor" 5 | capability "Actuator" 6 | command "api" 7 | } 8 | 9 | preferences { 10 | input("ip", "text", title: "IP Address", description: "Your Vera IP Address", required: true, displayDuringSetup: true) 11 | input("port", "number", title: "Port Number", description: "Your Vera Port Number (Default:3480)", defaultValue: "3480", required: true, displayDuringSetup: true) 12 | } 13 | 14 | tiles (scale: 2){ 15 | valueTile("hubInfo", "device.hubInfo", decoration: "flat", height: 2, width: 6, inactiveLabel: false, canChangeIcon: false) { 16 | state "hubInfo", label:'${currentValue}' 17 | } 18 | } 19 | main("hubInfo") 20 | details(["hubInfo"]) 21 | } 22 | 23 | def parse(description) { 24 | log.debug description 25 | def events = [] 26 | def result 27 | def descMap = parseDescriptionAsMap(description) 28 | def body = new String(descMap["body"]) 29 | if (body != "T0s="){ 30 | body = new String(descMap["body"].decodeBase64()) 31 | def slurper = new JsonSlurper() 32 | result = slurper.parseText(body) 33 | } else { 34 | result = "OK" 35 | } 36 | log.debug result 37 | if (result == "OK"){ 38 | events << createEvent(name:"hubInfo", value:result) 39 | } 40 | else (result.containsKey("OK")) { 41 | events << createEvent(name:"hubInfo", value:result.OK) 42 | } 43 | return events 44 | } 45 | 46 | def parseDescriptionAsMap(description) { 47 | description.split(",").inject([:]) { map, param -> 48 | def nameAndValue = param.split(":") 49 | 50 | if (nameAndValue.length == 2) map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] 51 | else map += [(nameAndValue[0].trim()):""] 52 | } 53 | } 54 | 55 | def installed() { 56 | log.debug "Installed with settings: ${settings}" 57 | initialize() 58 | } 59 | 60 | def updated() { 61 | log.debug "Updated with settings: ${settings}" 62 | initialize() 63 | } 64 | 65 | def initialize() { 66 | ipSetup() 67 | } 68 | 69 | def api(String veraCommand, String veraDevId) { 70 | ipSetup() 71 | def cmdPath 72 | def hubAction 73 | switch (veraCommand) { 74 | case "on": 75 | cmdPath = "/data_request?id=action&output_format=json&DeviceNum=${veraDevId}&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1" 76 | log.debug "The Switch On Command was sent to Vera Device ${veraDevId}" 77 | break; 78 | case "off": 79 | cmdPath = "/data_request?id=action&output_format=json&DeviceNum=${veraDevId}&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=0" 80 | log.debug "The Switch Off Command was sent to Vera Device ID: ${veraDevId}" 81 | break; 82 | case "open": 83 | cmdPath = "/data_request?id=variableset&output_format=json&DeviceNum=${veraDevId}&serviceId=urn:micasaverde-com:serviceId:SecuritySensor1&Variable=Tripped&Value=1" 84 | log.debug "The Contact Open Command was sent to Vera Device ID: ${veraDevId}" 85 | break; 86 | case "closed": 87 | cmdPath = "/data_request?id=variableset&output_format=json&DeviceNum=${veraDevId}&serviceId=urn:micasaverde-com:serviceId:SecuritySensor1&Variable=Tripped&Value=0" 88 | log.debug "The Contact Closed Command was sent to Vera Device ID: ${veraDevId}" 89 | break; 90 | case "active": 91 | cmdPath = "/data_request?id=variableset&output_format=json&DeviceNum=${veraDevId}&serviceId=urn:micasaverde-com:serviceId:SecuritySensor1&Variable=Tripped&Value=1" 92 | log.debug "The Motion active Command was sent to Vera Device ID: ${veraDevId}" 93 | break; 94 | case "inactive": 95 | cmdPath = "/data_request?id=variableset&output_format=json&DeviceNum=${veraDevId}&serviceId=urn:micasaverde-com:serviceId:SecuritySensor1&Variable=Tripped&Value=0" 96 | log.debug "The Motion inactive Command was sent to Vera Device ID: ${veraDevId}" 97 | break; 98 | } 99 | 100 | switch (veraCommand) { 101 | default: 102 | try { 103 | hubAction = [new physicalgraph.device.HubAction( 104 | method: "GET", 105 | path: cmdPath, 106 | headers: [HOST: "${settings.ip}:${settings.port}", Accept: "application/json"] 107 | )] 108 | } 109 | catch (Exception e) { 110 | log.debug "Hit Exception $e on $hubAction" 111 | } 112 | break; 113 | } 114 | return hubAction 115 | } 116 | 117 | def ipSetup() { 118 | log.debug "In IPSetup Area" 119 | def hosthex 120 | def porthex 121 | if (settings.ip) { 122 | hosthex = convertIPtoHex(settings.ip).toUpperCase() 123 | } 124 | if (settings.port) { 125 | porthex = convertPortToHex(settings.port).toUpperCase() 126 | } 127 | if (settings.ip && settings.port) { 128 | log.debug "updating Network ID to ${hosthex}:${porthex}" 129 | device.deviceNetworkId = "${hosthex}:${porthex}" 130 | } 131 | } 132 | 133 | private String convertIPtoHex(ip) { 134 | String hexip = ip.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join() 135 | return hexip 136 | } 137 | private String convertPortToHex(port) { 138 | String hexport = port.toString().format( '%04x', port.toInteger() ) 139 | return hexport 140 | } 141 | -------------------------------------------------------------------------------- /devicetypes/fuzzysb/virtual-presence-sensor.src/virtual-presence-sensor.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Based on original Simulated Presence code by Bob & Smartthings, but this can be used in Rule Machine 14 | * v1.1 Updated Presence Tile which was having state display issues. 15 | * v1.0 Initial Release 16 | */ 17 | metadata { 18 | // Automatically generated. Make future change here. 19 | definition (name: "Virtual Presence Sensor", namespace: "fuzzysb", author: "Stuart Buchanan") { 20 | capability "Presence Sensor" 21 | capability "Actuator" 22 | capability "Sensor" 23 | command "arrived" 24 | command "departed" 25 | } 26 | 27 | simulator { 28 | status "present": "presence: present" 29 | status "not present": "presence: not present" 30 | } 31 | 32 | tiles { 33 | standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { 34 | state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#53a7c0") 35 | state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ffffff") 36 | } 37 | main "presence" 38 | details "presence" 39 | } 40 | } 41 | 42 | def parse(String description) { 43 | def pair = description.split(":") 44 | createEvent(name: pair[0].trim(), value: pair[1].trim()) 45 | } 46 | 47 | // handle commands 48 | def arrived() { 49 | log.trace "Executing 'arrived'" 50 | sendEvent(name: "presence", value: "present") 51 | } 52 | 53 | 54 | def departed() { 55 | log.trace "Executing 'Departed'" 56 | sendEvent(name: "presence", value: "not present") 57 | } 58 | -------------------------------------------------------------------------------- /devicetypes/fuzzysb/voxcommando-tts-announcer.src/voxcommando-tts-announcer.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the License); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * httpwww.apache.orglicensesLICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * VoxCommando TTS Announcer 14 | * 15 | * Author Stuart Buchanan 16 | * Date 2016-04-04 v1.0 updated with new options in latest vox commando beta 17 | * Date 2016-03-28 v1.0 Initial Release 18 | **/ 19 | preferences { 20 | input("ServerIP", "text", title: "ServerIP", description: "Enter the IP Address of the Voxcommando Web Server") 21 | input("Port", "text", title: "Port", description: "Enter the TCP Port of the Voxcommando Web Server") 22 | input("DefaultVolume", "number", title: "Default Announcement Volume", description: "Enter the Default Volume % for Announcements") 23 | /* 24 | input("MasterSonos", "text", title: "Master Sonos Device", description: "Enter the name of the groups Master Sonos Player") 25 | input("SecondSonos", "text", title: "Second Sonos Device", description: "Enter the name of the second Sonos Player") 26 | input("ThirdSonos", "text", title: "Third Sonos Device", description: "Enter the name of the third Sonos Player") 27 | input("FourthSonos", "text", title: "Fourth Sonos Device", description: "Enter the name of the third Sonos Player") 28 | */ 29 | } 30 | 31 | metadata { 32 | definition (name: "VoxCommando TTS Announcer", namespace: "fuzzysb", author: "Stuart Buchanan") { 33 | capability "Speech Synthesis" 34 | capability "Actuator" 35 | capability "Refresh" 36 | command "announce" 37 | command "creategroup" 38 | command "getgroup" 39 | command "dissolveallgroups" 40 | command "dissolvegroup" 41 | command "nullplay" 42 | command "ttsplay" 43 | command "selectplayer" 44 | command "setvolume" 45 | command "test" 46 | } 47 | 48 | 49 | // simulator metadata 50 | simulator { 51 | // status messages 52 | 53 | // reply messages 54 | } 55 | 56 | tiles(scale: 2){ 57 | standardTile("Test", "device.switch", inactiveLabel: false, width: 2, height: 2, decoration: "flat") { 58 | state "default", label:"Test", action:"test", icon:"st.Entertainment.entertainment3" 59 | } 60 | 61 | 62 | main("Test") 63 | details(["Test"]) 64 | } 65 | } 66 | 67 | def updated() { 68 | log.info "VoxCommando TTS Announcer Updated" 69 | state.dni = createDNI(settings.ServerIP, settings.Port) 70 | state.hostAddress = "${settings.ServerIP}, ${settings.Port}" 71 | } 72 | 73 | def parse(String message) { 74 | def msg = stringToMap(message) 75 | if (msg.containsKey("simulator")) { 76 | // simulator input 77 | return parseHttpResponse(msg) 78 | } 79 | 80 | if (!msg.containsKey("headers")) { 81 | log.error "No HTTP headers found in '${message}'" 82 | return null 83 | } 84 | 85 | // parse HTTP response headers 86 | def headers = new String(msg.headers.decodeBase64()) 87 | def parsedHeaders = parseHttpHeaders(headers) 88 | //log.debug "parsedHeaders: ${parsedHeaders}" 89 | if (parsedHeaders.status != 200) { 90 | log.error "Return Code: ${parsedHeaders.status} Server error: ${parsedHeaders.reason}" 91 | return null 92 | } 93 | 94 | // parse HTTP response body 95 | if (!msg.body) { 96 | log.error "No HTTP body found in '${message}'" 97 | return null 98 | } 99 | 100 | def body = new String(msg.body.decodeBase64()) 101 | //log.debug "body: ${body}" 102 | body = body.replace("&", "&") 103 | def result = new XmlParser() 104 | return parseHttpResponse(result.parseText(body.toString())) 105 | } 106 | 107 | private parseHttpHeaders(String headers) { 108 | def lines = headers.readLines() 109 | def status = lines.remove(0).split() 110 | 111 | def result = [ 112 | protocol: status[0], 113 | status: status[1].toInteger(), 114 | reason: status[2] 115 | ] 116 | 117 | return result 118 | } 119 | 120 | private def parseHttpResponse(Node data) { 121 | log.debug("parseHttpResponse(${data})") 122 | 123 | def events = [] 124 | /* 125 | if (data.containsKey('state')) { 126 | def vcState = data.state 127 | //LOG("VLC state: ${vcState})") 128 | events << createEvent(name:"status", value:vcState) 129 | if (vcState == 'stopped') { 130 | events << createEvent([name:'trackDescription', value:'']) 131 | } 132 | } 133 | 134 | if (data.containsKey('volume')) { 135 | //LOG("VC volume: ${data.volume})") 136 | def volume = ((data.volume.toInteger() * 100) / 512) as int 137 | events << createEvent(name:'level', value:volume) 138 | } 139 | 140 | if (data.containsKey('information')) { 141 | parseTrackInfo(events, data.information) 142 | } 143 | */ 144 | //log.debug "events: ${events}" 145 | return events 146 | } 147 | 148 | 149 | def test() { 150 | def defvol = settings.DefaultVolume ?: 70 151 | log.debug "Executing Test Message" 152 | announce("this is a test message",defvol) 153 | } 154 | 155 | def announce(message,vol){ 156 | log.debug "Checking DNI" 157 | updateDNI() 158 | 159 | try { 160 | /* 161 | log.debug "Executing dissolvegroup" 162 | def dissolveGroupResult = dissolvegroup() 163 | sendHubCommand(dissolveGroupResult) 164 | delayHubAction(1000) 165 | 166 | log.debug "Executing creategroup" 167 | def createGroupResult = creategroup() 168 | sendHubCommand(createGroupResult) 169 | delayHubAction(1000) 170 | */ 171 | 172 | log.debug "Executing selectplayer" 173 | def selectPlayerResult = selectplayer() 174 | sendHubCommand(selectPlayerResult) 175 | delayHubAction(500) 176 | /* 177 | log.debug "Executing GetGroupPlayer" 178 | def getGroupResult = getgroup() 179 | sendHubCommand(getGroupResult) 180 | delayHubAction(500) 181 | */ 182 | 183 | message = URLEncoder.encode(message) 184 | log.debug "Executing ttsplay with the message: ${message} at Volume Level ${vol}" 185 | def ttsResult = ttsplay(message, vol) 186 | sendHubCommand(ttsResult) 187 | } 188 | catch (Exception e) 189 | { 190 | log.debug "Hit Exception $e" 191 | } 192 | } 193 | 194 | def creategroup(){ 195 | def headers = [:] 196 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 197 | def result = new physicalgraph.device.HubAction( 198 | method: "GET", 199 | path: "/api/Sonos.CreateGroup&&Conservatory&&Master+Bedroom&&Kitchen", 200 | headers: headers 201 | 202 | ) 203 | return result 204 | } 205 | 206 | def getgroup(){ 207 | def headers = [:] 208 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 209 | def result = new physicalgraph.device.HubAction( 210 | method: "GET", 211 | path: "/api/Sonos.GetGroupInfo", 212 | headers: headers 213 | ) 214 | return result 215 | } 216 | 217 | def ttsplay(message, vol){ 218 | def headers = [:] 219 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 220 | def result = new physicalgraph.device.HubAction( 221 | method: "GET", 222 | path: "/api/Sonos.TTS.Speak&&,+${message}&&${vol}", 223 | headers: headers 224 | ) 225 | //log.debug result 226 | return result 227 | } 228 | 229 | 230 | def nullplay(){ 231 | def headers = [:] 232 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 233 | def result = new physicalgraph.device.HubAction( 234 | method: "GET", 235 | path: "/api/Sonos.TTS.Speak&&,", 236 | headers: headers 237 | ) 238 | //log.debug result 239 | return result 240 | } 241 | 242 | 243 | def selectplayer(){ 244 | def headers = [:] 245 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 246 | def result = new physicalgraph.device.HubAction( 247 | method: "GET", 248 | //path: "/api/Sonos.SetPlayer&&Conservatory%20%2B%20Master%20Bedroom%20%2B%20Kitchen", 249 | path: "/api/Sonos.SetPlayer&&Conservatory", 250 | headers: headers 251 | ) 252 | return result 253 | } 254 | 255 | def setvolume(vol){ 256 | def headers = [:] 257 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 258 | def result = new physicalgraph.device.HubAction( 259 | method: "GET", 260 | path: "/api/Sonos.Player.SetGrpVol&&${vol}", 261 | headers: headers 262 | ) 263 | return result 264 | } 265 | 266 | def speak(message){ 267 | def defvol = settings.DefaultVolume ?: 70 268 | log.debug "Executing announcement with: ${message} at Volume: ${defvol}" 269 | announce("${message}",defvol) 270 | } 271 | 272 | def dissolvegroup(){ 273 | def headers = [:] 274 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 275 | def result = new physicalgraph.device.HubAction( 276 | method: "GET", 277 | path: "/api/Sonos.DissolveGroup&&Conservatory&&Master+Bedroom&&Kitchen", 278 | headers: headers 279 | ) 280 | return result 281 | } 282 | 283 | 284 | def dissolveallgroups(){ 285 | def headers = [:] 286 | headers.put("HOST","${settings.ServerIP}:${settings.Port}") 287 | def result = new physicalgraph.device.HubAction( 288 | method: "GET", 289 | path: "/api/Sonos.DissolveAllGroups", 290 | headers: headers 291 | ) 292 | return result 293 | } 294 | 295 | 296 | private String createDNI(ipaddr, port) { 297 | log.debug("createDNI(${ipaddr}, ${port})") 298 | 299 | def hexIp = ipaddr.tokenize('.').collect { 300 | String.format('%02X', it.toInteger()) 301 | }.join() 302 | 303 | def hexPort = String.format('%04X', port.toInteger()) 304 | log.debug "Hex IP:Port: ${hexIp}:${hexPort}" 305 | return "${hexIp}:${hexPort}" 306 | } 307 | 308 | private def delayHubAction(ms) { 309 | log.debug("delayHubAction(${ms})") 310 | return new physicalgraph.device.HubAction("delay ${ms}") 311 | } 312 | 313 | private updateDNI() { 314 | if (device.deviceNetworkId != state.dni) { 315 | device.deviceNetworkId = state.dni 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /devicetypes/fuzzysb/zipato-ph-pse02-eu.src/zipato-ph-pse02-eu.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * Zipato/Phileo PH-PSE02-EU Siren 14 | * 15 | * Author: fuzzysb 16 | * Date: 2015-01-05 17 | */ 18 | preferences { 19 | input "defaultSound", "enum", title: "Default Sound to use for the Siren?", options: ["Emergency","FireAlarm","Ambulance","PoliceCar","DoorChime"], required: false, defaultValue: "Emergency" 20 | } 21 | 22 | 23 | metadata { 24 | definition (name: "Zipato PH-PSE02-EU", namespace: "fuzzysb", author: "Stuart Buchanan") { 25 | capability "Actuator" 26 | capability "Alarm" 27 | capability "Switch" 28 | capability "Configuration" 29 | 30 | command "Emergency" 31 | command "FireAlarm" 32 | command "Ambulance" 33 | command "PoliceCar" 34 | command "DoorChime" 35 | 36 | fingerprint deviceId: "0x1005", inClusters: "0x5E,0x71,0x20,0x25,0x85,0x70,0x72,0x86,0x30,0x59,0x73,0x5A,0x98,0x7A" 37 | } 38 | 39 | simulator { 40 | 41 | } 42 | 43 | tiles(scale: 2) { 44 | multiAttributeTile(name:"alarm", type: "generic", width: 6, height: 4){ 45 | tileAttribute ("device.alarm", key: "PRIMARY_CONTROL") { 46 | attributeState "off", label:'off', action:'alarm.siren', icon:"st.alarm.alarm.alarm", backgroundColor:"#ffffff" 47 | attributeState "both", label:'alarm!', action:'alarm.off', icon:"st.alarm.alarm.alarm", backgroundColor:"#e86d13" 48 | } 49 | } 50 | standardTile("Emergency", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 51 | state "default", label:'Emergency', action:"Emergency", icon:"st.Weather.weather1" 52 | } 53 | standardTile("FireAlarm", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 54 | state "default", label:'Fire Alarm', action:"FireAlarm", icon:"st.Outdoor.outdoor10" 55 | } 56 | standardTile("Ambulance", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 57 | state "default", label:'Ambulance', action:"Ambulance", icon:"st.Transportation.transportation2" 58 | } 59 | standardTile("PoliceCar", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 60 | state "default", label:'Police Car', action:"PoliceCar", icon:"st.Transportation.transportation8" 61 | } 62 | standardTile("DoorChime", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 63 | state "default", label:'Door Chime', action:"DoorChime", icon:"st.Home.home30" 64 | } 65 | standardTile("off", "device.button", inactiveLabel: false, decoration: "flat", width: 1, height: 1) { 66 | state "default", label:'', action:"alarm.off", icon:"st.secondary.off" 67 | } 68 | main "alarm" 69 | details(["alarm", "Emergency", "FireAlarm", "Ambulance", "PoliceCar", "DoorChime", "off"]) 70 | } 71 | } 72 | 73 | def parse(String description) 74 | { 75 | def result = null 76 | if (description.startsWith("Err 106")) { 77 | state.sec = 0 78 | result = createEvent( name: "secureInclusion", value: "failed", isStateChange: true, 79 | descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.") 80 | } else if (description != "updated") { 81 | def cmd = zwave.parse(description, [0x20: 1, 0x25: 1, 0x26: 1, 0x70: 1, 0x80: 1]) 82 | if (cmd) { 83 | result = zwaveEvent(cmd) 84 | log.debug "Parse returned ${result?.inspect()}" 85 | } else { 86 | log.debug("Couldn't zwave.parse '$description'") 87 | null 88 | } 89 | 90 | } 91 | log.debug "Parsed '${description}' to ${result.inspect()}" 92 | return result 93 | } 94 | 95 | 96 | 97 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { 98 | def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x25: 3, 0x26: 3, 0x70: 1, 0x80: 1]) 99 | state.sec = 1 100 | log.debug "encapsulated: ${encapsulatedCommand}" 101 | if (encapsulatedCommand) { 102 | zwaveEvent(encapsulatedCommand) 103 | } else { 104 | log.warn "Unable to extract encapsulated cmd from $cmd" 105 | createEvent(descriptionText: cmd.toString()) 106 | } 107 | } 108 | 109 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) { 110 | response(configure()) 111 | } 112 | 113 | def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { 114 | log.debug "---CONFIGURATION REPORT V2--- ${device.displayName} parameter ${cmd.parameterNumber} with a byte size of ${cmd.size} is set to ${cmd.configurationValue}" 115 | } 116 | 117 | def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) 118 | { 119 | [name: "switch", value: cmd.value ? "on" : "off", type: "digital", displayed: true, isStateChange: true] 120 | } 121 | 122 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { 123 | log.debug "rx $cmd" 124 | [ 125 | createEvent([name: "switch", value: cmd.value ? "on" : "off", displayed: false]), 126 | createEvent([name: "alarm", value: cmd.value ? "both" : "off"]) 127 | ] 128 | } 129 | 130 | def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) { 131 | createEvent(name:"Alarm", cmd.sensorValue ? "on" : "off") 132 | } 133 | 134 | def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { 135 | if (cmd.notificationType == 6 && cmd.event == 22) { 136 | log.debug "Playing Door Chime" 137 | } else if (cmd.notificationType == 10 && cmd.event == 1) { 138 | log.debug "Playing Police Car" 139 | } else if (cmd.notificationType == 10 && cmd.event == 3) { 140 | log.debug "Playing Ambulance" 141 | } else if (cmd.notificationType == 10 && cmd.event == 2) { 142 | log.debug "Playing Fire Alarm" 143 | } else if (cmd.notificationType == 7 && cmd.event == 1) { 144 | log.debug "Playing Emergency" 145 | } 146 | } 147 | 148 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 149 | log.debug "Unhandled: $cmd" 150 | createEvent(descriptionText: cmd.toString(), isStateChange: false) 151 | } 152 | 153 | def strobe() { 154 | //on() 155 | } 156 | 157 | def siren() { 158 | //on() 159 | } 160 | 161 | def both() { 162 | //on() 163 | } 164 | 165 | 166 | def on() { 167 | log.debug("Sounding Siren") 168 | switch ( settings.defaultSound ) { 169 | case "Emergency": 170 | Emergency() 171 | break 172 | 173 | case "FireAlarm": 174 | FireAlarm() 175 | break 176 | 177 | case "Ambulance": 178 | Ambulance() 179 | break 180 | 181 | case "PoliceCar": 182 | PoliceCar() 183 | break 184 | 185 | case "DoorChime": 186 | DoorChime() 187 | break 188 | 189 | default: 190 | Emergency() 191 | break 192 | } 193 | } 194 | 195 | def off() { 196 | log.debug "sending off" 197 | [ 198 | secure(zwave.basicV1.basicSet(value: 0x00)), 199 | secure(zwave.basicV1.basicGet()) 200 | ] 201 | } 202 | 203 | def Emergency() { 204 | log.debug "Sounding Siren With Emergency" 205 | [ 206 | secure(zwave.notificationV3.notificationReport(event: 0x01, notificationType: 0x07)), 207 | secure(zwave.basicV1.basicGet()) 208 | ] 209 | } 210 | 211 | def FireAlarm() { 212 | log.debug "Sounding Siren With Fire Alarm" 213 | [ 214 | secure(zwave.notificationV3.notificationReport(event: 0x02, notificationType: 0x0A)), 215 | secure(zwave.basicV1.basicGet()) 216 | ] 217 | } 218 | 219 | def Ambulance() { 220 | log.debug "Sounding Siren With Ambulance" 221 | [ 222 | secure(zwave.notificationV3.notificationReport(event: 0x03, notificationType: 0x0A)), 223 | secure(zwave.basicV1.basicGet()) 224 | ] 225 | } 226 | 227 | def PoliceCar() { 228 | log.debug "Sounding Siren With Police Car" 229 | [ 230 | secure(zwave.notificationV3.notificationReport(event: 0x01, notificationType: 0x0A)), 231 | secure(zwave.basicV1.basicGet()) 232 | ] 233 | } 234 | 235 | def DoorChime() { 236 | log.debug "Sounding Siren With Door Chime" 237 | [ 238 | secure(zwave.notificationV3.notificationReport(event: 0x16, notificationType: 0x06)), 239 | secure(zwave.basicV1.basicGet()) 240 | ] 241 | } 242 | 243 | def configure() { 244 | log.debug "Resetting Siren Parameters to SmartThings Compatible Defaults" 245 | def cmds = [] 246 | 247 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1) 248 | cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 29, size: 1) 249 | cmds << zwave.configurationV1.configurationSet(configurationValue: [6], parameterNumber: 31, size: 1) 250 | 251 | delayBetween(cmds, 500) 252 | } 253 | 254 | private secure(physicalgraph.zwave.Command cmd) { 255 | if (state.sec) { 256 | log.debug "Sending Secure Command $cmd" 257 | zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() 258 | } else { 259 | log.debug "Sending Insecure Command $cmd" 260 | cmd.format() 261 | } 262 | } -------------------------------------------------------------------------------- /devicetypes/fuzzysb/zwave-me-zwave-controller.src/zwave-me-zwave-controller.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | */ 14 | metadata { 15 | definition (name: "ZWave.Me Z-Wave Controller", namespace: "fuzzysb", author: "Stuart Buchanan") { 16 | 17 | command "on" 18 | command "off" 19 | 20 | /* Capability notes 21 | 0x21 COMMAND_CLASS_CONTROLLER_REPLICATION 1 22 | 0x20 COMMAND_CLASS_BASIC 1 23 | 0x86 COMMAND_CLASS_VERSION 1 24 | 0x98 COMMAND_CLASS_SECURITY 1 25 | */ 26 | 27 | fingerprint deviceId: "0x0201", inClusters: "0x21 0x20 0x86 0x98" 28 | } 29 | 30 | simulator { 31 | 32 | } 33 | 34 | tiles { 35 | standardTile("state", "device.state", width: 2, height: 2) { 36 | state 'connected', icon: "st.unknown.zwave.static-controller", backgroundColor:"#ffffff" 37 | } 38 | standardTile("basicOn", "device.switch", inactiveLabel:false, decoration:"flat") { 39 | state "on", label:"on", action:"on", icon:"st.switches.switch.on" 40 | } 41 | standardTile("basicOff", "device.switch", inactiveLabel: false, decoration:"flat") { 42 | state "off", label:"off", action:"off", icon:"st.switches.switch.off" 43 | } 44 | 45 | main "state" 46 | details(["state", "basicOn", "basicOff"]) 47 | } 48 | } 49 | 50 | 51 | def parse(String description) 52 | { 53 | def result = null 54 | if (description.startsWith("Err 106")) { 55 | state.sec = 0 56 | result = createEvent( name: "secureInclusion", value: "failed", isStateChange: true, 57 | descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.") 58 | } else if (description != "updated") { 59 | def cmd = zwave.parse(description, [0x98: 1, 0x86: 1, 0x72: 2, 0x20: 1, 0x21: 1]) 60 | if (cmd) { 61 | result = zwaveEvent(cmd) 62 | log.debug "Parse returned ${result?.inspect()}" 63 | } else { 64 | log.debug("Couldn't zwave.parse '$description'") 65 | null 66 | } 67 | 68 | } 69 | log.debug "Parsed '${description}' to ${result.inspect()}" 70 | return result 71 | } 72 | 73 | 74 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { 75 | def encapsulatedCommand = cmd.encapsulatedCommand([0x86: 1, 0x72: 2, 0x20: 1, 0x21: 1]) 76 | state.sec = 1 77 | log.debug "encapsulated: ${encapsulatedCommand}" 78 | if (encapsulatedCommand) { 79 | zwaveEvent(encapsulatedCommand) 80 | } else { 81 | log.warn "Unable to extract encapsulated cmd from $cmd" 82 | createEvent(descriptionText: cmd.toString()) 83 | } 84 | } 85 | 86 | 87 | def zwaveEvent(physicalgraph.zwave.Command cmd) { 88 | def event = [isStateChange: true] 89 | event.linkText = device.label ?: device.name 90 | event.descriptionText = "$event.linkText: $cmd" 91 | event 92 | } 93 | 94 | 95 | def on() { 96 | zwave.basicV1.basicSet(value: 0xFF).format() 97 | } 98 | 99 | def off() { 100 | zwave.basicV1.basicSet(value: 0x00).format() 101 | } 102 | 103 | private secure(physicalgraph.zwave.Command cmd) { 104 | if (state.sec) { 105 | log.debug "Sending Secure Command $cmd" 106 | zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() 107 | } else { 108 | log.debug "Sending Insecure Command $cmd" 109 | cmd.format() 110 | } 111 | } -------------------------------------------------------------------------------- /smartapps/fuzzysb/asuswrt-tv-on-off-check.src/CheckTVon: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -d /tmp/CheckTV ] 4 | then 5 | mkdir /tmp/CheckTV 6 | fi 7 | 8 | LivingRoomTV=Off 9 | ConservatoryTV=Off 10 | BedroomTV=Off 11 | XBoxOne=Off 12 | 13 | macadresser=`wl -i eth1 assoclist` 14 | antal=0 15 | antal=`qcsapi_sockrpc get_count_assoc wifi0` 16 | 17 | while [ $antal -gt 0 ] 18 | do 19 | antal=`expr $antal - 1` 20 | macadresser="`qcsapi_sockrpc get_station_mac_addr wifi0 $antal`;$macadresser" 21 | done 22 | 23 | case "$macadresser" in 24 | *XX:XX:XX:XX:XX:XX*) 25 | LivingRoomTV=On 26 | ;; 27 | esac 28 | 29 | case "$macadresser" in 30 | *XX:XX:XX:XX:XX:XX*) 31 | ConservatoryTV=On 32 | ;; 33 | esac 34 | 35 | case "$macadresser" in 36 | *XX:XX:XX:XX:XX:XX*) 37 | BedroomTV=On 38 | ;; 39 | esac 40 | 41 | case "$macadresser" in 42 | *XX:XX:XX:XX:XX:XX*) 43 | XBoxOne=On 44 | ;; 45 | esac 46 | 47 | if [ "$LivingRoomTV" = On ] 48 | then 49 | if [ ! -f /tmp/CheckTV/LivingRoomTVOn ] 50 | then 51 | touch /tmp/CheckTV/LivingRoomTVOn 52 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations/9d0305a0-96c1-43a2-a2ae-f660a160bd61/TV/on?access_token=893daa02-fd91-4766-947a-e89e1d1d00bd" -k 53 | fi 54 | else 55 | if [ -f /tmp/CheckTV/LivingRoomTVOn ] 56 | then 57 | rm -f /tmp/CheckTV/LivingRoomTVOn 58 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations/9d0305a0-96c1-43a2-a2ae-f660a160bd61/TV/off?access_token=893daa02-fd91-4766-947a-e89e1d1d00bd" -k 59 | fi 60 | fi 61 | 62 | if [ "$ConservatoryTV" = On ] 63 | then 64 | if [ ! -f /tmp/CheckTV/ConservatoryTVOn ] 65 | then 66 | touch /tmp/CheckTV/ConservatoryTVOn 67 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations/85655506-f90d-426f-81fe-81b9687ac9bc/TV/on?access_token=4ad0d680-1616-4c4b-a0b7-f23584ac98b8" -k 68 | fi 69 | else 70 | if [ -f /tmp/CheckTV/ConservatoryTVOn ] 71 | then 72 | rm -f /tmp/CheckTV/ConservatoryTVOn 73 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations/85655506-f90d-426f-81fe-81b9687ac9bc/TV/off?access_token=4ad0d680-1616-4c4b-a0b7-f23584ac98b8" -k 74 | fi 75 | fi 76 | 77 | if [ "$BedroomTV" = On ] 78 | then 79 | if [ ! -f /tmp/CheckTV/BedroomTVOn ] 80 | then 81 | touch /tmp/CheckTV/BedroomTVOn 82 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations/5b230bee-7f50-4ef5-a564-ac1fbefe0791/TV/on?access_token=d1c1b409-269e-4614-8447-b0cd8216c111" -k 83 | fi 84 | else 85 | if [ -f /tmp/CheckTV/BedroomTVOn ] 86 | then 87 | rm -f /tmp/CheckTV/BedroomTVOn 88 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations/5b230bee-7f50-4ef5-a564-ac1fbefe0791/TV/off?access_token=d1c1b409-269e-4614-8447-b0cd8216c111" -k 89 | fi 90 | fi 91 | 92 | if [ "$XBoxOne" = On ] 93 | then 94 | if [ ! -f /tmp/CheckTV/XBoxOneOn ] 95 | then 96 | touch /tmp/CheckTV/XBoxOneOn 97 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations/183a2032-51c2-4e97-8dd6-b6ded0027355/TV/on?access_token=3dc940ec-cd27-4d05-9360-eb7ececf4155" -k 98 | fi 99 | else 100 | if [ -f /tmp/CheckTV/XBoxOneOn ] 101 | then 102 | rm -f /tmp/CheckTV/XBoxOneOn 103 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations/183a2032-51c2-4e97-8dd6-b6ded0027355/TV/off?access_token=3dc940ec-cd27-4d05-9360-eb7ececf4155" -k 104 | fi 105 | fi 106 | 107 | -------------------------------------------------------------------------------- /smartapps/fuzzysb/asuswrt-tv-on-off-check.src/asuswrt-tv-checker.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * AsusWRT TV Checker 14 | * 15 | * Author: Stuart Buchanan, Based on original work by midyear66 with thanks 16 | * This app recieves a HHTP Get request from a AsusWrt Router when a Smart TV Connects & Disconnects from the Wifi Network 17 | * Date: 2016-02-03 v1.0 Initial Release 18 | */ 19 | definition( 20 | name: "AsusWRT TV Checker", 21 | namespace: "fuzzysb", 22 | author: "Stuart Buchanan", 23 | description: "Triggers Virtual Switch when HTTP GET Request is recieved", 24 | category: "My Apps", 25 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 26 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 27 | 28 | 29 | preferences { 30 | section(title: "Select Devices") { 31 | input "virtualSwitch", "capability.Switch", title: "Select Virtual Switch", required: true, multiple:false 32 | } 33 | 34 | } 35 | 36 | def installed() { 37 | createAccessToken() 38 | getToken() 39 | DEBUG("Installed Phone with rest api: $app.id") 40 | DEBUG("Installed Phone with token: $state.accessToken") 41 | DEBUG("Installed with settings: $virtualSwitch.name") 42 | } 43 | def updated() { 44 | DEBUG("Updated Phone with rest api: $app.id") 45 | DEBUG("Updated Phone with token: $state.accessToken") 46 | } 47 | 48 | 49 | mappings { 50 | path("/TV/on") { 51 | action: [ 52 | GET: "switchon" 53 | ] 54 | } 55 | path("/TV/off") { 56 | action: [ 57 | GET:"switchoff" 58 | ] 59 | } 60 | } 61 | 62 | 63 | // Callback functions 64 | def getSwitch() { 65 | // This returns the current state of the switch in JSON 66 | return virtualSwitch.currentState("switch") 67 | } 68 | 69 | def switchon() { 70 | DEBUG("On") 71 | virtualSwitch.on(); 72 | } 73 | 74 | def switchoff() { 75 | DEBUG("Off") 76 | virtualSwitch.off(); 77 | } 78 | 79 | def getToken(){ 80 | if (!state.accessToken) { 81 | try { 82 | getAccessToken() 83 | DEBUG("Creating new Access Token: $state.accessToken") 84 | } catch (ex) { 85 | DEBUG("Did you forget to enable OAuth in SmartApp IDE settings") 86 | DEBUG(ex) 87 | } 88 | } 89 | } 90 | 91 | private def DEBUG(message){ 92 | log.debug message 93 | } -------------------------------------------------------------------------------- /smartapps/fuzzysb/asuswrt-tv-on-off-check.src/init-start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cru a CheckTVon "* * * * * /jffs/scripts/CheckTVon" -------------------------------------------------------------------------------- /smartapps/fuzzysb/asuswrt-wifi-presence.src/CheckIfHome: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -d /tmp/CheckUser ] 4 | then 5 | mkdir /tmp/CheckUser 6 | fi 7 | 8 | 9 | Stuart=Away 10 | Sharon=Away 11 | Dane=Away 12 | Scott=Away 13 | 14 | macadresser=`wl -i eth1 assoclist` 15 | antal=0 16 | antal=`qcsapi_sockrpc get_count_assoc wifi0` 17 | 18 | while [ $antal -gt 0 ] 19 | do 20 | antal=`expr $antal - 1` 21 | macadresser="`qcsapi_sockrpc get_station_mac_addr wifi0 $antal`;$macadresser" 22 | done 23 | 24 | case "$macadresser" in 25 | *XX:XX:XX:XX:XX:XX*) 26 | Stuart=Home 27 | ;; 28 | esac 29 | 30 | case "$macadresser" in 31 | *XX:XX:XX:XX:XX:XX*) 32 | Sharon=Home 33 | ;; 34 | esac 35 | 36 | case "$macadresser" in 37 | *XX:XX:XX:XX:XX:XX*) 38 | Dane=Home 39 | ;; 40 | esac 41 | 42 | case "$macadresser" in 43 | *XX:XX:XX:XX:XX:XX*) 44 | Scott=Home 45 | ;; 46 | esac 47 | 48 | if [ "$Stuart" = Home ] 49 | then 50 | if [ ! -f /tmp/CheckUser/StuartHome ] 51 | then 52 | touch /tmp/CheckUser/StuartHome 53 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations//Phone/home?access_token=" -k 54 | fi 55 | else 56 | if [ -f /tmp/CheckUser/StuartHome ] 57 | then 58 | rm -f /tmp/CheckUser/StuartHome 59 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations//Phone/away?access_token=" -k 60 | fi 61 | fi 62 | 63 | if [ "$Sharon" = Home ] 64 | then 65 | if [ ! -f /tmp/CheckUser/SharonHome ] 66 | then 67 | touch /tmp/CheckUser/SharonHome 68 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations//Phone/home?access_token=" -k 69 | fi 70 | else 71 | if [ -f /tmp/CheckUser/SharonHome ] 72 | then 73 | rm -f /tmp/CheckUser/SharonHome 74 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations//Phone/away?access_token=" -k 75 | fi 76 | fi 77 | 78 | if [ "$Dane" = Home ] 79 | then 80 | if [ ! -f /tmp/CheckUser/DaneHome ] 81 | then 82 | touch /tmp/CheckUser/DaneHome 83 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations//Phone/home?access_token=" -k 84 | fi 85 | else 86 | if [ -f /tmp/CheckUser/DaneHome ] 87 | then 88 | rm -f /tmp/CheckUser/DaneHome 89 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations//Phone/away?access_token=" -k 90 | fi 91 | fi 92 | 93 | if [ "$Scott" = Home ] 94 | then 95 | if [ ! -f /tmp/CheckUser/ScottHome ] 96 | then 97 | touch /tmp/CheckUser/ScottHome 98 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations//Phone/home?access_token=" -k 99 | fi 100 | else 101 | if [ -f /tmp/CheckUser/ScottHome ] 102 | then 103 | rm -f /tmp/CheckUser/ScottHome 104 | curl "https://graph-eu01-euwest1.api.smartthings.com/api/smartapps/installations//Phone/away?access_token=" -k 105 | fi 106 | fi 107 | -------------------------------------------------------------------------------- /smartapps/fuzzysb/asuswrt-wifi-presence.src/asuswrt-wifi-presence.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Stuart Buchanan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 10 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the License. 12 | * 13 | * AsusWRT Wifi Presence 14 | * 15 | * Author: Stuart Buchanan, Based on original work by midyear66 with thanks 16 | * 17 | * Date: 2016-02-01 v1.0 Initial Release 18 | */ 19 | definition( 20 | name: "AsusWRT Wifi Presence", 21 | namespace: "fuzzysb", 22 | author: "Stuart Buchanan", 23 | description: "Triggers Wifi Presence Status when HTTP GET Request is recieved", 24 | category: "My Apps", 25 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 26 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 27 | 28 | 29 | preferences { 30 | section(title: "Select Devices") { 31 | input "virtualPresence", "capability.presenceSensor", title: "Select Virtual Presence Sensor", required: true, multiple:false 32 | } 33 | 34 | } 35 | 36 | def installed() { 37 | createAccessToken() 38 | getToken() 39 | DEBUG("Installed Phone with rest api: $app.id") 40 | DEBUG("Installed Phone with token: $state.accessToken") 41 | DEBUG("Installed with settings: $virtualPresence.name") 42 | } 43 | def updated() { 44 | DEBUG("Updated Phone with rest api: $app.id") 45 | DEBUG("Updated Phone with token: $state.accessToken") 46 | } 47 | 48 | 49 | mappings { 50 | path("/Phone/home") { 51 | action: [ 52 | GET: "updatePresent" 53 | ] 54 | } 55 | path("/Phone/away") { 56 | action: [ 57 | GET:"updateNotPresent" 58 | ] 59 | } 60 | } 61 | 62 | 63 | // Callback functions 64 | def getSwitch() { 65 | // This returns the current state of the Presence sensor in JSON 66 | return virtualPresence.currentState("presence") 67 | } 68 | 69 | def updatePresent() { 70 | DEBUG("arrived") 71 | virtualPresence.arrived(); 72 | } 73 | 74 | def updateNotPresent() { 75 | DEBUG("departed") 76 | virtualPresence.departed(); 77 | } 78 | 79 | def getToken(){ 80 | if (!state.accessToken) { 81 | try { 82 | getAccessToken() 83 | DEBUG("Creating new Access Token: $state.accessToken") 84 | } catch (ex) { 85 | DEBUG("Did you forget to enable OAuth in SmartApp IDE settings") 86 | DEBUG(ex) 87 | } 88 | } 89 | } 90 | 91 | private def DEBUG(message){ 92 | log.debug message 93 | } -------------------------------------------------------------------------------- /smartapps/fuzzysb/asuswrt-wifi-presence.src/init-start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cru a CheckIfHome "* * * * * /jffs/scripts/CheckIfHome" 3 | cru a CheckIfHome15 "* * * * * sleep 15; /jffs/scripts/CheckIfHome" 4 | cru a CheckIfHome30 "* * * * * sleep 30; /jffs/scripts/CheckIfHome" 5 | cru a CheckIfHome45 "* * * * * sleep 45; /jffs/scripts/CheckIfHome" 6 | -------------------------------------------------------------------------------- /smartapps/fuzzysb/dummy-connect.src/dummy-connect.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Dummy (Connect) 3 | * 4 | * Copyright 2016 Stuart Buchanan 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: "Dummy (Connect)", 18 | namespace: "fuzzysb", 19 | author: "Stuart Buchanan", 20 | description: "Dummy App to Test Github integration", 21 | category: "SmartThings Labs", 22 | iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", 23 | iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", 24 | iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png") 25 | 26 | 27 | preferences { 28 | section("Title") { 29 | // TODO: put inputs here 30 | } 31 | } 32 | 33 | def installed() { 34 | log.debug "Installed with settings: ${settings}" 35 | 36 | initialize() 37 | } 38 | 39 | def updated() { 40 | log.debug "Updated with settings: ${settings}" 41 | 42 | unsubscribe() 43 | initialize() 44 | } 45 | 46 | def initialize() { 47 | // TODO: subscribe to attributes, devices, locations, etc. 48 | } 49 | 50 | // TODO: implement event handlers --------------------------------------------------------------------------------