homebridge-weatherlink-cloud 0.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.
Files changed (3) hide show
  1. package/Readme.md +62 -0
  2. package/index.js +175 -0
  3. package/package.json +31 -0
package/Readme.md ADDED
@@ -0,0 +1,62 @@
1
+ # homebridge-weatherlink-cloud
2
+
3
+ A custom [Homebridge](https://homebridge.io) dynamic platform plugin that polls
4
+ the **Davis WeatherLink v2 cloud API** and exposes your weather station readings
5
+ to Apple HomeKit.
6
+
7
+ Built for setups where the data lives in the WeatherLink cloud (e.g. a 6313
8
+ WeatherLink Console, which has no local API). Any station that uploads to
9
+ weatherlink.com is reachable this way.
10
+
11
+ ## What it exposes
12
+
13
+ | Reading | HomeKit service | Status |
14
+ |--------------|---------------------|---------------|
15
+ | Temperature | `TemperatureSensor` | native ✅ |
16
+ | Humidity | `HumiditySensor` | native ✅ |
17
+ | Wind / rain / pressure / UV | none | needs Eve custom characteristics or a LightSensor hack (not yet implemented) |
18
+
19
+ ## Heads-up on data freshness
20
+
21
+ The v2 `/current` endpoint returns roughly the last archive record, so values can
22
+ lag by up to your upload interval. Update rate is tied to your weatherlink.com
23
+ tier (~15 min free, ~5 min Pro). Set `pollMinutes` to match — there is no benefit
24
+ to polling faster than the data refreshes.
25
+
26
+ ## Configuration
27
+
28
+ Get your **API Key** and **API Secret** from the lower-left of your account page
29
+ at <https://www.weatherlink.com/account>. Find your numeric **Station ID** with:
30
+
31
+ ```bash
32
+ curl -H "X-Api-Secret: YOUR_SECRET" \
33
+ "https://api.weatherlink.com/v2/stations?api-key=YOUR_KEY"
34
+ ```
35
+
36
+ Then either use the Settings form in the Homebridge UI, or add a platform block
37
+ to `config.json`:
38
+
39
+ ```json
40
+ {
41
+ "platforms": [
42
+ {
43
+ "platform": "WeatherLinkCloud",
44
+ "name": "WeatherLink",
45
+ "apiKey": "YOUR_V2_API_KEY",
46
+ "apiSecret": "YOUR_V2_API_SECRET",
47
+ "stationId": 123456,
48
+ "pollMinutes": 15
49
+ }
50
+ ]
51
+ }
52
+ ```
53
+
54
+ ## First run
55
+
56
+ Uncomment the payload-dump line in `applyData()` once, restart, and read the
57
+ logged JSON to confirm the exact field names for your sensors before mapping
58
+ anything beyond temp/humidity.
59
+
60
+ ## License
61
+
62
+ MIT
package/index.js ADDED
@@ -0,0 +1,175 @@
1
+ // homebridge-weatherlink-cloud — starter skeleton
2
+ //
3
+ // A Homebridge dynamic platform plugin that polls the WeatherLink v2 cloud
4
+ // API and exposes your Vantage Vue readings to HomeKit.
5
+ //
6
+ // Pairs with a minimal package.json (see notes at bottom of this file).
7
+ //
8
+ // config.json example:
9
+ // {
10
+ // "platforms": [
11
+ // {
12
+ // "platform": "WeatherLinkCloud",
13
+ // "name": "WeatherLink",
14
+ // "apiKey": "YOUR_V2_API_KEY",
15
+ // "apiSecret": "YOUR_V2_API_SECRET",
16
+ // "stationId": 123456,
17
+ // "pollMinutes": 15
18
+ // }
19
+ // ]
20
+ // }
21
+
22
+ 'use strict';
23
+
24
+ const https = require('https');
25
+
26
+ const PLUGIN_NAME = 'homebridge-weatherlink-cloud';
27
+ const PLATFORM_NAME = 'WeatherLinkCloud';
28
+
29
+ let Service, Characteristic;
30
+
31
+ module.exports = (api) => {
32
+ Service = api.hap.Service;
33
+ Characteristic = api.hap.Characteristic;
34
+ api.registerPlatform(PLATFORM_NAME, WeatherLinkCloudPlatform);
35
+ };
36
+
37
+ class WeatherLinkCloudPlatform {
38
+ constructor(log, config, api) {
39
+ this.log = log;
40
+ this.api = api;
41
+
42
+ this.apiKey = config.apiKey;
43
+ this.apiSecret = config.apiSecret;
44
+ this.stationId = config.stationId;
45
+ // Match this to your weatherlink.com tier: ~15 (free) or ~5 (Pro).
46
+ // Polling faster than the data refreshes just wastes API calls.
47
+ this.pollSeconds = (config.pollMinutes || 15) * 60;
48
+
49
+ this.accessories = new Map(); // UUID -> cached accessory
50
+
51
+ this.api.on('didFinishLaunching', () => {
52
+ this.setupAccessories();
53
+ this.poll();
54
+ setInterval(() => this.poll(), this.pollSeconds * 1000);
55
+ });
56
+ }
57
+
58
+ // Homebridge calls this once per cached accessory at startup.
59
+ configureAccessory(accessory) {
60
+ this.accessories.set(accessory.UUID, accessory);
61
+ }
62
+
63
+ getOrCreate(idSuffix, name, serviceType) {
64
+ const uuid = this.api.hap.uuid.generate(
65
+ `${PLATFORM_NAME}:${this.stationId}:${idSuffix}`
66
+ );
67
+ let accessory = this.accessories.get(uuid);
68
+ if (!accessory) {
69
+ accessory = new this.api.platformAccessory(name, uuid);
70
+ accessory.addService(serviceType, name);
71
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
72
+ this.accessories.set(uuid, accessory);
73
+ this.log(`Created accessory: ${name}`);
74
+ }
75
+ return accessory;
76
+ }
77
+
78
+ setupAccessories() {
79
+ // Native HomeKit service types — these display cleanly in Apple's Home app.
80
+ this.tempAcc = this.getOrCreate('outTemp', 'Outdoor Temperature', Service.TemperatureSensor);
81
+ this.humAcc = this.getOrCreate('outHum', 'Outdoor Humidity', Service.HumiditySensor);
82
+
83
+ // Wind / rain / pressure / UV have NO native HomeKit service.
84
+ // Options to surface them:
85
+ // - Eve custom characteristics (shown in the Eve app, ignored by Home app)
86
+ // - A LightSensor service repurposed to carry a raw number (a hack)
87
+ // Add those here once you've decided which approach you want.
88
+ }
89
+
90
+ poll() {
91
+ this.fetchCurrent()
92
+ .then((data) => this.applyData(data))
93
+ .catch((err) => this.log.error('Poll failed:', err.message));
94
+ }
95
+
96
+ fetchCurrent() {
97
+ // v2 API: api-key as a query param, secret in the X-Api-Secret header.
98
+ // (v2 also supports an older HMAC api-signature scheme — the header
99
+ // method below is the simpler one. Confirm against the official docs:
100
+ // https://weatherlink.github.io/v2-api/ )
101
+ return new Promise((resolve, reject) => {
102
+ const options = {
103
+ hostname: 'api.weatherlink.com',
104
+ path: `/v2/current/${this.stationId}?api-key=${this.apiKey}`,
105
+ method: 'GET',
106
+ headers: { 'X-Api-Secret': this.apiSecret },
107
+ };
108
+ const req = https.request(options, (res) => {
109
+ let body = '';
110
+ res.on('data', (c) => (body += c));
111
+ res.on('end', () => {
112
+ try {
113
+ resolve(JSON.parse(body));
114
+ } catch (e) {
115
+ reject(new Error(`Bad JSON (HTTP ${res.statusCode}): ${body.slice(0, 200)}`));
116
+ }
117
+ });
118
+ });
119
+ req.on('error', reject);
120
+ req.end();
121
+ });
122
+ }
123
+
124
+ applyData(data) {
125
+ // The /current payload nests readings under sensors[].data[]. Each sensor
126
+ // block has a sensor_type and data_structure_type; you need to locate the
127
+ // ISS block for your Vantage Vue. The reliable way to learn the exact shape:
128
+ // log the whole payload ONCE, eyeball it, then pull the fields you need.
129
+ //
130
+ // this.log(JSON.stringify(data, null, 2)); // <- run this once, then delete
131
+ //
132
+ // Field names below (temp, hum) are typical for a Vue ISS but VERIFY them
133
+ // against your own dump — they live in °F and %.
134
+
135
+ const sensors = (data && data.sensors) || [];
136
+ let tempF, hum;
137
+
138
+ for (const s of sensors) {
139
+ const d = (s.data && s.data[0]) || {};
140
+ if (typeof d.temp === 'number') tempF = d.temp;
141
+ if (typeof d.hum === 'number') hum = d.hum;
142
+ // Other fields you'll likely find here once you dump the payload:
143
+ // d.wind_speed_last, d.wind_dir_last, d.rainfall_daily,
144
+ // d.bar_sea_level, d.uv_index, d.solar_rad ...
145
+ }
146
+
147
+ if (typeof tempF === 'number') {
148
+ const tempC = ((tempF - 32) * 5) / 9;
149
+ this.tempAcc
150
+ .getService(Service.TemperatureSensor)
151
+ .updateCharacteristic(Characteristic.CurrentTemperature, tempC);
152
+ }
153
+ if (typeof hum === 'number') {
154
+ this.humAcc
155
+ .getService(Service.HumiditySensor)
156
+ .updateCharacteristic(Characteristic.CurrentRelativeHumidity, hum);
157
+ }
158
+ }
159
+ }
160
+
161
+ // --- Minimal package.json to sit beside this file -------------------------
162
+ // {
163
+ // "name": "homebridge-weatherlink-cloud",
164
+ // "version": "0.1.0",
165
+ // "main": "index.js",
166
+ // "engines": { "homebridge": ">=1.6.0", "node": ">=18" },
167
+ // "keywords": ["homebridge-plugin"]
168
+ // }
169
+ //
170
+ // Dev loop:
171
+ // 1. mkdir homebridge-weatherlink-cloud && cd it; add index.js + package.json
172
+ // 2. npm install -g . (or `npm link`) into your Homebridge install
173
+ // 3. add the platform block to Homebridge config.json
174
+ // 4. restart Homebridge, watch the logs, dump the payload, map fields
175
+ // --------------------------------------------------------------------------
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "homebridge-weatherlink-cloud",
3
+ "displayName": "WeatherLink Cloud",
4
+ "version": "0.1.0",
5
+ "description": "Exposes Davis WeatherLink (v2 cloud API) weather data to HomeKit via Homebridge.",
6
+ "main": "index.js",
7
+ "license": "MIT",
8
+ "author": "YOUR NAME",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/mediatecture/homebridge-weatherlink-cloud.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/mediatecture/homebridge-weatherlink-cloud/issues"
15
+ },
16
+ "engines": {
17
+ "homebridge": "^1.6.0 || ^2.0.0",
18
+ "node": "^18 || ^20 || ^22"
19
+ },
20
+ "keywords": [
21
+ "homebridge-plugin",
22
+ "weatherlink",
23
+ "davis",
24
+ "vantage-vue",
25
+ "weather"
26
+ ],
27
+ "files": [
28
+ "index.js",
29
+ "config.schema.json"
30
+ ]
31
+ }