Control water_heater - rkubera/ArdPortal GitHub Wiki

water_heater control

Control catalogue · Documentation home

Portal water_heater control

Minimal field JSON:

{
  "id": "water_heater",
  "type": "water_heater",
  "name": "Water heater"
}

Full field JSON:

{
  "id": "water_heater",
  "type": "water_heater",
  "name": "Water heater",
  "default": {
    "temperature": 50,
    "current_temperature": null,
    "mode": "off"
  },
  "controls": [
    {
      "key": "temperature",
      "type": "slider",
      "label": "ui_101",
      "command": "temperature",
      "min": 30,
      "max": 80,
      "step": 1
    },
    {
      "key": "mode",
      "type": "select",
      "label": "ui_103",
      "command": "mode",
      "options": [
        "off",
        "eco",
        "electric",
        "performance",
        "heat_pump",
        "high_demand"
      ]
    }
  ],
  "ha": {
    "temperature_command_topic": "$command:temperature",
    "temperature_state_topic": "$state",
    "temperature_state_template": "{{ value_json.temperature }}",
    "current_temperature_topic": "$state",
    "current_temperature_template": "{{ value_json.current_temperature | default('', true) }}",
    "mode_command_topic": "$command:mode",
    "mode_state_topic": "$state",
    "mode_state_template": "{{ value_json.mode }}",
    "modes": [
      "off",
      "eco",
      "electric",
      "performance",
      "heat_pump",
      "high_demand"
    ],
    "min_temp": 30,
    "max_temp": 80,
    "temperature_unit": "C"
  }
}

Properties in the full example:

Property Required? Meaning / default
id Required Stable application key; unique across registered pages.
type Required Enabled field type.
name Required title¹ Display title; may be replaced by a nonempty names object.
default Optional Omit to use the built-in initial value.
controls Optional Omit to use the preset controls; supplying an array replaces them.
ha Optional Omit to use automatic HA discovery; supplied properties override defaults.

¹ name can be replaced by names. Other shared optional properties are listed in the catalogue.

The control uses a temperature dial (30–80 °C, step 1) and mode selector. Its mode selector uses rounded buttons like Climate, and the current mode appears inside the dial. Its dial uses solid dark orange in active modes and gray in off mode.

Its default is {"temperature":50,"current_temperature":null,"mode":"off"}. Preset modes are off, eco, electric, performance, heat_pump and high_demand. Commands use _temperature and _mode. The preset has no fan control. Custom ranges require matching slider controls and HA min_temp/max_temp; changing a top-level min alone is insufficient.

Climate displays the optional reported action inside the dial, for example heating, cooling, idle, or off. Set it with setAppConfigStateValue as part of the climate value; portal edits preserve it. Without an action, the portal shows Off for off mode and Idle otherwise. Humidifier displays its current mode in the same position. Standard action and mode names use the installed language catalog.

Dials can be dragged or operated with plus/minus and keyboard input. Current measurements are shown as gray dots; missing measurements hide the dot. These widgets display reported values and settings, not a thermostat algorithm.

Read and update values from C++

These examples use the field ID from the JSON above and an existing ArdPortal portal. Run them after page registration has finished and portal.portalAndAppConfigReady() is true. Call portal.loop() frequently. Register callbacks once in setup(); do not repeat setters on every loop iteration.

Set the value with automatic ArdFS persistence

Set "persist": true on this field in its page JSON. It is optional for editable fields whose preset already persists settings, but required to override a RAM-only preset such as a sensor. Calling setAppConfigValue() sets the control value and ArdPortal automatically saves AppConfig through its integrated ArdFS storage. Do not create an ArdFS object or call a separate write/flush method. The automatic save is coalesced and advanced by subsequent portal.loop() calls.

// Use this path when the value is a setting that should survive restart.
ArdJSON::JSONVar next = ArdJSON::JSON.parse(R"JSON({"temperature":50,"current_temperature":null,"mode":"off"})JSON");
if (!next.isValid() || !portal.setAppConfigValue("water_heater", next)) {
  Serial.println("Setting rejected or storage temporarily unavailable");
}

true from the setter means acceptance, not a completed flash write. Configure this callback once to observe the result:

portal.onPortalAndAppConfigSaved([](bool ok, const String& error) {
  if (!ok) Serial.println(error);
});

Update only RAM

// Use this alternative for live readings/status; never writes ArdFS.
ArdJSON::JSONVar next = ArdJSON::JSON.parse(R"JSON({"temperature":50,"current_temperature":null,"mode":"off"})JSON");
if (!next.isValid() || !portal.setAppConfigStateValue("water_heater", next, false)) {
  Serial.println("Runtime value rejected");
}

The third argument false avoids queuing an immediate MQTT publication. Use true or omit it to queue changed state. RAM updates work regardless of persist. Alternatively, with "persist": false, setAppConfigValue() also follows the RAM-only path.

Read the current value

const ArdJSON::JSONVar current = portal.getAppConfigValue("water_heater");
if (!current.isUndefined() && current.isValid()) {
  Serial.println(ArdJSON::JSON.stringify(current));
}

The getter returns the effective field value: RAM override first, then saved AppConfig, then the definition default. A RAM-only update is lost on restart; the saved value/default becomes effective again. Choose the persistent or RAM path for a given value. A RAM override can mask a later persistent change for the same ID; there is no API to clear just that override. Prefer separate setting and telemetry IDs when both are needed.

Back to control catalogue