Go to file
2026-09-22 22:26:58 +01:00
data init commit 2026-09-22 22:26:58 +01:00
public init commit 2026-09-22 22:26:58 +01:00
src init commit 2026-09-22 22:26:58 +01:00
.env.example init commit 2026-09-22 22:26:58 +01:00
.gitignore init commit 2026-09-22 22:26:58 +01:00
package-lock.json init commit 2026-09-22 22:26:58 +01:00
package.json init commit 2026-09-22 22:26:58 +01:00
README.md init commit 2026-09-22 22:26:58 +01:00

DVgo device emulator

Node.js ThingsBoard device emulator with a SQLite-backed web portal. Each virtual device can connect over MQTT, publish live *_s telemetry, react to shared-attribute commands from the apps, and be taken offline without deleting the ThingsBoard record.

Requires Node 22+ (uses the built-in node:sqlite module).

Python historical data generation is not included.

What it does

  • Create / delete real ThingsBoard devices via the REST API (delete still removes the emulator row if the TB device is already gone)
  • Persist credentials, telemetry, published-key selection, and plant settings in SQLite
  • MQTT connect with the device access token as username (v1/devices/me/telemetry, plus attributes and RPC)
  • Web portal (http://localhost:3080) to:
    • add devices (or attach an existing MQTT access token)
    • select devices (select all / deselect all) and enable, disable, or delete
    • Connect / Disconnect MQTT (new devices stay idle until you connect)
    • start / stop the telemetry loop (Start telemetry is disabled until connected)
    • toggle which keys are included in each publish (per-card On, plus select all / deselect all under Publish now)
    • edit every live _s key from the device dashboard
    • run a virtual plant that follows boost and schedule commands from the apps

On startup, devices already in SQLite are restored. They reconnect only if they were left enabled (not disabled). New devices are created disconnected, with telemetry off.

Virtual plant

While MQTT is connected, the device polls ThingsBoard shared attributes every 5 seconds (and also receives live pushes). It understands:

Source Keys
One-shot boost boostNowHw, boostNowAux (minutes; written by the apps via TB REST)
HW schedule hwRlyMode 2 or 3, plus hwRlySchedMode0/1StartTime / StopTime (minutes since midnight)
Aux schedule auxRlyMode 2 or 3, plus auxRlySchedMode0/1Start / Stop

Schedule saves in the apps are SQL-only, so this emulator polls rather than waiting for a TB push.

Behaviour:

  • Red / grey lamps for hot water boost and aux boost / divert
  • While HW is on, heatsink_s rises at Heat rate (°C / min) (integer, default 1)
  • At Thermostat max (°C) (default 65) heating stops, rlyInSel_s goes to 0, and boostNowHw is written back to ThingsBoard as 0
  • Live state is published as heatsink_s, rlyInSel_s, auxOn_s, auxRelay_s

Telemetry keys

Each device can publish the following live *_s keys on v1/devices/me/telemetry. Dashboard cards are ordered Status, Power, Energy flows, Energy today, Validity. Only keys with On checked are sent.

Status

  • heatsink_s — hot water / tank temperature (°C)
  • batterySoc_s — battery state of charge (%)
  • soc_s — SoC fallback (%)
  • batteryPercent_s — battery percent fallback (%)
  • auxOn_s — aux on (0/1)
  • auxRelay_s — aux relay (0/1)
  • rlyInSel_s — control load heating (0/1)
  • xCntlLoadState — control load state (integer; no _s suffix)

Power (W)

  • solarPower_s — solar generation
  • loadPower_s — home load
  • hotWaterInPower_s — hot water in
  • hotWaterOutPower_s — hot water out
  • batteryPower_s — battery power (negative = charging, positive = discharging)
  • gridPower_s — grid power (positive = importing, negative = exporting)

Energy flows (W)

  • solarToLoadPower_s
  • solarToHotWaterPower_s
  • solarToBatteryPower_s
  • solarToGridPower_s
  • gridToLoadPower_s
  • gridToHotWaterPower_s
  • gridToBatteryPower_s
  • batteryToLoadPower_s
  • batteryToHotWaterPower_s
  • batteryToGridPower_s

Energy today (Wh)

  • solarToday_s
  • loadToday_s
  • hotWaterInToday_s
  • hotWaterOutToday_s
  • solarToLoadToday_s
  • solarToHotWaterToday_s
  • solarToGridToday_s
  • solarToBatteryToday_s
  • gridToLoadToday_s
  • gridToHotWaterToday_s
  • gridToBatteryToday_s
  • batteryToLoadToday_s
  • batteryToHotwaterToday_s — note the lowercase w in Hotwater
  • batteryToGridToday_s
  • batteryExportToday_s
  • batteryImportToday_s
  • gridImport_s
  • gridExport_s

Validity

  • vPwrReadings_s — bitmask of valid live power keys (65535 = bits 0–15 all valid)

Setup

cp .env.example .env
# fill TB_USERNAME / TB_PASSWORD (tenant admin or a user who can create devices)
npm install
npm start

Open http://localhost:3080.

Optional env:

  • PORT — portal port (default 3080)
  • TB_HTTP_URL — ThingsBoard REST origin (default https://tb-dvgo.datavolt.au)
  • TB_DEVICE_PROFILE_ID — defaults to the Diverter / DVgo profile (e6b53240-7fec-11f1-a69d-edbc0b9170ac). The add-device dropdown is preselected to a profile named Diverter or DVgo when ThingsBoard returns one.
  • TB_DEVICE_TYPE — default DVgo
  • TB_MQTT_URL — default mqtts://tb-dvgo.datavolt.au:8883 (TLS). Port 1883 is not reachable from outside the TB host.
  • TB_MQTT_INSECURE — true to skip TLS certificate verification
  • PORTAL_USER / PORTAL_PASSWORD — basic auth for the control API
  • ALLOWED_IPS — comma-separated allowlist for /api
  • TELEMETRY_INTERVAL_MS — default 5000
  • DB_PATH — default ./data/emulator.db

If a device already exists in ThingsBoard, paste its MQTT access token in the add form and skip REST provisioning. If create times out, the portal can look the device up by name and adopt it.