@homebridge-plugins/homebridge-meross 10.8.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 +1346 -0
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/config.schema.json +2066 -0
- package/eslint.config.js +49 -0
- package/lib/connection/http.js +345 -0
- package/lib/connection/mqtt.js +174 -0
- package/lib/device/baby.js +532 -0
- package/lib/device/cooler-single.js +447 -0
- package/lib/device/diffuser.js +730 -0
- package/lib/device/fan.js +530 -0
- package/lib/device/garage-main.js +225 -0
- package/lib/device/garage-single.js +495 -0
- package/lib/device/garage-sub.js +376 -0
- package/lib/device/heater-single.js +445 -0
- package/lib/device/hub-contact.js +56 -0
- package/lib/device/hub-leak.js +86 -0
- package/lib/device/hub-main.js +403 -0
- package/lib/device/hub-sensor.js +115 -0
- package/lib/device/hub-smoke.js +40 -0
- package/lib/device/hub-valve.js +377 -0
- package/lib/device/humidifier.js +521 -0
- package/lib/device/index.js +63 -0
- package/lib/device/light-cct.js +474 -0
- package/lib/device/light-dimmer.js +312 -0
- package/lib/device/light-rgb.js +528 -0
- package/lib/device/outlet-multi.js +383 -0
- package/lib/device/outlet-single.js +405 -0
- package/lib/device/power-strip.js +282 -0
- package/lib/device/purifier-single.js +372 -0
- package/lib/device/purifier.js +403 -0
- package/lib/device/roller-location.js +317 -0
- package/lib/device/roller.js +234 -0
- package/lib/device/sensor-presence.js +201 -0
- package/lib/device/switch-multi.js +403 -0
- package/lib/device/switch-single.js +371 -0
- package/lib/device/template.js +177 -0
- package/lib/device/thermostat.js +493 -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 +316 -0
- package/lib/homebridge-ui/server.js +10 -0
- package/lib/index.js +8 -0
- package/lib/platform.js +1256 -0
- package/lib/utils/colour.js +581 -0
- package/lib/utils/constants.js +377 -0
- package/lib/utils/custom-chars.js +165 -0
- package/lib/utils/eve-chars.js +130 -0
- package/lib/utils/functions.js +39 -0
- package/lib/utils/lang-en.js +114 -0
- package/package.json +70 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
import PQueue from 'p-queue'
|
|
2
|
+
import { TimeoutError } from 'p-timeout'
|
|
3
|
+
|
|
4
|
+
import mqttClient from '../connection/mqtt.js'
|
|
5
|
+
import platformConsts from '../utils/constants.js'
|
|
6
|
+
import {
|
|
7
|
+
generateRandomString,
|
|
8
|
+
hasProperty,
|
|
9
|
+
parseError,
|
|
10
|
+
sleep,
|
|
11
|
+
} from '../utils/functions.js'
|
|
12
|
+
import platformLang from '../utils/lang-en.js'
|
|
13
|
+
|
|
14
|
+
export default class {
|
|
15
|
+
constructor(platform, accessory, accessoryLight) {
|
|
16
|
+
// Set up variables from the platform
|
|
17
|
+
this.cusChar = platform.cusChar
|
|
18
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
19
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
20
|
+
this.hapServ = platform.api.hap.Service
|
|
21
|
+
this.platform = platform
|
|
22
|
+
|
|
23
|
+
// Set up variables from the accessory
|
|
24
|
+
this.accessory = accessory
|
|
25
|
+
this.accessoryLight = accessoryLight
|
|
26
|
+
this.name = accessory.displayName
|
|
27
|
+
const cloudRefreshRate = hasProperty(platform.config, 'cloudRefreshRate')
|
|
28
|
+
? platform.config.cloudRefreshRate
|
|
29
|
+
: platformConsts.defaultValues.cloudRefreshRate
|
|
30
|
+
const localRefreshRate = hasProperty(platform.config, 'refreshRate')
|
|
31
|
+
? platform.config.refreshRate
|
|
32
|
+
: platformConsts.defaultValues.refreshRate
|
|
33
|
+
this.pollInterval = accessory.context.connection === 'local'
|
|
34
|
+
? localRefreshRate
|
|
35
|
+
: cloudRefreshRate
|
|
36
|
+
|
|
37
|
+
this.channelList = {
|
|
38
|
+
1: 'Cicada Chirping',
|
|
39
|
+
2: 'Rain Sound',
|
|
40
|
+
3: 'Ripple Sound',
|
|
41
|
+
4: 'Birdsong',
|
|
42
|
+
5: 'Lullaby',
|
|
43
|
+
6: 'Fan Sound',
|
|
44
|
+
7: 'Crystal Ball',
|
|
45
|
+
8: 'Music Box',
|
|
46
|
+
9: 'White Noise',
|
|
47
|
+
10: 'Thunder',
|
|
48
|
+
11: 'Ocean Wave',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.volumeHK2MR = (input) => {
|
|
52
|
+
if (input === 0) {
|
|
53
|
+
return 0
|
|
54
|
+
}
|
|
55
|
+
if (input < 7) {
|
|
56
|
+
return 1
|
|
57
|
+
}
|
|
58
|
+
if (input < 13) {
|
|
59
|
+
return 2
|
|
60
|
+
}
|
|
61
|
+
if (input < 19) {
|
|
62
|
+
return 3
|
|
63
|
+
}
|
|
64
|
+
if (input < 25) {
|
|
65
|
+
return 4
|
|
66
|
+
}
|
|
67
|
+
if (input < 31) {
|
|
68
|
+
return 5
|
|
69
|
+
}
|
|
70
|
+
if (input < 37) {
|
|
71
|
+
return 6
|
|
72
|
+
}
|
|
73
|
+
if (input < 43) {
|
|
74
|
+
return 7
|
|
75
|
+
}
|
|
76
|
+
if (input < 49) {
|
|
77
|
+
return 8
|
|
78
|
+
}
|
|
79
|
+
if (input < 55) {
|
|
80
|
+
return 9
|
|
81
|
+
}
|
|
82
|
+
if (input < 61) {
|
|
83
|
+
return 10
|
|
84
|
+
}
|
|
85
|
+
if (input < 67) {
|
|
86
|
+
return 11
|
|
87
|
+
}
|
|
88
|
+
if (input < 73) {
|
|
89
|
+
return 12
|
|
90
|
+
}
|
|
91
|
+
if (input < 79) {
|
|
92
|
+
return 13
|
|
93
|
+
}
|
|
94
|
+
if (input < 85) {
|
|
95
|
+
return 14
|
|
96
|
+
}
|
|
97
|
+
if (input < 91) {
|
|
98
|
+
return 15
|
|
99
|
+
}
|
|
100
|
+
return 16
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.volumeMR2HK = (input) => {
|
|
104
|
+
if (input === 16) {
|
|
105
|
+
return 100
|
|
106
|
+
}
|
|
107
|
+
return input * 6
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Add the tv service if it doesn't already exist
|
|
111
|
+
this.service = this.accessory.getService(this.hapServ.Television)
|
|
112
|
+
|| this.accessory.addService(this.hapServ.Television)
|
|
113
|
+
|
|
114
|
+
// Add a lightbulb service to the light accessory if it doesn't exist
|
|
115
|
+
this.serviceLight = this.accessoryLight.getService(this.hapServ.Lightbulb)
|
|
116
|
+
|| this.accessoryLight.addService(this.hapServ.Lightbulb)
|
|
117
|
+
|
|
118
|
+
// Remove any old tv speaker services
|
|
119
|
+
if (this.accessory.getService(this.hapServ.TelevisionSpeaker)) {
|
|
120
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.TelevisionSpeaker))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Add the tv speaker (fan) service if it doesn't already exist
|
|
124
|
+
this.speakerService = this.accessory.getService(this.hapServ.Fan) || this.accessory.addService(this.hapServ.Fan)
|
|
125
|
+
|
|
126
|
+
// Add the set handler to the tv active characteristic
|
|
127
|
+
this.service
|
|
128
|
+
.getCharacteristic(this.hapChar.Active)
|
|
129
|
+
.onSet(async value => this.internalActiveUpdate(value))
|
|
130
|
+
this.cacheState = this.service.getCharacteristic(this.hapChar.Active).value
|
|
131
|
+
this.cacheStateMR = this.cacheState === 1 ? 0 : 1
|
|
132
|
+
|
|
133
|
+
// Add the set handler to the switch active identifier characteristic
|
|
134
|
+
this.service
|
|
135
|
+
.getCharacteristic(this.hapChar.ActiveIdentifier)
|
|
136
|
+
.onSet(async value => this.internalChannelUpdate(value))
|
|
137
|
+
this.cacheChannel = this.service.getCharacteristic(this.hapChar.ActiveIdentifier).value
|
|
138
|
+
|
|
139
|
+
// Handle volume control
|
|
140
|
+
this.speakerService
|
|
141
|
+
.getCharacteristic(this.hapChar.RotationSpeed)
|
|
142
|
+
.onSet(async value => this.internalVolumeUpdate(value))
|
|
143
|
+
this.cacheVolume = this.speakerService.getCharacteristic(this.hapChar.RotationSpeed).value
|
|
144
|
+
this.cacheVolumeMR = this.volumeHK2MR(this.cacheVolume)
|
|
145
|
+
|
|
146
|
+
Object.entries(this.channelList).forEach((entry) => {
|
|
147
|
+
const [id, scene] = entry
|
|
148
|
+
const service = this.accessory.getService(scene)
|
|
149
|
+
|| this.accessory.addService(this.hapServ.InputSource, scene, id)
|
|
150
|
+
service
|
|
151
|
+
.setCharacteristic(this.hapChar.Identifier, id)
|
|
152
|
+
.setCharacteristic(this.hapChar.ConfiguredName, scene)
|
|
153
|
+
.setCharacteristic(this.hapChar.IsConfigured, 1)
|
|
154
|
+
.setCharacteristic(this.hapChar.InputSourceType, 3)
|
|
155
|
+
this.service.addLinkedService(service)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
// Add the baby scene custom characteristics
|
|
159
|
+
if (!this.serviceLight.testCharacteristic(this.cusChar.BabySceneOne)) {
|
|
160
|
+
this.serviceLight.addCharacteristic(this.cusChar.BabySceneOne)
|
|
161
|
+
}
|
|
162
|
+
this.serviceLight
|
|
163
|
+
.getCharacteristic(this.cusChar.BabySceneOne)
|
|
164
|
+
.onSet(async value => this.internalSceneUpdate(value, 3, 'BabySceneOne'))
|
|
165
|
+
if (!this.serviceLight.testCharacteristic(this.cusChar.BabySceneTwo)) {
|
|
166
|
+
this.serviceLight.addCharacteristic(this.cusChar.BabySceneTwo)
|
|
167
|
+
}
|
|
168
|
+
this.serviceLight
|
|
169
|
+
.getCharacteristic(this.cusChar.BabySceneTwo)
|
|
170
|
+
.onSet(async value => this.internalSceneUpdate(value, 4, 'BabySceneTwo'))
|
|
171
|
+
if (!this.serviceLight.testCharacteristic(this.cusChar.BabySceneThree)) {
|
|
172
|
+
this.serviceLight.addCharacteristic(this.cusChar.BabySceneThree)
|
|
173
|
+
}
|
|
174
|
+
this.serviceLight
|
|
175
|
+
.getCharacteristic(this.cusChar.BabySceneThree)
|
|
176
|
+
.onSet(async value => this.internalSceneUpdate(value, 1, 'BabySceneThree'))
|
|
177
|
+
if (!this.serviceLight.testCharacteristic(this.cusChar.BabySceneFour)) {
|
|
178
|
+
this.serviceLight.addCharacteristic(this.cusChar.BabySceneFour)
|
|
179
|
+
}
|
|
180
|
+
this.serviceLight
|
|
181
|
+
.getCharacteristic(this.cusChar.BabySceneFour)
|
|
182
|
+
.onSet(async value => this.internalSceneUpdate(value, 2, 'BabySceneFour'))
|
|
183
|
+
|
|
184
|
+
// Create the queue used for sending device requests
|
|
185
|
+
this.updateInProgress = false
|
|
186
|
+
this.queue = new PQueue({
|
|
187
|
+
concurrency: 1,
|
|
188
|
+
interval: 250,
|
|
189
|
+
intervalCap: 1,
|
|
190
|
+
timeout: 10000,
|
|
191
|
+
throwOnTimeout: true,
|
|
192
|
+
})
|
|
193
|
+
this.queue.on('idle', () => {
|
|
194
|
+
this.updateInProgress = false
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// Set up the mqtt client for cloud devices to send and receive device updates
|
|
198
|
+
if (accessory.context.connection !== 'local') {
|
|
199
|
+
this.accessory.mqtt = new mqttClient(platform, this.accessory)
|
|
200
|
+
this.accessory.mqtt.connect()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Always request a device update on startup, then start the interval for polling
|
|
204
|
+
setTimeout(() => this.requestUpdate(true), 2000)
|
|
205
|
+
this.accessory.refreshInterval = setInterval(
|
|
206
|
+
() => this.requestUpdate(),
|
|
207
|
+
this.pollInterval * 1000,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
// Output the customised options to the log
|
|
211
|
+
const opts = JSON.stringify({
|
|
212
|
+
connection: this.accessory.context.connection,
|
|
213
|
+
})
|
|
214
|
+
platform.log('[%s] %s %s.', this.name, platformLang.devInitOpts, opts)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async internalActiveUpdate(value) {
|
|
218
|
+
try {
|
|
219
|
+
// Add the request to the queue so updates are sent apart
|
|
220
|
+
await this.queue.add(async () => {
|
|
221
|
+
// Don't continue if the state is the same as before
|
|
222
|
+
if (value === this.cacheState) {
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
227
|
+
this.updateInProgress = true
|
|
228
|
+
|
|
229
|
+
// Generate the namespace and payload
|
|
230
|
+
const namespace = 'Appliance.Control.Mp3'
|
|
231
|
+
const payload = {
|
|
232
|
+
mp3: {
|
|
233
|
+
mute: value === 1 ? 0 : 1,
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Use the platform function to send the update to the device
|
|
238
|
+
await this.platform.sendUpdate(this.accessory, {
|
|
239
|
+
namespace,
|
|
240
|
+
payload,
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
// Update the cache
|
|
244
|
+
this.cacheState = value
|
|
245
|
+
this.cacheStateMR = value === 1 ? 0 : 1
|
|
246
|
+
this.accessory.log(`${platformLang.curState} [${value === 1 ? 'on' : 'off'}]`)
|
|
247
|
+
|
|
248
|
+
// Also update the speaker (fan) service
|
|
249
|
+
this.speakerService.updateCharacteristic(this.hapChar.On, value === 1)
|
|
250
|
+
})
|
|
251
|
+
} catch (err) {
|
|
252
|
+
// Catch any errors whilst updating the device
|
|
253
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
254
|
+
this.accessory.logWarn(`${platformLang.sendFailed} ${eText}`)
|
|
255
|
+
setTimeout(() => {
|
|
256
|
+
this.service.updateCharacteristic(this.hapChar.Active, this.cacheState)
|
|
257
|
+
}, 2000)
|
|
258
|
+
throw new this.hapErr(-70402)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async internalChannelUpdate(value) {
|
|
263
|
+
try {
|
|
264
|
+
// Add the request to the queue so updates are sent apart
|
|
265
|
+
await this.queue.add(async () => {
|
|
266
|
+
// Don't continue if the state is the same as before
|
|
267
|
+
if (value === this.cacheChannel) {
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
272
|
+
this.updateInProgress = true
|
|
273
|
+
|
|
274
|
+
// Generate the namespace and payload
|
|
275
|
+
const namespace = 'Appliance.Control.Mp3'
|
|
276
|
+
const payload = {
|
|
277
|
+
mp3: {
|
|
278
|
+
song: value,
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Use the platform function to send the update to the device
|
|
283
|
+
await this.platform.sendUpdate(this.accessory, {
|
|
284
|
+
namespace,
|
|
285
|
+
payload,
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
// Update the cache
|
|
289
|
+
this.cacheChannel = value
|
|
290
|
+
this.accessory.log(`${platformLang.curSong} [${this.channelList[this.cacheChannel]}]`)
|
|
291
|
+
})
|
|
292
|
+
} catch (err) {
|
|
293
|
+
// Catch any errors whilst updating the device
|
|
294
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
295
|
+
this.accessory.logWarn(`${platformLang.sendFailed} ${eText}`)
|
|
296
|
+
setTimeout(() => {
|
|
297
|
+
this.service.updateCharacteristic(this.hapChar.ActiveIdentifier, this.cacheChannel)
|
|
298
|
+
}, 2000)
|
|
299
|
+
throw new this.hapErr(-70402)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async internalVolumeUpdate(value) {
|
|
304
|
+
try {
|
|
305
|
+
// Add the request to the queue so updates are sent apart
|
|
306
|
+
await this.queue.add(async () => {
|
|
307
|
+
// Avoid multiple changes in short space of time
|
|
308
|
+
const updateKey = generateRandomString(5)
|
|
309
|
+
this.updateKey = updateKey
|
|
310
|
+
await sleep(300)
|
|
311
|
+
if (updateKey !== this.updateKey) {
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Calculate the value Meross needs
|
|
316
|
+
const volumeMR = this.volumeHK2MR(value)
|
|
317
|
+
|
|
318
|
+
if (volumeMR === this.cacheVolumeMR) {
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
323
|
+
this.updateInProgress = true
|
|
324
|
+
|
|
325
|
+
// Generate the namespace and payload
|
|
326
|
+
const namespace = 'Appliance.Control.Mp3'
|
|
327
|
+
const payload = {
|
|
328
|
+
mp3: {
|
|
329
|
+
volume: volumeMR,
|
|
330
|
+
},
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Use the platform function to send the update to the device
|
|
334
|
+
await this.platform.sendUpdate(this.accessory, {
|
|
335
|
+
namespace,
|
|
336
|
+
payload,
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
// Update the cache
|
|
340
|
+
this.cacheVolume = value
|
|
341
|
+
this.cacheVolumeMR = volumeMR
|
|
342
|
+
|
|
343
|
+
this.accessory.log(`${platformLang.curVol} [${this.cacheVolumeMR}/16]`)
|
|
344
|
+
})
|
|
345
|
+
} catch (err) {
|
|
346
|
+
// Catch any errors whilst updating the device
|
|
347
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
348
|
+
this.accessory.logWarn(`${platformLang.sendFailed} ${eText}`)
|
|
349
|
+
setTimeout(() => {
|
|
350
|
+
this.speakerService.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheVolume)
|
|
351
|
+
}, 2000)
|
|
352
|
+
throw new this.hapErr(-70402)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async internalSceneUpdate(value, scene, charToUpdate) {
|
|
357
|
+
try {
|
|
358
|
+
// Add the request to the queue so updates are sent apart
|
|
359
|
+
await this.queue.add(async () => {
|
|
360
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
361
|
+
this.updateInProgress = true
|
|
362
|
+
|
|
363
|
+
// Generate the namespace and payload
|
|
364
|
+
const namespace = 'Appliance.Control.Light'
|
|
365
|
+
const payload = {
|
|
366
|
+
light: {
|
|
367
|
+
effect: value ? scene : 0,
|
|
368
|
+
channel: 0,
|
|
369
|
+
},
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Use the platform function to send the update to the device
|
|
373
|
+
await this.platform.sendUpdate(this.accessory, {
|
|
374
|
+
namespace,
|
|
375
|
+
payload,
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
// Update the cache
|
|
379
|
+
this.accessory.log(`${platformLang.curScene} [${value ? scene : 0}]`);
|
|
380
|
+
|
|
381
|
+
// Also turn the other scenes off
|
|
382
|
+
['BabySceneOne', 'BabySceneTwo', 'BabySceneThree', 'BabySceneFour'].forEach((char) => {
|
|
383
|
+
if (char !== charToUpdate) {
|
|
384
|
+
if (this.serviceLight.getCharacteristic(this.cusChar[char]).value) {
|
|
385
|
+
this.serviceLight.updateCharacteristic(this.cusChar[char], false)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
})
|
|
389
|
+
})
|
|
390
|
+
} catch (err) {
|
|
391
|
+
// Catch any errors whilst updating the device
|
|
392
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
393
|
+
this.accessory.logWarn(`${platformLang.sendFailed} ${eText}`)
|
|
394
|
+
setTimeout(() => {
|
|
395
|
+
this.serviceLight.updateCharacteristic(this.cusChar[charToUpdate], false)
|
|
396
|
+
}, 2000)
|
|
397
|
+
throw new this.hapErr(-70402)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async requestUpdate(firstRun = false) {
|
|
402
|
+
try {
|
|
403
|
+
// Don't continue if an update is currently being sent to the device
|
|
404
|
+
if (this.updateInProgress) {
|
|
405
|
+
return
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Add the request to the queue so updates are sent apart
|
|
409
|
+
await this.queue.add(async () => {
|
|
410
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
411
|
+
this.updateInProgress = true
|
|
412
|
+
|
|
413
|
+
// Send the request
|
|
414
|
+
const res = await this.platform.sendUpdate(this.accessory, {
|
|
415
|
+
namespace: 'Appliance.System.All',
|
|
416
|
+
payload: {},
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
// Log the received data
|
|
420
|
+
this.accessory.logDebug(`${platformLang.incPoll}: ${JSON.stringify(res.data)}`)
|
|
421
|
+
|
|
422
|
+
// Check the response is in a useful format
|
|
423
|
+
const data = res.data.payload
|
|
424
|
+
if (data.all) {
|
|
425
|
+
if (data.all.digest && data.all.digest.mp3) {
|
|
426
|
+
this.applyUpdate(data.all.digest.mp3)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// A flag to check if we need to update the accessory context
|
|
430
|
+
let needsUpdate = false
|
|
431
|
+
|
|
432
|
+
// Get the mac address and hardware version of the device
|
|
433
|
+
if (data.all.system) {
|
|
434
|
+
// Mac address and hardware don't change regularly so only get on first poll
|
|
435
|
+
if (firstRun && data.all.system.hardware) {
|
|
436
|
+
this.accessory.context.macAddress = data.all.system.hardware.macAddress.toUpperCase()
|
|
437
|
+
this.accessory.context.hardware = data.all.system.hardware.version
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Get the ip address and firmware of the device
|
|
441
|
+
if (data.all.system.firmware) {
|
|
442
|
+
// Check for an IP change each and every time the device is polled
|
|
443
|
+
if (this.accessory.context.ipAddress !== data.all.system.firmware.innerIp) {
|
|
444
|
+
this.accessory.context.ipAddress = data.all.system.firmware.innerIp
|
|
445
|
+
needsUpdate = true
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Firmware doesn't change regularly so only get on first poll
|
|
449
|
+
if (firstRun) {
|
|
450
|
+
this.accessory.context.firmware = data.all.system.firmware.version
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Get the cloud online status of the device
|
|
456
|
+
if (data.all.system.online) {
|
|
457
|
+
const isOnline = data.all.system.online.status === 1
|
|
458
|
+
if (this.accessory.context.isOnline !== isOnline) {
|
|
459
|
+
this.accessory.context.isOnline = isOnline
|
|
460
|
+
needsUpdate = true
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Update the accessory cache if anything has changed
|
|
465
|
+
if (needsUpdate || firstRun) {
|
|
466
|
+
this.platform.updateAccessory(this.accessory)
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
})
|
|
470
|
+
} catch (err) {
|
|
471
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
472
|
+
this.accessory.logDebugWarn(`${platformLang.reqFailed}: ${eText}`)
|
|
473
|
+
|
|
474
|
+
// Set the homebridge-ui status of the device to offline if local and error is timeout
|
|
475
|
+
if (
|
|
476
|
+
(this.accessory.context.isOnline || firstRun)
|
|
477
|
+
&& ['EHOSTUNREACH', 'timed out'].some(el => eText.includes(el))
|
|
478
|
+
) {
|
|
479
|
+
this.accessory.context.isOnline = false
|
|
480
|
+
this.platform.updateAccessory(this.accessory)
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
receiveUpdate(params) {
|
|
486
|
+
try {
|
|
487
|
+
// Log the received data
|
|
488
|
+
this.accessory.logDebug(`${platformLang.incMQTT}: ${JSON.stringify(params)}`)
|
|
489
|
+
if (params.payload && params.payload.mp3) {
|
|
490
|
+
this.applyUpdate(params.payload.mp3)
|
|
491
|
+
}
|
|
492
|
+
} catch (err) {
|
|
493
|
+
this.accessory.logWarn(`${platformLang.refFailed} ${parseError(err)}`)
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
applyUpdate(data) {
|
|
498
|
+
// For TV Active characteristic
|
|
499
|
+
if (hasProperty(data, 'mute')) {
|
|
500
|
+
// Compare to this.cacheState (0, 1)
|
|
501
|
+
if (data.mute !== this.cacheStateMR) {
|
|
502
|
+
this.cacheStateMR = data.mute
|
|
503
|
+
this.cacheState = data.mute === 1 ? 0 : 1
|
|
504
|
+
this.service.updateCharacteristic(this.hapChar.Active, this.cacheState)
|
|
505
|
+
this.speakerService.updateCharacteristic(this.hapChar.On, this.cacheState === 1)
|
|
506
|
+
this.accessory.log(`${platformLang.curState} [${this.cacheState === 1 ? 'on' : 'off'}]`)
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// For TV Speaker (Fan) characteristic
|
|
511
|
+
if (hasProperty(data, 'volume')) {
|
|
512
|
+
// Compare to this.cacheVolume (0, ..., 100) or better this.cacheVolumeMR (0, 16)
|
|
513
|
+
// data.volume is (0, 16)
|
|
514
|
+
if (data.volume !== this.cacheVolumeMR) {
|
|
515
|
+
this.cacheVolumeMR = data.volume
|
|
516
|
+
this.cacheVolume = this.volumeMR2HK(this.cacheVolumeMR)
|
|
517
|
+
this.speakerService.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheVolume)
|
|
518
|
+
this.accessory.log(`${platformLang.curVol} [${this.cacheVolumeMR}/16].`)
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// For TV ActiveIdentifier characteristic
|
|
523
|
+
if (data.song) {
|
|
524
|
+
// Compare to this.cacheChannel (1, ..., 11)
|
|
525
|
+
if (data.song !== this.cacheChannel) {
|
|
526
|
+
this.cacheChannel = data.song
|
|
527
|
+
this.service.updateCharacteristic(this.hapChar.ActiveIdentifier, this.cacheChannel)
|
|
528
|
+
this.accessory.log(`${platformLang.curSong} [${this.channelList[this.cacheChannel]}]`)
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|