homebridge-pollen 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/CLAUDE.md +32 -28
- package/CONTRIBUTING.md +11 -12
- package/README.md +17 -50
- package/config.schema.json +2 -82
- package/dist/platform.js +14 -34
- package/dist/platform.js.map +1 -1
- package/dist/platformAccessory.d.ts +0 -2
- package/dist/platformAccessory.js +6 -30
- package/dist/platformAccessory.js.map +1 -1
- package/dist/pollenService.d.ts +1 -3
- package/dist/pollenService.js +22 -9
- package/dist/pollenService.js.map +1 -1
- package/dist/settings.d.ts +0 -11
- package/dist/settings.js +1 -7
- package/dist/settings.js.map +1 -1
- package/dist/types.d.ts +1 -29
- package/dist/types.js +0 -23
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# CLAUDE.md
|
|
2
2
|
|
|
3
|
-
## Project
|
|
3
|
+
## Project overview
|
|
4
4
|
|
|
5
|
-
Homebridge dynamic platform plugin that fetches pollen data from the Ambee API and exposes it as HomeKit
|
|
5
|
+
Homebridge dynamic platform plugin that fetches pollen data from the Ambee API and exposes it as HomeKit air quality sensors. No runtime dependencies — uses Node.js 20+ built-in `fetch`.
|
|
6
6
|
|
|
7
7
|
## Commands
|
|
8
8
|
|
|
@@ -10,13 +10,13 @@ Homebridge dynamic platform plugin that fetches pollen data from the Ambee API a
|
|
|
10
10
|
- `npm run lint` — ESLint
|
|
11
11
|
- `npm run dev` — build, link, and start Homebridge with nodemon (auto-restarts on src changes)
|
|
12
12
|
|
|
13
|
-
## Homebridge
|
|
13
|
+
## Homebridge plugin architecture
|
|
14
14
|
|
|
15
15
|
### Registration
|
|
16
16
|
|
|
17
17
|
A Homebridge plugin exports a default function that receives the `API` object and calls `api.registerPlatform(PLATFORM_NAME, PlatformClass)`. The platform name here must match `pluginAlias` in `config.schema.json`. The npm package name (`homebridge-pollen`) is how Homebridge discovers the plugin.
|
|
18
18
|
|
|
19
|
-
### Dynamic
|
|
19
|
+
### Dynamic platform plugin lifecycle
|
|
20
20
|
|
|
21
21
|
The plugin implements `DynamicPlatformPlugin` from `homebridge`. The lifecycle is:
|
|
22
22
|
|
|
@@ -30,26 +30,34 @@ The plugin implements `DynamicPlatformPlugin` from `homebridge`. The lifecycle i
|
|
|
30
30
|
|
|
31
31
|
### Accessory UUIDs
|
|
32
32
|
|
|
33
|
-
UUIDs must be deterministic and stable across restarts. Generate them with `api.hap.uuid.generate(stableString)`. This plugin uses `pollen-{category}-{
|
|
33
|
+
UUIDs must be deterministic and stable across restarts. Generate them with `api.hap.uuid.generate(stableString)`. This plugin uses `pollen-{category}-{location}` as the stable string. If the UUID changes, Homebridge treats it as a new accessory and the old one becomes stale.
|
|
34
34
|
|
|
35
|
-
### Services and
|
|
35
|
+
### Services and characteristics
|
|
36
36
|
|
|
37
37
|
Each `PlatformAccessory` has one or more `Service` objects. Always check `accessory.getService(ServiceType)` before calling `addService` to avoid duplicates on restore from cache.
|
|
38
38
|
|
|
39
39
|
Key pattern for getting/setting services:
|
|
40
40
|
```ts
|
|
41
|
-
const service = accessory.getService(api.hap.Service.
|
|
42
|
-
|| accessory.addService(api.hap.Service.
|
|
41
|
+
const service = accessory.getService(api.hap.Service.AirQualitySensor)
|
|
42
|
+
|| accessory.addService(api.hap.Service.AirQualitySensor, displayName);
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
**Push model** — use `service.updateCharacteristic(CharType, value)` to push updates. This is preferred over registering `onGet` handlers when data is fetched on a polling interval. HomeKit GET requests return the last pushed value instantly.
|
|
46
46
|
|
|
47
|
-
### HomeKit
|
|
47
|
+
### HomeKit service types used
|
|
48
48
|
|
|
49
|
-
- **
|
|
50
|
-
- **AirQualitySensor** — `AirQuality`: `UNKNOWN` (0), `EXCELLENT` (1), `GOOD` (2), `FAIR` (3), `INFERIOR` (4), `POOR` (5).
|
|
49
|
+
- **AirQualitySensor** — `AirQuality`: `UNKNOWN` (0), `EXCELLENT` (1), `GOOD` (2), `FAIR` (3), `INFERIOR` (4), `POOR` (5). Pollen counts are mapped to this scale.
|
|
51
50
|
- **AccessoryInformation** — always present on every accessory. Set `Manufacturer`, `Model`, `SerialNumber`.
|
|
52
51
|
|
|
52
|
+
### Accessories created
|
|
53
|
+
|
|
54
|
+
- **Pollen** (always) — overall air quality sensor using combined pollen count
|
|
55
|
+
- **Tree Pollen** (optional) — air quality sensor for tree pollen count
|
|
56
|
+
- **Grass Pollen** (optional) — air quality sensor for grass pollen count
|
|
57
|
+
- **Weed Pollen** (optional) — air quality sensor for weed pollen count
|
|
58
|
+
|
|
59
|
+
Enable per-category sensors via `enableCategorySensors` in config.
|
|
60
|
+
|
|
53
61
|
### config.schema.json
|
|
54
62
|
|
|
55
63
|
This file defines the Homebridge UI configuration form. Key fields:
|
|
@@ -57,9 +65,9 @@ This file defines the Homebridge UI configuration form. Key fields:
|
|
|
57
65
|
- `pluginAlias` — must match `PLATFORM_NAME` passed to `api.registerPlatform()`
|
|
58
66
|
- `pluginType` — `"platform"` for platform plugins
|
|
59
67
|
- `singular: true` — only one instance of this platform allowed
|
|
60
|
-
- `schema.properties` — defines form fields. Use `"required": true` on required fields.
|
|
68
|
+
- `schema.properties` — defines form fields. Use `"required": true` on required fields.
|
|
61
69
|
|
|
62
|
-
### TypeScript
|
|
70
|
+
### TypeScript configuration
|
|
63
71
|
|
|
64
72
|
- Target `ES2022` with `module: nodenext` / `moduleResolution: nodenext`
|
|
65
73
|
- Imports must use `.js` extensions (e.g., `import { Foo } from './foo.js'`) — this is required by nodenext module resolution even though the source files are `.ts`
|
|
@@ -73,26 +81,22 @@ This file defines the Homebridge UI configuration form. Key fields:
|
|
|
73
81
|
- **Response shape**: `{ message: string, data: [{ Count: { grass_pollen, tree_pollen, weed_pollen }, Risk: {...}, Species: {...}, updatedAt: string }] }`
|
|
74
82
|
- The `by-place` endpoint accepts zip codes and place names directly
|
|
75
83
|
|
|
76
|
-
##
|
|
77
|
-
|
|
78
|
-
Default thresholds based on NAB (National Allergy Bureau) guidelines. A count is classified as:
|
|
79
|
-
- **Low** if `count <= threshold.low`
|
|
80
|
-
- **High** if `count >= threshold.high`
|
|
81
|
-
- **Medium** otherwise
|
|
84
|
+
## Air quality mapping
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
|----------|-----|------|
|
|
85
|
-
| Overall | 50 | 200 |
|
|
86
|
-
| Tree | 15 | 90 |
|
|
87
|
-
| Grass | 20 | 200 |
|
|
88
|
-
| Weed | 10 | 50 |
|
|
86
|
+
Pollen counts are mapped to HomeKit's AirQuality scale:
|
|
89
87
|
|
|
90
|
-
|
|
88
|
+
| Pollen count | AirQuality |
|
|
89
|
+
|--------------|------------|
|
|
90
|
+
| 0-20 | Excellent |
|
|
91
|
+
| 21-80 | Good |
|
|
92
|
+
| 81-200 | Fair |
|
|
93
|
+
| 201-400 | Inferior |
|
|
94
|
+
| 400+ | Poor |
|
|
91
95
|
|
|
92
|
-
## Error
|
|
96
|
+
## Error handling
|
|
93
97
|
|
|
94
98
|
- Missing `apiKey`/`location` — logs error, plugin does not start polling
|
|
95
|
-
- 401 — logs
|
|
99
|
+
- 401 — logs error about invalid API key, returns cached data
|
|
96
100
|
- 429 — logs warning, increments consecutive failure counter for exponential backoff
|
|
97
101
|
- Network/5xx — logs warning, returns cached data, retries next cycle
|
|
98
102
|
- Empty data array — logs warning about unsupported location, retains cache
|
package/CONTRIBUTING.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Contributing
|
|
2
2
|
|
|
3
|
-
## Local
|
|
3
|
+
## Local development
|
|
4
4
|
|
|
5
5
|
### Prerequisites
|
|
6
6
|
|
|
@@ -40,9 +40,9 @@ npm run build
|
|
|
40
40
|
|
|
41
41
|
After running `npm run setup` and `npm run dev`, verify in the console output that:
|
|
42
42
|
|
|
43
|
-
-
|
|
44
|
-
- An initial pollen fetch completes and
|
|
45
|
-
-
|
|
43
|
+
- 1 AirQualitySensor accessory is registered (Pollen)
|
|
44
|
+
- An initial pollen fetch completes and logs the pollen counts
|
|
45
|
+
- The air quality value is set based on the overall pollen count
|
|
46
46
|
|
|
47
47
|
To test optional features, edit `.homebridge/config.json` and add:
|
|
48
48
|
|
|
@@ -51,12 +51,11 @@ To test optional features, edit `.homebridge/config.json` and add:
|
|
|
51
51
|
"platform": "HomebridgePollen",
|
|
52
52
|
"apiKey": "YOUR_AMBEE_API_KEY",
|
|
53
53
|
"location": "10001",
|
|
54
|
-
"enableCategorySensors": true
|
|
55
|
-
"enableAirQualitySensor": true
|
|
54
|
+
"enableCategorySensors": true
|
|
56
55
|
}
|
|
57
56
|
```
|
|
58
57
|
|
|
59
|
-
This adds
|
|
58
|
+
This adds 3 additional air quality sensors for Tree, Grass, and Weed pollen. Restart the dev server to pick up the changes.
|
|
60
59
|
|
|
61
60
|
### Pairing with Apple Home
|
|
62
61
|
|
|
@@ -69,19 +68,19 @@ To test end-to-end in the Home app:
|
|
|
69
68
|
|
|
70
69
|
The test bridge uses port 51826, so it can run alongside a production Homebridge instance on the default port. When you're done testing, unpair the bridge from Home to avoid stale accessories.
|
|
71
70
|
|
|
72
|
-
### Project
|
|
71
|
+
### Project structure
|
|
73
72
|
|
|
74
73
|
```
|
|
75
74
|
src/
|
|
76
75
|
├── index.ts # Entry point — registers platform with Homebridge
|
|
77
|
-
├── settings.ts # Constants
|
|
78
|
-
├── types.ts # TypeScript interfaces
|
|
76
|
+
├── settings.ts # Constants (plugin name, API URL, poll intervals)
|
|
77
|
+
├── types.ts # TypeScript interfaces
|
|
79
78
|
├── pollenService.ts # Ambee API client (fetch, parse, cache)
|
|
80
79
|
├── platform.ts # DynamicPlatformPlugin (discovery, polling)
|
|
81
|
-
└── platformAccessory.ts # Maps pollen data to HomeKit
|
|
80
|
+
└── platformAccessory.ts # Maps pollen data to HomeKit AirQualitySensor
|
|
82
81
|
```
|
|
83
82
|
|
|
84
|
-
## Submitting
|
|
83
|
+
## Submitting changes
|
|
85
84
|
|
|
86
85
|
1. Create a branch from `main`.
|
|
87
86
|
2. Make your changes and verify `npm run build` and `npm run lint` pass.
|
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# homebridge-pollen
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/homebridge-pollen)
|
|
4
|
-
[](https://www.npmjs.com/package/homebridge-pollen)
|
|
5
|
+
[](LICENSE)
|
|
5
6
|
|
|
6
|
-
Homebridge plugin that exposes pollen levels as HomeKit sensors using the [Ambee API](https://www.getambee.com/).
|
|
7
|
+
Homebridge plugin that exposes pollen levels as HomeKit air quality sensors using the [Ambee API](https://www.getambee.com/).
|
|
7
8
|
|
|
8
9
|
## Features
|
|
9
10
|
|
|
10
|
-
- **
|
|
11
|
+
- **Air quality sensors** — Pollen counts are mapped to HomeKit's 5-level air quality scale (Excellent, Good, Fair, Inferior, Poor)
|
|
11
12
|
- **Per-category sensors** — Optionally add separate sensors for tree, grass, and weed pollen
|
|
12
|
-
- **Air quality sensor** — Optionally add an air quality sensor that maps overall pollen count to a 1-5 severity scale
|
|
13
13
|
- **No runtime dependencies** — Uses Node.js 20+ built-in `fetch`
|
|
14
14
|
|
|
15
15
|
## Requirements
|
|
@@ -65,60 +65,27 @@ You can configure the plugin using the Homebridge UI or by editing your `config.
|
|
|
65
65
|
| `apiKey` | Yes | — | Your Ambee API key |
|
|
66
66
|
| `location` | Yes | — | Zip code or place name to fetch pollen data for |
|
|
67
67
|
| `pollInterval` | No | `60` | How often to fetch pollen data, in minutes (minimum 15) |
|
|
68
|
-
| `enableCategorySensors` | No | `false` | Add
|
|
69
|
-
| `enableAirQualitySensor` | No | `false` | Add an air quality sensor accessory |
|
|
70
|
-
| `thresholds` | No | — | Override default pollen count thresholds (see below) |
|
|
68
|
+
| `enableCategorySensors` | No | `false` | Add 3 additional sensors for tree, grass, and weed pollen |
|
|
71
69
|
|
|
72
70
|
## How it works
|
|
73
71
|
|
|
74
|
-
The plugin creates
|
|
72
|
+
The plugin creates an air quality sensor that displays the overall pollen level. Pollen counts are mapped to HomeKit's air quality scale:
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
| Pollen count | Air quality |
|
|
75
|
+
|--------------|-------------|
|
|
76
|
+
| 0-20 | Excellent |
|
|
77
|
+
| 21-80 | Good |
|
|
78
|
+
| 81-200 | Fair |
|
|
79
|
+
| 201-400 | Inferior |
|
|
80
|
+
| 400+ | Poor |
|
|
81
81
|
|
|
82
82
|
### Optional sensors
|
|
83
83
|
|
|
84
|
-
When `enableCategorySensors` is enabled, you get
|
|
85
|
-
|
|
86
|
-
- Tree Pollen High / Medium / Low
|
|
87
|
-
- Grass Pollen High / Medium / Low
|
|
88
|
-
- Weed Pollen High / Medium / Low
|
|
89
|
-
|
|
90
|
-
When `enableAirQualitySensor` is enabled, you get a single air quality sensor that maps the overall pollen count to HomeKit's 5-level air quality scale.
|
|
91
|
-
|
|
92
|
-
## Pollen thresholds
|
|
93
|
-
|
|
94
|
-
Pollen counts are classified as Low, Medium, or High based on thresholds from the National Allergy Bureau (NAB). You can override these defaults in your configuration.
|
|
84
|
+
When `enableCategorySensors` is enabled, you get 3 additional air quality sensors:
|
|
95
85
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
| Tree | 15 | 90 |
|
|
100
|
-
| Grass | 20 | 200 |
|
|
101
|
-
| Weed | 10 | 50 |
|
|
102
|
-
|
|
103
|
-
Counts between the low and high thresholds are classified as Medium.
|
|
104
|
-
|
|
105
|
-
### Customizing thresholds
|
|
106
|
-
|
|
107
|
-
```json
|
|
108
|
-
{
|
|
109
|
-
"platforms": [
|
|
110
|
-
{
|
|
111
|
-
"platform": "HomebridgePollen",
|
|
112
|
-
"apiKey": "your-api-key",
|
|
113
|
-
"location": "10001",
|
|
114
|
-
"thresholds": {
|
|
115
|
-
"overall": { "low": 30, "high": 150 },
|
|
116
|
-
"tree": { "low": 10, "high": 60 }
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
```
|
|
86
|
+
- **Tree Pollen** — Air quality based on tree pollen count
|
|
87
|
+
- **Grass Pollen** — Air quality based on grass pollen count
|
|
88
|
+
- **Weed Pollen** — Air quality based on weed pollen count
|
|
122
89
|
|
|
123
90
|
## License
|
|
124
91
|
|
package/config.schema.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"pluginAlias": "HomebridgePollen",
|
|
3
3
|
"pluginType": "platform",
|
|
4
4
|
"singular": true,
|
|
5
|
-
"headerDisplay": "Exposes pollen levels from the [Ambee API](https://www.getambee.com/) as HomeKit
|
|
5
|
+
"headerDisplay": "Exposes pollen levels from the [Ambee API](https://www.getambee.com/) as HomeKit air quality sensors.",
|
|
6
6
|
"footerDisplay": "For help, see the [README](https://github.com/pburtchaell/homebridge-pollen).",
|
|
7
7
|
"schema": {
|
|
8
8
|
"type": "object",
|
|
@@ -31,87 +31,7 @@
|
|
|
31
31
|
"title": "Enable Per-Category Sensors",
|
|
32
32
|
"type": "boolean",
|
|
33
33
|
"default": false,
|
|
34
|
-
"description": "Add
|
|
35
|
-
},
|
|
36
|
-
"enableAirQualitySensor": {
|
|
37
|
-
"title": "Enable Air Quality Sensor",
|
|
38
|
-
"type": "boolean",
|
|
39
|
-
"default": false,
|
|
40
|
-
"description": "Add an AirQualitySensor accessory that maps overall pollen count to a 1-5 severity scale."
|
|
41
|
-
},
|
|
42
|
-
"thresholds": {
|
|
43
|
-
"title": "Threshold Overrides",
|
|
44
|
-
"type": "object",
|
|
45
|
-
"expandable": true,
|
|
46
|
-
"description": "Override default pollen count thresholds for level classification.",
|
|
47
|
-
"properties": {
|
|
48
|
-
"overall": {
|
|
49
|
-
"title": "Overall",
|
|
50
|
-
"type": "object",
|
|
51
|
-
"properties": {
|
|
52
|
-
"low": {
|
|
53
|
-
"title": "Low Threshold",
|
|
54
|
-
"type": "integer",
|
|
55
|
-
"default": 50,
|
|
56
|
-
"description": "Counts at or below this value are classified as Low."
|
|
57
|
-
},
|
|
58
|
-
"high": {
|
|
59
|
-
"title": "High Threshold",
|
|
60
|
-
"type": "integer",
|
|
61
|
-
"default": 200,
|
|
62
|
-
"description": "Counts at or above this value are classified as High."
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
"tree": {
|
|
67
|
-
"title": "Tree",
|
|
68
|
-
"type": "object",
|
|
69
|
-
"properties": {
|
|
70
|
-
"low": {
|
|
71
|
-
"title": "Low Threshold",
|
|
72
|
-
"type": "integer",
|
|
73
|
-
"default": 15
|
|
74
|
-
},
|
|
75
|
-
"high": {
|
|
76
|
-
"title": "High Threshold",
|
|
77
|
-
"type": "integer",
|
|
78
|
-
"default": 90
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
"grass": {
|
|
83
|
-
"title": "Grass",
|
|
84
|
-
"type": "object",
|
|
85
|
-
"properties": {
|
|
86
|
-
"low": {
|
|
87
|
-
"title": "Low Threshold",
|
|
88
|
-
"type": "integer",
|
|
89
|
-
"default": 20
|
|
90
|
-
},
|
|
91
|
-
"high": {
|
|
92
|
-
"title": "High Threshold",
|
|
93
|
-
"type": "integer",
|
|
94
|
-
"default": 200
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
"weed": {
|
|
99
|
-
"title": "Weed",
|
|
100
|
-
"type": "object",
|
|
101
|
-
"properties": {
|
|
102
|
-
"low": {
|
|
103
|
-
"title": "Low Threshold",
|
|
104
|
-
"type": "integer",
|
|
105
|
-
"default": 10
|
|
106
|
-
},
|
|
107
|
-
"high": {
|
|
108
|
-
"title": "High Threshold",
|
|
109
|
-
"type": "integer",
|
|
110
|
-
"default": 50
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
34
|
+
"description": "Add 3 additional air quality sensors for Tree, Grass, and Weed pollen."
|
|
115
35
|
}
|
|
116
36
|
}
|
|
117
37
|
}
|
package/dist/platform.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PollenPlatform = void 0;
|
|
4
4
|
const settings_js_1 = require("./settings.js");
|
|
5
|
-
const types_js_1 = require("./types.js");
|
|
6
5
|
const pollenService_js_1 = require("./pollenService.js");
|
|
7
6
|
const platformAccessory_js_1 = require("./platformAccessory.js");
|
|
8
7
|
class PollenPlatform {
|
|
@@ -23,8 +22,7 @@ class PollenPlatform {
|
|
|
23
22
|
+ 'Plugin will not start.');
|
|
24
23
|
return;
|
|
25
24
|
}
|
|
26
|
-
|
|
27
|
-
this.pollenService = new pollenService_js_1.PollenService(pollenConfig.apiKey, pollenConfig.location, thresholds, this.log);
|
|
25
|
+
this.pollenService = new pollenService_js_1.PollenService(pollenConfig.apiKey, pollenConfig.location, this.log);
|
|
28
26
|
this.api.on('didFinishLaunching', () => {
|
|
29
27
|
this.discoverDevices(pollenConfig);
|
|
30
28
|
this.startPolling(pollenConfig);
|
|
@@ -72,42 +70,24 @@ class PollenPlatform {
|
|
|
72
70
|
buildAccessoryDefinitions(config) {
|
|
73
71
|
const location = config.location;
|
|
74
72
|
const definitions = [];
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
level,
|
|
83
|
-
category: 'overall',
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
// Optional per-category sensors (9 total)
|
|
73
|
+
// Main pollen air quality sensor (always created)
|
|
74
|
+
definitions.push({
|
|
75
|
+
id: `pollen-overall-${location}`,
|
|
76
|
+
name: 'Pollen',
|
|
77
|
+
category: 'overall',
|
|
78
|
+
});
|
|
79
|
+
// Optional per-category sensors
|
|
87
80
|
if (config.enableCategorySensors) {
|
|
88
81
|
const categories = ['tree', 'grass', 'weed'];
|
|
89
|
-
const levels = ['High', 'Medium', 'Low'];
|
|
90
82
|
for (const category of categories) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
level,
|
|
98
|
-
category,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
83
|
+
const label = category.charAt(0).toUpperCase() + category.slice(1);
|
|
84
|
+
definitions.push({
|
|
85
|
+
id: `pollen-${category}-${location}`,
|
|
86
|
+
name: `${label} Pollen`,
|
|
87
|
+
category,
|
|
88
|
+
});
|
|
101
89
|
}
|
|
102
90
|
}
|
|
103
|
-
// Optional air quality sensor
|
|
104
|
-
if (config.enableAirQualitySensor) {
|
|
105
|
-
definitions.push({
|
|
106
|
-
id: `pollen-airquality-${location}`,
|
|
107
|
-
name: 'Pollen Air Quality',
|
|
108
|
-
type: 'airquality',
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
91
|
return definitions;
|
|
112
92
|
}
|
|
113
93
|
startPolling(config) {
|
package/dist/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":";;;AAOA,+
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,5 @@ export declare class PollenAccessoryHandler {
|
|
|
9
9
|
private readonly Characteristic;
|
|
10
10
|
constructor(accessory: PlatformAccessory, definition: AccessoryDefinition, api: API, log: Logger);
|
|
11
11
|
updatePollenData(data: ParsedPollenData): void;
|
|
12
|
-
private updateContactSensor;
|
|
13
|
-
private updateAirQuality;
|
|
14
12
|
private mapCountToAirQuality;
|
|
15
13
|
}
|
|
@@ -18,43 +18,19 @@ class PollenAccessoryHandler {
|
|
|
18
18
|
.setCharacteristic(this.Characteristic.Manufacturer, 'Ambee')
|
|
19
19
|
.setCharacteristic(this.Characteristic.Model, 'Pollen Sensor')
|
|
20
20
|
.setCharacteristic(this.Characteristic.SerialNumber, definition.id);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|| this.accessory.addService(this.api.hap.Service.AirQualitySensor, definition.name);
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
this.service = this.accessory.getService(this.api.hap.Service.ContactSensor)
|
|
27
|
-
|| this.accessory.addService(this.api.hap.Service.ContactSensor, definition.name);
|
|
28
|
-
}
|
|
21
|
+
this.service = this.accessory.getService(this.api.hap.Service.AirQualitySensor)
|
|
22
|
+
|| this.accessory.addService(this.api.hap.Service.AirQualitySensor, definition.name);
|
|
29
23
|
this.service.setCharacteristic(this.Characteristic.Name, definition.name);
|
|
30
24
|
}
|
|
31
25
|
updatePollenData(data) {
|
|
32
|
-
if (this.definition.type === 'airquality') {
|
|
33
|
-
this.updateAirQuality(data);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
this.updateContactSensor(data);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
updateContactSensor(data) {
|
|
40
26
|
const category = this.definition.category;
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const isDetected = currentLevel === targetLevel;
|
|
44
|
-
const state = isDetected
|
|
45
|
-
? this.Characteristic.ContactSensorState.CONTACT_DETECTED
|
|
46
|
-
: this.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
|
|
47
|
-
this.service.updateCharacteristic(this.Characteristic.ContactSensorState, state);
|
|
48
|
-
this.log.debug(`${this.definition.name}: ${category} level is ${currentLevel}, `
|
|
49
|
-
+ `sensor ${targetLevel} → ${isDetected ? 'DETECTED' : 'NOT_DETECTED'}`);
|
|
50
|
-
}
|
|
51
|
-
updateAirQuality(data) {
|
|
52
|
-
const quality = this.mapCountToAirQuality(data.overall.count);
|
|
27
|
+
const count = data[category].count;
|
|
28
|
+
const quality = this.mapCountToAirQuality(count);
|
|
53
29
|
this.service.updateCharacteristic(this.Characteristic.AirQuality, quality);
|
|
54
|
-
this.log.debug(`${this.definition.name}:
|
|
30
|
+
this.log.debug(`${this.definition.name}: ${category} count=${count}, AirQuality=${quality}`);
|
|
55
31
|
}
|
|
56
32
|
mapCountToAirQuality(count) {
|
|
57
|
-
// Map
|
|
33
|
+
// Map pollen count to HomeKit AirQuality enum (0-5)
|
|
58
34
|
// 0=UNKNOWN, 1=EXCELLENT, 2=GOOD, 3=FAIR, 4=INFERIOR, 5=POOR
|
|
59
35
|
if (count <= 20) {
|
|
60
36
|
return this.Characteristic.AirQuality.EXCELLENT;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":";;;AASA,MAAa,sBAAsB;IAKd;IACA;IACA;IACA;IAPF,OAAO,CAAU;IACjB,cAAc,CAAwB;IAEvD,YACmB,SAA4B,EAC5B,UAA+B,EAC/B,GAAQ,EACR,GAAW;QAHX,cAAS,GAAT,SAAS,CAAmB;QAC5B,eAAU,GAAV,UAAU,CAAqB;QAC/B,QAAG,GAAH,GAAG,CAAK;QACR,QAAG,GAAH,GAAG,CAAQ;QAE5B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;QAElD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAE;aAClE,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC;aAC5D,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,eAAe,CAAC;aAC7D,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;QAEtE,IAAI,
|
|
1
|
+
{"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":";;;AASA,MAAa,sBAAsB;IAKd;IACA;IACA;IACA;IAPF,OAAO,CAAU;IACjB,cAAc,CAAwB;IAEvD,YACmB,SAA4B,EAC5B,UAA+B,EAC/B,GAAQ,EACR,GAAW;QAHX,cAAS,GAAT,SAAS,CAAmB;QAC5B,eAAU,GAAV,UAAU,CAAqB;QAC/B,QAAG,GAAH,GAAG,CAAK;QACR,QAAG,GAAH,GAAG,CAAQ;QAE5B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;QAElD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAE;aAClE,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC;aAC5D,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,eAAe,CAAC;aAC7D,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;QAEtE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC;eAC1E,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;QAEvF,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IAC5E,CAAC;IAED,gBAAgB,CAAC,IAAsB;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,QAA0B,CAAC;QAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAEjD,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAE3E,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,QAAQ,UAAU,KAAK,gBAAgB,OAAO,EAAE,CAC7E,CAAC;IACJ,CAAC;IAEO,oBAAoB,CAAC,KAAa;QACxC,oDAAoD;QACpD,6DAA6D;QAC7D,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,SAAS,CAAC;QAClD,CAAC;QACD,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC;QAC7C,CAAC;QACD,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC;QAC7C,CAAC;QACD,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,QAAQ,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC;IAC7C,CAAC;CACF;AApDD,wDAoDC"}
|
package/dist/pollenService.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { Logger } from 'homebridge';
|
|
2
|
-
import type { Thresholds } from './settings.js';
|
|
3
2
|
import type { ParsedPollenData } from './types.js';
|
|
4
3
|
export declare class PollenService {
|
|
5
4
|
private readonly apiKey;
|
|
6
5
|
private readonly location;
|
|
7
|
-
private readonly thresholds;
|
|
8
6
|
private readonly log;
|
|
9
7
|
private cache;
|
|
10
8
|
private consecutiveFailures;
|
|
11
|
-
constructor(apiKey: string, location: string,
|
|
9
|
+
constructor(apiKey: string, location: string, log: Logger);
|
|
12
10
|
fetchPollenData(): Promise<ParsedPollenData | null>;
|
|
13
11
|
getBackoffMultiplier(): number;
|
|
14
12
|
getCachedData(): ParsedPollenData | null;
|
package/dist/pollenService.js
CHANGED
|
@@ -2,18 +2,31 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PollenService = void 0;
|
|
4
4
|
const settings_js_1 = require("./settings.js");
|
|
5
|
-
|
|
5
|
+
// Internal thresholds for level classification (used for logging)
|
|
6
|
+
const THRESHOLDS = {
|
|
7
|
+
overall: { low: 50, high: 200 },
|
|
8
|
+
tree: { low: 15, high: 90 },
|
|
9
|
+
grass: { low: 20, high: 200 },
|
|
10
|
+
weed: { low: 10, high: 50 },
|
|
11
|
+
};
|
|
12
|
+
function classifyLevel(count, thresholds) {
|
|
13
|
+
if (count >= thresholds.high) {
|
|
14
|
+
return 'High';
|
|
15
|
+
}
|
|
16
|
+
if (count <= thresholds.low) {
|
|
17
|
+
return 'Low';
|
|
18
|
+
}
|
|
19
|
+
return 'Medium';
|
|
20
|
+
}
|
|
6
21
|
class PollenService {
|
|
7
22
|
apiKey;
|
|
8
23
|
location;
|
|
9
|
-
thresholds;
|
|
10
24
|
log;
|
|
11
25
|
cache = null;
|
|
12
26
|
consecutiveFailures = 0;
|
|
13
|
-
constructor(apiKey, location,
|
|
27
|
+
constructor(apiKey, location, log) {
|
|
14
28
|
this.apiKey = apiKey;
|
|
15
29
|
this.location = location;
|
|
16
|
-
this.thresholds = thresholds;
|
|
17
30
|
this.log = log;
|
|
18
31
|
}
|
|
19
32
|
async fetchPollenData() {
|
|
@@ -55,25 +68,25 @@ class PollenService {
|
|
|
55
68
|
const parsed = {
|
|
56
69
|
overall: {
|
|
57
70
|
count: totalCount,
|
|
58
|
-
level:
|
|
71
|
+
level: classifyLevel(totalCount, THRESHOLDS.overall),
|
|
59
72
|
},
|
|
60
73
|
tree: {
|
|
61
74
|
count: treeCount,
|
|
62
|
-
level:
|
|
75
|
+
level: classifyLevel(treeCount, THRESHOLDS.tree),
|
|
63
76
|
},
|
|
64
77
|
grass: {
|
|
65
78
|
count: grassCount,
|
|
66
|
-
level:
|
|
79
|
+
level: classifyLevel(grassCount, THRESHOLDS.grass),
|
|
67
80
|
},
|
|
68
81
|
weed: {
|
|
69
82
|
count: weedCount,
|
|
70
|
-
level:
|
|
83
|
+
level: classifyLevel(weedCount, THRESHOLDS.weed),
|
|
71
84
|
},
|
|
72
85
|
timestamp: item.updatedAt,
|
|
73
86
|
};
|
|
74
87
|
this.cache = parsed;
|
|
75
88
|
this.consecutiveFailures = 0;
|
|
76
|
-
this.log.
|
|
89
|
+
this.log.info(`Pollen data updated: overall=${totalCount} (${parsed.overall.level}), `
|
|
77
90
|
+ `tree=${treeCount} (${parsed.tree.level}), `
|
|
78
91
|
+ `grass=${grassCount} (${parsed.grass.level}), `
|
|
79
92
|
+ `weed=${weedCount} (${parsed.weed.level})`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pollenService.js","sourceRoot":"","sources":["../src/pollenService.ts"],"names":[],"mappings":";;;AACA,+CAA+C;AAG/C,
|
|
1
|
+
{"version":3,"file":"pollenService.js","sourceRoot":"","sources":["../src/pollenService.ts"],"names":[],"mappings":"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"}
|
package/dist/settings.d.ts
CHANGED
|
@@ -3,14 +3,3 @@ export declare const PLUGIN_NAME = "homebridge-pollen";
|
|
|
3
3
|
export declare const AMBEE_BASE_URL = "https://api.ambeedata.com";
|
|
4
4
|
export declare const DEFAULT_POLL_INTERVAL = 60;
|
|
5
5
|
export declare const MIN_POLL_INTERVAL = 15;
|
|
6
|
-
export interface ThresholdRange {
|
|
7
|
-
low: number;
|
|
8
|
-
high: number;
|
|
9
|
-
}
|
|
10
|
-
export interface Thresholds {
|
|
11
|
-
overall: ThresholdRange;
|
|
12
|
-
tree: ThresholdRange;
|
|
13
|
-
grass: ThresholdRange;
|
|
14
|
-
weed: ThresholdRange;
|
|
15
|
-
}
|
|
16
|
-
export declare const DEFAULT_THRESHOLDS: Thresholds;
|
package/dist/settings.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.MIN_POLL_INTERVAL = exports.DEFAULT_POLL_INTERVAL = exports.AMBEE_BASE_URL = exports.PLUGIN_NAME = exports.PLATFORM_NAME = void 0;
|
|
4
4
|
exports.PLATFORM_NAME = 'HomebridgePollen';
|
|
5
5
|
exports.PLUGIN_NAME = 'homebridge-pollen';
|
|
6
6
|
exports.AMBEE_BASE_URL = 'https://api.ambeedata.com';
|
|
7
7
|
exports.DEFAULT_POLL_INTERVAL = 60; // minutes
|
|
8
8
|
exports.MIN_POLL_INTERVAL = 15; // minutes
|
|
9
|
-
exports.DEFAULT_THRESHOLDS = {
|
|
10
|
-
overall: { low: 50, high: 200 },
|
|
11
|
-
tree: { low: 15, high: 90 },
|
|
12
|
-
grass: { low: 20, high: 200 },
|
|
13
|
-
weed: { low: 10, high: 50 },
|
|
14
|
-
};
|
|
15
9
|
//# sourceMappingURL=settings.js.map
|
package/dist/settings.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":";;;AAAa,QAAA,aAAa,GAAG,kBAAkB,CAAC;AACnC,QAAA,WAAW,GAAG,mBAAmB,CAAC;AAElC,QAAA,cAAc,GAAG,2BAA2B,CAAC;AAE7C,QAAA,qBAAqB,GAAG,EAAE,CAAC,CAAC,UAAU;AACtC,QAAA,iBAAiB,GAAG,EAAE,CAAC,CAAC,UAAU
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":";;;AAAa,QAAA,aAAa,GAAG,kBAAkB,CAAC;AACnC,QAAA,WAAW,GAAG,mBAAmB,CAAC;AAElC,QAAA,cAAc,GAAG,2BAA2B,CAAC;AAE7C,QAAA,qBAAqB,GAAG,EAAE,CAAC,CAAC,UAAU;AACtC,QAAA,iBAAiB,GAAG,EAAE,CAAC,CAAC,UAAU"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,29 +1,9 @@
|
|
|
1
1
|
import type { PlatformConfig } from 'homebridge';
|
|
2
|
-
import type { Thresholds } from './settings.js';
|
|
3
2
|
export interface PollenConfig extends PlatformConfig {
|
|
4
3
|
apiKey: string;
|
|
5
4
|
location: string;
|
|
6
5
|
pollInterval?: number;
|
|
7
6
|
enableCategorySensors?: boolean;
|
|
8
|
-
enableAirQualitySensor?: boolean;
|
|
9
|
-
thresholds?: Partial<{
|
|
10
|
-
overall: Partial<{
|
|
11
|
-
low: number;
|
|
12
|
-
high: number;
|
|
13
|
-
}>;
|
|
14
|
-
tree: Partial<{
|
|
15
|
-
low: number;
|
|
16
|
-
high: number;
|
|
17
|
-
}>;
|
|
18
|
-
grass: Partial<{
|
|
19
|
-
low: number;
|
|
20
|
-
high: number;
|
|
21
|
-
}>;
|
|
22
|
-
weed: Partial<{
|
|
23
|
-
low: number;
|
|
24
|
-
high: number;
|
|
25
|
-
}>;
|
|
26
|
-
}>;
|
|
27
7
|
}
|
|
28
8
|
export interface AmbeePollenCount {
|
|
29
9
|
grass_pollen: number;
|
|
@@ -63,16 +43,8 @@ export interface ParsedPollenData {
|
|
|
63
43
|
weed: CategoryData;
|
|
64
44
|
timestamp: string;
|
|
65
45
|
}
|
|
66
|
-
export type AccessoryType = 'contact' | 'airquality';
|
|
67
46
|
export interface AccessoryDefinition {
|
|
68
47
|
id: string;
|
|
69
48
|
name: string;
|
|
70
|
-
|
|
71
|
-
level?: PollenLevel;
|
|
72
|
-
category?: PollenCategory;
|
|
49
|
+
category: PollenCategory;
|
|
73
50
|
}
|
|
74
|
-
export declare function classifyLevel(count: number, thresholds: {
|
|
75
|
-
low: number;
|
|
76
|
-
high: number;
|
|
77
|
-
}): PollenLevel;
|
|
78
|
-
export declare function resolveThresholds(config: PollenConfig, defaults: Thresholds): Thresholds;
|
package/dist/types.js
CHANGED
|
@@ -1,26 +1,3 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.classifyLevel = classifyLevel;
|
|
4
|
-
exports.resolveThresholds = resolveThresholds;
|
|
5
|
-
function classifyLevel(count, thresholds) {
|
|
6
|
-
if (count >= thresholds.high) {
|
|
7
|
-
return 'High';
|
|
8
|
-
}
|
|
9
|
-
if (count <= thresholds.low) {
|
|
10
|
-
return 'Low';
|
|
11
|
-
}
|
|
12
|
-
return 'Medium';
|
|
13
|
-
}
|
|
14
|
-
function resolveThresholds(config, defaults) {
|
|
15
|
-
const overrides = config.thresholds;
|
|
16
|
-
if (!overrides) {
|
|
17
|
-
return defaults;
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
overall: { ...defaults.overall, ...overrides.overall },
|
|
21
|
-
tree: { ...defaults.tree, ...overrides.tree },
|
|
22
|
-
grass: { ...defaults.grass, ...overrides.grass },
|
|
23
|
-
weed: { ...defaults.weed, ...overrides.weed },
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
3
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|