hello-lights 0.3.4 → 0.3.6
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/README.md +98 -1
- package/lib/cli/serve/index.js +37 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,13 +10,37 @@ Works with a [Cleware USB traffic light](http://www.cleware.info/data/usbtischam
|
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
|
+
### As a CLI (global)
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
$ npm install -g hello-lights
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### As a library
|
|
20
|
+
|
|
13
21
|
```sh
|
|
14
22
|
$ npm install hello-lights --save
|
|
15
23
|
```
|
|
16
24
|
|
|
17
25
|
## Usage
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
### CLI
|
|
28
|
+
|
|
29
|
+
Install globally and run `hello-lights` from the command line:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
$ hello-lights exec bounce 300 # execute a command
|
|
33
|
+
$ hello-lights exec-file ./cmds.clj # execute commands from a file
|
|
34
|
+
$ hello-lights repl # start an interactive REPL
|
|
35
|
+
$ hello-lights serve # start the HTTP server on port 9000
|
|
36
|
+
$ hello-lights serve --port 3000 # start the HTTP server on a custom port
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use `--help` for the full list of options, including `--serial-num` to target a specific device and `--selector multi` to control multiple traffic lights at once.
|
|
40
|
+
|
|
41
|
+
### Library
|
|
42
|
+
|
|
43
|
+
Use `hello-lights` as a library in your Node.js project:
|
|
20
44
|
|
|
21
45
|
```js
|
|
22
46
|
const {Commander} = require('hello-lights');
|
|
@@ -75,6 +99,79 @@ $ npm run cli -- serve --port 3000 # start the HTTP server on a custom port
|
|
|
75
99
|
|
|
76
100
|
Use `--help` for the full list of options, including `--serial-num` to target a specific device and `--selector multi` to control multiple traffic lights at once.
|
|
77
101
|
|
|
102
|
+
## HTTP Server REST API
|
|
103
|
+
|
|
104
|
+
The `serve` command starts an HTTP server that exposes the Commander interface as a REST API. By default it listens on port 9000.
|
|
105
|
+
|
|
106
|
+
| Endpoint | Method | Body | Response |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `/run` | POST | Command string (plain text) | 202 Accepted |
|
|
109
|
+
| `/run?reset=true` | POST | Command string (plain text) | 202 Accepted (resets lights first) |
|
|
110
|
+
| `/cancel` | POST | — | 200 OK |
|
|
111
|
+
| `/definitions` | POST | Definition string (plain text) | 202 Accepted |
|
|
112
|
+
| `/commands` | GET | — | 200 + JSON array of command names |
|
|
113
|
+
| `/commands/:name` | GET | — | 200 + help text (`text/x-ansi`) or 404 |
|
|
114
|
+
| `/info` | GET | — | 200 + JSON array of `{ serialNum, status }` |
|
|
115
|
+
|
|
116
|
+
Examples:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
$ curl -X POST http://localhost:9000/run -d 'blink 3 green 300'
|
|
120
|
+
$ curl -X POST http://localhost:9000/run?reset=true -d 'twinkle red 400'
|
|
121
|
+
$ curl -X POST http://localhost:9000/cancel
|
|
122
|
+
$ curl -X POST http://localhost:9000/definitions -d '(def foo (blink 1 green 300))'
|
|
123
|
+
$ curl http://localhost:9000/commands
|
|
124
|
+
$ curl http://localhost:9000/commands/turn
|
|
125
|
+
$ curl http://localhost:9000/info
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The server also serves a browser demo at the root URL (`/`).
|
|
129
|
+
|
|
130
|
+
## Running as a Linux Service
|
|
131
|
+
|
|
132
|
+
You can set up `hello-lights serve` to run automatically as a systemd user service on Linux.
|
|
133
|
+
|
|
134
|
+
Create the service file at `~/.config/systemd/user/hello-lights.service`:
|
|
135
|
+
|
|
136
|
+
```ini
|
|
137
|
+
[Unit]
|
|
138
|
+
Description=Hello Lights Server
|
|
139
|
+
After=network.target
|
|
140
|
+
|
|
141
|
+
[Service]
|
|
142
|
+
Type=simple
|
|
143
|
+
Environment=PATH=/path/to/node/bin:/usr/bin:/bin
|
|
144
|
+
ExecStart=/path/to/node/bin/hello-lights serve
|
|
145
|
+
Restart=on-failure
|
|
146
|
+
RestartSec=5
|
|
147
|
+
|
|
148
|
+
[Install]
|
|
149
|
+
WantedBy=default.target
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Replace `/path/to/node/bin` with the directory containing your `node` and `hello-lights` binaries (e.g. `~/.nvm/versions/node/v24.13.1/bin`). The `Environment=PATH=...` line is needed because systemd doesn't load your shell profile, so tools installed via nvm won't be found otherwise.
|
|
153
|
+
|
|
154
|
+
Then enable and start the service:
|
|
155
|
+
|
|
156
|
+
```sh
|
|
157
|
+
$ systemctl --user daemon-reload
|
|
158
|
+
$ systemctl --user enable hello-lights # start automatically on login
|
|
159
|
+
$ systemctl --user start hello-lights # start now
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
To start the service on boot (without needing to log in), enable lingering for your user:
|
|
163
|
+
|
|
164
|
+
```sh
|
|
165
|
+
$ sudo loginctl enable-linger $USER
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Check status and logs:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
$ systemctl --user status hello-lights
|
|
172
|
+
$ journalctl --user -u hello-lights -f # follow logs
|
|
173
|
+
```
|
|
174
|
+
|
|
78
175
|
## CI
|
|
79
176
|
|
|
80
177
|
The CI workflow runs on every push and pull request to `master`. It has three jobs:
|
package/lib/cli/serve/index.js
CHANGED
|
@@ -10,14 +10,44 @@ function createApp(commander) {
|
|
|
10
10
|
const app = express();
|
|
11
11
|
|
|
12
12
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
13
|
+
app.use(express.text({type: '*/*'}));
|
|
14
|
+
|
|
13
15
|
app.post('/run', (req, res) => {
|
|
14
|
-
let
|
|
15
|
-
req.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
let reset = req.query.reset === 'true';
|
|
17
|
+
commander.run(req.body || '', reset);
|
|
18
|
+
res.sendStatus(202);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
app.post('/cancel', (req, res) => {
|
|
22
|
+
commander.cancel();
|
|
23
|
+
res.sendStatus(200);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
app.post('/definitions', (req, res) => {
|
|
27
|
+
commander.runDefinitions(req.body || '');
|
|
28
|
+
res.sendStatus(202);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
app.get('/commands', (req, res) => {
|
|
32
|
+
res.json(commander.commandNames);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
app.get('/commands/:name', (req, res) => {
|
|
36
|
+
let command = commander.interpreter.lookup(req.params.name);
|
|
37
|
+
if (!command) {
|
|
38
|
+
res.status(404).send(`Command not found: "${req.params.name}"`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let helpText = commander.formatter.format(command.meta);
|
|
42
|
+
res.type('text/x-ansi').send(helpText);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
app.get('/info', (req, res) => {
|
|
46
|
+
if (commander.manager) {
|
|
47
|
+
res.json(commander.manager.info());
|
|
48
|
+
} else {
|
|
49
|
+
res.json([]);
|
|
50
|
+
}
|
|
21
51
|
});
|
|
22
52
|
|
|
23
53
|
return app;
|