homebridge-plugin-utils 1.15.3 → 1.17.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 (51) hide show
  1. package/README.md +4 -1
  2. package/build/eslint-rules.mjs +1 -0
  3. package/dist/featureoptions.d.ts +83 -5
  4. package/dist/featureoptions.js +65 -6
  5. package/dist/featureoptions.js.map +1 -1
  6. package/dist/ffmpeg/codecs.d.ts +172 -0
  7. package/dist/ffmpeg/codecs.js +374 -0
  8. package/dist/ffmpeg/codecs.js.map +1 -0
  9. package/dist/ffmpeg/exec.d.ts +108 -0
  10. package/dist/ffmpeg/exec.js +122 -0
  11. package/dist/ffmpeg/exec.js.map +1 -0
  12. package/dist/ffmpeg/index.d.ts +8 -0
  13. package/dist/ffmpeg/index.js +13 -0
  14. package/dist/ffmpeg/index.js.map +1 -0
  15. package/dist/ffmpeg/options.d.ts +345 -0
  16. package/dist/ffmpeg/options.js +750 -0
  17. package/dist/ffmpeg/options.js.map +1 -0
  18. package/dist/ffmpeg/process.d.ts +155 -0
  19. package/dist/ffmpeg/process.js +344 -0
  20. package/dist/ffmpeg/process.js.map +1 -0
  21. package/dist/ffmpeg/record.d.ts +237 -0
  22. package/dist/ffmpeg/record.js +512 -0
  23. package/dist/ffmpeg/record.js.map +1 -0
  24. package/dist/ffmpeg/rtp.d.ts +205 -0
  25. package/dist/ffmpeg/rtp.js +335 -0
  26. package/dist/ffmpeg/rtp.js.map +1 -0
  27. package/dist/ffmpeg/settings.d.ts +6 -0
  28. package/dist/ffmpeg/settings.js +17 -0
  29. package/dist/ffmpeg/settings.js.map +1 -0
  30. package/dist/ffmpeg/stream.d.ts +143 -0
  31. package/dist/ffmpeg/stream.js +186 -0
  32. package/dist/ffmpeg/stream.js.map +1 -0
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/mqttclient.d.ts +161 -1
  37. package/dist/mqttclient.js +161 -9
  38. package/dist/mqttclient.js.map +1 -1
  39. package/dist/service.d.ts +9 -2
  40. package/dist/service.js +6 -0
  41. package/dist/service.js.map +1 -1
  42. package/dist/ui/featureoptions.js +65 -6
  43. package/dist/ui/featureoptions.js.map +1 -1
  44. package/dist/ui/webUi-featureoptions.mjs +4 -4
  45. package/dist/util.d.ts +203 -12
  46. package/dist/util.js +95 -12
  47. package/dist/util.js.map +1 -1
  48. package/package.json +14 -10
  49. package/dist/rtp.d.ts +0 -32
  50. package/dist/rtp.js +0 -178
  51. package/dist/rtp.js.map +0 -1
@@ -0,0 +1,143 @@
1
+ /**
2
+ * FFmpeg process management and socket handling to support HomeKit livestreaming sessions.
3
+ *
4
+ * This module defines the `FfmpegStreamingProcess` class and related interfaces for orchestrating and monitoring FFmpeg-powered video streams. It manages process
5
+ * lifecycle, handles UDP socket creation for video health monitoring, and enables integration with Homebridge streaming delegates for robust error handling, stream
6
+ * cleanup, and automatic tuning.
7
+ *
8
+ * Key features:
9
+ *
10
+ * - Automated start, monitoring, and termination of HomeKit-compatible FFmpeg video streams.
11
+ * - Integration with Homebridge’s CameraStreamingDelegate for custom error hooks and lifecycle control.
12
+ * - UDP socket creation and management for realtime video stream liveness detection.
13
+ * - Intelligent error handling, including automatic tuning for FFmpeg’s stream probing requirements.
14
+ * - Exposes access to the underlying FFmpeg child process for advanced scenarios.
15
+ *
16
+ * Designed for plugin developers and advanced users who require fine-grained control and diagnostics for HomeKit livestreaming, with seamless Homebridge integration.
17
+ *
18
+ * @module
19
+ */
20
+ import type { CameraController, CameraStreamingDelegate, StreamRequestCallback } from "homebridge";
21
+ import type { ChildProcessWithoutNullStreams } from "child_process";
22
+ import type { FfmpegOptions } from "./options.js";
23
+ import { FfmpegProcess } from "./process.js";
24
+ import type { Nullable } from "../util.js";
25
+ /**
26
+ * Extension of the Homebridge CameraStreamingDelegate with additional streaming controls and error handling hooks.
27
+ *
28
+ * @property adjustProbeSize - Optional. Invoked to adjust probe size after stream startup errors.
29
+ * @property controller - The Homebridge CameraController instance managing the stream.
30
+ * @property ffmpegErrorCheck - Optional. Returns a user-friendly error message for specific FFmpeg errors, if detected.
31
+ * @property stopStream - Optional. Invoked to force stop a specific stream session by ID.
32
+ *
33
+ * @see CameraController
34
+ * @see CameraStreamingDelegate
35
+ *
36
+ * @category FFmpeg
37
+ */
38
+ export interface HomebridgeStreamingDelegate extends CameraStreamingDelegate {
39
+ adjustProbeSize?: () => void;
40
+ controller: CameraController;
41
+ ffmpegErrorCheck?: (logEntry: string[]) => string | undefined;
42
+ stopStream?: (sessionId: string) => void;
43
+ }
44
+ /**
45
+ * Provides FFmpeg process management and socket handling to support HomeKit livestreaming sessions.
46
+ *
47
+ * This class extends `FfmpegProcess` to create, monitor, and terminate HomeKit-compatible video streams. Additionally, it invokes delegate hooks for error processing and
48
+ * stream lifecycle management.
49
+ *
50
+ * @example
51
+ *
52
+ * ```ts
53
+ * const streamingDelegate: HomebridgeStreamingDelegate = {
54
+ *
55
+ * controller,
56
+ * stopStream: (sessionId) => { ... } // End-of-session cleanup code.
57
+ * };
58
+ *
59
+ * const process = new FfmpegStreamingProcess(
60
+ *
61
+ * streamingDelegate,
62
+ * sessionId,
63
+ * ffmpegOptions,
64
+ * commandLineArgs,
65
+ * { addressVersion: "ipv4", port: 5000 }
66
+ * );
67
+ * ```
68
+ *
69
+ * @see HomebridgeStreamingDelegate
70
+ * @see FfmpegProcess
71
+ *
72
+ * @category FFmpeg
73
+ */
74
+ export declare class FfmpegStreamingProcess extends FfmpegProcess {
75
+ private delegate;
76
+ /**
77
+ * The unique session identifier for this streaming process.
78
+ */
79
+ private sessionId;
80
+ /**
81
+ * The timeout reference used to monitor UDP stream health.
82
+ */
83
+ private streamTimeout?;
84
+ /**
85
+ * Constructs a new FFmpeg streaming process for a HomeKit session.
86
+ *
87
+ * Sets up required delegate hooks, creates UDP return sockets if needed, and starts the FFmpeg process. Automatically handles FFmpeg process errors and cleans up on
88
+ * failures.
89
+ *
90
+ * @param delegate - The Homebridge streaming delegate for this session.
91
+ * @param sessionId - The HomeKit session identifier for this stream.
92
+ * @param ffmpegOptions - The FFmpeg configuration options.
93
+ * @param commandLineArgs - FFmpeg command-line arguments.
94
+ * @param returnPort - Optional. UDP port info for the return stream (used except for two-way audio).
95
+ * @param callback - Optional. Callback invoked when the stream is ready or errors occur.
96
+ *
97
+ * @example
98
+ *
99
+ * ```ts
100
+ * const process = new FfmpegStreamingProcess(delegate, sessionId, ffmpegOptions, commandLineArgs, { addressVersion: "ipv6", port: 6000 });
101
+ * ```
102
+ */
103
+ constructor(delegate: HomebridgeStreamingDelegate, sessionId: string, ffmpegOptions: FfmpegOptions, commandLineArgs: string[], returnPort?: {
104
+ addressVersion: string;
105
+ port: number;
106
+ }, callback?: StreamRequestCallback);
107
+ /**
108
+ * Creates and binds a UDP socket for monitoring the health of the outgoing video stream.
109
+ *
110
+ * Listens for UDP "message" events, sets and clears timeouts, and handles error/cleanup scenarios. If no messages are received within 5 seconds, forcibly stops the
111
+ * stream and informs the delegate.
112
+ *
113
+ * @param portInfo - Object containing the address version ("ipv4" or "ipv6") and port number.
114
+ */
115
+ private createSocket;
116
+ /**
117
+ * Returns the underlying FFmpeg child process, or null if the process is not running.
118
+ *
119
+ * @returns The current FFmpeg process, or `null` if not running.
120
+ *
121
+ * @example
122
+ *
123
+ * ```ts
124
+ * const ffmpeg = process.ffmpegProcess;
125
+ *
126
+ * if(ffmpeg) {
127
+ *
128
+ * // Interact directly with the child process if necessary.
129
+ * }
130
+ * ```
131
+ */
132
+ get ffmpegProcess(): Nullable<ChildProcessWithoutNullStreams>;
133
+ /**
134
+ * Handle and logs FFmpeg process errors.
135
+ *
136
+ * If a known error condition is detected by the delegate, logs the custom message and returns. For "not enough frames to estimate rate; consider increasing probesize"
137
+ * errors, invokes the delegate's `adjustProbeSize` hook for automatic tuning. Otherwise, falls back to the parent class's logging.
138
+ *
139
+ * @param exitCode - The exit code from FFmpeg.
140
+ * @param signal - The signal, if any, used to terminate the process.
141
+ */
142
+ protected logFfmpegError(exitCode: number, signal: NodeJS.Signals): void;
143
+ }
@@ -0,0 +1,186 @@
1
+ /* Copyright(C) 2017-2025, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * ffmpeg/stream.ts: Provide FFmpeg process control to support HomeKit livestreaming.
4
+ */
5
+ import { FfmpegProcess } from "./process.js";
6
+ import { createSocket } from "node:dgram";
7
+ /**
8
+ * Provides FFmpeg process management and socket handling to support HomeKit livestreaming sessions.
9
+ *
10
+ * This class extends `FfmpegProcess` to create, monitor, and terminate HomeKit-compatible video streams. Additionally, it invokes delegate hooks for error processing and
11
+ * stream lifecycle management.
12
+ *
13
+ * @example
14
+ *
15
+ * ```ts
16
+ * const streamingDelegate: HomebridgeStreamingDelegate = {
17
+ *
18
+ * controller,
19
+ * stopStream: (sessionId) => { ... } // End-of-session cleanup code.
20
+ * };
21
+ *
22
+ * const process = new FfmpegStreamingProcess(
23
+ *
24
+ * streamingDelegate,
25
+ * sessionId,
26
+ * ffmpegOptions,
27
+ * commandLineArgs,
28
+ * { addressVersion: "ipv4", port: 5000 }
29
+ * );
30
+ * ```
31
+ *
32
+ * @see HomebridgeStreamingDelegate
33
+ * @see FfmpegProcess
34
+ *
35
+ * @category FFmpeg
36
+ */
37
+ export class FfmpegStreamingProcess extends FfmpegProcess {
38
+ /*
39
+ * The streaming delegate instance responsible for handling stream events and errors.
40
+ */
41
+ delegate;
42
+ /**
43
+ * The unique session identifier for this streaming process.
44
+ */
45
+ sessionId;
46
+ /**
47
+ * The timeout reference used to monitor UDP stream health.
48
+ */
49
+ streamTimeout;
50
+ /**
51
+ * Constructs a new FFmpeg streaming process for a HomeKit session.
52
+ *
53
+ * Sets up required delegate hooks, creates UDP return sockets if needed, and starts the FFmpeg process. Automatically handles FFmpeg process errors and cleans up on
54
+ * failures.
55
+ *
56
+ * @param delegate - The Homebridge streaming delegate for this session.
57
+ * @param sessionId - The HomeKit session identifier for this stream.
58
+ * @param ffmpegOptions - The FFmpeg configuration options.
59
+ * @param commandLineArgs - FFmpeg command-line arguments.
60
+ * @param returnPort - Optional. UDP port info for the return stream (used except for two-way audio).
61
+ * @param callback - Optional. Callback invoked when the stream is ready or errors occur.
62
+ *
63
+ * @example
64
+ *
65
+ * ```ts
66
+ * const process = new FfmpegStreamingProcess(delegate, sessionId, ffmpegOptions, commandLineArgs, { addressVersion: "ipv6", port: 6000 });
67
+ * ```
68
+ */
69
+ constructor(delegate, sessionId, ffmpegOptions, commandLineArgs, returnPort, callback) {
70
+ // Initialize our parent.
71
+ super(ffmpegOptions);
72
+ this.delegate = delegate;
73
+ this.delegate.adjustProbeSize ??= () => { };
74
+ this.delegate.ffmpegErrorCheck ??= () => undefined;
75
+ this.delegate.stopStream ??= () => { };
76
+ this.sessionId = sessionId;
77
+ // Create the return port for FFmpeg, if requested to do so. The only time we don't do this is when we're standing up
78
+ // a two-way audio stream - in that case, the audio work is done through RtpSplitter and not here.
79
+ if (returnPort) {
80
+ this.createSocket(returnPort);
81
+ }
82
+ // Start it up, with appropriate error handling.
83
+ this.start(commandLineArgs, callback, (errorMessage) => {
84
+ // Stop the stream.
85
+ this.delegate.stopStream?.(this.sessionId);
86
+ // Let homebridge know what happened and stop the stream if we've already started.
87
+ if (!this.isStarted && this.callback) {
88
+ this.callback(new Error(errorMessage));
89
+ this.callback = null;
90
+ return;
91
+ }
92
+ // Tell Homebridge to forcibly stop the streaming session.
93
+ this.delegate.controller.forceStopStreamingSession(this.sessionId);
94
+ this.delegate.stopStream?.(this.sessionId);
95
+ });
96
+ }
97
+ /**
98
+ * Creates and binds a UDP socket for monitoring the health of the outgoing video stream.
99
+ *
100
+ * Listens for UDP "message" events, sets and clears timeouts, and handles error/cleanup scenarios. If no messages are received within 5 seconds, forcibly stops the
101
+ * stream and informs the delegate.
102
+ *
103
+ * @param portInfo - Object containing the address version ("ipv4" or "ipv6") and port number.
104
+ */
105
+ createSocket(portInfo) {
106
+ let errorListener;
107
+ let messageListener;
108
+ const socket = createSocket(portInfo.addressVersion === "ipv6" ? "udp6" : "udp4");
109
+ // Cleanup after ourselves when the socket closes.
110
+ socket.once("close", () => {
111
+ if (this.streamTimeout) {
112
+ clearTimeout(this.streamTimeout);
113
+ }
114
+ socket.off("error", errorListener);
115
+ socket.off("message", messageListener);
116
+ });
117
+ // Handle potential network errors.
118
+ socket.on("error", errorListener = (error) => {
119
+ this.log.error("Socket error: %s.", error.name);
120
+ void this.delegate.stopStream?.(this.sessionId);
121
+ });
122
+ // Manage our video streams in case we haven't received a stop request, but we're in fact dead zombies.
123
+ socket.on("message", messageListener = () => {
124
+ // Clear our last canary.
125
+ if (this.streamTimeout) {
126
+ clearTimeout(this.streamTimeout);
127
+ }
128
+ // Set our new canary.
129
+ this.streamTimeout = setTimeout(() => {
130
+ this.log.debug("Video stream appears to be inactive for 5 seconds. Stopping stream.");
131
+ this.delegate.controller.forceStopStreamingSession(this.sessionId);
132
+ void this.delegate.stopStream?.(this.sessionId);
133
+ }, 5000);
134
+ });
135
+ // Bind to the port we're opening.
136
+ socket.bind(portInfo.port, (portInfo.addressVersion === "ipv6") ? "::1" : "127.0.0.1");
137
+ }
138
+ /**
139
+ * Returns the underlying FFmpeg child process, or null if the process is not running.
140
+ *
141
+ * @returns The current FFmpeg process, or `null` if not running.
142
+ *
143
+ * @example
144
+ *
145
+ * ```ts
146
+ * const ffmpeg = process.ffmpegProcess;
147
+ *
148
+ * if(ffmpeg) {
149
+ *
150
+ * // Interact directly with the child process if necessary.
151
+ * }
152
+ * ```
153
+ */
154
+ get ffmpegProcess() {
155
+ return this.process;
156
+ }
157
+ /**
158
+ * Handle and logs FFmpeg process errors.
159
+ *
160
+ * If a known error condition is detected by the delegate, logs the custom message and returns. For "not enough frames to estimate rate; consider increasing probesize"
161
+ * errors, invokes the delegate's `adjustProbeSize` hook for automatic tuning. Otherwise, falls back to the parent class's logging.
162
+ *
163
+ * @param exitCode - The exit code from FFmpeg.
164
+ * @param signal - The signal, if any, used to terminate the process.
165
+ */
166
+ logFfmpegError(exitCode, signal) {
167
+ // We want to process known streaming-related errors due to the performance and latency tweaks we've made to the FFmpeg command line. In some cases we may inform the
168
+ // user and take no action, in others, we tune our own internal parameters.
169
+ // Process any specific errors our caller is interested in.
170
+ const errTest = this.delegate.ffmpegErrorCheck?.(this.stderrLog);
171
+ if (errTest) {
172
+ this.log.error(errTest);
173
+ return;
174
+ }
175
+ // Test for probesize errors.
176
+ const probesizeRegex = new RegExp("not enough frames to estimate rate; consider increasing probesize");
177
+ if (this.stderrLog.some(logEntry => probesizeRegex.test(logEntry))) {
178
+ // Let the streaming delegate know to adjust it's parameters for the next run and inform the user.
179
+ this.delegate.adjustProbeSize?.();
180
+ return;
181
+ }
182
+ // Otherwise, revert to our default logging in our parent.
183
+ super.logFfmpegError(exitCode, signal);
184
+ }
185
+ }
186
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/ffmpeg/stream.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from "./featureoptions.js";
2
2
  export * from "./mqttclient.js";
3
- export * from "./rtp.js";
4
3
  export * from "./service.js";
5
4
  export * from "./util.js";
5
+ export * from "./ffmpeg/index.js";
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export * from "./featureoptions.js";
6
6
  export * from "./mqttclient.js";
7
- export * from "./rtp.js";
8
7
  export * from "./service.js";
9
8
  export * from "./util.js";
9
+ export * from "./ffmpeg/index.js";
10
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC"}
@@ -1,4 +1,42 @@
1
- import { HomebridgePluginLogging } from "./util.js";
1
+ /**
2
+ * MQTT connectivity and topic management for Homebridge plugins.
3
+ *
4
+ * @module
5
+ */
6
+ import type { HomebridgePluginLogging } from "./util.js";
7
+ /**
8
+ * MQTT connectivity and topic management class for Homebridge plugins.
9
+ *
10
+ * This class manages connection, publishing, subscription, and message handling for an MQTT broker, and provides convenience methods for Homebridge accessories to
11
+ * interact with MQTT topics using a standard topic prefix.
12
+ *
13
+ * @example
14
+ *
15
+ * ```ts
16
+ * const mqtt = new MqttClient("mqtt://localhost:1883", "homebridge", log);
17
+ *
18
+ * // Publish a message to a topic.
19
+ * mqtt.publish("device1", "status", "on");
20
+ *
21
+ * // Subscribe to a topic.
22
+ * mqtt.subscribe("device1", "status", (msg) => {
23
+ *
24
+ * console.log(msg.toString());
25
+ * });
26
+ *
27
+ * // Subscribe to a 'get' topic and automatically publish a value in response.
28
+ * mqtt.subscribeGet("device1", "temperature", "Temperature", () => "21.5");
29
+ *
30
+ * // Subscribe to a 'set' topic and handle value changes.
31
+ * mqtt.subscribeSet("device1", "switch", "Switch", (value) => {
32
+ *
33
+ * console.log("Switch set to", value);
34
+ * });
35
+ *
36
+ * // Unsubscribe from a topic.
37
+ * mqtt.unsubscribe("device1", "status");
38
+ * ```
39
+ */
2
40
  export declare class MqttClient {
3
41
  private brokerUrl;
4
42
  private isConnected;
@@ -7,12 +45,134 @@ export declare class MqttClient {
7
45
  private mqtt;
8
46
  private subscriptions;
9
47
  private topicPrefix;
48
+ /**
49
+ * Creates a new MQTT client for connecting to a broker and managing topics with a given prefix.
50
+ *
51
+ * @param brokerUrl - The MQTT broker URL (e.g., "mqtt://localhost:1883").
52
+ * @param topicPrefix - Prefix to use for all MQTT topics (e.g., "homebridge").
53
+ * @param log - Logger for debug and info messages.
54
+ * @param reconnectInterval - Optional. Interval (in seconds) to wait between reconnection attempts. Defaults to 60 seconds.
55
+ *
56
+ * @example
57
+ *
58
+ * ```ts
59
+ * const mqtt = new MqttClient("mqtt://localhost", "homebridge", log);
60
+ * ```
61
+ *
62
+ * @remarks URL must conform to formats supported by {@link https://github.com/mqttjs/MQTT.js | MQTT.js}.
63
+ */
10
64
  constructor(brokerUrl: string, topicPrefix: string, log: HomebridgePluginLogging, reconnectInterval?: number);
65
+ /**
66
+ * Initializes and connects the MQTT client to the broker, setting up event handlers for connection, messages, and errors.
67
+ *
68
+ * Catches invalid broker URLs and logs errors. Handles all major MQTT client events internally.
69
+ */
11
70
  private configure;
71
+ /**
72
+ * Publishes a message to a topic for a specific device.
73
+ *
74
+ * Expands the topic using the topic prefix and device ID, then publishes the provided message string.
75
+ *
76
+ * @param id - The device or accessory identifier.
77
+ * @param topic - The topic name to publish to.
78
+ * @param message - The message payload to publish.
79
+ *
80
+ * @example
81
+ *
82
+ * ```ts
83
+ * mqtt.publish("device1", "status", "on");
84
+ * ```
85
+ */
12
86
  publish(id: string, topic: string, message: string): void;
87
+ /**
88
+ * Subscribes to a topic for a specific device and registers a handler for incoming messages.
89
+ *
90
+ * The topic is expanded using the prefix and device ID, and the callback will be called for each message received.
91
+ *
92
+ * @param id - The device or accessory identifier.
93
+ * @param topic - The topic name to subscribe to.
94
+ * @param callback - Handler function called with the message buffer.
95
+ *
96
+ * @example
97
+ *
98
+ * ```ts
99
+ * mqtt.subscribe("device1", "status", (msg) => {
100
+ *
101
+ * console.log(msg.toString());
102
+ * });
103
+ * ```
104
+ */
13
105
  subscribe(id: string, topic: string, callback: (cbBuffer: Buffer) => void): void;
106
+ /**
107
+ * Subscribes to a '<topic>/get' topic and publishes a value in response to "true" messages.
108
+ *
109
+ * When a message "true" is received on the '<topic>/get' topic, this method will publish the result of `getValue()` on the main topic. The log will record each status
110
+ * publication event.
111
+ *
112
+ * @param id - The device or accessory identifier.
113
+ * @param topic - The topic name to use.
114
+ * @param type - A human-readable label for log messages (e.g., "Temperature").
115
+ * @param getValue - Function to get the value to publish as a string.
116
+ * @param log - Optional logger for status output. Defaults to the class logger.
117
+ *
118
+ * @example
119
+ *
120
+ * ```ts
121
+ * mqtt.subscribeGet("device1", "temperature", "Temperature", () => "21.5");
122
+ * ```
123
+ */
14
124
  subscribeGet(id: string, topic: string, type: string, getValue: () => string, log?: HomebridgePluginLogging): void;
125
+ /**
126
+ * Subscribes to a '<topic>/set' topic and calls a setter when a message is received.
127
+ *
128
+ * The `setValue` function is called with both a normalized value and the raw string. Handles both synchronous and promise-based setters. Logs when set messages are
129
+ * received and when errors occur.
130
+ *
131
+ * @param id - The device or accessory identifier.
132
+ * @param topic - The topic name to use.
133
+ * @param type - A human-readable label for log messages (e.g., "Switch").
134
+ * @param setValue - Function to call when a value is set. Can be synchronous or return a Promise.
135
+ * @param log - Optional logger for status output. Defaults to the class logger.
136
+ *
137
+ * @example
138
+ *
139
+ * ```ts
140
+ * mqtt.subscribeSet("device1", "switch", "Switch", (value) => {
141
+ *
142
+ * console.log("Switch set to", value);
143
+ * });
144
+ * ```
145
+ */
15
146
  subscribeSet(id: string, topic: string, type: string, setValue: (value: string, rawValue: string) => Promise<void> | void, log?: HomebridgePluginLogging): void;
147
+ /**
148
+ * Unsubscribes from a topic for a specific device, removing its message handler.
149
+ *
150
+ * @param id - The device or accessory identifier.
151
+ * @param topic - The topic name to unsubscribe from.
152
+ *
153
+ * @example
154
+ *
155
+ * ```ts
156
+ * mqtt.unsubscribe("device1", "status");
157
+ * ```
158
+ */
16
159
  unsubscribe(id: string, topic: string): void;
160
+ /**
161
+ * Expands a topic string into a fully-formed topic path including the prefix and device ID.
162
+ *
163
+ * Returns `null` if the device ID is missing or empty.
164
+ *
165
+ * @param id - The device or accessory identifier.
166
+ * @param topic - The topic name to expand.
167
+ *
168
+ * @returns The expanded topic string, or `null` if the ID is missing.
169
+ *
170
+ * @example
171
+ *
172
+ * ```ts
173
+ * const topic = mqtt['expandTopic']("device1", "status");
174
+ * // topic = "homebridge/device1/status"
175
+ * ```
176
+ */
17
177
  private expandTopic;
18
178
  }