Deploying and verifying
Continues Manual installation from section 6.
6. Deploy from the development machine
The software always reaches the node as an installation package (bundle). It holds the dashboard binary, the Python services with their wheels, units and manifests, and the bootstrap steps that install them. The desktop installer ships a released version or a package file you pick. For a build of your own checkout, use the installer’s developer CLI. It builds the bundle, uploads it over SSH and runs the same steps:
scripts/dev/run-installer.sh deploy --dev-unsigned --dry-run # look first
scripts/dev/run-installer.sh ensure-secrets --dev-unsigned # first run: MQTT and admin passwords
scripts/dev/run-installer.sh deploy --dev-unsigned
The CLI reads the target host and user from the gitignored
secrets/deploy-target.env (TARGET_USER, TARGET_HOST) and uses
/home/<TARGET_USER> as the base unless you pass --host, --user or
--base. Installer developer CLI covers SSH
authentication and every flag.
The first run does the one-time setup:
/etc/energy-node/mqtt.pw:ensure-secretsasks for the password, keeps a local copy in the gitignoredsecrets/, and step 60 installs it asroot:energynode, mode0640./etc/energy-node-dashboard/auth.pw: the dashboard admin password, same treatment./etc/energy-node/config.json: installed fromservices/energy-node.config.jsonif missing.
A redeploy never overwrites existing files. The dashboard may edit
config.json, and a deploy must not throw those edits away. Run
deploy --force-config (it asks for confirmation) if you want the template
back.
A full deploy runs every step and restarts every service. Restrict a run with
--only <target>: dashboard, wheels, or a service id (apsystems,
battery-soc, shelly, trucki, tuya, automation). That step runs even
if it already ran for this version, and its unit restarts afterwards.
restart [--only <target>] restarts units without deploying anything.
On every run, step 60 also writes the complete set of service manifests to
/etc/energy-node/manifests/<service_id>.json. The dashboard and the Python
bridges read the active services from this directory, and the Python bridges
do not start without it. The set has to match the services block in
config.json, otherwise the bridges fail to start.
Upgrading from an earlier release (≤ 0.4)
A deploy never overwrites a live config.json or a deployed device file, so
some changes need manual work. Do the following once on the node, around the
first start of the new dashboard:
-
Retire the old Python node service. Node telemetry is now part of the dashboard binary as
internal/nodeagent, and the separate service no longer exists. If it keeps running, it becomes a second publisher onoutstation/energy_node/….sudo systemctl disable --now energy-node.service sudo rm -f /etc/systemd/system/energy-node.service sudo systemctl daemon-reload rm -rf ~/energy-node # the old service's code dir under the target base -
Let the dashboard migrate
config.json. Aschema_version 1file has a top-levelnodeblock, a hyphenatednode.device_idand, on very old nodes,services.*.device_idinstead ofservice_id. The new dashboard upgrades it to version 2 on its first start. The four remainingnode.*fields move todashboard.node_*,node.managed_bridgesis dropped,node_device_idbecomes"energy_node"and everyservices.*.device_idis renamed toservice_id. The dashboard writes the result back through its privileged system action helper (apply-app-config) and keeps the old file as/etc/energy-node/.config.json.bak. This needs the helper and its sudoers entry, which bootstrap step 60 installs (deploy --only dashboard). Without them the dashboard still runs on the migrated config, but the file on disk stays at version 1. The Python services reject version 1, so start the dashboard first and restart the bridges afterwards (systemctl restart apsystems-ez1 shelly-rpc trucki-http tuya battery-soc automation). Anode_device_idother thanenergy-nodeorenergy_nodeis left as it is, and the dashboard then refuses to start. Set it to"energy_node"by hand in that case. -
Deliver service manifests. A node set up before this change has no
/etc/energy-node/manifests/. Rundeploy --only dashboardto create it (step 60 installs the manifests), then restart the Python bridges withrestartorsystemctl restart apsystems-ez1 shelly-rpc trucki-http tuya battery-soc automation. Without deploy access, create the six files by hand fromservices/<name>/manifest.json, each as{"service_id": "<id>", "unit": "<unit>", "schema": "config.schema.json", "required": [...]}. -
Fix
via_devicein the operator-editable device files. In the deployedbattery_soc_devices.jsonandtrucki_devices.json(underpaths.devices_dir, usually~/devices/), change everyvia_devicefrom"energy-node"to"energy_node"so Home Assistant keeps linking those entities to the node device.
7. Configure devices
Edit /etc/energy-node/config.json for the broker, paths, log level, the
settings of each service and the dashboard settings including the node_*
fields. ../operating/configuration.md
documents every field. There is no fallback to environment variables. If the
file is missing, the services do not start.
Devices live in separate JSON files under paths.devices_dir
(shelly_devices.json, apsystems_devices.json, trucki_devices.json,
tuya_devices.json, battery_soc_devices.json, automation_rules.json),
each with a schema next to it. The dashboard’s configuration editor is the
easiest way to fill them in. It validates against the schema, keeps revisions
and, for the battery service, suggests MQTT topics and JSON keys from payloads
it has seen.
Some devices need extra first steps.
For Tuya, run python3 -m tinytuya wizard once to get the device ID, local
key, IP and protocol version. It needs a Tuya IoT developer account linked to
the Smart Life app. The switch is not always data point 1, so start the
service by hand once and read the raw DPS dump in the log before you set
datapoints.switch.
For the APsystems EZ1, enable local mode on the inverter and enter its IP.
For Shelly, leave MQTT disabled on the devices, because the node polls them
over HTTP. Restart the service after editing shelly_devices.json. That file
is not reloaded while the service runs.
8. Dashboard access
Plain HTTP on port 8080 works right away. Admin sessions are blocked over plain HTTP, so for HTTPS and the admin login put Caddy in front.
Install Caddy on the node first. On ARMv6, take the armv6 or armhf build
from the Caddy project, since your distribution may not ship one. Then install
the repository’s Caddyfile:
scp dashboard/Caddyfile <user>@<node>:/tmp/Caddyfile
ssh <user>@<node> "
sudo install -o root -g root -m 0644 /tmp/Caddyfile /etc/caddy/Caddyfile &&
sudo caddy validate --config /etc/caddy/Caddyfile &&
sudo systemctl reload caddy"
tls internal issues a certificate from Caddy’s local CA. The CA is not
trusted system wide automatically, because the Caddy service user has no sudo
rights, so browsers warn until you trust it yourself. Deploying the dashboard
does not touch the Caddy configuration.
To serve the dashboard under a sub-path behind another reverse proxy, set
X-Forwarded-Prefix (or X-Ingress-Path for Home Assistant ingress). See
../operating/reverse-proxy.md.
9. Verify
systemctl is-active mosquitto tailscaled energy-node-dashboard.service
systemctl is-active apsystems-ez1 shelly-rpc trucki-http tuya battery-soc \
automation
journalctl -u shelly-rpc -f
mosquitto_sub -h localhost -u <mqtt-user> -P <password> -t 'outstation/#' -v
Then check, in this order:
- The node appears in Home Assistant as its own device, with CPU, RAM, disk, throttling and Tailscale sensors.
- The devices of every bridge appear, linked to the node via
via_device. - The dashboard shows live values, and the bridges acknowledge its central poll interval controls.