homebridge-broadlink-rm-blaster 1.3.2 → 1.4.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,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## [1.4.0] - 2026-08-05
10
+
11
+ ### Added
12
+ - `broadlink-rm-learner`, an interactive CLI to learn IR/RF codes straight
13
+ from a remote and write the resulting Basic Accessory, TV, or Dimmer
14
+ Light into `config.json` - backs up `config.json` to `config.json.backup`
15
+ once per session before writing anything. RF codes are learned at a known
16
+ frequency (defaults to 433.92 MHz, entered by hand) rather than an
17
+ automatic sweep, which proved unreliable on real hardware. Menu choices
18
+ resolve on a single keypress, no Enter required. Connects to the RM
19
+ device before prompting to press any button, rather than after. Dimmer
20
+ Light learning asks the number of brightness levels up front, then Power
21
+ On/Off, then each brightness signal from 0% up to 100% last.
22
+
23
+ ### Changed
24
+ - README: moved the learner CLI's docs into a new "Learning hex codes"
25
+ section under Configuration (previously part of Debugging), and removed
26
+ the link to the standalone `learn-broadlink-rm4-codes` project.
27
+
9
28
  ## [1.3.2] - 2026-08-04
10
29
 
11
30
  ### Added
package/README.md CHANGED
@@ -1,15 +1,16 @@
1
- # Broadlink RM Blaster 1.3.2
1
+ # Broadlink RM Blaster 1.4.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,33 @@ 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
25
27
  - **Dimmer lights** one signal per discrete brightness level
26
28
  - **TVs** power on/off plus a usable remote in the iOS Remote app
27
29
  - **Temperature/humidity sensor** optionally expose sensor data
28
30
  - **ntfy.sh notifications** push notification when a RM fails to connect
29
31
  - **MQTT publishing** optionally publish sensor data to a MQTT broker
30
- - Fully configurable via the Homebridge Config UI X plugin settings form
32
+ - **Fully configurable** via the Homebridge Config UI X plugin settings form
31
33
 
32
34
  ## Configuration
33
35
 
34
36
  This plugin can be fully configured from the Homebridge Config UI X plugin
35
37
  settings form.
36
38
 
39
+ ## Learning hex codes
40
+
41
+ To learn codes straight from a remote and add the resulting accessory to
42
+ your config, run this in the Homebridge shell:
43
+
44
+ ```bash
45
+ broadlink-rm-learner
46
+ ```
47
+
48
+ Run `broadlink-rm-learner --help` for usage details.
49
+
50
+ Backs up `config.json` to `config.json.backup` once per session before writing anything.
51
+
37
52
  ## Debugging
38
53
 
39
54
  To send a single hex code straight to your RM, bypassing Homebridge/HomeKit entirely run this in the Homebridge shell:
@@ -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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,396 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const fs = __importStar(require("fs"));
38
+ const broadlinkClient_1 = require("./broadlinkClient");
39
+ const cli_1 = require("./cli");
40
+ const configFile_1 = require("./configFile");
41
+ const settings_1 = require("./settings");
42
+ const terminal_1 = require("./terminal");
43
+ const USAGE = 'Usage: broadlink-rm-learner [--config <path>]';
44
+ const DEFAULT_RF_FREQUENCY_MHZ = 433.92;
45
+ const HELP = `${USAGE}
46
+
47
+ Interactively learns IR/RF codes from a Broadlink RM and adds the finished
48
+ accessory straight into your Homebridge config.json - a guided walkthrough
49
+ per accessory type, same idea as the learn-broadlink-rm4-codes project but
50
+ built in, and it writes the config for you.
51
+
52
+ A backup is made once per session, before anything is written, saved
53
+ alongside your config as config.json.backup.
54
+
55
+ Options:
56
+ --config <path> Path to config.json (defaults to ./config.json)
57
+ -h, --help Show this help message
58
+ `;
59
+ // Set only for the duration of an in-flight learnIrCode/learnRfCode call, so
60
+ // Ctrl-C can abort it cleanly instead of the process just dying mid-learn.
61
+ let activeAbortController = null;
62
+ async function askNonEmpty(prompt) {
63
+ for (;;) {
64
+ const answer = (await (0, terminal_1.readLine)(prompt)).trim();
65
+ if (answer) {
66
+ return answer;
67
+ }
68
+ console.log('This field is required.');
69
+ }
70
+ }
71
+ async function pickRmDevice(rmDevices) {
72
+ console.log('\nWhich RM device?');
73
+ rmDevices.forEach((device, index) => console.log(` ${index + 1}. ${device.name} (${device.ip})`));
74
+ const choices = rmDevices.map((_, index) => String(index + 1));
75
+ const choice = await (0, terminal_1.readKey)('Choice: ', choices);
76
+ return rmDevices[Number(choice) - 1];
77
+ }
78
+ // Connects (or confirms the cached connection) up front, so "press the
79
+ // button now" prompts never appear before the RM is actually ready to
80
+ // receive a learning command.
81
+ async function connectToDevice(client, ip) {
82
+ console.log(`\nConnecting to ${ip}...`);
83
+ try {
84
+ await client.connect(ip);
85
+ console.log('Connected.');
86
+ return true;
87
+ }
88
+ catch (error) {
89
+ const message = error instanceof Error ? error.message : String(error);
90
+ console.log(`Failed to connect: ${message}`);
91
+ return false;
92
+ }
93
+ }
94
+ async function askFrequency() {
95
+ for (;;) {
96
+ const answer = (await (0, terminal_1.readLine)(`\nRF frequency in MHz (Enter for ${DEFAULT_RF_FREQUENCY_MHZ}): `)).trim();
97
+ if (!answer) {
98
+ return DEFAULT_RF_FREQUENCY_MHZ;
99
+ }
100
+ const frequency = Number(answer);
101
+ if (Number.isFinite(frequency) && frequency > 0) {
102
+ return frequency;
103
+ }
104
+ console.log(`Enter a positive number, e.g. ${DEFAULT_RF_FREQUENCY_MHZ}.`);
105
+ }
106
+ }
107
+ async function askSignalSettings() {
108
+ const choice = await (0, terminal_1.readKey)('\nIs the remote for this accessory IR or RF? [I/r]: ', ['', 'i', 'r']);
109
+ if (choice === 'r') {
110
+ const frequencyMHz = await askFrequency();
111
+ return { signalType: 'rf', frequencyMHz };
112
+ }
113
+ return { signalType: 'ir' };
114
+ }
115
+ async function runLearnLoop(client, ip, settings, label) {
116
+ for (;;) {
117
+ console.log(settings.signalType === 'rf'
118
+ ? `\nLearning "${label}" - press the button now (${settings.frequencyMHz} MHz)...`
119
+ : `\nLearning "${label}" - press the button now...`);
120
+ const controller = new AbortController();
121
+ activeAbortController = controller;
122
+ let hex;
123
+ try {
124
+ hex = settings.signalType === 'rf'
125
+ ? await client.learnRfCode(ip, settings.frequencyMHz, controller.signal)
126
+ : await client.learnIrCode(ip, controller.signal);
127
+ }
128
+ catch (error) {
129
+ activeAbortController = null;
130
+ const message = error instanceof Error ? error.message : String(error);
131
+ if (message === 'Cancelled') {
132
+ return { status: 'cancelled' };
133
+ }
134
+ console.log(`Failed: ${message}`);
135
+ const retry = await (0, terminal_1.readKey)('Enter to retry, or "q" to cancel this accessory: ', ['', 'q']);
136
+ if (retry === 'q') {
137
+ return { status: 'cancelled' };
138
+ }
139
+ continue;
140
+ }
141
+ activeAbortController = null;
142
+ console.log(`Captured: ${hex}`);
143
+ const confirm = await (0, terminal_1.readKey)('Enter to keep, "r" to retry, or "q" to cancel this accessory: ', ['', 'r', 'q']);
144
+ if (confirm === 'r') {
145
+ continue;
146
+ }
147
+ if (confirm === 'q') {
148
+ return { status: 'cancelled' };
149
+ }
150
+ return { status: 'learned', hex };
151
+ }
152
+ }
153
+ async function learnRequiredSignal(client, ip, settings, label) {
154
+ return runLearnLoop(client, ip, settings, label);
155
+ }
156
+ async function learnOptionalSignal(client, ip, settings, label) {
157
+ const choice = await (0, terminal_1.readKey)(`\nLearn "${label}"? Enter to learn it, "s" to skip, or "q" to cancel this accessory: `, ['', 's', 'q']);
158
+ if (choice === 's') {
159
+ return { status: 'skipped' };
160
+ }
161
+ if (choice === 'q') {
162
+ return { status: 'cancelled' };
163
+ }
164
+ return runLearnLoop(client, ip, settings, label);
165
+ }
166
+ async function learnBasicAccessory(client, rmDevices, pendingItems) {
167
+ const rmDevice = await pickRmDevice(rmDevices);
168
+ if (!(await connectToDevice(client, rmDevice.ip))) {
169
+ return;
170
+ }
171
+ const name = await askNonEmpty('\nName for this accessory: ');
172
+ const settings = await askSignalSettings();
173
+ const powerOn = await learnRequiredSignal(client, rmDevice.ip, settings, 'Power On');
174
+ if (powerOn.status === 'cancelled') {
175
+ console.log('Cancelled - nothing saved for this accessory.');
176
+ return;
177
+ }
178
+ const powerOff = await learnOptionalSignal(client, rmDevice.ip, settings, 'Power Off (skip to reuse Power On)');
179
+ if (powerOff.status === 'cancelled') {
180
+ console.log('Cancelled - nothing saved for this accessory.');
181
+ return;
182
+ }
183
+ const item = {
184
+ name,
185
+ rmDevice: rmDevice.name,
186
+ accessoryType: 'light',
187
+ powerOnCode: powerOn.hex,
188
+ };
189
+ if (powerOff.status === 'learned') {
190
+ item.powerOffCode = powerOff.hex;
191
+ }
192
+ pendingItems.push({ type: 'accessories', item });
193
+ console.log(`\n"${name}" queued to be saved.`);
194
+ }
195
+ async function learnTv(client, rmDevices, pendingItems) {
196
+ const rmDevice = await pickRmDevice(rmDevices);
197
+ if (!(await connectToDevice(client, rmDevice.ip))) {
198
+ return;
199
+ }
200
+ const name = await askNonEmpty('\nName for this TV: ');
201
+ const settings = await askSignalSettings();
202
+ const powerOn = await learnRequiredSignal(client, rmDevice.ip, settings, 'Power On');
203
+ if (powerOn.status === 'cancelled') {
204
+ console.log('Cancelled - nothing saved for this TV.');
205
+ return;
206
+ }
207
+ const item = {
208
+ name,
209
+ rmDevice: rmDevice.name,
210
+ powerOnCode: powerOn.hex,
211
+ };
212
+ const optionalSignals = [
213
+ ['Power Off (skip to reuse Power On)', 'powerOffCode'],
214
+ ['Volume Up', 'volumeUpCode'],
215
+ ['Volume Down', 'volumeDownCode'],
216
+ ['Mute', 'muteCode'],
217
+ ['Arrow Up', 'arrowUpCode'],
218
+ ['Arrow Down', 'arrowDownCode'],
219
+ ['Arrow Left', 'arrowLeftCode'],
220
+ ['Arrow Right', 'arrowRightCode'],
221
+ ['Select', 'selectCode'],
222
+ ['Info', 'infoCode'],
223
+ ['Back', 'backCode'],
224
+ ['Exit', 'exitCode'],
225
+ ];
226
+ for (const [label, field] of optionalSignals) {
227
+ const result = await learnOptionalSignal(client, rmDevice.ip, settings, label);
228
+ if (result.status === 'cancelled') {
229
+ console.log('Cancelled - nothing saved for this TV.');
230
+ return;
231
+ }
232
+ if (result.status === 'learned') {
233
+ item[field] = result.hex;
234
+ }
235
+ }
236
+ pendingItems.push({ type: 'tvs', item });
237
+ console.log(`\n"${name}" queued to be saved.`);
238
+ }
239
+ async function askStepCount() {
240
+ for (;;) {
241
+ const answer = (await (0, terminal_1.readLine)('\nHow many brightness steps, including 100% (e.g. 5 for 20/40/60/80/100)? Minimum 2: ')).trim();
242
+ const steps = Number(answer);
243
+ if (Number.isInteger(steps) && steps >= 2) {
244
+ return steps;
245
+ }
246
+ console.log('Enter a whole number of 2 or more.');
247
+ }
248
+ }
249
+ async function learnDimmer(client, rmDevices, pendingItems) {
250
+ const rmDevice = await pickRmDevice(rmDevices);
251
+ if (!(await connectToDevice(client, rmDevice.ip))) {
252
+ return;
253
+ }
254
+ const name = await askNonEmpty('\nName for this dimmer light: ');
255
+ const settings = await askSignalSettings();
256
+ const steps = await askStepCount();
257
+ const requiredSignals = [
258
+ ['Power On', 'powerOnCode'],
259
+ ['Power Off', 'powerOffCode'],
260
+ ];
261
+ const codes = {};
262
+ for (const [label, field] of requiredSignals) {
263
+ const result = await learnRequiredSignal(client, rmDevice.ip, settings, label);
264
+ if (result.status === 'cancelled') {
265
+ console.log('Cancelled - nothing saved for this dimmer light.');
266
+ return;
267
+ }
268
+ codes[field] = result.hex;
269
+ }
270
+ const zero = await learnRequiredSignal(client, rmDevice.ip, settings, 'Brightness 0%');
271
+ if (zero.status === 'cancelled') {
272
+ console.log('Cancelled - nothing saved for this dimmer light.');
273
+ return;
274
+ }
275
+ codes.zeroPercentCode = zero.hex;
276
+ const levels = [];
277
+ for (let i = 1; i < steps; i++) {
278
+ const percent = Math.round((i * 100) / steps);
279
+ const result = await learnRequiredSignal(client, rmDevice.ip, settings, `Brightness ${percent}%`);
280
+ if (result.status === 'cancelled') {
281
+ console.log('Cancelled - nothing saved for this dimmer light.');
282
+ return;
283
+ }
284
+ levels.push({ level: percent, code: result.hex });
285
+ }
286
+ const hundred = await learnRequiredSignal(client, rmDevice.ip, settings, 'Brightness 100%');
287
+ if (hundred.status === 'cancelled') {
288
+ console.log('Cancelled - nothing saved for this dimmer light.');
289
+ return;
290
+ }
291
+ codes.hundredPercentCode = hundred.hex;
292
+ const item = {
293
+ name,
294
+ rmDevice: rmDevice.name,
295
+ powerOnCode: codes.powerOnCode,
296
+ powerOffCode: codes.powerOffCode,
297
+ zeroPercentCode: codes.zeroPercentCode,
298
+ hundredPercentCode: codes.hundredPercentCode,
299
+ levels,
300
+ };
301
+ pendingItems.push({ type: 'dimmers', item });
302
+ console.log(`\n"${name}" queued to be saved.`);
303
+ }
304
+ function printFallback(pendingItems) {
305
+ console.error('\nHere is what would have been added - add it manually if needed:');
306
+ console.error(JSON.stringify(pendingItems.map((pending) => pending.item), null, 2));
307
+ }
308
+ function finishAndExit(configPath, pendingItems) {
309
+ if (pendingItems.length === 0) {
310
+ console.log('\nNothing learned this session - exiting.');
311
+ process.exit(0);
312
+ }
313
+ let freshConfig;
314
+ try {
315
+ freshConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
316
+ }
317
+ catch (error) {
318
+ console.error(`\nCould not re-read config.json to save: ${error.message}`);
319
+ printFallback(pendingItems);
320
+ process.exit(1);
321
+ }
322
+ const platform = (0, configFile_1.findPlatformBlock)(freshConfig);
323
+ if (!platform) {
324
+ console.error(`\nCould not find the "${settings_1.PLATFORM_NAME}" platform in config.json anymore - not writing, to avoid losing changes.`);
325
+ printFallback(pendingItems);
326
+ process.exit(1);
327
+ }
328
+ const platformRecord = platform;
329
+ for (const pending of pendingItems) {
330
+ const list = platformRecord[pending.type] ?? [];
331
+ list.push(pending.item);
332
+ platformRecord[pending.type] = list;
333
+ }
334
+ (0, configFile_1.writeConfigAtomically)(configPath, freshConfig);
335
+ console.log(`\nSaved ${pendingItems.length} item(s) to ${configPath}. Restart Homebridge to pick up the changes.`);
336
+ process.exit(0);
337
+ }
338
+ async function main() {
339
+ const args = process.argv.slice(2);
340
+ if (args.includes('-h') || args.includes('--help')) {
341
+ console.log(HELP);
342
+ process.exit(0);
343
+ }
344
+ const configFlagIndex = args.indexOf('--config');
345
+ const explicitConfigPath = configFlagIndex !== -1 ? args[configFlagIndex + 1] : undefined;
346
+ let configPath;
347
+ try {
348
+ configPath = (0, configFile_1.findConfigPath)(explicitConfigPath);
349
+ }
350
+ catch (error) {
351
+ console.error(error.message);
352
+ process.exit(1);
353
+ }
354
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
355
+ const platform = (0, configFile_1.findPlatformBlock)(config);
356
+ if (!platform || !platform.rmDevices?.length) {
357
+ console.error(`Could not find a configured "${settings_1.PLATFORM_NAME}" platform with at least one RM device in ${configPath}.\n` +
358
+ 'Configure at least one RM device in the Homebridge Config UI X plugin settings first.');
359
+ process.exit(1);
360
+ }
361
+ (0, configFile_1.backupConfig)(configPath);
362
+ console.log(`Backed up config.json to ${configPath}.backup`);
363
+ const client = new broadlinkClient_1.BroadlinkClient((0, cli_1.createConsoleLogger)());
364
+ const pendingItems = [];
365
+ (0, terminal_1.initTerminal)();
366
+ (0, terminal_1.setSigintHandler)(() => {
367
+ console.log('\n\nCtrl-C - stopping.');
368
+ activeAbortController?.abort();
369
+ finishAndExit(configPath, pendingItems);
370
+ });
371
+ for (;;) {
372
+ console.log('\nWhat would you like to learn?');
373
+ console.log(' 1. Basic Accessory (light/switch/outlet/fan)');
374
+ console.log(' 2. TV');
375
+ console.log(' 3. Dimmer Light');
376
+ console.log(' q. Quit and save');
377
+ const choice = await (0, terminal_1.readKey)('Choice: ', ['1', '2', '3', 'q']);
378
+ if (choice === '1') {
379
+ await learnBasicAccessory(client, platform.rmDevices, pendingItems);
380
+ }
381
+ else if (choice === '2') {
382
+ await learnTv(client, platform.rmDevices, pendingItems);
383
+ }
384
+ else if (choice === '3') {
385
+ await learnDimmer(client, platform.rmDevices, pendingItems);
386
+ }
387
+ else {
388
+ break;
389
+ }
390
+ }
391
+ finishAndExit(configPath, pendingItems);
392
+ }
393
+ if (require.main === module) {
394
+ main();
395
+ }
396
+ //# sourceMappingURL=learner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learner.js","sourceRoot":"","sources":["../src/learner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export type SigintHandler = () => void;
2
+ export declare function setSigintHandler(handler: SigintHandler): void;
3
+ export declare function initTerminal(): void;
4
+ export declare function readKey(promptText: string, choices: string[]): Promise<string>;
5
+ export declare function readLine(promptText: string): Promise<string>;
@@ -0,0 +1,119 @@
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.setSigintHandler = setSigintHandler;
37
+ exports.initTerminal = initTerminal;
38
+ exports.readKey = readKey;
39
+ exports.readLine = readLine;
40
+ const readline = __importStar(require("readline"));
41
+ let sigintHandler = null;
42
+ function setSigintHandler(handler) {
43
+ sigintHandler = handler;
44
+ }
45
+ function initTerminal() {
46
+ readline.emitKeypressEvents(process.stdin);
47
+ if (process.stdin.isTTY) {
48
+ process.stdin.setRawMode(true);
49
+ }
50
+ process.stdin.resume();
51
+ process.stdin.setEncoding('utf8');
52
+ }
53
+ function isCtrlC(str, key) {
54
+ return Boolean(key?.ctrl && key.name === 'c') || str === '';
55
+ }
56
+ function isEnter(key) {
57
+ return key?.name === 'return' || key?.name === 'enter';
58
+ }
59
+ // Resolves as soon as a keystroke matches one of `choices` - no Enter
60
+ // needed. Enter itself is normalized to ''.
61
+ function readKey(promptText, choices) {
62
+ process.stdout.write(promptText);
63
+ return new Promise((resolve) => {
64
+ const cleanup = () => {
65
+ process.stdin.removeListener('keypress', onKeypress);
66
+ };
67
+ const onKeypress = (str, key) => {
68
+ if (isCtrlC(str, key)) {
69
+ cleanup();
70
+ sigintHandler?.();
71
+ return;
72
+ }
73
+ const normalized = isEnter(key) ? '' : (str ?? '').toLowerCase();
74
+ if (choices.includes(normalized)) {
75
+ cleanup();
76
+ console.log(normalized);
77
+ resolve(normalized);
78
+ }
79
+ };
80
+ process.stdin.on('keypress', onKeypress);
81
+ });
82
+ }
83
+ // A minimal hand-rolled line editor (echo + backspace) since raw mode
84
+ // doesn't echo or line-buffer anything itself.
85
+ function readLine(promptText) {
86
+ process.stdout.write(promptText);
87
+ return new Promise((resolve) => {
88
+ let buffer = '';
89
+ const cleanup = () => {
90
+ process.stdin.removeListener('keypress', onKeypress);
91
+ };
92
+ const onKeypress = (str, key) => {
93
+ if (isCtrlC(str, key)) {
94
+ cleanup();
95
+ sigintHandler?.();
96
+ return;
97
+ }
98
+ if (isEnter(key)) {
99
+ cleanup();
100
+ process.stdout.write('\n');
101
+ resolve(buffer);
102
+ return;
103
+ }
104
+ if (key?.name === 'backspace') {
105
+ if (buffer.length > 0) {
106
+ buffer = buffer.slice(0, -1);
107
+ process.stdout.write('\b \b');
108
+ }
109
+ return;
110
+ }
111
+ if (str && !key?.ctrl && !key?.meta) {
112
+ buffer += str;
113
+ process.stdout.write(str);
114
+ }
115
+ };
116
+ process.stdin.on('keypress', onKeypress);
117
+ });
118
+ }
119
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-broadlink-rm-blaster",
3
3
  "displayName": "Broadlink RM Blaster",
4
- "version": "1.3.2",
4
+ "version": "1.4.0",
5
5
  "description": "Blast RF and IR signals from Broadlink RM devices using Homebridge",
6
6
  "author": "Luud Jacobs",
7
7
  "license": "MIT",
@@ -19,7 +19,8 @@
19
19
  },
20
20
  "main": "dist/index.js",
21
21
  "bin": {
22
- "broadlink-rm-blaster": "dist/cli.js"
22
+ "broadlink-rm-blaster": "dist/cli.js",
23
+ "broadlink-rm-learner": "dist/learner.js"
23
24
  },
24
25
  "files": [
25
26
  "dist",