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.
- package/Readme.md +62 -0
- package/index.js +175 -0
- 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
|
+
}
|