Skip to content

Latest commit

 

History

56 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Boiler Automation and Telemetry Stack

This repository contains a set of Python daemons, shell helpers, an Arduino sketch, and systemd units used to monitor and control a home heating and power setup through Home Assistant.

The project combines:

  • Boiler control (PID-based water temperature setpoint control).
  • OpenTherm gateway polling and command dispatch.
  • Inverter telemetry collection.
  • Mercury 230 power meter polling.
  • Furnace/kiln controller polling over Modbus ASCII.
  • 1-Wire temperature ingestion.

Architecture Overview

The system is built around periodic poll/push loops:

  • Hardware is read through serial ports or 1-Wire pseudo-files.
  • Data is normalized into Home Assistant entities via REST POST to /api/states/....
  • Control values (for example, boiler setpoint) are read from Home Assistant via REST GET.
  • cycler.sh schedules one-shot scripts as recurring jobs under systemd.

There are two parallel integration tracks:

  • Legacy track: scripts use token and host from secrets.
  • New track: scripts prefixed with new_ use new_token and new_host (for a newer HA environment / VM-backed setup).

Repository Components

Core Boiler Control

boiler_control.py

  • Main legacy heating control loop (executed periodically, not a persistent loop itself).
  • Reads boiler settings and temperatures from Home Assistant entities.
  • Reads thermostat target (climate.kitchen) and current indoor temperature.
  • Maintains PI(D)-like state in /tmp/pid.json (E, I, D, timestamp, output).
  • Computes target heat carrier temperature (calc_heatant), clamps to configured min/max.
  • Applies anti-freeze protection if heat carrier temperature is too low.
  • Writes:
    • Thermostat hvac state (heat / off) back to HA.
    • Boiler setpoint sensor (sensor.heatant_setpoint) back to HA.
  • Includes a failsafe path: on error, forces heating with a high default temperature.

new_boiler_control.py

  • New-environment equivalent of boiler_control.py.
  • Uses fixed local settings in script (instead of reading settings entities at runtime).
  • Reads thermostat from climate.main_climate and heat carrier temp from input_number.heatant_temp.
  • Writes setpoint to input_number.heatant_setpoint.
  • Keeps same PI regulator pattern and /tmp/pid.json persistence logic.

OpenTherm Integration

otgw.py

  • Legacy OpenTherm gateway poller for /dev/otgw.
  • Reads desired setpoint from HA (sensor.heatant_setpoint), with optional CLI override.
  • Sends gateway shell commands:
    • s <temp> to set target.
    • g to fetch boiler JSON state.
  • Publishes returned values to HA:
    • Heat carrier temperature (sensor.heatant_temp).
    • Boiler modulation (sensor.boiler_power).
    • Boiler fault (sensor.boiler_fault).
  • Retries malformed/non-JSON serial responses up to a max retry count.

new_otgw.py

  • New-environment OpenTherm poller.
  • Reads/writes input_number / input_boolean entities instead of legacy sensor entities.
  • Adds flame state publication (input_boolean.boiler_flame).
  • Converts boolean fault/flame fields into HA-compatible on/off state strings.
  • Contains prepared (currently commented) update path for climate state synchronization.

opentherm.ino

  • Arduino firmware implementing the serial-side OpenTherm gateway protocol used by otgw.py/new_otgw.py.
  • Uses OpenTherm and Shell libraries.
  • Exposes serial commands:
    • g -> JSON boiler snapshot (Tout, Tin, Modulation, Fault, Flame, etc.).
    • s <temp> -> set boiler target temperature; disables heating below threshold.
    • p -> ping/status.
  • Polls boiler on interval and updates internal state fields used by g.

otgw_checker.sh

  • Health-recovery helper for missing /dev/otgw.
  • If symlink is absent, power-cycles a specific USB device via /sys/bus/usb/.../authorized.
  • Used as an automated self-healing action by otgw_checker.service.

Inverter Gateway

mapgw.py

  • Legacy MAP inverter poller for /dev/ttyMAP (19200 baud).
  • Sends vendor commands (PCRD...) and parses MOk.. responses.
  • Publishes inverter mode and grid/battery measurements into HA sensor.* entities.
  • Supports decoded mode string mapping from numeric state.

new_mapgw.py

  • New-environment version targeting input_text / input_number entities.
  • Adds additional power metric (Pgrid) compared to the older flow.
  • Keeps same serial protocol and command/response strategy.

Power Meter (Mercury 230)

m230.py

  • Continuous poller for Mercury 230 meter (/dev/moxa by default).
  • Implements low-level protocol framing and CRC16 calculation internally.
  • Collects:
    • Per-phase voltage/current/cos(phi).
    • Derived active and reactive power.
    • Total active/reactive sums.
    • Frequency.
    • Energy sums (including T1-T4 style channels).
  • Flattens nested measurement tree into HA entity IDs (sensor.m230_*) and posts them.
  • Includes safety/failure gates:
    • Overvoltage/overcurrent detection.
    • Refusal to publish partial/bad payloads if acquisition fails.

Furnace / Kiln Polling

furnance.py

  • Poller for TRM251-like furnace/kiln controller over Modbus ASCII (minimalmodbus).
  • Reads state, temperature, and power from specific registers.
  • Publishes to:
    • sensor.furnance_temp
    • sensor.furnance_power
    • sensor.furnance_state
  • Persistent retry loop with high error tolerance and auto-restart behavior.

Note: file/service names use furnance spelling consistently in this repository.

1-Wire Ingestion

wire2ha.py

  • Reads local 1-Wire temperature files from /1wire/.../temperature.
  • Pushes values to HA entities (kitchen and street temperature sensors).
  • Acts as a bridge between filesystem-exported sensor values and HA REST states.

Scheduling and Service Wrappers

cycler.sh

  • Generic loop runner for one-shot scripts:
    • Runs /usr/local/sbin/<script>
    • Sleeps <interval>
    • Repeats forever
  • Default: wire2ha.py every 60 seconds.
  • Used by several systemd services to standardize periodic execution.

Device Naming

99-usb-serial.rules

  • Udev persistent symlink rules:
    • OpenTherm adapter -> /dev/otgw
    • Bluetooth stick -> /dev/btstick
    • MAP interface -> /dev/ttyMAP
  • Stabilizes device discovery across reboots and USB enumeration changes.

systemd Units

boiler_control.service

  • Runs boiler_control.py via cycler.sh every 60s.
  • Depends on hass.service and wire2ha.service.

otgw.service

  • Runs otgw.py every 60s.
  • Depends on hass.service.

mapgw.service

  • Runs mapgw.py every 60s.
  • Depends on hass.service.

wire2ha.service

  • Runs wire2ha.py every 60s.
  • Depends on owserver.service and hass.service.

m230.service

  • Runs m230.py as a continuously looping process.

furnance.service

  • Runs furnance.py continuously.
  • Adds/removes an NPort Real COM mapping (mxaddsvr / mxdelsvr) on service start/stop.

new_boiler_control.service

  • Runs new_boiler_control.py every 60s.
  • Depends on qemu-kvm.service.

new_otgw.service

  • Runs new_otgw.py every 60s.
  • Depends on qemu-kvm.service.

new_mapgw.service

  • Runs new_mapgw.py every 60s.
  • Depends on qemu-kvm.service.

otgw_checker.service

  • Runs otgw_checker.sh every 60s via cycler.sh.
  • Depends on qemu-kvm.service.

Home Assistant Integration Pattern

All Python integration scripts follow the same pattern:

  • Import token/host or new_token/new_host from secrets.
  • Use HA REST API headers:
    • Authorization: <token>
    • content-type: application/json
  • Use /api/states/<entity_id> for both reads and writes.

Expected local dependency:

  • A local secrets.py (not committed) containing API endpoint and auth token values.

Hardware/Interface Expectations

Expected device paths:

  • /dev/otgw for OpenTherm adapter.
  • /dev/ttyMAP for inverter interface.
  • /dev/moxa (or configured equivalent) for Mercury 230 meter.
  • /dev/ttyr00 for furnace controller.
  • /1wire/... filesystem for temperature sensors.

Commit History Analysis (Component Evolution)

Key milestones from project history:

  1. Initial platform bootstrap: May 2020
  • Added boiler, OpenTherm, inverter, 1-Wire scripts and basic services in the first commit.
  1. Device stability and platform hardening: June 2020 to January 2021
  • Added persistent USB aliasing (99-usb-serial.rules).
  • Removed unnecessary service dependency from otgw.service.
  1. Mercury 230 growth: June 2020 to May 2023
  • Introduced as a separate module, then iteratively expanded.
  • Added Home Assistant posting, friendly names/units, energy counters, reactive power, and data-quality guards.
  • Added overcurrent and tighter overvoltage checks.
  1. Boiler control maturation: January 2021 to December 2021
  • Switched to PI-based regulation and tuned PID constants.
  • Increased debug visibility and refined behavior.
  1. Furnace integration: May 2021 to July 2021
  • Added dedicated monitor script and service.
  • Migrated to Python 3, Modbus ASCII, and NPort start/stop hooks.
  1. OpenTherm firmware support: September 2021
  • Added Arduino sketch implementing gateway protocol consumed by Python pollers.
  1. New-environment migration: September 2025 to November 2025
  • Added new_* gateway/control scripts and services tied to qemu-kvm.service.
  • Mapped outputs to alternate HA entity model (input_number, input_boolean, input_text).
  • Extended new inverter flow with Pgrid.
  1. Self-healing operations: November 2025
  • Added otgw_checker.sh + service to recover missing OpenTherm USB path.

Operational Notes

  • Most scripts are intentionally simple and state-light; reliability is delegated to:
    • systemd restart policies.
    • cycler.sh periodic execution.
    • Retries and fail-safe defaults inside scripts.
  • Debug logging is widely enabled in current source (debug = True / DEBUG = True in many files), useful for diagnostics but potentially noisy in production logs.
  • Several entities are written directly under /api/states, which creates state objects but does not replace proper native integrations where available.

Running and Deployment

Typical deployment model inferred from service files:

  1. Install scripts to /usr/local/sbin/.
  2. Install service units to /etc/systemd/system/.
  3. Reload and enable units:
    • systemctl daemon-reload
    • systemctl enable --now <service>
  4. Ensure device rules and dependencies are present (udev, serial drivers, minimalmodbus, requests, Home Assistant availability).

File Index

  • Boiler control: boiler_control.py, new_boiler_control.py, boiler_control.service, new_boiler_control.service
  • OpenTherm: otgw.py, new_otgw.py, opentherm.ino, otgw.service, new_otgw.service, otgw_checker.sh, otgw_checker.service
  • Inverter: mapgw.py, new_mapgw.py, mapgw.service, new_mapgw.service
  • Power meter: m230.py, m230.service
  • Furnace: furnance.py, furnance.service
  • 1-Wire: wire2ha.py, wire2ha.service
  • Infrastructure: cycler.sh, 99-usb-serial.rules

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages