led-ticker-pool 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.
- led_ticker_pool-0.1.0/.gitignore +9 -0
- led_ticker_pool-0.1.0/.pre-commit-config.yaml +16 -0
- led_ticker_pool-0.1.0/CLAUDE.md +121 -0
- led_ticker_pool-0.1.0/LICENSE +21 -0
- led_ticker_pool-0.1.0/Makefile +18 -0
- led_ticker_pool-0.1.0/PKG-INFO +152 -0
- led_ticker_pool-0.1.0/README.md +125 -0
- led_ticker_pool-0.1.0/docs/widget-pool-two-row.gif +0 -0
- led_ticker_pool-0.1.0/docs/widget-pool.gif +0 -0
- led_ticker_pool-0.1.0/pyproject.toml +58 -0
- led_ticker_pool-0.1.0/src/led_ticker_pool/__init__.py +11 -0
- led_ticker_pool-0.1.0/src/led_ticker_pool/monitor.py +609 -0
- led_ticker_pool-0.1.0/tests/test_import_purity.py +29 -0
- led_ticker_pool-0.1.0/tests/test_monitor.py +842 -0
- led_ticker_pool-0.1.0/tests/test_smoke.py +25 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.4.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: local
|
|
9
|
+
hooks:
|
|
10
|
+
- id: pyright
|
|
11
|
+
name: pyright
|
|
12
|
+
entry: uv run pyright src
|
|
13
|
+
language: system
|
|
14
|
+
pass_filenames: false
|
|
15
|
+
always_run: true
|
|
16
|
+
stages: [pre-push]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for Claude Code when working in **led-ticker-pool**, an external plugin for
|
|
4
|
+
[led-ticker](https://github.com/JamesAwesome/led-ticker).
|
|
5
|
+
|
|
6
|
+
`README.md` is the source of truth for the user-facing surface (config options, temperature
|
|
7
|
+
zones, layouts, InfluxDB setup). This file keeps the **load-bearing invariants** a contributor
|
|
8
|
+
must respect, plus navigation aids. When a fact here and the README disagree about *how a
|
|
9
|
+
feature works*, the README wins; this file is the source of truth for *how to keep it working*.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
This plugin contributes, via the `led_ticker.plugins` entry point, a single widget:
|
|
14
|
+
|
|
15
|
+
- `pool.monitor` — pool water-temperature from an InfluxDB v2 server (Flux queries). Cycles a
|
|
16
|
+
title card + today's temp (trend arrow), 7-day mean (hi/lo), and season hi/lo, zone-colored by
|
|
17
|
+
temperature. Two layouts: `ticker` (default, single-row segmented; smallsign-friendly) and
|
|
18
|
+
`two_row` (label-on-top / big-number-on-bottom; bigsign/longboi).
|
|
19
|
+
|
|
20
|
+
The entry-point name `pool` is the plugin namespace, so the config `type` is `pool.monitor`
|
|
21
|
+
(see `register()` in `__init__.py`).
|
|
22
|
+
|
|
23
|
+
## Commands
|
|
24
|
+
|
|
25
|
+
led-ticker is **not on PyPI**; it resolves from a sibling checkout via
|
|
26
|
+
`[tool.uv.sources] led-ticker = { path = "../led-ticker", editable = true }`. CI checks out
|
|
27
|
+
`led-ticker` next to this repo using a read-only deploy key (`LED_TICKER_DEPLOY_KEY`). The
|
|
28
|
+
sibling checkout matters at test time too: `pyproject.toml` puts `../led-ticker/tests/stubs`
|
|
29
|
+
on the pytest path so the rgbmatrix stub is importable headless.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv sync --extra dev # install deps (needs ../led-ticker checked out)
|
|
33
|
+
uv run pytest -q # full suite (asyncio_mode = "auto")
|
|
34
|
+
uv run ruff check src tests # lint — run before pushing
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Python **3.14+** only. Running the widget needs `INFLUXDB_TOKEN` set (see below).
|
|
38
|
+
|
|
39
|
+
## Package layout
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
src/led_ticker_pool/
|
|
43
|
+
__init__.py # register(api) → api.widget("monitor")(PoolMonitor)
|
|
44
|
+
monitor.py # PoolMonitor: async InfluxDB Flux fetch, dual-layout screen building,
|
|
45
|
+
# config validation, zone coloring, trend arrows, staleness dimming
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`register(api)` (in `__init__.py`):
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
def register(api):
|
|
52
|
+
api.widget("monitor")(PoolMonitor)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Load-bearing invariants
|
|
56
|
+
|
|
57
|
+
Each rule must hold when modifying `monitor.py`.
|
|
58
|
+
|
|
59
|
+
**Import only the public surface** — every `led_ticker` import MUST come from `led_ticker.plugin`,
|
|
60
|
+
never `led_ticker.<internal>`. Enforced by `tests/test_import_purity.py`, which AST-walks every
|
|
61
|
+
source file. If you need a core symbol that isn't on `led_ticker.plugin.__all__`, that's a core
|
|
62
|
+
API change — raise it upstream, don't reach around the surface.
|
|
63
|
+
|
|
64
|
+
**Python 3.14 / PEP 649** — no `from __future__ import annotations` (same rule as core).
|
|
65
|
+
|
|
66
|
+
**`validate_config()` contract** (`PoolMonitor.validate_config`, a classmethod run pre-coercion by
|
|
67
|
+
the engine) — this widget **raises `ValueError`** directly on a bad config (unlike some plugins
|
|
68
|
+
that return message strings). It rejects: a `current_window` that isn't a negative Flux duration
|
|
69
|
+
(`^-(\d+(ns|us|ms|s|m|h|d|w))+$`); a `sensor_id` outside `[A-Za-z0-9_-]+` (the value is interpolated
|
|
70
|
+
into Flux, so this is an injection-safety gate); a `layout` not in `("ticker", "two_row")`; and any
|
|
71
|
+
of the two-row-only fields (`top_font`/`bottom_font`/`top_row_height`/the per-row size+threshold
|
|
72
|
+
knobs) when `layout != "two_row"` — named, not silently ignored.
|
|
73
|
+
|
|
74
|
+
**Flux `group()` before aggregation** — multi-sensor buckets MUST `group()` before `last`/`mean`/
|
|
75
|
+
`min`/`max`, otherwise the CSV parser picks the first series in tag-sort order and season HI/LO
|
|
76
|
+
diverges from today/7-day (the real bug: "season HI 37°F but pool app shows 90°F"). Tripwire:
|
|
77
|
+
`test_inserts_group_before_aggregation` in `tests/test_monitor.py`.
|
|
78
|
+
|
|
79
|
+
**Thread `font` and `label_color` everywhere** — the configured `font` and `label_color` must reach
|
|
80
|
+
every `SegmentMessage` / `TwoRowMessage` the widget builds (title + all three stories + the
|
|
81
|
+
placeholder). Miss one and the config knob silently no-ops (the "chunky text misplaced" / "label
|
|
82
|
+
color ignored" class of bug).
|
|
83
|
+
|
|
84
|
+
**`current_window` vs `stale_after` are decoupled** — `current_window` is a hard cutoff: no reading
|
|
85
|
+
inside it → display `--`. `stale_after` only controls the dim-gray coloring; a reading older than
|
|
86
|
+
`stale_after` still displays, dimmed. Don't collapse them.
|
|
87
|
+
|
|
88
|
+
**No degree symbol in temperatures** — `_fmt_temp` emits bare `F`/`C`, never `°F`/`°C`, because the
|
|
89
|
+
hires Inter font rasterized small drops U+00B0 to `?` (consistent with the weather widget).
|
|
90
|
+
|
|
91
|
+
**`INFLUXDB_TOKEN` is required, never logged** — the widget raises `ValueError` in `start()` (before
|
|
92
|
+
entering the monitor loop) if the token is missing, so it surfaces immediately in logs. The token
|
|
93
|
+
must never appear in any log line.
|
|
94
|
+
|
|
95
|
+
**One INFO log per successful `update()`** — the Container contract: a silent log stream after
|
|
96
|
+
startup signals the background task died. Each successful update emits exactly one INFO line
|
|
97
|
+
(including the current temp, never the token).
|
|
98
|
+
|
|
99
|
+
**Zone color is always evaluated in °F** regardless of the display unit, so thresholds stay
|
|
100
|
+
consistent across imperial/metric. `PoolMonitor` is an `attrs.define` class with several
|
|
101
|
+
`kw_only=True` fields (`font`, `layout`, `label_color`, `top_font`, `bottom_font`, `top_row_height`)
|
|
102
|
+
— construct with named args for those.
|
|
103
|
+
|
|
104
|
+
## Tests / CI
|
|
105
|
+
|
|
106
|
+
`uv run pytest -q` runs the suite (`tests/`):
|
|
107
|
+
|
|
108
|
+
- `test_import_purity.py` — AST tripwire (public-surface-only). A failure is a contract violation.
|
|
109
|
+
- `test_smoke.py` — loads the plugin through led-ticker's real loader; asserts `pool.monitor`
|
|
110
|
+
registers under the `pool` namespace.
|
|
111
|
+
- `test_monitor.py` — zone coloring, trend arrows, unit conversion, CSV parsing, Flux query
|
|
112
|
+
building (incl. the `group()` tripwire), both layouts' screen building, `validate_config`, and the
|
|
113
|
+
token/staleness/logging contracts.
|
|
114
|
+
|
|
115
|
+
CI (`.github/workflows/ci.yml`): checks out this repo + led-ticker as siblings (deploy key),
|
|
116
|
+
Python 3.14, `uv sync --extra dev`, then `ruff check src tests` and `pytest -q`.
|
|
117
|
+
|
|
118
|
+
## Adding to the plugin
|
|
119
|
+
|
|
120
|
+
Register the class in `register()` in `__init__.py` (`api.widget`); it becomes `pool.<name>`.
|
|
121
|
+
Import any core dependency from `led_ticker.plugin` only, and keep the import-purity test green.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 James Awesome
|
|
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,18 @@
|
|
|
1
|
+
.PHONY: dev test lint format typecheck
|
|
2
|
+
|
|
3
|
+
dev: ## Install dev deps + pre-commit hooks
|
|
4
|
+
uv sync --extra dev
|
|
5
|
+
uv run pre-commit install
|
|
6
|
+
uv run pre-commit install --hook-type pre-push
|
|
7
|
+
|
|
8
|
+
test: ## Run tests with coverage
|
|
9
|
+
uv run pytest --cov=src --cov-report=term-missing
|
|
10
|
+
|
|
11
|
+
lint: ## Ruff lint
|
|
12
|
+
uv run ruff check src tests
|
|
13
|
+
|
|
14
|
+
format: ## Ruff format
|
|
15
|
+
uv run ruff format src tests
|
|
16
|
+
|
|
17
|
+
typecheck: ## Pyright
|
|
18
|
+
uv run pyright src
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: led-ticker-pool
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pool water-temperature monitor widget for led-ticker (InfluxDB v2 backed).
|
|
5
|
+
Project-URL: Homepage, https://docs.ledticker.dev
|
|
6
|
+
Project-URL: Repository, https://github.com/JamesAwesome/led-ticker-plugins
|
|
7
|
+
Project-URL: Issues, https://github.com/JamesAwesome/led-ticker-plugins/issues
|
|
8
|
+
Author-email: James Awesome <james@morelli.nyc>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
16
|
+
Requires-Python: >=3.14
|
|
17
|
+
Requires-Dist: aiohttp
|
|
18
|
+
Requires-Dist: led-ticker-core>=2.0
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pre-commit>=4.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pyright>=1.1; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# led-ticker-pool
|
|
29
|
+
|
|
30
|
+
A pool water-temperature monitor **widget** for [led-ticker](https://github.com/JamesAwesome/led-ticker), backed by an InfluxDB v2 server (e.g. [pool_monitor](https://github.com/JamesAwesome/pool_monitor)). It's a led-ticker **plugin** — installing this package contributes a `pool.monitor` widget you reference in your led-ticker config.
|
|
31
|
+
|
|
32
|
+
It cycles four screens — a title card, today's current temperature with a trend arrow (`^`/`v`/`-`) and hi/lo, a 7-day mean with hi/lo, and a season (current-year) hi/lo. Temperature is zone-colored — blue below 70°F, green 70–79°F, orange 80–89°F, red 90°F+ — so the comfort level is readable at a glance. Data is fetched in the background via async polling, so the display keeps running even if the server is briefly unreachable.
|
|
33
|
+
|
|
34
|
+
## Screenshots
|
|
35
|
+
|
|
36
|
+
**`layout = "ticker"`** (default — single-row segmented screens, smallsign-friendly):
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
**`layout = "two_row"`** (stacked label-on-top / big-number-on-bottom, bigsign / longboi):
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
## Prerequisites
|
|
45
|
+
|
|
46
|
+
- **A running led-ticker** (the sign + its config). This widget plugs into it.
|
|
47
|
+
- **A running InfluxDB v2 server** holding pool temperature data. The reference stack is [pool_monitor](https://github.com/JamesAwesome/pool_monitor), which provides the bucket and sensor schema this widget expects.
|
|
48
|
+
- **An InfluxDB auth token** — set `INFLUXDB_TOKEN` in your led-ticker `.env` (or as a per-widget config key). The widget raises `ValueError` at startup if it's missing. See [InfluxDB setup](#influxdb-setup) for all four connection variables.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
The widget auto-registers via the `led_ticker.plugins` entry point — once the package is installed, no `[plugins]` config change is needed.
|
|
53
|
+
|
|
54
|
+
**Into a containerized led-ticker (recommended):** add this package to `config/requirements-plugins.txt` (copy it from `config/requirements-plugins.example.txt`, which already lists it), then rebuild:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# in your led-ticker checkout
|
|
58
|
+
cp config/requirements-plugins.example.txt config/requirements-plugins.txt
|
|
59
|
+
docker compose up -d --build
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Standalone (bare-metal / a venv that already has led-ticker):**
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install "git+https://github.com/JamesAwesome/led-ticker-pool.git@main"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
(led-ticker isn't on PyPI, so `pip` can't fetch it — this path works only where led-ticker is already installed, e.g. inside the led-ticker Docker image or a venv set up as in [Development](#development) below. See the led-ticker [Plugins docs](https://docs.ledticker.dev/plugins/) for the constraint-based install the image uses.)
|
|
69
|
+
|
|
70
|
+
## Configuration
|
|
71
|
+
|
|
72
|
+
Reference the widget in a playlist section by `type = "pool.monitor"`:
|
|
73
|
+
|
|
74
|
+
```toml
|
|
75
|
+
[[playlist.section.widget]]
|
|
76
|
+
type = "pool.monitor"
|
|
77
|
+
title = "POOL TEMPS"
|
|
78
|
+
units = "imperial"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Options
|
|
82
|
+
|
|
83
|
+
| Option | Type | Default | Description |
|
|
84
|
+
|--------|------|---------|-------------|
|
|
85
|
+
| `title` | string | `"POOL TEMPS"` | Label shown on the title screen. |
|
|
86
|
+
| `sensor_id` | string | none | Sensor ID to filter on. Omit to use the only/first sensor in the bucket. Must match `[A-Za-z0-9_-]+`. |
|
|
87
|
+
| `units` | string | `"imperial"` | `"imperial"` (°F) or `"metric"` (°C). |
|
|
88
|
+
| `update_interval` | int | `300` | Seconds between InfluxDB fetches (5 min default). |
|
|
89
|
+
| `current_window` | string | `"-24h"` | How far back to search for the latest reading, as a negative Flux duration (`"-24h"`, `"-90m"`). Older than this → `--` placeholder. Widen it if your sensor reports infrequently. |
|
|
90
|
+
| `stale_after` | float | `14400` | Seconds since the last reading before the temperature dims to gray (stale signal). 4 h default. |
|
|
91
|
+
| `influxdb_url` | string | `$INFLUXDB_URL` / `"http://influxdb:8086"` | InfluxDB v2 base URL. Config overrides the env var. |
|
|
92
|
+
| `influxdb_org` | string | `$INFLUXDB_ORG` / `"pool"` | InfluxDB organization. |
|
|
93
|
+
| `influxdb_bucket` | string | `$INFLUXDB_BUCKET` / `"pool_temps"` | InfluxDB bucket. |
|
|
94
|
+
| `influxdb_token` | string | `$INFLUXDB_TOKEN` | InfluxDB v2 token. **Required** — the widget raises `ValueError` at startup if it's missing. |
|
|
95
|
+
| `layout` | `"ticker"` \| `"two_row"` | `"ticker"` | Render mode (see below). |
|
|
96
|
+
| `label_color` | `[r,g,b]` | white | Color for prefix labels / separators. |
|
|
97
|
+
| `font` / `font_size` / `font_threshold` | font name / int / int | `"6x12"` | Main text font. A BDF alias (`6x12`, `5x8`) or a hires font name (`Inter-Regular`) with `font_size` in real pixels. In `two_row`, the per-row knobs below override this. |
|
|
98
|
+
| `top_font` / `top_font_size` / `top_font_threshold` | font / int / int | inherit `font` | **two_row only:** top (label) row font knobs. |
|
|
99
|
+
| `bottom_font` / `bottom_font_size` / `bottom_font_threshold` | font / int / int | inherit | **two_row only:** bottom (value) row font knobs. |
|
|
100
|
+
| `top_row_height` | int (logical rows) | `None` | **two_row only:** top band height. `None` = symmetric 8/8 split. |
|
|
101
|
+
|
|
102
|
+
The per-row knobs apply ONLY when `layout = "two_row"`; setting them under `ticker` fails config validation.
|
|
103
|
+
|
|
104
|
+
### Layouts
|
|
105
|
+
|
|
106
|
+
- **`ticker`** (default) — single-row segmented screens; the today screen shows current temp + trend arrow and hi/lo, the 7-day screen the mean + hi/lo, the season screen HI/LO together. Best for small panels (smallsign 160×16).
|
|
107
|
+
- **`two_row`** — stacked label-on-top / big-number-on-bottom. Cycles four screens with top-row labels `POOL` (title), `POOL 24H` (current temp, zone-colored), `POOL 7D` (7-day HI/LO), and `POOL SEASON` (season HI/LO) — HI in orange, LO in blue, shown together on one screen (e.g. `84/72F`). The trend arrow is dropped (bottom is the value only). Best for bigsign / longboi (256×64 / 512×64).
|
|
108
|
+
|
|
109
|
+
A `two_row` example:
|
|
110
|
+
|
|
111
|
+
```toml
|
|
112
|
+
[[playlist.section.widget]]
|
|
113
|
+
type = "pool.monitor"
|
|
114
|
+
title = "POOL TEMPS"
|
|
115
|
+
layout = "two_row"
|
|
116
|
+
units = "imperial"
|
|
117
|
+
font = "Inter-Regular"
|
|
118
|
+
font_size = 32
|
|
119
|
+
label_color = [130, 220, 255]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## InfluxDB setup
|
|
123
|
+
|
|
124
|
+
The widget reads connection details from your led-ticker `.env` (or per-widget overrides). `INFLUXDB_TOKEN` is required; the rest default to the standard pool_monitor Docker Compose stack.
|
|
125
|
+
|
|
126
|
+
| Variable | Required | Default | Description |
|
|
127
|
+
|----------|----------|---------|-------------|
|
|
128
|
+
| `INFLUXDB_TOKEN` | **yes** | — | InfluxDB v2 auth token. |
|
|
129
|
+
| `INFLUXDB_URL` | no | `http://influxdb:8086` | Base URL. |
|
|
130
|
+
| `INFLUXDB_ORG` | no | `pool` | Organization. |
|
|
131
|
+
| `INFLUXDB_BUCKET` | no | `pool_temps` | Bucket. |
|
|
132
|
+
|
|
133
|
+
The widget queries water-temperature readings with Flux over HTTP and computes today / 7-day / season aggregates. Stale data (older than `stale_after`) renders dim gray; the trend arrow compares the latest reading to a ~45-minute trailing average (sub-0.5°F shows `-`).
|
|
134
|
+
|
|
135
|
+
## Development
|
|
136
|
+
|
|
137
|
+
led-ticker isn't on PyPI, so install it editable from a sibling checkout. This repo's `pyproject.toml` pins `led-ticker` to `../led-ticker` via `[tool.uv.sources]`:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone https://github.com/JamesAwesome/led-ticker ../led-ticker # sibling checkout
|
|
141
|
+
git clone https://github.com/JamesAwesome/led-ticker-pool && cd led-ticker-pool
|
|
142
|
+
uv venv
|
|
143
|
+
uv pip install -e ../led-ticker -e ".[dev]"
|
|
144
|
+
uv run pytest -q
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
> **Note:** led-ticker's `graphics` surface works headless via its bundled stub, but the full `RGBMatrix`/canvas test stub lives in led-ticker's `tests/stubs/` and isn't shipped. This repo's tests put it on the path via `pyproject.toml`'s `[tool.pytest.ini_options] pythonpath = ["../led-ticker/tests/stubs"]`.
|
|
148
|
+
|
|
149
|
+
## Links
|
|
150
|
+
|
|
151
|
+
- led-ticker project: <https://github.com/JamesAwesome/led-ticker>
|
|
152
|
+
- led-ticker plugin system: <https://docs.ledticker.dev/plugins/>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# led-ticker-pool
|
|
2
|
+
|
|
3
|
+
A pool water-temperature monitor **widget** for [led-ticker](https://github.com/JamesAwesome/led-ticker), backed by an InfluxDB v2 server (e.g. [pool_monitor](https://github.com/JamesAwesome/pool_monitor)). It's a led-ticker **plugin** — installing this package contributes a `pool.monitor` widget you reference in your led-ticker config.
|
|
4
|
+
|
|
5
|
+
It cycles four screens — a title card, today's current temperature with a trend arrow (`^`/`v`/`-`) and hi/lo, a 7-day mean with hi/lo, and a season (current-year) hi/lo. Temperature is zone-colored — blue below 70°F, green 70–79°F, orange 80–89°F, red 90°F+ — so the comfort level is readable at a glance. Data is fetched in the background via async polling, so the display keeps running even if the server is briefly unreachable.
|
|
6
|
+
|
|
7
|
+
## Screenshots
|
|
8
|
+
|
|
9
|
+
**`layout = "ticker"`** (default — single-row segmented screens, smallsign-friendly):
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
**`layout = "two_row"`** (stacked label-on-top / big-number-on-bottom, bigsign / longboi):
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- **A running led-ticker** (the sign + its config). This widget plugs into it.
|
|
20
|
+
- **A running InfluxDB v2 server** holding pool temperature data. The reference stack is [pool_monitor](https://github.com/JamesAwesome/pool_monitor), which provides the bucket and sensor schema this widget expects.
|
|
21
|
+
- **An InfluxDB auth token** — set `INFLUXDB_TOKEN` in your led-ticker `.env` (or as a per-widget config key). The widget raises `ValueError` at startup if it's missing. See [InfluxDB setup](#influxdb-setup) for all four connection variables.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
The widget auto-registers via the `led_ticker.plugins` entry point — once the package is installed, no `[plugins]` config change is needed.
|
|
26
|
+
|
|
27
|
+
**Into a containerized led-ticker (recommended):** add this package to `config/requirements-plugins.txt` (copy it from `config/requirements-plugins.example.txt`, which already lists it), then rebuild:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# in your led-ticker checkout
|
|
31
|
+
cp config/requirements-plugins.example.txt config/requirements-plugins.txt
|
|
32
|
+
docker compose up -d --build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Standalone (bare-metal / a venv that already has led-ticker):**
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install "git+https://github.com/JamesAwesome/led-ticker-pool.git@main"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
(led-ticker isn't on PyPI, so `pip` can't fetch it — this path works only where led-ticker is already installed, e.g. inside the led-ticker Docker image or a venv set up as in [Development](#development) below. See the led-ticker [Plugins docs](https://docs.ledticker.dev/plugins/) for the constraint-based install the image uses.)
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
Reference the widget in a playlist section by `type = "pool.monitor"`:
|
|
46
|
+
|
|
47
|
+
```toml
|
|
48
|
+
[[playlist.section.widget]]
|
|
49
|
+
type = "pool.monitor"
|
|
50
|
+
title = "POOL TEMPS"
|
|
51
|
+
units = "imperial"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Options
|
|
55
|
+
|
|
56
|
+
| Option | Type | Default | Description |
|
|
57
|
+
|--------|------|---------|-------------|
|
|
58
|
+
| `title` | string | `"POOL TEMPS"` | Label shown on the title screen. |
|
|
59
|
+
| `sensor_id` | string | none | Sensor ID to filter on. Omit to use the only/first sensor in the bucket. Must match `[A-Za-z0-9_-]+`. |
|
|
60
|
+
| `units` | string | `"imperial"` | `"imperial"` (°F) or `"metric"` (°C). |
|
|
61
|
+
| `update_interval` | int | `300` | Seconds between InfluxDB fetches (5 min default). |
|
|
62
|
+
| `current_window` | string | `"-24h"` | How far back to search for the latest reading, as a negative Flux duration (`"-24h"`, `"-90m"`). Older than this → `--` placeholder. Widen it if your sensor reports infrequently. |
|
|
63
|
+
| `stale_after` | float | `14400` | Seconds since the last reading before the temperature dims to gray (stale signal). 4 h default. |
|
|
64
|
+
| `influxdb_url` | string | `$INFLUXDB_URL` / `"http://influxdb:8086"` | InfluxDB v2 base URL. Config overrides the env var. |
|
|
65
|
+
| `influxdb_org` | string | `$INFLUXDB_ORG` / `"pool"` | InfluxDB organization. |
|
|
66
|
+
| `influxdb_bucket` | string | `$INFLUXDB_BUCKET` / `"pool_temps"` | InfluxDB bucket. |
|
|
67
|
+
| `influxdb_token` | string | `$INFLUXDB_TOKEN` | InfluxDB v2 token. **Required** — the widget raises `ValueError` at startup if it's missing. |
|
|
68
|
+
| `layout` | `"ticker"` \| `"two_row"` | `"ticker"` | Render mode (see below). |
|
|
69
|
+
| `label_color` | `[r,g,b]` | white | Color for prefix labels / separators. |
|
|
70
|
+
| `font` / `font_size` / `font_threshold` | font name / int / int | `"6x12"` | Main text font. A BDF alias (`6x12`, `5x8`) or a hires font name (`Inter-Regular`) with `font_size` in real pixels. In `two_row`, the per-row knobs below override this. |
|
|
71
|
+
| `top_font` / `top_font_size` / `top_font_threshold` | font / int / int | inherit `font` | **two_row only:** top (label) row font knobs. |
|
|
72
|
+
| `bottom_font` / `bottom_font_size` / `bottom_font_threshold` | font / int / int | inherit | **two_row only:** bottom (value) row font knobs. |
|
|
73
|
+
| `top_row_height` | int (logical rows) | `None` | **two_row only:** top band height. `None` = symmetric 8/8 split. |
|
|
74
|
+
|
|
75
|
+
The per-row knobs apply ONLY when `layout = "two_row"`; setting them under `ticker` fails config validation.
|
|
76
|
+
|
|
77
|
+
### Layouts
|
|
78
|
+
|
|
79
|
+
- **`ticker`** (default) — single-row segmented screens; the today screen shows current temp + trend arrow and hi/lo, the 7-day screen the mean + hi/lo, the season screen HI/LO together. Best for small panels (smallsign 160×16).
|
|
80
|
+
- **`two_row`** — stacked label-on-top / big-number-on-bottom. Cycles four screens with top-row labels `POOL` (title), `POOL 24H` (current temp, zone-colored), `POOL 7D` (7-day HI/LO), and `POOL SEASON` (season HI/LO) — HI in orange, LO in blue, shown together on one screen (e.g. `84/72F`). The trend arrow is dropped (bottom is the value only). Best for bigsign / longboi (256×64 / 512×64).
|
|
81
|
+
|
|
82
|
+
A `two_row` example:
|
|
83
|
+
|
|
84
|
+
```toml
|
|
85
|
+
[[playlist.section.widget]]
|
|
86
|
+
type = "pool.monitor"
|
|
87
|
+
title = "POOL TEMPS"
|
|
88
|
+
layout = "two_row"
|
|
89
|
+
units = "imperial"
|
|
90
|
+
font = "Inter-Regular"
|
|
91
|
+
font_size = 32
|
|
92
|
+
label_color = [130, 220, 255]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## InfluxDB setup
|
|
96
|
+
|
|
97
|
+
The widget reads connection details from your led-ticker `.env` (or per-widget overrides). `INFLUXDB_TOKEN` is required; the rest default to the standard pool_monitor Docker Compose stack.
|
|
98
|
+
|
|
99
|
+
| Variable | Required | Default | Description |
|
|
100
|
+
|----------|----------|---------|-------------|
|
|
101
|
+
| `INFLUXDB_TOKEN` | **yes** | — | InfluxDB v2 auth token. |
|
|
102
|
+
| `INFLUXDB_URL` | no | `http://influxdb:8086` | Base URL. |
|
|
103
|
+
| `INFLUXDB_ORG` | no | `pool` | Organization. |
|
|
104
|
+
| `INFLUXDB_BUCKET` | no | `pool_temps` | Bucket. |
|
|
105
|
+
|
|
106
|
+
The widget queries water-temperature readings with Flux over HTTP and computes today / 7-day / season aggregates. Stale data (older than `stale_after`) renders dim gray; the trend arrow compares the latest reading to a ~45-minute trailing average (sub-0.5°F shows `-`).
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
led-ticker isn't on PyPI, so install it editable from a sibling checkout. This repo's `pyproject.toml` pins `led-ticker` to `../led-ticker` via `[tool.uv.sources]`:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
git clone https://github.com/JamesAwesome/led-ticker ../led-ticker # sibling checkout
|
|
114
|
+
git clone https://github.com/JamesAwesome/led-ticker-pool && cd led-ticker-pool
|
|
115
|
+
uv venv
|
|
116
|
+
uv pip install -e ../led-ticker -e ".[dev]"
|
|
117
|
+
uv run pytest -q
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
> **Note:** led-ticker's `graphics` surface works headless via its bundled stub, but the full `RGBMatrix`/canvas test stub lives in led-ticker's `tests/stubs/` and isn't shipped. This repo's tests put it on the path via `pyproject.toml`'s `[tool.pytest.ini_options] pythonpath = ["../led-ticker/tests/stubs"]`.
|
|
121
|
+
|
|
122
|
+
## Links
|
|
123
|
+
|
|
124
|
+
- led-ticker project: <https://github.com/JamesAwesome/led-ticker>
|
|
125
|
+
- led-ticker plugin system: <https://docs.ledticker.dev/plugins/>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "led-ticker-pool"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Pool water-temperature monitor widget for led-ticker (InfluxDB v2 backed)."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.14"
|
|
9
|
+
authors = [{ name = "James Awesome", email = "james@morelli.nyc" }]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.14",
|
|
14
|
+
"Operating System :: POSIX :: Linux",
|
|
15
|
+
"Topic :: Multimedia :: Graphics",
|
|
16
|
+
]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"led-ticker-core>=2.0",
|
|
19
|
+
"aiohttp",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
# The widget is contributed to led-ticker via this entry point. The entry-point
|
|
23
|
+
# NAME ("pool") becomes the plugin namespace, so the widget is referenced in
|
|
24
|
+
# TOML as `type = "pool.monitor"`.
|
|
25
|
+
[project.entry-points."led_ticker.plugins"]
|
|
26
|
+
pool = "led_ticker_pool:register"
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = [
|
|
30
|
+
"pytest>=8.0",
|
|
31
|
+
"pytest-asyncio>=0.23",
|
|
32
|
+
"pytest-cov>=5.0",
|
|
33
|
+
"pre-commit>=4.0",
|
|
34
|
+
"ruff>=0.4",
|
|
35
|
+
"pyright>=1.1",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://docs.ledticker.dev"
|
|
40
|
+
Repository = "https://github.com/JamesAwesome/led-ticker-plugins"
|
|
41
|
+
Issues = "https://github.com/JamesAwesome/led-ticker-plugins/issues"
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["hatchling"]
|
|
45
|
+
build-backend = "hatchling.build"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["src/led_ticker_pool"]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
target-version = "py314"
|
|
52
|
+
src = ["src"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
56
|
+
|
|
57
|
+
[tool.coverage.report]
|
|
58
|
+
fail_under = 90
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""led-ticker-pool: a pool water-temperature monitor widget for led-ticker.
|
|
2
|
+
|
|
3
|
+
Contributed via the ``led_ticker.plugins`` entry point. The entry-point name
|
|
4
|
+
``pool`` is the plugin namespace, so the widget is ``type = "pool.monitor"``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from led_ticker_pool.monitor import PoolMonitor
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def register(api):
|
|
11
|
+
api.widget("monitor")(PoolMonitor)
|