- Remove .env.example and its .gitignore entry; compose sets the dev credentials directly. Basic auth in the image is unchanged. - Preselect the last dataset by name instead of by mtime, which is meaningless after a git checkout in CI - README: Esc zoom reset, data update flow, data visibility note, runner image requirements, dev login for compose - Remove duplicate data/history2.csv Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
BASIC_AUTH_USER=admin
|
|
||||||
BASIC_AUTH_PASSWORD=change-me
|
|
||||||
# HEATPUMP_PORT=8080
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
.env
|
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
A small static web app that visualises when a heat pump compressor is running,
|
A small static web app that visualises when a heat pump compressor is running,
|
||||||
based on Home Assistant history exports (`binary_sensor.*` on/off states).
|
based on Home Assistant history exports (`binary_sensor.*` on/off states).
|
||||||
|
|
||||||
- Interactive timeline (pan, zoom, mouse-wheel, quick ranges 6 h / 24 h / 3 d / 7 d)
|
- Interactive timeline (pan, zoom, mouse-wheel, quick ranges 6 h / 24 h / 3 d / 7 d;
|
||||||
|
double-click or Esc resets the zoom)
|
||||||
- Hover an on-phase to see its start, end and duration
|
- Hover an on-phase to see its start, end and duration
|
||||||
- Statistics for the visible range: number of phases, min / max / mean / median
|
- Statistics for the visible range: number of phases, min / max / mean / median
|
||||||
duration, total on-time, duty cycle, mean pause between phases
|
duration, total on-time, duty cycle, mean pause between phases
|
||||||
@@ -20,13 +21,18 @@ Put CSV exports into `data/`. Every `*.csv` in there is baked into the image.
|
|||||||
|
|
||||||
- **One file:** it is loaded directly.
|
- **One file:** it is loaded directly.
|
||||||
- **Several files:** a dropdown in the header lists them alphabetically. The
|
- **Several files:** a dropdown in the header lists them alphabetically. The
|
||||||
most recently modified file (or the viewer's last choice) is preselected.
|
viewer's last choice is preselected, otherwise the **last file by name**.
|
||||||
|
Name files by date (`2026-09.csv`, `2026-10.csv`, …) so the newest comes last.
|
||||||
|
|
||||||
A different CSV can also be opened ad hoc with **Load CSV…**. It is parsed in
|
A different CSV can also be opened ad hoc with **Load CSV…**. It is parsed in
|
||||||
the browser and not uploaded.
|
the browser and not uploaded.
|
||||||
|
|
||||||
The file list comes from nginx's JSON directory listing of `/data/`, so nothing
|
The file list comes from nginx's JSON directory listing of `/data/`, so nothing
|
||||||
has to be registered anywhere. Add or remove files and rebuild the image.
|
has to be registered anywhere. To add or remove files, commit and push; CI
|
||||||
|
builds a new image. Locally, use `docker compose up -d --build`.
|
||||||
|
|
||||||
|
> The data is part of the image. Anyone who can pull the image (or read this
|
||||||
|
> repository) can read the CSVs; basic auth only protects the running web app.
|
||||||
|
|
||||||
### CSV format
|
### CSV format
|
||||||
|
|
||||||
@@ -76,11 +82,12 @@ the app without a password.
|
|||||||
### Local run with Compose
|
### Local run with Compose
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cp .env.example .env # set BASIC_AUTH_USER / BASIC_AUTH_PASSWORD
|
|
||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
Open <http://localhost:8080> (port configurable with `HEATPUMP_PORT`).
|
Open <http://localhost:8080> and log in as `dev` / `dev`. These development
|
||||||
|
credentials are set in `docker-compose.yml`. The host port can be changed with
|
||||||
|
`HEATPUMP_PORT=9000 docker compose up -d`.
|
||||||
|
|
||||||
## CI: build and push
|
## CI: build and push
|
||||||
|
|
||||||
@@ -102,7 +109,9 @@ Setup:
|
|||||||
- `REGISTRY_TOKEN`: that robot account's secret
|
- `REGISTRY_TOKEN`: that robot account's secret
|
||||||
2. A runner with Docker access must be registered for the repo, user or
|
2. A runner with Docker access must be registered for the repo, user or
|
||||||
instance (`act_runner` with the Docker socket mounted), and it must provide
|
instance (`act_runner` with the Docker socket mounted), and it must provide
|
||||||
the `ubuntu-latest` label.
|
the `ubuntu-latest` label. The job image needs Node 20 and the Docker CLI,
|
||||||
|
e.g. `docker.gitea.com/runner-images:ubuntu-latest`. The old default
|
||||||
|
`node:16-bullseye` fails on `actions/checkout@v4`.
|
||||||
|
|
||||||
## Project layout
|
## Project layout
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
entity_id,state,last_changed
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-17T03:00:19.068Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-17T03:08:01.062Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-17T11:26:17.041Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-17T11:41:03.220Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-18T03:00:22.102Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-18T03:19:35.943Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-18T10:19:43.304Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-18T10:36:39.042Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-19T01:20:46.148Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-19T01:22:54.097Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-19T02:22:51.267Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-19T02:28:06.281Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-19T03:28:40.219Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-19T03:35:46.221Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-19T04:41:58.300Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-19T04:49:50.417Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-19T05:57:54.487Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-19T06:43:11.390Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-19T09:12:49.606Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-19T09:29:53.590Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-20T03:39:39.610Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-20T03:42:03.650Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-20T04:41:55.897Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-20T04:47:38.833Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-20T05:48:38.700Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-20T05:55:45.486Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-20T06:00:23.429Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-20T06:36:28.105Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-20T09:50:15.244Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-20T10:01:52.926Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-21T03:00:04.587Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-21T03:05:11.361Z
|
|
||||||
binary_sensor.heatpump_compressor,unknown,2026-09-21T08:18:28.438Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-21T08:18:30.643Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-22T03:00:05.235Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-22T03:20:51.957Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-22T09:45:24.980Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-22T10:05:30.687Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-23T03:00:08.414Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-23T03:12:46.457Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-23T09:45:29.000Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-23T10:04:03.857Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-24T03:00:09.997Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-24T03:11:26.925Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-24T12:34:34.499Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-24T13:05:30.499Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-25T03:00:07.285Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-25T03:39:07.324Z
|
|
||||||
binary_sensor.heatpump_compressor,unknown,2026-09-25T08:35:58.362Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-25T08:36:05.755Z
|
|
||||||
binary_sensor.heatpump_compressor,unknown,2026-09-25T08:36:57.698Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-25T08:36:58.846Z
|
|
||||||
binary_sensor.heatpump_compressor,unknown,2026-09-25T08:41:12.345Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-25T08:41:15.133Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-25T09:28:41.719Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-25T09:54:51.506Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-26T00:42:45.260Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-26T00:44:28.166Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-26T01:18:30.843Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-26T01:23:43.918Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-26T02:10:50.330Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-26T02:18:56.282Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-26T03:08:29.828Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-26T03:17:03.786Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-26T04:07:50.486Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-26T04:17:22.353Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-26T05:06:08.060Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-26T05:16:05.238Z
|
|
||||||
binary_sensor.heatpump_compressor,on,2026-09-26T06:00:08.484Z
|
|
||||||
binary_sensor.heatpump_compressor,off,2026-09-26T06:37:50.037Z
|
|
||||||
|
+3
-3
@@ -348,11 +348,11 @@ async function init() {
|
|||||||
}
|
}
|
||||||
state.datasets = files.map(f => f.name);
|
state.datasets = files.map(f => f.name);
|
||||||
|
|
||||||
// last selection if still present, otherwise the most recently modified file
|
// last selection if still present, otherwise the last file by name
|
||||||
|
// (mtimes are useless here: git checkouts in CI give all files the same time)
|
||||||
let saved = null;
|
let saved = null;
|
||||||
try { saved = localStorage.getItem(DATASET_KEY); } catch { /* storage unavailable */ }
|
try { saved = localStorage.getItem(DATASET_KEY); } catch { /* storage unavailable */ }
|
||||||
const newest = files.reduce((a, b) => Date.parse(b.mtime) > Date.parse(a.mtime) ? b : a);
|
loadDataset(state.datasets.includes(saved) ? saved : state.datasets[state.datasets.length - 1]);
|
||||||
loadDataset(state.datasets.includes(saved) ? saved : newest.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|||||||
Reference in New Issue
Block a user