@homebridge-plugins/homebridge-govee 11.30.0-beta.2 → 11.30.0-beta.3

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 CHANGED
@@ -9,6 +9,7 @@ All notable changes to `@homebridge-plugins/homebridge-govee` will be documented
9
9
  - chore(ble): let noble apply its own adapter timeout instead of racing a second one
10
10
  - fix: restore aws brightness scaling, so lights reach the brightness asked for (#1262) (@gradyg21)
11
11
  - fix: skip a periodic sync when the previous one is still running, so slow responses stop stacking up (#1256) (@schwaggy)
12
+ - fix: work out whether the openapi sensor temperature is celsius or fahrenheit, so thermometers no longer read too high (#1269) (@tdporter)
12
13
 
13
14
  ## v11.29.3 (2026-07-21)
14
15
 
@@ -1143,6 +1143,35 @@
1143
1143
  "functionBody": "return (model.thermoDevices && model.thermoDevices[arrayIndices] && model.thermoDevices[arrayIndices].deviceId && [23,26].includes(model.thermoDevices[arrayIndices].deviceId.length) && !model.thermoDevices[arrayIndices].ignoreDevice);"
1144
1144
  }
1145
1145
  },
1146
+ "openApiTempUnit": {
1147
+ "type": "string",
1148
+ "title": "OpenAPI Temperature Unit",
1149
+ "description": "Govee's OpenAPI reports the temperature in whatever unit your Govee app is set to, without saying which. Leave this on Auto and the plugin will work it out. Only set it manually if the reported temperature is wrong.",
1150
+ "default": "auto",
1151
+ "oneOf": [
1152
+ {
1153
+ "title": "Auto (Recommended)",
1154
+ "enum": [
1155
+ "auto"
1156
+ ]
1157
+ },
1158
+ {
1159
+ "title": "Celsius",
1160
+ "enum": [
1161
+ "c"
1162
+ ]
1163
+ },
1164
+ {
1165
+ "title": "Fahrenheit",
1166
+ "enum": [
1167
+ "f"
1168
+ ]
1169
+ }
1170
+ ],
1171
+ "condition": {
1172
+ "functionBody": "return (model.thermoDevices && model.thermoDevices[arrayIndices] && model.thermoDevices[arrayIndices].deviceId && [23,26].includes(model.thermoDevices[arrayIndices].deviceId.length) && !model.thermoDevices[arrayIndices].ignoreDevice);"
1173
+ }
1174
+ },
1146
1175
  "showExtraSwitch": {
1147
1176
  "type": "boolean",
1148
1177
  "title": "Show Extra Switch",
@@ -1625,6 +1654,7 @@
1625
1654
  "thermoDevices[].deviceId",
1626
1655
  "thermoDevices[].ignoreDevice",
1627
1656
  "thermoDevices[].lowBattThreshold",
1657
+ "thermoDevices[].openApiTempUnit",
1628
1658
  "thermoDevices[].showExtraSwitch"
1629
1659
  ]
1630
1660
  }
package/lib/platform.js CHANGED
@@ -236,6 +236,7 @@ export default class {
236
236
  this.deviceConf[id][k] = v === 'false' ? false : !!v
237
237
  break
238
238
  case 'awsColourMode':
239
+ case 'openApiTempUnit':
239
240
  case 'showAs': {
240
241
  if (typeof v !== 'string' || !platformConsts.allowed[k].includes(v)) {
241
242
  logIgnore(`${key}.${id}.${k}`)
@@ -1291,6 +1292,13 @@ export default class {
1291
1292
  accessory.context.temperatureSource = deviceConf.temperatureSource
1292
1293
  }
1293
1294
 
1295
+ // Add the openApiTempUnit config to the context if set to something other than auto
1296
+ if (deviceConf.openApiTempUnit && deviceConf.openApiTempUnit !== 'auto') {
1297
+ accessory.context.openApiTempUnit = deviceConf.openApiTempUnit
1298
+ } else {
1299
+ delete accessory.context.openApiTempUnit
1300
+ }
1301
+
1294
1302
  // Get a supported command list if provided, with their options
1295
1303
  if (device.supportCmds && Array.isArray(device.supportCmds)) {
1296
1304
  accessory.context.supportedCmds = device.supportCmds
@@ -1969,6 +1977,12 @@ export default class {
1969
1977
  delete data.kelvinOutOfRange
1970
1978
  }
1971
1979
 
1980
+ // Skip the reading if we can't tell whether govee sent celsius or fahrenheit
1981
+ if (data.temperatureUnitUnknown) {
1982
+ accessory.logDebugWarn(platformLang.openApiTempUnitUnknown)
1983
+ delete data.temperatureUnitUnknown
1984
+ }
1985
+
1972
1986
  // Check if we actually parsed anything useful (more than just the source property)
1973
1987
  if (Object.keys(data).length > 1) {
1974
1988
  try {
@@ -92,7 +92,7 @@ export default {
92
92
  'temperatureSource',
93
93
  ],
94
94
  leakDevices: ['label', 'deviceId', 'ignoreDevice', 'lowBattThreshold'],
95
- thermoDevices: ['label', 'deviceId', 'ignoreDevice', 'lowBattThreshold', 'showExtraSwitch'],
95
+ thermoDevices: ['label', 'deviceId', 'ignoreDevice', 'lowBattThreshold', 'openApiTempUnit', 'showExtraSwitch'],
96
96
  fanDevices: ['label', 'deviceId', 'ignoreDevice', 'hideLight'],
97
97
  heaterDevices: ['label', 'deviceId', 'ignoreDevice', 'tempReporting'],
98
98
  humidifierDevices: ['label', 'deviceId', 'ignoreDevice'],
@@ -112,6 +112,7 @@ export default {
112
112
  ],
113
113
  iceMakerDevices: ['label', 'deviceId', 'ignoreDevice'],
114
114
  awsColourMode: ['default', 'rgb', 'redgreenblue'],
115
+ openApiTempUnit: ['auto', 'c', 'f'],
115
116
  showAs: [
116
117
  'default',
117
118
  'audio',
@@ -17,6 +17,7 @@ export default {
17
17
  available: 'client enabled',
18
18
  availableWithDevices: n => `client enabled and found ${n} device(s)`,
19
19
  openApiNoKey: 'api key not provided',
20
+ openApiTempUnitUnknown: 'ignoring the openapi temperature as it is unclear whether govee sent celsius or fahrenheit - you can set this in the plugin settings for this device',
20
21
  brand: 'Govee',
21
22
  bleNonControl: 'will be visible but uncontrollable as BLE not available',
22
23
  bleNoPackage: 'required hardware/packages not available',
@@ -2,6 +2,50 @@ import platformConsts from './constants.js'
2
2
 
3
3
  const hasProperty = (obj, prop) => Object.hasOwn(obj, prop)
4
4
 
5
+ /**
6
+ * Works out which unit an OpenAPI `sensorTemperature` value is in.
7
+ *
8
+ * Govee's OpenAPI reports this value in whatever unit the Govee app is set to, and
9
+ * sends no unit alongside it. The HTTP API always reports hundredths of a degree
10
+ * Celsius, so where a device has both we can compare the two and see which fits.
11
+ *
12
+ * @param {number} value - The raw OpenAPI sensorTemperature value
13
+ * @param {object} context - Accessory context
14
+ * @returns {'c'|'f'|null} The unit, or null if it cannot be determined
15
+ */
16
+ function resolveOpenApiTempUnit(value, context) {
17
+ // An explicit setting always wins
18
+ if (context.openApiTempUnit === 'c' || context.openApiTempUnit === 'f') {
19
+ return context.openApiTempUnit
20
+ }
21
+
22
+ // Govee may declare the unit on the capability itself
23
+ const declared = context.openApiCapabilities?.sensorTemperature?.parameters?.unit
24
+ if (typeof declared === 'string') {
25
+ if (declared.toLowerCase().startsWith('f')) {
26
+ return 'f'
27
+ }
28
+ if (declared.toLowerCase().startsWith('c')) {
29
+ return 'c'
30
+ }
31
+ }
32
+
33
+ // Otherwise compare against the last HTTP reading, which is always Celsius
34
+ const reference = context.lastHttpTempCelsius
35
+ if (typeof reference === 'number') {
36
+ const asCelsius = Math.abs(value - reference)
37
+ const asFahrenheit = Math.abs(((value - 32) * 5) / 9 - reference)
38
+ if (asFahrenheit + 0.5 < asCelsius) {
39
+ return 'f'
40
+ }
41
+ if (asCelsius + 0.5 < asFahrenheit) {
42
+ return 'c'
43
+ }
44
+ }
45
+
46
+ return null
47
+ }
48
+
5
49
  /**
6
50
  * Parses a raw device update payload into a standardised data object.
7
51
  * Handles different payload structures from AWS, LAN, OpenAPI, and legacy devices.
@@ -97,6 +141,11 @@ export function parseDeviceUpdate(params, context) {
97
141
  // CURRENT TEMPERATURE
98
142
  if (hasProperty(params, 'temperature')) {
99
143
  data.temperature = params.temperature
144
+
145
+ // The HTTP value is always Celsius, so keep it as a reference for the OpenAPI value
146
+ if (params.source === 'HTTP' && Number.isFinite(params.temperature)) {
147
+ context.lastHttpTempCelsius = params.temperature / 100
148
+ }
100
149
  } else if (params?.state?.sta && hasProperty(params.state.sta, 'curTem')) {
101
150
  data.temperature = params.state.sta.curTem
102
151
  }
@@ -136,7 +185,16 @@ export function parseDeviceUpdate(params, context) {
136
185
 
137
186
  // SENSOR TEMPERATURE (OpenAPI property)
138
187
  if (params.state && hasProperty(params.state, 'sensorTemperature')) {
139
- data.temperature = params.state.sensorTemperature * 100
188
+ const sensorTemp = Number(params.state.sensorTemperature)
189
+ const unit = resolveOpenApiTempUnit(sensorTemp, context)
190
+ if (unit === 'f') {
191
+ data.temperature = Math.round((((sensorTemp - 32) * 5) / 9) * 100)
192
+ } else if (unit === 'c') {
193
+ data.temperature = Math.round(sensorTemp * 100)
194
+ } else {
195
+ // Unit unknown, so report nothing rather than a wrong reading
196
+ data.temperatureUnitUnknown = true
197
+ }
140
198
  }
141
199
 
142
200
  // SENSOR HUMIDITY (OpenAPI property)
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": "11.30.0-beta.2",
5
+ "version": "11.30.0-beta.3",
6
6
  "description": "Homebridge plugin to integrate Govee devices into HomeKit.",
7
7
  "author": {
8
8
  "name": "bwp91",