thermocktat

module
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 20, 2026 License: MIT

README

Thermocktat

A lightweight thermostat emulator, primarily designed for BMS software testing (Building Management Systems).

A cool dino just for fun

Attributes

Name Type Default Comment
ambient_temperature float 21.0 Current temperature reading.
setpoint_temperature float 22.0 Target temperature. Must be between setpoint_temperature_min and setpoint_temperature_max.
mode string "auto" Operating mode: auto | heat | cool | fan.
fan_speed string "medium" Fan speed setting: auto | low | medium | high.
enabled boolean true Indicates if the thermostat is powered (on/off).
setpoint_temperature_min float 16.0 setpoint lower bound.
setpoint_temperature_max float 28.0 setpoint upper bound.

Regulation - ambient temperature simulation

The regulation of ambient temperature is simulated using a PID regulator and hysteresis (see diagram below).

Thermocktat Regulation Diagram

Regulation simulates the effect of heating or cooling systems controlled by the thermostat that will actually heat and cool the room in order to reach the desired temperature (setpoint).

This example is for the heating mode. If the ambient temperature is above setpoint, or below within a hysteresis range (- TargetHysteresis, 1°C in this example), heating is not triggered. When it is lower with a difference greater than the target hysteresis, heating start until the target temperature is reached. The target is the setpoint temperature plus the target hysteresis.

In auto mode, a second hysteresis ModeChangeHysteresis (greater than TargetHysteresis) can trigger switching regulation direction between cooling and heating. For example, if the TargetHysteresis is 1 and the ModeChangeHysteresis is 2 (default values):

  • if temperature setpoint is 20, and ambient temperature is above 22 (setpoint + ModeChangeHysteresis), regulation will switch to cooling, and cool until 19 (setpoint - TargetHysteresis);
  • if temperature setpoint is 20, and ambient temperature is below 18 (setpoint - ModeChangeHysteresis), regulation will switch to heating, and cool until 21 (setpoint + TargetHysteresis).

Regulation params can be set in the config.yaml file (see cmd/app/config_defaults.yaml). Regulation can also be disabled (in this case, ambient temperature will remain constant).

Heat losses (or gains) through room walls are also simulated and simply modeled by a conduction coefficient. A temperature delta proportional to the difference between outdoor and ambient temperatures and to this coefficient is added to the ambient temperature every second. The heat loss coefficient represents the room's thermal condictivity (the higher the coefficient, the higher the loss). It can be configured in the heat_loss section of the config file (see cmd/app/config_defaults.yaml). Set to 0 for no heat loss.

API Documentation

Configuration

Thermocktat can be configured from a file (see cmd/app/config_defaults.yaml).

Usage:

go run ./cmd/thermocktat -config config.yaml

Configuration can also be passed using environment variables with the TMK_ prefix. Environment variables have priority over config file.

If no config is provided, default values will be used (values from cmd/app/config_defaults.yaml).

For each controller, the addr field is in the format host:port (host will be localhost by default). For most controllers, it is used to set the url that the server will expose. For mqtt, addr is the address of the broker.

Usage

Run directly
TMK_CONTROLLER=http \
TMK_ADDR=:8080 \
go run ./cmd/thermocktat
Compile and run binary
go build -o thermocktat ./cmd/thermocktat

./thermocktat -config config.yaml

Docker Examples

# Run with default params
docker run -p 8080:8080 thermocktat

# Set controller and address using environment variables
docker run --rm -e TMK_CONTROLLER=http -e TMK_ADDR=:8080 -p 8080:8080 thermocktat

docker run --rm -e TMK_CONTROLLER=mqtt -e TMK_ADDR=tcp://host.docker.internal:1883 -e TMK_DEVICE_ID=my-thermocktat thermocktat

docker run --rm -e TMK_CONTROLLER=modbus -e TMK_ADDR=0.0.0.0:1502 -e TMK_DEVICE_ID=my-thermocktat -p 1502:1502 thermocktat

# Run with a config file mounted as a volume
docker run -v $(pwd)/config.yaml:/config.yaml -p 8080:8080 thermocktat -config /config.yaml

CI/CD

Two GitHub Actions workflows handle CI and releases separately.

CI (.github/workflows/ci.yaml)

Runs on PRs (and workflow_dispatch). Main is protected — all changes go through PRs.

test-lint-build ──→ integration-tests ──┐
                                        ├──→ docker-push
docker-build ──→ docker-scan ───────────┘
  • test-lint-build: Go quality checks (gofmt, goimports, go vet, gopls), unit tests with coverage, binary build.
  • docker-build: Builds linux/amd64 and linux/arm64 images in parallel, uploads as artifacts.
  • docker-scan: Runs Trivy vulnerability scan on the built image. Fails on CRITICAL/HIGH CVEs.
  • integration-tests: Python (pytest) end-to-end tests for each controller protocol.
  • docker-push: After all checks pass, pushes the validated images to ghcr.io tagged with the PR number (e.g., :pr-42).
Release (.github/workflows/release.yaml)

Runs on tag pushes. Finds the merged PR for the tagged commit, retags its :pr-N image with the version and latest — no rebuild. Signs the image with cosign (keyless) and creates a GitHub Release with an auto-generated changelog.

Releasing
git tag v0.7.0
git push origin v0.7.0

The release workflow finds the merged PR for that commit, locates the corresponding :pr-N image, retags it, signs it, and creates the GitHub Release.

License

MIT

Directories

Path Synopsis
cmd
app
thermocktat command
internal
logging
Package logging builds a configured *slog.Logger for the app.
Package logging builds a configured *slog.Logger for the app.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL