@homebridge-plugins/homebridge-govee 11.32.2-beta.13 → 11.32.2-beta.14
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 +2 -0
- package/lib/device/ice-maker-H7172.js +12 -6
- package/lib/device/index.js +4 -7
- package/lib/device/switch-double.js +34 -15
- package/lib/device/switch-triple.js +36 -17
- package/lib/utils/device-capabilities.js +7 -0
- package/package.json +1 -1
- package/lib/device/ice-maker-H8121.js +0 -228
- package/lib/device/outlet-double.js +0 -188
- package/lib/device/outlet-triple.js +0 -160
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,8 @@ All notable changes to `@homebridge-plugins/homebridge-govee` will be documented
|
|
|
28
28
|
- refactor: point three more models at a shared handler instead of a near-copy of the same file, keeping each one's behaviour exactly as it was
|
|
29
29
|
- fix: give the H7124 the air quality handling the H7123 already had, so a poor reading is named properly and stops being logged over and over (#1261)
|
|
30
30
|
- refactor: point the H7151 and H7111 at the handlers they were byte-for-byte copies of
|
|
31
|
+
- refactor: build the ice maker size codes from the model rather than writing both orderings out by hand
|
|
32
|
+
- refactor: build the switch and outlet tiles of a multi-socket device from one handler instead of two copies of it
|
|
31
33
|
|
|
32
34
|
## v11.32.1 (2026-08-02)
|
|
33
35
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getDeviceCapabilities } from '../utils/device-capabilities.js'
|
|
2
|
+
import { base64ToHex, generateCodeFromHexValues, getTwoItemPosition, hexToTwoItems, parseError } from '../utils/functions.js'
|
|
2
3
|
import platformLang from '../utils/lang-en.js'
|
|
3
4
|
import { logUnknownData } from '../utils/report-unknown.js'
|
|
4
5
|
|
|
5
|
-
export default class {
|
|
6
|
+
export default class GoveeIceMaker {
|
|
6
7
|
constructor(platform, accessory) {
|
|
7
8
|
// Set up variables from the platform
|
|
8
9
|
this.hapChar = platform.api.hap.Characteristic
|
|
@@ -14,10 +15,15 @@ export default class {
|
|
|
14
15
|
this.accessory = accessory
|
|
15
16
|
|
|
16
17
|
// Ice size codes: small=1, medium=2, large=3 (matching OpenAPI workMode values)
|
|
18
|
+
// Small, medium and large are all `33 05 <size>`. Which byte means which
|
|
19
|
+
// size runs the opposite way on the older models, so the order comes from
|
|
20
|
+
// the model rather than being written out twice
|
|
21
|
+
const ascending = getDeviceCapabilities(accessory.context.gvModel).iceSizeAscending
|
|
22
|
+
const sizeBytes = ascending ? [0x01, 0x02, 0x03] : [0x03, 0x02, 0x01]
|
|
17
23
|
this.sizeCodes = {
|
|
18
|
-
1:
|
|
19
|
-
2:
|
|
20
|
-
3:
|
|
24
|
+
1: generateCodeFromHexValues([0x33, 0x05, sizeBytes[0]]), // small
|
|
25
|
+
2: generateCodeFromHexValues([0x33, 0x05, sizeBytes[1]]), // medium
|
|
26
|
+
3: generateCodeFromHexValues([0x33, 0x05, sizeBytes[2]]), // large
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
this.sizeLabels = {
|
|
@@ -204,7 +210,7 @@ export default class {
|
|
|
204
210
|
}
|
|
205
211
|
case 'aa05': {
|
|
206
212
|
// Ice size report. The payload encoding is deliberately NOT decoded here:
|
|
207
|
-
//
|
|
213
|
+
// the size byte order differs by model while the OpenAPI path sends
|
|
208
214
|
// workMode un-inverted, and which is authoritative is unresolved (#1329).
|
|
209
215
|
// Claiming a mapping here could silently swap sizes, so this case exists
|
|
210
216
|
// only to stop a routine status report being logged as a new scene code.
|
package/lib/device/index.js
CHANGED
|
@@ -24,13 +24,10 @@ import deviceHumidifierH7142 from './humidifier-H7142.js'
|
|
|
24
24
|
import deviceHumidifierH7143 from './humidifier-H7143.js'
|
|
25
25
|
import deviceHumidifierH7160 from './humidifier-H7160.js'
|
|
26
26
|
import deviceIceMakerH7172 from './ice-maker-H7172.js'
|
|
27
|
-
import deviceIceMakerH8121 from './ice-maker-H8121.js'
|
|
28
27
|
import deviceKettle from './kettle.js'
|
|
29
28
|
import deviceLightSwitch from './light-switch.js'
|
|
30
29
|
import deviceLight from './light.js'
|
|
31
|
-
import deviceOutletDouble from './outlet-double.js'
|
|
32
30
|
import deviceOutletSingle from './outlet-single.js'
|
|
33
|
-
import deviceOutletTriple from './outlet-triple.js'
|
|
34
31
|
import devicePurifierH7120 from './purifier-H7120.js'
|
|
35
32
|
import devicePurifierH7122 from './purifier-H7122.js'
|
|
36
33
|
import devicePurifierH7123 from './purifier-H7123.js'
|
|
@@ -44,9 +41,9 @@ import deviceSensorPresence from './sensor-presence.js'
|
|
|
44
41
|
import deviceSensorThermo4 from './sensor-thermo4.js'
|
|
45
42
|
import deviceSensorThermoSwitch from './sensor-thermo-switch.js'
|
|
46
43
|
import deviceSensorThermo from './sensor-thermo.js'
|
|
47
|
-
import deviceSwitchDouble from './switch-double.js'
|
|
44
|
+
import deviceSwitchDouble, { asOutlet as deviceOutletDouble } from './switch-double.js'
|
|
48
45
|
import deviceSwitchSingle from './switch-single.js'
|
|
49
|
-
import deviceSwitchTriple from './switch-triple.js'
|
|
46
|
+
import deviceSwitchTriple, { asOutlet as deviceOutletTriple } from './switch-triple.js'
|
|
50
47
|
import deviceTapSingle from './tap-single.js'
|
|
51
48
|
import deviceTemplate from './template.js'
|
|
52
49
|
import deviceTVSingle from './tv-single.js'
|
|
@@ -90,8 +87,8 @@ export default {
|
|
|
90
87
|
deviceIceMakerH7172,
|
|
91
88
|
deviceIceMakerH717D: deviceIceMakerH7172, // identical to the H7172
|
|
92
89
|
deviceIceMakerH8120: deviceIceMakerH7172, // identical to the H7172
|
|
93
|
-
deviceIceMakerH8121,
|
|
94
|
-
deviceIceMakerH8122:
|
|
90
|
+
deviceIceMakerH8121: deviceIceMakerH7172, // same handler; its size byte order comes from the model
|
|
91
|
+
deviceIceMakerH8122: deviceIceMakerH7172, // identical to the H8121
|
|
95
92
|
deviceKettle,
|
|
96
93
|
deviceLight,
|
|
97
94
|
deviceLightSwitch,
|
|
@@ -8,8 +8,8 @@ import platformLang from '../utils/lang-en.js'
|
|
|
8
8
|
// outright rather than accepted and silently dropped (#1323).
|
|
9
9
|
const nightLightModels = ['H5089']
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
constructor(platform, accessory) {
|
|
11
|
+
class MultiSwitchDouble {
|
|
12
|
+
constructor(platform, accessory, config) {
|
|
13
13
|
// Set up variables from the platform
|
|
14
14
|
this.hapChar = platform.api.hap.Characteristic
|
|
15
15
|
this.hapErr = platform.api.hap.HapStatusError
|
|
@@ -19,12 +19,13 @@ export default class {
|
|
|
19
19
|
// Set up variables from the accessory
|
|
20
20
|
this.accessory = accessory
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
// Drop the tiles of the other kind, left behind if this device has been
|
|
23
|
+
// shown as the other one before
|
|
24
|
+
if (this.accessory.getService(`${config.other} 1`)) {
|
|
25
|
+
this.accessory.removeService(this.accessory.getService(`${config.other} 1`))
|
|
25
26
|
}
|
|
26
|
-
if (this.accessory.getService(
|
|
27
|
-
this.accessory.removeService(this.accessory.getService(
|
|
27
|
+
if (this.accessory.getService(`${config.other} 2`)) {
|
|
28
|
+
this.accessory.removeService(this.accessory.getService(`${config.other} 2`))
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
// A model that used to be treated as a single switch or outlet leaves an
|
|
@@ -35,15 +36,15 @@ export default class {
|
|
|
35
36
|
&& [this.hapServ.Switch.UUID, this.hapServ.Outlet.UUID].includes(service.UUID))
|
|
36
37
|
.forEach(service => this.accessory.removeService(service))
|
|
37
38
|
|
|
38
|
-
// Add
|
|
39
|
-
this.service1 = this.accessory.getService(
|
|
40
|
-
|| this.accessory.addService(this.hapServ.
|
|
41
|
-
this.service2 = this.accessory.getService(
|
|
42
|
-
|| this.accessory.addService(this.hapServ.
|
|
39
|
+
// Add this device's tiles if they are not there already
|
|
40
|
+
this.service1 = this.accessory.getService(`${config.label} 1`)
|
|
41
|
+
|| this.accessory.addService(this.hapServ[config.label], `${config.label} 1`, `${config.subtype}1`)
|
|
42
|
+
this.service2 = this.accessory.getService(`${config.label} 2`)
|
|
43
|
+
|| this.accessory.addService(this.hapServ[config.label], `${config.label} 2`, `${config.subtype}2`)
|
|
43
44
|
|
|
44
45
|
if (!this.service1.testCharacteristic(this.hapChar.ConfiguredName)) {
|
|
45
46
|
this.service1.addCharacteristic(this.hapChar.ConfiguredName)
|
|
46
|
-
this.service1.updateCharacteristic(this.hapChar.ConfiguredName,
|
|
47
|
+
this.service1.updateCharacteristic(this.hapChar.ConfiguredName, `${config.label} 1`)
|
|
47
48
|
}
|
|
48
49
|
if (!this.service1.testCharacteristic(this.hapChar.ServiceLabelIndex)) {
|
|
49
50
|
this.service1.addCharacteristic(this.hapChar.ServiceLabelIndex)
|
|
@@ -51,7 +52,7 @@ export default class {
|
|
|
51
52
|
}
|
|
52
53
|
if (!this.service2.testCharacteristic(this.hapChar.ConfiguredName)) {
|
|
53
54
|
this.service2.addCharacteristic(this.hapChar.ConfiguredName)
|
|
54
|
-
this.service2.updateCharacteristic(this.hapChar.ConfiguredName,
|
|
55
|
+
this.service2.updateCharacteristic(this.hapChar.ConfiguredName, `${config.label} 2`)
|
|
55
56
|
}
|
|
56
57
|
if (!this.service2.testCharacteristic(this.hapChar.ServiceLabelIndex)) {
|
|
57
58
|
this.service2.addCharacteristic(this.hapChar.ServiceLabelIndex)
|
|
@@ -86,7 +87,7 @@ export default class {
|
|
|
86
87
|
|
|
87
88
|
// Output the customised options to the log
|
|
88
89
|
const opts = JSON.stringify({
|
|
89
|
-
showAs:
|
|
90
|
+
showAs: config.showAs,
|
|
90
91
|
})
|
|
91
92
|
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
92
93
|
|
|
@@ -186,3 +187,21 @@ export default class {
|
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* A double switch and a double outlet are the same device with a different tile
|
|
193
|
+
* in the Home app, chosen by the owner's `showAs` setting. Only the service
|
|
194
|
+
* type, the tile names and the logged option differ, so they share this and are
|
|
195
|
+
* built from it rather than from a copy of it.
|
|
196
|
+
*/
|
|
197
|
+
function make(config) {
|
|
198
|
+
return class extends MultiSwitchDouble {
|
|
199
|
+
constructor(platform, accessory) {
|
|
200
|
+
super(platform, accessory, config)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default make({ label: 'Switch', subtype: 'switch', other: 'Outlet', showAs: 'switch' })
|
|
206
|
+
|
|
207
|
+
export const asOutlet = make({ label: 'Outlet', subtype: 'outlet', other: 'Switch', showAs: 'outlet' })
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { parseError } from '../utils/functions.js'
|
|
2
2
|
import platformLang from '../utils/lang-en.js'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
constructor(platform, accessory) {
|
|
4
|
+
class MultiSwitchTriple {
|
|
5
|
+
constructor(platform, accessory, config) {
|
|
6
6
|
// Set up variables from the platform
|
|
7
7
|
this.hapChar = platform.api.hap.Characteristic
|
|
8
8
|
this.hapErr = platform.api.hap.HapStatusError
|
|
@@ -12,24 +12,25 @@ export default class {
|
|
|
12
12
|
// Set up variables from the accessory
|
|
13
13
|
this.accessory = accessory
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
// Drop the tiles of the other kind, left behind if this device has been
|
|
16
|
+
// shown as the other one before
|
|
17
|
+
if (this.accessory.getService(`${config.other} 1`)) {
|
|
18
|
+
this.accessory.removeService(this.accessory.getService(`${config.other} 1`))
|
|
18
19
|
}
|
|
19
|
-
if (this.accessory.getService(
|
|
20
|
-
this.accessory.removeService(this.accessory.getService(
|
|
20
|
+
if (this.accessory.getService(`${config.other} 2`)) {
|
|
21
|
+
this.accessory.removeService(this.accessory.getService(`${config.other} 2`))
|
|
21
22
|
}
|
|
22
|
-
if (this.accessory.getService(
|
|
23
|
-
this.accessory.removeService(this.accessory.getService(
|
|
23
|
+
if (this.accessory.getService(`${config.other} 3`)) {
|
|
24
|
+
this.accessory.removeService(this.accessory.getService(`${config.other} 3`))
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
// Add
|
|
27
|
-
this.service1 = this.accessory.getService(
|
|
28
|
-
|| this.accessory.addService(this.hapServ.
|
|
29
|
-
this.service2 = this.accessory.getService(
|
|
30
|
-
|| this.accessory.addService(this.hapServ.
|
|
31
|
-
this.service3 = this.accessory.getService(
|
|
32
|
-
|| this.accessory.addService(this.hapServ.
|
|
27
|
+
// Add this device's tiles if they are not there already
|
|
28
|
+
this.service1 = this.accessory.getService(`${config.label} 1`)
|
|
29
|
+
|| this.accessory.addService(this.hapServ[config.label], `${config.label} 1`, `${config.subtype}1`)
|
|
30
|
+
this.service2 = this.accessory.getService(`${config.label} 2`)
|
|
31
|
+
|| this.accessory.addService(this.hapServ[config.label], `${config.label} 2`, `${config.subtype}2`)
|
|
32
|
+
this.service3 = this.accessory.getService(`${config.label} 3`)
|
|
33
|
+
|| this.accessory.addService(this.hapServ[config.label], `${config.label} 3`, `${config.subtype}3`)
|
|
33
34
|
|
|
34
35
|
// Add the set handler to the switch on/off characteristic
|
|
35
36
|
this.service1.getCharacteristic(this.hapChar.On).onSet(async (value) => {
|
|
@@ -47,7 +48,7 @@ export default class {
|
|
|
47
48
|
|
|
48
49
|
// Output the customised options to the log
|
|
49
50
|
const opts = JSON.stringify({
|
|
50
|
-
showAs:
|
|
51
|
+
showAs: config.showAs,
|
|
51
52
|
})
|
|
52
53
|
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
53
54
|
|
|
@@ -158,3 +159,21 @@ export default class {
|
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* A triple switch and a triple outlet are the same device with a different tile
|
|
165
|
+
* in the Home app, chosen by the owner's `showAs` setting. Only the service
|
|
166
|
+
* type, the tile names and the logged option differ, so they share this and are
|
|
167
|
+
* built from it rather than from a copy of it.
|
|
168
|
+
*/
|
|
169
|
+
function make(config) {
|
|
170
|
+
return class extends MultiSwitchTriple {
|
|
171
|
+
constructor(platform, accessory) {
|
|
172
|
+
super(platform, accessory, config)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default make({ label: 'Switch', subtype: 'switch', other: 'Outlet', showAs: 'switch' })
|
|
178
|
+
|
|
179
|
+
export const asOutlet = make({ label: 'Outlet', subtype: 'outlet', other: 'Switch', showAs: 'outlet' })
|
|
@@ -41,6 +41,12 @@ const modelOverrides = {
|
|
|
41
41
|
H1310: { readsSwingStatus: false },
|
|
42
42
|
R1310: { readsSwingStatus: false },
|
|
43
43
|
|
|
44
|
+
// Later ice makers number their ice sizes the natural way round. The older
|
|
45
|
+
// ones run the other way, small being the highest byte, so the two families
|
|
46
|
+
// share a handler and differ only here
|
|
47
|
+
H8121: { iceSizeAscending: true },
|
|
48
|
+
H8122: { iceSizeAscending: true },
|
|
49
|
+
|
|
44
50
|
// AWS outlet uses 17/16 for on/off instead of 1/0
|
|
45
51
|
H5080: { awsPowerOn: 17, awsPowerOff: 16 },
|
|
46
52
|
H5083: { awsPowerOn: 17, awsPowerOff: 16 },
|
|
@@ -109,6 +115,7 @@ const defaults = {
|
|
|
109
115
|
awsStatusCmdVersion: 2,
|
|
110
116
|
awsBrightnessNoScale: false,
|
|
111
117
|
readsSwingStatus: true,
|
|
118
|
+
iceSizeAscending: false,
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
export function getDeviceCapabilities(model, firmware) {
|
package/package.json
CHANGED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import { base64ToHex, getTwoItemPosition, hexToTwoItems, parseError } from '../utils/functions.js'
|
|
2
|
-
import platformLang from '../utils/lang-en.js'
|
|
3
|
-
import { logUnknownData } from '../utils/report-unknown.js'
|
|
4
|
-
|
|
5
|
-
export default class GoveeIceMakerH8121 {
|
|
6
|
-
constructor(platform, accessory) {
|
|
7
|
-
// Set up variables from the platform
|
|
8
|
-
this.hapChar = platform.api.hap.Characteristic
|
|
9
|
-
this.hapErr = platform.api.hap.HapStatusError
|
|
10
|
-
this.hapServ = platform.api.hap.Service
|
|
11
|
-
this.platform = platform
|
|
12
|
-
|
|
13
|
-
// Set up variables from the accessory
|
|
14
|
-
this.accessory = accessory
|
|
15
|
-
|
|
16
|
-
// Ice size codes: small=1, medium=2, large=3, matching the OpenAPI workMode
|
|
17
|
-
// values. Unlike the H8120, this generation's BLE size byte runs in the SAME
|
|
18
|
-
// direction as workMode (small=0x05 01 .. large=0x05 03), so the two paths
|
|
19
|
-
// agree here and no inversion is needed.
|
|
20
|
-
this.sizeCodes = {
|
|
21
|
-
1: 'MwUBAAAAAAAAAAAAAAAAAAAAADc=', // small -> 33 05 01
|
|
22
|
-
2: 'MwUCAAAAAAAAAAAAAAAAAAAAADQ=', // medium -> 33 05 02
|
|
23
|
-
3: 'MwUDAAAAAAAAAAAAAAAAAAAAADU=', // large -> 33 05 03
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
this.sizeLabels = {
|
|
27
|
-
1: 'small',
|
|
28
|
-
2: 'medium',
|
|
29
|
-
3: 'large',
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Remove old switch service if migrating
|
|
33
|
-
if (this.accessory.getService(this.hapServ.Switch)) {
|
|
34
|
-
this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Add the fan service for ice size control
|
|
38
|
-
this.service = this.accessory.getService(this.hapServ.Fanv2)
|
|
39
|
-
|| this.accessory.addService(this.hapServ.Fanv2)
|
|
40
|
-
|
|
41
|
-
// Add the set handler to the on/off characteristic
|
|
42
|
-
this.service.getCharacteristic(this.hapChar.Active).onSet(async (value) => {
|
|
43
|
-
await this.internalStateUpdate(value)
|
|
44
|
-
})
|
|
45
|
-
this.cacheState = this.service.getCharacteristic(this.hapChar.Active).value === 1 ? 'on' : 'off'
|
|
46
|
-
|
|
47
|
-
// Add the set handler to the rotation speed characteristic (ice size)
|
|
48
|
-
this.service
|
|
49
|
-
.getCharacteristic(this.hapChar.RotationSpeed)
|
|
50
|
-
.setProps({
|
|
51
|
-
maxValue: 3,
|
|
52
|
-
minStep: 1,
|
|
53
|
-
minValue: 0,
|
|
54
|
-
unit: 'unitless',
|
|
55
|
-
})
|
|
56
|
-
.onSet(async value => this.internalSizeUpdate(value))
|
|
57
|
-
this.cacheSize = this.service.getCharacteristic(this.hapChar.RotationSpeed).value || 2
|
|
58
|
-
|
|
59
|
-
// Keep the characteristic in step with the cache. Without this an accessory
|
|
60
|
-
// that has never had a size set reports Active=1 with RotationSpeed=0, which
|
|
61
|
-
// is contradictory and which the Home app renders as "0".
|
|
62
|
-
if (this.service.getCharacteristic(this.hapChar.RotationSpeed).value !== this.cacheSize) {
|
|
63
|
-
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Output the customised options to the log
|
|
67
|
-
const opts = JSON.stringify({})
|
|
68
|
-
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async internalStateUpdate(value) {
|
|
72
|
-
try {
|
|
73
|
-
const newState = value ? 'on' : 'off'
|
|
74
|
-
|
|
75
|
-
// Don't continue if the new value is the same as before
|
|
76
|
-
if (newState === this.cacheState) {
|
|
77
|
-
return
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (value) {
|
|
81
|
-
// Turn on with current cached ice size
|
|
82
|
-
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
83
|
-
cmd: 'ptReal',
|
|
84
|
-
value: this.sizeCodes[this.cacheSize],
|
|
85
|
-
openApi: this.accessory.context.openApiCapabilities?.workMode
|
|
86
|
-
? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: this.cacheSize, modeValue: 0 } }
|
|
87
|
-
: undefined,
|
|
88
|
-
})
|
|
89
|
-
} else {
|
|
90
|
-
// Turn off. The OpenAPI workMode=0 path is authoritative; the BLE frame
|
|
91
|
-
// mirrors the H8120 ice-maker-off command (33 19 00) as a best effort
|
|
92
|
-
// and is pending confirmation from an owner of either model.
|
|
93
|
-
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
94
|
-
cmd: 'ptReal',
|
|
95
|
-
value: 'MxkAAAAAAAAAAAAAAAAAAAAAACo=',
|
|
96
|
-
openApi: this.accessory.context.openApiCapabilities?.workMode
|
|
97
|
-
? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: 0, modeValue: 0 } }
|
|
98
|
-
: undefined,
|
|
99
|
-
})
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Cache the new state and log if appropriate
|
|
103
|
-
if (this.cacheState !== newState) {
|
|
104
|
-
this.cacheState = newState
|
|
105
|
-
this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
|
|
106
|
-
}
|
|
107
|
-
} catch (err) {
|
|
108
|
-
// Catch any errors during the process
|
|
109
|
-
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
110
|
-
|
|
111
|
-
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
112
|
-
setTimeout(() => {
|
|
113
|
-
this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
|
|
114
|
-
}, 2000)
|
|
115
|
-
throw new this.hapErr(-70402)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async internalSizeUpdate(value) {
|
|
120
|
-
try {
|
|
121
|
-
// 0 is not a valid ice size. Put the slider back, otherwise HomeKit keeps 0
|
|
122
|
-
// while the device carries on at cacheSize - and that 0 is what gets written
|
|
123
|
-
// to the accessory cache, so it returns on the next restart. Deferred for the
|
|
124
|
-
// same reason as the error reverts below: an immediate update would be
|
|
125
|
-
// overwritten by the value HomeKit is currently writing.
|
|
126
|
-
if (value === 0) {
|
|
127
|
-
setTimeout(() => {
|
|
128
|
-
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
129
|
-
}, 1000)
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Don't continue if the new value is the same as before
|
|
134
|
-
if (value === this.cacheSize) {
|
|
135
|
-
return
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
139
|
-
cmd: 'ptReal',
|
|
140
|
-
value: this.sizeCodes[value],
|
|
141
|
-
openApi: this.accessory.context.openApiCapabilities?.workMode
|
|
142
|
-
? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: value, modeValue: 0 } }
|
|
143
|
-
: undefined,
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
// Cache the new state and log if appropriate
|
|
147
|
-
this.cacheSize = value
|
|
148
|
-
this.cacheState = 'on'
|
|
149
|
-
this.service.updateCharacteristic(this.hapChar.Active, 1)
|
|
150
|
-
this.accessory.log(`${platformLang.curSpeed} [${this.sizeLabels[value]}]`)
|
|
151
|
-
} catch (err) {
|
|
152
|
-
// Catch any errors during the process
|
|
153
|
-
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
154
|
-
|
|
155
|
-
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
156
|
-
setTimeout(() => {
|
|
157
|
-
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
158
|
-
}, 2000)
|
|
159
|
-
throw new this.hapErr(-70402)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
externalUpdate(params) {
|
|
164
|
-
// Handle OpenAPI workMode
|
|
165
|
-
if (params.workMode) {
|
|
166
|
-
const mode = params.workMode.workMode
|
|
167
|
-
if (mode > 0 && mode <= 3) {
|
|
168
|
-
if (this.cacheState !== 'on') {
|
|
169
|
-
this.cacheState = 'on'
|
|
170
|
-
this.service.updateCharacteristic(this.hapChar.Active, 1)
|
|
171
|
-
this.accessory.log(`${platformLang.curState} [on]`)
|
|
172
|
-
}
|
|
173
|
-
if (this.cacheSize !== mode) {
|
|
174
|
-
this.cacheSize = mode
|
|
175
|
-
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
176
|
-
this.accessory.log(`${platformLang.curSpeed} [${this.sizeLabels[this.cacheSize]}]`)
|
|
177
|
-
}
|
|
178
|
-
} else if (mode === 0) {
|
|
179
|
-
if (this.cacheState !== 'off') {
|
|
180
|
-
this.cacheState = 'off'
|
|
181
|
-
this.service.updateCharacteristic(this.hapChar.Active, 0)
|
|
182
|
-
this.accessory.log(`${platformLang.curState} [off]`)
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Check for some other scene/mode change
|
|
188
|
-
(params.commands || []).forEach((command) => {
|
|
189
|
-
const hexString = base64ToHex(command)
|
|
190
|
-
const hexParts = hexToTwoItems(hexString)
|
|
191
|
-
|
|
192
|
-
// Return now if not a device query update code
|
|
193
|
-
if (getTwoItemPosition(hexParts, 1) !== 'aa') {
|
|
194
|
-
return
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const deviceFunction = `${getTwoItemPosition(hexParts, 1)}${getTwoItemPosition(hexParts, 2)}`
|
|
198
|
-
|
|
199
|
-
switch (deviceFunction) {
|
|
200
|
-
case 'aa19': {
|
|
201
|
-
// On/Off
|
|
202
|
-
const newState = getTwoItemPosition(hexParts, 3) === '01' ? 'on' : 'off'
|
|
203
|
-
if (this.cacheState !== newState) {
|
|
204
|
-
this.cacheState = newState
|
|
205
|
-
this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
|
|
206
|
-
this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
|
|
207
|
-
}
|
|
208
|
-
break
|
|
209
|
-
}
|
|
210
|
-
case 'aa05': {
|
|
211
|
-
// Ice size report. Left undecoded for now: the OpenAPI workMode report
|
|
212
|
-
// above is the authoritative source of the current size, so decoding
|
|
213
|
-
// the BLE echo would only risk a mismatch. This case exists to stop a
|
|
214
|
-
// routine status report being logged as an unrecognised scene code.
|
|
215
|
-
break
|
|
216
|
-
}
|
|
217
|
-
default:
|
|
218
|
-
logUnknownData(this.accessory, {
|
|
219
|
-
kind: 'scene',
|
|
220
|
-
source: params.source,
|
|
221
|
-
raw: command,
|
|
222
|
-
hex: hexString,
|
|
223
|
-
})
|
|
224
|
-
break
|
|
225
|
-
}
|
|
226
|
-
})
|
|
227
|
-
}
|
|
228
|
-
}
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { base64ToHex, parseError } from '../utils/functions.js'
|
|
2
|
-
import platformLang from '../utils/lang-en.js'
|
|
3
|
-
|
|
4
|
-
// Models that expose a night light. The H5089 reports its light as
|
|
5
|
-
// `aa 1b 01 [on] [level]` and that reading is correct, so the tile shows the
|
|
6
|
-
// real state. No command shape tried moves it - neither mirroring its own
|
|
7
|
-
// status code nor the `33 18` the purifiers use - so control is refused
|
|
8
|
-
// outright rather than accepted and silently dropped (#1323).
|
|
9
|
-
const nightLightModels = ['H5089']
|
|
10
|
-
|
|
11
|
-
export default class {
|
|
12
|
-
constructor(platform, accessory) {
|
|
13
|
-
// Set up variables from the platform
|
|
14
|
-
this.hapChar = platform.api.hap.Characteristic
|
|
15
|
-
this.hapErr = platform.api.hap.HapStatusError
|
|
16
|
-
this.hapServ = platform.api.hap.Service
|
|
17
|
-
this.platform = platform
|
|
18
|
-
|
|
19
|
-
// Set up variables from the accessory
|
|
20
|
-
this.accessory = accessory
|
|
21
|
-
|
|
22
|
-
// Remove switch services if they exist
|
|
23
|
-
if (this.accessory.getService('Switch 1')) {
|
|
24
|
-
this.accessory.removeService(this.accessory.getService('Switch 1'))
|
|
25
|
-
}
|
|
26
|
-
if (this.accessory.getService('Switch 2')) {
|
|
27
|
-
this.accessory.removeService(this.accessory.getService('Switch 2'))
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// A model that used to be treated as a single switch or outlet leaves an
|
|
31
|
-
// unnumbered service behind, which would sit next to the numbered pair as
|
|
32
|
-
// a tile that no longer does anything (#1323)
|
|
33
|
-
this.accessory.services
|
|
34
|
-
.filter(service => !service.subtype
|
|
35
|
-
&& [this.hapServ.Switch.UUID, this.hapServ.Outlet.UUID].includes(service.UUID))
|
|
36
|
-
.forEach(service => this.accessory.removeService(service))
|
|
37
|
-
|
|
38
|
-
// Add the outlet services if they don't already exist
|
|
39
|
-
this.service1 = this.accessory.getService('Outlet 1')
|
|
40
|
-
|| this.accessory.addService(this.hapServ.Outlet, 'Outlet 1', 'outlet1')
|
|
41
|
-
this.service2 = this.accessory.getService('Outlet 2')
|
|
42
|
-
|| this.accessory.addService(this.hapServ.Outlet, 'Outlet 2', 'outlet2')
|
|
43
|
-
|
|
44
|
-
if (!this.service1.testCharacteristic(this.hapChar.ConfiguredName)) {
|
|
45
|
-
this.service1.addCharacteristic(this.hapChar.ConfiguredName)
|
|
46
|
-
this.service1.updateCharacteristic(this.hapChar.ConfiguredName, 'Outlet 1')
|
|
47
|
-
}
|
|
48
|
-
if (!this.service1.testCharacteristic(this.hapChar.ServiceLabelIndex)) {
|
|
49
|
-
this.service1.addCharacteristic(this.hapChar.ServiceLabelIndex)
|
|
50
|
-
this.service1.updateCharacteristic(this.hapChar.ServiceLabelIndex, 1)
|
|
51
|
-
}
|
|
52
|
-
if (!this.service2.testCharacteristic(this.hapChar.ConfiguredName)) {
|
|
53
|
-
this.service2.addCharacteristic(this.hapChar.ConfiguredName)
|
|
54
|
-
this.service2.updateCharacteristic(this.hapChar.ConfiguredName, 'Outlet 2')
|
|
55
|
-
}
|
|
56
|
-
if (!this.service2.testCharacteristic(this.hapChar.ServiceLabelIndex)) {
|
|
57
|
-
this.service2.addCharacteristic(this.hapChar.ServiceLabelIndex)
|
|
58
|
-
this.service2.updateCharacteristic(this.hapChar.ServiceLabelIndex, 2)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Add the set handler to the switch on/off characteristic
|
|
62
|
-
this.service1.getCharacteristic(this.hapChar.On).onSet(async (value) => {
|
|
63
|
-
await this.internalStateUpdate(this.service1, value ? 17 : 16)
|
|
64
|
-
})
|
|
65
|
-
this.service1.cacheState = this.service1.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
66
|
-
this.service2.getCharacteristic(this.hapChar.On).onSet(async (value) => {
|
|
67
|
-
await this.internalStateUpdate(this.service2, value ? 34 : 32)
|
|
68
|
-
})
|
|
69
|
-
this.service2.cacheState = this.service2.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
70
|
-
|
|
71
|
-
// Add a night light service for the models that have one
|
|
72
|
-
if (nightLightModels.includes(this.accessory.context.gvModel)) {
|
|
73
|
-
this.lightService = this.accessory.getService('Night Light')
|
|
74
|
-
|| this.accessory.addService(this.hapServ.Lightbulb, 'Night Light', 'nightlight')
|
|
75
|
-
if (!this.lightService.testCharacteristic(this.hapChar.ConfiguredName)) {
|
|
76
|
-
this.lightService.addCharacteristic(this.hapChar.ConfiguredName)
|
|
77
|
-
this.lightService.updateCharacteristic(this.hapChar.ConfiguredName, 'Night Light')
|
|
78
|
-
}
|
|
79
|
-
this.lightService.getCharacteristic(this.hapChar.On).onSet(() => this.refuseLightControl())
|
|
80
|
-
this.cacheLightState = this.lightService.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
81
|
-
this.lightService.getCharacteristic(this.hapChar.Brightness).onSet(() => this.refuseLightControl())
|
|
82
|
-
this.cacheLightBright = this.lightService.getCharacteristic(this.hapChar.Brightness).value || 100
|
|
83
|
-
} else if (this.accessory.getService('Night Light')) {
|
|
84
|
-
this.accessory.removeService(this.accessory.getService('Night Light'))
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Output the customised options to the log
|
|
88
|
-
const opts = JSON.stringify({
|
|
89
|
-
showAs: 'outlet',
|
|
90
|
-
})
|
|
91
|
-
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
92
|
-
|
|
93
|
-
// The value is (which outlets to change << 4) | (which of those to turn on)
|
|
94
|
-
// 51 turns BOTH ON
|
|
95
|
-
// 48 turns BOTH OFF
|
|
96
|
-
// 17 turns outlet 1 ON
|
|
97
|
-
// 16 turns outlet 1 OFF
|
|
98
|
-
// 34 turns outlet 2 ON
|
|
99
|
-
// 32 turns outlet 2 OFF
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async internalStateUpdate(service, value) {
|
|
103
|
-
try {
|
|
104
|
-
const newValue = value % 16 === 0 ? 'off' : 'on'
|
|
105
|
-
|
|
106
|
-
// Don't continue if the new value is the same as before
|
|
107
|
-
if (service.cacheState === newValue) {
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Send the request to the platform sender function
|
|
112
|
-
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
113
|
-
cmd: 'stateDual',
|
|
114
|
-
value,
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
// Cache the new state and log if appropriate
|
|
118
|
-
if (service.cacheState !== newValue) {
|
|
119
|
-
service.cacheState = newValue
|
|
120
|
-
this.accessory.log(`[${service.getCharacteristic(this.hapChar.ConfiguredName).value}] ${platformLang.curState} [${newValue}]`)
|
|
121
|
-
}
|
|
122
|
-
} catch (err) {
|
|
123
|
-
// Catch any errors during the process
|
|
124
|
-
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
125
|
-
|
|
126
|
-
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
127
|
-
setTimeout(() => {
|
|
128
|
-
service.updateCharacteristic(this.hapChar.On, service.cacheState === 'on')
|
|
129
|
-
}, 2000)
|
|
130
|
-
throw new this.hapErr(-70402)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// The night light reading is correct, but every command shape tried has been
|
|
135
|
-
// ignored by the plug, so refuse the write. HomeKit reverts the tile rather
|
|
136
|
-
// than showing a state the plug was never given (#1323)
|
|
137
|
-
refuseLightControl() {
|
|
138
|
-
this.accessory.logWarn(`[Night Light] ${platformLang.devNotUpdated} this device does not accept night light commands`)
|
|
139
|
-
throw new this.hapErr(-70402)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
externalUpdate(params) {
|
|
143
|
-
if (Array.isArray(params.state)) {
|
|
144
|
-
if (params.state[0] !== this.service1.cacheState) {
|
|
145
|
-
this.service1.cacheState = params.state[0]
|
|
146
|
-
this.service1.updateCharacteristic(this.hapChar.On, this.service1.cacheState === 'on')
|
|
147
|
-
|
|
148
|
-
this.accessory.log(`[${this.service1.getCharacteristic(this.hapChar.ConfiguredName).value}] ${platformLang.curState} [${this.service1.cacheState}]`)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (params.state[1] !== this.service2.cacheState) {
|
|
152
|
-
this.service2.cacheState = params.state[1]
|
|
153
|
-
this.service2.updateCharacteristic(this.hapChar.On, this.service2.cacheState === 'on')
|
|
154
|
-
|
|
155
|
-
this.accessory.log(`[${this.service2.getCharacteristic(this.hapChar.ConfiguredName).value}] ${platformLang.curState} [${this.service2.cacheState}]`)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// The night light reports as `aa 1b 01 [on] [level]` (#1323)
|
|
160
|
-
if (this.lightService && Array.isArray(params.commands)) {
|
|
161
|
-
params.commands.forEach((command) => {
|
|
162
|
-
let hexString
|
|
163
|
-
try {
|
|
164
|
-
hexString = base64ToHex(command)
|
|
165
|
-
} catch {
|
|
166
|
-
return
|
|
167
|
-
}
|
|
168
|
-
if (!hexString.startsWith('aa1b01')) {
|
|
169
|
-
return
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const newState = hexString.substring(6, 8) === '01' ? 'on' : 'off'
|
|
173
|
-
if (newState !== this.cacheLightState) {
|
|
174
|
-
this.cacheLightState = newState
|
|
175
|
-
this.lightService.updateCharacteristic(this.hapChar.On, newState === 'on')
|
|
176
|
-
this.accessory.log(`[Night Light] ${platformLang.curState} [${newState}]`)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const newBright = Number.parseInt(hexString.substring(8, 10), 16)
|
|
180
|
-
if (newBright >= 1 && newBright <= 100 && newBright !== this.cacheLightBright) {
|
|
181
|
-
this.cacheLightBright = newBright
|
|
182
|
-
this.lightService.updateCharacteristic(this.hapChar.Brightness, newBright)
|
|
183
|
-
this.accessory.log(`[Night Light] ${platformLang.curBright} [${newBright}%]`)
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { parseError } from '../utils/functions.js'
|
|
2
|
-
import platformLang from '../utils/lang-en.js'
|
|
3
|
-
|
|
4
|
-
export default class {
|
|
5
|
-
constructor(platform, accessory) {
|
|
6
|
-
// Set up variables from the platform
|
|
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
|
-
// Remove switch services if they exist
|
|
16
|
-
if (this.accessory.getService('Switch 1')) {
|
|
17
|
-
this.accessory.removeService(this.accessory.getService('Switch 1'))
|
|
18
|
-
}
|
|
19
|
-
if (this.accessory.getService('Switch 2')) {
|
|
20
|
-
this.accessory.removeService(this.accessory.getService('Switch 2'))
|
|
21
|
-
}
|
|
22
|
-
if (this.accessory.getService('Switch 3')) {
|
|
23
|
-
this.accessory.removeService(this.accessory.getService('Switch 3'))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Add the outlet services if they don't already exist
|
|
27
|
-
this.service1 = this.accessory.getService('Outlet 1')
|
|
28
|
-
|| this.accessory.addService(this.hapServ.Outlet, 'Outlet 1', 'outlet1')
|
|
29
|
-
this.service2 = this.accessory.getService('Outlet 2')
|
|
30
|
-
|| this.accessory.addService(this.hapServ.Outlet, 'Outlet 2', 'outlet2')
|
|
31
|
-
this.service3 = this.accessory.getService('Outlet 3')
|
|
32
|
-
|| this.accessory.addService(this.hapServ.Outlet, 'Outlet 3', 'outlet3')
|
|
33
|
-
|
|
34
|
-
// Add the set handler to the switch on/off characteristic
|
|
35
|
-
this.service1.getCharacteristic(this.hapChar.On).onSet(async (value) => {
|
|
36
|
-
await this.internalStateUpdate(this.service1, value ? 17 : 16)
|
|
37
|
-
})
|
|
38
|
-
this.service1.cacheState = this.service1.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
39
|
-
this.service2.getCharacteristic(this.hapChar.On).onSet(async (value) => {
|
|
40
|
-
await this.internalStateUpdate(this.service2, value ? 34 : 32)
|
|
41
|
-
})
|
|
42
|
-
this.service2.cacheState = this.service2.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
43
|
-
this.service3.getCharacteristic(this.hapChar.On).onSet(async (value) => {
|
|
44
|
-
await this.internalStateUpdate(this.service3, value ? 68 : 64)
|
|
45
|
-
})
|
|
46
|
-
this.service3.cacheState = this.service3.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
47
|
-
|
|
48
|
-
// Output the customised options to the log
|
|
49
|
-
const opts = JSON.stringify({
|
|
50
|
-
showAs: 'outlet',
|
|
51
|
-
})
|
|
52
|
-
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
53
|
-
|
|
54
|
-
// 119 turns BOTH ON
|
|
55
|
-
// 112 turns BOTH OFF
|
|
56
|
-
|
|
57
|
-
// 68 turns outlet 3 ON
|
|
58
|
-
// 64 turns outlet 3 OFF
|
|
59
|
-
|
|
60
|
-
// 34 turns outlet 2 ON
|
|
61
|
-
// 32 turns outlet 2 OFF
|
|
62
|
-
|
|
63
|
-
// 17 turns outlet 1 ON
|
|
64
|
-
// 16 turns outlet 1 OFF
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async internalStateUpdate(service, value) {
|
|
68
|
-
try {
|
|
69
|
-
const newValue = value % 16 === 0 ? 'off' : 'on'
|
|
70
|
-
|
|
71
|
-
// Don't continue if the new value is the same as before
|
|
72
|
-
if (service.cacheState === newValue) {
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Send the request to the platform sender function
|
|
77
|
-
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
78
|
-
cmd: 'stateDual',
|
|
79
|
-
value,
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
// Cache the new state and log if appropriate
|
|
83
|
-
service.cacheState = newValue
|
|
84
|
-
this.accessory.log(`[${service.displayName}] ${platformLang.curState} [${newValue}]`)
|
|
85
|
-
} catch (err) {
|
|
86
|
-
// Catch any errors during the process
|
|
87
|
-
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
88
|
-
|
|
89
|
-
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
90
|
-
setTimeout(() => {
|
|
91
|
-
service.updateCharacteristic(this.hapChar.On, service.cacheState === 'on')
|
|
92
|
-
}, 2000)
|
|
93
|
-
throw new this.hapErr(-70402)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
externalUpdate(params) {
|
|
98
|
-
// Check to see if the provided state is different from the cached state
|
|
99
|
-
if (!params.stateDual) {
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if ([112, 119].includes(params.stateDual)) {
|
|
104
|
-
// 112 -> both off
|
|
105
|
-
// 119 -> both on
|
|
106
|
-
const newState = params.stateDual === 119 ? 'on' : 'off'
|
|
107
|
-
if (newState !== this.service1.cacheState) {
|
|
108
|
-
this.service1.cacheState = newState
|
|
109
|
-
this.service1.updateCharacteristic(this.hapChar.On, this.service1.cacheState === 'on')
|
|
110
|
-
|
|
111
|
-
// Log the change
|
|
112
|
-
this.accessory.log(`[${this.service1.displayName}] ${platformLang.curState} [${this.service1.cacheState}]`)
|
|
113
|
-
}
|
|
114
|
-
if (newState !== this.service2.cacheState) {
|
|
115
|
-
this.service2.cacheState = newState
|
|
116
|
-
this.service2.updateCharacteristic(this.hapChar.On, this.service2.cacheState === 'on')
|
|
117
|
-
|
|
118
|
-
// Log the change
|
|
119
|
-
this.accessory.log(`[${this.service2.displayName}] ${platformLang.curState} [${this.service2.cacheState}]`)
|
|
120
|
-
}
|
|
121
|
-
if (newState !== this.service3.cacheState) {
|
|
122
|
-
this.service3.cacheState = newState
|
|
123
|
-
this.service3.updateCharacteristic(this.hapChar.On, this.service3.cacheState === 'on')
|
|
124
|
-
|
|
125
|
-
// Log the change
|
|
126
|
-
this.accessory.log(`[${this.service3.displayName}] ${platformLang.curState} [${this.service3.cacheState}]`)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if ([16, 17].includes(params.stateDual)) {
|
|
130
|
-
const newState = params.stateDual === 17 ? 'on' : 'off'
|
|
131
|
-
if (newState !== this.service1.cacheState) {
|
|
132
|
-
this.service1.cacheState = newState
|
|
133
|
-
this.service1.updateCharacteristic(this.hapChar.On, this.service1.cacheState === 'on')
|
|
134
|
-
|
|
135
|
-
// Log the change
|
|
136
|
-
this.accessory.log(`[${this.service1.displayName}] ${platformLang.curState} [${this.service1.cacheState}]`)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if ([32, 34].includes(params.stateDual)) {
|
|
140
|
-
const newState = params.stateDual === 34 ? 'on' : 'off'
|
|
141
|
-
if (newState !== this.service2.cacheState) {
|
|
142
|
-
this.service2.cacheState = newState
|
|
143
|
-
this.service2.updateCharacteristic(this.hapChar.On, this.service2.cacheState === 'on')
|
|
144
|
-
|
|
145
|
-
// Log the change
|
|
146
|
-
this.accessory.log(`[${this.service2.displayName}] ${platformLang.curState} [${this.service2.cacheState}]`)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if ([64, 68].includes(params.stateDual)) {
|
|
150
|
-
const newState = params.stateDual === 68 ? 'on' : 'off'
|
|
151
|
-
if (newState !== this.service3.cacheState) {
|
|
152
|
-
this.service3.cacheState = newState
|
|
153
|
-
this.service3.updateCharacteristic(this.hapChar.On, this.service3.cacheState === 'on')
|
|
154
|
-
|
|
155
|
-
// Log the change
|
|
156
|
-
this.accessory.log(`[${this.service3.displayName}] ${platformLang.curState} [${this.service3.cacheState}]`)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|