Repository navigation
Recipes
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.
- 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.
- Use the same status interval in both (PVOutput's system setting).
- 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.
- 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.
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.
action: switch.turn_off
target:
entity_id: switch.my_system_uploadingRecording carries on; everything is sent when it is switched back on.
action: select.select_option
target:
entity_id: select.my_system_v7_grid
data:
option: record_only # send, record_only or downloadThe site reloads to apply it.
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: trueOnly worth it if the cause of rejections fixes itself (e.g. a sensor that was briefly wrong); otherwise fix the cause first.
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 }}"type: statistics-graph
title: Generation (PVOutput)
period: day
stat_types: [change]
chart_type: bar
entities:
- pvoutput_uploader:12345_v1Replace 12345 with your system ID. Use stat_types: [mean, max] and period: hour
for power or temperature.
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.
Getting started
Running it
Reference