1 """The Fronius integration."""
3 from __future__
import annotations
6 from datetime
import datetime, timedelta
8 from typing
import Final
10 from pyfronius
import Fronius, FroniusError
24 SOLAR_NET_DISCOVERY_NEW,
26 SOLAR_NET_RESCAN_TIMER,
29 from .coordinator
import (
30 FroniusCoordinatorBase,
31 FroniusInverterUpdateCoordinator,
32 FroniusLoggerUpdateCoordinator,
33 FroniusMeterUpdateCoordinator,
34 FroniusOhmpilotUpdateCoordinator,
35 FroniusPowerFlowUpdateCoordinator,
36 FroniusStorageUpdateCoordinator,
39 _LOGGER: Final = logging.getLogger(__name__)
40 PLATFORMS: Final = [Platform.SENSOR]
42 type FroniusConfigEntry = ConfigEntry[FroniusSolarNet]
46 """Set up fronius from a config entry."""
47 host = entry.data[CONF_HOST]
50 await solar_net.init_devices()
52 entry.runtime_data = solar_net
53 await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
58 """Unload a config entry."""
59 return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
63 hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry
65 """Remove a config entry from a device."""
70 """The FroniusSolarNet class routes received values to sensor entities."""
73 self, hass: HomeAssistant, entry: ConfigEntry, fronius: Fronius
75 """Initialize FroniusSolarNet class."""
80 self.host: str = entry.data[CONF_HOST]
86 self.inverter_coordinators: list[FroniusInverterUpdateCoordinator] = []
87 self.
logger_coordinatorlogger_coordinator: FroniusLoggerUpdateCoordinator |
None =
None
88 self.
meter_coordinatormeter_coordinator: FroniusMeterUpdateCoordinator |
None =
None
94 """Initialize DataUpdateCoordinators for SolarNet devices."""
100 name=f
"{DOMAIN}_logger_{self.host}",
114 name=f
"{DOMAIN}_meters_{self.host}",
123 name=f
"{DOMAIN}_ohmpilot_{self.host}",
132 name=f
"{DOMAIN}_power_flow_{self.host}",
141 name=f
"{DOMAIN}_storages_{self.host}",
150 timedelta(minutes=SOLAR_NET_RESCAN_TIMER),
155 """Create a device for the Fronius SolarNet system."""
157 configuration_url=self.
froniusfronius.url,
159 manufacturer=
"Fronius",
165 solar_net_device[ATTR_MODEL] = _logger_info.get(
"product_type", {}).
get(
166 "value",
"Datalogger Web"
168 solar_net_device[ATTR_SW_VERSION] = _logger_info[
"software_version"][
172 device_registry = dr.async_get(self.
hasshass)
173 device_registry.async_get_or_create(
177 return solar_net_device
180 """Get available inverters and set up coordinators for new found devices."""
183 _LOGGER.debug(
"Processing inverters for: %s", _inverter_infos)
184 for _inverter_info
in _inverter_infos:
186 f
"{DOMAIN}_inverter_{_inverter_info.solar_net_id}_{self.host}"
190 if _inverter_info.solar_net_id
in [
191 inv.inverter_info.solar_net_id
for inv
in self.inverter_coordinators
200 inverter_info=_inverter_info,
202 if self.
config_entryconfig_entry.state == ConfigEntryState.LOADED:
203 await _coordinator.async_refresh()
205 await _coordinator.async_config_entry_first_refresh()
206 self.inverter_coordinators.append(_coordinator)
209 if self.
config_entryconfig_entry.state == ConfigEntryState.LOADED:
213 "New inverter added (UID: %s)",
214 _inverter_info.unique_id,
218 """Get information about the inverters in the SolarNet system."""
219 inverter_infos: list[FroniusDeviceInfo] = []
222 _inverter_info = await self.
froniusfronius.inverter_info()
223 except FroniusError
as err:
224 if self.
config_entryconfig_entry.state == ConfigEntryState.LOADED:
226 _LOGGER.debug(
"Re-scan failed for %s", self.host)
227 return inverter_infos
229 raise ConfigEntryNotReady
from err
231 for inverter
in _inverter_info[
"inverters"]:
232 solar_net_id = inverter[
"device_id"][
"value"]
233 unique_id = inverter[
"unique_id"][
"value"]
235 identifiers={(DOMAIN, unique_id)},
236 manufacturer=inverter[
"device_type"].
get(
"manufacturer",
"Fronius"),
237 model=inverter[
"device_type"].
get(
238 "model", inverter[
"device_type"][
"value"]
240 name=inverter.get(
"custom_name", {}).
get(
"value"),
243 inverter_infos.append(
245 device_info=device_info,
246 solar_net_id=solar_net_id,
251 "Inverter found at %s (Device ID: %s, UID: %s)",
256 return inverter_infos
259 async
def _init_optional_coordinator[_FroniusCoordinatorT: FroniusCoordinatorBase](
260 coordinator: _FroniusCoordinatorT,
261 ) -> _FroniusCoordinatorT |
None:
262 """Initialize an update coordinator and return it if devices are found."""
264 await coordinator.async_config_entry_first_refresh()
265 except ConfigEntryNotReady:
270 if not coordinator.data:
list[FroniusDeviceInfo] _get_inverter_infos(self)
None _init_devices_inverter(self, datetime|None _now=None)
DeviceInfo _create_solar_net_device(self)
None __init__(self, HomeAssistant hass, ConfigEntry entry, Fronius fronius)
web.Response get(self, web.Request request, str config_key)
bool async_setup_entry(HomeAssistant hass, FroniusConfigEntry entry)
bool async_remove_config_entry_device(HomeAssistant hass, ConfigEntry config_entry, dr.DeviceEntry device_entry)
bool async_unload_entry(HomeAssistant hass, FroniusConfigEntry entry)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)