homebridge-withings-environment-data 1.0.0 → 1.1.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 +14 -0
- package/README.md +6 -2
- package/config.schema.json +28 -0
- package/index.js +30 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## [1.1.0] - 2026-07-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Air Quality threshold config fields (`airQualityExcellentMaxPpm`,
|
|
13
|
+
`airQualityGoodMaxPpm`, `airQualityFairMaxPpm`,
|
|
14
|
+
`airQualityInferiorMaxPpm`) to make the AirQualitySensor's ppm
|
|
15
|
+
boundaries configurable instead of hardcoded
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- ntfy notifications for an authentication failure now use a fixed,
|
|
19
|
+
generic message instead of the raw internal error text; other poll
|
|
20
|
+
failures still send the raw error message. The Homebridge log still
|
|
21
|
+
always shows the detailed error either way.
|
|
22
|
+
|
|
9
23
|
## [1.0.0] - 2026-07-26
|
|
10
24
|
|
|
11
25
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Homebridge Withings Environment Data v1.
|
|
1
|
+
# Homebridge Withings Environment Data v1.1.0
|
|
2
2
|
|
|
3
3
|
**This Homebridge plugin has been 100% vibe coded with Claude.**
|
|
4
4
|
|
|
@@ -41,7 +41,8 @@ accessory:
|
|
|
41
41
|
- **CarbonDioxideSensor**: precise CO2 level in ppm, plus a normal/abnormal
|
|
42
42
|
detected alert based on a configurable threshold.
|
|
43
43
|
- **AirQualitySensor**: the same CO2 reading mapped to HomeKit's
|
|
44
|
-
Excellent/Good/Fair/Inferior/Poor category
|
|
44
|
+
Excellent/Good/Fair/Inferior/Poor category, based on configurable ppm
|
|
45
|
+
thresholds.
|
|
45
46
|
- **TemperatureSensor**: room temperature in °C.
|
|
46
47
|
|
|
47
48
|
These appear automatically in the Home app after Homebridge restarts, no
|
|
@@ -68,6 +69,9 @@ Fields:
|
|
|
68
69
|
matching the scale's own upload cadence).
|
|
69
70
|
- **CO2 Detected Threshold (ppm)**: ppm above which the CarbonDioxideSensor
|
|
70
71
|
reports "abnormal" (default 1000).
|
|
72
|
+
- **Air Quality: Excellent/Good/Fair/Inferior below (ppm)**: the four ppm
|
|
73
|
+
boundaries the AirQualitySensor category is based on (defaults 800, 1000,
|
|
74
|
+
1500, 2000).
|
|
71
75
|
- **ntfy Topic (optional)**: if set, sends a push notification via
|
|
72
76
|
[ntfy.sh](https://ntfy.sh) to this topic the first time a poll fails
|
|
73
77
|
(not repeated on every subsequent failure in the same streak; only once
|
package/config.schema.json
CHANGED
|
@@ -49,6 +49,34 @@
|
|
|
49
49
|
"default": 1000,
|
|
50
50
|
"minimum": 400
|
|
51
51
|
},
|
|
52
|
+
"airQualityExcellentMaxPpm": {
|
|
53
|
+
"title": "Air Quality: Excellent below (ppm)",
|
|
54
|
+
"type": "integer",
|
|
55
|
+
"default": 800,
|
|
56
|
+
"minimum": 1,
|
|
57
|
+
"description": "CO2 readings below this are reported as Excellent air quality."
|
|
58
|
+
},
|
|
59
|
+
"airQualityGoodMaxPpm": {
|
|
60
|
+
"title": "Air Quality: Good below (ppm)",
|
|
61
|
+
"type": "integer",
|
|
62
|
+
"default": 1000,
|
|
63
|
+
"minimum": 1,
|
|
64
|
+
"description": "CO2 readings below this are reported as Good."
|
|
65
|
+
},
|
|
66
|
+
"airQualityFairMaxPpm": {
|
|
67
|
+
"title": "Air Quality: Fair below (ppm)",
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"default": 1500,
|
|
70
|
+
"minimum": 1,
|
|
71
|
+
"description": "CO2 readings below this are reported as Fair."
|
|
72
|
+
},
|
|
73
|
+
"airQualityInferiorMaxPpm": {
|
|
74
|
+
"title": "Air Quality: Inferior below (ppm)",
|
|
75
|
+
"type": "integer",
|
|
76
|
+
"default": 2000,
|
|
77
|
+
"minimum": 1,
|
|
78
|
+
"description": "CO2 readings below this are reported as Inferior. Anything above is reported as Poor."
|
|
79
|
+
},
|
|
52
80
|
"noResponseAfterMissedPolls": {
|
|
53
81
|
"title": "No Response After Missed Polls",
|
|
54
82
|
"type": "integer",
|
package/index.js
CHANGED
|
@@ -63,11 +63,11 @@ async function fetchLatest({ cookieHeader, sessionToken, deviceId, userId }) {
|
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function mapCo2ToAirQuality(ppm, AirQuality) {
|
|
67
|
-
if (ppm <
|
|
68
|
-
if (ppm <
|
|
69
|
-
if (ppm <
|
|
70
|
-
if (ppm <
|
|
66
|
+
function mapCo2ToAirQuality(ppm, AirQuality, thresholds) {
|
|
67
|
+
if (ppm < thresholds.excellentMaxPpm) return AirQuality.EXCELLENT;
|
|
68
|
+
if (ppm < thresholds.goodMaxPpm) return AirQuality.GOOD;
|
|
69
|
+
if (ppm < thresholds.fairMaxPpm) return AirQuality.FAIR;
|
|
70
|
+
if (ppm < thresholds.inferiorMaxPpm) return AirQuality.INFERIOR;
|
|
71
71
|
return AirQuality.POOR;
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -216,7 +216,13 @@ class WithingsEnvironmentDataPlatform {
|
|
|
216
216
|
|
|
217
217
|
async poll() {
|
|
218
218
|
try {
|
|
219
|
-
|
|
219
|
+
let cookieHeader, sessionToken;
|
|
220
|
+
try {
|
|
221
|
+
({ cookieHeader, sessionToken } = await this.authenticate());
|
|
222
|
+
} catch (err) {
|
|
223
|
+
err.isAuthFailure = true;
|
|
224
|
+
throw err;
|
|
225
|
+
}
|
|
220
226
|
|
|
221
227
|
if (!this.deviceId || !this.userId) {
|
|
222
228
|
const discovered = await discoverDevice(cookieHeader);
|
|
@@ -252,7 +258,10 @@ class WithingsEnvironmentDataPlatform {
|
|
|
252
258
|
|
|
253
259
|
if (!this.hasNotifiedFailure) {
|
|
254
260
|
this.hasNotifiedFailure = true;
|
|
255
|
-
|
|
261
|
+
const notificationMessage = err.isAuthFailure
|
|
262
|
+
? 'Authentication for Withings account failed. Check the Homebridge logs for details.'
|
|
263
|
+
: err.message;
|
|
264
|
+
await this.sendNtfyNotification(notificationMessage);
|
|
256
265
|
}
|
|
257
266
|
}
|
|
258
267
|
}
|
|
@@ -282,6 +291,19 @@ class WithingsEnvironmentDataPlatform {
|
|
|
282
291
|
return Number.isFinite(threshold) && threshold >= 0 ? threshold : 2;
|
|
283
292
|
}
|
|
284
293
|
|
|
294
|
+
getAirQualityThresholds() {
|
|
295
|
+
const excellentMaxPpm = Number(this.config.airQualityExcellentMaxPpm);
|
|
296
|
+
const goodMaxPpm = Number(this.config.airQualityGoodMaxPpm);
|
|
297
|
+
const fairMaxPpm = Number(this.config.airQualityFairMaxPpm);
|
|
298
|
+
const inferiorMaxPpm = Number(this.config.airQualityInferiorMaxPpm);
|
|
299
|
+
return {
|
|
300
|
+
excellentMaxPpm: Number.isFinite(excellentMaxPpm) && excellentMaxPpm > 0 ? excellentMaxPpm : 800,
|
|
301
|
+
goodMaxPpm: Number.isFinite(goodMaxPpm) && goodMaxPpm > 0 ? goodMaxPpm : 1000,
|
|
302
|
+
fairMaxPpm: Number.isFinite(fairMaxPpm) && fairMaxPpm > 0 ? fairMaxPpm : 1500,
|
|
303
|
+
inferiorMaxPpm: Number.isFinite(inferiorMaxPpm) && inferiorMaxPpm > 0 ? inferiorMaxPpm : 2000,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
285
307
|
isStale() {
|
|
286
308
|
return !this.hasEverSucceeded || this.missedCycles > this.getNoResponseThreshold();
|
|
287
309
|
}
|
|
@@ -306,7 +328,7 @@ class WithingsEnvironmentDataPlatform {
|
|
|
306
328
|
|
|
307
329
|
getAirQualityOrThrow() {
|
|
308
330
|
this.throwIfStale();
|
|
309
|
-
return mapCo2ToAirQuality(this.lastReading.co2, this.Characteristic.AirQuality);
|
|
331
|
+
return mapCo2ToAirQuality(this.lastReading.co2, this.Characteristic.AirQuality, this.getAirQualityThresholds());
|
|
310
332
|
}
|
|
311
333
|
|
|
312
334
|
getTemperatureOrThrow() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-withings-environment-data",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Homebridge plugin exposing ambient CO2/air-quality and room temperature readings from a Withings WS-50 scale as HomeKit sensors",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|