homebridge-weatherlink-cloud 0.1.5 → 0.2.1
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.
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<!-- Custom settings UI for homebridge-weatherlink-cloud.
|
|
2
|
+
No <html>/<head>/<body> tags — the Homebridge UI injects those, and
|
|
3
|
+
injects Bootstrap 5 CSS, so the btn/alert classes below are styled. -->
|
|
4
|
+
|
|
5
|
+
<div class="card card-body mb-3">
|
|
6
|
+
<p class="mb-2">
|
|
7
|
+
Enter your API Key and Secret below, then look up your Station ID
|
|
8
|
+
automatically. Leaving Station ID blank also works — the plugin discovers
|
|
9
|
+
it at startup — but this lets you see and confirm the value.
|
|
10
|
+
</p>
|
|
11
|
+
<button type="button" id="fetchBtn" class="btn btn-primary">
|
|
12
|
+
Fetch Station ID from account
|
|
13
|
+
</button>
|
|
14
|
+
<div id="result" class="mt-3"></div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<script>
|
|
18
|
+
(async () => {
|
|
19
|
+
// Render the normal config.schema.json form beneath our custom card.
|
|
20
|
+
homebridge.showSchemaForm();
|
|
21
|
+
|
|
22
|
+
const fetchBtn = document.getElementById('fetchBtn');
|
|
23
|
+
const result = document.getElementById('result');
|
|
24
|
+
|
|
25
|
+
// Read the single platform config block (or an empty object if none yet).
|
|
26
|
+
async function getConfig() {
|
|
27
|
+
const blocks = await homebridge.getPluginConfig();
|
|
28
|
+
return blocks[0] || {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Write a station ID back into the config and persist it. updatePluginConfig
|
|
32
|
+
// refreshes the visible form field; savePluginConfig writes config.json.
|
|
33
|
+
async function setStationId(station) {
|
|
34
|
+
const config = await getConfig();
|
|
35
|
+
config.stationId = station.id;
|
|
36
|
+
await homebridge.updatePluginConfig([config]);
|
|
37
|
+
await homebridge.savePluginConfig();
|
|
38
|
+
result.innerHTML =
|
|
39
|
+
`<div class="alert alert-success mb-0">` +
|
|
40
|
+
`Station ID set to <strong>${station.id}</strong> ` +
|
|
41
|
+
`(“${station.name}”) and saved.</div>`;
|
|
42
|
+
homebridge.toast.success(`Station ID ${station.id} written to config.`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function renderPicker(stations) {
|
|
46
|
+
result.innerHTML =
|
|
47
|
+
'<div class="mb-2">Multiple stations found — choose one:</div>';
|
|
48
|
+
stations.forEach((s) => {
|
|
49
|
+
const b = document.createElement('button');
|
|
50
|
+
b.type = 'button';
|
|
51
|
+
b.className = 'btn btn-outline-secondary btn-sm d-block mb-1';
|
|
52
|
+
b.textContent = `${s.name} (ID ${s.id})`;
|
|
53
|
+
b.addEventListener('click', () => setStationId(s));
|
|
54
|
+
result.appendChild(b);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fetchBtn.addEventListener('click', async () => {
|
|
59
|
+
result.innerHTML = '';
|
|
60
|
+
homebridge.showSpinner();
|
|
61
|
+
try {
|
|
62
|
+
const { apiKey, apiSecret } = await getConfig();
|
|
63
|
+
const stations = await homebridge.request('/stations', { apiKey, apiSecret });
|
|
64
|
+
|
|
65
|
+
if (!stations.length) {
|
|
66
|
+
result.innerHTML =
|
|
67
|
+
'<div class="alert alert-warning mb-0">No stations found on this account.</div>';
|
|
68
|
+
} else if (stations.length === 1) {
|
|
69
|
+
await setStationId(stations[0]);
|
|
70
|
+
} else {
|
|
71
|
+
renderPicker(stations);
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// Errors thrown in server.js surface here with their message.
|
|
75
|
+
homebridge.toast.error(e.message || 'Lookup failed', 'WeatherLink');
|
|
76
|
+
} finally {
|
|
77
|
+
homebridge.hideSpinner();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
})();
|
|
81
|
+
</script>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { HomebridgePluginUiServer } = require('@homebridge/plugin-ui-utils');
|
|
4
|
+
const https = require('https');
|
|
5
|
+
|
|
6
|
+
// This script is spawned as a child process when the plugin's settings modal
|
|
7
|
+
// is opened, and terminated when it closes. It exposes request handlers that
|
|
8
|
+
// the front-end (public/index.html) calls via homebridge.request(...).
|
|
9
|
+
class PluginUiServer extends HomebridgePluginUiServer {
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
|
|
13
|
+
// Front-end calls homebridge.request('/stations', { apiKey, apiSecret }).
|
|
14
|
+
this.onRequest('/stations', async (payload) => {
|
|
15
|
+
const { apiKey, apiSecret } = payload || {};
|
|
16
|
+
if (!apiKey || !apiSecret) {
|
|
17
|
+
throw new Error('Enter your API Key and API Secret first.');
|
|
18
|
+
}
|
|
19
|
+
const data = await this.fetchStations(apiKey, apiSecret);
|
|
20
|
+
const stations = (data && data.stations) || [];
|
|
21
|
+
// Return a trimmed shape the UI can render directly.
|
|
22
|
+
return stations.map((s) => ({
|
|
23
|
+
id: s.station_id,
|
|
24
|
+
name: s.station_name || `Station ${s.station_id}`,
|
|
25
|
+
}));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Signal to the UI that the server is ready to receive requests.
|
|
29
|
+
this.ready();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fetchStations(apiKey, apiSecret) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const options = {
|
|
35
|
+
hostname: 'api.weatherlink.com',
|
|
36
|
+
path: `/v2/stations?api-key=${encodeURIComponent(apiKey)}`,
|
|
37
|
+
method: 'GET',
|
|
38
|
+
headers: { 'X-Api-Secret': apiSecret },
|
|
39
|
+
};
|
|
40
|
+
const req = https.request(options, (res) => {
|
|
41
|
+
let body = '';
|
|
42
|
+
res.on('data', (c) => (body += c));
|
|
43
|
+
res.on('end', () => {
|
|
44
|
+
let json;
|
|
45
|
+
try {
|
|
46
|
+
json = JSON.parse(body);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return reject(new Error(`Unexpected response (HTTP ${res.statusCode}).`));
|
|
49
|
+
}
|
|
50
|
+
if (res.statusCode !== 200) {
|
|
51
|
+
// The v2 API returns { message: "..." } on errors (e.g. bad key).
|
|
52
|
+
return reject(new Error(json.message || `Request failed (HTTP ${res.statusCode}).`));
|
|
53
|
+
}
|
|
54
|
+
resolve(json);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
req.on('error', reject);
|
|
58
|
+
req.end();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Instantiate. (IIFE keeps the linter happy about an "unused" instance.)
|
|
64
|
+
(() => new PluginUiServer())();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-weatherlink-cloud",
|
|
3
3
|
"displayName": "WeatherLink Cloud",
|
|
4
|
-
"version": "0.1
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"description": "Exposes Davis WeatherLink (v2 cloud API) weather data to HomeKit via Homebridge.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"license": "MIT",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
],
|
|
27
27
|
"files": [
|
|
28
28
|
"index.js",
|
|
29
|
-
"config.schema.json"
|
|
30
|
-
|
|
29
|
+
"config.schema.json",
|
|
30
|
+
"homebridge-ui"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@homebridge/plugin-ui-utils": "^2.0.0"
|
|
34
|
+
}
|
|
31
35
|
}
|