@homebridge-plugins/homebridge-wemo 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +809 -0
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/config.schema.json +814 -0
- package/eslint.config.js +50 -0
- package/lib/connection/http.js +174 -0
- package/lib/connection/upnp.js +155 -0
- package/lib/device/coffee.js +167 -0
- package/lib/device/crockpot.js +380 -0
- package/lib/device/dimmer.js +280 -0
- package/lib/device/heater.js +416 -0
- package/lib/device/humidifier.js +379 -0
- package/lib/device/index.js +39 -0
- package/lib/device/insight.js +353 -0
- package/lib/device/lightswitch.js +154 -0
- package/lib/device/link-bulb.js +467 -0
- package/lib/device/link-hub.js +63 -0
- package/lib/device/maker-garage.js +426 -0
- package/lib/device/maker-switch.js +202 -0
- package/lib/device/motion.js +148 -0
- package/lib/device/outlet.js +148 -0
- package/lib/device/purifier.js +468 -0
- package/lib/device/simulation/purifier-insight.js +357 -0
- package/lib/device/simulation/purifier.js +159 -0
- package/lib/device/simulation/switch-insight.js +315 -0
- package/lib/device/simulation/switch.js +154 -0
- package/lib/fakegato/LICENSE +21 -0
- package/lib/fakegato/fakegato-history.js +814 -0
- package/lib/fakegato/fakegato-storage.js +108 -0
- package/lib/fakegato/fakegato-timer.js +125 -0
- package/lib/fakegato/uuid.js +27 -0
- package/lib/homebridge-ui/public/index.html +315 -0
- package/lib/homebridge-ui/server.js +10 -0
- package/lib/index.js +8 -0
- package/lib/platform.js +1423 -0
- package/lib/utils/colour.js +135 -0
- package/lib/utils/constants.js +129 -0
- package/lib/utils/eve-chars.js +130 -0
- package/lib/utils/functions.js +52 -0
- package/lib/utils/lang-en.js +125 -0
- package/package.json +70 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { parseStringPromise } from 'xml2js'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
hs2rgb,
|
|
5
|
+
rgb2hs,
|
|
6
|
+
rgb2xy,
|
|
7
|
+
xy2rgb,
|
|
8
|
+
} from '../utils/colour.js'
|
|
9
|
+
import platformConsts from '../utils/constants.js'
|
|
10
|
+
import { generateRandomString, parseError, sleep } from '../utils/functions.js'
|
|
11
|
+
import platformLang from '../utils/lang-en.js'
|
|
12
|
+
|
|
13
|
+
export default class {
|
|
14
|
+
constructor(platform, priAcc, accessory) {
|
|
15
|
+
// Set up variables from the platform
|
|
16
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
17
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
18
|
+
this.hapServ = platform.api.hap.Service
|
|
19
|
+
this.platform = platform
|
|
20
|
+
|
|
21
|
+
// Set up variables from the accessory
|
|
22
|
+
this.accessory = accessory
|
|
23
|
+
this.priAcc = priAcc
|
|
24
|
+
|
|
25
|
+
// Set up variables from the device
|
|
26
|
+
this.deviceID = accessory.context.deviceId
|
|
27
|
+
|
|
28
|
+
// Set up custom variables for this device type
|
|
29
|
+
const deviceConf = platform.deviceConf[this.deviceID] || {}
|
|
30
|
+
this.brightStep = deviceConf.brightnessStep
|
|
31
|
+
? Math.min(deviceConf.brightnessStep, 100)
|
|
32
|
+
: platformConsts.defaultValues.brightnessStep
|
|
33
|
+
this.alShift = deviceConf.adaptiveLightingShift || platformConsts.defaultValues.adaptiveLightingShift
|
|
34
|
+
this.transitionTime = deviceConf.transitionTime || platformConsts.defaultValues.transitionTime
|
|
35
|
+
|
|
36
|
+
// Objects containing mapping info for the device capabilities
|
|
37
|
+
this.linkCodes = {
|
|
38
|
+
switch: '10006',
|
|
39
|
+
brightness: '10008',
|
|
40
|
+
color: '10300',
|
|
41
|
+
temperature: '30301',
|
|
42
|
+
}
|
|
43
|
+
this.linkCodesRev = {
|
|
44
|
+
10600: 'switch',
|
|
45
|
+
10008: 'brightness',
|
|
46
|
+
10300: 'color',
|
|
47
|
+
30301: 'temperature',
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Quick check variables for later use
|
|
51
|
+
this.hasBrightSupport = accessory.context.capabilities[this.linkCodes.brightness]
|
|
52
|
+
this.hasColourSupport = accessory.context.capabilities[this.linkCodes.color]
|
|
53
|
+
&& deviceConf?.enableColourControl
|
|
54
|
+
this.hasCTempSupport = accessory.context.capabilities[this.linkCodes.temperature]
|
|
55
|
+
|
|
56
|
+
// Add the lightbulb service if it doesn't already exist
|
|
57
|
+
this.service = this.accessory.getService(this.hapServ.Lightbulb)
|
|
58
|
+
|| this.accessory.addService(this.hapServ.Lightbulb)
|
|
59
|
+
|
|
60
|
+
// If adaptive lighting has just been disabled then remove and re-add service to hide AL icon
|
|
61
|
+
if (this.alShift === -1 && this.accessory.context.adaptiveLighting) {
|
|
62
|
+
this.accessory.removeService(this.service)
|
|
63
|
+
this.service = this.accessory.addService(this.hapServ.Lightbulb)
|
|
64
|
+
this.accessory.context.adaptiveLighting = false
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add the set handler to the lightbulb on/off characteristic
|
|
68
|
+
this.service
|
|
69
|
+
.getCharacteristic(this.hapChar.On)
|
|
70
|
+
.removeOnSet()
|
|
71
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
72
|
+
|
|
73
|
+
// Add the set handler to the brightness characteristic if supported
|
|
74
|
+
if (this.hasBrightSupport) {
|
|
75
|
+
this.service
|
|
76
|
+
.getCharacteristic(this.hapChar.Brightness)
|
|
77
|
+
.setProps({ minStep: this.brightStep })
|
|
78
|
+
.onSet(async (value) => {
|
|
79
|
+
await this.internalBrightnessUpdate(value)
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Add the set handler to the colour temperature characteristic if supported
|
|
84
|
+
if (this.hasColourSupport) {
|
|
85
|
+
this.service.getCharacteristic(this.hapChar.Hue).onSet(async (value) => {
|
|
86
|
+
await this.internalColourUpdate(value)
|
|
87
|
+
})
|
|
88
|
+
this.cacheHue = this.service.getCharacteristic(this.hapChar.Hue).value
|
|
89
|
+
this.cacheSat = this.service.getCharacteristic(this.hapChar.Saturation).value
|
|
90
|
+
} else {
|
|
91
|
+
if (this.service.testCharacteristic(this.hapChar.Hue)) {
|
|
92
|
+
this.service.removeCharacteristic(this.service.getCharacteristic(this.hapChar.Hue))
|
|
93
|
+
}
|
|
94
|
+
if (this.service.testCharacteristic(this.hapChar.Saturation)) {
|
|
95
|
+
this.service.removeCharacteristic(this.service.getCharacteristic(this.hapChar.Saturation))
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Add the set handler to the colour temperature characteristic if supported
|
|
100
|
+
if (this.hasCTempSupport) {
|
|
101
|
+
this.service.getCharacteristic(this.hapChar.ColorTemperature).onSet(async (value) => {
|
|
102
|
+
await this.internalCTUpdate(value)
|
|
103
|
+
})
|
|
104
|
+
this.cacheMired = this.service.getCharacteristic(this.hapChar.ColorTemperature).value
|
|
105
|
+
|
|
106
|
+
// Add support for adaptive lighting if not disabled by user
|
|
107
|
+
if (this.alShift !== -1) {
|
|
108
|
+
this.alController = new platform.api.hap.AdaptiveLightingController(this.service, {
|
|
109
|
+
customTemperatureAdjustment: this.alShift,
|
|
110
|
+
})
|
|
111
|
+
this.accessory.configureController(this.alController)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Output the customised options to the log
|
|
116
|
+
const opts = JSON.stringify({
|
|
117
|
+
adaptiveLightingShift: this.alShift,
|
|
118
|
+
brightnessStep: this.brightStep,
|
|
119
|
+
transitionTime: this.transitionTime,
|
|
120
|
+
})
|
|
121
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
122
|
+
|
|
123
|
+
// Request a device update immediately
|
|
124
|
+
this.requestDeviceUpdate()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
receiveDeviceUpdate(attribute) {
|
|
128
|
+
// Log the receiving update if debug is enabled
|
|
129
|
+
this.accessory.logDebug(`${platformLang.recUpd} [${this.linkCodesRev[attribute.name]}: ${attribute.value}]`)
|
|
130
|
+
|
|
131
|
+
// Check which attribute we are getting
|
|
132
|
+
switch (attribute.name) {
|
|
133
|
+
case this.linkCodes.switch:
|
|
134
|
+
// Need a HomeKit true/false value for the state update
|
|
135
|
+
this.externalStateUpdate(Number.parseInt(attribute.value, 10) !== 0)
|
|
136
|
+
break
|
|
137
|
+
case this.linkCodes.brightness:
|
|
138
|
+
// Need a HomeKit int value for the brightness update
|
|
139
|
+
this.externalBrightnessUpdate(Math.round(attribute.value.split(':').shift() / 2.55))
|
|
140
|
+
break
|
|
141
|
+
case this.linkCodes.color: {
|
|
142
|
+
if (this.hasColourSupport) {
|
|
143
|
+
// Need a HomeKit int values for the colour update
|
|
144
|
+
const xy = attribute.value.split(':')
|
|
145
|
+
this.externalColourUpdate(xy[0], xy[1])
|
|
146
|
+
}
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
case this.linkCodes.temperature:
|
|
150
|
+
// Need a HomeKit int value for the colour temperature update
|
|
151
|
+
this.externalCTUpdate(Math.round(attribute.value.split(':').shift()))
|
|
152
|
+
break
|
|
153
|
+
default:
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async sendDeviceUpdate(capability, value) {
|
|
158
|
+
// Log the sending update if debug is enabled
|
|
159
|
+
this.accessory.logDebug(`${platformLang.senUpd} [${capability}: ${value}]`)
|
|
160
|
+
|
|
161
|
+
// Send the update
|
|
162
|
+
await this.priAcc.control.sendDeviceUpdate(
|
|
163
|
+
this.accessory.context.serialNumber,
|
|
164
|
+
capability,
|
|
165
|
+
value,
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async requestDeviceUpdate() {
|
|
170
|
+
try {
|
|
171
|
+
// Request the update via the main (hidden) accessory
|
|
172
|
+
const data = await this.priAcc.control.requestDeviceUpdate(
|
|
173
|
+
this.accessory.context.serialNumber,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
// Parse the response
|
|
177
|
+
const res = await parseStringPromise(data.DeviceStatusList, { explicitArray: false })
|
|
178
|
+
const deviceStatus = res.DeviceStatusList.DeviceStatus
|
|
179
|
+
const values = deviceStatus.CapabilityValue.split(',')
|
|
180
|
+
const caps = {}
|
|
181
|
+
deviceStatus.CapabilityID.split(',').forEach((val, index) => {
|
|
182
|
+
caps[val] = values[index]
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// If no capability values received then device must be offline
|
|
186
|
+
if (!caps[this.linkCodes.switch] || !caps[this.linkCodes.switch].length) {
|
|
187
|
+
this.accessory.logWarn(platformLang.devOffline)
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Need a HomeKit true/false value for the state update
|
|
192
|
+
if (caps[this.linkCodes.switch]) {
|
|
193
|
+
this.externalStateUpdate(Number.parseInt(caps[this.linkCodes.switch], 10) !== 0)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Need a HomeKit int value for the brightness update
|
|
197
|
+
if (caps[this.linkCodes.brightness] && this.hasBrightSupport) {
|
|
198
|
+
this.externalBrightnessUpdate(
|
|
199
|
+
Math.round(caps[this.linkCodes.brightness].split(':').shift() / 2.55),
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Need a HomeKit int value for the colour update
|
|
204
|
+
if (caps[this.linkCodes.color] && this.hasColourSupport) {
|
|
205
|
+
const xy = caps[this.linkCodes.color].split(':')
|
|
206
|
+
this.externalColourUpdate(xy[0], xy[1])
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Need a HomeKit int value for the colour temperature update
|
|
210
|
+
if (caps[this.linkCodes.temperature] && this.hasCTempSupport) {
|
|
211
|
+
this.externalCTUpdate(Math.round(caps[this.linkCodes.temperature].split(':').shift()))
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
const eText = parseError(err, [
|
|
215
|
+
platformLang.timeout,
|
|
216
|
+
platformLang.timeoutUnreach,
|
|
217
|
+
platformLang.noService,
|
|
218
|
+
])
|
|
219
|
+
this.accessory.logDebugWarn(`${platformLang.rduErr} ${eText}`)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async internalStateUpdate(value) {
|
|
224
|
+
try {
|
|
225
|
+
// Wait a longer time than the brightness so in scenes brightness is sent first
|
|
226
|
+
await sleep(500)
|
|
227
|
+
|
|
228
|
+
// Send the update
|
|
229
|
+
await this.sendDeviceUpdate(this.linkCodes.switch, value ? 1 : 0)
|
|
230
|
+
|
|
231
|
+
// Update the cache and log if appropriate
|
|
232
|
+
this.cacheState = value
|
|
233
|
+
this.accessory.log(`${platformLang.curState} [${value ? 'on' : 'off'}]`)
|
|
234
|
+
} catch (err) {
|
|
235
|
+
const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
|
|
236
|
+
this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
|
|
237
|
+
|
|
238
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
239
|
+
setTimeout(() => {
|
|
240
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState)
|
|
241
|
+
}, 2000)
|
|
242
|
+
throw new this.hapErr(-70402)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async internalBrightnessUpdate(value) {
|
|
247
|
+
try {
|
|
248
|
+
// Avoid multiple updates in quick succession
|
|
249
|
+
const updateKey = generateRandomString(5)
|
|
250
|
+
this.updateKeyBR = updateKey
|
|
251
|
+
await sleep(300)
|
|
252
|
+
if (updateKey !== this.updateKeyBR) {
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Don't continue if this value is same as before
|
|
257
|
+
if (this.cacheBright === value) {
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Send the update - value = brightness:transition_time
|
|
262
|
+
await this.sendDeviceUpdate(
|
|
263
|
+
this.linkCodes.brightness,
|
|
264
|
+
`${value * 2.55}:${this.transitionTime}`,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
// Update the cache and log if appropriate
|
|
268
|
+
this.cacheBright = value
|
|
269
|
+
this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
|
|
270
|
+
} catch (err) {
|
|
271
|
+
const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
|
|
272
|
+
this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
|
|
273
|
+
|
|
274
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
275
|
+
setTimeout(() => {
|
|
276
|
+
this.service.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
|
|
277
|
+
}, 2000)
|
|
278
|
+
throw new this.hapErr(-70402)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async internalColourUpdate(value) {
|
|
283
|
+
try {
|
|
284
|
+
// Avoid multiple updates in quick succession
|
|
285
|
+
const updateKey = generateRandomString(5)
|
|
286
|
+
this.updateKeyHue = updateKey
|
|
287
|
+
await sleep(400)
|
|
288
|
+
if (updateKey !== this.updateKeyHue) {
|
|
289
|
+
return
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Don't continue if this value is same as before
|
|
293
|
+
if (this.cacheHue === value) {
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// First convert to RGB
|
|
298
|
+
const currentSat = this.service.getCharacteristic(this.hapChar.Saturation).value
|
|
299
|
+
const [r, g, b] = hs2rgb(value, currentSat)
|
|
300
|
+
|
|
301
|
+
// Then convert the RGB to the values needed for Wemo
|
|
302
|
+
const [x, y] = rgb2xy(r, g, b)
|
|
303
|
+
const X = Math.round(x * 65535)
|
|
304
|
+
const Y = Math.round(y * 65535)
|
|
305
|
+
|
|
306
|
+
// Send the update - value = ct:transition_time
|
|
307
|
+
await this.sendDeviceUpdate(this.linkCodes.color, `${X}:${Y}:${this.transitionTime}`)
|
|
308
|
+
|
|
309
|
+
// Update the cache and log if appropriate
|
|
310
|
+
this.cacheHue = value
|
|
311
|
+
this.cacheSat = currentSat
|
|
312
|
+
this.cacheMired = 0
|
|
313
|
+
this.accessory.log(`${platformLang.curColour} [X:${X} Y:${Y}]`)
|
|
314
|
+
} catch (err) {
|
|
315
|
+
const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
|
|
316
|
+
this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
|
|
317
|
+
|
|
318
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
319
|
+
setTimeout(() => {
|
|
320
|
+
this.service.updateCharacteristic(this.hapChar.Hue, this.cacheHue)
|
|
321
|
+
}, 2000)
|
|
322
|
+
throw new this.hapErr(-70402)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async internalCTUpdate(value) {
|
|
327
|
+
try {
|
|
328
|
+
// Avoid multiple updates in quick succession
|
|
329
|
+
const updateKey = generateRandomString(5)
|
|
330
|
+
this.updateKeyCT = updateKey
|
|
331
|
+
await sleep(400)
|
|
332
|
+
if (updateKey !== this.updateKeyCT) {
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Value needs to be between 170 and 370
|
|
337
|
+
value = Math.min(Math.max(value, 170), 370)
|
|
338
|
+
|
|
339
|
+
// Don't continue if this value is same as before
|
|
340
|
+
if (this.cacheMired === value) {
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Send the update - value = ct:transition_time
|
|
345
|
+
await this.sendDeviceUpdate(this.linkCodes.temperature, `${value}:${this.transitionTime}`)
|
|
346
|
+
|
|
347
|
+
// Update the cache and log if appropriate
|
|
348
|
+
this.cacheMired = value
|
|
349
|
+
this.cacheHue = 0
|
|
350
|
+
this.cacheSat = 0
|
|
351
|
+
// Convert mired value to kelvin for logging
|
|
352
|
+
const mToK = Math.round(1000000 / value)
|
|
353
|
+
if (this.alController?.isAdaptiveLightingActive()) {
|
|
354
|
+
this.accessory.log(`${platformLang.curCCT} [${mToK}K / ${value}M] ${platformLang.viaAL}`)
|
|
355
|
+
} else {
|
|
356
|
+
this.accessory.log(`${platformLang.curCCT} [${mToK}K / ${value}M]`)
|
|
357
|
+
}
|
|
358
|
+
} catch (err) {
|
|
359
|
+
const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
|
|
360
|
+
this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
|
|
361
|
+
|
|
362
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
363
|
+
setTimeout(() => {
|
|
364
|
+
this.service.updateCharacteristic(this.hapChar.ColorTemperature, this.cacheMired)
|
|
365
|
+
}, 2000)
|
|
366
|
+
throw new this.hapErr(-70402)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
externalStateUpdate(value) {
|
|
371
|
+
try {
|
|
372
|
+
// Don't continue if the state is the same as before
|
|
373
|
+
if (value === this.cacheState) {
|
|
374
|
+
return
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Update the state HomeKit characteristic
|
|
378
|
+
this.service.updateCharacteristic(this.hapChar.On, value)
|
|
379
|
+
|
|
380
|
+
// Update the cache and log if appropriate
|
|
381
|
+
this.cacheState = value
|
|
382
|
+
this.accessory.log(`${platformLang.curState} [${value ? 'on' : 'off'}]`)
|
|
383
|
+
} catch (err) {
|
|
384
|
+
this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
externalBrightnessUpdate(value) {
|
|
389
|
+
try {
|
|
390
|
+
// Don't continue if the brightness is the same as before
|
|
391
|
+
if (value === this.cacheBright) {
|
|
392
|
+
return
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Update the brightness HomeKit characteristic
|
|
396
|
+
this.service.updateCharacteristic(this.hapChar.Brightness, value)
|
|
397
|
+
|
|
398
|
+
// Update the cache and log if appropriate
|
|
399
|
+
this.cacheBright = value
|
|
400
|
+
this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
|
|
401
|
+
} catch (err) {
|
|
402
|
+
this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
externalColourUpdate(valueX, valueY) {
|
|
407
|
+
try {
|
|
408
|
+
// Convert the given values to RGB and hue/saturation
|
|
409
|
+
const [r, g, b] = xy2rgb(valueX / 65535, valueY / 65535)
|
|
410
|
+
const [h, s] = rgb2hs(r, g, b)
|
|
411
|
+
|
|
412
|
+
// Don't continue if the hue and saturation are the same as before
|
|
413
|
+
if (this.cacheHue !== h || this.cacheSat !== s) {
|
|
414
|
+
// Update the HomeKit characteristics
|
|
415
|
+
this.service.updateCharacteristic(this.hapChar.ColorTemperature, 140)
|
|
416
|
+
this.service.updateCharacteristic(this.hapChar.Hue, h)
|
|
417
|
+
this.service.updateCharacteristic(this.hapChar.Saturation, s)
|
|
418
|
+
|
|
419
|
+
// Update the cache values
|
|
420
|
+
this.cacheMired = 0
|
|
421
|
+
this.cacheHue = h
|
|
422
|
+
this.cacheSat = s
|
|
423
|
+
|
|
424
|
+
// Log the change if appropriate
|
|
425
|
+
this.accessory.log(`${platformLang.curColour} [X:${valueX} Y:${valueY}]`)
|
|
426
|
+
|
|
427
|
+
// Colour chosen externally so disable adaptive lighting
|
|
428
|
+
if (this.alController?.isAdaptiveLightingActive()) {
|
|
429
|
+
this.alController.disableAdaptiveLighting()
|
|
430
|
+
this.accessory.log(platformLang.alDisabled)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
} catch (err) {
|
|
434
|
+
this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
externalCTUpdate(value) {
|
|
439
|
+
try {
|
|
440
|
+
// Don't continue if the mired value is the same as before
|
|
441
|
+
if (value === this.cacheMired) {
|
|
442
|
+
return
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Update the mired HomeKit characteristic
|
|
446
|
+
this.service.updateCharacteristic(this.hapChar.ColorTemperature, value)
|
|
447
|
+
|
|
448
|
+
// Log the change if appropriate
|
|
449
|
+
const mToK = Math.round(1000000 / value)
|
|
450
|
+
this.accessory.log(`${platformLang.curCCT} [${mToK}K / ${value}M]`)
|
|
451
|
+
|
|
452
|
+
// If the difference is significant (>20) then disable adaptive lighting
|
|
453
|
+
if (!Number.isNaN(this.cacheMired)) {
|
|
454
|
+
const diff = Math.abs(value - this.cacheMired) > 20
|
|
455
|
+
if (this.alController?.isAdaptiveLightingActive() && diff) {
|
|
456
|
+
this.alController.disableAdaptiveLighting()
|
|
457
|
+
this.accessory.log(platformLang.alDisabled)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Update the cache value after the adaptive lighting check
|
|
462
|
+
this.cacheMired = value
|
|
463
|
+
} catch (err) {
|
|
464
|
+
this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { create as xmlCreate } from 'xmlbuilder'
|
|
2
|
+
|
|
3
|
+
export default class {
|
|
4
|
+
constructor(platform, accessory, devicesInHB) {
|
|
5
|
+
// Set up variables from the platform
|
|
6
|
+
this.devicesInHB = devicesInHB
|
|
7
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
8
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
9
|
+
this.hapServ = platform.api.hap.Service
|
|
10
|
+
this.platform = platform
|
|
11
|
+
|
|
12
|
+
// Set up variables from the accessory
|
|
13
|
+
this.accessory = accessory
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
receiveDeviceUpdate(deviceId, attribute) {
|
|
17
|
+
// Find the accessory to which this relates
|
|
18
|
+
this.devicesInHB.forEach((accessory) => {
|
|
19
|
+
if (
|
|
20
|
+
accessory.context.serialNumber === deviceId
|
|
21
|
+
&& accessory.control?.receiveDeviceUpdate
|
|
22
|
+
) {
|
|
23
|
+
accessory.control.receiveDeviceUpdate(attribute)
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async sendDeviceUpdate(deviceId, capability, value) {
|
|
29
|
+
// Generate the XML to send
|
|
30
|
+
const deviceStatusList = xmlCreate('DeviceStatus', {
|
|
31
|
+
version: '1.0',
|
|
32
|
+
encoding: 'utf-8',
|
|
33
|
+
})
|
|
34
|
+
.ele({
|
|
35
|
+
IsGroupAction: deviceId.length === 10 ? 'YES' : 'NO',
|
|
36
|
+
DeviceID: deviceId,
|
|
37
|
+
CapabilityID: capability,
|
|
38
|
+
CapabilityValue: value,
|
|
39
|
+
})
|
|
40
|
+
.end()
|
|
41
|
+
|
|
42
|
+
// Send the update
|
|
43
|
+
return this.platform.httpClient.sendDeviceUpdate(
|
|
44
|
+
this.accessory,
|
|
45
|
+
'urn:Belkin:service:bridge:1',
|
|
46
|
+
'SetDeviceStatus',
|
|
47
|
+
{
|
|
48
|
+
DeviceStatusList: { '#text': deviceStatusList },
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async requestDeviceUpdate(deviceId) {
|
|
54
|
+
return this.platform.httpClient.sendDeviceUpdate(
|
|
55
|
+
this.accessory,
|
|
56
|
+
'urn:Belkin:service:bridge:1',
|
|
57
|
+
'GetDeviceStatus',
|
|
58
|
+
{
|
|
59
|
+
DeviceIDs: deviceId,
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
}
|