homebridge-shelly-blu-trv 1.0.6 → 1.0.7
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 +26 -10
- package/dist/accessory.js +1 -1
- package/dist/shellyApi.js +60 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,22 +99,38 @@ To ensure coverage is sufficient:
|
|
|
99
99
|
|
|
100
100
|
## Publishing
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
- The repository contains two publishing workflows:
|
|
103
|
+
- **Publish** (`.github/workflows/publish.yml`) — runs when a GitHub Release is *created* and performs an `npm publish` step.
|
|
104
|
+
- **Semantic Release** (`.github/workflows/semantic-release.yml`) — when enabled, automatically creates releases, changelogs and publishes to npm on `main` via `semantic-release`.
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
**NOTE:** The automatic semantic-release runs are currently **paused** (workflow trigger switched to `workflow_dispatch`). To run it manually: go to the Actions tab → **Semantic Release** → **Run workflow** (choose `main`).
|
|
107
|
+
|
|
108
|
+
Manual publish steps
|
|
109
|
+
|
|
110
|
+
1. Ensure you're logged in to npm: `npm login`.
|
|
106
111
|
2. Build the package: `npm run build`.
|
|
107
|
-
3. Bump the version
|
|
108
|
-
4.
|
|
112
|
+
3. Bump the package version (e.g. `npm version patch`) and push the tag.
|
|
113
|
+
4. Create a GitHub Release (or run `npm publish --access public` manually).
|
|
114
|
+
|
|
115
|
+
Developer notes about `NPM_TOKEN` and automation
|
|
109
116
|
|
|
110
|
-
|
|
117
|
+
- For CI-based publishing you must set the repository secret `NPM_TOKEN` (Settings → Secrets → Actions) to a valid **npm automation token** with publish rights.
|
|
118
|
+
- Create it on npm: Settings → Access Tokens → **Create New Token** → choose **Automation** and ensure it has publish rights.
|
|
119
|
+
- If your npm account uses Two-Factor Authentication (2FA), pick a token that is usable for CI (set 2FA to **Authorization only** for automation tokens). See https://docs.npmjs.com/getting-started/working_with_tokens for details.
|
|
120
|
+
- If semantic-release reports `EINVALIDNPMTOKEN` or `401 Unauthorized`, try re-creating the token and updating `NPM_TOKEN` in repo secrets.
|
|
121
|
+
- The repository also includes a Publish workflow (triggered on Release creation) which can publish even if semantic-release is paused — this is useful for one-off releases.
|
|
111
122
|
|
|
112
|
-
|
|
123
|
+
E2E and local verification (developer)
|
|
113
124
|
|
|
114
|
-
- `
|
|
115
|
-
-
|
|
125
|
+
- Run the E2E runner locally: `npm run e2e` — this will install deps, build the project, install a local shim, start the fake gateway and a headless Homebridge instance and look for discovery/polling logs.
|
|
126
|
+
- Run unit tests + coverage: `npm test` (creates `coverage/lcov.info` and an `lcov-report` directory).
|
|
127
|
+
- Check coverage threshold: `npm run check-coverage` (defaults to 80% lines; use `COVERAGE_THRESHOLD` to override).
|
|
128
|
+
|
|
129
|
+
If you'd like, I can add a small diagnostic Action that runs `npm whoami` using `NPM_TOKEN` (safely) so you can quickly confirm which npm account the token maps to before re-enabling semantic-release.
|
|
130
|
+
|
|
131
|
+
---
|
|
116
132
|
|
|
117
|
-
If you
|
|
133
|
+
If you want this documentation expanded into a `DEVELOPING.md` file or want me to add the diagnostic Action, tell me which and I'll add it in a PR.
|
|
118
134
|
|
|
119
135
|
## License
|
|
120
136
|
|
package/dist/accessory.js
CHANGED
|
@@ -52,7 +52,7 @@ class ShellyTrvAccessory {
|
|
|
52
52
|
this.log.warn(`[${this.accessory.displayName}] Device offline, unable to retrieve ${key}`);
|
|
53
53
|
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
|
|
54
54
|
}
|
|
55
|
-
// accessing
|
|
55
|
+
// accessing typed state keys
|
|
56
56
|
const value = s[key];
|
|
57
57
|
this.log.debug(`[${this.accessory.displayName}] Retrieved ${key}: ${value}`);
|
|
58
58
|
return value;
|
package/dist/shellyApi.js
CHANGED
|
@@ -21,8 +21,16 @@ class ShellyApi {
|
|
|
21
21
|
signal: AbortSignal.timeout(this.requestTimeout)
|
|
22
22
|
});
|
|
23
23
|
if (!res.ok) {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
// Try to extract body text for better diagnostics
|
|
25
|
+
let bodyText = '';
|
|
26
|
+
try {
|
|
27
|
+
bodyText = await res.text();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
bodyText = '<no body>';
|
|
31
|
+
}
|
|
32
|
+
this.log.error(`[ShellyApi] HTTP ${res.status} from gateway ${this.gw.host}${path} - ${bodyText}`);
|
|
33
|
+
throw new Error(`HTTP ${res.status}: ${bodyText}`);
|
|
26
34
|
}
|
|
27
35
|
const data = await res.json();
|
|
28
36
|
this.log.debug(`[ShellyApi] Successfully fetched from ${this.gw.host}${path}`);
|
|
@@ -30,11 +38,58 @@ class ShellyApi {
|
|
|
30
38
|
}
|
|
31
39
|
catch (error) {
|
|
32
40
|
if (error instanceof Error) {
|
|
33
|
-
this.log.error(`[ShellyApi] Request failed to ${this.gw.host}: ${error.message}`);
|
|
41
|
+
this.log.error(`[ShellyApi] Request failed to ${this.gw.host}${path}: ${error.message}`);
|
|
34
42
|
}
|
|
35
43
|
throw error;
|
|
36
44
|
}
|
|
37
45
|
}
|
|
46
|
+
async rpcCall(id, method, params) {
|
|
47
|
+
// Try several RPC variants for wider compatibility with firmware differences
|
|
48
|
+
const paramsStr = params ? `¶ms=${encodeURIComponent(JSON.stringify(params))}` : '';
|
|
49
|
+
const candidates = [
|
|
50
|
+
`/rpc/BluTrv.call?id=${id}&method=${method}${paramsStr}`,
|
|
51
|
+
`/rpc/BluTrv.call&id=${id}&method=${method}${paramsStr}`
|
|
52
|
+
];
|
|
53
|
+
for (const path of candidates) {
|
|
54
|
+
try {
|
|
55
|
+
return await this.get(path);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
// If 404, try next candidate; otherwise propagate
|
|
59
|
+
if (err instanceof Error && err.message.includes('HTTP 404')) {
|
|
60
|
+
this.log.debug(`[ShellyApi] RPC variant failed (404), trying next: ${path}`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Fallback: try POST to /rpc/BluTrv.call with JSON body
|
|
67
|
+
try {
|
|
68
|
+
const url = buildUrl(this.gw.host, '/rpc/BluTrv.call', this.gw.token);
|
|
69
|
+
const res = await fetch(url, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
body: JSON.stringify({ id, method, params }),
|
|
72
|
+
headers: { 'Content-Type': 'application/json' },
|
|
73
|
+
signal: AbortSignal.timeout(this.requestTimeout)
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
let bodyText = '';
|
|
77
|
+
try {
|
|
78
|
+
bodyText = await res.text();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
bodyText = '<no body>';
|
|
82
|
+
}
|
|
83
|
+
this.log.error(`[ShellyApi] HTTP ${res.status} from gateway ${this.gw.host} (POST /rpc/BluTrv.call): ${bodyText}`);
|
|
84
|
+
throw new Error(`HTTP ${res.status}: ${bodyText}`);
|
|
85
|
+
}
|
|
86
|
+
return res.json();
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
this.log.error(`[ShellyApi] RPC call failed for TRV ${id} method ${method}: ${err instanceof Error ? err.message : String(err)}`);
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
38
93
|
async discoverTrvs() {
|
|
39
94
|
this.log.debug(`[ShellyApi] Discovering TRVs from gateway ${this.gw.host}`);
|
|
40
95
|
try {
|
|
@@ -57,7 +112,7 @@ class ShellyApi {
|
|
|
57
112
|
async getTrvState(id) {
|
|
58
113
|
this.log.debug(`[ShellyApi] Fetching state for TRV ${id}`);
|
|
59
114
|
try {
|
|
60
|
-
const rpc = await this.
|
|
115
|
+
const rpc = await this.rpcCall(id, 'TRV.GetStatus');
|
|
61
116
|
const status = await this.get("/status");
|
|
62
117
|
const dev = status.ble?.devices?.find((d) => d.id === id);
|
|
63
118
|
const state = {
|
|
@@ -78,8 +133,7 @@ class ShellyApi {
|
|
|
78
133
|
async setTargetTemp(id, value) {
|
|
79
134
|
this.log.debug(`[ShellyApi] Setting target temperature for TRV ${id} to ${value}°C`);
|
|
80
135
|
try {
|
|
81
|
-
await this.
|
|
82
|
-
encodeURIComponent(JSON.stringify({ target_C: value })));
|
|
136
|
+
await this.rpcCall(id, 'TRV.SetTarget', { target_C: value });
|
|
83
137
|
this.log.debug(`[ShellyApi] Successfully set target temperature for TRV ${id}`);
|
|
84
138
|
}
|
|
85
139
|
catch (error) {
|
package/package.json
CHANGED