-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathzwaveTools.parameterManagementTools.groovy
187 lines (143 loc) · 8.5 KB
/
zwaveTools.parameterManagementTools.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
library (
base: "driver",
author: "jvm33",
category: "zwave",
description: "Tools to manage device parameters and other device-specific data.",
name: "parameterManagementTools",
namespace: "zwaveTools",
documentationLink: "https://github.com/jvmahon/HubitatDriverTools",
version: "0.0.1",
dependencies: "",
librarySource:"https://github.com/jvmahon/HubitatDriverTools/blob/main/parameterManagementTools.groovy"
)
import java.util.concurrent.*
import groovy.transform.Field
void updated()
{
if (txtEnable) log.info "Device ${device.displayName}: Updating changed parameters (if any) . . ."
if (logEnable) runIn(1800,logsOff)
ConcurrentHashMap<Integer, BigInteger> parameterValueMap = getParameterValuesFromDevice()
if (parameterValueMap.is( null ))
{
log.error "In function updated, parameterValueMap is ${parameterValueMap}"
return
}
ConcurrentHashMap<Integer, BigInteger> pendingChanges = getPendingChangeMap()
Map<Integer, BigInteger> settingValueMap = getParameterValuesFromInputControls()
pendingChangeMap.putAll(settingValueMap.findAll{it.value} - parameterValueMap)
if (txtEnable) log.info "Device ${device.displayName}: new Setting values are ${settingValueMap}, Last Device Parameters were ${parameterValueMap}, Pending parameter changes are: ${pendingChanges ?: "None"}"
if (logEnable) log.debug "new pending change map is ${getPendingChangeMap()}"
processPendingChanges()
if (txtEnable) log.info "Device ${device.displayName}: Done updating changed parameters (if any) . . ."
}
void processPendingChanges()
{
if (txtEnable) log.info "Device ${device.displayName}: Processing Pending parameter changes: ${getPendingChangeMap()}"
// pendingChangeMap?.findAll{k, v -> !(v.is( null ))}.each{ k, v ->
pendingChangeMap?.each{ k, v ->
if (txtEnable) log.info "Updating parameter ${k} to value ${v}"
setParameter(parameterNumber: k , value: v)
}
}
void getParameter(parameterNumber) {
advancedZwaveSend( zwave.configurationV1.configurationGet(parameterNumber: parameterNumber))
hubitat.zwave.Command report = myReportQueue("7006").poll(5, TimeUnit.SECONDS)
if (logEnable) log.debug "Device ${device.displayName}: Received a parameter configuration report: ${report}."
}
void setParameter(parameterNumber, value = null ) {
if (parameterNumber && value) {
setParameter(parameterNumber:parameterNumber, value:value)
} else if (parameterNumber) {
getParameter(parameterNumber)
}
}
Boolean setParameter(Map params = [parameterNumber: null , value: null ] ){
if (params.parameterNumber.is( null ) || params.value.is( null ) ) {
log.warn "Device ${device.displayName}: Can't set parameter ${parameterNumber}, Incomplete parameter list supplied... syntax: setParameter(parameterNumber,size,value), received: setParameter(${parameterNumber}, ${size}, ${value})."
return false
}
String getThis = "${params.parameterNumber}" as String
Integer PSize = ( deviceInputs.get(getThis)?.size) ?: (deviceInputs.get(params.parameterNumber as Integer)?.size )
if (!PSize) {log.error "Device ${device.displayName}: Could not get parameter size in function setParameter. Defaulting to 1"; PSize = 1}
if (logEnable) log.debug "Sending a parameter update for: scaledConfigurationValue: ${params.value as BigInteger}, parameterNumber: ${params.parameterNumber}, size: ${PSize} "
advancedZwaveSend(zwave.configurationV1.configurationSet(scaledConfigurationValue: params.value as BigInteger, parameterNumber: params.parameterNumber, size: PSize))
// The 'get' should not be supervised!
advancedZwaveSend( zwave.configurationV1.configurationGet(parameterNumber: params.parameterNumber))
// Wait for the report that is returned after the configurationGet, and then update the input controls so they display the updated value.
Boolean success = myReportQueue("7006").poll(5, TimeUnit.SECONDS) ? true : false
}
// Gets a map of all the values currently stored in the input controls.
Map<Integer, BigInteger> getParameterValuesFromInputControls()
{
ConcurrentHashMap inputs = getDeviceInputs()
if (!inputs) return
Map<Integer, BigInteger> settingValues = [:]
inputs.each
{ PKey , PData ->
BigInteger newValue = 0
// if the setting returns an array, then it is a bitmap control, and add together the values.
if (settings.get(PData.name as String) instanceof ArrayList) {
settings.get(PData.name as String).each{ newValue += it as BigInteger }
} else {
newValue = settings.get(PData.name as String) as BigInteger
}
settingValues.put(PKey, newValue)
}
if (txtEnable) log.info "Device ${device.displayName}: Current Parameter Setting Values are: " + settingValues
return settingValues
}
@Field static ConcurrentHashMap<String, ConcurrentHashMap> allPendingParameterChanges = new ConcurrentHashMap<String, ConcurrentHashMap>(128, 0.75, 1)
@Field static ConcurrentHashMap<String, ConcurrentHashMap> allDevicesParameterValues = new ConcurrentHashMap<String, ConcurrentHashMap>(128, 0.75, 1)
ConcurrentHashMap getPendingChangeMap() {
return allPendingParameterChanges.get(device.deviceNetworkId, new ConcurrentHashMap(32, 0.75, 1) )
}
Map<Integer, BigInteger> getParameterValuesFromDevice()
{
ConcurrentHashMap parameterValues = allDevicesParameterValues.get(device.deviceNetworkId, new ConcurrentHashMap<Integer, BigInteger>(32, 0.75, 1))
ConcurrentHashMap inputs = getDeviceInputs()
if (!inputs) return null
if ((parameterValues?.size() as Integer) == (inputs?.size() as Integer) ) {
return parameterValues
} else {
Integer waitTime = 1
inputs.eachWithIndex
{ k, v, i ->
if (parameterValues.get(k as Integer).is( null ) ) {
if (txtEnable) log.info "Device ${device.displayName}: Obtaining value from Zwave device for parameter # ${k}"
advancedZwaveSend(zwave.configurationV2.configurationGet(parameterNumber: k))
// Wait 2 second for most of the reports, but wait up to 10 seconds for the last one.
waitTime = (i >= (inputs.size() -1 )) ? 10 : 2
myReportQueue("7006").poll(waitTime, TimeUnit.SECONDS)
} else {
// if (logEnable) log.debug "Device ${device.displayName}: For parameter: ${k} previously retrieved a value of ${parameterValues.get(k as Integer)}."
}
}
return parameterValues
}
return null
}
void zwaveEvent(hubitat.zwave.commands.configurationv2.ConfigurationReport cmd)
{
if (logEnable) log.debug "Received a configurationReport ${cmd}"
ConcurrentHashMap parameterValues = allDevicesParameterValues.get(device.deviceNetworkId, new ConcurrentHashMap<Integer, BigInteger>(32, 0.75, 1))
BigInteger newValue = (cmd.size == 1) ? cmd.configurationValue[0] : cmd.scaledConfigurationValue
if (newValue < 0) log.warn "Device ${device.displayName}: Negative configuration value reported for configuration parameter ${cmd.parameterNumber}."
parameterValues.put((cmd.parameterNumber as Integer), newValue )
pendingChangeMap.remove(cmd.parameterNumber as Integer)
if (txtEnable) log.info "Device ${device.displayName}: updating parameter: ${cmd.parameterNumber} to ${newValue}."
device.updateSetting("${cmd.parameterNumber}", newValue)
myReportQueue(cmd.CMD).offer( cmd )
(childDevices + this).each{ it.parse([[name:"parameterUpdate", report:cmd]])}
}
//////////////////////////////////////////////////////////////////////
////// Report Queues ///////
//////////////////////////////////////////////////////////////////////
// reportQueues stores a map of SynchronousQueues. When requesting a report from a device, the report handler communicates the report back to the requesting function using a queue. This makes programming more like "synchronous" programming, rather than asynchronous handling.
// This is a map within a map. The first level map is by deviceNetworkId. Since @Field static variables are shared among all devices using the same driver code, this ensures that you get a unique second-level map for a particular device. The second map is keyed by the report class hex string. For example, if you want to wait for the configurationGet report, wait for "7006".
@Field static reportQueues = new ConcurrentHashMap<String, ConcurrentHashMap>(128, 0.75, 1)
SynchronousQueue myReportQueue(String reportClass) {
ConcurrentHashMap thisDeviceQueues = reportQueues.get(device.deviceNetworkId, new ConcurrentHashMap<String,SynchronousQueue>(32, 0.75, 1))
// Get the queue if it exists, create (new) it if it does not.
SynchronousQueue thisReportQueue = thisDeviceQueues.get(reportClass, new SynchronousQueue())
return thisReportQueue
}