Skip to content

Commit

Permalink
Merge pull request #116 from Poshy163/main
Browse files Browse the repository at this point in the history
Storion-S5 System fix and Improvements
  • Loading branch information
CharlesGillanders authored Jul 24, 2024
2 parents 9f84f81 + fccd9ce commit 653e232
Show file tree
Hide file tree
Showing 7 changed files with 426 additions and 247 deletions.
22 changes: 14 additions & 8 deletions custom_components/alphaess/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from alphaess import alphaess

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant

import homeassistant.helpers.config_validation as cv
Expand Down Expand Up @@ -38,11 +37,11 @@
}
)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Alpha ESS from a config entry."""


client = alphaess.alphaess(entry.data["AppID"],entry.data["AppSecret"])
client = alphaess.alphaess(entry.data["AppID"], entry.data["AppSecret"])

coordinator = AlphaESSDataUpdateCoordinator(hass, client=client)
await coordinator.async_config_entry_first_refresh()
Expand All @@ -52,16 +51,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

entry.async_on_unload(entry.add_update_listener(update_listener))

async def async_battery_charge_handler(call):
await client.updateChargeConfigInfo(call.data.get('serial'), call.data.get('chargestopsoc'), int(call.data.get('enabled') == True), call.data.get('cp1end'), call.data.get('cp2end'), call.data.get('cp1start'), call.data.get('cp2start'))

await client.updateChargeConfigInfo(call.data.get('serial'), call.data.get('chargestopsoc'),
int(call.data.get('enabled') == True), call.data.get('cp1end'),
call.data.get('cp2end'), call.data.get('cp1start'),
call.data.get('cp2start'))

async def async_battery_discharge_handler(call):
await client.updateDisChargeConfigInfo(call.data.get('serial'), call.data.get('dischargecutoffsoc'), int(call.data.get('enabled') == True), call.data.get('dp1end'), call.data.get('dp2end'), call.data.get('dp1start'), call.data.get('dp2start'))
await client.updateDisChargeConfigInfo(call.data.get('serial'), call.data.get('dischargecutoffsoc'),
int(call.data.get('enabled') == True), call.data.get('dp1end'),
call.data.get('dp2end'), call.data.get('dp1start'),
call.data.get('dp2start'))

hass.services.async_register(
DOMAIN, 'setbatterycharge', async_battery_charge_handler, SERVICE_BATTERY_CHARGE_SCHEMA)

hass.services.async_register(
DOMAIN, 'setbatterydischarge', async_battery_discharge_handler, SERVICE_BATTERY_DISCHARGE_SCHEMA)

Expand All @@ -75,6 +80,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

return unload_ok


async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)
11 changes: 11 additions & 0 deletions custom_components/alphaess/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
DOMAIN = "alphaess"
PLATFORMS = [Platform.SENSOR]
SCAN_INTERVAL = timedelta(minutes=1)
THROTTLE_MULTIPLIER = 1.25
INVERTER_COUNT = 0

NAME = "Alpha ESS"
ISSUE_URL = "https://github.com/CharlesGillanders/homeassistant-alphaESS/issues"
Expand All @@ -19,3 +21,12 @@
{ISSUE_URL}
-------------------------------------------------------------------
"""


def increment_inverter_count():
global INVERTER_COUNT
INVERTER_COUNT += 1


def get_inverter_count():
return INVERTER_COUNT
126 changes: 68 additions & 58 deletions custom_components/alphaess/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""Coordinator for AlphaEss integration."""
import datetime
import json
import logging

import aiohttp
Expand All @@ -9,11 +7,17 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN, SCAN_INTERVAL
from .const import DOMAIN, SCAN_INTERVAL, THROTTLE_MULTIPLIER, get_inverter_count

_LOGGER: logging.Logger = logging.getLogger(__package__)


async def process_value(value):
if value is None or (isinstance(value, str) and value.strip() == ''):
return 0
return value


class AlphaESSDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the API."""

Expand All @@ -26,75 +30,81 @@ def __init__(self, hass: HomeAssistant, client: alphaess.alphaess) -> None:

async def _async_update_data(self):
"""Update data via library."""

inverter_count = get_inverter_count()
if inverter_count == 1:
LOCAL_INVERTER_COUNT = 0
else:
LOCAL_INVERTER_COUNT = inverter_count

try:
jsondata: json = await self.api.getdata()
if jsondata != None:
jsondata = await self.api.getdata(THROTTLE_MULTIPLIER * LOCAL_INVERTER_COUNT)
if jsondata is not None:
for invertor in jsondata:

inverterdata: dict[str, any] = {}
if invertor.get("minv") != None:
inverterdata.update({"Model": invertor.get("minv")})

inverterdata = {}
if invertor.get("minv") is not None:
inverterdata["Model"] = await process_value(invertor.get("minv"))
inverterdata["EMS Status"] = await process_value(invertor.get("emsStatus"))

# data from summary data API
_sumdata = invertor.get("SumData", {})
# data from one date energy API
_onedateenergy = invertor.get("OneDateEnergy", {})
# data from last power data API
_powerdata = invertor.get("LastPower", {})

if _sumdata != None:
# Still will keep in, but will be provided with the timezone difference
inverterdata.update({"Total Load": _sumdata.get("eload")})

if _onedateenergy != None:
_pv = _onedateenergy.get("epv")
_feedin = _onedateenergy.get("eOutput")
_gridcharge = _onedateenergy.get("eGridCharge")
_charge = _onedateenergy.get("eCharge")

inverterdata.update({"Solar Production": _pv})
inverterdata.update({"Solar to Load": _pv - _feedin})
inverterdata.update({"Solar to Grid": _feedin})
inverterdata.update({"Solar to Battery": _charge - _gridcharge})

inverterdata.update({"Grid to Load": _onedateenergy.get("eInput")})
inverterdata.update({"Grid to Battery": _gridcharge})

inverterdata.update({"Charge": _charge})
inverterdata.update({"Discharge": _onedateenergy.get("eDischarge")})

inverterdata.update({"EV Charger": _onedateenergy.get("eChargingPile")})

if _powerdata != None:
_soc = _powerdata.get("soc")
_gridpowerdetails = _powerdata.get("pgridDetail",{})
_pvpowerdetails = _powerdata.get("ppvDetail",{})

# wonder why do we have this twice
inverterdata.update({"Instantaneous Battery SOC": _soc})
inverterdata.update({"State of Charge": _soc})

inverterdata.update({"Instantaneous Battery I/O": _powerdata.get("pbat")})
inverterdata.update({"Instantaneous Load": _powerdata.get("pload")})

inverterdata.update({"Instantaneous Generation": _powerdata.get("ppv")})
# pv power generation details
inverterdata.update({"Instantaneous PPV1": _pvpowerdetails.get("ppv1")})
inverterdata.update({"Instantaneous PPV2": _pvpowerdetails.get("ppv2")})
inverterdata.update({"Instantaneous PPV3": _pvpowerdetails.get("ppv3")})
inverterdata.update({"Instantaneous PPV4": _pvpowerdetails.get("ppv4")})

inverterdata.update({"Instantaneous Grid I/O Total": _powerdata.get("pgrid")})
if _sumdata is not None:
inverterdata["Total Load"] = await process_value(_sumdata.get("eload"))
inverterdata["Total Income"] = await process_value(_sumdata.get("totalIncome"))
inverterdata["Self Consumption"] = await process_value(_sumdata.get("eselfConsumption") * 100)
inverterdata["Self Sufficiency"] = await process_value(_sumdata.get("eselfSufficiency") * 100)

if _onedateenergy is not None:
_pv = await process_value(_onedateenergy.get("epv"))
_feedin = await process_value(_onedateenergy.get("eOutput"))
_gridcharge = await process_value(_onedateenergy.get("eGridCharge"))
_charge = await process_value(_onedateenergy.get("eCharge"))

inverterdata["Solar Production"] = await process_value(_pv)
inverterdata["Solar to Load"] = await process_value(_pv - _feedin)
inverterdata["Solar to Grid"] = _feedin
inverterdata["Solar to Battery"] = await process_value(_charge - _gridcharge)
inverterdata["Grid to Load"] = await process_value(_onedateenergy.get("eInput"))
inverterdata["Grid to Battery"] = _gridcharge
inverterdata["Charge"] = _charge
inverterdata["Discharge"] = await process_value(_onedateenergy.get("eDischarge"))
inverterdata["EV Charger"] = await process_value(_onedateenergy.get("eChargingPile"))

if _powerdata is not None:
_soc = await process_value(_powerdata.get("soc"))
_gridpowerdetails = _powerdata.get("pgridDetail", {})
_pvpowerdetails = _powerdata.get("ppvDetail", {})

inverterdata["Instantaneous Battery SOC"] = _soc
inverterdata["State of Charge"] = _soc
inverterdata["Instantaneous Battery I/O"] = await process_value(_powerdata.get("pbat"))
inverterdata["Instantaneous Load"] = await process_value(_powerdata.get("pload"))
# pv power generation details
inverterdata["Instantaneous Generation"] = await process_value(_powerdata.get("ppv"))
inverterdata["Instantaneous PPV1"] = await process_value(_pvpowerdetails.get("ppv1"))
inverterdata["Instantaneous PPV2"] = await process_value(_pvpowerdetails.get("ppv2"))
inverterdata["Instantaneous PPV3"] = await process_value(_pvpowerdetails.get("ppv3"))
inverterdata["Instantaneous PPV4"] = await process_value(_pvpowerdetails.get("ppv4"))
# grid power usage details
inverterdata.update({"Instantaneous Grid I/O L1": _gridpowerdetails.get("pmeterL1")})
inverterdata.update({"Instantaneous Grid I/O L2": _gridpowerdetails.get("pmeterL2")})
inverterdata.update({"Instantaneous Grid I/O L3": _gridpowerdetails.get("pmeterL3")})

inverterdata["Instantaneous Grid I/O Total"] = await process_value(_powerdata.get("pgrid"))
inverterdata["Instantaneous Grid I/O L1"] = await process_value(
_gridpowerdetails.get("pmeterL1"))
inverterdata["Instantaneous Grid I/O L2"] = await process_value(
_gridpowerdetails.get("pmeterL2"))
inverterdata["Instantaneous Grid I/O L3"] = await process_value(
_gridpowerdetails.get("pmeterL3"))

self.data.update({invertor["sysSn"]: inverterdata})

return self.data
except (
aiohttp.client_exceptions.ClientConnectorError,
aiohttp.ClientResponseError,
aiohttp.client_exceptions.ClientConnectorError,
aiohttp.ClientResponseError,
) as error:
raise UpdateFailed(error) from error
4 changes: 4 additions & 0 deletions custom_components/alphaess/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ class AlphaESSNames(str, Enum):
GridIOL2 = "Instantaneous Grid I/O L2"
GridIOL3 = "Instantaneous Grid I/O L3"
Load = "Instantaneous Load"
Income = "Total Income"
SelfSufficiency = "Self Sufficiency"
SelfConsumption = "Self Consumption"
EmsStatus = "EMS Status"
2 changes: 1 addition & 1 deletion custom_components/alphaess/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"documentation": "https://github.com/CharlesGillanders/homeassistant-alphaESS",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/CharlesGillanders/homeassistant-alphaESS/issues",
"requirements": ["alphaessopenapi==0.0.9"],
"requirements": ["alphaessopenapi==0.0.10"],
"version": "0.4.9"
}
Loading

0 comments on commit 653e232

Please sign in to comment.