Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create virtual production meter by summing inverters #67

Merged
merged 4 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Home Assistant SunPower Integration using the local installer ethernet interface.

Original Integration is [https://github.com/krbaker/hass-sunpower](https://github.com/krbaker/hass-sunpower)

* If this is a fork, please add what's different here and fix up the badges below

[![GitHub Release][releases-shield]][releases]
[![GitHub Activity][commits-shield]][commits]
[![hacs_badge](https://img.shields.io/badge/HACS-Custom-orange.svg?style=for-the-badge)](https://github.com/custom-components/hacs)
Expand All @@ -21,6 +25,14 @@ YMMV, any damage to your system is your responsibility (see also: [LICENSE](LICE

## Component to integrate with [sunpower][sunpower-us] PVS 5/6 monitors

Notes:

* For PVS < 5 you can try [jcronq](https://github.com/jcronq/)'s fork
[https://github.com/jcronq/hass-sunpower](https://github.com/jcronq/hass-sunpower)
* The Original ESS fork by [CanisUrsa](https://github.com/CanisUrsa/) is
here
[https://github.com/CanisUrsa/hass-sunpower](https://github.com/CanisUrsa/hass-sunpower)

**This component will set up the following platforms.**

| Platform | Description |
Expand Down
59 changes: 55 additions & 4 deletions custom_components/sunpower/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
DOMAIN,
ESS_DEVICE_TYPE,
HUBPLUS_DEVICE_TYPE,
INVERTER_DEVICE_TYPE,
METER_DEVICE_TYPE,
PVS_DEVICE_TYPE,
SETUP_TIMEOUT_MIN,
SUNPOWER_COORDINATOR,
Expand Down Expand Up @@ -51,17 +53,63 @@
PREVIOUS_ESS_SAMPLE = {}


def create_vmeter(data):
# Create a virtual 'METER' that uses the sum of inverters
kwh = 0.0
kw = 0.0
amps = 0.0
freq = []
volts = []
state = "working"
for _serial, inverter in data.get(INVERTER_DEVICE_TYPE, {}).items():
if "STATE" in inverter and inverter["STATE"] != "working":
state = inverter["STATE"]
kwh += float(inverter.get("ltea_3phsum_kwh", "0"))
kw += float(inverter.get("p_mppt1_kw", "0"))
amps += float(inverter.get("i_3phsum_a", "0"))
if "freq_hz" in inverter:
freq.append(float(inverter["freq_hz"]))
if "vln_3phavg_v" in inverter:
volts.append(float(inverter["vln_3phavg_v"]))

freq_avg = sum(freq) / len(freq)
volts_avg = sum(volts) / len(volts)

pvs_serial = next(iter(data[PVS_DEVICE_TYPE])) # only one PVS
vmeter_serial = f"{pvs_serial}pv"
data.setdefault(METER_DEVICE_TYPE, {})[vmeter_serial] = {
"SERIAL": vmeter_serial,
"TYPE": "PVS-METER-P",
"STATE": state,
"MODEL": "Virtual",
"DESCR": f"Power Meter {vmeter_serial}",
"DEVICE_TYPE": "Power Meter",
"interface": "virtual",
"SWVER": "1.0",
"HWVER": "Virtual",
"origin": "virtual",
"net_ltea_3phsum_kwh": kwh,
"p_3phsum_kw": kw,
"freq_hz": freq_avg,
"i_a": amps,
"v12_v": volts_avg,
}
return data


def convert_sunpower_data(sunpower_data):
"""Convert PVS data into indexable format data[device_type][serial]"""
data = {}
for device in sunpower_data["devices"]:
data.setdefault(device["DEVICE_TYPE"], {})[device["SERIAL"]] = device

create_vmeter(data)

return data


def convert_ess_data(ess_data, pvs_serial):
def convert_ess_data(ess_data, data):
"""Do all the gymnastics to Integrate ESS data from its unique data source into the PVS data"""
data = {}
sunvault_amperages = []
sunvault_voltages = []
sunvault_temperatures = []
Expand Down Expand Up @@ -177,6 +225,7 @@ def convert_ess_data(ess_data, pvs_serial):
if True:
# Generate a usable serial number for this virtual device, use PVS serial as base
# since we must be talking through one and it has a serial
pvs_serial = next(iter(data[PVS_DEVICE_TYPE])) # only one PVS
sunvault_serial = f"sunvault_{pvs_serial}"
data[SUNVAULT_DEVICE_TYPE] = {sunvault_serial: {}}
data[SUNVAULT_DEVICE_TYPE][sunvault_serial]["sunvault_amperage"] = sum(
Expand Down Expand Up @@ -238,9 +287,11 @@ def sunpower_fetch(sunpower_monitor, use_ess, sunpower_update_invertal, sunvault

try:
data = convert_sunpower_data(sunpower_data)
pvs_serial = next(iter(data[PVS_DEVICE_TYPE])) # only one PVS
if use_ess:
data.update(convert_ess_data(ess_data, pvs_serial))
convert_ess_data(
ess_data,
data,
) # ess converter appends to items in existing PVS structure
return data
except ParseException as error:
raise UpdateFailed from error
Expand Down
7 changes: 2 additions & 5 deletions custom_components/sunpower/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,9 @@ def native_value(self):
if self._my_device_class == SensorDeviceClass.POWER_FACTOR:
try:
value = float(
self.coordinator.data[self._device_type][self.base_unique_id].get(
self._field,
None,
),
self.coordinator.data[self._device_type][self.base_unique_id][self._field],
)
return value * 100.0
except ValueError:
except (ValueError, KeyError):
pass # sometimes this value might be something like 'unavailable'
return self.coordinator.data[self._device_type][self.base_unique_id].get(self._field, None)