homebridge-broadlink-rm-blaster 1.3.2 → 1.5.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/CHANGELOG.md CHANGED
@@ -6,6 +6,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## [1.5.0] - 2026-08-06
10
+
11
+ ### Added
12
+ - Advanced Accessories: a new standalone accessory type where one button
13
+ press sends multiple signals in sequence, with a configurable timeout
14
+ between them. An optional Off signal gives it real on/off state (a
15
+ normal Switch); without one, it's an auto-resetting momentary trigger
16
+ instead. `broadlink-rm-learner` can learn these too.
17
+
18
+ ## [1.4.0] - 2026-08-05
19
+
20
+ ### Added
21
+ - `broadlink-rm-learner`, an interactive CLI to learn IR/RF codes straight
22
+ from a remote and write the resulting Basic Accessory, TV, or Dimmer
23
+ Light into `config.json` - backs up `config.json` to `config.json.backup`
24
+ once per session before writing anything. RF codes are learned at a known
25
+ frequency (defaults to 433.92 MHz, entered by hand) rather than an
26
+ automatic sweep, which proved unreliable on real hardware. Menu choices
27
+ resolve on a single keypress, no Enter required. Connects to the RM
28
+ device before prompting to press any button, rather than after. Dimmer
29
+ Light learning asks the number of brightness levels up front, then Power
30
+ On/Off, then each brightness signal from 0% up to 100% last.
31
+
32
+ ### Changed
33
+ - README: moved the learner CLI's docs into a new "Learning hex codes"
34
+ section under Configuration (previously part of Debugging), and removed
35
+ the link to the standalone `learn-broadlink-rm4-codes` project.
36
+
9
37
  ## [1.3.2] - 2026-08-04
10
38
 
11
39
  ### Added
package/README.md CHANGED
@@ -1,15 +1,16 @@
1
- # Broadlink RM Blaster 1.3.2
1
+ # Broadlink RM Blaster 1.5.0
2
2
 
3
3
  **This Homebridge plugin has been 100% vibe coded with Claude.**
4
4
 
5
5
  Blast RF and IR signals from Broadlink RM devices using Homebridge. Sends
6
- pre-recorded hex codes to a known device IP; does not learn signals or
7
- autodiscover devices. To capture hex codes from your own remotes, see
8
- [learn-broadlink-rm4-codes](https://github.com/LuudJacobs/learn-broadlink-rm4-codes).
6
+ hex codes to a known device IP; this plugin does not autodiscover devices.
7
+ Codes can be learned interactively via the included `broadlink-rm-learner`
8
+ command (see [Learning hex codes](#learning-hex-codes)).
9
9
 
10
10
  ## Requirements
11
11
 
12
12
  - A Broadlink RM device, unlocked using the official Broadlink app
13
+ - A fixed IP for the RM device, since the plugin does no autodiscovery
13
14
 
14
15
  ## Installation
15
16
 
@@ -21,19 +22,36 @@ hb-service add homebridge-broadlink-rm-blaster
21
22
  ## Features
22
23
 
23
24
  - **Multiple RM devices** configure several Broadlink RMs and assign each accessory to whichever one
25
+ - **Interactive code learning** learn codes straight from your remote and add them to your config
24
26
  - **Basic accessories** power on/off
27
+ - **Advanced accessories** one press sends multiple signals in sequence, with a configurable timeout between them
25
28
  - **Dimmer lights** one signal per discrete brightness level
26
29
  - **TVs** power on/off plus a usable remote in the iOS Remote app
27
30
  - **Temperature/humidity sensor** optionally expose sensor data
28
31
  - **ntfy.sh notifications** push notification when a RM fails to connect
29
32
  - **MQTT publishing** optionally publish sensor data to a MQTT broker
30
- - Fully configurable via the Homebridge Config UI X plugin settings form
33
+ - **Fully configurable** via the Homebridge Config UI X plugin settings form
31
34
 
32
35
  ## Configuration
33
36
 
34
37
  This plugin can be fully configured from the Homebridge Config UI X plugin
35
38
  settings form.
36
39
 
40
+ ## Learning hex codes
41
+
42
+ To learn codes straight from a remote and add the resulting accessory to
43
+ your config, run this in the Homebridge shell:
44
+
45
+ ```bash
46
+ broadlink-rm-learner
47
+ ```
48
+
49
+ Run `broadlink-rm-learner --help` for usage details. Advanced Accessories
50
+ (multiple signals per press) can be learned too, alongside Basic
51
+ Accessories, TVs, and Dimmer Lights.
52
+
53
+ Backs up `config.json` to `config.json.backup` once per session before writing anything.
54
+
37
55
  ## Debugging
38
56
 
39
57
  To send a single hex code straight to your RM, bypassing Homebridge/HomeKit entirely run this in the Homebridge shell:
@@ -154,6 +154,53 @@
154
154
  }
155
155
  }
156
156
  },
157
+ "advancedAccessories": {
158
+ "title": "Advanced Accessories",
159
+ "type": "array",
160
+ "items": {
161
+ "type": "object",
162
+ "properties": {
163
+ "name": {
164
+ "title": "Name",
165
+ "type": "string",
166
+ "required": true
167
+ },
168
+ "rmDevice": {
169
+ "title": "RM Device",
170
+ "type": "string",
171
+ "required": true,
172
+ "description": "Must match the name of one of the configured RM devices exactly."
173
+ },
174
+ "offCode": {
175
+ "title": "Off Signal (leave empty for an auto-resetting trigger instead of a real on/off switch)",
176
+ "type": "string",
177
+ "required": false
178
+ },
179
+ "timeoutSeconds": {
180
+ "title": "Timeout Between Signals (seconds)",
181
+ "type": "number",
182
+ "minimum": 0,
183
+ "default": 0.5,
184
+ "required": false
185
+ },
186
+ "signals": {
187
+ "title": "Signals",
188
+ "type": "array",
189
+ "minItems": 1,
190
+ "items": {
191
+ "type": "object",
192
+ "properties": {
193
+ "code": {
194
+ "title": "Signal",
195
+ "type": "string",
196
+ "required": true
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ }
203
+ },
157
204
  "dimmers": {
158
205
  "title": "Dimmer Lights",
159
206
  "type": "array",
@@ -405,6 +452,33 @@
405
452
  "type": "help",
406
453
  "helpvalue": "<div style=\"height: 1.5rem\"></div>"
407
454
  },
455
+ {
456
+ "type": "array",
457
+ "key": "advancedAccessories",
458
+ "title": "Advanced Accessories",
459
+ "expandable": true,
460
+ "expanded": false,
461
+ "items": [
462
+ "advancedAccessories[].name",
463
+ "advancedAccessories[].rmDevice",
464
+ "advancedAccessories[].offCode",
465
+ "advancedAccessories[].timeoutSeconds",
466
+ {
467
+ "type": "array",
468
+ "key": "advancedAccessories[].signals",
469
+ "title": "Signals",
470
+ "expandable": true,
471
+ "expanded": false,
472
+ "items": [
473
+ "advancedAccessories[].signals[].code"
474
+ ]
475
+ }
476
+ ]
477
+ },
478
+ {
479
+ "type": "help",
480
+ "helpvalue": "<div style=\"height: 1.5rem\"></div>"
481
+ },
408
482
  {
409
483
  "type": "array",
410
484
  "key": "dimmers",
@@ -0,0 +1,15 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { BroadlinkRMBlasterPlatform } from '../platform';
3
+ import type { AdvancedAccessoryConfig } from '../configTypes';
4
+ export declare class AdvancedAccessory {
5
+ private readonly platform;
6
+ private readonly accessory;
7
+ private readonly config;
8
+ private readonly ip;
9
+ constructor(platform: BroadlinkRMBlasterPlatform, accessory: PlatformAccessory, config: AdvancedAccessoryConfig, ip: string);
10
+ private getOn;
11
+ private setOn;
12
+ private turnOff;
13
+ private sendSequence;
14
+ private scheduleAutoReset;
15
+ }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AdvancedAccessory = void 0;
4
+ const DEFAULT_TIMEOUT_SECONDS = 0.5;
5
+ // How long to wait before resetting an auto-resetting (no offCode) trigger
6
+ // back to off. Not a stylistic choice - HomeKit's own optimistic UI update
7
+ // ignores a follow-up characteristic update that arrives too soon after the
8
+ // triggering write, so the tile visually sticks "on" unless this is a real
9
+ // delay (same lesson learned from this project's old dimmer up/down
10
+ // switches, removed in v0.7.0, but the timing constraint still applies to
11
+ // any momentary HomeKit switch).
12
+ const RESET_DELAY_MS = 1000;
13
+ function sleep(ms) {
14
+ return new Promise((resolve) => setTimeout(resolve, ms));
15
+ }
16
+ class AdvancedAccessory {
17
+ platform;
18
+ accessory;
19
+ config;
20
+ ip;
21
+ constructor(platform, accessory, config, ip) {
22
+ this.platform = platform;
23
+ this.accessory = accessory;
24
+ this.config = config;
25
+ this.ip = ip;
26
+ const service = this.accessory.getService(this.platform.Service.Switch)
27
+ ?? this.accessory.addService(this.platform.Service.Switch);
28
+ service.setCharacteristic(this.platform.Characteristic.Name, this.config.name);
29
+ service.getCharacteristic(this.platform.Characteristic.On)
30
+ .onGet(() => this.getOn())
31
+ .onSet((value) => this.setOn(value));
32
+ }
33
+ // Same as every other blaster accessory - no feedback from the device
34
+ // being controlled, so "On" is an assumed state we track ourselves.
35
+ getOn() {
36
+ return Boolean(this.accessory.context.on);
37
+ }
38
+ async setOn(value) {
39
+ const on = Boolean(value);
40
+ if (!on) {
41
+ await this.turnOff();
42
+ return;
43
+ }
44
+ try {
45
+ await this.sendSequence();
46
+ this.accessory.context.on = true;
47
+ this.platform.log.info(`Sent signal sequence to ${this.config.name}`);
48
+ }
49
+ catch (error) {
50
+ this.platform.log.error(`Failed to send code for "${this.config.name}": ${error.message}`);
51
+ const { HapStatusError, HAPStatus } = this.platform.api.hap;
52
+ throw new HapStatusError(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
53
+ }
54
+ if (!this.config.offCode) {
55
+ this.scheduleAutoReset();
56
+ }
57
+ }
58
+ async turnOff() {
59
+ if (!this.config.offCode) {
60
+ // Momentary trigger - nothing to send, just reflect the state.
61
+ this.accessory.context.on = false;
62
+ return;
63
+ }
64
+ try {
65
+ await this.platform.broadlinkClient.sendCode(this.ip, this.config.offCode);
66
+ this.accessory.context.on = false;
67
+ this.platform.log.info(`Sent Off to ${this.config.name}`);
68
+ }
69
+ catch (error) {
70
+ this.platform.log.error(`Failed to send code for "${this.config.name}": ${error.message}`);
71
+ const { HapStatusError, HAPStatus } = this.platform.api.hap;
72
+ throw new HapStatusError(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
73
+ }
74
+ }
75
+ async sendSequence() {
76
+ const timeoutMs = (this.config.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS) * 1000;
77
+ const { signals } = this.config;
78
+ for (let i = 0; i < signals.length; i++) {
79
+ await this.platform.broadlinkClient.sendCode(this.ip, signals[i].code);
80
+ if (i < signals.length - 1) {
81
+ await sleep(timeoutMs);
82
+ }
83
+ }
84
+ }
85
+ scheduleAutoReset() {
86
+ setTimeout(() => {
87
+ this.accessory.context.on = false;
88
+ this.accessory.getService(this.platform.Service.Switch)
89
+ ?.updateCharacteristic(this.platform.Characteristic.On, false);
90
+ }, RESET_DELAY_MS);
91
+ }
92
+ }
93
+ exports.AdvancedAccessory = AdvancedAccessory;
94
+ //# sourceMappingURL=advancedAccessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advancedAccessory.js","sourceRoot":"","sources":["../../src/accessories/advancedAccessory.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { Logger } from 'homebridge';
2
2
  import type { NtfyNotifier } from './ntfyNotifier';
3
3
  export declare function parseHexCode(hexCode: string): Buffer;
4
+ export declare function buildFindRfPacket(frequencyMHz: number): Buffer;
4
5
  export interface TemperatureHumidityReading {
5
6
  temperature: number;
6
7
  humidity: number;
@@ -14,5 +15,9 @@ export declare class BroadlinkClient {
14
15
  private getBroadlink;
15
16
  private getDevice;
16
17
  sendCode(ip: string, hexCode: string): Promise<void>;
18
+ connect(ip: string): Promise<void>;
17
19
  readTemperatureHumidity(ip: string): Promise<TemperatureHumidityReading>;
20
+ private waitForEvent;
21
+ learnIrCode(ip: string, signal: AbortSignal): Promise<string>;
22
+ learnRfCode(ip: string, frequencyMHz: number, signal: AbortSignal): Promise<string>;
18
23
  }
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.BroadlinkClient = void 0;
7
7
  exports.parseHexCode = parseHexCode;
8
+ exports.buildFindRfPacket = buildFindRfPacket;
8
9
  const kiwicam_broadlinkjs_rm_1 = __importDefault(require("kiwicam-broadlinkjs-rm"));
9
10
  // Device type code the library reserves for a manually added RM (RF capable),
10
11
  // used when connecting directly by IP instead of relying on UDP discovery.
@@ -12,9 +13,40 @@ const MANUAL_RM_DEVICE_TYPE = 0x2227;
12
13
  const BROADLINK_PORT = 80;
13
14
  const AUTH_TIMEOUT_MS = 10_000;
14
15
  const READ_TIMEOUT_MS = 10_000;
16
+ const LEARN_POLL_INTERVAL_MS = 1_000;
17
+ const LEARN_TIMEOUT_MS = 20_000;
15
18
  function parseHexCode(hexCode) {
16
19
  return Buffer.from(hexCode.replace(/\s+/g, ''), 'hex');
17
20
  }
21
+ // Builds the raw "find RF packet at a known frequency" command - matches
22
+ // python-broadlink's rmpro.find_rf_packet(frequency=...), which is what
23
+ // actually works reliably (confirmed: our own blind-sweep attempt via
24
+ // enterRFSweep()/checkRFData()/checkRFData2() failed on real hardware, while
25
+ // this known-frequency approach - same as the learn-broadlink-rm4-codes
26
+ // Python project - succeeded for IR; RF still needed this fix).
27
+ //
28
+ // The "new firmware"/RM4-generation wire format (confirmed against
29
+ // python-broadlink's actual rmminib._send, not guessed) is
30
+ // [length:2][command:4][data], all little-endian, where length = data.length
31
+ // + 4. kiwicam-broadlinkjs-rm's own request_header ([0x04, 0x00]) is only
32
+ // the length prefix for an EMPTY data payload, and its command-sending
33
+ // methods (checkData() etc) only send a 1-byte command instead of the full
34
+ // 4-byte one - both of those gaps are silently absorbed by AES block padding
35
+ // for empty-payload commands, which is why checkData()/enterLearning() work
36
+ // fine despite being technically malformed. That padding can't save a
37
+ // command with a real (non-zero) payload like this one: the frequency bytes
38
+ // would land 3 bytes early, inside what should still be the command field.
39
+ // So this builds the full, correct frame by hand instead of reusing
40
+ // request_header.
41
+ function buildFindRfPacket(frequencyMHz) {
42
+ const data = Buffer.alloc(4);
43
+ data.writeUInt32LE(Math.round(frequencyMHz * 1000), 0);
44
+ const length = Buffer.alloc(2);
45
+ length.writeUInt16LE(data.length + 4, 0);
46
+ const command = Buffer.alloc(4);
47
+ command.writeUInt32LE(0x1b, 0);
48
+ return Buffer.concat([length, command, data]);
49
+ }
18
50
  // The manual/direct-by-IP placeholder MAC. Confirmed (via a real regression:
19
51
  // see the comment on broadlinkInstances below) that real Broadlink RM4 Pro
20
52
  // units silently refuse to authenticate at all if this is anything other
@@ -82,6 +114,13 @@ class BroadlinkClient {
82
114
  const device = await this.getDevice(ip);
83
115
  await device.sendData(parseHexCode(hexCode));
84
116
  }
117
+ // Establishes (or reuses, since getDevice caches per IP) a connection
118
+ // without sending any command - lets a caller wait for a real connection
119
+ // and report on it before prompting for anything time-sensitive (e.g. "now
120
+ // press the button on the remote").
121
+ async connect(ip) {
122
+ await this.getDevice(ip);
123
+ }
85
124
  async readTemperatureHumidity(ip) {
86
125
  const device = await this.getDevice(ip);
87
126
  return new Promise((resolve, reject) => {
@@ -98,6 +137,65 @@ class BroadlinkClient {
98
137
  device.checkTemperature();
99
138
  });
100
139
  }
140
+ // Waits for `event` to fire, actively re-issuing `poll()` at a fixed
141
+ // interval in the meantime (this library never pushes learning data
142
+ // unprompted - it has to be asked again on a timer, same pattern as the
143
+ // Python reference project this CLI flow is modeled on). `signal` lets the
144
+ // caller cancel mid-wait (Ctrl-C / "press q to cancel" in the learner CLI).
145
+ waitForEvent(device, event, poll, signal, timeoutMessage) {
146
+ return new Promise((resolve, reject) => {
147
+ if (signal.aborted) {
148
+ reject(new Error('Cancelled'));
149
+ return;
150
+ }
151
+ const cleanup = () => {
152
+ clearTimeout(timeout);
153
+ clearInterval(interval);
154
+ device.removeListener(event, onData);
155
+ signal.removeEventListener('abort', onAbort);
156
+ };
157
+ const onData = (data) => {
158
+ cleanup();
159
+ resolve(data);
160
+ };
161
+ const onAbort = () => {
162
+ cleanup();
163
+ reject(new Error('Cancelled'));
164
+ };
165
+ const timeout = setTimeout(() => {
166
+ cleanup();
167
+ reject(new Error(timeoutMessage));
168
+ }, LEARN_TIMEOUT_MS);
169
+ const interval = setInterval(poll, LEARN_POLL_INTERVAL_MS);
170
+ device.on(event, onData);
171
+ signal.addEventListener('abort', onAbort);
172
+ });
173
+ }
174
+ // One-shot: press the remote button any time after this is called.
175
+ async learnIrCode(ip, signal) {
176
+ const device = await this.getDevice(ip);
177
+ device.enterLearning();
178
+ try {
179
+ const data = await this.waitForEvent(device, 'rawData', () => device.checkData(), signal, 'Timed out waiting for a signal.');
180
+ return data.toString('hex');
181
+ }
182
+ finally {
183
+ device.cancelLearn();
184
+ }
185
+ }
186
+ // Known-frequency RF learning (see buildFindRfPacket above for why) - also
187
+ // one-shot: press the button any time after this is called, same as IR.
188
+ async learnRfCode(ip, frequencyMHz, signal) {
189
+ const device = await this.getDevice(ip);
190
+ await device.sendPacket(0x6a, buildFindRfPacket(frequencyMHz));
191
+ try {
192
+ const data = await this.waitForEvent(device, 'rawData', () => device.checkData(), signal, 'Timed out waiting for a signal.');
193
+ return data.toString('hex');
194
+ }
195
+ finally {
196
+ device.cancelLearn();
197
+ }
198
+ }
101
199
  }
102
200
  exports.BroadlinkClient = BroadlinkClient;
103
201
  //# sourceMappingURL=broadlinkClient.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"broadlinkClient.js","sourceRoot":"","sources":["../src/broadlinkClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"broadlinkClient.js","sourceRoot":"","sources":["../src/broadlinkClient.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  #!/usr/bin/env node
2
+ import type { Logger } from 'homebridge';
2
3
  export declare function isValidHexCode(hexCode: string): boolean;
4
+ export declare function createConsoleLogger(): Logger;
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.isValidHexCode = isValidHexCode;
5
+ exports.createConsoleLogger = createConsoleLogger;
5
6
  const broadlinkClient_1 = require("./broadlinkClient");
6
7
  const USAGE = 'Usage: broadlink-rm-blaster <ip> <hexCode>';
7
8
  const HELP = `${USAGE}
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { BlasterPlatformConfig } from './configTypes';
2
+ export interface HomebridgeConfigFile {
3
+ platforms?: Array<Record<string, unknown>>;
4
+ [key: string]: unknown;
5
+ }
6
+ export declare function findConfigPath(explicitPath?: string): string;
7
+ export declare function findPlatformBlock(config: HomebridgeConfigFile): BlasterPlatformConfig | undefined;
8
+ export declare function backupConfig(configPath: string): void;
9
+ export declare function writeConfigAtomically(configPath: string, config: HomebridgeConfigFile): void;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.findConfigPath = findConfigPath;
37
+ exports.findPlatformBlock = findPlatformBlock;
38
+ exports.backupConfig = backupConfig;
39
+ exports.writeConfigAtomically = writeConfigAtomically;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const settings_1 = require("./settings");
43
+ function findConfigPath(explicitPath) {
44
+ const configPath = explicitPath ?? path.join(process.cwd(), 'config.json');
45
+ if (!fs.existsSync(configPath)) {
46
+ throw new Error(`Could not find a config.json at "${configPath}". Run this from your Homebridge storage ` +
47
+ 'directory, or pass --config <path>.');
48
+ }
49
+ return configPath;
50
+ }
51
+ function findPlatformBlock(config) {
52
+ return config.platforms?.find((platform) => platform.platform === settings_1.PLATFORM_NAME);
53
+ }
54
+ function backupConfig(configPath) {
55
+ fs.copyFileSync(configPath, `${configPath}.backup`);
56
+ }
57
+ function writeConfigAtomically(configPath, config) {
58
+ const tempPath = path.join(path.dirname(configPath), `.${path.basename(configPath)}.tmp`);
59
+ fs.writeFileSync(tempPath, `${JSON.stringify(config, null, 2)}\n`);
60
+ fs.renameSync(tempPath, configPath);
61
+ }
62
+ //# sourceMappingURL=configFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configFile.js","sourceRoot":"","sources":["../src/configFile.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,wCASC;AAED,8CAEC;AAED,oCAEC;AAED,sDAIC;AAlCD,uCAAyB;AACzB,2CAA6B;AAE7B,yCAA2C;AAQ3C,SAAgB,cAAc,CAAC,YAAqB;IAClD,MAAM,UAAU,GAAG,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAC;IAC3E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,oCAAoC,UAAU,2CAA2C;YACzF,qCAAqC,CACtC,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAgB,iBAAiB,CAAC,MAA4B;IAC5D,OAAO,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,KAAK,wBAAa,CAAsC,CAAC;AACxH,CAAC;AAED,SAAgB,YAAY,CAAC,UAAkB;IAC7C,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,UAAU,SAAS,CAAC,CAAC;AACtD,CAAC;AAED,SAAgB,qBAAqB,CAAC,UAAkB,EAAE,MAA4B;IACpF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1F,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACnE,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACtC,CAAC"}
@@ -32,6 +32,16 @@ export interface DimmerAccessoryConfig {
32
32
  debounceSeconds?: number;
33
33
  levels: BrightnessLevelConfig[];
34
34
  }
35
+ export interface AdvancedSignalConfig {
36
+ code: string;
37
+ }
38
+ export interface AdvancedAccessoryConfig {
39
+ name: string;
40
+ rmDevice: string;
41
+ signals: AdvancedSignalConfig[];
42
+ offCode?: string;
43
+ timeoutSeconds?: number;
44
+ }
35
45
  export interface TvAccessoryConfig {
36
46
  name: string;
37
47
  rmDevice: string;
@@ -52,6 +62,7 @@ export interface TvAccessoryConfig {
52
62
  export interface BlasterPlatformConfig extends PlatformConfig {
53
63
  rmDevices: RmDeviceConfig[];
54
64
  accessories?: BasicAccessoryConfig[];
65
+ advancedAccessories?: AdvancedAccessoryConfig[];
55
66
  dimmers?: DimmerAccessoryConfig[];
56
67
  tvs?: TvAccessoryConfig[];
57
68
  ntfyTopic?: string;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};