Skip to content

Commit 0f0c85c

Browse files
authored
Merge pull request #86 from srkoster/ft_split
Split coordinator and sensor into separate files and use inverter for via_device
2 parents 29ec20b + 8d1ef9f commit 0f0c85c

File tree

3 files changed

+131
-88
lines changed

3 files changed

+131
-88
lines changed

custom_components/solaredgeoptimizers/__init__.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@
88

99
from solaredgeoptimizers import solaredgeoptimizers
1010
from .const import (
11-
CONF_SITE_ID,
1211
DOMAIN,
1312
LOGGER,
14-
DATA_API_CLIENT,
15-
PANEEL_DATA,
1613
)
14+
from .coordinator import MyCoordinator
1715

1816
PLATFORMS: list[Platform] = [Platform.SENSOR]
1917

@@ -35,7 +33,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
3533
raise ConfigEntryNotReady
3634

3735
hass.data.setdefault(DOMAIN, {})
38-
hass.data[DOMAIN][entry.entry_id] = {DATA_API_CLIENT: api}
36+
37+
coordinator = MyCoordinator(hass, api, True)
38+
39+
# Fetch initial data so we have data when entities subscribe
40+
#
41+
# If the refresh fails, async_config_entry_first_refresh will
42+
# raise ConfigEntryNotReady and setup will try again later
43+
await coordinator.async_config_entry_first_refresh()
44+
45+
hass.data[DOMAIN][entry.entry_id] = coordinator
3946

4047
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
4148

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""Example integration using DataUpdateCoordinator."""
2+
from datetime import datetime
3+
4+
import logging
5+
import async_timeout
6+
7+
from homeassistant.helpers import device_registry as dr
8+
from homeassistant.helpers.update_coordinator import (
9+
DataUpdateCoordinator,
10+
UpdateFailed,
11+
)
12+
13+
from .const import (
14+
DOMAIN,
15+
UPDATE_DELAY,
16+
CHECK_TIME_DELTA,
17+
)
18+
19+
from solaredgeoptimizers import (
20+
solaredgeoptimizers,
21+
)
22+
23+
_LOGGER = logging.getLogger(__name__)
24+
25+
class MyCoordinator(DataUpdateCoordinator):
26+
"""My custom coordinator."""
27+
28+
def __init__(self, hass, my_api: solaredgeoptimizers, first_boot):
29+
"""Initialize my coordinator."""
30+
super().__init__(
31+
hass,
32+
_LOGGER,
33+
# Name of the data. For logging purposes.
34+
name="SolarEdgeOptimizer",
35+
# Polling interval. Will only be polled if there are subscribers.
36+
update_interval=UPDATE_DELAY,
37+
)
38+
self.my_api = my_api
39+
self.first_boot = first_boot
40+
41+
async def _async_setup(self) -> None:
42+
"""Set up the coordinator.
43+
44+
Can be overwritten by integrations to load data or resources
45+
only once during the first refresh.
46+
"""
47+
48+
site = await self.hass.async_add_executor_job(self.my_api.requestListOfAllPanels)
49+
50+
_LOGGER.info("Found all information for site: %s", site.siteId)
51+
_LOGGER.info("Site has %s inverters", len(site.inverters))
52+
_LOGGER.info(
53+
"Adding all optimizers (%s) found to Home Assistant",
54+
site.returnNumberOfOptimizers(),
55+
)
56+
57+
58+
i = 1
59+
for inverter in site.inverters:
60+
_LOGGER.info("Adding all optimizers from inverter: %s", i)
61+
62+
device_registry = dr.async_get(self.hass)
63+
device_registry.async_get_or_create(
64+
config_entry_id=self.config_entry.entry_id,
65+
identifiers={(DOMAIN, inverter.serialNumber)},
66+
manufacturer="SolarEdge",
67+
model=inverter.type,
68+
name=inverter.displayName,
69+
)
70+
71+
async def _async_update_data(self):
72+
"""Fetch data from API endpoint.
73+
74+
This is the place to pre-process the data to lookup tables
75+
so entities can quickly look up their data.
76+
"""
77+
try:
78+
# Note: asyncio.TimeoutError and aiohttp.ClientError are already
79+
# handled by the data update coordinator.
80+
async with async_timeout.timeout(300):
81+
_LOGGER.debug("Update from the coordinator")
82+
data = await self.hass.async_add_executor_job(
83+
self.my_api.requestAllData
84+
)
85+
86+
update = False
87+
88+
timetocheck = datetime.now() - CHECK_TIME_DELTA
89+
90+
for optimizer in data:
91+
_LOGGER.debug(
92+
"Checking time: %s | Versus last measerument: %s",
93+
timetocheck,
94+
optimizer.lastmeasurement,
95+
)
96+
97+
if optimizer.lastmeasurement > timetocheck:
98+
update = True
99+
break
100+
101+
if update or self.first_boot:
102+
_LOGGER.debug("We enter new data")
103+
self.first_boot = False
104+
return data
105+
else:
106+
_LOGGER.debug("No new data to enter")
107+
return None
108+
109+
except Exception as err:
110+
_LOGGER.error("Error in updating updater")
111+
_LOGGER.error(err)
112+
raise UpdateFailed(err)

custom_components/solaredgeoptimizers/sensor.py

Lines changed: 8 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,8 @@
44
from homeassistant.helpers.entity_platform import AddEntitiesCallback
55
from homeassistant.helpers.entity import DeviceInfo
66

7-
from datetime import datetime, timedelta
8-
97
import logging
108

11-
import async_timeout
12-
import pytz
13-
149
from homeassistant.components.sensor import (
1510
SensorDeviceClass,
1611
SensorEntity,
@@ -21,33 +16,21 @@
2116

2217
from homeassistant.helpers.update_coordinator import (
2318
CoordinatorEntity,
24-
DataUpdateCoordinator,
25-
UpdateFailed,
2619
)
2720

2821
from .const import (
29-
DATA_API_CLIENT,
3022
DOMAIN,
31-
UPDATE_DELAY,
3223
SENSOR_TYPE,
3324
SENSOR_TYPE_OPT_VOLTAGE,
3425
SENSOR_TYPE_CURRENT,
3526
SENSOR_TYPE_POWER,
3627
SENSOR_TYPE_VOLTAGE,
3728
SENSOR_TYPE_ENERGY,
3829
SENSOR_TYPE_LASTMEASUREMENT,
39-
CHECK_TIME_DELTA,
4030
)
4131

32+
from .coordinator import MyCoordinator
4233

43-
# from homeassistant.const import (
44-
# POWER_WATT,
45-
# ELECTRIC_POTENTIAL_VOLT,
46-
# ELECTRIC_CURRENT_AMPERE,
47-
# ENERGY_KILO_WATT_HOUR,
48-
# )
49-
50-
# FROM 2023.2!
5134
from homeassistant.const import (
5235
UnitOfPower,
5336
UnitOfElectricPotential,
@@ -57,7 +40,6 @@
5740

5841
from solaredgeoptimizers import (
5942
SolarEdgeOptimizerData,
60-
solaredgeoptimizers,
6143
SolarlEdgeOptimizer,
6244
)
6345

@@ -71,10 +53,9 @@ async def async_setup_entry(
7153
) -> None:
7254
"""Add an solarEdge entry."""
7355
# Add the needed sensors to hass
74-
my_api = hass.data[DOMAIN][entry.entry_id][DATA_API_CLIENT]
75-
site = await hass.async_add_executor_job(my_api.requestListOfAllPanels)
56+
coordinator: MyCoordinator = hass.data[DOMAIN][entry.entry_id]
7657

77-
coordinator = MyCoordinator(hass, my_api, True)
58+
site = await hass.async_add_executor_job(coordinator.my_api.requestListOfAllPanels)
7859

7960
_LOGGER.info("Found all information for site: %s", site.siteId)
8061
_LOGGER.info("Site has %s inverters", len(site.inverters))
@@ -95,7 +76,7 @@ async def async_setup_entry(
9576

9677
# extra informatie ophalen
9778
info = await hass.async_add_executor_job(
98-
my_api.requestSystemData, optimizer.optimizerId
79+
coordinator.my_api.requestSystemData, optimizer.optimizerId
9980
)
10081

10182
if info is not None:
@@ -109,6 +90,7 @@ async def async_setup_entry(
10990
info,
11091
sensortype,
11192
optimizer,
93+
inverter
11294
)
11395
],
11496
update_before_add=True,
@@ -119,66 +101,6 @@ async def async_setup_entry(
119101
)
120102

121103

122-
class MyCoordinator(DataUpdateCoordinator):
123-
"""My custom coordinator."""
124-
125-
def __init__(self, hass, my_api: solaredgeoptimizers, first_boot):
126-
"""Initialize my coordinator."""
127-
super().__init__(
128-
hass,
129-
_LOGGER,
130-
# Name of the data. For logging purposes.
131-
name="SolarEdgeOptimizer",
132-
# Polling interval. Will only be polled if there are subscribers.
133-
update_interval=UPDATE_DELAY,
134-
)
135-
self.my_api = my_api
136-
self.first_boot = first_boot
137-
138-
async def _async_update_data(self):
139-
"""Fetch data from API endpoint.
140-
141-
This is the place to pre-process the data to lookup tables
142-
so entities can quickly look up their data.
143-
"""
144-
try:
145-
# Note: asyncio.TimeoutError and aiohttp.ClientError are already
146-
# handled by the data update coordinator.
147-
async with async_timeout.timeout(300):
148-
_LOGGER.debug("Update from the coordinator")
149-
data = await self.hass.async_add_executor_job(
150-
self.my_api.requestAllData
151-
)
152-
153-
update = False
154-
155-
timetocheck = datetime.now() - CHECK_TIME_DELTA
156-
157-
for optimizer in data:
158-
_LOGGER.debug(
159-
"Checking time: %s | Versus last measerument: %s",
160-
timetocheck,
161-
optimizer.lastmeasurement,
162-
)
163-
164-
if optimizer.lastmeasurement > timetocheck:
165-
update = True
166-
break
167-
168-
if update or self.first_boot:
169-
_LOGGER.debug("We enter new data")
170-
self.first_boot = False
171-
return data
172-
else:
173-
_LOGGER.debug("No new data to enter")
174-
return None
175-
176-
except Exception as err:
177-
_LOGGER.error("Error in updating updater")
178-
_LOGGER.error(err)
179-
raise UpdateFailed(err)
180-
181-
182104
# class MyEntity(CoordinatorEntity, SensorEntity):
183105
class SolarEdgeOptimizersSensor(CoordinatorEntity, SensorEntity):
184106
"""An entity using CoordinatorEntity.
@@ -201,12 +123,14 @@ def __init__(
201123
paneel: SolarEdgeOptimizerData,
202124
sensortype,
203125
optimizer: SolarlEdgeOptimizer,
126+
inverter
204127
) -> None:
205128
super().__init__(coordinator)
206129
self._hass = hass
207130
self._entry = entry
208131
self._paneelobject = paneel
209132
self._optimizerobject = optimizer
133+
self._inverter = inverter
210134
self._paneel = paneel.paneel_desciption
211135
self._attr_unique_id = "{}_{}".format(paneel.serialnumber, sensortype)
212136
self._sensor_type = sensortype
@@ -251,7 +175,7 @@ def device_info(self):
251175
"manufacturer": self._paneelobject.manufacturer,
252176
"model": self._paneelobject.model,
253177
"hw_version": self._paneelobject.serialnumber,
254-
"via_device": (DOMAIN, self._entry.entry_id),
178+
"via_device": (DOMAIN, self._inverter.serialNumber),
255179
}
256180

257181
@callback

0 commit comments

Comments
 (0)