@homebridge-plugins/homebridge-govee 11.32.2-beta.14 → 11.32.2-beta.16
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 +5 -0
- package/lib/device/appliance-basic.js +10 -0
- package/lib/device/dehumidifier-H7150.js +1 -1
- package/lib/device/fan-H7100.js +11 -1
- package/lib/device/index.js +5 -5
- package/lib/platform.js +36 -475
- package/lib/utils/config-parser.js +280 -0
- package/lib/utils/dispatch.js +159 -0
- package/package.json +1 -1
- package/lib/device/dehumidifier-H7152.js +0 -186
- package/lib/device/fan-H7101.js +0 -347
package/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,11 @@ All notable changes to `@homebridge-plugins/homebridge-govee` will be documented
|
|
|
30
30
|
- refactor: point the H7151 and H7111 at the handlers they were byte-for-byte copies of
|
|
31
31
|
- refactor: build the ice maker size codes from the model rather than writing both orderings out by hand
|
|
32
32
|
- refactor: build the switch and outlet tiles of a multi-socket device from one handler instead of two copies of it
|
|
33
|
+
- fix: move the H7152 dehumidifier's speed slider when it reports a new speed, which until now only the H7150 and H7151 did
|
|
34
|
+
- refactor: point the H7101 and H7111 fans at the H7100 handler they matched command for command
|
|
35
|
+
- feat: give a diffuser model that has not been worked out yet the same starting handler the other appliances have, so it works for on and off
|
|
36
|
+
- refactor: choose a device handler from a table, so adding a device type no longer means editing the platform
|
|
37
|
+
- refactor: move the settings parsing into its own file, away from the code that talks to devices
|
|
33
38
|
|
|
34
39
|
## v11.32.1 (2026-08-02)
|
|
35
40
|
|
|
@@ -356,6 +356,16 @@ export const deviceDehumidifierBasic = makeBasicAppliance({
|
|
|
356
356
|
currentStateOff: 0, // inactive
|
|
357
357
|
})
|
|
358
358
|
|
|
359
|
+
// Diffusers are power only, so there is no variable control to offer. They use
|
|
360
|
+
// the same tile as the models already handled, so a new one looks the same
|
|
361
|
+
export const deviceDiffuserBasic = makeBasicAppliance({
|
|
362
|
+
family: 'diffuser',
|
|
363
|
+
service: 'AirPurifier',
|
|
364
|
+
currentStateChar: 'CurrentAirPurifierState',
|
|
365
|
+
currentStateOn: 2, // purifying air
|
|
366
|
+
currentStateOff: 0, // inactive
|
|
367
|
+
})
|
|
368
|
+
|
|
359
369
|
export const devicePurifierBasic = makeBasicAppliance({
|
|
360
370
|
family: 'purifier',
|
|
361
371
|
service: 'AirPurifier',
|
package/lib/device/fan-H7100.js
CHANGED
|
@@ -10,6 +10,16 @@ import {
|
|
|
10
10
|
import platformLang from '../utils/lang-en.js'
|
|
11
11
|
import { logUnknownData } from '../utils/report-unknown.js'
|
|
12
12
|
|
|
13
|
+
/*
|
|
14
|
+
The models on this handler and the modes each offers:
|
|
15
|
+
|
|
16
|
+
H7100, H7102, H7106 -> Sleep, Nature, Custom, Auto
|
|
17
|
+
H7101 -> Sleep, Nature, Turbo, Custom, Auto
|
|
18
|
+
H7111 -> Sleep, Nature, Custom, Auto
|
|
19
|
+
H1310, R1310 -> as the H7100, but its oscillation status message has
|
|
20
|
+
never been confirmed, so it is not read back
|
|
21
|
+
*/
|
|
22
|
+
|
|
13
23
|
export default class GoveeFanH7100 {
|
|
14
24
|
constructor(platform, accessory) {
|
|
15
25
|
// Set up variables from the platform
|
|
@@ -267,7 +277,7 @@ export default class GoveeFanH7100 {
|
|
|
267
277
|
this.cacheSpeed = this.autoSpeed
|
|
268
278
|
// Cache auto so we can tell it apart from "user asked for max speed"
|
|
269
279
|
this.service.updateCharacteristic(this.hapChar.RotationSpeed, 100)
|
|
270
|
-
this.accessory.log(`${platformLang.curSpeed} [auto]`)
|
|
280
|
+
this.accessory.log(`${platformLang.curSpeed} [auto -> 100%]`)
|
|
271
281
|
}
|
|
272
282
|
break
|
|
273
283
|
}
|
package/lib/device/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deviceDehumidifierBasic,
|
|
3
|
+
deviceDiffuserBasic,
|
|
3
4
|
deviceFanBasic,
|
|
4
5
|
deviceHumidifierBasic,
|
|
5
6
|
deviceIceMakerBasic,
|
|
@@ -7,11 +8,9 @@ import {
|
|
|
7
8
|
} from './appliance-basic.js'
|
|
8
9
|
import deviceCoolerSingle from './cooler-single.js'
|
|
9
10
|
import deviceDehumidifierH7150 from './dehumidifier-H7150.js'
|
|
10
|
-
import deviceDehumidifierH7152 from './dehumidifier-H7152.js'
|
|
11
11
|
import deviceDiffuserH7161 from './diffuser-H7161.js'
|
|
12
12
|
import deviceFanH1370 from './fan-H1370.js'
|
|
13
13
|
import deviceFanH7100 from './fan-H7100.js'
|
|
14
|
-
import deviceFanH7101 from './fan-H7101.js'
|
|
15
14
|
import deviceFanH7105 from './fan-H7105.js'
|
|
16
15
|
import deviceFanH7107 from './fan-H7107.js'
|
|
17
16
|
import deviceHeater1A from './heater1a.js'
|
|
@@ -53,10 +52,11 @@ export default {
|
|
|
53
52
|
deviceCoolerSingle,
|
|
54
53
|
deviceDehumidifierH7150,
|
|
55
54
|
deviceDehumidifierH7151: deviceDehumidifierH7150, // identical to the H7150
|
|
56
|
-
deviceDehumidifierH7152,
|
|
55
|
+
deviceDehumidifierH7152: deviceDehumidifierH7150, // same device, and this gives it proper speed feedback
|
|
57
56
|
deviceDiffuserH7161,
|
|
58
57
|
deviceDiffuserH7162: deviceDiffuserH7161, // identical to the H7161
|
|
59
58
|
deviceDehumidifierBasic,
|
|
59
|
+
deviceDiffuserBasic,
|
|
60
60
|
deviceFanBasic,
|
|
61
61
|
deviceFanH1310: deviceFanH7100, // same fan, minus a confirmed swing status message
|
|
62
62
|
deviceFanH1370,
|
|
@@ -64,10 +64,10 @@ export default {
|
|
|
64
64
|
deviceFanH7100,
|
|
65
65
|
deviceFanH7102: deviceFanH7100, // identical to the H7100
|
|
66
66
|
deviceFanH7106: deviceFanH7100, // identical to the H7100
|
|
67
|
-
deviceFanH7101,
|
|
67
|
+
deviceFanH7101: deviceFanH7100, // identical to the H7100, with a turbo mode it is never sent
|
|
68
68
|
deviceFanH7105,
|
|
69
69
|
deviceFanH7107,
|
|
70
|
-
deviceFanH7111:
|
|
70
|
+
deviceFanH7111: deviceFanH7100, // same fan without the turbo mode, which is not sent
|
|
71
71
|
deviceHeaterSingle,
|
|
72
72
|
deviceHeater1A,
|
|
73
73
|
deviceHeater1B,
|
package/lib/platform.js
CHANGED
|
@@ -11,18 +11,18 @@ import awsClient from './connection/aws.js'
|
|
|
11
11
|
import httpClient from './connection/http.js'
|
|
12
12
|
import lanClient from './connection/lan.js'
|
|
13
13
|
import openApiClient from './connection/openapi.js'
|
|
14
|
-
import deviceTypes from './device/index.js'
|
|
15
14
|
import eveService from './fakegato/fakegato-history.js'
|
|
16
15
|
import { BLE_UPDATE_TIMEOUT } from './utils/ble-protocol.js'
|
|
17
16
|
import { buildCommand } from './utils/command-builder.js'
|
|
17
|
+
import { applyUserConfig } from './utils/config-parser.js'
|
|
18
18
|
import platformConsts from './utils/constants.js'
|
|
19
19
|
import platformChars from './utils/custom-chars.js'
|
|
20
20
|
import { getDeviceCapabilities } from './utils/device-capabilities.js'
|
|
21
|
+
import { resolveDeviceType } from './utils/dispatch.js'
|
|
21
22
|
import eveChars from './utils/eve-chars.js'
|
|
22
23
|
import {
|
|
23
24
|
base64ToHex,
|
|
24
25
|
hasProperty,
|
|
25
|
-
parseDeviceId,
|
|
26
26
|
parseError,
|
|
27
27
|
pfxToCertAndKey,
|
|
28
28
|
updateFailureError,
|
|
@@ -86,7 +86,7 @@ export default class {
|
|
|
86
86
|
|
|
87
87
|
// Apply the user's configuration
|
|
88
88
|
this.config = platformConsts.defaultConfig
|
|
89
|
-
|
|
89
|
+
applyUserConfig(this, config)
|
|
90
90
|
|
|
91
91
|
// Set up empty clients
|
|
92
92
|
this.bleClient = false
|
|
@@ -104,269 +104,6 @@ export default class {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
applyUserConfig(config) {
|
|
108
|
-
// These shorthand functions save line space during config parsing
|
|
109
|
-
const logDefault = (k, def) => {
|
|
110
|
-
this.log.warn('%s [%s] %s %s.', platformLang.cfgItem, k, platformLang.cfgDef, def)
|
|
111
|
-
}
|
|
112
|
-
const logDuplicate = (k) => {
|
|
113
|
-
this.log.warn('%s [%s] %s.', platformLang.cfgItem, k, platformLang.cfgDup)
|
|
114
|
-
}
|
|
115
|
-
const logIgnore = (k) => {
|
|
116
|
-
this.log.warn('%s [%s] %s.', platformLang.cfgItem, k, platformLang.cfgIgn)
|
|
117
|
-
}
|
|
118
|
-
const logIgnoreItem = (k) => {
|
|
119
|
-
this.log.warn('%s [%s] %s.', platformLang.cfgItem, k, platformLang.cfgIgnItem)
|
|
120
|
-
}
|
|
121
|
-
const logIncrease = (k, min) => {
|
|
122
|
-
this.log.warn('%s [%s] %s %s.', platformLang.cfgItem, k, platformLang.cfgLow, min)
|
|
123
|
-
}
|
|
124
|
-
const logQuotes = (k) => {
|
|
125
|
-
this.log.warn('%s [%s] %s.', platformLang.cfgItem, k, platformLang.cfgQts)
|
|
126
|
-
}
|
|
127
|
-
const logRemove = (k) => {
|
|
128
|
-
this.log.warn('%s [%s] %s.', platformLang.cfgItem, k, platformLang.cfgRmv)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Begin applying the user's config
|
|
132
|
-
Object.entries(config).forEach((entry) => {
|
|
133
|
-
const [key, val] = entry
|
|
134
|
-
switch (key) {
|
|
135
|
-
case 'apiKey':
|
|
136
|
-
case 'code':
|
|
137
|
-
case 'password':
|
|
138
|
-
case 'username':
|
|
139
|
-
if (typeof val !== 'string' || val === '') {
|
|
140
|
-
logIgnore(key)
|
|
141
|
-
} else {
|
|
142
|
-
this.config[key] = val
|
|
143
|
-
}
|
|
144
|
-
break
|
|
145
|
-
case 'lanScanSubnets': {
|
|
146
|
-
// Comma-separated list of CIDR subnets to scan directly (for devices
|
|
147
|
-
// on a different subnet from Homebridge, which multicast can't reach)
|
|
148
|
-
if (typeof val !== 'string' || val.trim() === '') {
|
|
149
|
-
break
|
|
150
|
-
}
|
|
151
|
-
const subnets = val
|
|
152
|
-
.split(',')
|
|
153
|
-
.map(s => s.trim())
|
|
154
|
-
.filter(s => /^\d{1,3}(?:\.\d{1,3}){3}\/\d{1,2}$/.test(s))
|
|
155
|
-
if (subnets.length === 0) {
|
|
156
|
-
logIgnore(key)
|
|
157
|
-
} else {
|
|
158
|
-
this.config[key] = subnets
|
|
159
|
-
}
|
|
160
|
-
break
|
|
161
|
-
}
|
|
162
|
-
case 'awsDisable':
|
|
163
|
-
case 'bleDisable':
|
|
164
|
-
case 'colourSafeMode':
|
|
165
|
-
case 'disableDeviceLogging':
|
|
166
|
-
case 'ignoreMatter':
|
|
167
|
-
case 'lanDisable':
|
|
168
|
-
if (typeof val === 'string') {
|
|
169
|
-
logQuotes(key)
|
|
170
|
-
}
|
|
171
|
-
this.config[key] = val === 'false' ? false : !!val
|
|
172
|
-
break
|
|
173
|
-
case 'bleControlInterval':
|
|
174
|
-
case 'bleRefreshTime':
|
|
175
|
-
case 'httpRefreshTime':
|
|
176
|
-
case 'lanRefreshTime':
|
|
177
|
-
case 'lanScanInterval': {
|
|
178
|
-
if (typeof val === 'string') {
|
|
179
|
-
logQuotes(key)
|
|
180
|
-
}
|
|
181
|
-
const intVal = Number.parseInt(val, 10)
|
|
182
|
-
if (Number.isNaN(intVal)) {
|
|
183
|
-
logDefault(key, platformConsts.defaultValues[key])
|
|
184
|
-
this.config[key] = platformConsts.defaultValues[key]
|
|
185
|
-
} else if (intVal < platformConsts.minValues[key]) {
|
|
186
|
-
logIncrease(key, platformConsts.minValues[key])
|
|
187
|
-
this.config[key] = platformConsts.minValues[key]
|
|
188
|
-
} else {
|
|
189
|
-
this.config[key] = intVal
|
|
190
|
-
}
|
|
191
|
-
break
|
|
192
|
-
}
|
|
193
|
-
case 'dehumidifierDevices':
|
|
194
|
-
case 'fanDevices':
|
|
195
|
-
case 'heaterDevices':
|
|
196
|
-
case 'humidifierDevices':
|
|
197
|
-
case 'iceMakerDevices':
|
|
198
|
-
case 'kettleDevices':
|
|
199
|
-
case 'leakDevices':
|
|
200
|
-
case 'lightDevices':
|
|
201
|
-
case 'purifierDevices':
|
|
202
|
-
case 'diffuserDevices':
|
|
203
|
-
case 'switchDevices':
|
|
204
|
-
case 'thermoDevices':
|
|
205
|
-
if (Array.isArray(val) && val.length > 0) {
|
|
206
|
-
val.forEach((x) => {
|
|
207
|
-
if (!x.deviceId) {
|
|
208
|
-
logIgnoreItem(key)
|
|
209
|
-
return
|
|
210
|
-
}
|
|
211
|
-
const id = parseDeviceId(x.deviceId)
|
|
212
|
-
if (Object.keys(this.deviceConf).includes(id)) {
|
|
213
|
-
logDuplicate(`${key}.${id}`)
|
|
214
|
-
return
|
|
215
|
-
}
|
|
216
|
-
const entries = Object.entries(x)
|
|
217
|
-
if (entries.length === 1) {
|
|
218
|
-
logRemove(`${key}.${id}`)
|
|
219
|
-
return
|
|
220
|
-
}
|
|
221
|
-
this.deviceConf[id] = {}
|
|
222
|
-
entries.forEach((subEntry) => {
|
|
223
|
-
const [k, v] = subEntry
|
|
224
|
-
switch (k) {
|
|
225
|
-
case 'adaptiveLightingShift':
|
|
226
|
-
case 'brightnessStep':
|
|
227
|
-
case 'lowBattThreshold': {
|
|
228
|
-
if (typeof v === 'string') {
|
|
229
|
-
logQuotes(`${key}.${k}`)
|
|
230
|
-
}
|
|
231
|
-
const intVal = Number.parseInt(v, 10)
|
|
232
|
-
if (Number.isNaN(intVal)) {
|
|
233
|
-
logDefault(`${key}.${id}.${k}`, platformConsts.defaultValues[k])
|
|
234
|
-
this.deviceConf[id][k] = platformConsts.defaultValues[k]
|
|
235
|
-
} else if (intVal < platformConsts.minValues[k]) {
|
|
236
|
-
logIncrease(`${key}.${id}.${k}`, platformConsts.minValues[k])
|
|
237
|
-
this.deviceConf[id][k] = platformConsts.minValues[k]
|
|
238
|
-
} else {
|
|
239
|
-
this.deviceConf[id][k] = intVal
|
|
240
|
-
}
|
|
241
|
-
break
|
|
242
|
-
}
|
|
243
|
-
case 'awsBrightnessNoScale':
|
|
244
|
-
case 'hideLight':
|
|
245
|
-
case 'hideModeGreenTea':
|
|
246
|
-
case 'hideModeOolongTea':
|
|
247
|
-
case 'hideModeCoffee':
|
|
248
|
-
case 'hideModeBlackTea':
|
|
249
|
-
case 'showCustomMode1':
|
|
250
|
-
case 'showCustomMode2':
|
|
251
|
-
case 'showExtraSwitch':
|
|
252
|
-
case 'tempReporting':
|
|
253
|
-
if (typeof v === 'string') {
|
|
254
|
-
logQuotes(`${key}.${id}.${k}`)
|
|
255
|
-
}
|
|
256
|
-
this.deviceConf[id][k] = v === 'false' ? false : !!v
|
|
257
|
-
break
|
|
258
|
-
case 'awsColourMode':
|
|
259
|
-
case 'openApiTempUnit':
|
|
260
|
-
case 'showAs': {
|
|
261
|
-
if (typeof v !== 'string' || !platformConsts.allowed[k].includes(v)) {
|
|
262
|
-
logIgnore(`${key}.${id}.${k}`)
|
|
263
|
-
} else {
|
|
264
|
-
this.deviceConf[id][k] = v
|
|
265
|
-
}
|
|
266
|
-
break
|
|
267
|
-
}
|
|
268
|
-
case 'customAddress':
|
|
269
|
-
case 'customIPAddress':
|
|
270
|
-
if (typeof v !== 'string' || v === '') {
|
|
271
|
-
logIgnore(`${key}.${id}.${k}`)
|
|
272
|
-
} else {
|
|
273
|
-
this.deviceConf[id][k] = v.replace(WHITESPACE_REGEX, '')
|
|
274
|
-
}
|
|
275
|
-
break
|
|
276
|
-
case 'deviceId':
|
|
277
|
-
break
|
|
278
|
-
case 'diyMode':
|
|
279
|
-
case 'diyModeTwo':
|
|
280
|
-
case 'diyModeThree':
|
|
281
|
-
case 'diyModeFour':
|
|
282
|
-
case 'musicMode':
|
|
283
|
-
case 'musicModeTwo':
|
|
284
|
-
case 'scene':
|
|
285
|
-
case 'sceneTwo':
|
|
286
|
-
case 'sceneThree':
|
|
287
|
-
case 'sceneFour':
|
|
288
|
-
case 'segmented':
|
|
289
|
-
case 'segmentedTwo':
|
|
290
|
-
case 'segmentedThree':
|
|
291
|
-
case 'segmentedFour':
|
|
292
|
-
case 'temperatureSource':
|
|
293
|
-
case 'videoMode':
|
|
294
|
-
case 'videoModeTwo': {
|
|
295
|
-
if (typeof v === 'string') {
|
|
296
|
-
this.log.warn(`${key}.${id}.${k} incorrectly configured - please use the config screen to reconfigure this item:`)
|
|
297
|
-
this.log.warn(`${key}.${id}.${k}: ${v}`)
|
|
298
|
-
}
|
|
299
|
-
if (typeof v === 'object') {
|
|
300
|
-
// object - only allowed keys are 'sceneCode', 'bleCode' and 'showAs'
|
|
301
|
-
const subEntries = Object.entries(v)
|
|
302
|
-
if (subEntries.length > 0) {
|
|
303
|
-
this.deviceConf[id][k] = {}
|
|
304
|
-
subEntries.forEach((subSubEntry) => {
|
|
305
|
-
const [k1, v1] = subSubEntry
|
|
306
|
-
switch (k1) {
|
|
307
|
-
case 'bleCode':
|
|
308
|
-
case 'sceneCode':
|
|
309
|
-
if (typeof v1 !== 'string' || v1 === '') {
|
|
310
|
-
logIgnore(`${key}.${id}.${k}.${k1}`)
|
|
311
|
-
} else {
|
|
312
|
-
this.deviceConf[id][k][k1] = v1
|
|
313
|
-
}
|
|
314
|
-
break
|
|
315
|
-
case 'showAs': {
|
|
316
|
-
if (typeof v1 !== 'string' || !['default', 'switch'].includes(v1)) {
|
|
317
|
-
logIgnore(`${key}.${id}.${k}.${k1}`)
|
|
318
|
-
} else {
|
|
319
|
-
this.deviceConf[id][k][k1] = v1
|
|
320
|
-
}
|
|
321
|
-
break
|
|
322
|
-
}
|
|
323
|
-
default:
|
|
324
|
-
logIgnore(`${key}.${id}.${k}.${k1}`)
|
|
325
|
-
break
|
|
326
|
-
}
|
|
327
|
-
})
|
|
328
|
-
} else {
|
|
329
|
-
logIgnore(`${key}.${id}.${k}`)
|
|
330
|
-
}
|
|
331
|
-
} else {
|
|
332
|
-
logIgnore(`${key}.${id}.${k}`)
|
|
333
|
-
}
|
|
334
|
-
break
|
|
335
|
-
}
|
|
336
|
-
case 'ignoreDevice':
|
|
337
|
-
if (typeof v === 'string') {
|
|
338
|
-
logQuotes(`${key}.${id}.${k}`)
|
|
339
|
-
}
|
|
340
|
-
if (!!v && v !== 'false') {
|
|
341
|
-
this.ignoredDevices.push(id)
|
|
342
|
-
}
|
|
343
|
-
break
|
|
344
|
-
case 'label':
|
|
345
|
-
if (typeof v !== 'string' || v === '') {
|
|
346
|
-
logIgnore(`${key}.${id}.${k}`)
|
|
347
|
-
} else {
|
|
348
|
-
this.deviceConf[id][k] = v
|
|
349
|
-
}
|
|
350
|
-
break
|
|
351
|
-
default:
|
|
352
|
-
logRemove(`${key}.${id}.${k}`)
|
|
353
|
-
}
|
|
354
|
-
})
|
|
355
|
-
})
|
|
356
|
-
} else {
|
|
357
|
-
logIgnore(key)
|
|
358
|
-
}
|
|
359
|
-
break
|
|
360
|
-
case 'name':
|
|
361
|
-
case 'platform':
|
|
362
|
-
break
|
|
363
|
-
default:
|
|
364
|
-
logRemove(key)
|
|
365
|
-
break
|
|
366
|
-
}
|
|
367
|
-
})
|
|
368
|
-
}
|
|
369
|
-
|
|
370
107
|
async pluginSetup() {
|
|
371
108
|
// Plugin has finished initialising so now onto setup
|
|
372
109
|
try {
|
|
@@ -1095,215 +832,14 @@ export default class {
|
|
|
1095
832
|
try {
|
|
1096
833
|
const deviceConf = this.deviceConf[device.device.toUpperCase()] || {}
|
|
1097
834
|
const uuid = this.api.hap.uuid.generate(device.device)
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1107
|
-
} else if (platformConsts.models.switchSingle.includes(device.model)) {
|
|
1108
|
-
// Device is a cloud enabled Wi-Fi switch
|
|
1109
|
-
switch (deviceConf.showAs || platformConsts.defaultValues.showAs) {
|
|
1110
|
-
case 'audio': {
|
|
1111
|
-
if (devicesInHB.get(uuid)) {
|
|
1112
|
-
this.removeAccessory(devicesInHB.get(uuid))
|
|
1113
|
-
}
|
|
1114
|
-
devInstance = deviceTypes.deviceTVSingle
|
|
1115
|
-
accessory = this.addExternalAccessory(device, 34)
|
|
1116
|
-
break
|
|
1117
|
-
}
|
|
1118
|
-
case 'box': {
|
|
1119
|
-
if (devicesInHB.get(uuid)) {
|
|
1120
|
-
this.removeAccessory(devicesInHB.get(uuid))
|
|
1121
|
-
}
|
|
1122
|
-
devInstance = deviceTypes.deviceTVSingle
|
|
1123
|
-
accessory = this.addExternalAccessory(device, 35)
|
|
1124
|
-
break
|
|
1125
|
-
}
|
|
1126
|
-
case 'stick': {
|
|
1127
|
-
if (devicesInHB.get(uuid)) {
|
|
1128
|
-
this.removeAccessory(devicesInHB.get(uuid))
|
|
1129
|
-
}
|
|
1130
|
-
devInstance = deviceTypes.deviceTVSingle
|
|
1131
|
-
accessory = this.addExternalAccessory(device, 36)
|
|
1132
|
-
break
|
|
1133
|
-
}
|
|
1134
|
-
case 'cooler': {
|
|
1135
|
-
if (!deviceConf.temperatureSource) {
|
|
1136
|
-
this.log.warn('[%s] %s.', device.deviceName, platformLang.heaterSimNoSensor)
|
|
1137
|
-
if (devicesInHB.has(uuid)) {
|
|
1138
|
-
this.removeAccessory(devicesInHB.get(uuid))
|
|
1139
|
-
}
|
|
1140
|
-
return
|
|
1141
|
-
}
|
|
1142
|
-
devInstance = deviceTypes.deviceCoolerSingle
|
|
1143
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1144
|
-
break
|
|
1145
|
-
}
|
|
1146
|
-
case 'heater': {
|
|
1147
|
-
if (!deviceConf.temperatureSource) {
|
|
1148
|
-
this.log.warn('[%s] %s.', device.deviceName, platformLang.heaterSimNoSensor)
|
|
1149
|
-
if (devicesInHB.has(uuid)) {
|
|
1150
|
-
this.removeAccessory(devicesInHB.get(uuid))
|
|
1151
|
-
}
|
|
1152
|
-
return
|
|
1153
|
-
}
|
|
1154
|
-
devInstance = deviceTypes.deviceHeater2Single
|
|
1155
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1156
|
-
break
|
|
1157
|
-
}
|
|
1158
|
-
case 'purifier': {
|
|
1159
|
-
devInstance = deviceTypes.devicePurifierSingle
|
|
1160
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1161
|
-
break
|
|
1162
|
-
}
|
|
1163
|
-
case 'switch': {
|
|
1164
|
-
devInstance = deviceTypes.deviceSwitchSingle
|
|
1165
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1166
|
-
break
|
|
1167
|
-
}
|
|
1168
|
-
case 'tap': {
|
|
1169
|
-
devInstance = deviceTypes.deviceTapSingle
|
|
1170
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1171
|
-
break
|
|
1172
|
-
}
|
|
1173
|
-
case 'valve': {
|
|
1174
|
-
devInstance = deviceTypes.deviceValveSingle
|
|
1175
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1176
|
-
break
|
|
1177
|
-
}
|
|
1178
|
-
default:
|
|
1179
|
-
devInstance = deviceTypes.deviceOutletSingle
|
|
1180
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1181
|
-
break
|
|
1182
|
-
}
|
|
1183
|
-
} else if (platformConsts.models.switchDouble.includes(device.model)) {
|
|
1184
|
-
// Device is an AWS enabled Wi-Fi double switch
|
|
1185
|
-
switch (deviceConf.showAs || platformConsts.defaultValues.showAs) {
|
|
1186
|
-
case 'switch': {
|
|
1187
|
-
devInstance = deviceTypes.deviceSwitchDouble
|
|
1188
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1189
|
-
break
|
|
1190
|
-
}
|
|
1191
|
-
default: {
|
|
1192
|
-
devInstance = deviceTypes.deviceOutletDouble
|
|
1193
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1194
|
-
break
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
} else if (platformConsts.models.switchTriple.includes(device.model)) {
|
|
1198
|
-
// Device is an AWS enabled Wi-Fi double switch
|
|
1199
|
-
switch (deviceConf.showAs || platformConsts.defaultValues.showAs) {
|
|
1200
|
-
case 'switch': {
|
|
1201
|
-
devInstance = deviceTypes.deviceSwitchTriple
|
|
1202
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1203
|
-
break
|
|
1204
|
-
}
|
|
1205
|
-
default: {
|
|
1206
|
-
devInstance = deviceTypes.deviceOutletTriple
|
|
1207
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1208
|
-
break
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
} else if (platformConsts.models.sensorLeak.includes(device.model)) {
|
|
1212
|
-
// Device is a leak sensor
|
|
1213
|
-
devInstance = deviceTypes.deviceSensorLeak
|
|
1214
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1215
|
-
} else if (platformConsts.models.sensorPresence.includes(device.model)) {
|
|
1216
|
-
// Device is a presence sensor
|
|
1217
|
-
devInstance = deviceTypes.deviceSensorPresence
|
|
1218
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1219
|
-
} else if (platformConsts.models.sensorThermo.includes(device.model)) {
|
|
1220
|
-
// Device is a thermo-hygrometer sensor
|
|
1221
|
-
devInstance = deviceConf.showExtraSwitch
|
|
1222
|
-
? deviceTypes.deviceSensorThermoSwitch
|
|
1223
|
-
: deviceTypes.deviceSensorThermo
|
|
1224
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1225
|
-
} else if (platformConsts.models.sensorThermo4.includes(device.model)) {
|
|
1226
|
-
// Device is a thermo-hygrometer sensor with 4 prongs and AWS support
|
|
1227
|
-
devInstance = deviceTypes.deviceSensorThermo4
|
|
1228
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1229
|
-
} else if (platformConsts.models.sensorMonitor.includes(device.model)) {
|
|
1230
|
-
devInstance = deviceTypes.deviceSensorMonitor
|
|
1231
|
-
doAWSPolling = true
|
|
1232
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1233
|
-
} else if (platformConsts.models.sensorCO2.includes(device.model)) {
|
|
1234
|
-
// Device is a CO2 + temperature + humidity monitor (H5140)
|
|
1235
|
-
devInstance = deviceTypes.deviceSensorCO2
|
|
1236
|
-
doAWSPolling = true
|
|
1237
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1238
|
-
} else if (platformConsts.models.fan.includes(device.model)) {
|
|
1239
|
-
// Device is a fan. Models whose speeds have not been worked out yet fall
|
|
1240
|
-
// back to the basic handler, which offers on and off and asks for what
|
|
1241
|
-
// it needs rather than leaving the device unsupported altogether
|
|
1242
|
-
devInstance = deviceTypes[`deviceFan${device.model}`] || deviceTypes.deviceFanBasic
|
|
1243
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1244
|
-
} else if (platformConsts.models.heater1.includes(device.model)) {
|
|
1245
|
-
// Some of these heaters report the room temperature and some do not,
|
|
1246
|
-
// and the two need different tiles in HomeKit - a plain fan against a
|
|
1247
|
-
// heater with a temperature readout. Rather than leave the owner to
|
|
1248
|
-
// work that out and tick a box, take Govee's own word for it where the
|
|
1249
|
-
// device says so, and otherwise use what a previous run learnt from the
|
|
1250
|
-
// readings themselves. The setting still wins if it has been set, so
|
|
1251
|
-
// nobody who has already configured this sees their tile change.
|
|
1252
|
-
const reportsTemperature = device.openApiInfo?.byInstance?.sensorTemperature
|
|
1253
|
-
? true
|
|
1254
|
-
: devicesInHB.get(uuid)?.context?.supportsAmbientTemp
|
|
1255
|
-
devInstance = (deviceConf.tempReporting ?? reportsTemperature)
|
|
1256
|
-
? deviceTypes.deviceHeater1B
|
|
1257
|
-
: deviceTypes.deviceHeater1A
|
|
1258
|
-
doAWSPolling = true
|
|
1259
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1260
|
-
} else if (platformConsts.models.heater2.includes(device.model)) {
|
|
1261
|
-
// Device is a H7131/H7132
|
|
1262
|
-
devInstance = deviceTypes.deviceHeater2
|
|
1263
|
-
doAWSPolling = true
|
|
1264
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1265
|
-
} else if (platformConsts.models.humidifier.includes(device.model)) {
|
|
1266
|
-
// Device is a humidifier
|
|
1267
|
-
doAWSPolling = true
|
|
1268
|
-
devInstance = deviceTypes[`deviceHumidifier${device.model}`] || deviceTypes.deviceHumidifierBasic
|
|
1269
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1270
|
-
} else if (platformConsts.models.dehumidifier.includes(device.model)) {
|
|
1271
|
-
// Device is a dehumidifier
|
|
1272
|
-
devInstance = deviceTypes[`deviceDehumidifier${device.model}`] || deviceTypes.deviceDehumidifierBasic
|
|
1273
|
-
doAWSPolling = true
|
|
1274
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1275
|
-
} else if (platformConsts.models.purifier.includes(device.model)) {
|
|
1276
|
-
// Device is a purifier
|
|
1277
|
-
devInstance = deviceTypes[`devicePurifier${device.model}`] || deviceTypes.devicePurifierBasic
|
|
1278
|
-
doAWSPolling = true
|
|
1279
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1280
|
-
} else if (platformConsts.models.diffuser.includes(device.model)) {
|
|
1281
|
-
// Device is a diffuser
|
|
1282
|
-
devInstance = deviceTypes[`deviceDiffuser${device.model}`]
|
|
1283
|
-
doAWSPolling = true
|
|
1284
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1285
|
-
} else if (platformConsts.models.sensorButton.includes(device.model)) {
|
|
1286
|
-
// Device is a button
|
|
1287
|
-
devInstance = deviceTypes.deviceSensorButton
|
|
1288
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1289
|
-
} else if (platformConsts.models.sensorContact.includes(device.model)) {
|
|
1290
|
-
// Device is a contact sensor
|
|
1291
|
-
devInstance = deviceTypes.deviceSensorContact
|
|
1292
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1293
|
-
} else if (platformConsts.models.kettle.includes(device.model)) {
|
|
1294
|
-
// Device is a kettle
|
|
1295
|
-
devInstance = deviceTypes.deviceKettle
|
|
1296
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1297
|
-
} else if (platformConsts.models.iceMaker.includes(device.model)) {
|
|
1298
|
-
// Device is an ice maker
|
|
1299
|
-
devInstance = deviceTypes[`deviceIceMaker${device.model}`] || deviceTypes.deviceIceMakerBasic
|
|
1300
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1301
|
-
} else if (platformConsts.models.template.includes(device.model)) {
|
|
1302
|
-
// Device is a work-in-progress
|
|
1303
|
-
devInstance = deviceTypes.deviceTemplate
|
|
1304
|
-
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1305
|
-
} else {
|
|
1306
|
-
// Device is not in any supported model list but could be implemented into the plugin
|
|
835
|
+
const cached = devicesInHB.get(uuid)
|
|
836
|
+
|
|
837
|
+
// Which handler this device gets is decided by the dispatch table, so
|
|
838
|
+
// adding a device type does not mean editing this file
|
|
839
|
+
const route = resolveDeviceType(device, deviceConf, cached?.context)
|
|
840
|
+
|
|
841
|
+
if (!route) {
|
|
842
|
+
// Not in any supported model list, but it could be added to the plugin
|
|
1307
843
|
this.log.warn(
|
|
1308
844
|
'[%s] %s:\n%s',
|
|
1309
845
|
device.deviceName,
|
|
@@ -1313,6 +849,31 @@ export default class {
|
|
|
1313
849
|
return
|
|
1314
850
|
}
|
|
1315
851
|
|
|
852
|
+
if (route.skip === 'needsTemperatureSource') {
|
|
853
|
+
// Standing in for a heater or cooler needs a temperature from somewhere
|
|
854
|
+
// else, and without one there is nothing to show
|
|
855
|
+
this.log.warn('[%s] %s.', device.deviceName, platformLang.heaterSimNoSensor)
|
|
856
|
+
if (cached) {
|
|
857
|
+
this.removeAccessory(cached)
|
|
858
|
+
}
|
|
859
|
+
return
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const devInstance = route.handler
|
|
863
|
+
const doAWSPolling = route.awsPolling
|
|
864
|
+
|
|
865
|
+
let accessory
|
|
866
|
+
if (route.externalCategory) {
|
|
867
|
+
// A television has to be published as its own accessory rather than as
|
|
868
|
+
// a tile on the bridge, so any tile from a previous setting goes first
|
|
869
|
+
if (cached) {
|
|
870
|
+
this.removeAccessory(cached)
|
|
871
|
+
}
|
|
872
|
+
accessory = this.addExternalAccessory(device, route.externalCategory)
|
|
873
|
+
} else {
|
|
874
|
+
accessory = cached || this.addAccessory(device)
|
|
875
|
+
}
|
|
876
|
+
|
|
1316
877
|
// Final check the accessory now exists in Homebridge
|
|
1317
878
|
if (!accessory) {
|
|
1318
879
|
throw new Error(platformLang.accNotFound)
|