matterbridge-litetouch 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 +123 -0
- package/README.md +214 -0
- package/config.schema.json +95 -0
- package/dist/commandQueue.d.ts +58 -0
- package/dist/commandQueue.js +122 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +20 -0
- package/dist/litetouchConnection.d.ts +102 -0
- package/dist/litetouchConnection.js +284 -0
- package/dist/platform.d.ts +44 -0
- package/dist/platform.js +313 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# PolyForm Noncommercial License 1.0.0
|
|
2
|
+
|
|
3
|
+
<https://polyformproject.org/licenses/noncommercial/1.0.0>
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
In order to get any license under these terms, you must agree
|
|
8
|
+
to them as both strict obligations and conditions to all
|
|
9
|
+
your licenses.
|
|
10
|
+
|
|
11
|
+
## Copyright License
|
|
12
|
+
|
|
13
|
+
The licensor grants you a copyright license for the software
|
|
14
|
+
to do everything you might do with the software that would
|
|
15
|
+
otherwise infringe the licensor's copyright in it for any
|
|
16
|
+
permitted purpose. However, you may only distribute the software
|
|
17
|
+
according to Distribution License and make changes or new works
|
|
18
|
+
based on the software according to Changes and New Works License.
|
|
19
|
+
|
|
20
|
+
## Distribution License
|
|
21
|
+
|
|
22
|
+
The licensor grants you an additional copyright license to
|
|
23
|
+
distribute copies of the software. Your license to distribute
|
|
24
|
+
covers distributing the software with changes and new works
|
|
25
|
+
permitted by Changes and New Works License.
|
|
26
|
+
|
|
27
|
+
## Notices
|
|
28
|
+
|
|
29
|
+
You must ensure that anyone who gets a copy of any part of
|
|
30
|
+
the software from you also gets a copy of these terms or the
|
|
31
|
+
URL for them above, as well as copies of any plain-text lines
|
|
32
|
+
beginning with `Required Notice:` that the licensor provided
|
|
33
|
+
with the software.
|
|
34
|
+
|
|
35
|
+
## Changes and New Works License
|
|
36
|
+
|
|
37
|
+
The licensor grants you an additional copyright license to
|
|
38
|
+
make changes and new works based on the software for any
|
|
39
|
+
permitted purpose.
|
|
40
|
+
|
|
41
|
+
## Patent License
|
|
42
|
+
|
|
43
|
+
The licensor grants you a patent license for the software that
|
|
44
|
+
covers patent claims the licensor can license, or becomes able
|
|
45
|
+
to license, that you would infringe by using the software.
|
|
46
|
+
|
|
47
|
+
## Noncommercial Purposes
|
|
48
|
+
|
|
49
|
+
Any noncommercial purpose is a permitted purpose.
|
|
50
|
+
|
|
51
|
+
## Personal Uses
|
|
52
|
+
|
|
53
|
+
Personal use for research, experiment, and testing for the
|
|
54
|
+
benefit of public knowledge, personal study, private entertainment,
|
|
55
|
+
hobby projects, amateur pursuits, or religious observance, without
|
|
56
|
+
any anticipated commercial application, is use for a permitted purpose.
|
|
57
|
+
|
|
58
|
+
## Noncommercial Organizations
|
|
59
|
+
|
|
60
|
+
Use by any charitable organization, educational institution,
|
|
61
|
+
public research organization, public safety or health organization,
|
|
62
|
+
environmental protection organization, or government institution
|
|
63
|
+
is use for a permitted purpose regardless of the source of funding
|
|
64
|
+
or obligations resulting from the funding.
|
|
65
|
+
|
|
66
|
+
## Fair Use
|
|
67
|
+
|
|
68
|
+
You may have "fair use" rights for the software under the law.
|
|
69
|
+
These terms do not limit them.
|
|
70
|
+
|
|
71
|
+
## No Other Rights
|
|
72
|
+
|
|
73
|
+
These terms do not allow you to sublicense or transfer any of
|
|
74
|
+
your licenses to anyone else, or prevent the licensor from
|
|
75
|
+
granting licenses to anyone else. These terms do not imply
|
|
76
|
+
any other licenses.
|
|
77
|
+
|
|
78
|
+
## Patent Defense
|
|
79
|
+
|
|
80
|
+
If you make any written claim that the software infringes or
|
|
81
|
+
contributes to infringement of any patent, your patent license
|
|
82
|
+
for the software granted under these terms ends immediately.
|
|
83
|
+
If your company makes such a claim, your patent license ends
|
|
84
|
+
immediately for work on behalf of your company.
|
|
85
|
+
|
|
86
|
+
## Violations
|
|
87
|
+
|
|
88
|
+
The first time you are notified in writing that you have
|
|
89
|
+
violated any of these terms, or done anything with the software
|
|
90
|
+
not covered by your licenses, your licenses can nonetheless
|
|
91
|
+
continue if you come into full compliance with these terms,
|
|
92
|
+
and take practical steps to correct past violations, within
|
|
93
|
+
32 days of receiving notice. Otherwise, all your licenses
|
|
94
|
+
end immediately.
|
|
95
|
+
|
|
96
|
+
## No Liability
|
|
97
|
+
|
|
98
|
+
***As far as the law allows, the software comes as is, without
|
|
99
|
+
any warranty or condition, and the licensor will not be liable
|
|
100
|
+
to you for any damages arising out of these terms or the use
|
|
101
|
+
or nature of the software, under any kind of legal claim.***
|
|
102
|
+
|
|
103
|
+
## Definitions
|
|
104
|
+
|
|
105
|
+
The **licensor** is the individual or entity offering these
|
|
106
|
+
terms, and the **software** is the software the licensor makes
|
|
107
|
+
available under these terms.
|
|
108
|
+
|
|
109
|
+
**You** refers to the individual or entity agreeing to these terms.
|
|
110
|
+
|
|
111
|
+
**Your company** is any legal entity, sole proprietorship,
|
|
112
|
+
or other kind of organization that you work for, plus all
|
|
113
|
+
organizations that have control over, are under the control of,
|
|
114
|
+
or are under common control with that organization. **Control**
|
|
115
|
+
means ownership of substantially all the assets of an entity,
|
|
116
|
+
or the power to direct its management and policies by vote,
|
|
117
|
+
contract, or otherwise. Control can be direct or indirect.
|
|
118
|
+
|
|
119
|
+
**Your licenses** are all the licenses granted to you for
|
|
120
|
+
the software under these terms.
|
|
121
|
+
|
|
122
|
+
**Use** means anything you do with the software requiring
|
|
123
|
+
one of your licenses.
|
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Matterbridge Litetouch 2000
|
|
2
|
+
|
|
3
|
+
A [Matterbridge](https://github.com/Luligu/matterbridge) plugin that exposes Litetouch 2000 lighting loads as Matter devices.
|
|
4
|
+
|
|
5
|
+
This allows you to control your Litetouch lighting system from any Matter-compatible ecosystem:
|
|
6
|
+
- Apple Home (HomeKit)
|
|
7
|
+
- Google Home
|
|
8
|
+
- Amazon Alexa
|
|
9
|
+
- Home Assistant
|
|
10
|
+
- Hubitat Elevation
|
|
11
|
+
- And more...
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Dimmers**: Full brightness control with on/off and level adjustment
|
|
16
|
+
- **Switches**: On/off control for relay loads
|
|
17
|
+
- **Status Polling**: Automatically polls loads to detect changes from wall keypads
|
|
18
|
+
- **Priority Queue**: User commands are prioritized over polling for responsive control
|
|
19
|
+
- **Apple Home Optimized**: Includes workaround for Apple Home's command sequencing to prevent brightness flash when turning on dimmers
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- Raspberry Pi (or similar Linux system) with Node.js 18+
|
|
24
|
+
- USB-to-serial adapter connected to your Litetouch CCU
|
|
25
|
+
- Litetouch 2000 with Standard or Compact CCU (not compatible with 5000LC)
|
|
26
|
+
|
|
27
|
+
## Hardware Setup
|
|
28
|
+
|
|
29
|
+
### Serial Cable
|
|
30
|
+
|
|
31
|
+
You need a serial cable between the USB-serial adapter and the Litetouch CCU. Use an RJ45-to-RS232 adapter with the following pinout:
|
|
32
|
+
|
|
33
|
+
| RJ45 Pin | RS232 Signal |
|
|
34
|
+
|----------|--------------|
|
|
35
|
+
| 2 | TX |
|
|
36
|
+
| 3 | RX |
|
|
37
|
+
| 5 | GND |
|
|
38
|
+
| 7 & 8 | Bridge together on CCU side only |
|
|
39
|
+
|
|
40
|
+
Bridging pins 7 & 8 on the CCU side enables polling mode.
|
|
41
|
+
|
|
42
|
+
### Identify Serial Port
|
|
43
|
+
|
|
44
|
+
After connecting the USB-serial adapter, find the device path:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ls -la /dev/ttyUSB*
|
|
48
|
+
# or
|
|
49
|
+
ls -la /dev/serial/by-id/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
### 1. Install Matterbridge
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
sudo npm install -g matterbridge
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Install This Plugin
|
|
61
|
+
|
|
62
|
+
Install globally alongside Matterbridge:
|
|
63
|
+
```bash
|
|
64
|
+
# Clone the repository
|
|
65
|
+
git clone https://github.com/signal15/matterbridge-litetouch.git
|
|
66
|
+
cd matterbridge-litetouch
|
|
67
|
+
|
|
68
|
+
# Install dependencies and build
|
|
69
|
+
npm install
|
|
70
|
+
npm run build
|
|
71
|
+
|
|
72
|
+
# Install globally (must use sudo since matterbridge runs as root)
|
|
73
|
+
sudo npm install -g .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then register with Matterbridge:
|
|
77
|
+
```bash
|
|
78
|
+
sudo matterbridge -add matterbridge-litetouch
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Updating the Plugin
|
|
82
|
+
|
|
83
|
+
After making changes or pulling updates:
|
|
84
|
+
```bash
|
|
85
|
+
cd matterbridge-litetouch
|
|
86
|
+
npm run build
|
|
87
|
+
sudo cp -r dist/* /usr/lib/node_modules/matterbridge-litetouch/dist/
|
|
88
|
+
sudo systemctl restart matterbridge
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 3. Configure the Plugin
|
|
92
|
+
|
|
93
|
+
**Note:** The Matterbridge web UI does not support the array-of-objects format needed for load configuration. Edit the config file directly:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
sudo nano /root/.matterbridge/matterbridge-litetouch.config.json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
After editing, restart matterbridge:
|
|
100
|
+
```bash
|
|
101
|
+
sudo systemctl restart matterbridge
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Configuration options:
|
|
105
|
+
|
|
106
|
+
- **Serial Port**: Path to your USB-serial device (e.g., `/dev/ttyUSB0`)
|
|
107
|
+
- **Baud Rate**: Usually 9600 (default)
|
|
108
|
+
- **Polling Interval**: Time between status polls in milliseconds (default: 2000)
|
|
109
|
+
- **Dimmers**: List of dimmer load addresses with friendly names
|
|
110
|
+
- **Switches**: List of relay load addresses with friendly names
|
|
111
|
+
|
|
112
|
+
### Example Configuration
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"name": "Litetouch 2000",
|
|
117
|
+
"serialPort": "/dev/ttyUSB0",
|
|
118
|
+
"baudRate": 9600,
|
|
119
|
+
"pollingInterval": 2000,
|
|
120
|
+
"commandTimeout": 1000,
|
|
121
|
+
"dimmers": [
|
|
122
|
+
{ "address": "01-1", "name": "Living Room Main" },
|
|
123
|
+
{ "address": "01-2", "name": "Living Room Accent" },
|
|
124
|
+
{ "address": "02-1", "name": "Kitchen" },
|
|
125
|
+
{ "address": "03-4", "name": "Master Bedroom" }
|
|
126
|
+
],
|
|
127
|
+
"switches": [
|
|
128
|
+
{ "address": "05-1", "name": "Garage" },
|
|
129
|
+
{ "address": "05-2", "name": "Porch Light" }
|
|
130
|
+
],
|
|
131
|
+
"debug": false
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Load Addressing
|
|
136
|
+
|
|
137
|
+
Litetouch loads are addressed as `MM-O` where:
|
|
138
|
+
- `MM` = Module number (1-99)
|
|
139
|
+
- `O` = Output number on that module (1-6 typically)
|
|
140
|
+
|
|
141
|
+
Example addresses: `01-1`, `03-4`, `10-6`
|
|
142
|
+
|
|
143
|
+
To find your load addresses, refer to your Litetouch programming documentation or use the Litetouch Designer software.
|
|
144
|
+
|
|
145
|
+
## Starting Matterbridge
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
matterbridge
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
For production use, set up Matterbridge as a systemd service:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
sudo matterbridge -service install
|
|
155
|
+
sudo systemctl enable matterbridge
|
|
156
|
+
sudo systemctl start matterbridge
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Commissioning to Your Ecosystem
|
|
160
|
+
|
|
161
|
+
1. Start Matterbridge and wait for it to initialize
|
|
162
|
+
2. Open the Matterbridge web UI and note the QR code or pairing code
|
|
163
|
+
3. In your Matter controller app (Apple Home, Google Home, etc.):
|
|
164
|
+
- Choose "Add Accessory" or "Set up device"
|
|
165
|
+
- Scan the QR code or enter the pairing code
|
|
166
|
+
4. The Litetouch loads will appear as individual light/switch devices
|
|
167
|
+
|
|
168
|
+
### Hubitat-Specific Instructions
|
|
169
|
+
|
|
170
|
+
1. Go to **Devices** → **Add Device** → **Matter**
|
|
171
|
+
2. Use the Hubitat mobile app to scan the QR code
|
|
172
|
+
3. Devices will appear automatically in your device list
|
|
173
|
+
|
|
174
|
+
## Troubleshooting
|
|
175
|
+
|
|
176
|
+
### Serial Port Access
|
|
177
|
+
|
|
178
|
+
If you get permission errors, add your user to the dialout group:
|
|
179
|
+
```bash
|
|
180
|
+
sudo usermod -a -G dialout $USER
|
|
181
|
+
# Log out and back in for changes to take effect
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Debug Logging
|
|
185
|
+
|
|
186
|
+
Enable debug mode in the plugin configuration to see detailed serial communication logs.
|
|
187
|
+
|
|
188
|
+
### Common Issues
|
|
189
|
+
|
|
190
|
+
| Issue | Solution |
|
|
191
|
+
|-------|----------|
|
|
192
|
+
| "Serial port not found" | Check USB connection and port path |
|
|
193
|
+
| "Permission denied" | Add user to dialout group |
|
|
194
|
+
| "No response from CCU" | Verify cable pinout and baud rate |
|
|
195
|
+
| "Devices not updating" | Check that polling is running in logs |
|
|
196
|
+
|
|
197
|
+
## Protocol Reference
|
|
198
|
+
|
|
199
|
+
The Litetouch 2000 uses an ASCII protocol over RS-232:
|
|
200
|
+
|
|
201
|
+
- **Query load**: ` 18 MM-O` → Response: `R 18 MM-O LLL`
|
|
202
|
+
- **Set level**: ` 10 MM-O LLL`
|
|
203
|
+
- For relays: `LLL` = `000` (off) or `001` (on)
|
|
204
|
+
- For dimmers: `LLL` = `000`-`250`
|
|
205
|
+
|
|
206
|
+
Commands are terminated with carriage return (`\r`).
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
[PolyForm Noncommercial 1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0/) - Free for personal and noncommercial use.
|
|
211
|
+
|
|
212
|
+
## Credits
|
|
213
|
+
|
|
214
|
+
Based on the original [Vera Litetouch plugin](https://github.com/signal15/vera-litetouch-2000) by signal15.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Litetouch 2000 Configuration",
|
|
3
|
+
"description": "Configure your Litetouch 2000 lighting system connection and loads",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"name": {
|
|
7
|
+
"title": "Plugin Name",
|
|
8
|
+
"description": "Name displayed in Matterbridge",
|
|
9
|
+
"type": "string",
|
|
10
|
+
"default": "Litetouch 2000"
|
|
11
|
+
},
|
|
12
|
+
"serialPort": {
|
|
13
|
+
"title": "Serial Port",
|
|
14
|
+
"description": "Path to the USB-serial device (e.g., /dev/ttyUSB0)",
|
|
15
|
+
"type": "string",
|
|
16
|
+
"default": "/dev/ttyUSB0"
|
|
17
|
+
},
|
|
18
|
+
"baudRate": {
|
|
19
|
+
"title": "Baud Rate",
|
|
20
|
+
"description": "Serial port baud rate",
|
|
21
|
+
"type": "integer",
|
|
22
|
+
"default": 9600,
|
|
23
|
+
"enum": [9600, 19200, 38400, 57600, 115200]
|
|
24
|
+
},
|
|
25
|
+
"pollingInterval": {
|
|
26
|
+
"title": "Polling Interval (ms)",
|
|
27
|
+
"description": "Time between polling each load for status updates (minimum 500ms)",
|
|
28
|
+
"type": "integer",
|
|
29
|
+
"default": 2000,
|
|
30
|
+
"minimum": 500,
|
|
31
|
+
"maximum": 10000
|
|
32
|
+
},
|
|
33
|
+
"commandTimeout": {
|
|
34
|
+
"title": "Command Timeout (ms)",
|
|
35
|
+
"description": "How long to wait for a response before timing out",
|
|
36
|
+
"type": "integer",
|
|
37
|
+
"default": 1000,
|
|
38
|
+
"minimum": 250,
|
|
39
|
+
"maximum": 5000
|
|
40
|
+
},
|
|
41
|
+
"dimmers": {
|
|
42
|
+
"title": "Dimmer Loads",
|
|
43
|
+
"description": "List of dimmer module-output addresses (e.g., 01-1, 03-4)",
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"properties": {
|
|
48
|
+
"address": {
|
|
49
|
+
"title": "Address",
|
|
50
|
+
"description": "Module-output address (e.g., 01-1)",
|
|
51
|
+
"type": "string",
|
|
52
|
+
"pattern": "^\\d{1,2}-\\d{1,2}$"
|
|
53
|
+
},
|
|
54
|
+
"name": {
|
|
55
|
+
"title": "Name",
|
|
56
|
+
"description": "Friendly name for this load",
|
|
57
|
+
"type": "string"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"required": ["address", "name"]
|
|
61
|
+
},
|
|
62
|
+
"default": []
|
|
63
|
+
},
|
|
64
|
+
"switches": {
|
|
65
|
+
"title": "Relay/Switch Loads",
|
|
66
|
+
"description": "List of relay/switch module-output addresses (e.g., 02-3, 05-6)",
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"properties": {
|
|
71
|
+
"address": {
|
|
72
|
+
"title": "Address",
|
|
73
|
+
"description": "Module-output address (e.g., 02-3)",
|
|
74
|
+
"type": "string",
|
|
75
|
+
"pattern": "^\\d{1,2}-\\d{1,2}$"
|
|
76
|
+
},
|
|
77
|
+
"name": {
|
|
78
|
+
"title": "Name",
|
|
79
|
+
"description": "Friendly name for this load",
|
|
80
|
+
"type": "string"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"required": ["address", "name"]
|
|
84
|
+
},
|
|
85
|
+
"default": []
|
|
86
|
+
},
|
|
87
|
+
"debug": {
|
|
88
|
+
"title": "Debug Logging",
|
|
89
|
+
"description": "Enable verbose debug logging",
|
|
90
|
+
"type": "boolean",
|
|
91
|
+
"default": false
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"required": ["serialPort", "dimmers", "switches"]
|
|
95
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command queue with priority handling for Litetouch serial communication.
|
|
3
|
+
* User-initiated commands (on/off, dimming) take priority over background polling.
|
|
4
|
+
*/
|
|
5
|
+
export declare enum CommandPriority {
|
|
6
|
+
HIGH = 0,// User-initiated commands (on/off, set level)
|
|
7
|
+
NORMAL = 1
|
|
8
|
+
}
|
|
9
|
+
export interface QueuedCommand {
|
|
10
|
+
command: string;
|
|
11
|
+
priority: CommandPriority;
|
|
12
|
+
resolve: (response: string | null) => void;
|
|
13
|
+
reject: (error: Error) => void;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class CommandQueue {
|
|
17
|
+
private queue;
|
|
18
|
+
private processing;
|
|
19
|
+
private processCallback;
|
|
20
|
+
/**
|
|
21
|
+
* Set the callback that processes commands (sends to serial port)
|
|
22
|
+
*/
|
|
23
|
+
setProcessor(callback: (cmd: QueuedCommand) => Promise<string | null>): void;
|
|
24
|
+
/**
|
|
25
|
+
* Add a command to the queue with specified priority.
|
|
26
|
+
* High priority commands are inserted before normal priority ones.
|
|
27
|
+
*/
|
|
28
|
+
enqueue(command: string, priority?: CommandPriority): Promise<string | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Add a high-priority command (user action)
|
|
31
|
+
*/
|
|
32
|
+
enqueueHighPriority(command: string): Promise<string | null>;
|
|
33
|
+
/**
|
|
34
|
+
* Add a normal-priority command (polling)
|
|
35
|
+
*/
|
|
36
|
+
enqueuePolling(command: string): Promise<string | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Process the next command in the queue
|
|
39
|
+
*/
|
|
40
|
+
private processNext;
|
|
41
|
+
/**
|
|
42
|
+
* Get the current queue length
|
|
43
|
+
*/
|
|
44
|
+
get length(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Check if currently processing a command
|
|
47
|
+
*/
|
|
48
|
+
get isProcessing(): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Clear all pending commands (used during shutdown)
|
|
51
|
+
*/
|
|
52
|
+
clear(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Remove all polling commands from the queue (useful when user commands come in)
|
|
55
|
+
*/
|
|
56
|
+
clearPollingCommands(): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=commandQueue.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command queue with priority handling for Litetouch serial communication.
|
|
3
|
+
* User-initiated commands (on/off, dimming) take priority over background polling.
|
|
4
|
+
*/
|
|
5
|
+
export var CommandPriority;
|
|
6
|
+
(function (CommandPriority) {
|
|
7
|
+
CommandPriority[CommandPriority["HIGH"] = 0] = "HIGH";
|
|
8
|
+
CommandPriority[CommandPriority["NORMAL"] = 1] = "NORMAL";
|
|
9
|
+
})(CommandPriority || (CommandPriority = {}));
|
|
10
|
+
export class CommandQueue {
|
|
11
|
+
queue = [];
|
|
12
|
+
processing = false;
|
|
13
|
+
processCallback = null;
|
|
14
|
+
/**
|
|
15
|
+
* Set the callback that processes commands (sends to serial port)
|
|
16
|
+
*/
|
|
17
|
+
setProcessor(callback) {
|
|
18
|
+
this.processCallback = callback;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Add a command to the queue with specified priority.
|
|
22
|
+
* High priority commands are inserted before normal priority ones.
|
|
23
|
+
*/
|
|
24
|
+
async enqueue(command, priority = CommandPriority.NORMAL) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const queuedCmd = {
|
|
27
|
+
command,
|
|
28
|
+
priority,
|
|
29
|
+
resolve,
|
|
30
|
+
reject,
|
|
31
|
+
timestamp: Date.now(),
|
|
32
|
+
};
|
|
33
|
+
// Insert based on priority
|
|
34
|
+
if (priority === CommandPriority.HIGH) {
|
|
35
|
+
// Find the first normal priority command and insert before it
|
|
36
|
+
const insertIndex = this.queue.findIndex(cmd => cmd.priority === CommandPriority.NORMAL);
|
|
37
|
+
if (insertIndex === -1) {
|
|
38
|
+
this.queue.push(queuedCmd);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.queue.splice(insertIndex, 0, queuedCmd);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.queue.push(queuedCmd);
|
|
46
|
+
}
|
|
47
|
+
// Start processing if not already running
|
|
48
|
+
this.processNext();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Add a high-priority command (user action)
|
|
53
|
+
*/
|
|
54
|
+
async enqueueHighPriority(command) {
|
|
55
|
+
return this.enqueue(command, CommandPriority.HIGH);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Add a normal-priority command (polling)
|
|
59
|
+
*/
|
|
60
|
+
async enqueuePolling(command) {
|
|
61
|
+
return this.enqueue(command, CommandPriority.NORMAL);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Process the next command in the queue
|
|
65
|
+
*/
|
|
66
|
+
async processNext() {
|
|
67
|
+
if (this.processing || this.queue.length === 0 || !this.processCallback) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.processing = true;
|
|
71
|
+
const cmd = this.queue.shift();
|
|
72
|
+
try {
|
|
73
|
+
const response = await this.processCallback(cmd);
|
|
74
|
+
cmd.resolve(response);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
cmd.reject(error instanceof Error ? error : new Error(String(error)));
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
this.processing = false;
|
|
81
|
+
// Process next command if queue is not empty
|
|
82
|
+
if (this.queue.length > 0) {
|
|
83
|
+
// Use setImmediate to prevent stack overflow on large queues
|
|
84
|
+
setImmediate(() => this.processNext());
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the current queue length
|
|
90
|
+
*/
|
|
91
|
+
get length() {
|
|
92
|
+
return this.queue.length;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if currently processing a command
|
|
96
|
+
*/
|
|
97
|
+
get isProcessing() {
|
|
98
|
+
return this.processing;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Clear all pending commands (used during shutdown)
|
|
102
|
+
*/
|
|
103
|
+
clear() {
|
|
104
|
+
for (const cmd of this.queue) {
|
|
105
|
+
cmd.reject(new Error('Queue cleared'));
|
|
106
|
+
}
|
|
107
|
+
this.queue = [];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Remove all polling commands from the queue (useful when user commands come in)
|
|
111
|
+
*/
|
|
112
|
+
clearPollingCommands() {
|
|
113
|
+
this.queue = this.queue.filter(cmd => {
|
|
114
|
+
if (cmd.priority === CommandPriority.NORMAL) {
|
|
115
|
+
cmd.reject(new Error('Polling command cancelled'));
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=commandQueue.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matterbridge Plugin for Litetouch 2000 Lighting System
|
|
3
|
+
*
|
|
4
|
+
* Exposes Litetouch dimmers and switches as Matter devices that can be
|
|
5
|
+
* controlled from any Matter-compatible ecosystem (Apple Home, Google Home,
|
|
6
|
+
* Amazon Alexa, Home Assistant, Hubitat, etc.)
|
|
7
|
+
*/
|
|
8
|
+
import { MatterbridgeDynamicPlatform, PlatformConfig, PlatformMatterbridge } from 'matterbridge';
|
|
9
|
+
import { AnsiLogger } from 'matterbridge/logger';
|
|
10
|
+
/**
|
|
11
|
+
* Factory function to create the Litetouch platform
|
|
12
|
+
* This is the entry point for Matterbridge
|
|
13
|
+
*/
|
|
14
|
+
export default function createPlatform(matterbridge: PlatformMatterbridge, log: AnsiLogger, config: PlatformConfig): MatterbridgeDynamicPlatform;
|
|
15
|
+
export { LitetouchPlatform } from './platform.js';
|
|
16
|
+
export { LitetouchConnection } from './litetouchConnection.js';
|
|
17
|
+
export { CommandQueue, CommandPriority } from './commandQueue.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matterbridge Plugin for Litetouch 2000 Lighting System
|
|
3
|
+
*
|
|
4
|
+
* Exposes Litetouch dimmers and switches as Matter devices that can be
|
|
5
|
+
* controlled from any Matter-compatible ecosystem (Apple Home, Google Home,
|
|
6
|
+
* Amazon Alexa, Home Assistant, Hubitat, etc.)
|
|
7
|
+
*/
|
|
8
|
+
import { LitetouchPlatform } from './platform.js';
|
|
9
|
+
/**
|
|
10
|
+
* Factory function to create the Litetouch platform
|
|
11
|
+
* This is the entry point for Matterbridge
|
|
12
|
+
*/
|
|
13
|
+
export default function createPlatform(matterbridge, log, config) {
|
|
14
|
+
return new LitetouchPlatform(matterbridge, log, config);
|
|
15
|
+
}
|
|
16
|
+
// Re-export the platform class for advanced usage
|
|
17
|
+
export { LitetouchPlatform } from './platform.js';
|
|
18
|
+
export { LitetouchConnection } from './litetouchConnection.js';
|
|
19
|
+
export { CommandQueue, CommandPriority } from './commandQueue.js';
|
|
20
|
+
//# sourceMappingURL=index.js.map
|