Commit Graph
50 Commits
Author SHA1 Message Date
tliu93 57f2459f60 M6-T09: add Energy data API (prices/costs/summary/dsmr-latest/recompute/tibber-test)
- routes/api/energy.py + schemas/energy.py: GET prices (tibber points or manual
  tariff, buy/sell consistent with the pricing strategies), GET costs (time
  window + limit, ascending), GET costs/summary (summarize passthrough), GET
  dsmr/latest, POST costs/recompute (idempotent, <=366d guard, CSRF), POST
  tibber/test (three-state, token never echoed, CSRF).
- main.py registers router; OpenAPI re-exported; tests for all endpoints.
2026-06-23 23:05:45 +02:00
tliu93 c61fc2d4ba M6-T08: add energy_cost expose provider + post-billing state publish
- expose.py: _energy_cost_provider yields buy/sell_price_now + import_cost_total
  /export_revenue_total (total_increasing, monetary), value_getters read from
  energy_cost_period; 2-element identifiers so the existing HA Discovery builder
  (which indexes identifiers[1]) works. Other providers/mechanism untouched.
- main.py: energy-cost job calls publish_states after computing periods (no-op
  when MQTT/Discovery off). Tests added.
Note: energy-cost sensors lack an availability heartbeat (ha_discovery mechanism
limitation, out of scope here).
2026-06-23 22:44:41 +02:00
tliu93 d35ac7d752 M6-T07: add metering billing engine + period job + summary + recompute
- app/services/energy_cost.py: register_at (register values at a UTC boundary),
  compute_period (register deltas x active-version strategy, snapshot price +
  contract_version_id, immutable unless overwrite), compute_closed_periods
  (bounded lookback), recompute_range (explicit overwrite), summarize
  (sum net + standing fees month/30 - heffingskorting year/365).
- Missing tibber price -> skip; missing reading -> degraded (zeroed amounts,
  null version), consistently on both create and recompute paths.
- main.py: 1-minute energy-cost job (existing jobs/MQTT untouched). Tests added.
2026-06-23 22:26:56 +02:00
tliu93 ef48032adb M6-T06: extend MqttManager with subscribe + DSMR ingest
- mqtt.py: subscription registry, on_message dispatch (swallows handler
  errors), re-subscribe on (re)connect; publish/connect/disconnect/reconnect
  unchanged.
- app/services/dsmr_ingest.py: handle_message stores the full telegram frame
  (no allowlist; gas/phases/null preserved), 10s downsample by timestamp.second,
  source_id idempotency (select + IntegrityError rollback). Net-thread safe.
- main.py registers the DSMR subscription only when dsmr_ingest_enabled.
2026-06-23 21:52:57 +02:00
tliu93 7e266a21eb M6-T05: add Tibber GraphQL client + price refresh service + schedule
- app/integrations/tibber/client.py: fetch_price_range/fetch_current_price
  (priceInfoRange QUARTER_HOURLY, startsAt->UTC, no fixed node count assumption),
  TibberError/TibberAuthError; token never logged or put in exception text.
- app/services/tibber_prices.py: refresh_prices upserts by starts_at (idempotent),
  no-op unless an active kind=tibber contract + token exist.
- main.py: hourly tibber-refresh job (existing jobs/MQTT untouched). Tests added.
2026-06-23 21:35:29 +02:00
tliu93 4284bd40a7 M6-T04: add EnergyContract CRUD + versions + profile validation API
- app/services/contracts.py: create_contract, add_version (append-only,
  auto-closes prior version), activate_contract (mutex), and
  active_contract_version_at (half-open [from, to)) for the billing engine.
- app/schemas/energy_contract.py + routes/api/energy_contracts.py: 6 endpoints
  (GET/POST contracts, GET/PATCH contracts/{id}, POST .../versions, GET profiles).
  Values validated against pricing profiles (422 on mismatch); no DELETE.
- main.py registers router; OpenAPI re-exported; tests for CRUD/version/activate.
2026-06-23 21:19:25 +02:00
tliu93 bedea196c3 M6-T03: add pricing profile framework + strategy registry
- app/integrations/pricing: profiles.py (pydantic ManualProfile/TibberProfile,
  load_profile/list_profiles/validate_values), manual.yaml + tibber.yaml
  (structure-only, no price values), strategies.py (register/get_strategy,
  manual dual-tariff + tibber strategies, all Decimal).
- tibber strategy matches nearest tibber_price with starts_at <= t0.
- tests for profiles + strategies (hand-checked dual-tariff & tibber math).
2026-06-23 20:58:34 +02:00
tliu93 df54f5518b M6-T02: add DSMR + Tibber flat config fields
- Settings: dsmr_ingest_enabled, dsmr_mqtt_topic, dsmr_sample_interval_s,
  tibber_api_token (secret), tibber_home_id (all opt-in / off by default).
- CONFIG_FIELDS DSMR + Tibber sections; _settings_payload extended.
- .env.example examples; test_api_config coverage (sections, secret, 422).
2026-06-23 20:43:46 +02:00
tliu93 18fe18281b M6-T01: add 5 energy tables, models, and migration
- app/models/energy.py: DsmrReading, EnergyContract, EnergyContractVersion,
  TibberPrice, EnergyCostPeriod (per design §3.5; JSON blobs, RESTRICT FKs).
- alembic_app migration 20260623_11_energy_tables (head), env.py imports.
- app_db_adopt APP_BASELINE_REVISION bumped to new head.
- tests/test_energy_models.py + expose_catalog test fixups for new head.
2026-06-23 20:33:35 +02:00
tliu93 c64693b8e5 M6: add design doc — generic pricing layer (manual/tibber) + DSMR cost engine 2026-06-23 19:53:26 +02:00
tliu93 4767a7af60 M5-T13: document Modbus/Energy/MQTT, mark M5 done, finalize OpenAPI 2026-06-22 16:10:11 +02:00
tliu93 45d87f36f7 M5-T12: add expose toggle API and Home Assistant Expose settings panel 2026-06-22 15:50:49 +02:00
tliu93 35b95f5c88 M5-T11: add HA discovery and state publishing wired to polling 2026-06-22 15:32:43 +02:00
tliu93 9d05f5ea62 M5-T10: add paho MQTT client with lifespan connect/reconnect and mqtt/test endpoint 2026-06-22 15:06:30 +02:00
tliu93 360cddc05b M5-T09: add exposed_entities table and ExposableEntity provider framework 2026-06-22 14:50:40 +02:00
tliu93 14b846549e M5-T08: add MQTT, HA Discovery, and Modbus polling config fields 2026-06-22 14:27:21 +02:00
tliu93 68165f0e01 M5-T07: add latest-reading cards and Recharts trend charts; readings return most-recent rows 2026-06-22 14:17:59 +02:00
tliu93 2dc469274a M5-T06: add Energy device management UI and sidebar entry 2026-06-22 13:52:33 +02:00
tliu93 702a1cec00 M5-T05: add Modbus JSON API (device CRUD, readings, metrics, test) 2026-06-22 13:37:48 +02:00
tliu93 49d15d8ffe M5-T04: add Modbus poll service and APScheduler polling job 2026-06-22 13:24:11 +02:00
tliu93 c89cea4953 M5-T03: add Modbus TCP driver, YAML profile framework, sdm120, modbus_cli 2026-06-22 13:03:50 +02:00
tliu93 d9c82e39fb M5-T02: add modbus_device and modbus_reading tables and models 2026-06-22 12:45:50 +02:00
tliu93 eac7860b51 M5-T01B: render ConfigPage sections as Mantine Accordion 2026-06-22 12:34:04 +02:00
tliu93 da7d0f9d62 M5-T01: refactor AppLayout from top bar to sidebar navigation 2026-06-22 12:26:52 +02:00
tliu93 e49a6ad963 M5(design): generic modbus_device + JSON readings; YAML profiles; Accordion config; manual CLI/MQTT testing
frontend / frontend (push) Successful in 1m26s
pytest / test (push) Successful in 3m52s
Rework the M5 design doc data model and add manual-testing surface:

- Two-layer model: rename energy_meters/energy_readings -> generic
  modbus_device + modbus_reading; readings become a JSON payload blob
  instead of a wide fixed-column table (aggregation via SQLite
  json_extract, generated-column index as a future lever).
- Protocol knowledge moves to read-only YAML profiles (data+functions,
  no OOP inheritance); deployment/config (friendly_name, unit_id, host)
  stays on the device row; multiple devices share one profile.
- UUID = stable internal identity, also anchors HA discovery unique_id
  (Z2M rename model); device metadata for HA derived from the profile.
- Naming scheme C: modbus_* / /api/modbus/devices at the data+API layer,
  'Energy' as the first domain view in the frontend.
- New task M5-T01B: ConfigPage Accordion sectioning (avoids a second
  in-page side nav conflicting with the T01 app sidebar).
- Manual testing: read-only modbus_cli 'probe' subcommand (FC03/04 only,
  no register writes); mqtt/test now publishes a test message viewable
  in MQTT Explorer; no MQTT CLI (manual verification via UI + HA).
2026-06-22 11:55:39 +02:00
tliu93 ae75e4582d M4-T09: document login hardening, finalize OpenAPI and roadmap 2026-06-21 23:01:50 +02:00
tliu93 ee1264b66b M4-T08: add two-step login and TOTP settings panel (qrcode.react) 2026-06-21 22:42:41 +02:00
tliu93 51da8c5716 M4-T07: add admin_cli disable-totp and reissue-totp escape commands 2026-06-21 22:27:07 +02:00
tliu93 81bd32f613 M4-T06: add TOTP second factor to login (totp_code + recovery codes) 2026-06-21 22:08:55 +02:00
tliu93 ee3031013e M4-T05: add TOTP service and setup/enable/disable/status API 2026-06-21 21:55:17 +02:00
tliu93 5026967cee M4-T04: add AuthUser TOTP columns and auth_recovery_code table 2026-06-21 21:38:22 +02:00
tliu93 5481fbc99c M4-T03: add admin_cli escape hatch (reset-password, unlock, list-admin) 2026-06-21 21:28:41 +02:00
tliu93 e480a84a44 M4-T02: add exponential login back-off service and wire into login endpoint 2026-06-21 21:20:28 +02:00
tliu93 64d3882c16 M4-T01: add auth_login_throttle table + LoginThrottle model 2026-06-21 21:03:20 +02:00
tliu93 0cb94d85ec docs(design): plan M4 login-hardening (first) + M5 IoT/energy
- Add docs/references/SDM120-Modbus-Protocol.md extracted from the SDM120 PDF
- M4 login hardening: brute-force exponential backoff, CLI escape hatch
  (reset-password/unlock/disable-totp), optional TOTP 2FA
- M5 IoT/energy: Modbus-TCP SDM120 polling + energy tables, MQTT/HA
  Discovery expose framework, frontend sidebar + energy view
- Add manual acceptance walkthroughs (M4 lock/unlock; M5 energy_cli read)
- Index both milestones in docs/design/README.md
2026-06-21 20:46:12 +02:00
tliu93 bd09523e94 M2-T13: docs wrap-up + retire frontend constraints + dependency cleanup
- README: add 前端 v2 (React SPA) section (dev/build/codegen/hosting/gates),
  update directory listing, drop stale Jinja descriptions
- architecture-overview: retire '不引入前后端分离' constraint; reflect SPA + JSON API
- roadmap: mark M2 done
- remove orphaned jinja2 dependency (recompile requirements*.txt; no other churn)
- delete empty tests/test_auth.py stub; drop dead _extract_csrf_token in test_api_data
- verified image still builds and app imports with the slimmer deps
2026-06-13 15:20:50 +02:00
tliu93 53f1245d83 docs(m2): mark M2-T12 done 2026-06-13 15:20:50 +02:00
tliu93 f8b1e5fc71 docs(m2): mark M2-T11 done 2026-06-13 15:20:50 +02:00
tliu93 8aa7316b26 docs(m2): mark M2-T09 done 2026-06-13 15:20:50 +02:00
tliu93 0d988a9b28 docs(m2): mark M2-T10 done 2026-06-13 15:20:50 +02:00
tliu93 6cc6382515 docs(m2): mark M2-T08 done 2026-06-13 15:20:50 +02:00
tliu93 cc2c02a2e2 docs(m2): mark M2-T07 done 2026-06-13 15:20:50 +02:00
tliu93 8975acc48b docs(m2): mark M2-T06 done 2026-06-13 15:20:50 +02:00
tliu93 dba9e28540 docs(m2): mark M2-T05 done 2026-06-13 15:20:50 +02:00
tliu93 3ec663e138 docs(m2): mark M2-T04 done 2026-06-12 23:35:56 +02:00
tliu93 9ce3f2a0b8 docs(m2): mark M2-T03 done 2026-06-12 23:27:02 +02:00
tliu93 d8303eaa3d docs(m2): mark M2-T02 done 2026-06-12 23:18:43 +02:00
tliu93 de77019ce3 docs(m2): mark M2-T01 done 2026-06-12 23:11:38 +02:00
tliu93 66ec9979cc docs(m2): lock M2 frontend design decisions
pytest / test (push) Failing after 11m46s
Record the decisions reached in planning into docs/design/m2-frontend-v2.md:
component library = Mantine; map = Leaflet (react-leaflet + leaflet.heat +
markercluster, isolated behind a component seam for a future MapLibre swap);
OpenAPI typed client committed + CI-checked; CSRF simplified to SameSite=Lax +
a custom write header (no per-session token); heatmap-first map as the home
view with a required time-range picker and an auxiliary paginated list;
record CRUD edits non-PK fields and deletes single rows (no UI create); bare
ingestion endpoints stay until M3; trips optional. Wireframes intentionally
skipped for this milestone.
2026-06-12 22:40:57 +02:00
tliu93 b359bbe3bf docs: add next-phase roadmap, milestone design docs, and CLAUDE.md
pytest / test (push) Successful in 54s
- roadmap.md: M1 (DB consolidation) -> M2 (React SPA) -> M3 (token/mobile)
- docs/design/: agent-pipeline design docs with atomic tasks for M1-M3
- CLAUDE.md: workflow, doc map, commit conventions, review-notes briefing flow
- .gitignore: ignore local review-notes/
2026-06-12 15:37:17 +02:00