@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,157 @@
|
|
|
1
|
+
import {
|
|
2
|
+
base64ToHex,
|
|
3
|
+
getTwoItemPosition,
|
|
4
|
+
hexToTwoItems,
|
|
5
|
+
parseError,
|
|
6
|
+
} from '../utils/functions.js'
|
|
7
|
+
import platformLang from '../utils/lang-en.js'
|
|
8
|
+
|
|
9
|
+
export default class {
|
|
10
|
+
constructor(platform, accessory) {
|
|
11
|
+
// Set up variables from the platform
|
|
12
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
13
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
14
|
+
this.hapServ = platform.api.hap.Service
|
|
15
|
+
this.platform = platform
|
|
16
|
+
|
|
17
|
+
// Set up variables from the accessory
|
|
18
|
+
this.accessory = accessory
|
|
19
|
+
|
|
20
|
+
// Rotation speed to value in {1, 2, ..., 8}
|
|
21
|
+
this.speed2Value = speed => Math.min(Math.max(Number.parseInt(Math.round(speed / 10), 10), 1), 8)
|
|
22
|
+
|
|
23
|
+
// Speed codes
|
|
24
|
+
this.value2Code = {
|
|
25
|
+
1: 'MwUBAQAAAAAAAAAAAAAAAAAAADY=',
|
|
26
|
+
2: 'MwUBAgAAAAAAAAAAAAAAAAAAADU=',
|
|
27
|
+
3: 'MwUBAwAAAAAAAAAAAAAAAAAAADQ=',
|
|
28
|
+
4: 'MwUBBAAAAAAAAAAAAAAAAAAAADM=',
|
|
29
|
+
5: 'MwUBBQAAAAAAAAAAAAAAAAAAADI=',
|
|
30
|
+
6: 'MwUBBgAAAAAAAAAAAAAAAAAAADE=',
|
|
31
|
+
7: 'MwUBBwAAAAAAAAAAAAAAAAAAADA=',
|
|
32
|
+
8: 'MwUBCAAAAAAAAAAAAAAAAAAAAD8=',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Add the fan service if it doesn't already exist
|
|
36
|
+
this.service = this.accessory.getService(this.hapServ.Fan) || this.accessory.addService(this.hapServ.Fan)
|
|
37
|
+
|
|
38
|
+
// Add the set handler to the fan on/off characteristic
|
|
39
|
+
this.service
|
|
40
|
+
.getCharacteristic(this.hapChar.On)
|
|
41
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
42
|
+
this.cacheState = this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
43
|
+
|
|
44
|
+
// Add the set handler to the fan rotation speed characteristic
|
|
45
|
+
this.service
|
|
46
|
+
.getCharacteristic(this.hapChar.RotationSpeed)
|
|
47
|
+
.setProps({
|
|
48
|
+
minStep: 10,
|
|
49
|
+
validValues: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
|
|
50
|
+
})
|
|
51
|
+
.onSet(async value => this.internalSpeedUpdate(value))
|
|
52
|
+
this.cacheSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
|
|
53
|
+
|
|
54
|
+
// Output the customised options to the log
|
|
55
|
+
const opts = JSON.stringify({})
|
|
56
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async internalStateUpdate(value) {
|
|
60
|
+
try {
|
|
61
|
+
const newValue = value ? 'on' : 'off'
|
|
62
|
+
|
|
63
|
+
// Don't continue if the new value is the same as before
|
|
64
|
+
if (this.cacheState === newValue) {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Send the request to the platform sender function
|
|
69
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
70
|
+
cmd: 'stateHumi',
|
|
71
|
+
value: value ? 1 : 0,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// Cache the new state and log if appropriate
|
|
75
|
+
this.cacheState = newValue
|
|
76
|
+
this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
|
|
77
|
+
} catch (err) {
|
|
78
|
+
// Catch any errors during the process
|
|
79
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
80
|
+
|
|
81
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
84
|
+
}, 2000)
|
|
85
|
+
throw new this.hapErr(-70402)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async internalSpeedUpdate(value) {
|
|
90
|
+
try {
|
|
91
|
+
// Don't continue if the speed is 0
|
|
92
|
+
if (value === 0) {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Get the single Govee value {1, 2, ..., 8}
|
|
97
|
+
const newValue = this.speed2Value(value)
|
|
98
|
+
|
|
99
|
+
// Don't continue if the speed value won't have effect
|
|
100
|
+
if (newValue * 10 === this.cacheSpeed) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Get the scene code for this value
|
|
105
|
+
const newCode = this.value2Code[newValue]
|
|
106
|
+
|
|
107
|
+
// Send the request to the platform sender function
|
|
108
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
109
|
+
cmd: 'ptReal',
|
|
110
|
+
value: newCode,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// Cache the new state and log if appropriate
|
|
114
|
+
this.cacheSpeed = newValue * 10
|
|
115
|
+
this.accessory.log(`${platformLang.curSpeed} [${newValue}]`)
|
|
116
|
+
} catch (err) {
|
|
117
|
+
// Catch any errors during the process
|
|
118
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
119
|
+
|
|
120
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
121
|
+
setTimeout(() => {
|
|
122
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
|
|
123
|
+
}, 2000)
|
|
124
|
+
throw new this.hapErr(-70402)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
externalUpdate(params) {
|
|
129
|
+
// Check for an ON/OFF change
|
|
130
|
+
if (params.state && params.state !== this.cacheState) {
|
|
131
|
+
this.cacheState = params.state
|
|
132
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
133
|
+
|
|
134
|
+
// Log the change
|
|
135
|
+
this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check for some other scene/mode change
|
|
139
|
+
(params.commands || []).forEach((command) => {
|
|
140
|
+
const hexString = base64ToHex(command)
|
|
141
|
+
const hexParts = hexToTwoItems(hexString)
|
|
142
|
+
|
|
143
|
+
// Return now if not a device query update code
|
|
144
|
+
if (getTwoItemPosition(hexParts, 1) !== 'aa') {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const deviceFunction = `${getTwoItemPosition(hexParts, 1)}${getTwoItemPosition(hexParts, 2)}`
|
|
149
|
+
|
|
150
|
+
switch (deviceFunction) {
|
|
151
|
+
default:
|
|
152
|
+
this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
|
|
153
|
+
break
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { hs2rgb, rgb2hs } from '../utils/colour.js'
|
|
2
|
+
import {
|
|
3
|
+
base64ToHex,
|
|
4
|
+
generateCodeFromHexValues,
|
|
5
|
+
generateRandomString,
|
|
6
|
+
getTwoItemPosition,
|
|
7
|
+
hexToDecimal,
|
|
8
|
+
hexToTwoItems,
|
|
9
|
+
parseError,
|
|
10
|
+
sleep,
|
|
11
|
+
} from '../utils/functions.js'
|
|
12
|
+
import platformLang from '../utils/lang-en.js'
|
|
13
|
+
|
|
14
|
+
export default class {
|
|
15
|
+
constructor(platform, accessory) {
|
|
16
|
+
// Set up variables from the platform
|
|
17
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
18
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
19
|
+
this.hapServ = platform.api.hap.Service
|
|
20
|
+
this.platform = platform
|
|
21
|
+
|
|
22
|
+
// Set up variables from the accessory
|
|
23
|
+
this.accessory = accessory
|
|
24
|
+
|
|
25
|
+
// Rotation speed to value in {1, 2, ..., 8}
|
|
26
|
+
this.speed2Value = speed => Math.min(Math.max(Number.parseInt(Math.round(speed / 10), 10), 1), 9)
|
|
27
|
+
|
|
28
|
+
// Speed codes
|
|
29
|
+
this.value2Code = {
|
|
30
|
+
1: 'MwUBAQAAAAAAAAAAAAAAAAAAADY=',
|
|
31
|
+
2: 'MwUBAgAAAAAAAAAAAAAAAAAAADU=',
|
|
32
|
+
3: 'MwUBAwAAAAAAAAAAAAAAAAAAADQ=',
|
|
33
|
+
4: 'MwUBBAAAAAAAAAAAAAAAAAAAADM=',
|
|
34
|
+
5: 'MwUBBQAAAAAAAAAAAAAAAAAAADI=',
|
|
35
|
+
6: 'MwUBBgAAAAAAAAAAAAAAAAAAADE=',
|
|
36
|
+
7: 'MwUBBwAAAAAAAAAAAAAAAAAAADA=',
|
|
37
|
+
8: 'MwUBCAAAAAAAAAAAAAAAAAAAAD8=',
|
|
38
|
+
9: 'MwUBCQAAAAAAAAAAAAAAAAAAAD4=',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Add the fan service if it doesn't already exist
|
|
42
|
+
this.service = this.accessory.getService(this.hapServ.Fan) || this.accessory.addService(this.hapServ.Fan)
|
|
43
|
+
|
|
44
|
+
// Remove humidity sensor service if it exists
|
|
45
|
+
if (this.accessory.getService(this.hapServ.HumiditySensor)) {
|
|
46
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.HumiditySensor))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Add the night light service if it doesn't already exist
|
|
50
|
+
this.lightService = this.accessory.getService(this.hapServ.Lightbulb)
|
|
51
|
+
|| this.accessory.addService(this.hapServ.Lightbulb)
|
|
52
|
+
|
|
53
|
+
// Add the set handler to the fan on/off characteristic
|
|
54
|
+
this.service
|
|
55
|
+
.getCharacteristic(this.hapChar.On)
|
|
56
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
57
|
+
this.cacheState = this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
58
|
+
|
|
59
|
+
// Add the set handler to the fan rotation speed characteristic
|
|
60
|
+
this.service
|
|
61
|
+
.getCharacteristic(this.hapChar.RotationSpeed)
|
|
62
|
+
.setProps({
|
|
63
|
+
minStep: 10,
|
|
64
|
+
validValues: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
|
|
65
|
+
})
|
|
66
|
+
.onSet(async value => this.internalSpeedUpdate(value))
|
|
67
|
+
this.cacheSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
|
|
68
|
+
this.cacheSpeedRaw = `0${this.cacheSpeed / 10}` // example '02' for 20%
|
|
69
|
+
|
|
70
|
+
// Add the set handler to the lightbulb on/off characteristic
|
|
71
|
+
this.lightService.getCharacteristic(this.hapChar.On).onSet(async (value) => {
|
|
72
|
+
await this.internalLightStateUpdate(value)
|
|
73
|
+
})
|
|
74
|
+
this.cacheLightState = this.lightService.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
75
|
+
|
|
76
|
+
// Add the set handler to the lightbulb brightness characteristic
|
|
77
|
+
this.lightService
|
|
78
|
+
.getCharacteristic(this.hapChar.Brightness)
|
|
79
|
+
.onSet(async (value) => {
|
|
80
|
+
await this.internalBrightnessUpdate(value)
|
|
81
|
+
})
|
|
82
|
+
this.cacheBright = this.lightService.getCharacteristic(this.hapChar.Brightness).value
|
|
83
|
+
|
|
84
|
+
// Add the set handler to the lightbulb hue characteristic
|
|
85
|
+
this.lightService.getCharacteristic(this.hapChar.Hue).onSet(async (value) => {
|
|
86
|
+
await this.internalColourUpdate(value)
|
|
87
|
+
})
|
|
88
|
+
this.cacheHue = this.lightService.getCharacteristic(this.hapChar.Hue).value
|
|
89
|
+
this.cacheSat = this.lightService.getCharacteristic(this.hapChar.Saturation).value
|
|
90
|
+
|
|
91
|
+
// Output the customised options to the log
|
|
92
|
+
const opts = JSON.stringify({})
|
|
93
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async internalStateUpdate(value) {
|
|
97
|
+
try {
|
|
98
|
+
const newValue = value ? 'on' : 'off'
|
|
99
|
+
|
|
100
|
+
// Don't continue if the new value is the same as before
|
|
101
|
+
if (this.cacheState === newValue) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Send the request to the platform sender function
|
|
106
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
107
|
+
cmd: 'stateHumi',
|
|
108
|
+
value: value ? 1 : 0,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Cache the new state and log if appropriate
|
|
112
|
+
if (this.cacheState !== newValue) {
|
|
113
|
+
this.cacheState = newValue
|
|
114
|
+
this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Also turn the light off if turning off
|
|
118
|
+
if (!value && this.cacheLightState === 'on') {
|
|
119
|
+
this.lightService.updateCharacteristic(this.hapChar.On, false)
|
|
120
|
+
this.accessory.log(`current light state [${this.cacheLightState}]`)
|
|
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
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
129
|
+
}, 2000)
|
|
130
|
+
throw new this.hapErr(-70402)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async internalSpeedUpdate(value) {
|
|
135
|
+
try {
|
|
136
|
+
// Don't continue if the speed is 0
|
|
137
|
+
if (value === 0) {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Get the single Govee value {1, 2, ..., 8}
|
|
142
|
+
const newValue = this.speed2Value(value)
|
|
143
|
+
|
|
144
|
+
// Don't continue if the speed value won't have effect
|
|
145
|
+
if (newValue * 10 === this.cacheSpeed) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Get the scene code for this value
|
|
150
|
+
const newCode = this.value2Code[newValue]
|
|
151
|
+
|
|
152
|
+
this.accessory.log(newCode)
|
|
153
|
+
|
|
154
|
+
// Send the request to the platform sender function
|
|
155
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
156
|
+
cmd: 'ptReal',
|
|
157
|
+
value: newCode,
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// Cache the new state and log if appropriate
|
|
161
|
+
this.cacheSpeed = newValue * 10
|
|
162
|
+
this.accessory.log(`${platformLang.curSpeed} [${newValue}]`)
|
|
163
|
+
} catch (err) {
|
|
164
|
+
// Catch any errors during the process
|
|
165
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
166
|
+
|
|
167
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
168
|
+
setTimeout(() => {
|
|
169
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
|
|
170
|
+
}, 2000)
|
|
171
|
+
throw new this.hapErr(-70402)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async internalLightStateUpdate(value) {
|
|
176
|
+
try {
|
|
177
|
+
const newValue = value ? 'on' : 'off'
|
|
178
|
+
|
|
179
|
+
// Don't continue if the new value is the same as before
|
|
180
|
+
if (this.cacheLightState === newValue) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Generate the hex values for the code
|
|
185
|
+
let hexValues
|
|
186
|
+
if (value) {
|
|
187
|
+
// Calculate current RGB values
|
|
188
|
+
const newRGB = hs2rgb(
|
|
189
|
+
this.lightService.getCharacteristic(this.hapChar.Hue).value,
|
|
190
|
+
this.lightService.getCharacteristic(this.hapChar.Saturation).value,
|
|
191
|
+
)
|
|
192
|
+
hexValues = [0x33, 0x1B, 0x01, this.cacheBright, ...newRGB]
|
|
193
|
+
} else {
|
|
194
|
+
hexValues = [0x33, 0x1B, 0x00]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Send the request to the platform sender function
|
|
198
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
199
|
+
cmd: 'ptReal',
|
|
200
|
+
value: generateCodeFromHexValues(hexValues),
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// Cache the new state and log if appropriate
|
|
204
|
+
if (this.cacheLightState !== newValue) {
|
|
205
|
+
this.cacheLightState = newValue
|
|
206
|
+
this.accessory.log(`${platformLang.curLight} [${newValue}]`)
|
|
207
|
+
}
|
|
208
|
+
} catch (err) {
|
|
209
|
+
// Catch any errors during the process
|
|
210
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
211
|
+
|
|
212
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
213
|
+
setTimeout(() => {
|
|
214
|
+
this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLightState === 'on')
|
|
215
|
+
}, 2000)
|
|
216
|
+
throw new this.hapErr(-70402)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async internalBrightnessUpdate(value) {
|
|
221
|
+
try {
|
|
222
|
+
// This acts like a debounce function when endlessly sliding the brightness scale
|
|
223
|
+
const updateKeyBright = generateRandomString(5)
|
|
224
|
+
this.updateKeyBright = updateKeyBright
|
|
225
|
+
await sleep(350)
|
|
226
|
+
if (updateKeyBright !== this.updateKeyBright) {
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Don't continue if the new value is the same as before
|
|
231
|
+
if (value === this.cacheBright || value === 0) {
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Generate the hex values for the code
|
|
236
|
+
const newRGB = hs2rgb(
|
|
237
|
+
this.lightService.getCharacteristic(this.hapChar.Hue).value,
|
|
238
|
+
this.lightService.getCharacteristic(this.hapChar.Saturation).value,
|
|
239
|
+
)
|
|
240
|
+
const hexValues = [0x33, 0x1B, 0x01, value, ...newRGB]
|
|
241
|
+
|
|
242
|
+
// Send the request to the platform sender function
|
|
243
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
244
|
+
cmd: 'ptReal',
|
|
245
|
+
value: generateCodeFromHexValues(hexValues),
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
// Govee considers 0% brightness to be off
|
|
249
|
+
if (value === 0) {
|
|
250
|
+
setTimeout(() => {
|
|
251
|
+
this.cacheLightState = 'off'
|
|
252
|
+
if (this.lightService.getCharacteristic(this.hapChar.On).value) {
|
|
253
|
+
this.lightService.updateCharacteristic(this.hapChar.On, false)
|
|
254
|
+
this.accessory.log(`${platformLang.curLight} [${this.cacheLightState}]`)
|
|
255
|
+
}
|
|
256
|
+
this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
|
|
257
|
+
}, 1500)
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Cache the new state and log if appropriate
|
|
262
|
+
if (this.cacheBright !== value) {
|
|
263
|
+
this.cacheBright = value
|
|
264
|
+
this.accessory.log(`${platformLang.curBright} [${value}%]`)
|
|
265
|
+
}
|
|
266
|
+
} catch (err) {
|
|
267
|
+
// Catch any errors during the process
|
|
268
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
269
|
+
|
|
270
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
271
|
+
setTimeout(() => {
|
|
272
|
+
this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
|
|
273
|
+
}, 2000)
|
|
274
|
+
throw new this.hapErr(-70402)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async internalColourUpdate(value) {
|
|
279
|
+
try {
|
|
280
|
+
// This acts like a debounce function when endlessly sliding the colour wheel
|
|
281
|
+
const updateKeyColour = generateRandomString(5)
|
|
282
|
+
this.updateKeyColour = updateKeyColour
|
|
283
|
+
await sleep(300)
|
|
284
|
+
if (updateKeyColour !== this.updateKeyColour) {
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Don't continue if the new value is the same as before
|
|
289
|
+
if (value === this.cacheHue) {
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Generate the hex values for the code
|
|
294
|
+
const newRGB = hs2rgb(
|
|
295
|
+
value,
|
|
296
|
+
this.lightService.getCharacteristic(this.hapChar.Saturation).value,
|
|
297
|
+
)
|
|
298
|
+
const hexValues = [0x33, 0x1B, 0x01, this.cacheBright, ...newRGB]
|
|
299
|
+
|
|
300
|
+
// Send the request to the platform sender function
|
|
301
|
+
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
302
|
+
cmd: 'ptReal',
|
|
303
|
+
value: generateCodeFromHexValues(hexValues),
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
// Cache the new state and log if appropriate
|
|
307
|
+
if (this.cacheHue !== value) {
|
|
308
|
+
this.cacheHue = value
|
|
309
|
+
this.accessory.log(`${platformLang.curColour} [rgb ${newRGB.join(' ')}]`)
|
|
310
|
+
}
|
|
311
|
+
} catch (err) {
|
|
312
|
+
// Catch any errors during the process
|
|
313
|
+
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
314
|
+
|
|
315
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
316
|
+
setTimeout(() => {
|
|
317
|
+
this.lightService.updateCharacteristic(this.hapChar.Hue, this.cacheHue)
|
|
318
|
+
}, 2000)
|
|
319
|
+
throw new this.hapErr(-70402)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
externalUpdate(params) {
|
|
324
|
+
// Check for an ON/OFF change
|
|
325
|
+
if (params.state && params.state !== this.cacheState) {
|
|
326
|
+
this.cacheState = params.state
|
|
327
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
328
|
+
|
|
329
|
+
// Log the change
|
|
330
|
+
this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Check for some other scene/mode change
|
|
334
|
+
(params.commands || []).forEach((command) => {
|
|
335
|
+
const hexString = base64ToHex(command)
|
|
336
|
+
const hexParts = hexToTwoItems(hexString)
|
|
337
|
+
|
|
338
|
+
// Return now if not a device query update code
|
|
339
|
+
if (getTwoItemPosition(hexParts, 1) !== 'aa') {
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const deviceFunction = `${getTwoItemPosition(hexParts, 2)}${getTwoItemPosition(hexParts, 3)}`
|
|
344
|
+
|
|
345
|
+
switch (deviceFunction) {
|
|
346
|
+
case '0500': { // mode
|
|
347
|
+
// Mode
|
|
348
|
+
const newModeRaw = getTwoItemPosition(hexParts, 4)
|
|
349
|
+
let newMode
|
|
350
|
+
switch (newModeRaw) {
|
|
351
|
+
case '01': {
|
|
352
|
+
// Manual
|
|
353
|
+
newMode = 'manual'
|
|
354
|
+
break
|
|
355
|
+
}
|
|
356
|
+
case '02': {
|
|
357
|
+
// Custom
|
|
358
|
+
newMode = 'custom'
|
|
359
|
+
break
|
|
360
|
+
}
|
|
361
|
+
case '03': {
|
|
362
|
+
// Auto
|
|
363
|
+
newMode = 'auto'
|
|
364
|
+
break
|
|
365
|
+
}
|
|
366
|
+
default:
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
if (this.cacheMode !== newMode) {
|
|
370
|
+
this.cacheMode = newMode
|
|
371
|
+
this.accessory.log(`${platformLang.curMode} [${this.cacheMode}]`)
|
|
372
|
+
}
|
|
373
|
+
break
|
|
374
|
+
}
|
|
375
|
+
case '0501': {
|
|
376
|
+
// Manual speed
|
|
377
|
+
const newSpeedRaw = getTwoItemPosition(hexParts, 4)
|
|
378
|
+
if (newSpeedRaw !== this.cacheSpeedRaw) {
|
|
379
|
+
this.cacheSpeedRaw = newSpeedRaw
|
|
380
|
+
this.cacheSpeed = Number.parseInt(newSpeedRaw, 10) * 10
|
|
381
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
|
|
382
|
+
this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}]`)
|
|
383
|
+
}
|
|
384
|
+
break
|
|
385
|
+
}
|
|
386
|
+
case '1800': // display off
|
|
387
|
+
case '1801': { // display on
|
|
388
|
+
const newDisplay = deviceFunction === '1801' ? 'on' : 'off'
|
|
389
|
+
if (newDisplay !== this.cacheDisplay) {
|
|
390
|
+
this.cacheDisplay = newDisplay
|
|
391
|
+
this.accessory.log(`${platformLang.curDisplay} [${this.cacheDisplay ? 'on' : 'off'}]`)
|
|
392
|
+
}
|
|
393
|
+
break
|
|
394
|
+
}
|
|
395
|
+
case '1b00': // night light off
|
|
396
|
+
case '1b01': { // night light on
|
|
397
|
+
const newNight = deviceFunction === '1b01' ? 'on' : 'off'
|
|
398
|
+
if (newNight !== this.cacheLightState) {
|
|
399
|
+
this.cacheLightState = newNight
|
|
400
|
+
this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLightState === 'on')
|
|
401
|
+
this.accessory.log(`current night light state [${this.cacheLightState}]`)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Brightness and colour
|
|
405
|
+
if (this.cacheLightState === 'on') {
|
|
406
|
+
const newBrightHex = getTwoItemPosition(hexParts, 4)
|
|
407
|
+
const newBrightDec = Math.round(hexToDecimal(newBrightHex))
|
|
408
|
+
if (newBrightDec !== this.cacheBright) {
|
|
409
|
+
this.cacheBright = newBrightDec
|
|
410
|
+
this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
|
|
411
|
+
this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const newR = hexToDecimal(getTwoItemPosition(hexParts, 5))
|
|
415
|
+
const newG = hexToDecimal(getTwoItemPosition(hexParts, 6))
|
|
416
|
+
const newB = hexToDecimal(getTwoItemPosition(hexParts, 7))
|
|
417
|
+
const hs = rgb2hs(newR, newG, newB)
|
|
418
|
+
|
|
419
|
+
// Check for a colour change
|
|
420
|
+
if (hs[0] !== this.cacheHue) {
|
|
421
|
+
// Colour is different so update Homebridge with new values
|
|
422
|
+
this.lightService.updateCharacteristic(this.hapChar.Hue, hs[0])
|
|
423
|
+
this.lightService.updateCharacteristic(this.hapChar.Saturation, hs[1]);
|
|
424
|
+
[this.cacheHue] = hs
|
|
425
|
+
|
|
426
|
+
// Log the change
|
|
427
|
+
this.accessory.log(`${platformLang.curColour} [rgb ${newR} ${newG} ${newB}]`)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
break
|
|
431
|
+
}
|
|
432
|
+
case '0502': // custom mode
|
|
433
|
+
case '0503': // auto mode
|
|
434
|
+
case '1100': // timer
|
|
435
|
+
case '1101': // timer
|
|
436
|
+
case '1300': // scheduling
|
|
437
|
+
case '1500': { // scheduling
|
|
438
|
+
break
|
|
439
|
+
}
|
|
440
|
+
default:
|
|
441
|
+
this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
|
|
442
|
+
break
|
|
443
|
+
}
|
|
444
|
+
})
|
|
445
|
+
}
|
|
446
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { base64ToHex, getTwoItemPosition, hexToTwoItems } 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
|
+
// Output the customised options to the log
|
|
16
|
+
const opts = JSON.stringify({})
|
|
17
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
externalUpdate(params) {
|
|
21
|
+
// Update the active characteristic
|
|
22
|
+
if (params.state && params.state !== this.cacheState) {
|
|
23
|
+
this.cacheState = params.state
|
|
24
|
+
this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check for some other scene/mode change
|
|
28
|
+
(params.commands || []).forEach((command) => {
|
|
29
|
+
const hexString = base64ToHex(command)
|
|
30
|
+
const hexParts = hexToTwoItems(hexString)
|
|
31
|
+
|
|
32
|
+
// Return now if not a device query update code
|
|
33
|
+
if (getTwoItemPosition(hexParts, 1) !== 'aa') {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const deviceFunction = `${getTwoItemPosition(hexParts, 2)}${getTwoItemPosition(hexParts, 3)}`
|
|
38
|
+
|
|
39
|
+
switch (deviceFunction) {
|
|
40
|
+
default:
|
|
41
|
+
this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
}
|