Data relay from Huawei FusionSolar (Northbound OpenAPI and/or Kiosk) to InfluxDB, MQTT, PVOutput and Home Assistant
- Overview
- Features
- Installation
- Breaking Changes
- Integrations
- Configuration
- Output Structure
- Examples and Dashboards
- Changelog
- License
PyFusionSolarDataRelay is a Python application that serves as a data bridge between Huawei solar systems and various monitoring/visualization platforms. It can collect data from multiple sources and distribute it to multiple destinations.
- FusionSolar Northbound OpenAPI: Official API for accessing inverter and grid meter data
- FusionSolar Public Kiosk: Alternative source when API access is unavailable
- Kenter Metering Service API: Dutch utility grid energy usage data for commercial transformers
- InfluxDB/VictoriaMetrics: For time-series storage and visualization
- PVOutput.org: For public solar generation tracking
- MQTT: For integration with home automation systems
- Home Assistant: For energy dashboard visualization
The application supports multiple parallel configurations and automatic device discovery over the Northbound API.
- Multiple Data Sources: Connect to both FusionSolar API and public kiosk interfaces
- Flexible Output Options: Send data to any combination of supported platforms
- Device Auto-Discovery: Automatically detect and monitor devices on your FusionSolar account
- Comprehensive Metrics: Collect data from inverters, grid meters, and Kenter transformers
- Docker Ready: Easy deployment with Docker and docker-compose
- Configurable: Extensive environment variable configuration options
PyFusionSolarDataRelay is designed to run as a Docker container, but can also be run directly with Python. Docker-compose examples can be found in the examples directory.
# See the complete example in examples/docker-compose.yml
version: '3'
services:
pyfusionsolardatarelay:
image: jsprnl/pyfusionsolardatarelay:latest
container_name: pyfusionsolardatarelay
restart: unless-stopped
environment:
- TZ=Europe/Amsterdam
- site_descriptive_name=mysolarsite
# Add your configuration options here
- Clone the repository
- Install dependencies:
pip install -r requirements.txt
- Rename
.env.example
to.env
and configure your settings - Run the application:
python main.py
Important: Version 2.0.0 introduced significant changes to environment variables and the structure of output to MQTT (topics/payload) and InfluxDB (values/tags/fields).
- Northbound OpenAPI: Fetch inverter and grid meter metrics via Huawei's Northbound API
- Kiosk Mode: Scrape public kiosk endpoints for PV data when API access is unavailable
A free service for sharing and comparing PV output data.
An open‑source time-series database for storing energy data.
A lightweight publish/subscribe protocol for IoT.
An open-source home automation platform with an energy dashboard.
Fetches transformer grid energy usage data from the Dutch Kenter API.
All configuration is done through environment variables or an optional .env
file in the directory where main.py
is.
Parameter | Description | Default |
---|---|---|
debug_mode | Enables verbose logging | False |
fetch_on_startup | Starts API fetching and processing on startup one, then schedule cron jobs | False |
site_descriptive_name | Descriptive name for complete site. Use lowercase, and no special characters. This will be used for MQTT topics and InfluxDB record tags | site01 |
Parameter | Description | Default |
---|---|---|
fusionsolar_kiosk_module_enabled | Can be True or False , determines if fusionsolar kiosk API functionality is enabled |
True |
fusionsolar_kiosk_fetch_cron_hour | Hour component for python cron job to fetch and process data from fusionsolar. | * |
fusionsolar_kiosk_fetch_cron_minute | Minute component for python cron job to fetch and process data from fusionsolar | 0,30 |
fusionsolar_kiosks__0__descriptive_name | Descriptive name for PV system for which this kiosk entity provides data. Use lowercase, and no special characters. This will be used for InfluxDB record tags | inverter01 |
fusionsolar_kiosks__0__enabled | To disable individual kiosk configurations. Can be True or False |
True |
fusionsolar_kiosks__0__api_url | Link to the fusionsolar kiosk data backend, multiple records supported by adding an extra param with __1__ etc. |
Click url |
fusionsolar_kiosks__0__api_kkid | Unique kiosk ID, can be found by looking the kiosk URL and then taking the code after kk= |
GET_THIS_FROM_KIOSK_URL |
fusionsolar_kiosks__0__output_influxdb | Write to influxdb if influx module enabled. Can be True or False |
True |
fusionsolar_kiosks__0__output_mqtt | Write to mqtt if mqtt module enabled. Can be True or False |
True |
fusionsolar_kiosks__0__output_pvoutput | If pvoutput_module_enabled then write this pv metric to pvoutput | False |
fusionsolar_kiosks__0__output_pvoutput_system_id | System ID for PVOutput.org, should be numeric | 0 |
Parameter | Description | Default |
---|---|---|
fusionsolar_open_api_module_enabled | Can be True or False , determines if fusionsolar OpenAPI functionality is enabled |
True |
fusionsolar_open_api_url | Link to the fusionsolar OpenAPI data backend. | Click url |
fusionsolar_open_api_user_name | Username for FusionSolar Northbound OpenAPI. | |
fusionsolar_open_api_system_code | Password for FusionSolar Northbound OpenAPI. | |
fusionsolar_open_api_cron_hour | Hour component for python cron job to fetch and process data from fusionsolar. | * |
fusionsolar_open_api_cron_minute | Minute component for python cron job to fetch and process data from fusionsolar | */5 |
fusionsolar_open_api_mqtt_for_discovered_dev | Write KPI's to MQTT for devices discovered over the API without a matching dev_id | True |
fusionsolar_open_api_influxdb_for_discovered_dev | Write KPI's to InfluxDB for devices discovered over the API without a matching dev_id | True |
Parameter | Description | Default |
---|---|---|
fusionsolar_open_api_inverters__0__descriptive_name | Descriptive name for inverter. Use lowercase, and no special characters. This will be used for InfluxDB record tags | inverter01 |
fusionsolar_open_api_inverters__0__enabled | To disable individual OpenAPI inverter configurations. Can be True or False |
True |
fusionsolar_open_api_inverters__0__dev_id | Unique device ID nr, can be found by inspecting ./cache/fusion_solar_openapi_devices.json or inspecting stdout logs after startup | |
fusionsolar_open_api_inverters__0__output_influxdb | Write to influxdb if influx module enabled. Can be True or False |
True |
fusionsolar_open_api_inverters__0__output_mqtt | Write to mqtt if mqtt module enabled. Can be True or False |
True |
fusionsolar_open_api_inverters__0__output_pvoutput | If pvoutput_module_enabled then write this pv metric to pvoutput | False |
fusionsolar_open_api_inverters__0__output_pvoutput_system_id | System ID for PVOutput.org, should be numeric | 0 |
Parameter | Description | Default |
---|---|---|
fusionsolar_open_api_meters__0__descriptive_name | Descriptive name for grid meter. Use lowercase, and no special characters. This will be used for InfluxDB record tags | meter01 |
fusionsolar_open_api_meters__0__enabled | To disable individual OpenAPI meter configurations. Can be True or False |
True |
fusionsolar_open_api_meters__0__dev_id | Unique device ID nr, can be found by inspecting ./cache/fusion_solar_openapi_devices.json or inspecting stdout logs after startup | |
fusionsolar_open_api_meters__0__output_influxdb | Write to influxdb if influx module enabled. Can be True or False |
True |
fusionsolar_open_api_meters__0__output_mqtt | Write to mqtt if mqtt module enabled. Can be True or False |
True |
Parameter | Description | Default |
---|---|---|
kenter_module_enabled | Can be True or False , determines if data is fetched from Kenter's klantportaal.kenter.nu API |
False |
kenter_api_url | Kenter API url for fetching transformer grid measurements | Click url |
kenter_token_url | Kenter API url for fetching auth token | Click url |
kenter_clientid | Username for Kenter's API | user |
kenter_password | Password for Kenter's API | passwd |
kenter_fetch_cron_hour | Hour component for python cron job to fetch and process data from Kenter. | 8 |
kenter_fetch_cron_minute | Minute component for python cron job to fetch and process data from Kenter | 0 |
kenter_days_back | Kenter's klantportaal.kenter.nu does not provide live data. Data is only available up until an X amount of days back. May vary per transformer. | 1 |
kenter_days_backfill | How many additional days before days_back to process on startup | 0 |
kenter_metering_points__0__descriptive_name | Descriptive name for transformer. Use lowercase, and no special characters. This will be used for MQTT topics and InfluxDB record tags | transformer01 |
kenter_metering_points__0__connection_id | ConnectionId as shown in meter list on startup stdout (EAN code) | XXX |
kenter_metering_points__0__metering_point_id | MeteringPointId as shown in meter list on startup stdout | XXX |
kenter_metering_points__0__channel_id | See kenter API docs, 16180 is delivery for allocation with transformer correction factor for billing, 10180 is delivery kWh from an individual meter | 16180 |
kenter_metering_points__0__output_influxdb | Write to influxdb if influx module enabled. Can be True or False |
True |
Parameter | Description | Default |
---|---|---|
influxdb_module_enabled | Can be True or False , determines if InfluxDB processing is enabled |
False |
influxdb_is_v2 | If True the InfluxDBv2 methods are used. If False InfluxDBv1 methods are used |
True |
influxdb_host | Hostname of the influxdb server | localhost |
influxdb_port | Port of influxdb server | 8086 |
influxdb_v1_db_name | Database name for InfluxDBv1, only required if influx2=False | fusionsolar |
influxdb_v1_username | Username for InfluxDBv1, only required if influx2=False | fusionsolar |
influxdb_v1_password | Password for InfluxDBv1, only required if influx2=False | fusionsolar |
influxdb_v2_protocol | Protocol for InfluxDBv2, can be https or http , only required if influx2=True |
https |
influxdb_v2_org | Organization for InfluxDBv2, only required if influx2=True | acme |
influxdb_v2_bucket | Bucket for InfluxDBv2, only required if influx2=True | fusionsolar |
influxdb_v2_token | Token for InfluxDBv2, only required if influx2=True | XXXXXXX |
Parameter | Description | Default |
---|---|---|
pvoutput_module_enabled | Can be True or False , determines if PVOutput.org API is enabled |
False |
pvoutput_record_url | API url for PVOutput.org live output posting | Click url |
pvoutput_api_key | API Key for PVOutput.org | yourapikey |
Parameter | Description | Default |
---|---|---|
mqtt_module_enabled | Can be True or False , determines if MQTT publishing is enabled |
False |
mqtt_host | Hostname of MQTT server | localhost |
mqtt_port | Port of MQTT server | 1883 |
mqtt_auth | Can be True or False , determines if MQTT authentication is enabled |
False |
mqtt_username | MQTT Username | fusionsolar |
mqtt_password | MQTT Password | fusionsolar |
mqtt_root_topic | MQTT Topic for publishing | pyfusionsolar |
mqtt_hass_discovery_enabled | Automatically publish all sensors in MQTT home assistant device discovery | True |
This section outlines the structure of output data generated by PyFusionSolarDataRelay for MQTT and InfluxDB.
Topic Structure:
<mqtt_root_topic>/<site_descriptive_name>/sensors/<data_source>/<measurement_type>s/<station_dn>/<device_dn>/state
Where:
- <mqtt_root_topic>: Defined in the configuration (e.g.
pyfusionsolar
). - <site_descriptive_name>: A unique, descriptive identifier for the site.
- <data_source>: Indicates the source of the data (e.g.
kiosk
,openapi
). - <measurement_type>: The type of measurement (e.g.
inverter
,meter
), with an appended "s". - <station_dn> and <device_dn>: Sanitized versions (non-alphanumeric characters replaced) of station and device identifiers.
- The final segment,
state
, denotes the data state.
Payload Structure:
-
For Inverter Measurements (published via
publish_pvdata_to_mqtt
):descriptive_name
: The configured descriptive name for the inverter.real_time_power_w
: Current power in Watts.lifetime_energy_wh
: Cumulative energy in Watt-hours over the device's lifetime.day_energy_wh
: Energy generated on the current day in Watt-hours.
-
For Grid Meter Measurements (published via
publish_grid_data_to_mqtt
):descriptive_name
: The configured descriptive name for the meter.active_power_w
: Active power in Watts.
Additionally, Home Assistant MQTT discovery messages are published, which include device metadata such as device class, unit of measurement, and unique sensor IDs for auto-configuration in Home Assistant.
The InfluxDB records are structured as follows:
- Measurement Name: Always set to
energy
. - Time: A UTC timestamp formatted as
YYYY-MM-DDTHH:MM:SSZ
. - Tags: Metadata key-value pairs, including (as applicable):
site_descriptive_name
inverter_descriptive_name
ortransformer_descriptive_name
measurement_type
data_source
device_type
(e.g.inverter
,grid_meter
, orgrid_transformer
)- Additional tags:
station_name
,station_dn
,device_id
,device_dn
,device_name
,device_model
- For transformer data:
connection_id
,metering_point_id
, andchannel_id
.
- Fields: Contains the measurement data:
- For Inverter Measurements:
real_time_power_w
: Current power in Watts.lifetime_energy_wh
: Cumulative energy in Watt-hours.
- For Grid Meter Measurements:
active_power_w
: Active power in Watts.
- For Transformer Measurements (Kenter data):
interval_power_avg_w
: Average power during the measurement interval.interval_energy_wh
: Energy during the measurement interval.
- For Inverter Measurements:
Each record is constructed as a dictionary (or a list of dictionaries in the case of transformer data) and is ready to be written to InfluxDB using either the v1 or v2 client.
A grafana dashboard export is included in the examples subfolder in the Git repository.
I'm using individual the elements on this dashboard to show the PV solar statistics on a free and open source Xibo Digital Signage narrowcasting system.
Take the following steps to achieve this:
- Enable Grafana anonymous mode (see examples/docker-compose.yml)
- Create a new layout in Xibo and add some regions, or import the example Xibo layout.
- Back in Grafana, open the dashboard and click 'Share' in the grafana individual graph dropdown dialog (not the entire dashboard, but the individual graph on the dashboard)
- Share in "Link" mode (do not use snapshot or embed)
- Back in Xibo, drop the "Webpage" widget on your region, or select the existing widget in the imported example layout.
- Configure the webpage widget to show the link copied in step 4.
- Optionally alter the url to format like
&from=now-12h
instead of the default&from=1655015379544&to=1655058579544
- Publish the layout, the graphs will now fit nicely in the width/height of the defined regions.
Version | Description |
---|---|
2.0.5 | Bugfix for incorrect fusionsolar northbound API docs, which state active_power for inverters returns kW, but in fact returns W |
2.0.4 | Bugfix for fusionsolar cumulative energy quirk where cumulative energy provided by dashboard shortly decreases with the days production |
2.0.3 | Bugfix in scheduler |
2.0.2 | Default to kW in home assistant with 3 digit precision suggestion |
2.0.1 | Now sending MQTT device discovery message |
2.0.1 | Bugfix in environment variable parsing for custom pydantic nested env list parser |
2.0.0 | Introduced possibility to configure multiple input sources (kiosks, openapi meters/inverters and kenter meters) |
2.0.0 | Implemented Huawei Northbound OpenAPI as data source for metrics |
2.0.0 | Now supporting Kenter API v2 |
2.0.0 | Removed currentPower kiosk property (fetched from powercurve API obj) in favor of realTimePower |
2.0.0 | Removed functionality to write utility grid consumption (non-pv) to PVOutput (InfluxDB fully supported!) |
2.0.0 | BREAKING CHANGE: Environment variables for config changed names and structure. Please review the configuration section in README for updated variable names |
2.0.0 | Refactored class names and .py file structure |
2.0.0 | Implemented pydantic to replace pvconf.py, now supporting non-environment variable based settings using config.yaml |
1.0.6 | Fixed a bug parsing the environment cron settings, which are in string format, but were interpreted as int, causing an exception |
1.0.6 | FusionSolar API will now immediately be queried on startup if debug mode is enabled (so no waiting for cron to trigger is required for testing) |
1.0.5 | Added InfluxDB support for an optional secondary grid telemetry EAN configuration (pvoutput output is only supported on the primary EAN) |
1.0.5 | Bugfix for InfluxDB v1 implementation and removed auto-database creation for VictoriaMetrics compatibility |
1.0.3 | Grid transformer usage measurement polling from Kenter's klantportaal.kenter.nu API has been implemented |
1.0.3 | Changed docker-compose.yml template not to use host networking mode |
1.0.3 | main.py now uses separate threads for RelayFusionSolar and RelayKenter classes |
1.0.3 | Implemented apscheduler's cron implementation to be able to specify exact moments to fetch fusionsolar data |
1.0.3 | Code and method name refactoring including PvConf type hints in classes where this class was injected as method parameter |
Released under MIT by @JasperE84.
This project has been partly developed in time donated by Contour - Sheet metal supplier
Dit project is deels ontwikkeld ontwikkeld in de tijd van Contour - Plaatwerkleverancier