Aqotec RM360 over Modbus, Part 2: The Energy Counter Workaround

Why the RM360 energy register is kept raw first, then filtered into a stable total_increasing sensor for Home Assistant.

Share
Aqotec RM360 over Modbus, Part 2: The Energy Counter Workaround

Part 1 created a small read-only Modbus setup for the Aqotec RM360. One value deserves special treatment: the energy counter.

Home Assistant’s Energy Dashboard expects a stable, monotonic sensor. In practice, Modbus reads can occasionally return bad values: a brief glitch, a reset-looking value, or a jump that is not physically plausible.

So I split the value in two:

  • rm360_energy_raw: the direct Modbus read
  • rm360_energy: the filtered sensor used by dashboards and long-term statistics

Raw Modbus sensor

- name: rm360_energy_raw
  unique_id: rm360_energy_raw
  address: 43750
  scan_interval: 720
  unit_of_measurement: "kWh"
  data_type: float32
  precision: 1

The raw sensor is useful for debugging. It should not be the value used by the Energy Dashboard.

Filtered template sensor

The filtered sensor keeps the old value when the new raw value is invalid, lower than the previous value, or jumps by more than 1000 kWh at once.

template:
  - triggers:
      - trigger: state
        entity_id: sensor.rm360_energy_raw
      - trigger: homeassistant
        event: start

    sensor:
      - name: "RM360 Energy"
        default_entity_id: sensor.rm360_energy
        unique_id: rm360_energy_filtered
        unit_of_measurement: "kWh"
        device_class: energy
        state_class: total_increasing

        state: >
          {% set new = states('sensor.rm360_energy_raw') | float(none) %}
          {% set old = this.state | float(none) %}

          {% if new is none or new < 0 %}
            {{ old if old is not none else none }}
          {% elif old is none %}
            {{ new }}
          {% elif new >= old and (new - old) <= 1000 %}
            {{ new }}
          {% else %}
            {{ old }}
          {% endif %}

        attributes:
          raw_value: >
            {{ states('sensor.rm360_energy_raw') }}

          rejected: >
            {% set new = states('sensor.rm360_energy_raw') | float(none) %}
            {% set old = this.state | float(none) %}
            {{
              new is not none
              and old is not none
              and (
                new < old
                or (new - old) > 1000
              )
            }}

          rejection_reason: >
            {% set new = states('sensor.rm360_energy_raw') | float(none) %}
            {% set old = this.state | float(none) %}

            {% if new is none %}
              invalid_or_unavailable
            {% elif new < 0 %}
              negative_value
            {% elif old is not none and new < old %}
              counter_decreased
            {% elif old is not none and (new - old) > 1000 %}
              increase_over_1000_kwh
            {% else %}
              none
            {% endif %}

          maximum_increase_kwh: "{{ 1000 }}"

Why this workaround matters

The Energy Dashboard stores long-term statistics. Once a bad spike lands there, cleaning it up is much more annoying than filtering it before it enters the statistics pipeline.

The raw sensor remains visible, so debugging is still possible. The filtered sensor is the one you chart, use for statistics, and expose to the Energy Dashboard.

Next up: writing values back to the RM360 safely.


Series navigation

Previous step: Aqotec RM360 over Modbus, Part 1: Read-Only Home Assistant Sensors

Next step: Aqotec RM360 over Modbus, Part 3: Safe Writes and Automations