-
-
Notifications
You must be signed in to change notification settings - Fork 96
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
11 changed files
with
514 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,21 @@ | ||
# Huawei Solar Sensors | ||
|
||
This integration splits out the various values that are fetched from your | ||
Huawei Solar inverter into separate HomeAssistant sensors. These are properly | ||
configured to allow immediate integration into the HA Energy view. | ||
|
||
![sensors](images/sensors-screenshot.png) | ||
![energy-config](images/energy-config.png) | ||
|
||
## Installation | ||
|
||
1. Install this integration with HACS, or copy the contents of this | ||
repository into the `custom_components/huawei_solar` directory | ||
2. Restart HA | ||
3. Go to `Configuration` -> `Integrations` and click the `+ Add Integration` | ||
button | ||
4. Select `Huawei Solar` from the list | ||
5. Enter the IP address of your inverter (192.168.200.1 if you are connected to | ||
its WiFi AP). Select if you have a battery and/or optimizers. The slave id is | ||
typically 0. | ||
|
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,38 @@ | ||
"""The Huawei Solar integration.""" | ||
from __future__ import annotations | ||
|
||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.const import CONF_HOST | ||
|
||
from .const import DOMAIN, CONF_SLAVE, DATA_MODBUS_CLIENT | ||
|
||
from huawei_solar import HuaweiSolar, ConnectionException | ||
|
||
# TODO List the platforms that you want to support. | ||
# For your initial PR, limit it to 1 platform. | ||
PLATFORMS: list[str] = ["sensor"] | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Set up Huawei Solar from a config entry.""" | ||
# TODO Store an API object for your platforms to access | ||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { | ||
DATA_MODBUS_CLIENT: HuaweiSolar( | ||
host=entry.data[CONF_HOST], slave=entry.data[CONF_SLAVE] | ||
) | ||
} | ||
|
||
hass.config_entries.async_setup_platforms(entry, PLATFORMS) | ||
|
||
return True | ||
|
||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Unload a config entry.""" | ||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) | ||
if unload_ok: | ||
hass.data[DOMAIN].pop(entry.entry_id) | ||
|
||
return unload_ok | ||
"""Huawei Solar integration which connects to the local Modbus TCP endpoint""" |
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,101 @@ | ||
"""Config flow for Huawei Solar integration.""" | ||
from __future__ import annotations | ||
|
||
import logging | ||
from typing import Any | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.data_entry_flow import FlowResult | ||
from homeassistant.exceptions import HomeAssistantError | ||
|
||
from homeassistant.const import CONF_HOST | ||
from .const import ( | ||
DOMAIN, | ||
CONF_BATTERY, | ||
CONF_OPTIMIZERS, | ||
CONF_SLAVE, | ||
ATTR_MODEL_ID, | ||
ATTR_SERIAL_NUMBER, | ||
) | ||
|
||
from huawei_solar import HuaweiSolar, ConnectionException | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
STEP_USER_DATA_SCHEMA = vol.Schema( | ||
{ | ||
vol.Required(CONF_HOST): str, | ||
vol.Optional(CONF_OPTIMIZERS, default=False): bool, | ||
vol.Optional(CONF_BATTERY, default=False): bool, | ||
vol.Optional(CONF_SLAVE, default=0): int, | ||
} | ||
) | ||
|
||
|
||
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. | ||
""" | ||
# TODO validate the data can be used to set up a connection. | ||
|
||
# If your PyPI package is not built with async, pass your methods | ||
# to the executor: | ||
# await hass.async_add_executor_job( | ||
# your_validate_func, data["username"], data["password"] | ||
# ) | ||
|
||
inverter = HuaweiSolar(host=data[CONF_HOST]) | ||
|
||
try: | ||
model_name = inverter.get(ATTR_MODEL_ID).value | ||
serial_number = inverter.get(ATTR_SERIAL_NUMBER).value | ||
|
||
# Return info that you want to store in the config entry. | ||
return dict(model_name=model_name, serial_number=serial_number) | ||
except ConnectionException as ex: | ||
raise CannotConnect from ex | ||
|
||
|
||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Handle a config flow for Huawei Solar.""" | ||
|
||
VERSION = 1 | ||
|
||
async def async_step_user( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Handle the initial step.""" | ||
if user_input is None: | ||
return self.async_show_form( | ||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA | ||
) | ||
|
||
errors = {} | ||
|
||
try: | ||
info = await validate_input(self.hass, user_input) | ||
except CannotConnect: | ||
errors["base"] = "cannot_connect" | ||
except InvalidAuth: | ||
errors["base"] = "invalid_auth" | ||
except Exception: # pylint: disable=broad-except | ||
_LOGGER.exception("Unexpected exception") | ||
errors["base"] = "unknown" | ||
else: | ||
return self.async_create_entry(title=info["model_name"], data=user_input) | ||
|
||
return self.async_show_form( | ||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors | ||
) | ||
|
||
|
||
class CannotConnect(HomeAssistantError): | ||
"""Error to indicate we cannot connect.""" | ||
|
||
|
||
class InvalidAuth(HomeAssistantError): | ||
"""Error to indicate there is invalid auth.""" |
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,198 @@ | ||
"""Constants for the Huawei Solar integration.""" | ||
from dataclasses import dataclass | ||
|
||
from homeassistant.const import ( | ||
DEVICE_CLASS_BATTERY, | ||
POWER_WATT, | ||
ENERGY_KILO_WATT_HOUR, | ||
DEVICE_CLASS_ENERGY, | ||
DEVICE_CLASS_POWER, | ||
DEVICE_CLASS_POWER_FACTOR, | ||
PERCENTAGE, | ||
) | ||
|
||
from homeassistant.components.sensor import ( | ||
STATE_CLASS_MEASUREMENT, | ||
STATE_CLASS_TOTAL, | ||
STATE_CLASS_TOTAL_INCREASING, | ||
SensorEntityDescription, | ||
) | ||
|
||
DOMAIN = "huawei_solar" | ||
|
||
DATA_MODBUS_CLIENT = "client" | ||
|
||
|
||
ATTR_MODEL_ID = "model_id" | ||
ATTR_SERIAL_NUMBER = "serial_number" | ||
|
||
CONF_OPTIMIZERS = "optimizers" | ||
CONF_BATTERY = "battery" | ||
CONF_SLAVE = "slave" | ||
|
||
ATTR_DAILY_YIELD = "daily_yield_energy" | ||
ATTR_TOTAL_YIELD = "accumulated_yield_energy" | ||
|
||
ATTR_POWER_FACTOR = "power_factor" | ||
|
||
ATTR_STORAGE_TOTAL_CHARGE = "storage_total_charge" | ||
ATTR_STORAGE_TOTAL_DISCHARGE = "storage_total_discharge" | ||
|
||
ATTR_STORAGE_DAY_CHARGE = "storage_current_day_charge_capacity" | ||
ATTR_STORAGE_DAY_DISCHARGE = "storage_current_day_discharge_capacity" | ||
|
||
ATTR_STORAGE_STATE_OF_CAPACITY = "storage_state_of_capacity" | ||
ATTR_STORAGE_CHARGE_DISCHARGE_POWER = "storage_charge_discharge_power" | ||
|
||
ATTR_GRID_EXPORTED = "grid_exported_energy" | ||
ATTR_GRID_ACCUMULATED = "grid_accumulated_energy" | ||
|
||
ATTR_ACTIVE_POWER = "active_power" | ||
ATTR_INPUT_POWER = "input_power" | ||
ATTR_POWER_METER_ACTIVE_POWER = "power_meter_active_power" | ||
|
||
ATTR_NB_OPTIMIZERS = "nb_optimizers" | ||
ATTR_NB_ONLINE_OPTIMIZERS = "nb_online_optimizers" | ||
|
||
ATTR_NB_PV_STRINGS = "nb_pv_strings" | ||
ATTR_RATED_POWER = "rated_power" | ||
ATTR_GRID_STANDARD = "grid_standard" | ||
ATTR_GRID_COUNTRY = "grid_country" | ||
|
||
ATTR_DAY_POWER_PEAK = "day_active_power_peak" | ||
ATTR_REACTIVE_POWER = "reactive_power" | ||
ATTR_EFFICIENCY = "efficiency" | ||
ATTR_GRID_FREQUENCY = "grid_frequency" | ||
ATTR_GRID_VOLTAGE = "grid_voltage" | ||
ATTR_GRID_CURRENT = "grid_current" | ||
ATTR_STARTUP_TIME = "startup_time" | ||
ATTR_SHUTDOWN_TIME = "shutdown_time" | ||
ATTR_INTERNAL_TEMPERATURE = "internal_temperature" | ||
ATTR_DEVICE_STATUS = "device_status" | ||
ATTR_SYSTEM_TIME = "system_time" | ||
|
||
|
||
@dataclass | ||
class HuaweiSolarSensorEntityDescription(SensorEntityDescription): | ||
pass | ||
|
||
|
||
SENSOR_TYPES: tuple[HuaweiSolarSensorEntityDescription] = ( | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_DAILY_YIELD, | ||
name="Daily Yield", | ||
icon="mdi:solar-power", | ||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
state_class=STATE_CLASS_TOTAL_INCREASING, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_TOTAL_YIELD, | ||
name="Total Yield", | ||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
state_class=STATE_CLASS_TOTAL, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_ACTIVE_POWER, | ||
name="Active Power", | ||
native_unit_of_measurement=POWER_WATT, | ||
device_class=DEVICE_CLASS_POWER, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_INPUT_POWER, | ||
name="Input Power", | ||
native_unit_of_measurement=POWER_WATT, | ||
device_class=DEVICE_CLASS_POWER, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_POWER_METER_ACTIVE_POWER, | ||
name="Power Meter Active Power", | ||
native_unit_of_measurement=POWER_WATT, | ||
device_class=DEVICE_CLASS_POWER, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_POWER_FACTOR, | ||
name="Power Factor", | ||
device_class=DEVICE_CLASS_POWER_FACTOR, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_GRID_ACCUMULATED, | ||
name="Grid Consumption", | ||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
state_class=STATE_CLASS_TOTAL_INCREASING, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_GRID_EXPORTED, | ||
name="Grid Exported", | ||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
state_class=STATE_CLASS_TOTAL_INCREASING, | ||
), | ||
) | ||
|
||
BATTERY_SENSOR_TYPES = ( | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_STORAGE_TOTAL_CHARGE, | ||
name="Battery Total Charge", | ||
icon="mdi:battery-plus-variant", | ||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, | ||
state_class=STATE_CLASS_TOTAL, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_STORAGE_DAY_CHARGE, | ||
name="Battery Day Charge", | ||
icon="mdi:battery-plus-variant", | ||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, | ||
state_class=STATE_CLASS_TOTAL_INCREASING, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_STORAGE_TOTAL_DISCHARGE, | ||
name="Battery Total Discharge", | ||
icon="mdi:battery-minus-variant", | ||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, | ||
state_class=STATE_CLASS_TOTAL, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_STORAGE_DAY_DISCHARGE, | ||
name="Battery Day Discharge", | ||
icon="mdi:battery-minus-variant", | ||
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, | ||
state_class=STATE_CLASS_TOTAL_INCREASING, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_STORAGE_STATE_OF_CAPACITY, | ||
name="Battery State of Capacity", | ||
icon="mdi:home-battery", | ||
native_unit_of_measurement=PERCENTAGE, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_STORAGE_CHARGE_DISCHARGE_POWER, | ||
name="Charge/Discharge Power", | ||
icon="mdi:flash", | ||
native_unit_of_measurement=POWER_WATT, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
device_class=DEVICE_CLASS_POWER, | ||
), | ||
) | ||
|
||
OPTIMIZER_SENSOR_TYPES = ( | ||
HuaweiSolarSensorEntityDescription( | ||
key=ATTR_NB_ONLINE_OPTIMIZERS, | ||
name="Optimizers Online", | ||
icon="mdi:solar-panel", | ||
native_unit_of_measurement="optimizers", | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
device_class=DEVICE_CLASS_ENERGY, | ||
), | ||
) |
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,5 @@ | ||
{ | ||
"name": "Huawei Solar", | ||
"content_in_root": true, | ||
"render_readme": true | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 @@ | ||
{ | ||
"domain": "huawei_solar", | ||
"name": "Huawei Solar", | ||
"config_flow": true, | ||
"documentation": "https://www.home-assistant.io/integrations/huawei_solar", | ||
"requirements": [ | ||
"huawei_solar==1.1.0" | ||
], | ||
"ssdp": [], | ||
"zeroconf": [], | ||
"homekit": {}, | ||
"dependencies": [], | ||
"codeowners": [ | ||
"@wlcrs" | ||
], | ||
"iot_class": "local_polling", | ||
"version": "2.0.0-alpha1" | ||
} |
Oops, something went wrong.