vestaboard-tools 0.1.0__tar.gz
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.
- vestaboard_tools-0.1.0/LICENSE +21 -0
- vestaboard_tools-0.1.0/PKG-INFO +393 -0
- vestaboard_tools-0.1.0/README.md +373 -0
- vestaboard_tools-0.1.0/pyproject.toml +41 -0
- vestaboard_tools-0.1.0/setup.cfg +4 -0
- vestaboard_tools-0.1.0/vesta.py +1396 -0
- vestaboard_tools-0.1.0/vestaboard_tools.egg-info/PKG-INFO +393 -0
- vestaboard_tools-0.1.0/vestaboard_tools.egg-info/SOURCES.txt +10 -0
- vestaboard_tools-0.1.0/vestaboard_tools.egg-info/dependency_links.txt +1 -0
- vestaboard_tools-0.1.0/vestaboard_tools.egg-info/entry_points.txt +2 -0
- vestaboard_tools-0.1.0/vestaboard_tools.egg-info/requires.txt +1 -0
- vestaboard_tools-0.1.0/vestaboard_tools.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Corey Bertram
|
|
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.
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vestaboard-tools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Formatter, previewer, and publisher for Vestaboard devices
|
|
5
|
+
Author: Corey Bertram
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/q/vesta
|
|
8
|
+
Project-URL: Repository, https://github.com/q/vesta
|
|
9
|
+
Keywords: vestaboard,led,display,board,formatter
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: requests>=2.28
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# vesta
|
|
22
|
+
|
|
23
|
+
A small Python formatter / previewer / publisher for Vestaboard devices.
|
|
24
|
+
|
|
25
|
+
**semantic input → board layout → terminal preview → optional publish**
|
|
26
|
+
|
|
27
|
+
<img src="docs/example.png" width="600" alt="Terminal preview showing a metrics board with color indicators and explain output" />
|
|
28
|
+
|
|
29
|
+
## What it does
|
|
30
|
+
|
|
31
|
+
- Formats structured data (JSON, CSV, plain text) for Vestaboard
|
|
32
|
+
- Supports multiple device profiles:
|
|
33
|
+
- flagship: **6 × 22**
|
|
34
|
+
- note: **3 × 15**
|
|
35
|
+
- Previews output in the terminal before sending
|
|
36
|
+
- Publishes via Vestaboard Cloud API or Local API
|
|
37
|
+
|
|
38
|
+
## Templates
|
|
39
|
+
|
|
40
|
+
| Template | Input | Behaviour |
|
|
41
|
+
|----------|-------|-----------|
|
|
42
|
+
| `text` | string | Wrapped and centered text |
|
|
43
|
+
| `kv` | JSON object | Key / value rows, no formatting |
|
|
44
|
+
| `data` | JSON object or array | Label/value rows (object) or columnar table (array). Applies suffix formatting and color indicators. |
|
|
45
|
+
| `auto` | any | Picks the best renderer based on input shape (default) |
|
|
46
|
+
|
|
47
|
+
`metrics` and `table` are accepted as aliases for `data`.
|
|
48
|
+
|
|
49
|
+
CSV is auto-detected — no `--template` flag needed.
|
|
50
|
+
|
|
51
|
+
## Key suffixes
|
|
52
|
+
|
|
53
|
+
Field name suffixes control formatting automatically. The suffix is stripped from the label on the board.
|
|
54
|
+
|
|
55
|
+
| Suffix | Effect |
|
|
56
|
+
|--------|--------|
|
|
57
|
+
| `_pct` / `_percent` | formats value as `3.2%`, adds color tone indicator |
|
|
58
|
+
| `_curr` | formats value as `$84.2K` |
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"revenue_curr": 84210.50,
|
|
63
|
+
"sessions": 10823,
|
|
64
|
+
"growth_pct": 12.4
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Renders as:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
REVENUE $84.2K
|
|
72
|
+
SESSIONS 10.8K
|
|
73
|
+
GROWTH 12.4% ██
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Color indicators
|
|
77
|
+
|
|
78
|
+
Color is driven by semantic tone, not raw cell placement. The trailing colored tile is added automatically when a tone can be determined.
|
|
79
|
+
|
|
80
|
+
**Auto-detection:** inferred when a field name contains `pct`, `percent`, `change`, `delta`, or `diff` and the value is numeric:
|
|
81
|
+
- positive → green
|
|
82
|
+
- negative → red
|
|
83
|
+
- zero → white
|
|
84
|
+
|
|
85
|
+
**Explicit tone:** set via `_style`:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"score": 91.2,
|
|
90
|
+
"_style": { "score": "good" }
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Accepted tone names: `good`, `bad`, `warn`, `info`, `neutral`, `muted`,
|
|
95
|
+
or a direct color: `green`, `red`, `yellow`, `blue`, `white`, `black`, `violet`, `orange`.
|
|
96
|
+
|
|
97
|
+
**Range-based tone:** specify `good` and `bad` thresholds for a 4-step gradient. Direction is implicit — wherever `good` sits numerically is the green end:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"bounce_rate": 68.4,
|
|
102
|
+
"_style": { "bounce_rate": {"good": 30, "bad": 80} }
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Zone | Color | Position |
|
|
107
|
+
|------|-------|----------|
|
|
108
|
+
| 1st quarter | green | 0–25% toward bad |
|
|
109
|
+
| 2nd quarter | yellow | 25–50% |
|
|
110
|
+
| 3rd quarter | orange | 50–75% |
|
|
111
|
+
| 4th quarter | red | 75–100% (and beyond) |
|
|
112
|
+
|
|
113
|
+
`_style` and other `_`-prefixed keys are never shown on the board.
|
|
114
|
+
|
|
115
|
+
Use `--explain` to see which fields got indicators and why:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
cat metrics.json | vesta render --template data --preview-only --explain
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Layout flags
|
|
122
|
+
|
|
123
|
+
**`--title TEXT`**
|
|
124
|
+
|
|
125
|
+
Adds a title row at the top with colored tile bookends. Tries 2 tiles each side, falls back to 1 if the text is long.
|
|
126
|
+
|
|
127
|
+
**`--title-color COLOR[,COLOR,COLOR]|none`**
|
|
128
|
+
|
|
129
|
+
Color of the bookend tiles. Defaults to `white`. Pass `none` for a plain centered title with no tiles. Pass up to 3 comma-separated colors to use multiple tiles — the right side mirrors the left: `--title-color red,blue,orange` places `red blue orange` on the left and `orange blue red` on the right. Falls back through fewer tiles if the title text is too long to fit.
|
|
130
|
+
|
|
131
|
+
**`--subtitle TEXT|time`**
|
|
132
|
+
|
|
133
|
+
Optional second row below the title, with a single tile bookend on each side (same color as title). Use the special value `time` to insert the current time. You can also embed the subtitle directly in the title using a newline — `--title $'Weather\nSan Francisco'` — and the second line becomes the subtitle automatically (explicit `--subtitle` takes precedence).
|
|
134
|
+
|
|
135
|
+
**`--separator [PATTERN]`**
|
|
136
|
+
|
|
137
|
+
Adds a full-width row of colored tiles below the title block (after subtitle if present). Used alone, defaults to solid white. Accepts:
|
|
138
|
+
|
|
139
|
+
| Pattern | Result |
|
|
140
|
+
|---------|--------|
|
|
141
|
+
| *(omitted)* | solid white |
|
|
142
|
+
| `white`, `blue`, `red`, … | solid named color |
|
|
143
|
+
| `rainbow` | R O Y G B V cycling |
|
|
144
|
+
| `red,black` | alternating colors |
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
echo '{"temp": "72F", "wind": "12mph", "humidity": "45%"}' | \
|
|
148
|
+
vesta render --template kv --title "Weather" --separator rainbow --preview-only
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
┌────────────── flagship 6x22 ───────────────┐
|
|
153
|
+
│████ W E A T H E R ████│
|
|
154
|
+
│████████████████████████████████████████████│
|
|
155
|
+
│T E M P 7 2 F │
|
|
156
|
+
│W I N D 1 2 M P H │
|
|
157
|
+
│H U M I D I T Y 4 5 % │
|
|
158
|
+
│ │
|
|
159
|
+
└────────────────────────────────────────────┘
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
vesta render --input weather.json --template kv --columns 2 \
|
|
164
|
+
--title "BROOKLYN WEATHER" --title-color white --subtitle time --preview-only
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
┌────────────── flagship 6x22 ───────────────┐
|
|
169
|
+
│████ B R O O K L Y N W E A T H E R ████│
|
|
170
|
+
│██ 1 0 : 4 3 P ██│
|
|
171
|
+
│N O W 6 2 F R A I N 0 % │
|
|
172
|
+
│L I K E 6 2 F W I N D 1 5 M P H │
|
|
173
|
+
│H I G H 6 6 F U V 4 . 4 ██│
|
|
174
|
+
│L O W 4 8 F S E T 7 : 3 2 P M │
|
|
175
|
+
└────────────────────────────────────────────┘
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**`--columns [1|2]`** *(kv template only)*
|
|
179
|
+
|
|
180
|
+
Pack two key-value pairs per row instead of one. Each column is sized independently to its own content, which creates a natural gap between columns. Color indicators from `_style` or auto-detection still apply: left-column tiles appear in the gap; right-column tiles appear at the board's right edge.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
echo '{"now":"62F","rain":"0%","high":"66F","low":"48F"}' | \
|
|
184
|
+
vesta render --template kv --columns 2 --preview-only
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
┌────────────── flagship 6x22 ───────────────┐
|
|
189
|
+
│N O W 6 2 F R A I N 0 % │
|
|
190
|
+
│H I G H 6 6 F L O W 4 8 F │
|
|
191
|
+
│ │
|
|
192
|
+
│ │
|
|
193
|
+
│ │
|
|
194
|
+
│ │
|
|
195
|
+
└────────────────────────────────────────────┘
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Falls back to `--columns 1` with a warning if the content is too wide for the profile.
|
|
199
|
+
|
|
200
|
+
**`--align [left|center|right]`**
|
|
201
|
+
|
|
202
|
+
For **metrics** (JSON object): aligns the content block horizontally as a unit. Default is `left`.
|
|
203
|
+
|
|
204
|
+
For **tables** (JSON array or CSV): default is `center` (compact block, centered). `left` and `right` spread columns edge-to-edge with equal inter-column gaps — first column anchored to the chosen edge, last column anchored to the opposite edge.
|
|
205
|
+
|
|
206
|
+
**`--valign [top|center]`**
|
|
207
|
+
|
|
208
|
+
Vertical alignment of the content block. Default is `top`. Use `center` for breathing room when you have fewer rows than the board height.
|
|
209
|
+
|
|
210
|
+
**`--timestamp`**
|
|
211
|
+
|
|
212
|
+
Adds the current time (`10:01A`, `9:30P`) to the bottom-right corner. Silently skipped if there isn't room. Use `--force-timestamp` to place it regardless, overwriting content if needed.
|
|
213
|
+
|
|
214
|
+
**`--tz`**
|
|
215
|
+
|
|
216
|
+
IANA timezone for the timestamp, e.g. `America/New_York`. Defaults to local system time.
|
|
217
|
+
|
|
218
|
+
**`--profile [flagship|note]`**
|
|
219
|
+
|
|
220
|
+
Board profile. Auto-detected from API grid dimensions when publishing. Defaults to flagship.
|
|
221
|
+
|
|
222
|
+
## Example usage
|
|
223
|
+
|
|
224
|
+
**Text:**
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
echo '"hello world"' | vesta render --preview-only
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
┌────────────── flagship 6x22 ───────────────┐
|
|
232
|
+
│ │
|
|
233
|
+
│ │
|
|
234
|
+
│ H E L L O W O R L D │
|
|
235
|
+
│ │
|
|
236
|
+
│ │
|
|
237
|
+
│ │
|
|
238
|
+
└────────────────────────────────────────────┘
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Key/value:**
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
echo '{"temp": "72F", "wind": "12mph"}' | vesta render --template kv --preview-only
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
┌────────────── flagship 6x22 ───────────────┐
|
|
249
|
+
│T E M P 7 2 F │
|
|
250
|
+
│W I N D 1 2 M P H │
|
|
251
|
+
│ │
|
|
252
|
+
│ │
|
|
253
|
+
│ │
|
|
254
|
+
│ │
|
|
255
|
+
└────────────────────────────────────────────┘
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Key/value 2-col with title:**
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
vesta render --input testdata/weather.json --template kv --columns 2 \
|
|
262
|
+
--title "BROOKLYN WEATHER" --subtitle time --preview-only
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
┌────────────── flagship 6x22 ───────────────┐
|
|
267
|
+
│████ B R O O K L Y N W E A T H E R ████│
|
|
268
|
+
│██ 1 0 : 4 3 P ██│
|
|
269
|
+
│N O W 6 2 F R A I N 0 % │
|
|
270
|
+
│L I K E 6 2 F W I N D 1 5 M P H │
|
|
271
|
+
│H I G H 6 6 F U V 4 . 4 ██│
|
|
272
|
+
│L O W 4 8 F S E T 7 : 3 2 P M │
|
|
273
|
+
└────────────────────────────────────────────┘
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**CSV table** (auto-detected, centered by default):
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
vesta render --input scores.csv --preview-only
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
┌────────────── flagship 6x22 ───────────────┐
|
|
284
|
+
│ N A M E S C O R E R A N K │
|
|
285
|
+
│ A L I C E 9 8 1 │
|
|
286
|
+
│ B O B 8 7 2 │
|
|
287
|
+
│ C A R O L 7 6 3 │
|
|
288
|
+
│ D A V E 6 1 4 │
|
|
289
|
+
│ │
|
|
290
|
+
└────────────────────────────────────────────┘
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Use `--align left` or `--align right` to spread columns edge-to-edge instead.
|
|
294
|
+
|
|
295
|
+
**Metrics with color indicators:**
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
echo '{
|
|
299
|
+
"revenue_curr": 84210.50,
|
|
300
|
+
"sessions": 10823,
|
|
301
|
+
"conversion_pct": 3.2,
|
|
302
|
+
"bounce_rate_pct": 68.4,
|
|
303
|
+
"_style": {
|
|
304
|
+
"revenue_curr": "good",
|
|
305
|
+
"conversion_pct": {"good": 8, "bad": 2},
|
|
306
|
+
"bounce_rate_pct": {"good": 30, "bad": 80}
|
|
307
|
+
}
|
|
308
|
+
}' | vesta render --template data --valign center --align center --timestamp --preview-only
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
┌────────────── flagship 6x22 ───────────────┐
|
|
313
|
+
│ │
|
|
314
|
+
│ R E V E N U E $ 8 4 . 2 K ██ │
|
|
315
|
+
│ S E S S I O N S 1 0 . 8 K │
|
|
316
|
+
│ C O N V E R S I O N 3 . 2 % ██ │
|
|
317
|
+
│ B O U N C E R A T E 6 8 . 4 % ██ │
|
|
318
|
+
│ 9 : 3 4 P │
|
|
319
|
+
└────────────────────────────────────────────┘
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Note profile:**
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
vesta render --input data.json --profile note --template data --preview-only
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
┌───────── note 3x15 ──────────┐
|
|
330
|
+
│T E M P $ 7 2 . 0 0 │
|
|
331
|
+
│H U M I D I T Y 5 4 % ██│
|
|
332
|
+
│C H A N G E - 2 . 1 % ██│
|
|
333
|
+
└──────────────────────────────┘
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Get raw character codes** (for direct API use):
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
cat data.json | vesta render --json-only
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Publishing
|
|
343
|
+
|
|
344
|
+
**Cloud API:**
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
cat data.json | vesta post-cloud --token $VESTABOARD_TOKEN
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**Local API:**
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
cat data.json | vesta post-local --api-key $VESTABOARD_LOCAL_API_KEY
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Preview current board state:**
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
vesta read-cloud
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
`VESTABOARD_TOKEN` is read from the environment. Board profile is auto-detected from the grid dimensions returned by the API. Pass `--profile` to override.
|
|
363
|
+
|
|
364
|
+
**Re-render a saved board:**
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
cat data.json | vesta render --json-only > saved.json
|
|
368
|
+
cat saved.json | vesta render --preview-only
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Running the examples
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
./run_examples.sh
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Runs all bundled examples against local test data. Requires no API credentials.
|
|
378
|
+
|
|
379
|
+
## Installation
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
pip install vesta
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Or run directly from source with [uv](https://github.com/astral-sh/uv):
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
uv run vesta.py render
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Why this exists
|
|
392
|
+
|
|
393
|
+
Hitting the Vestaboard API directly is straightforward. The harder part is making structured data fit well on a small fixed-size grid — compacting numbers, handling suffixes, previewing locally, and reusing layouts across scripts and data sources. This project is mainly that rendering layer.
|