Skip to content

Recipes

Darek Margas edited this page Oct 13, 2026 · 1 revision

Recipes

IoTaWatt and this integration together

IoTaWatt's PVOutput service sends generation and consumption (v1-v4); this integration can add what IoTaWatt does not have, such as temperature, voltage or battery, to the same statuses.

  1. Add only the extra fields here (e.g. v5, v6, b1-b6). Fields not configured here are left empty in its statuses, so PVOutput keeps IoTaWatt's values.
  2. Use the same status interval in both (PVOutput's system setting).
  3. PVOutput needs one of v1-v4 in a status. When this integration's status reaches PVOutput first, it is retried for up to two hours, until IoTaWatt has created it.
  4. Optional: add v1/v2 From PVOutput to see IoTaWatt's values in Home Assistant, and their history via PVOutput history.

Both count against the same API key's hourly limit; see Rate limits.

Battery state from a Victron SmartShunt

PVOutput's b6 wants a state code, which battery monitors such as a Victron SmartShunt do not report. A template sensor can work it out from battery power, the alarm flag and the alarm reason. Map it to b6 with Last value:

template:
  - sensor:
      - name: "Battery State"
        unique_id: battery_state_pvoutput
        state: >
          {% set p = states('sensor.battery_power') %}
          {% set alarm = is_state('binary_sensor.battery_has_alarm', 'on') %}
          {% set reason = states('sensor.battery_alarm_reason') %}
          {% if not is_number(p) %} 0
          {% elif alarm and 'Temperature' in reason %} 2
          {% elif alarm %} 4
          {% elif p | float | abs >= 20 %} 3
          {% else %} 1
          {% endif %}

0 Invalid without data, 2 Thermal management on a temperature alarm, 4 Fault on any other alarm, 3 Enabled while charging or discharging (20 W or more, so noise does not flip it), 1 Standby otherwise. Replace the entity IDs with yours. With ESPHome the same logic can run on the device as a template sensor.

For b4/b5 (lifetime charge and discharge), a battery monitor rarely has counters; an integration sensor (Riemann sum) of charge and discharge power works, ideally with restore so it survives restarts.

Pause uploads at night, or during maintenance

action: switch.turn_off
target:
  entity_id: switch.my_system_uploading

Recording carries on; everything is sent when it is switched back on.

Switch a field between Send and Record only

action: select.select_option
target:
  entity_id: select.my_system_v7_grid
data:
  option: record_only   # send, record_only or download

The site reloads to apply it.

Resend everything unsent every morning

automation:
  - alias: "PVOutput: resend unsent"
    triggers:
      - trigger: time
        at: "06:00:00"
    conditions:
      - condition: numeric_state
        entity_id: sensor.my_system_rejected_samples
        above: 0
    actions:
      - action: pvoutput_uploader.reupload
        data:
          since_failure: true

Only worth it if the cause of rejections fixes itself (e.g. a sensor that was briefly wrong); otherwise fix the cause first.

Alert on upload errors

automation:
  - alias: "PVOutput: upload error"
    triggers:
      - trigger: state
        entity_id: sensor.my_system_last_error
    conditions:
      - condition: template
        value_template: "{{ trigger.to_state.state not in ['unknown', 'unavailable', ''] }}"
    actions:
      - action: notify.notify
        data:
          message: "PVOutput: {{ trigger.to_state.state }}"

Statistics graph of PVOutput history

type: statistics-graph
title: Generation (PVOutput)
period: day
stat_types: [change]
chart_type: bar
entities:
  - pvoutput_uploader:12345_v1

Replace 12345 with your system ID. Use stat_types: [mean, max] and period: hour for power or temperature.

Compare what you sent with what PVOutput holds

Put your source sensor and the PVOutput history statistic (or the from PVOutput download sensor) in one statistics graph card. Differences show rejected values, a second uploader overwriting fields, or unit mistakes.

Clone this wiki locally