homebridge-navilink 0.1.2 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.3](https://github.com/tbaur/homebridge-navilink/compare/v0.1.2...v0.1.3) (2026-09-20)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * use a diagnostics slider and name the mqtt channel ([#8](https://github.com/tbaur/homebridge-navilink/issues/8)) ([324d700](https://github.com/tbaur/homebridge-navilink/commit/324d7006dda002ad0469599012c67222558aae53))
9
+
3
10
  ## [0.1.2](https://github.com/tbaur/homebridge-navilink/compare/v0.1.1...v0.1.2) (2026-09-20)
4
11
 
5
12
 
@@ -60,6 +60,9 @@
60
60
  "default": 0,
61
61
  "minimum": 0,
62
62
  "maximum": 3600,
63
+ "x-schema-form": {
64
+ "type": "number"
65
+ },
63
66
  "description": "Emit a plugin health line to the Homebridge log every N seconds. 0 is off (the default). Allowed range 0–3600; values between 1 and 29 are clamped up to 30. Logs only; nothing is exposed in HomeKit."
64
67
  },
65
68
  "structuredLogs": {
@@ -15,7 +15,7 @@ export declare function diagnosticLabel(msg: string): string;
15
15
  export declare function formatReasons(reasons: string[]): string;
16
16
  /**
17
17
  * Concise summary matching the sibling plugins:
18
- * `Health: healthy | devices 1/1 | mqtt live | api p50 12ms p95 40ms (req 3, err 0)`.
18
+ * `Health: healthy | devices 1/1 | Publish-subscribe (mqtt) live | api p50 12ms p95 40ms (req 3, err 0)`.
19
19
  */
20
20
  export declare function formatDiagnosticLine(report: DiagnosticsSnapshot): string;
21
21
  /** State-only line for a healthy/degraded flip. The heartbeat already has the body. */
@@ -13,6 +13,7 @@ exports.diagnosticLabel = diagnosticLabel;
13
13
  exports.formatReasons = formatReasons;
14
14
  exports.formatDiagnosticLine = formatDiagnosticLine;
15
15
  exports.formatHealthTransitionLine = formatHealthTransitionLine;
16
+ const labels_1 = require("../utils/labels");
16
17
  /** Short operator-facing label for the MQTT session lifecycle. */
17
18
  function formatMqttTransportState(state) {
18
19
  switch (state) {
@@ -52,14 +53,14 @@ function formatReasons(reasons) {
52
53
  }
53
54
  /**
54
55
  * Concise summary matching the sibling plugins:
55
- * `Health: healthy | devices 1/1 | mqtt live | api p50 12ms p95 40ms (req 3, err 0)`.
56
+ * `Health: healthy | devices 1/1 | Publish-subscribe (mqtt) live | api p50 12ms p95 40ms (req 3, err 0)`.
56
57
  */
57
58
  function formatDiagnosticLine(report) {
58
59
  const { lifecycle, devices, transport, api } = report;
59
60
  return [
60
61
  `${diagnosticLabel(report.msg)}: ${lifecycle.health}${formatReasons(lifecycle.reasons)}`,
61
62
  `devices ${devices.online}/${devices.total}`,
62
- `mqtt ${formatMqttTransportState(transport.mqttState)}`,
63
+ `${labels_1.MQTT_CHANNEL} ${formatMqttTransportState(transport.mqttState)}`,
63
64
  `api p50 ${api.p50Ms}ms p95 ${api.p95Ms}ms (req ${api.requests}, err ${api.errors})`,
64
65
  ].join(' | ');
65
66
  }
package/dist/platform.js CHANGED
@@ -347,7 +347,7 @@ class NaviLinkPlatform {
347
347
  }
348
348
  this.cloudOffline = false;
349
349
  this.lastOutageWarnAt = 0;
350
- this.log.info('mqtt recovered');
350
+ this.log.info(`${utils_1.MQTT_CHANNEL} recovered`);
351
351
  }
352
352
  // --- AccessoryHost ----------------------------------------------------------
353
353
  deviceFor(deviceId) {
package/dist/session.d.ts CHANGED
@@ -107,7 +107,7 @@ export declare class NaviLinkSession {
107
107
  private readonly announcedFamilies;
108
108
  /** Firmware lines already announced at info, so a credential refresh is not a new event. */
109
109
  private readonly announcedFirmware;
110
- /** True after the first `mqtt up` line, so a refresh is not a boot. */
110
+ /** True after the first `Publish-subscribe (mqtt) up` line, so a refresh is not a boot. */
111
111
  private liveAnnounced;
112
112
  /** True after an unexpected drop, so the next connect is a recovery. */
113
113
  private liveWasDown;
package/dist/session.js CHANGED
@@ -80,7 +80,7 @@ class NaviLinkSession {
80
80
  announcedFamilies = new Set();
81
81
  /** Firmware lines already announced at info, so a credential refresh is not a new event. */
82
82
  announcedFirmware = new Set();
83
- /** True after the first `mqtt up` line, so a refresh is not a boot. */
83
+ /** True after the first `Publish-subscribe (mqtt) up` line, so a refresh is not a boot. */
84
84
  liveAnnounced = false;
85
85
  /** True after an unexpected drop, so the next connect is a recovery. */
86
86
  liveWasDown = false;
@@ -416,19 +416,19 @@ class NaviLinkSession {
416
416
  // A refused fallback must not look like an outage mid-establish.
417
417
  connection.onClose((error) => this.handleConnectionClosed(connection, error));
418
418
  if (!this.liveAnnounced || this.liveWasDown) {
419
- this.log.info('mqtt up');
419
+ this.log.info(`${utils_1.MQTT_CHANNEL} up`);
420
420
  this.liveAnnounced = true;
421
421
  this.liveWasDown = false;
422
422
  }
423
423
  else {
424
- this.log.debug('mqtt up');
424
+ this.log.debug(`${utils_1.MQTT_CHANNEL} up`);
425
425
  }
426
426
  return;
427
427
  }
428
428
  catch (error) {
429
429
  lastError = error;
430
430
  connection.close();
431
- this.log.debug(`MQTT connect failed with host${signHostWithPort ? ':443' : ''} signature: `
431
+ this.log.debug(`${utils_1.MQTT_CHANNEL} connect failed with host${signHostWithPort ? ':443' : ''} signature: `
432
432
  + (0, utils_1.describeError)(error));
433
433
  }
434
434
  }
@@ -11,6 +11,8 @@
11
11
  * with. Use the name the user gave the appliance. Fall back to a masked
12
12
  * gateway only when there is no name, and say that it is a gateway.
13
13
  */
14
+ /** Operator-facing name for the MQTT live channel. */
15
+ export declare const MQTT_CHANNEL = "Publish-subscribe (mqtt)";
14
16
  /** The appliance name, or `gateway …ABCD` when none is known. */
15
17
  export declare function labelAppliance(input: {
16
18
  name?: string;
@@ -13,9 +13,12 @@
13
13
  * gateway only when there is no name, and say that it is a gateway.
14
14
  */
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.MQTT_CHANNEL = void 0;
16
17
  exports.labelAppliance = labelAppliance;
17
18
  const validators_1 = require("./validators");
18
19
  const redact_1 = require("./redact");
20
+ /** Operator-facing name for the MQTT live channel. */
21
+ exports.MQTT_CHANNEL = 'Publish-subscribe (mqtt)';
19
22
  /** The appliance name, or `gateway …ABCD` when none is known. */
20
23
  function labelAppliance(input) {
21
24
  const name = input.name?.trim();
package/docs/FEATURES.md CHANGED
@@ -35,7 +35,7 @@ A checklist of what is built. The plugin aims to cover everything about a Navien
35
35
  - ✅ Read-only mode: every accessory reports state, and no control command is ever sent (`readOnly; write ignored`)
36
36
  - ✅ Power-off guard, on by default, because a combi's power state governs central heating as well as hot water (`power-off disabled (allowPowerOff is off)`)
37
37
  - ✅ Accessory name prefix (`options.accessoryPrefix`): one editable stem for every HomeKit tile, without changing accessory identity
38
- - ✅ Opt-in diagnostics (`options.diagnosticsInterval`, default 0 / off): a periodic heartbeat (`Health: healthy | devices n/n | mqtt live | api p50/p95 (req, err)`), with optional structured JSON (`options.structuredLogs`)
38
+ - ✅ Opt-in diagnostics (`options.diagnosticsInterval`, default 0 / off): a periodic heartbeat (`Health: healthy | devices n/n | Publish-subscribe (mqtt) live | api p50/p95 (req, err)`), with optional structured JSON (`options.structuredLogs`)
39
39
  - ✅ Secrets redacted from every log line by shape, not by field name, so an unfamiliar response cannot leak a token
40
40
  - ✅ HomeKit serial numbers are opaque generated values, never the gateway MAC
41
41
  - ✅ Bounded I/O: separate connect and total timeouts, a capped response size, and a capped MQTT packet size
@@ -106,7 +106,7 @@ An appliance with no probe reports `0`. The plugin treats that as absent, not as
106
106
 
107
107
  **Control is rate-limited, and the cloud's own limit is honoured.** Commands to one gateway are spaced out. When the cloud refuses one for arriving too soon, the log is `rate limited` and the plugin pauses for 30 seconds. It does not keep sending to a channel that is already refusing.
108
108
 
109
- **Backoff has jitter.** Reconnects use exponential backoff with full jitter to a capped ceiling, so a regional outage does not produce a synchronised stampede when it ends. A real drop logs the close, then `reconnect in Ns`, then `mqtt recovered` when the session is up again.
109
+ **Backoff has jitter.** Reconnects use exponential backoff with full jitter to a capped ceiling, so a regional outage does not produce a synchronised stampede when it ends. A real drop logs the close, then `reconnect in Ns`, then `Publish-subscribe (mqtt) recovered` when the session is up again.
110
110
 
111
111
  ## Full configuration reference
112
112
 
@@ -198,7 +198,7 @@ Startup, in order:
198
198
  adding Boiler Hot Water
199
199
  adding Boiler Heating
200
200
  Boiler firmware 4352
201
- mqtt up
201
+ Publish-subscribe (mqtt) up
202
202
  Boiler channel 1 family=NCB
203
203
  1 appliance(s), 8 accessory(ies)
204
204
  ```
@@ -228,13 +228,13 @@ An outage, and its recovery. Every outage warns on the way in so that it gets a
228
228
  ```
229
229
  NaviLink broker closed the connection (code 1006)
230
230
  reconnect in 2s
231
- mqtt up
232
- mqtt recovered
231
+ Publish-subscribe (mqtt) up
232
+ Publish-subscribe (mqtt) recovered
233
233
  ```
234
234
 
235
235
  A continuing outage repeats at most hourly, at debug in between.
236
236
 
237
- A credential refresh is not an outage. The plugin closes the socket itself a few minutes before the AWS credentials expire, signs in again, and the tiles stay current. That is debug only. `family=`, firmware and `mqtt up` do not repeat at info.
237
+ A credential refresh is not an outage. The plugin closes the socket itself a few minutes before the AWS credentials expire, signs in again, and the tiles stay current. That is debug only. `family=`, firmware and `Publish-subscribe (mqtt) up` do not repeat at info.
238
238
 
239
239
  ### Diagnostics (optional)
240
240
 
@@ -243,7 +243,7 @@ Set `options.diagnosticsInterval` to a value between `30` and `3600` seconds to
243
243
  It pairs with `options.structuredLogs: true`, which adds a JSON line next to the human one.
244
244
 
245
245
  ```
246
- Health: healthy | devices 1/1 | mqtt live | api p50 80ms p95 120ms (req 3, err 0)
246
+ Health: healthy | devices 1/1 | Publish-subscribe (mqtt) live | api p50 80ms p95 120ms (req 3, err 0)
247
247
  ```
248
248
 
249
249
  | `msg` | Level | When |
@@ -124,18 +124,14 @@
124
124
  </div>
125
125
 
126
126
  <div class="form-group mt-3 mb-2">
127
- <label for="diagnostics-interval" style="font-size: .8rem;">Diagnostics</label>
128
- <select class="form-control form-control-sm" id="diagnostics-interval">
129
- <option value="0" selected>Off</option>
130
- <option value="30">Every 30 seconds</option>
131
- <option value="60">Every minute</option>
132
- <option value="300">Every 5 minutes</option>
133
- <option value="900">Every 15 minutes</option>
134
- <option value="1800">Every 30 minutes</option>
135
- <option value="3600">Every 60 minutes</option>
136
- </select>
127
+ <label for="diagnostics-interval" style="font-size: .8rem;">
128
+ Diagnostics interval (seconds)
129
+ <span id="diagnostics-interval-value" class="float-right">0</span>
130
+ </label>
131
+ <input type="range" class="custom-range" id="diagnostics-interval"
132
+ min="0" max="3600" step="1" value="0" />
137
133
  <div class="nl-meta">
138
- A health line in the Homebridge log. Off by default. Nothing is exposed in HomeKit.
134
+ A health line in the Homebridge log. 0 is off. Nothing is exposed in HomeKit.
139
135
  </div>
140
136
  </div>
141
137
  <div class="nl-options">
@@ -247,14 +247,17 @@
247
247
  ? platformConfig.options.accessoryPrefix
248
248
  : ''
249
249
  const diagnostics = Number(platformConfig.options.diagnosticsInterval) || 0
250
- const diagnosticsSelect = byId('diagnostics-interval')
251
- if (!Array.from(diagnosticsSelect.options).some((option) => Number(option.value) === diagnostics)) {
252
- diagnosticsSelect.append(el('option', { value: String(diagnostics) }, `Every ${diagnostics} seconds`))
253
- }
254
- diagnosticsSelect.value = String(diagnostics)
250
+ byId('diagnostics-interval').value = String(diagnostics)
251
+ syncDiagnosticsLabel()
255
252
  byId('structured-logs').checked = platformConfig.options.structuredLogs === true
256
253
  }
257
254
 
255
+ /** Keep the slider's number in step with the thumb. 0 is Off. */
256
+ function syncDiagnosticsLabel() {
257
+ const seconds = Number(byId('diagnostics-interval').value) || 0
258
+ byId('diagnostics-interval-value').textContent = seconds === 0 ? 'Off' : String(seconds)
259
+ }
260
+
258
261
  /**
259
262
  * Fold the whole-install settings back into the configuration.
260
263
  *
@@ -501,7 +504,14 @@
501
504
  byId('accessory-prefix').addEventListener('input', () => changed())
502
505
  byId('read-only').addEventListener('change', () => changed())
503
506
  byId('allow-power-off').addEventListener('change', () => changed())
504
- byId('diagnostics-interval').addEventListener('change', () => changed())
507
+ byId('diagnostics-interval').addEventListener('input', () => {
508
+ syncDiagnosticsLabel()
509
+ changed()
510
+ })
511
+ byId('diagnostics-interval').addEventListener('change', () => {
512
+ syncDiagnosticsLabel()
513
+ changed()
514
+ })
505
515
  byId('structured-logs').addEventListener('change', () => changed())
506
516
  byId('toggle-json').addEventListener('click', () => {
507
517
  schemaFormVisible = !schemaFormVisible
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-navilink",
3
3
  "displayName": "Homebridge NaviLink",
4
- "version": "0.1.2",
4
+ "version": "0.1.3",
5
5
  "description": "Homebridge plugin for Navien combi boilers and water heaters through the North American NaviLink cloud: hot water and space-heating thermostats, power, recirculation, fault and temperature accessories. Verified on an NCB-240E (firmware 4352)",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",