homebridge-philips-airplus-cloud 0.3.8 → 0.3.9

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
@@ -2,6 +2,10 @@
2
2
 
3
3
  The Home Assistant integration in `custom_components/` is unchanged from upstream and keeps its own history.
4
4
 
5
+ ## 0.3.9 — 2026-10-11
6
+
7
+ - New **Allergens** sensor on models that report the Philips allergen index (AC0651/10, AC1715/11, AC3221). HomeKit has no allergen reading, so the index (1–12) is shown as a second air quality sensor: 1 Excellent, 2–3 Good, 4–6 Fair, 7–9 Inferior, 10–12 Poor. Option `allergenSensor` (default on).
8
+
5
9
  ## 0.3.8 — 2026-10-11
6
10
 
7
11
  - Homebridge verification: `supports-hap` keyword; config schema uses object-level `required` arrays instead of per-field `required: true`. No functional changes.
package/README.md CHANGED
@@ -18,6 +18,7 @@ One accessory per purifier with:
18
18
  - **Sleep switch** — turns Sleep mode on; turning it off returns to Auto.
19
19
  - **Display light** — the purifier's display/LED as a dimmable light. It appears after the purifier first reports its light setting (property `D03104`, `D03105` or `D0312D`); devices with three light levels are detected automatically.
20
20
  - **Air Quality Sensor** — PM2.5 (µg/m³) and a HomeKit air-quality level derived from it.
21
+ - **Allergens** — on models that report the Philips allergen index (1–12): a second air quality sensor whose level follows the index (1 Excellent, 2–3 Good, 4–6 Fair, 7–9 Inferior, 10–12 Poor). HomeKit has no allergen or pollen reading of its own.
21
22
  - **Filter Maintenance** — main filter and pre-filter (cleaning), with life level, change alert (below 10% by default) and reset from the Home app. The reset writes the same value the official app does.
22
23
 
23
24
  State changes arrive in real time over MQTT; a status poll runs every 120 s as a safety net.
@@ -77,6 +78,7 @@ The refresh token rotates on every use; the plugin keeps the current one in `<ho
77
78
  |---|---|---|
78
79
  | `devices` | all | `[{ "uuid": "…", "name": "…", "model": "AC0651/10" }]` — pick/rename devices; `model` is only needed if auto-detection fails |
79
80
  | `airQualitySensor` | `true` | Show the PM2.5 sensor |
81
+ | `allergenSensor` | `true` | Show the allergen index as a second air quality sensor (models that report it) |
80
82
  | `filterSensors` | `true` | Show filter and pre-filter status |
81
83
  | `sleepSwitch` | `true` | Show the Sleep mode switch |
82
84
  | `sleepSwitchStyle` | `switch` | `purifier` shows the Sleep control as a second air purifier (purifier icon in Home) that only turns Sleep on/off |
@@ -62,6 +62,12 @@
62
62
  "type": "boolean",
63
63
  "default": true
64
64
  },
65
+ "allergenSensor": {
66
+ "title": "Show allergen sensor",
67
+ "type": "boolean",
68
+ "default": true,
69
+ "description": "HomeKit has no allergen reading: the Philips allergen index (1–12) is shown as a second air quality sensor, 'Allergens'. Only on models that report it."
70
+ },
65
71
  "filterSensors": {
66
72
  "title": "Show filter and pre-filter status",
67
73
  "type": "boolean",
@@ -174,6 +180,7 @@
174
180
  "expanded": false,
175
181
  "items": [
176
182
  "airQualitySensor",
183
+ "allergenSensor",
177
184
  "filterSensors",
178
185
  "sleepSwitch",
179
186
  "sleepSwitchStyle",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-philips-airplus-cloud",
3
3
  "displayName": "Philips Air+",
4
- "version": "0.3.8",
4
+ "version": "0.3.9",
5
5
  "description": "Homebridge plugin for Philips Air+ / PureProtect air purifiers via the Philips (Versuni) cloud — the same MQTT protocol used by the Air+ app and Google Home.",
6
6
  "main": "src/index.js",
7
7
  "license": "MIT",
package/src/accessory.js CHANGED
@@ -35,6 +35,19 @@ const LIGHT_KEYS = ['D03104', 'D03105', 'D0312D'];
35
35
  // Bump to re-apply default service labels once on existing accessories.
36
36
  const NAMES_VERSION = 2;
37
37
 
38
+ /**
39
+ * Philips allergen index (IAI, 1-12) to a HomeKit air quality level.
40
+ * Philips bands: 1-3 good, 4-6 fair, 7-9 poor, 10-12 very poor.
41
+ */
42
+ function allergenToAirQuality(idx) {
43
+ if (idx == null || Number.isNaN(idx) || idx <= 0) return 0; // UNKNOWN
44
+ if (idx <= 1) return 1; // EXCELLENT
45
+ if (idx <= 3) return 2; // GOOD
46
+ if (idx <= 6) return 3; // FAIR
47
+ if (idx <= 9) return 4; // INFERIOR
48
+ return 5; // POOR
49
+ }
50
+
38
51
  function pm25ToAirQuality(pm) {
39
52
  if (pm == null || Number.isNaN(pm)) return 0; // UNKNOWN
40
53
  if (pm <= 12) return 1; // EXCELLENT
@@ -212,7 +225,9 @@ class PurifierAccessory {
212
225
  }
213
226
 
214
227
  _remove(type, subtype) {
215
- const s = subtype ? this.accessory.getServiceById(type, subtype) : this.accessory.getService(type);
228
+ const s = subtype
229
+ ? this.accessory.getServiceById(type, subtype)
230
+ : this.accessory.services.find((x) => x.UUID === type.UUID && !x.subtype);
216
231
  if (s) this.accessory.removeService(s);
217
232
  }
218
233
 
@@ -263,6 +278,18 @@ class PurifierAccessory {
263
278
  this.aq = null;
264
279
  }
265
280
 
281
+ // --- Allergen index (HomeKit has no allergen characteristic: shown as
282
+ // a second air quality sensor whose level follows the Philips index)
283
+ if (this._prop('allergen_index') && cfg.allergenSensor !== false) {
284
+ const al = this._service(Service.AirQualitySensor, 'Allergens', 'allergen');
285
+ this.allergen = al;
286
+ al.getCharacteristic(Characteristic.AirQuality).onGet(() => allergenToAirQuality(this._num('allergen_index')));
287
+ al.getCharacteristic(Characteristic.StatusActive).onGet(() => this.conn.connected);
288
+ } else {
289
+ this._remove(Service.AirQualitySensor, 'allergen');
290
+ this.allergen = null;
291
+ }
292
+
266
293
  // --- Filters
267
294
  this.filters = {};
268
295
  const filterDefs = [
@@ -669,6 +696,11 @@ class PurifierAccessory {
669
696
  }
670
697
  this.aq.updateCharacteristic(Characteristic.StatusActive, this.conn.connected);
671
698
  }
699
+ if (this.allergen) {
700
+ const idx = this._num('allergen_index');
701
+ if (idx !== null) this.allergen.updateCharacteristic(Characteristic.AirQuality, allergenToAirQuality(idx));
702
+ this.allergen.updateCharacteristic(Characteristic.StatusActive, this.conn.connected);
703
+ }
672
704
  if (this.sleepSwitch) this.sleepSwitch.updateCharacteristic(Characteristic.On, this._isSleep());
673
705
  if (this.sleepPurifier) {
674
706
  this.sleepPurifier.updateCharacteristic(Characteristic.Active, this._isSleep() ? 1 : 0);
@@ -687,4 +719,4 @@ class PurifierAccessory {
687
719
  }
688
720
  }
689
721
 
690
- module.exports = { PurifierAccessory, pm25ToAirQuality, FALLBACK_MODEL };
722
+ module.exports = { PurifierAccessory, pm25ToAirQuality, allergenToAirQuality, FALLBACK_MODEL };