@homebridge-plugins/homebridge-govee 10.12.1
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 +1937 -0
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/config.schema.json +1727 -0
- package/eslint.config.js +49 -0
- package/lib/connection/aws.js +174 -0
- package/lib/connection/ble.js +208 -0
- package/lib/connection/cert/AmazonRootCA1.pem +20 -0
- package/lib/connection/http.js +240 -0
- package/lib/connection/lan.js +284 -0
- package/lib/device/cooler-single.js +300 -0
- package/lib/device/dehumidifier-H7150.js +182 -0
- package/lib/device/dehumidifier-H7151.js +157 -0
- package/lib/device/diffuser-H7161.js +117 -0
- package/lib/device/diffuser-H7162.js +117 -0
- package/lib/device/fan-H7100.js +274 -0
- package/lib/device/fan-H7101.js +330 -0
- package/lib/device/fan-H7102.js +274 -0
- package/lib/device/fan-H7105.js +503 -0
- package/lib/device/fan-H7106.js +274 -0
- package/lib/device/fan-H7111.js +335 -0
- package/lib/device/heater-single.js +300 -0
- package/lib/device/heater1a.js +353 -0
- package/lib/device/heater1b.js +616 -0
- package/lib/device/heater2.js +838 -0
- package/lib/device/humidifier-H7140.js +224 -0
- package/lib/device/humidifier-H7141.js +257 -0
- package/lib/device/humidifier-H7142.js +522 -0
- package/lib/device/humidifier-H7143.js +157 -0
- package/lib/device/humidifier-H7148.js +157 -0
- package/lib/device/humidifier-H7160.js +446 -0
- package/lib/device/ice-maker-H7162.js +46 -0
- package/lib/device/index.js +105 -0
- package/lib/device/kettle.js +269 -0
- package/lib/device/light-switch.js +86 -0
- package/lib/device/light.js +617 -0
- package/lib/device/outlet-double.js +121 -0
- package/lib/device/outlet-single.js +172 -0
- package/lib/device/outlet-triple.js +160 -0
- package/lib/device/purifier-H7120.js +336 -0
- package/lib/device/purifier-H7121.js +336 -0
- package/lib/device/purifier-H7122.js +449 -0
- package/lib/device/purifier-H7123.js +411 -0
- package/lib/device/purifier-H7124.js +411 -0
- package/lib/device/purifier-H7126.js +296 -0
- package/lib/device/purifier-H7127.js +296 -0
- package/lib/device/purifier-H712C.js +296 -0
- package/lib/device/purifier-single.js +119 -0
- package/lib/device/sensor-button.js +22 -0
- package/lib/device/sensor-contact.js +22 -0
- package/lib/device/sensor-leak.js +87 -0
- package/lib/device/sensor-monitor.js +190 -0
- package/lib/device/sensor-presence.js +53 -0
- package/lib/device/sensor-thermo.js +144 -0
- package/lib/device/sensor-thermo4.js +55 -0
- package/lib/device/switch-double.js +121 -0
- package/lib/device/switch-single.js +95 -0
- package/lib/device/switch-triple.js +160 -0
- package/lib/device/tap-single.js +108 -0
- package/lib/device/template.js +43 -0
- package/lib/device/tv-single.js +84 -0
- package/lib/device/valve-single.js +155 -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 +433 -0
- package/lib/homebridge-ui/server.js +10 -0
- package/lib/index.js +8 -0
- package/lib/platform.js +1967 -0
- package/lib/utils/colour.js +564 -0
- package/lib/utils/constants.js +579 -0
- package/lib/utils/custom-chars.js +225 -0
- package/lib/utils/eve-chars.js +68 -0
- package/lib/utils/functions.js +117 -0
- package/lib/utils/lang-en.js +131 -0
- package/package.json +75 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import {
|
|
2
|
+
base64ToHex,
|
|
3
|
+
getTwoItemPosition,
|
|
4
|
+
hexToBase64,
|
|
5
|
+
hexToDecimal,
|
|
6
|
+
hexToTwoItems,
|
|
7
|
+
parseError,
|
|
8
|
+
statusToActionCode,
|
|
9
|
+
} from '../utils/functions.js'
|
|
10
|
+
import platformLang from '../utils/lang-en.js'
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
H7122
|
|
14
|
+
{
|
|
15
|
+
"mode": {
|
|
16
|
+
"options": [
|
|
17
|
+
{
|
|
18
|
+
"name": "Low",
|
|
19
|
+
"value": 1
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "Medium",
|
|
23
|
+
"value": 2
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "High",
|
|
27
|
+
"value": 3
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "Auto mode",
|
|
31
|
+
"value": 4
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "Sleep mode",
|
|
35
|
+
"value": 5
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "CustomMode mode",
|
|
39
|
+
"value": 6
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
export default class {
|
|
47
|
+
constructor(platform, accessory) {
|
|
48
|
+
// Set up variables from the platform
|
|
49
|
+
this.cusChar = platform.cusChar
|
|
50
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
51
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
52
|
+
this.hapServ = platform.api.hap.Service
|
|
53
|
+
this.platform = platform
|
|
54
|
+
|
|
55
|
+
// Set up variables from the accessory
|
|
56
|
+
this.accessory = accessory
|
|
57
|
+
|
|
58
|
+
// Speed codes
|
|
59
|
+
this.value2Code = {
|
|
60
|
+
1: 'OgUFAAAAAAAAAAAAAAAAAAAAADo=', // sleep
|
|
61
|
+
2: 'OgUBAQAAAAAAAAAAAAAAAAAAAD8=', // low
|
|
62
|
+
3: 'OgUBAgAAAAAAAAAAAAAAAAAAADw=', // med
|
|
63
|
+
4: 'OgUBAwAAAAAAAAAAAAAAAAAAAD0=', // high
|
|
64
|
+
5: 'OgUDAAAAAAAAAAAAAAAAAAAAADw=', // auto
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.value2Label = {
|
|
68
|
+
0: 'off',
|
|
69
|
+
1: 'sleep',
|
|
70
|
+
2: 'low',
|
|
71
|
+
3: 'medium',
|
|
72
|
+
4: 'high',
|
|
73
|
+
5: 'auto',
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Add the purifier service if it doesn't already exist
|
|
77
|
+
this.service = this.accessory.getService(this.hapServ.AirPurifier)
|
|
78
|
+
|| this.accessory.addService(this.hapServ.AirPurifier)
|
|
79
|
+
|
|
80
|
+
// Add the air quality service if it doesn't already exist
|
|
81
|
+
this.airService = this.accessory.getService(this.hapServ.AirQualitySensor)
|
|
82
|
+
|
|
83
|
+
if (!this.airService) {
|
|
84
|
+
this.airService = this.accessory.addService(this.hapServ.AirQualitySensor)
|
|
85
|
+
this.airService.addCharacteristic(this.hapChar.PM2_5Density)
|
|
86
|
+
}
|
|
87
|
+
this.cacheAir = this.airService.getCharacteristic(this.hapChar.PM2_5Density).value
|
|
88
|
+
|
|
89
|
+
// Add the set handler to the switch on/off characteristic
|
|
90
|
+
this.service.getCharacteristic(this.hapChar.Active).onSet(async (value) => {
|
|
91
|
+
await this.internalStateUpdate(value)
|
|
92
|
+
})
|
|
93
|
+
this.cacheState = this.service.getCharacteristic(this.hapChar.Active).value === 1 ? 'on' : 'off'
|
|
94
|
+
|
|
95
|
+
// Add options to the purifier target state characteristic
|
|
96
|
+
this.service
|
|
97
|
+
.getCharacteristic(this.hapChar.TargetAirPurifierState)
|
|
98
|
+
.updateValue(1)
|
|
99
|
+
.setProps({
|
|
100
|
+
minValue: 1,
|
|
101
|
+
maxValue: 1,
|
|
102
|
+
validValues: [1],
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Add the set handler to the fan rotation speed characteristic
|
|
106
|
+
this.service
|
|
107
|
+
.getCharacteristic(this.hapChar.RotationSpeed)
|
|
108
|
+
.setProps({
|
|
109
|
+
minStep: 20,
|
|
110
|
+
validValues: [0, 20, 40, 60, 80, 100],
|
|
111
|
+
})
|
|
112
|
+
.onSet(async value => this.internalSpeedUpdate(value))
|
|
113
|
+
this.cacheMode = this.service.getCharacteristic(this.hapChar.RotationSpeed).value / 20
|
|
114
|
+
|
|
115
|
+
// Add the set handler to the lock controls characteristic
|
|
116
|
+
this.service.getCharacteristic(this.hapChar.LockPhysicalControls).onSet(async (value) => {
|
|
117
|
+
await this.internalLockUpdate(value)
|
|
118
|
+
})
|
|
119
|
+
this.cacheLock = this.service.getCharacteristic(this.hapChar.LockPhysicalControls).value === 0 ? 'off' : 'on'
|
|
120
|
+
|
|
121
|
+
// Add night light Eve characteristic if it doesn't exist already
|
|
122
|
+
if (!this.service.testCharacteristic(this.cusChar.NightLight)) {
|
|
123
|
+
this.service.addCharacteristic(this.cusChar.NightLight)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Add display light Eve characteristic if it doesn't exist already
|
|
127
|
+
if (!this.service.testCharacteristic(this.cusChar.DisplayLight)) {
|
|
128
|
+
this.service.addCharacteristic(this.cusChar.DisplayLight)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Add the set handler to the custom display light characteristic
|
|
132
|
+
this.service.getCharacteristic(this.cusChar.DisplayLight).onSet(async (value) => {
|
|
133
|
+
await this.internalDisplayLightUpdate(value)
|
|
134
|
+
})
|
|
135
|
+
this.cacheDisplay = this.service.getCharacteristic(this.cusChar.DisplayLight).value
|
|
136
|
+
? 'on'
|
|
137
|
+
: 'off'
|
|
138
|
+
|
|
139
|
+
// Output the customised options to the log
|
|
140
|
+
const opts = JSON.stringify({})
|
|
141
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async internalStateUpdate(value) {
|
|
145
|
+
try {
|
|
146
|
+
const newValue = value === 1 ? 'on' : 'off'
|
|
147
|
+
|
|
148
|
+
// Don't continue if the new value is the same as before
|
|
149
|
+
if (this.cacheState === newValue) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Send the request to the platform sender function
|
|
154
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
155
|
+
cmd: 'statePuri',
|
|
156
|
+
value: value ? 1 : 0,
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// Update the current state characteristic
|
|
160
|
+
this.service.updateCharacteristic(this.hapChar.CurrentAirPurifierState, value === 1 ? 2 : 0)
|
|
161
|
+
|
|
162
|
+
// Cache the new state and log if appropriate
|
|
163
|
+
this.cacheState = newValue
|
|
164
|
+
this.accessory.log(`${platformLang.curState} [${newValue}]`)
|
|
165
|
+
} catch (err) {
|
|
166
|
+
// Catch any errors during the process
|
|
167
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
168
|
+
|
|
169
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
170
|
+
setTimeout(() => {
|
|
171
|
+
this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
|
|
172
|
+
}, 2000)
|
|
173
|
+
throw new this.hapErr(-70402)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async internalSpeedUpdate(value) {
|
|
178
|
+
try {
|
|
179
|
+
// Don't continue if the speed is 0
|
|
180
|
+
if (value === 0) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Get the single Govee value {1, 2, 3, 4}
|
|
185
|
+
const newValueKey = value / 20
|
|
186
|
+
|
|
187
|
+
// Don't continue if the speed value won't have effect
|
|
188
|
+
if (!newValueKey || newValueKey === this.cacheMode) {
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Send the request to the platform sender function
|
|
193
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
194
|
+
cmd: 'ptReal',
|
|
195
|
+
value: this.value2Code[newValueKey],
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// Cache the new state and log if appropriate
|
|
199
|
+
this.cacheMode = newValueKey
|
|
200
|
+
this.accessory.log(`${platformLang.curMode} [${this.value2Label[this.cacheMode]}]`)
|
|
201
|
+
} catch (err) {
|
|
202
|
+
// Catch any errors during the process
|
|
203
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
204
|
+
|
|
205
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
206
|
+
setTimeout(() => {
|
|
207
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheMode * 20)
|
|
208
|
+
}, 2000)
|
|
209
|
+
throw new this.hapErr(-70402)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async internalLockUpdate(value) {
|
|
214
|
+
try {
|
|
215
|
+
const newValue = value === 1 ? 'on' : 'off'
|
|
216
|
+
|
|
217
|
+
// Don't continue if the new value is the same as before
|
|
218
|
+
if (this.cacheLock === newValue) {
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Send the request to the platform sender function
|
|
223
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
224
|
+
cmd: 'ptReal',
|
|
225
|
+
value: value ? 'MxABAAAAAAAAAAAAAAAAAAAAACI=' : 'MxAAAAAAAAAAAAAAAAAAAAAAACM=',
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// Cache the new state and log if appropriate
|
|
229
|
+
this.cacheLock = newValue
|
|
230
|
+
this.accessory.log(`${platformLang.curLock} [${newValue}]`)
|
|
231
|
+
} catch (err) {
|
|
232
|
+
// Catch any errors during the process
|
|
233
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
234
|
+
|
|
235
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
236
|
+
setTimeout(() => {
|
|
237
|
+
this.service.updateCharacteristic(
|
|
238
|
+
this.hapChar.LockPhysicalControls,
|
|
239
|
+
this.cacheLock === 'on' ? 1 : 0,
|
|
240
|
+
)
|
|
241
|
+
}, 2000)
|
|
242
|
+
throw new this.hapErr(-70402)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async internalDisplayLightUpdate(value) {
|
|
247
|
+
try {
|
|
248
|
+
const newValue = value ? 'on' : 'off'
|
|
249
|
+
|
|
250
|
+
// Don't continue if the new value is the same as before
|
|
251
|
+
if (this.cacheDisplay === newValue) {
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Generate the code to send
|
|
256
|
+
let codeToSend
|
|
257
|
+
if (value) {
|
|
258
|
+
codeToSend = this.accessory.context.cacheDisplayCode
|
|
259
|
+
? hexToBase64(statusToActionCode(this.accessory.context.cacheDisplayCode))
|
|
260
|
+
: 'MxYBAAAAAAAAAAAAAAAAAAAAACQ='
|
|
261
|
+
} else {
|
|
262
|
+
codeToSend = 'MxYAAAAAAAAAAAAAAAAAAAAAACU='
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Send the request to the platform sender function
|
|
266
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
267
|
+
cmd: 'ptReal',
|
|
268
|
+
value: codeToSend,
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
// Cache the new state and log if appropriate
|
|
272
|
+
this.cacheDisplay = newValue
|
|
273
|
+
this.accessory.log(`${platformLang.curDisplay} [${newValue}]`)
|
|
274
|
+
} catch (err) {
|
|
275
|
+
// Catch any errors during the process
|
|
276
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
277
|
+
|
|
278
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
279
|
+
setTimeout(() => {
|
|
280
|
+
this.service.updateCharacteristic(this.cusChar.DisplayLight, this.cacheDisplay === 'on')
|
|
281
|
+
}, 2000)
|
|
282
|
+
throw new this.hapErr(-70402)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
externalUpdate(params) {
|
|
287
|
+
// Check for an ON/OFF change
|
|
288
|
+
if (params.state && params.state !== this.cacheState) {
|
|
289
|
+
this.cacheState = params.state
|
|
290
|
+
this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on')
|
|
291
|
+
this.service.updateCharacteristic(this.hapChar.CurrentAirPurifierState, this.cacheState === 'on' ? 2 : 0)
|
|
292
|
+
|
|
293
|
+
// Log the change
|
|
294
|
+
this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
(params.commands || []).forEach((command) => {
|
|
298
|
+
const hexString = base64ToHex(command)
|
|
299
|
+
const hexParts = hexToTwoItems(hexString)
|
|
300
|
+
|
|
301
|
+
const deviceFunction = `${getTwoItemPosition(hexParts, 1)}${getTwoItemPosition(hexParts, 2)}`
|
|
302
|
+
|
|
303
|
+
switch (deviceFunction) {
|
|
304
|
+
case 'aa05': // speed
|
|
305
|
+
case '3a05': { // speed
|
|
306
|
+
const newSpeedCode = `${getTwoItemPosition(hexParts, 3)}${getTwoItemPosition(hexParts, 4)}`
|
|
307
|
+
|
|
308
|
+
// Different behaviour for custom speed
|
|
309
|
+
if (newSpeedCode === '0202') {
|
|
310
|
+
this.accessory.log(`${platformLang.curMode} [custom]`)
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
let newMode
|
|
315
|
+
|
|
316
|
+
switch (newSpeedCode) {
|
|
317
|
+
case '0500': {
|
|
318
|
+
// Sleep
|
|
319
|
+
newMode = 1
|
|
320
|
+
break
|
|
321
|
+
}
|
|
322
|
+
case '0101': {
|
|
323
|
+
// Low
|
|
324
|
+
newMode = 2
|
|
325
|
+
break
|
|
326
|
+
}
|
|
327
|
+
case '0102': {
|
|
328
|
+
// Medium
|
|
329
|
+
newMode = 3
|
|
330
|
+
break
|
|
331
|
+
}
|
|
332
|
+
case '0103': {
|
|
333
|
+
// High
|
|
334
|
+
newMode = 4
|
|
335
|
+
break
|
|
336
|
+
}
|
|
337
|
+
case '0300': {
|
|
338
|
+
// Auto
|
|
339
|
+
newMode = 5
|
|
340
|
+
break
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (newMode && newMode !== this.cacheMode) {
|
|
345
|
+
this.cacheMode = newMode
|
|
346
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheMode * 20)
|
|
347
|
+
this.accessory.log(`${platformLang.curMode} [${this.value2Label[this.cacheMode]}]`)
|
|
348
|
+
}
|
|
349
|
+
break
|
|
350
|
+
}
|
|
351
|
+
case 'aa10': { // lock
|
|
352
|
+
const newLock = getTwoItemPosition(hexParts, 3) === '01' ? 'on' : 'off'
|
|
353
|
+
if (newLock !== this.cacheLock) {
|
|
354
|
+
this.cacheLock = newLock
|
|
355
|
+
this.service.updateCharacteristic(this.hapChar.LockPhysicalControls, this.cacheLock === 'on' ? 1 : 0)
|
|
356
|
+
this.accessory.log(`${platformLang.curLock} [${this.cacheLock}]`)
|
|
357
|
+
}
|
|
358
|
+
break
|
|
359
|
+
}
|
|
360
|
+
case 'aa16': { // display light
|
|
361
|
+
const newDisplay = getTwoItemPosition(hexParts, 3) === '01' ? 'on' : 'off'
|
|
362
|
+
if (newDisplay === 'on') {
|
|
363
|
+
this.accessory.context.cacheDisplayCode = hexString
|
|
364
|
+
}
|
|
365
|
+
if (newDisplay !== this.cacheDisplay) {
|
|
366
|
+
this.cacheDisplay = newDisplay
|
|
367
|
+
this.service.updateCharacteristic(this.cusChar.DisplayLight, this.cacheDisplay === 'on')
|
|
368
|
+
|
|
369
|
+
// Log the change
|
|
370
|
+
this.accessory.log(`${platformLang.curDisplay} [${this.cacheDisplay}]`)
|
|
371
|
+
}
|
|
372
|
+
break
|
|
373
|
+
}
|
|
374
|
+
case 'aa1c': {
|
|
375
|
+
// Check air quality reading
|
|
376
|
+
// part 3 may be the air quality reading, (i.e. 1=green, 2=blue, 3=yellow, 4=red)
|
|
377
|
+
const qualHex = `${getTwoItemPosition(hexParts, 4)}${getTwoItemPosition(hexParts, 5)}`
|
|
378
|
+
const qualDec = hexToDecimal(`0x${qualHex}`)
|
|
379
|
+
if (qualDec !== this.cacheAir) {
|
|
380
|
+
// Air quality is different so update Homebridge with new values
|
|
381
|
+
this.cacheAir = qualDec
|
|
382
|
+
this.airService.updateCharacteristic(this.hapChar.PM2_5Density, this.cacheAir)
|
|
383
|
+
|
|
384
|
+
// Log the change
|
|
385
|
+
this.accessory.log(`${platformLang.curPM25} [${qualDec}µg/m³]`)
|
|
386
|
+
|
|
387
|
+
// Check for any change to the main air quality characteristic
|
|
388
|
+
// PM2.5 has a range of 0-1000µg/m³
|
|
389
|
+
// HK characteristic ranges from 1-5 (excellent, good, fair, inferior, poor)
|
|
390
|
+
// Scales based on Govee manual
|
|
391
|
+
// 0-12.0µg/m³ = excellent
|
|
392
|
+
// 12-35µg/m³ = good
|
|
393
|
+
// 35-75µg/m³ = fair
|
|
394
|
+
// 75-115µg/m³ = inferior
|
|
395
|
+
// 115-500µg/m³ = poor (use 1000 for HK)
|
|
396
|
+
if (this.cacheAir <= 12) {
|
|
397
|
+
const newValue = 'excellent'
|
|
398
|
+
if (this.cacheAirQual !== newValue) {
|
|
399
|
+
this.cacheAirQual = newValue
|
|
400
|
+
this.airService.updateCharacteristic(this.hapChar.AirQuality, 1)
|
|
401
|
+
this.accessory.log(`${platformLang.curAirQual} [${newValue}]`)
|
|
402
|
+
}
|
|
403
|
+
} else if (this.cacheAir <= 35) {
|
|
404
|
+
const newValue = 'good'
|
|
405
|
+
if (this.cacheAirQual !== newValue) {
|
|
406
|
+
this.cacheAirQual = newValue
|
|
407
|
+
this.airService.updateCharacteristic(this.hapChar.AirQuality, 2)
|
|
408
|
+
this.accessory.log(`${platformLang.curAirQual} [${newValue}]`)
|
|
409
|
+
}
|
|
410
|
+
} else if (this.cacheAir <= 75) {
|
|
411
|
+
const newValue = 'fair'
|
|
412
|
+
if (this.cacheAirQual !== newValue) {
|
|
413
|
+
this.cacheAirQual = newValue
|
|
414
|
+
this.airService.updateCharacteristic(this.hapChar.AirQuality, 3)
|
|
415
|
+
this.accessory.log(`${platformLang.curAirQual} [${newValue}]`)
|
|
416
|
+
}
|
|
417
|
+
} else if (this.cacheAir <= 115) {
|
|
418
|
+
const newValue = 'inferior'
|
|
419
|
+
if (this.cacheAirQual !== newValue) {
|
|
420
|
+
this.cacheAirQual = newValue
|
|
421
|
+
this.airService.updateCharacteristic(this.hapChar.AirQuality, 4)
|
|
422
|
+
this.accessory.log(`${platformLang.curAirQual} [${newValue}]`)
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
const newValue = 'poor'
|
|
426
|
+
if (this.cacheAirQual !== newValue) {
|
|
427
|
+
this.cacheAirQual = newValue
|
|
428
|
+
this.airService.updateCharacteristic(this.hapChar.AirQuality, 5)
|
|
429
|
+
this.accessory.log(`${platformLang.curAirQual} [${newValue}]`)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
break
|
|
434
|
+
}
|
|
435
|
+
case 'aa11': // timer
|
|
436
|
+
case 'aa13': // scheduling
|
|
437
|
+
case '3310': // lock (same command for on and off)
|
|
438
|
+
case '3311': // timer
|
|
439
|
+
case '3313': // scheduling
|
|
440
|
+
case '3316': { // display light
|
|
441
|
+
break
|
|
442
|
+
}
|
|
443
|
+
default:
|
|
444
|
+
this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
|
|
445
|
+
break
|
|
446
|
+
}
|
|
447
|
+
})
|
|
448
|
+
}
|
|
449
|
+
}
|