-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
name: Validate | ||
|
||
on: | ||
push: | ||
pull_request: | ||
schedule: | ||
- cron: "0 0 * * *" | ||
workflow_dispatch: | ||
|
||
jobs: | ||
validate-hacs: | ||
runs-on: "ubuntu-latest" | ||
steps: | ||
- uses: "actions/checkout@v3" | ||
- name: HACS validation | ||
uses: "hacs/action@main" | ||
with: | ||
category: "integration" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/bin/sh | ||
|
||
cp -R ../homeassistant-core/homeassistant/components/lg_ess custom_components/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
"""The LG ESS inverter integration.""" | ||
|
||
import logging | ||
|
||
from pyess.aio_ess import ESS, ESSException | ||
|
||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import Platform | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.exceptions import ConfigEntryNotReady | ||
|
||
from .const import DOMAIN | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
PLATFORMS: list[Platform] = [Platform.SENSOR] | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Set up LG ESS from config entry.""" | ||
|
||
hass.data.setdefault(DOMAIN, {}) | ||
|
||
try: | ||
ess = await ESS.create("LG_ESS", entry.data["password"], entry.data["host"]) | ||
hass.data[DOMAIN][entry.entry_id] = ess | ||
except ESSException as e: | ||
_LOGGER.exception("Error setting up ESS api") | ||
await ess.destruct() | ||
raise ConfigEntryNotReady from e | ||
|
||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) | ||
|
||
return True | ||
|
||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Unload a config entry.""" | ||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): | ||
api = hass.data[DOMAIN].pop(entry.entry_id) | ||
await api.destruct() | ||
|
||
return unload_ok |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
"""Config flow for LG ESS integration.""" | ||
import logging | ||
from typing import Any | ||
|
||
from pyess.aio_ess import ESS, ESSAuthException, ESSException | ||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries | ||
from homeassistant.const import CONF_HOST, CONF_PASSWORD | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.data_entry_flow import FlowResult | ||
|
||
from .const import DOMAIN | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
STEP_USER_DATA_SCHEMA = vol.Schema( | ||
{ | ||
vol.Required(CONF_HOST): str, | ||
vol.Required(CONF_PASSWORD): str, | ||
} | ||
) | ||
|
||
|
||
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]: | ||
"""Validate the user input allows us to connect. | ||
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user. | ||
""" | ||
# We cannot check with current ESS implementation... | ||
|
||
try: | ||
ess = await ESS.create("TEST_ENTRY", data[CONF_HOST], data[CONF_PASSWORD]) | ||
except Exception as e: | ||
await ess.destruct() | ||
raise e | ||
|
||
# Return info that you want to store in the config entry. | ||
return {"title": "LG ESS"} | ||
|
||
|
||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Handle a config flow for LG ESS.""" | ||
|
||
VERSION = 1 | ||
|
||
async def async_step_user( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Handle the initial step.""" | ||
errors: dict[str, str] = {} | ||
if user_input is not None: | ||
try: | ||
info = await validate_input(self.hass, user_input) | ||
return self.async_create_entry(title=info["title"], data=user_input) | ||
except ESSAuthException: | ||
_LOGGER.exception("Wrong password") | ||
errors["base"] = "invalid_auth" | ||
except ESSException: | ||
_LOGGER.exception("Generic error setting up the ESS Api") | ||
errors["base"] = "unknown" | ||
except Exception: # pylint: disable=broad-except | ||
_LOGGER.exception("Unexpected exception") | ||
errors["base"] = "unknown" | ||
|
||
return self.async_show_form( | ||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
"""Constants for the LG ESS Inverter integration.""" | ||
|
||
DOMAIN = "lg_ess" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
"""Coordinator to fetch the data once for all sensors.""" | ||
|
||
from datetime import timedelta | ||
import logging | ||
|
||
from pyess.aio_ess import ESS | ||
|
||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
class CommonCoordinator(DataUpdateCoordinator): | ||
"""LG ESS coordinator. | ||
Data: | ||
{'PV': | ||
{'brand': 'LGE-SOLAR', 'capacity': '10935', | ||
'pv1_voltage': '52.900002', 'pv2_voltage': '36.099998', 'pv3_voltage': '35.500000', | ||
'pv1_power': '0', 'pv2_power': '1', 'pv3_power': '1', | ||
'pv1_current': '0.010000', 'pv2_current': '0.030000', 'pv3_current': '0.030000', | ||
'today_pv_generation_sum': '16294', 'today_month_pv_generation_sum': '17469'}, | ||
'BATT': | ||
{'status': '2', 'soc': '10.3', 'dc_power': '627', | ||
'winter_setting': 'off', 'winter_status': 'off', 'safety_soc': '20', | ||
'backup_setting': 'off', 'backup_status': 'off', 'backup_soc': '30', | ||
'today_batt_discharge_enery': '6855', 'today_batt_charge_energy': '9050', 'month_batt_charge_energy': '9616', 'month_batt_discharge_energy': '9264'}, | ||
'GRID': | ||
{'active_power': '9', 'a_phase': '230.199997', 'freq': '50.020000', | ||
'today_grid_feed_in_energy': '968', 'today_grid_power_purchase_energy': '7442', | ||
'month_grid_feed_in_energy': '994', 'month_grid_power_purchase_energy': '13497'}, | ||
'LOAD': | ||
{'load_power': '638', | ||
'today_load_consumption_sum': '20573', 'today_pv_direct_consumption_enegy': '6276', 'today_batt_discharge_enery': '6855', 'today_grid_power_purchase_energy': '7442', | ||
'month_load_consumption_sum': '29620', 'month_pv_direct_consumption_energy': '6859', 'month_batt_discharge_energy': '9264', 'month_grid_power_purchase_energy': '13497'}, | ||
'PCS': | ||
{'today_self_consumption': '94.1', 'month_co2_reduction_accum': '12402', | ||
'today_pv_generation_sum': '16294', 'today_grid_feed_in_energy': '968', | ||
'month_pv_generation_sum': '17469', 'month_grid_feed_in_energy': '994', | ||
'pcs_stauts': '3', 'feed_in_limitation': '100', 'operation_mode': '0'}} | ||
""" | ||
|
||
def __init__(self, hass: HomeAssistant, ess: ESS) -> None: | ||
"""Initialize my coordinator.""" | ||
super().__init__( | ||
hass, | ||
_LOGGER, | ||
# Name of the data. For logging purposes. | ||
name="LG ESS common", | ||
# Polling interval. Will only be polled if there are subscribers. | ||
update_interval=timedelta(seconds=5), | ||
) | ||
self.ess = ess | ||
|
||
async def _async_update_data(self): | ||
"""Fetch data from API endpoint. | ||
This is the place to pre-process the data to lookup tables | ||
so entities can quickly look up their data. | ||
""" | ||
# try: | ||
# Note: asyncio.TimeoutError and aiohttp.ClientError are already | ||
# handled by the data update coordinator. | ||
return await self.ess.get_common() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"domain": "lg_ess", | ||
"name": "LG ESS Inverter", | ||
"codeowners": ["@dkarv"], | ||
"config_flow": true, | ||
"dependencies": [], | ||
"documentation": "https://github.com/dkarv/hacs-lg-ess/blob/main/README.md", | ||
"version": "0.1.0", | ||
"homekit": {}, | ||
"iot_class": "local_polling", | ||
"issue_tracker": "https://github.com/dkarv/hacs-lg-ess/issues", | ||
"requirements": ["pyess==0.1.15"], | ||
"ssdp": [], | ||
"zeroconf": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
"""Example integration using DataUpdateCoordinator.""" | ||
|
||
import logging | ||
|
||
from pyess.aio_ess import ESSAuthException | ||
|
||
from homeassistant.components.sensor import ( | ||
SensorDeviceClass, | ||
SensorEntity, | ||
SensorStateClass, | ||
) | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import PERCENTAGE, UnitOfEnergy, UnitOfPower | ||
from homeassistant.core import HomeAssistant, callback | ||
from homeassistant.exceptions import ConfigEntryAuthFailed | ||
from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
|
||
from .const import DOMAIN | ||
from .coordinator import CommonCoordinator | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
async def async_setup_entry( | ||
hass: HomeAssistant, | ||
config_entry: ConfigEntry, | ||
async_add_entities: AddEntitiesCallback, | ||
) -> None: | ||
"""Set up sensors from config entry.""" | ||
ess = hass.data[DOMAIN][config_entry.entry_id] | ||
coordinator = CommonCoordinator(hass, ess) | ||
|
||
# Fetch initial data so we have data when entities subscribe | ||
# | ||
# If the refresh fails, async_config_entry_first_refresh will | ||
# raise ConfigEntryNotReady and setup will try again later | ||
# | ||
# If you do not want to retry setup on failure, use | ||
# coordinator.async_refresh() instead | ||
# | ||
try: | ||
await coordinator.async_config_entry_first_refresh() | ||
except ESSAuthException as e: | ||
# Raising ConfigEntryAuthFailed will cancel future updates | ||
# and start a config flow with SOURCE_REAUTH (async_step_reauth) | ||
raise ConfigEntryAuthFailed from e | ||
|
||
async_add_entities( | ||
[ | ||
MeasurementSensor(coordinator, "BATT", "soc", PERCENTAGE), | ||
MeasurementSensor(coordinator, "BATT", "dc_power", UnitOfPower.WATT), | ||
MeasurementSensor(coordinator, "LOAD", "load_power", UnitOfPower.WATT), | ||
MeasurementSensor(coordinator, "PCS", "today_self_consumption", PERCENTAGE), | ||
IncreasingSensor(coordinator, "BATT", "today_batt_discharge_enery"), | ||
IncreasingSensor(coordinator, "BATT", "today_batt_charge_energy"), | ||
IncreasingSensor(coordinator, "BATT", "month_batt_discharge_energy"), | ||
IncreasingSensor(coordinator, "BATT", "month_batt_charge_energy"), | ||
IncreasingSensor(coordinator, "LOAD", "today_load_consumption_sum"), | ||
IncreasingSensor(coordinator, "LOAD", "today_pv_direct_consumption_enegy"), | ||
IncreasingSensor(coordinator, "LOAD", "today_grid_power_purchase_energy"), | ||
IncreasingSensor(coordinator, "LOAD", "month_load_consumption_sum"), | ||
IncreasingSensor(coordinator, "LOAD", "month_pv_direct_consumption_energy"), | ||
IncreasingSensor(coordinator, "LOAD", "month_grid_power_purchase_energy"), | ||
IncreasingSensor(coordinator, "PCS", "today_pv_generation_sum"), | ||
IncreasingSensor(coordinator, "PCS", "today_grid_feed_in_energy"), | ||
IncreasingSensor(coordinator, "PCS", "month_pv_generation_sum"), | ||
IncreasingSensor(coordinator, "PCS", "month_grid_feed_in_energy"), | ||
] | ||
) | ||
|
||
|
||
class EssSensor(CoordinatorEntity[CommonCoordinator], SensorEntity): | ||
"""Basic sensor with common functionality.""" | ||
|
||
_group: str | ||
_key: str | ||
|
||
def __init__(self, coordinator, group: str, key: str) -> None: | ||
"""Initialize the sensor with the common coordinator.""" | ||
super().__init__(coordinator) | ||
self._group = group | ||
self._key = key | ||
# Fix typos | ||
self._attr_translation_key = key.replace("_enery", "_energy").replace( | ||
"_enegy", "_energy" | ||
) | ||
self._attr_unique_id = self._attr_translation_key | ||
|
||
@callback | ||
def _handle_coordinator_update(self) -> None: | ||
"""Handle updated data from the coordinator.""" | ||
self._attr_native_value = self.coordinator.data[self._group][self._key] | ||
self.async_write_ha_state() | ||
|
||
|
||
class MeasurementSensor(EssSensor): | ||
"""Measurement sensor.""" | ||
|
||
_attr_state_class = SensorStateClass.MEASUREMENT | ||
|
||
def __init__(self, coordinator, group: str, key: str, unit) -> None: | ||
"""Initialize the sensor with the common coordinator.""" | ||
super().__init__(coordinator, group, key) | ||
self._attr_native_unit_of_measurement = unit | ||
|
||
|
||
class IncreasingSensor(EssSensor): | ||
"""Increasing total sensor.""" | ||
|
||
_attr_state_class = SensorStateClass.TOTAL_INCREASING | ||
_attr_device_class = SensorDeviceClass.ENERGY | ||
|
||
def __init__( | ||
self, coordinator, group: str, key: str, unit=UnitOfEnergy.WATT_HOUR | ||
) -> None: | ||
"""Initialize the sensor with the common coordinator.""" | ||
super().__init__(coordinator, group, key) | ||
self._attr_native_unit_of_measurement = unit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"config": { | ||
"step": { | ||
"user": { | ||
"data": { | ||
"host": "[%key:common::config_flow::data::host%]", | ||
"username": "[%key:common::config_flow::data::username%]", | ||
"password": "[%key:common::config_flow::data::password%]" | ||
} | ||
} | ||
}, | ||
"error": { | ||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", | ||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", | ||
"unknown": "[%key:common::config_flow::error::unknown%]" | ||
}, | ||
"abort": { | ||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"name": "LG ESS Inverter Integration" | ||
} |