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.
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .env
@@ -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
+ ![Pool widget in ticker layout — single-row segmented screens with trend arrow and hi/lo](docs/widget-pool.gif)
39
+
40
+ **`layout = "two_row"`** (stacked label-on-top / big-number-on-bottom, bigsign / longboi):
41
+
42
+ ![Pool widget in two_row layout — stacked label-on-top, big-number-on-bottom](docs/widget-pool-two-row.gif)
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
+ ![Pool widget in ticker layout — single-row segmented screens with trend arrow and hi/lo](docs/widget-pool.gif)
12
+
13
+ **`layout = "two_row"`** (stacked label-on-top / big-number-on-bottom, bigsign / longboi):
14
+
15
+ ![Pool widget in two_row layout — stacked label-on-top, big-number-on-bottom](docs/widget-pool-two-row.gif)
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/>
@@ -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)