Update thermostat-mode-director.groovy
[smartapps.git] / third-party / ZWN-SC7.groovy
1 /**
2  *  ZWN-SC7 Enerwave 7 Button Scene Controller
3  *
4  *      Author: Matt Frank based on VRCS Button Controller by Brian Dahlem, based on SmartThings Button Controller
5  *      Date Created: 2014-12-18
6  *  Last Updated: 2015-02-13
7  *
8  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
9  *  in compliance with the License. You may obtain a copy of the License at:
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
14  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
15  *  for the specific language governing permissions and limitations under the License.
16  *
17  */
18
19 metadata {
20   // Automatically generated. Make future change here.
21   definition (name: "ZWN-SC7 Enerwave 7 Button Scene Controller", namespace: "mattjfrank", author: "Matt Frank") {
22     capability "Actuator"
23     capability "Button"
24     capability "Configuration"
25     capability "Indicator"
26     capability "Sensor"
27
28     attribute "currentButton", "STRING"
29     attribute "numButtons", "STRING"
30
31     fingerprint deviceId: "0x0202", inClusters:"0x21, 0x2D, 0x85, 0x86, 0x72"
32     fingerprint deviceId: "0x0202", inClusters:"0x2D, 0x85, 0x86, 0x72"
33   }
34
35   simulator {
36     status "button 1 pushed":  "command: 2B01, payload: 01 FF"
37     status "button 2 pushed":  "command: 2B01, payload: 02 FF"
38     status "button 3 pushed":  "command: 2B01, payload: 03 FF"
39     status "button 4 pushed":  "command: 2B01, payload: 04 FF"
40     status "button 5 pushed":  "command: 2B01, payload: 05 FF"
41     status "button 6 pushed":  "command: 2B01, payload: 06 FF"
42     status "button 7 pushed":  "command: 2B01, payload: 07 FF"
43     status "button released":  "command: 2C02, payload: 00"
44   }
45
46   tiles {
47     standardTile("button", "device.button", width: 2, height: 2) {
48       state "default", label: " ", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
49       state "button 1", label: "1", icon: "st.Weather.weather14", backgroundColor: "#79b821"
50       state "button 2", label: "2", icon: "st.Weather.weather14", backgroundColor: "#79b821"
51       state "button 3", label: "3", icon: "st.Weather.weather14", backgroundColor: "#79b821"
52       state "button 4", label: "4", icon: "st.Weather.weather14", backgroundColor: "#79b821"
53       state "button 5", label: "5", icon: "st.Weather.weather14", backgroundColor: "#79b821"
54       state "button 6", label: "6", icon: "st.Weather.weather14", backgroundColor: "#79b821"
55       state "button 7", label: "7", icon: "st.Weather.weather14", backgroundColor: "#79b821"
56     }
57
58     // Configure button.  Syncronize the device capabilities that the UI provides
59     standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
60       state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
61     }
62
63     main "button"
64     details (["button", "configure"])
65   }
66 }
67
68 // parse events into attributes
69 def parse(String description) {
70   log.debug "Parsing '${description}'"
71
72   def result = null
73   def cmd = zwave.parse(description)
74   if (cmd) {
75     result = zwaveEvent(cmd)
76   }
77
78   return result
79 }
80
81 // Handle a button being pressed
82 def buttonEvent(button) {
83   button = button as Integer
84   def result = []
85
86   updateState("currentButton", "$button")
87
88   if (button > 0) {
89     // update the device state, recording the button press
90     result << createEvent(name: "button", value: "pushed", data: [buttonNumber: button], descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
91
92     // turn off the button LED
93     result << response(zwave.sceneActuatorConfV1.sceneActuatorConfReport(dimmingDuration: 255, level: 255, sceneId: 0))
94   }
95   else {
96     // update the device state, recording the button press
97     result << createEvent(name: "button", value: "default", descriptionText: "$device.displayName button was released", isStateChange: true)
98
99     result << response(zwave.sceneActuatorConfV1.sceneActuatorConfReport(dimmingDuration: 255, level: 255, sceneId: 0))
100   }
101
102   result
103 }
104
105 // A zwave command for a button press was received
106 def zwaveEvent(physicalgraph.zwave.commands.sceneactivationv1.SceneActivationSet cmd) {
107
108   // The controller likes to repeat the command... ignore repeats
109   if (state.lastScene == cmd.sceneId && (state.repeatCount < 4) && (now() - state.repeatStart < 1000)) {
110     log.debug "Button ${cmd.sceneId} repeat ${state.repeatCount}x ${now()}"
111     state.repeatCount = state.repeatCount + 1
112     createEvent([:])
113   }
114   else {
115     // If the button was really pressed, store the new scene and handle the button press
116     state.lastScene = cmd.sceneId
117     state.lastLevel = 0
118     state.repeatCount = 0
119     state.repeatStart = now()
120
121     buttonEvent(cmd.sceneId)
122   }
123 }
124
125 // A scene command was received -- it's probably scene 0, so treat it like a button release
126 def zwaveEvent(physicalgraph.zwave.commands.sceneactuatorconfv1.SceneActuatorConfGet cmd) {
127   buttonEvent(cmd.sceneId)
128 }
129
130 // The controller sent a scene activation report.  Log it, but it really shouldn't happen.
131 def zwaveEvent(physicalgraph.zwave.commands.sceneactuatorconfv1.SceneActuatorConfReport cmd) {
132   log.debug "Scene activation report"
133   log.debug "Scene ${cmd.sceneId} set to ${cmd.level}"
134
135   createEvent([:])
136 }
137
138
139 // Configuration Reports are replys to configuration value requests... If we knew what configuration parameters
140 // to request this could be very helpful.
141 def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
142   createEvent([:])
143 }
144
145 // The VRC supports hail commands, but I haven't seen them.
146 def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
147   createEvent([name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false])
148 }
149
150 // Update manufacturer information when it is reported
151 def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
152   if (state.manufacturer != cmd.manufacturerName) {
153     updateDataValue("manufacturer", cmd.manufacturerName)
154   }
155
156   createEvent([:])
157 }
158
159 // Association Groupings Reports tell us how many groupings the device supports.  This equates to the number of
160 // buttons/scenes in the VRCS
161 def zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationGroupingsReport cmd) {
162   def response = []
163
164   log.debug "${getDataByName("numButtons")} buttons stored"
165   if (getDataByName("numButtons") != "$cmd.supportedGroupings") {
166     updateState("numButtons", "$cmd.supportedGroupings")
167     log.debug "${cmd.supportedGroupings} groups available"
168     response << createEvent(name: "numButtons", value: cmd.supportedGroupings, displayed: false)
169
170     response << associateHub()
171   }
172   else {
173     response << createEvent(name: "numButtons", value: cmd.supportedGroupings, displayed: false)
174   }
175
176   return response
177 }
178
179
180 // Handles all Z-Wave commands we don't know we are interested in
181 def zwaveEvent(physicalgraph.zwave.Command cmd) {
182   createEvent([:])
183 }
184
185 // handle commands
186
187 // Create a list of the configuration commands to send to the device
188 def configurationCmds() {
189   // Always check the manufacturer and the number of groupings allowed
190   def commands = [
191   zwave.manufacturerSpecificV1.manufacturerSpecificGet().format(),
192   zwave.associationV1.associationGroupingsGet().format(),
193   zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:1, sceneId:1).format(),
194   zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:2, sceneId:2).format(),
195   zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:3, sceneId:3).format(),
196   zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:4, sceneId:4).format(),
197   zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:5, sceneId:5).format(),
198   zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:6, sceneId:6).format(),
199   zwave.sceneControllerConfV1.sceneControllerConfSet(groupId:7, sceneId:7).format()
200   ]
201
202   commands << associateHub()
203
204   delayBetween(commands)
205 }
206
207 // Configure the device
208 def configure() {
209   def cmd=configurationCmds()
210   log.debug("Sending configuration: ${cmd}")
211   return cmd
212 }
213
214
215 //
216 // Associate the hub with the buttons on the device, so we will get status updates
217 def associateHub() {
218   def commands = []
219
220   // Loop through all the buttons on the controller
221   for (def buttonNum = 1; buttonNum <= integer(getDataByName("numButtons")); buttonNum++) {
222
223     // Associate the hub with the button so we will get status updates
224     commands << zwave.associationV1.associationSet(groupingIdentifier: buttonNum, nodeId: zwaveHubNodeId).format()
225
226   }
227
228   return commands
229 }
230
231 // Update State
232 // Store mode and settings
233 def updateState(String name, String value) {
234   state[name] = value
235   device.updateDataValue(name, value)
236 }
237
238 // Get Data By Name
239 // Given the name of a setting/attribute, lookup the setting's value
240 def getDataByName(String name) {
241   state[name] ?: device.getDataValue(name)
242 }
243
244 //Stupid conversions
245
246 // convert a double to an integer
247 def integer(double v) {
248   return v.toInteger()
249 }
250
251 // convert a hex string to integer
252 def integerhex(String v) {
253   if (v == null) {
254     return 0
255   }
256
257   return Integer.parseInt(v, 16)
258 }
259
260 // convert a hex string to integer
261 def integer(String v) {
262   if (v == null) {
263     return 0
264   }
265
266   return Integer.parseInt(v)
267 }