@homebridge-plugins/homebridge-govee 10.17.0 → 10.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/lib/device/index.js +2 -0
- package/lib/device/sensor-thermo-H5109.js +156 -0
- package/lib/homebridge-ui/public/index.html +1 -1
- package/lib/platform.js +6 -1
- package/lib/utils/constants.js +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,16 @@ This project tries to adhere to [Semantic Versioning](http://semver.org/). In pr
|
|
|
8
8
|
- `MINOR` version when a new device type is added, or when a new feature is added that is backwards-compatible
|
|
9
9
|
- `PATCH` version when backwards-compatible bug fixes are implemented
|
|
10
10
|
|
|
11
|
+
## v10.18.0 (2025-04-13)
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- add support for `H5109` as a thermostat (#1082) (@nrc2358)
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- updated dependencies
|
|
20
|
+
|
|
11
21
|
## v10.17.0 (2025-03-22)
|
|
12
22
|
|
|
13
23
|
### Added
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Homebridge plugin to integrate Govee devices into HomeKit
|
|
|
28
28
|
### Prerequisites
|
|
29
29
|
|
|
30
30
|
- To use this plugin, you will need to already have:
|
|
31
|
-
- [Node](https://nodejs.org): latest version of `v18` or `
|
|
31
|
+
- [Node](https://nodejs.org): latest version of `v18`, `v20` or `v22` - any other major version is not supported.
|
|
32
32
|
- [Homebridge](https://homebridge.io): `v1.6` - refer to link for more information and installation instructions.
|
|
33
33
|
- For bluetooth connectivity, it may be necessary to install extra packages on your system, see [Bluetooth Control](https://github.com/homebridge-plugins/homebridge-govee/wiki/Bluetooth-Control). Bluetooth works best when using a Raspberry Pi, not been tested on Windows, and Mac devices are unsupported.
|
|
34
34
|
|
package/lib/device/index.js
CHANGED
|
@@ -46,6 +46,7 @@ import deviceSensorLeak from './sensor-leak.js'
|
|
|
46
46
|
import deviceSensorMonitor from './sensor-monitor.js'
|
|
47
47
|
import deviceSensorPresence from './sensor-presence.js'
|
|
48
48
|
import deviceSensorThermo4 from './sensor-thermo4.js'
|
|
49
|
+
import deviceSensorThermoH5109 from './sensor-thermo-H5109.js'
|
|
49
50
|
import deviceSensorThermo from './sensor-thermo.js'
|
|
50
51
|
import deviceSwitchDouble from './switch-double.js'
|
|
51
52
|
import deviceSwitchSingle from './switch-single.js'
|
|
@@ -104,6 +105,7 @@ export default {
|
|
|
104
105
|
deviceSensorMonitor,
|
|
105
106
|
deviceSensorPresence,
|
|
106
107
|
deviceSensorThermo,
|
|
108
|
+
deviceSensorThermoH5109,
|
|
107
109
|
deviceSensorThermo4,
|
|
108
110
|
deviceSwitchDouble,
|
|
109
111
|
deviceSwitchSingle,
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import platformConsts from '../utils/constants.js'
|
|
2
|
+
import {
|
|
3
|
+
cenToFar,
|
|
4
|
+
generateRandomString,
|
|
5
|
+
hasProperty,
|
|
6
|
+
parseError,
|
|
7
|
+
} from '../utils/functions.js'
|
|
8
|
+
import platformLang from '../utils/lang-en.js'
|
|
9
|
+
|
|
10
|
+
export default class {
|
|
11
|
+
constructor(platform, accessory) {
|
|
12
|
+
// Set up variables from the platform
|
|
13
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
14
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
15
|
+
this.hapServ = platform.api.hap.Service
|
|
16
|
+
this.platform = platform
|
|
17
|
+
this.httpTimeout = platform.config.bleRefreshTime * 4.5 * 1000
|
|
18
|
+
|
|
19
|
+
// Set up variables from the accessory
|
|
20
|
+
this.accessory = accessory
|
|
21
|
+
|
|
22
|
+
// Set up custom variables for this device type
|
|
23
|
+
const deviceConf = platform.deviceConf[accessory.context.gvDeviceId]
|
|
24
|
+
this.lowBattThreshold = deviceConf && deviceConf.lowBattThreshold
|
|
25
|
+
? Math.min(deviceConf.lowBattThreshold, 100)
|
|
26
|
+
: platformConsts.defaultValues.lowBattThreshold
|
|
27
|
+
|
|
28
|
+
// Remove temperature sensor service if it exists
|
|
29
|
+
if (this.accessory.getService(this.hapServ.TemperatureSensor)) {
|
|
30
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.TemperatureSensor))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Add the thermostat service if it doesn't already exist
|
|
34
|
+
this.thermoService = this.accessory.getService(this.hapServ.Thermostat)
|
|
35
|
+
|| this.accessory.addService(this.hapServ.Thermostat)
|
|
36
|
+
|
|
37
|
+
// Set up thermostat characteristics
|
|
38
|
+
this.cacheTemp = this.thermoService.getCharacteristic(this.hapChar.CurrentTemperature).value || 20
|
|
39
|
+
|
|
40
|
+
// Configure thermostat to be read-only (sensor only)
|
|
41
|
+
this.thermoService.getCharacteristic(this.hapChar.TargetTemperature)
|
|
42
|
+
.updateValue(this.cacheTemp)
|
|
43
|
+
.onGet(() => {
|
|
44
|
+
return this.cacheTemp
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Set heating/cooling state to OFF (0)
|
|
48
|
+
this.thermoService.getCharacteristic(this.hapChar.CurrentHeatingCoolingState)
|
|
49
|
+
.updateValue(0) // 0 = OFF
|
|
50
|
+
.setProps({
|
|
51
|
+
validValues: [0], // Only allow OFF state
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
this.thermoService.getCharacteristic(this.hapChar.TargetHeatingCoolingState)
|
|
55
|
+
.updateValue(0) // 0 = OFF
|
|
56
|
+
.setProps({
|
|
57
|
+
validValues: [0], // Only allow OFF state
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Set temperature display units to Celsius (0)
|
|
61
|
+
this.thermoService.getCharacteristic(this.hapChar.TemperatureDisplayUnits)
|
|
62
|
+
.updateValue(0)
|
|
63
|
+
|
|
64
|
+
this.updateCache()
|
|
65
|
+
|
|
66
|
+
// Add the battery service if it doesn't already exist
|
|
67
|
+
this.battService = this.accessory.getService(this.hapServ.Battery)
|
|
68
|
+
|| this.accessory.addService(this.hapServ.Battery)
|
|
69
|
+
this.cacheBatt = this.battService.getCharacteristic(this.hapChar.BatteryLevel).value
|
|
70
|
+
|
|
71
|
+
// Pass the accessory to Fakegato to set up with Eve
|
|
72
|
+
this.accessory.eveService = new platform.eveService('custom', this.accessory, {
|
|
73
|
+
log: () => {},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Output the customised options to the log
|
|
77
|
+
const opts = JSON.stringify({
|
|
78
|
+
lowBattThreshold: this.lowBattThreshold,
|
|
79
|
+
})
|
|
80
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async externalUpdate(params) {
|
|
84
|
+
// Check to see if the provided online status is different from the cache value
|
|
85
|
+
if (hasProperty(params, 'online') && this.cacheOnline !== params.online) {
|
|
86
|
+
this.cacheOnline = params.online
|
|
87
|
+
this.platform.updateAccessoryStatus(this.accessory, this.cacheOnline)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (params.source === 'BLE') {
|
|
91
|
+
// If we have a BLE update then we should ignore HTTP updates for the next 4 BLE refresh cycles
|
|
92
|
+
// Since BLE will be more accurate and may not have updated with the cloud yet
|
|
93
|
+
// Generate a random key
|
|
94
|
+
const bleKey = generateRandomString(5)
|
|
95
|
+
this.bleKey = bleKey
|
|
96
|
+
setTimeout(() => {
|
|
97
|
+
if (this.bleKey === bleKey) {
|
|
98
|
+
this.bleKey = false
|
|
99
|
+
}
|
|
100
|
+
}, this.httpTimeout)
|
|
101
|
+
}
|
|
102
|
+
if (params.source === 'HTTP' && this.bleKey) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Check to see if the provided battery is different from the cached state
|
|
107
|
+
if (params.battery !== this.cacheBatt && this.battService) {
|
|
108
|
+
// Battery is different so update Homebridge with new values
|
|
109
|
+
this.cacheBatt = params.battery
|
|
110
|
+
this.battService.updateCharacteristic(this.hapChar.BatteryLevel, this.cacheBatt)
|
|
111
|
+
this.battService.updateCharacteristic(
|
|
112
|
+
this.hapChar.StatusLowBattery,
|
|
113
|
+
this.cacheBatt < this.lowBattThreshold ? 1 : 0,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
// Log the change
|
|
117
|
+
this.accessory.log(`${platformLang.curBatt} [${this.cacheBatt}%]`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check to see if the provided temperature is different from the cached state
|
|
121
|
+
if (hasProperty(params, 'temperature')) {
|
|
122
|
+
let newTemp = Number.parseInt(params.temperature + this.accessory.context.offTemp, 10)
|
|
123
|
+
newTemp /= 100
|
|
124
|
+
if (newTemp !== this.cacheTemp) {
|
|
125
|
+
// Temperature is different so update Homebridge with new values
|
|
126
|
+
this.cacheTemp = newTemp
|
|
127
|
+
this.thermoService.updateCharacteristic(this.hapChar.CurrentTemperature, this.cacheTemp)
|
|
128
|
+
this.thermoService.updateCharacteristic(this.hapChar.TargetTemperature, this.cacheTemp)
|
|
129
|
+
this.accessory.eveService.addEntry({ temp: this.cacheTemp })
|
|
130
|
+
|
|
131
|
+
// Log the change
|
|
132
|
+
this.accessory.log(`${platformLang.curTemp} [${this.cacheTemp}°C / ${cenToFar(this.cacheTemp)}°F]`)
|
|
133
|
+
|
|
134
|
+
// Update the cache file with the new temperature
|
|
135
|
+
this.updateCache()
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async updateCache() {
|
|
141
|
+
// Don't continue if the storage client hasn't initialised properly
|
|
142
|
+
if (!this.platform.storageClientData) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Attempt to save the new temperature to the cache
|
|
147
|
+
try {
|
|
148
|
+
await this.platform.storageData.setItem(
|
|
149
|
+
`${this.accessory.context.gvDeviceId}_temp`,
|
|
150
|
+
this.cacheTemp,
|
|
151
|
+
)
|
|
152
|
+
} catch (err) {
|
|
153
|
+
this.accessory.logWarn(`${platformLang.storageWriteErr} ${parseError(err)}`)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -251,10 +251,10 @@
|
|
|
251
251
|
try {
|
|
252
252
|
const currentConfig = await homebridge.getPluginConfig()
|
|
253
253
|
showIntro = () => {
|
|
254
|
+
homebridge.disableSaveButton?.()
|
|
254
255
|
const introContinue = document.getElementById('introContinue')
|
|
255
256
|
introContinue.addEventListener('click', () => {
|
|
256
257
|
homebridge.showSpinner()
|
|
257
|
-
homebridge.disableSaveButton?.()
|
|
258
258
|
document.getElementById('pageIntro').style.display = 'none'
|
|
259
259
|
document.getElementById('menuWrapper').style.display = 'inline-flex'
|
|
260
260
|
showSettings()
|
package/lib/platform.js
CHANGED
|
@@ -940,7 +940,12 @@ export default class {
|
|
|
940
940
|
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
941
941
|
} else if (platformConsts.models.sensorThermo.includes(device.model)) {
|
|
942
942
|
// Device is a thermo-hygrometer sensor
|
|
943
|
-
|
|
943
|
+
if (device.model === 'H5109') {
|
|
944
|
+
// Special handling for Gateway H5042+ with Floating Pool Sensor H5109
|
|
945
|
+
devInstance = deviceTypes.deviceSensorThermoH5109
|
|
946
|
+
} else {
|
|
947
|
+
devInstance = deviceTypes.deviceSensorThermo
|
|
948
|
+
}
|
|
944
949
|
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
945
950
|
} else if (platformConsts.models.sensorThermo4.includes(device.model)) {
|
|
946
951
|
// Device is a thermo-hygrometer sensor with 4 prongs and AWS support
|
package/lib/utils/constants.js
CHANGED
|
@@ -446,6 +446,7 @@ export default {
|
|
|
446
446
|
'H5104',
|
|
447
447
|
'H5105',
|
|
448
448
|
'H5108',
|
|
449
|
+
'H5109',
|
|
449
450
|
'H5174',
|
|
450
451
|
'H5177',
|
|
451
452
|
'H5179',
|
|
@@ -475,7 +476,6 @@ export default {
|
|
|
475
476
|
'H5121', // https://github.com/homebridge-plugins/homebridge-govee/issues/913
|
|
476
477
|
'H5126', // https://github.com/homebridge-plugins/homebridge-govee/issues/910
|
|
477
478
|
'H5107', // https://github.com/homebridge-plugins/homebridge-govee/issues/803
|
|
478
|
-
'H5109', // https://github.com/homebridge-plugins/homebridge-govee/issues/823
|
|
479
479
|
'H5125', // https://github.com/homebridge-plugins/homebridge-govee/issues/981
|
|
480
480
|
'H5185', // https://github.com/homebridge-plugins/homebridge-govee/issues/804
|
|
481
481
|
],
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@homebridge-plugins/homebridge-govee",
|
|
3
3
|
"alias": "Govee",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "10.
|
|
5
|
+
"version": "10.18.0",
|
|
6
6
|
"description": "Homebridge plugin to integrate Govee devices into HomeKit.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "bwp91",
|
|
@@ -48,14 +48,14 @@
|
|
|
48
48
|
"main": "lib/index.js",
|
|
49
49
|
"engines": {
|
|
50
50
|
"homebridge": "^1.6.0 || ^2.0.0-beta.0",
|
|
51
|
-
"node": "^18.20.
|
|
51
|
+
"node": "^18.20.8 || ^20.19.0 || ^22.14.0"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"lint": "eslint . --fix",
|
|
55
55
|
"rebuild": "rm -rf package-lock.json && rm -rf node_modules && npm install"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@homebridge/plugin-ui-utils": "^2.0.
|
|
58
|
+
"@homebridge/plugin-ui-utils": "^2.0.2",
|
|
59
59
|
"aws-iot-device-sdk": "^2.2.15",
|
|
60
60
|
"axios": "^1.8.4",
|
|
61
61
|
"node-persist": "^4.0.4",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"govee-bt-client": "^1.0.15"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@antfu/eslint-config": "^4.
|
|
72
|
+
"@antfu/eslint-config": "^4.12.0"
|
|
73
73
|
},
|
|
74
74
|
"overrides": {
|
|
75
75
|
"govee-bt-client": {
|