homebridge-smartika 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Philippe Blondin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,367 @@
1
+ # Homebridge Smartika
2
+
3
+ [![npm](https://img.shields.io/npm/v/homebridge-smartika.svg)](https://www.npmjs.com/package/homebridge-smartika)
4
+ [![npm](https://img.shields.io/npm/dt/homebridge-smartika.svg)](https://www.npmjs.com/package/homebridge-smartika)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ A [Homebridge](https://homebridge.io) plugin for **Smartika** (Artika) smart home devices with **100% local control** — no cloud required!
8
+
9
+ ## Features
10
+
11
+ - 🏠 **100% Local Control** — All communication stays on your local network
12
+ - � **Auto-Discovery** — Automatically finds your Smartika hub on the network
13
+ - �💡 **Lights** — On/off, brightness, and color temperature control
14
+ - 🌀 **Ceiling Fans** — On/off and speed control
15
+ - 🔌 **Smart Plugs** — On/off control
16
+ - 🔄 **Real-time Updates** — Device status polling keeps HomeKit in sync
17
+ - 🔐 **Secure** — AES-128-CBC encrypted communication with your hub
18
+ - 🛠️ **CLI Tool** — Command-line interface for debugging and direct control
19
+
20
+ ## Supported Devices
21
+
22
+ | Category | Devices |
23
+ | --------------- | --------------------------------------------------------------------------------------------------------------------- |
24
+ | **Lights** | Champagne Track, Mini Wall Washer, Glowbox, Recessed Lighting, Pendants (1-5), Smart Bulb, Spotlight, Sandwich Lights |
25
+ | **Fans** | Ceiling Fan |
26
+ | **Plugs** | Smart Plug |
27
+ | **Thermostats** | Thermostat, Smart Heater *(coming soon)* |
28
+
29
+ ## Requirements
30
+
31
+ - [Homebridge](https://homebridge.io) v1.8.0 or later
32
+ - Node.js v18.20.4, v20.16.0, or v22.5.1+
33
+ - Smartika Hub on your local network
34
+
35
+ ## Installation
36
+
37
+ ### Using Homebridge Config UI X (Recommended)
38
+
39
+ 1. Search for **"Smartika"** in the Plugins tab
40
+ 2. Click **Install**
41
+ 3. Configure the plugin with your hub's IP address
42
+
43
+ ### Manual Installation
44
+
45
+ ```bash
46
+ sudo npm install -g homebridge-smartika
47
+ ```
48
+
49
+ ## Configuration
50
+
51
+ ### Auto-Discovery (Recommended)
52
+
53
+ The plugin can automatically discover your Smartika hub on the network — no manual configuration needed! Simply install the plugin and restart Homebridge.
54
+
55
+ The hub broadcasts its presence on UDP port 4156 every ~10 seconds. If auto-discovery doesn't work (e.g., due to network segmentation or firewall rules), you can configure the hub IP manually.
56
+
57
+ ### Finding Your Hub IP Address (Manual)
58
+
59
+ If auto-discovery doesn't work, find your hub's IP address by:
60
+
61
+ 1. Checking your router's DHCP client list
62
+ 2. Using a network scanner app
63
+ 3. Looking for a device with MAC address starting with `00:12:4B`
64
+
65
+ ### Using Homebridge Config UI X
66
+
67
+ 1. Go to the **Plugins** tab
68
+ 2. Find **Homebridge Smartika** and click **Settings**
69
+ 3. Enter your hub's IP address
70
+ 4. Click **Save**
71
+
72
+ ### Manual Configuration
73
+
74
+ Add the following to your `config.json`:
75
+
76
+ ```json
77
+ {
78
+ "platforms": [
79
+ {
80
+ "platform": "Smartika",
81
+ "name": "Smartika Hub"
82
+ }
83
+ ]
84
+ }
85
+ ```
86
+
87
+ With manual hub IP (if auto-discovery doesn't work):
88
+
89
+ ```json
90
+ {
91
+ "platforms": [
92
+ {
93
+ "platform": "Smartika",
94
+ "name": "Smartika Hub",
95
+ "hubHost": "10.0.0.122"
96
+ }
97
+ ]
98
+ }
99
+ ```
100
+
101
+ With all options:
102
+
103
+ ### Configuration Options
104
+
105
+ | Option | Required | Default | Description |
106
+ | ----------------- | -------- | ---------------- | ------------------------------------------------------------ |
107
+ | `platform` | ✅ | — | Must be `"Smartika"` |
108
+ | `name` | ❌ | `"Smartika Hub"` | Display name in Homebridge logs |
109
+ | `hubHost` | ❌ | Auto-discover | IP address of your Smartika hub (auto-discovered if not set) |
110
+ | `hubPort` | ❌ | `1234` | TCP port for hub communication |
111
+ | `pollingInterval` | ❌ | `5000` | Status polling interval in milliseconds |
112
+ | `debug` | ❌ | `false` | Enable verbose debug logging |
113
+
114
+ ## CLI Tool
115
+
116
+ This plugin includes a powerful command-line interface for direct hub control and debugging.
117
+
118
+ ### Installation
119
+
120
+ The CLI is installed automatically with the plugin:
121
+
122
+ ```bash
123
+ # If installed globally
124
+ smartika-cli --help
125
+
126
+ # Or run directly
127
+ npx smartika-cli --help
128
+ ```
129
+
130
+ ### Usage
131
+
132
+ ```bash
133
+ # Discover hubs on the network (no IP needed)
134
+ smartika-cli hub-discover
135
+
136
+ # Run commands on a specific hub
137
+ smartika-cli <hub-ip> <command> [arguments...]
138
+ ```
139
+
140
+ ### Examples
141
+
142
+ ```bash
143
+ # Discover Smartika hubs on your network
144
+ smartika-cli hub-discover
145
+
146
+ # Get hub information (ID, MAC, firmware, encryption key)
147
+ smartika-cli 10.0.0.122 hub-info
148
+
149
+ # Get status of all devices
150
+ smartika-cli 10.0.0.122 status
151
+
152
+ # Turn on a device
153
+ smartika-cli 10.0.0.122 on 0x28cf
154
+
155
+ # Set brightness to 50%
156
+ smartika-cli 10.0.0.122 dim 50% 0x28cf
157
+
158
+ # Set color temperature (0=warm, 255=cool)
159
+ smartika-cli 10.0.0.122 temp 128 0x28cf
160
+
161
+ # List registered devices
162
+ smartika-cli 10.0.0.122 list
163
+
164
+ # Get firmware version
165
+ smartika-cli 10.0.0.122 firmware
166
+
167
+ # Preview what devices will appear in HomeKit
168
+ smartika-cli 10.0.0.122 homekit-preview
169
+
170
+ # Interactive pairing wizard for new devices
171
+ smartika-cli 10.0.0.122 pair
172
+ ```
173
+
174
+ ### Available Commands
175
+
176
+ | Category | Command | Description |
177
+ | ----------------- | -------------- | ----------------------------------------- |
178
+ | **Hub Discovery** | `hub-discover` | Find hubs on the network (no IP needed) |
179
+ | **System** | `hub-info` | Get hub ID, MAC, firmware, encryption key |
180
+ | | `ping` | Send keep-alive ping |
181
+ | | `firmware` | Get hub firmware version |
182
+ | | `join-enable` | Enable device pairing mode |
183
+ | | `join-disable` | Disable device pairing mode |
184
+ | **Device** | `discover` | Discover active devices |
185
+ | | `status` | Get device status |
186
+ | | `on` | Turn device(s) on |
187
+ | | `off` | Turn device(s) off |
188
+ | | `dim` | Set light brightness |
189
+ | | `temp` | Set color temperature |
190
+ | | `fan` | Set fan speed |
191
+ | **Database** | `list` | List registered devices |
192
+ | | `db-add` | Add device(s) to database |
193
+ | | `db-remove` | Remove device(s) from database |
194
+ | **Groups** | `groups` | List all groups |
195
+ | | `group-read` | Read group members |
196
+ | | `group-create` | Create a new group |
197
+ | | `group-update` | Update group members |
198
+ | | `group-delete` | Delete group(s) |
199
+ | **HomeKit** | `homekit-preview` | Preview HomeKit accessories |
200
+ | **Pairing** | `pair` | Interactive wizard to pair new devices |
201
+
202
+ ## Troubleshooting
203
+
204
+ ### Auto-Discovery Not Working
205
+
206
+ 1. **macOS firewall** — On macOS, you may need to allow Node.js to receive incoming connections:
207
+ - Open **System Settings** → **Network** → **Firewall** → **Options**
208
+ - Add Node.js (run `which node` to find the path) and set to **Allow incoming connections**
209
+ - Or run the CLI with `sudo` to bypass the firewall temporarily
210
+ 2. **Check firewall** — Ensure UDP port 4156 is not blocked
211
+ 3. **Same network** — Hub and Homebridge must be on the same subnet
212
+ 4. **Use CLI to test** — Verify discovery works:
213
+ ```bash
214
+ # May need sudo on macOS if firewall blocks UDP
215
+ sudo smartika-cli hub-discover
216
+ ```
217
+ 5. **Fallback to manual** — Configure `hubHost` manually if discovery fails
218
+
219
+ ### Hub Not Connecting
220
+
221
+ 1. **Verify the IP address** — Make sure your hub's IP hasn't changed (consider setting a DHCP reservation)
222
+ 2. **Check network connectivity** — Ensure Homebridge can reach the hub: `ping 10.0.0.122`
223
+ 3. **Test with CLI** — Use the CLI tool to verify connectivity:
224
+ ```bash
225
+ smartika-cli 10.0.0.122 ping
226
+ ```
227
+ 4. **Check firewall** — Ensure port 1234 (TCP) is not blocked
228
+
229
+ ### Devices Not Appearing
230
+
231
+ 1. **Check device registration** — Devices must be registered in the hub's database:
232
+ ```bash
233
+ smartika-cli 10.0.0.122 list
234
+ ```
235
+ 2. **Pair new devices** — Use the interactive pairing wizard:
236
+ ```bash
237
+ smartika-cli 10.0.0.122 pair
238
+ ```
239
+ Or manually discover and add:
240
+ ```bash
241
+ smartika-cli 10.0.0.122 discover
242
+ smartika-cli 10.0.0.122 db-add 0x28cf
243
+ ```
244
+ 3. **Preview HomeKit accessories** — Check what will appear in HomeKit:
245
+ ```bash
246
+ smartika-cli 10.0.0.122 homekit-preview
247
+ ```
248
+ 4. **Restart Homebridge** — After adding devices, restart Homebridge to re-discover
249
+
250
+ ### Status Not Updating
251
+
252
+ 1. **Increase polling frequency** — Reduce `pollingInterval` to 2000-3000ms
253
+ 2. **Enable debug logging** — Set `"debug": true` to see communication details
254
+ 3. **Check for errors** — Look for error messages in Homebridge logs
255
+
256
+ ### Debug Mode
257
+
258
+ Enable debug logging to see detailed communication:
259
+
260
+ ```json
261
+ {
262
+ "platform": "Smartika",
263
+ "hubHost": "10.0.0.122",
264
+ "debug": true
265
+ }
266
+ ```
267
+
268
+ This will log:
269
+ - Connection status
270
+ - Request/response packets (hex)
271
+ - Device status updates
272
+ - Error details
273
+
274
+ ## Technical Details
275
+
276
+ ### Protocol
277
+
278
+ The plugin communicates with the Smartika hub using:
279
+ - **Transport**: TCP on port 1234
280
+ - **Encryption**: AES-128-CBC with key derived from hub MAC address
281
+ - **Protocol**: Custom binary protocol with XOR checksum
282
+
283
+ ### Security
284
+
285
+ - The encryption key is derived from your hub's unique MAC address using 8-pass AES-ECB
286
+ - All commands are encrypted before transmission
287
+ - No data is sent to external servers
288
+
289
+ ### Architecture
290
+
291
+ ```
292
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
293
+ │ Apple Home │────▶│ Homebridge │────▶│ Smartika │
294
+ │ (iOS) │◀────│ Plugin │◀────│ Hub │
295
+ └─────────────────┘ └──────────────────┘ └─────────────┘
296
+ HomeKit TCP/AES-CBC Local Network
297
+ ```
298
+
299
+ ## Development
300
+
301
+ ### Project Structure
302
+
303
+ ```
304
+ smartika-homebridge/
305
+ ├── src/
306
+ │ ├── index.js # Plugin entry point
307
+ │ ├── settings.js # Plugin constants
308
+ │ ├── SmartikaPlatform.js # Main platform class
309
+ │ ├── SmartikaHubConnection.js # Hub communication
310
+ │ ├── SmartikaCrypto.js # AES encryption
311
+ │ ├── SmartikaProtocol.js # Binary protocol
312
+ │ └── accessories/
313
+ │ ├── SmartikaLightAccessory.js
314
+ │ ├── SmartikaFanAccessory.js
315
+ │ └── SmartikaPlugAccessory.js
316
+ ├── tools/
317
+ │ └── smartika-cli.js # CLI tool
318
+ ├── test/
319
+ │ ├── test-protocol.js
320
+ │ └── test-crypto.js
321
+ ├── config.schema.json # Homebridge UI schema
322
+ └── package.json
323
+ ```
324
+
325
+ ### Building & Testing
326
+
327
+ ```bash
328
+ # Clone the repository
329
+ git clone https://github.com/pblondin/smartika-homebridge.git
330
+ cd smartika-homebridge
331
+
332
+ # Install dependencies
333
+ npm install
334
+
335
+ # Run tests
336
+ npm test
337
+ npm run test:crypto
338
+
339
+ # Lint code
340
+ npm run lint
341
+
342
+ # Link for local development
343
+ npm link
344
+ ```
345
+
346
+ ### Contributing
347
+
348
+ Contributions are welcome! Please:
349
+
350
+ 1. Fork the repository
351
+ 2. Create a feature branch
352
+ 3. Make your changes
353
+ 4. Run tests and linting
354
+ 5. Submit a pull request
355
+
356
+ ## License
357
+
358
+ MIT License — see [LICENSE](LICENSE) for details.
359
+
360
+ ## Acknowledgments
361
+
362
+ - [Homebridge](https://homebridge.io) team for the amazing platform
363
+ - Smartika/Artika for the hardware
364
+
365
+ ---
366
+
367
+ **Note**: This plugin is not officially affiliated with or endorsed by Smartika or Artika. Use at your own risk.
@@ -0,0 +1,71 @@
1
+ {
2
+ "pluginAlias": "Smartika",
3
+ "pluginType": "platform",
4
+ "singular": true,
5
+ "headerDisplay": "Homebridge plugin for Smartika smart lighting hub with 100% local communication (no cloud required). Supports lights, fans, and smart plugs. Leave Hub IP empty for auto-discovery.",
6
+ "footerDisplay": "For help and documentation, see the [GitHub repository](https://github.com/pblondin/smartika-homebridge). Use `smartika-cli hub-discover` to find your hub.",
7
+ "schema": {
8
+ "type": "object",
9
+ "properties": {
10
+ "name": {
11
+ "title": "Platform Name",
12
+ "type": "string",
13
+ "default": "Smartika Hub",
14
+ "required": true,
15
+ "description": "The name that will appear in Homebridge logs."
16
+ },
17
+ "hubHost": {
18
+ "title": "Hub IP Address",
19
+ "type": "string",
20
+ "format": "ipv4",
21
+ "placeholder": "10.0.0.122",
22
+ "description": "IP address of your Smartika hub. Leave empty for auto-discovery (requires UDP port 4156). Recommended to set a static IP or DHCP reservation."
23
+ },
24
+ "hubPort": {
25
+ "title": "Hub Port",
26
+ "type": "integer",
27
+ "default": 1234,
28
+ "minimum": 1,
29
+ "maximum": 65535,
30
+ "description": "TCP port for hub communication (default: 1234)."
31
+ },
32
+ "pollingInterval": {
33
+ "title": "Polling Interval (ms)",
34
+ "type": "integer",
35
+ "default": 5000,
36
+ "minimum": 1000,
37
+ "maximum": 60000,
38
+ "description": "How often to poll the hub for device status updates (in milliseconds)."
39
+ },
40
+ "debug": {
41
+ "title": "Debug Mode",
42
+ "type": "boolean",
43
+ "default": false,
44
+ "description": "Enable verbose debug logging for troubleshooting connection issues."
45
+ }
46
+ },
47
+ "required": []
48
+ },
49
+ "layout": [
50
+ {
51
+ "type": "fieldset",
52
+ "title": "Hub Connection",
53
+ "expandable": false,
54
+ "items": [
55
+ "name",
56
+ "hubHost",
57
+ "hubPort"
58
+ ]
59
+ },
60
+ {
61
+ "type": "fieldset",
62
+ "title": "Advanced Settings",
63
+ "expandable": true,
64
+ "expanded": false,
65
+ "items": [
66
+ "pollingInterval",
67
+ "debug"
68
+ ]
69
+ }
70
+ ]
71
+ }
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "homebridge-smartika",
3
+ "displayName": "Homebridge Smartika",
4
+ "description": "Homebridge plugin for Smartika smart lighting hub with 100% local control (no cloud required). Supports lights, fans, and smart plugs.",
5
+ "version": "1.0.0",
6
+ "license": "MIT",
7
+ "author": "Philippe Blondin",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/pblondin/homebridge-smartika.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/pblondin/homebridge-smartika/issues"
14
+ },
15
+ "funding": {
16
+ "type": "github",
17
+ "url": "https://github.com/sponsors/pblondin"
18
+ },
19
+ "keywords": [
20
+ "homebridge-plugin",
21
+ "smartika",
22
+ "artika",
23
+ "homekit",
24
+ "smart-home",
25
+ "lighting",
26
+ "local-control"
27
+ ],
28
+ "main": "src/index.js",
29
+ "files": [
30
+ "src/",
31
+ "tools/",
32
+ "config.schema.json",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "engines": {
37
+ "node": "^18.20.4 || ^20.16.0 || ^22.5.1",
38
+ "homebridge": "^1.8.0 || ^2.0.0-beta.0"
39
+ },
40
+ "scripts": {
41
+ "cli": "node tools/smartika-cli.js",
42
+ "test": "node test/test-protocol.js",
43
+ "test:crypto": "node test/test-crypto.js",
44
+ "lint": "eslint src/",
45
+ "watch": "nodemon",
46
+ "prepublishOnly": "npm run lint"
47
+ },
48
+ "bin": {
49
+ "smartika-cli": "tools/smartika-cli.js"
50
+ },
51
+ "dependencies": {},
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.0.0",
54
+ "eslint": "^9.0.0",
55
+ "nodemon": "^3.0.0"
56
+ }
57
+ }
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+
5
+ // Constants from hub_security.md
6
+ const PRIVATE_KEY = Buffer.from([
7
+ 0x42, 0x6B, 0xD6, 0xCD, 0x00, 0x59, 0x5B, 0x03, 0xFC, 0xCB, 0xF4, 0xDD, 0x09, 0x25, 0x85, 0x1B,
8
+ 0x3F, 0x91, 0x93, 0x81, 0xB3, 0x19, 0xB2, 0xD1, 0x41, 0x5B, 0xF7, 0x7D, 0xFD, 0x4F, 0x4C, 0xD3,
9
+ 0x5E, 0x00, 0xE1, 0xC0, 0x89, 0xA1, 0x94, 0xD4, 0xF6, 0xEA, 0x77, 0xAA, 0xC5, 0x1B, 0x66, 0x67,
10
+ 0xEE, 0x96, 0xCD, 0x6E, 0xC3, 0x7D, 0x8A, 0xF1, 0xD0, 0x2A, 0x10, 0x98, 0xA7, 0xF5, 0xB1, 0xC3,
11
+ 0x90, 0x3A, 0x4A, 0xB7, 0xB9, 0xE5, 0x0E, 0x47, 0xE5, 0xA0, 0xD2, 0x1B, 0x17, 0xD0, 0x8B, 0x5A,
12
+ 0x55, 0x7C, 0x50, 0xBA, 0x02, 0x66, 0xA7, 0xC1, 0xCC, 0x4D, 0x67, 0x3E, 0xD1, 0xB7, 0xEE, 0xC0,
13
+ 0xE3, 0x34, 0x00, 0x1F, 0x89, 0x7A, 0x0E, 0xC7, 0xC0, 0x49, 0x2F, 0xEE, 0x01, 0x7B, 0x94, 0x52,
14
+ 0x93, 0x22, 0xC0, 0xB9, 0xBB, 0x2C, 0x46, 0xD1, 0xBD, 0x65, 0x5F, 0x91, 0x56, 0x4B, 0x17, 0xCD
15
+ ]);
16
+
17
+ const BASE_KEY = Buffer.from([
18
+ 0xB9, 0x43, 0x34, 0xB5, 0xBE, 0xDE, 0x9E, 0x05, 0x58, 0xE2, 0xE6, 0xD8, 0xCE, 0xBA, 0x7E, 0x47
19
+ ]);
20
+
21
+ const IV = Buffer.from([
22
+ 0xA7, 0x2D, 0xD1, 0x29, 0x20, 0xDF, 0xAD, 0x61, 0x82, 0x03, 0x98, 0xFA, 0x9E, 0xEF, 0x59, 0x20
23
+ ]);
24
+
25
+ const BLOCK_SIZE = 16;
26
+
27
+ /**
28
+ * Generate encryption key from hub MAC address
29
+ * Uses specific bytes from the MAC address as per hub simulator's security.py
30
+ *
31
+ * @param {Buffer} macAddress - 6 bytes MAC address
32
+ * @returns {Buffer} - 16 bytes encryption key
33
+ */
34
+ function generateKey(macAddress) {
35
+ if (macAddress.length !== 6) {
36
+ throw new Error('MAC address must be 6 bytes');
37
+ }
38
+
39
+ // Insert MAC address bytes in specific positions (from security.py)
40
+ // seed = hub_id[-6:] (entire MAC for 6-byte MAC)
41
+ // base_key[9] = seed[0], base_key[7] = seed[3], base_key[13] = seed[4], base_key[3] = seed[5]
42
+ const baseKey = Buffer.from(BASE_KEY);
43
+ baseKey[9] = macAddress[0];
44
+ baseKey[7] = macAddress[3];
45
+ baseKey[13] = macAddress[4];
46
+ baseKey[3] = macAddress[5];
47
+
48
+ // 8-pass encryption (1024-bit)
49
+ let result = baseKey;
50
+ for (let i = 0; i < 8; i++) {
51
+ const key = PRIVATE_KEY.subarray(i * 16, (i + 1) * 16);
52
+ const cipher = crypto.createCipheriv('aes-128-ecb', key, null);
53
+ cipher.setAutoPadding(false);
54
+ result = Buffer.concat([cipher.update(result), cipher.final()]);
55
+ }
56
+
57
+ return result;
58
+ }
59
+
60
+ /**
61
+ * Pad message to multiple of 16 bytes with random data
62
+ * @param {Buffer} message
63
+ * @returns {Buffer}
64
+ */
65
+ function pad(message) {
66
+ const remainder = message.length % BLOCK_SIZE;
67
+ if (remainder === 0) {
68
+ return message;
69
+ }
70
+ const paddingLength = BLOCK_SIZE - remainder;
71
+ const padding = crypto.randomBytes(paddingLength);
72
+ return Buffer.concat([message, padding]);
73
+ }
74
+
75
+ /**
76
+ * Encrypt message using AES-128-CBC
77
+ * @param {Buffer} message - Message to encrypt
78
+ * @param {Buffer} key - 16 bytes encryption key
79
+ * @returns {Buffer} - Encrypted message
80
+ */
81
+ function encrypt(message, key) {
82
+ const paddedMessage = pad(message);
83
+ const cipher = crypto.createCipheriv('aes-128-cbc', key, IV);
84
+ cipher.setAutoPadding(false);
85
+ return Buffer.concat([cipher.update(paddedMessage), cipher.final()]);
86
+ }
87
+
88
+ /**
89
+ * Decrypt message using AES-128-CBC
90
+ * @param {Buffer} message - Encrypted message
91
+ * @param {Buffer} key - 16 bytes encryption key
92
+ * @returns {Buffer} - Decrypted message
93
+ */
94
+ function decrypt(message, key) {
95
+ const decipher = crypto.createDecipheriv('aes-128-cbc', key, IV);
96
+ decipher.setAutoPadding(false);
97
+ const decrypted = Buffer.concat([decipher.update(message), decipher.final()]);
98
+
99
+ // Remove padding by finding actual command length
100
+ if (decrypted.length > 11) {
101
+ const startMark = decrypted.readUInt16BE(0);
102
+ if (startMark === 0xFE00 || startMark === 0xFE01) {
103
+ const dataLen = decrypted.readUInt16BE(4);
104
+ const cmdLen = dataLen + 11; // header(8) + fcs(1) + end(2)
105
+ if (cmdLen <= decrypted.length) {
106
+ return decrypted.subarray(0, cmdLen);
107
+ }
108
+ }
109
+ }
110
+
111
+ return decrypted;
112
+ }
113
+
114
+ /**
115
+ * Parse MAC address string to Buffer
116
+ * @param {string} macString - MAC address like "00:12:4B:32:89:BB" or "00124B3289BB"
117
+ * @returns {Buffer} - 6 bytes
118
+ */
119
+ function parseMacAddress(macString) {
120
+ const hex = macString.replace(/[:-]/g, '');
121
+ if (hex.length !== 12) {
122
+ throw new Error('Invalid MAC address format');
123
+ }
124
+ return Buffer.from(hex, 'hex');
125
+ }
126
+
127
+ module.exports = {
128
+ generateKey,
129
+ encrypt,
130
+ decrypt,
131
+ parseMacAddress,
132
+ IV,
133
+ BLOCK_SIZE,
134
+ };