11import asyncio
2+ import datetime
23import logging
34
45from homeassistant .core import HomeAssistant , callback
78from powersensor_local import PlugApi , VirtualHousehold
89
910from custom_components .powersensor .AsyncSet import AsyncSet
10- from custom_components .powersensor .const import POWER_SENSOR_UPDATE_SIGNAL , DOMAIN , DEFAULT_PORT
11+ from custom_components .powersensor .const import POWER_SENSOR_UPDATE_SIGNAL , DOMAIN
1112
1213_LOGGER = logging .getLogger (__name__ )
1314class PowersensorMessageDispatcher :
14- def __init__ (self , hass : HomeAssistant , vhh : VirtualHousehold ,debounce_timeout : float = 60 ):
15+ def __init__ (self , hass : HomeAssistant , vhh : VirtualHousehold , debounce_timeout : float = 60 ):
1516 self ._hass = hass
1617 self ._vhh = vhh
1718 self .plugs = dict ()
@@ -21,6 +22,11 @@ def __init__(self, hass: HomeAssistant, vhh: VirtualHousehold,debounce_timeout:
2122 self .on_start_sensor_queue = dict ()
2223 self ._pending_removals = {}
2324 self ._debounce_seconds = debounce_timeout
25+ self .has_solar = False
26+ self ._virtual_household_has_been_setup = False
27+ self ._last_request_to_notify_about_solar = datetime .datetime (1970 ,1 ,1 ,0 ,0 ,0 )
28+ self ._solar_request_limit = datetime .timedelta (seconds = 10 )
29+
2430 self ._unsubscribe_from_signals = [
2531 async_dispatcher_connect (self ._hass ,
2632 f"{ DOMAIN } _sensor_added_to_homeassistant" ,
@@ -37,6 +43,9 @@ def __init__(self, hass: HomeAssistant, vhh: VirtualHousehold,debounce_timeout:
3743 async_dispatcher_connect (self ._hass ,
3844 f"{ DOMAIN } _plug_added_to_homeassistant" ,
3945 self ._acknowledge_plug_added_to_homeassistant ),
46+ async_dispatcher_connect (self ._hass ,
47+ f"{ DOMAIN } _solar_added_to_virtual_household" ,
48+ self ._acknowledge_solar_added_to_virtual_household ),
4049 ]
4150
4251 self ._monitor_add_plug_queue = None
@@ -98,6 +107,21 @@ async def stop_processing_plug_queue(self):
98107 _LOGGER .debug ("Background task stopped" )
99108 self ._monitor_add_plug_queue = None
100109
110+
111+ async def stop_pending_removal_tasks (self ):
112+ """Stop the background removal tasks."""
113+ for k in range (len (self ._pending_removals )):
114+ if self ._pending_removals [k ] and not self ._pending_removals [k ].done ():
115+ self ._pending_removals [k ].cancel ()
116+ try :
117+ await self ._pending_removals [k ]
118+ except asyncio .CancelledError :
119+ pass
120+ _LOGGER .debug ("Background removal task stopped" )
121+ self ._pending_removals [k ] = None
122+ self ._pending_removals = []
123+
124+
101125 def _create_api (self , mac_address , ip , port , name ):
102126 _LOGGER .info (f"Creating API for mac={ mac_address } , ip={ ip } , port={ port } " )
103127 api = PlugApi (mac = mac_address , ip = ip , port = port )
@@ -126,28 +150,36 @@ def add_api(self, network_info):
126150 self ._create_api (mac_address = network_info ['mac' ], ip = network_info ['host' ],
127151 port = network_info ['port' ], name = network_info ['name' ])
128152
129-
130- async def handle_message (self , event : str , message : dict ):
131- mac = message ['mac' ]
153+ def cancel_any_pending_removal (self , mac , source ):
132154 task = self ._pending_removals .pop (mac , None )
133155 if task :
134156 task .cancel ()
135- _LOGGER .info (f"Cancelled pending removal for { mac } by new message received from plug." )
157+ _LOGGER .info (f"Cancelled pending removal for { mac } by { source } ." )
158+
159+ async def handle_message (self , event : str , message : dict ):
160+ mac = message ['mac' ]
161+ role = message .get ('role' , None )
162+ self .cancel_any_pending_removal (mac , "new message received from plug" )
136163
137164 if mac not in self .plugs .keys ():
138165 if mac not in self .sensors :
139- role = None
140- if 'role' in message :
166+ if role is not None :
141167 self .on_start_sensor_queue [mac ] = role
142- role = message ['role' ]
143168 async_dispatcher_send (self ._hass , f"{ DOMAIN } _create_sensor" , mac , role )
144169
145170 # Feed the household calculations
146171 if event == 'average_power' :
147172 await self ._vhh .process_average_power_event (message )
148173 elif event == 'summation_energy' :
149174 await self ._vhh .process_summation_event (message )
150-
175+ if role == 'solar' :
176+ self .has_solar = True
177+ if not self ._virtual_household_has_been_setup :
178+ new_time = datetime .datetime .now ()
179+ if self ._last_request_to_notify_about_solar + self ._solar_request_limit < new_time :
180+ self ._last_request_to_notify_about_solar = new_time
181+ _LOGGER .debug ("Notifying integration that solar is present." )
182+ async_dispatcher_send (self ._hass , f"{ DOMAIN } _solar_sensor_detected" )
151183 async_dispatcher_send (self ._hass , f"{ POWER_SENSOR_UPDATE_SIGNAL } _{ mac } _{ event } " , event , message )
152184
153185 async def disconnect (self ):
@@ -159,11 +191,17 @@ async def disconnect(self):
159191 unsubscribe ()
160192
161193 await self .stop_processing_plug_queue ()
194+ await self .stop_pending_removal_tasks ()
162195
163196 @callback
164197 def _acknowledge_sensor_added_to_homeassistant (self ,mac , role ):
165198 self .sensors [mac ] = role
166199
200+ @callback
201+ def _acknowledge_solar_added_to_virtual_household (self , success ):
202+ _LOGGER .debug ("Solar has been added to virtual household." )
203+ self ._virtual_household_has_been_setup = success
204+
167205 async def _acknowledge_plug_added_to_homeassistant (self , mac_address , host , port , name ):
168206 _LOGGER .info (f"Adding new API for mac={ mac_address } , ip={ host } , port={ port } " )
169207 self ._create_api (mac_address , host , port , name )
@@ -174,15 +212,11 @@ async def _plug_added(self, info):
174212 network_info = dict ()
175213 mac = info ['properties' ][b'id' ].decode ('utf-8' )
176214 network_info ['mac' ] = mac
215+ self .cancel_any_pending_removal (mac , "request to add plug" )
177216 network_info ['host' ] = info ['addresses' ][0 ]
178217 network_info ['port' ] = info ['port' ]
179218 network_info ['name' ] = info ['name' ]
180219
181- task = self ._pending_removals .pop (mac , None )
182- if task :
183- task .cancel ()
184- _LOGGER .info (f"Cancelled pending removal for { mac } by request to add api." )
185-
186220 if self ._safe_to_process_plug_queue :
187221 await self .enqueue_plug_for_adding (network_info )
188222 await self .process_plug_queue ()
@@ -192,13 +226,11 @@ async def _plug_added(self, info):
192226 async def _plug_updated (self , info ):
193227 _LOGGER .debug (f" Request to update plug received: { info } " )
194228 mac = info ['properties' ][b'id' ].decode ('utf-8' )
229+ self .cancel_any_pending_removal (mac , "request to update plug" )
195230 host = info ['addresses' ][0 ]
196231 port = info ['port' ]
197232 name = info ['name' ]
198- task = self ._pending_removals .pop (mac , None )
199- if task :
200- task .cancel ()
201- _LOGGER .info (f"Cancelled pending removal for { mac } by request to update api." )
233+
202234 if mac in self .plugs :
203235 current_api : PlugApi = self .plugs [mac ]
204236 if current_api ._listener ._ip == host and current_api ._listener ._port == port :
@@ -218,6 +250,7 @@ async def _plug_updated(self, info):
218250 await self .enqueue_plug_for_adding (network_info )
219251 await self .process_plug_queue ()
220252
253+
221254 async def _schedule_plug_removal (self , name , info ):
222255 _LOGGER .debug (f" Request to delete plug received: { info } " )
223256 if name in self ._known_plug_names :
@@ -228,7 +261,10 @@ async def _schedule_plug_removal(self, name, info):
228261 return
229262
230263 _LOGGER .info (f"Scheduling removal for { name } " )
231- self ._pending_removals [name ] = asyncio .create_task (self ._delayed_plug_remove (name ,mac ))
264+ self ._pending_removals [mac ] = self ._hass .async_create_background_task (
265+ self ._delayed_plug_remove (name ,mac ),
266+ name = f"Removal-Task-For-{ name } "
267+ )
232268 else :
233269 _LOGGER .warning (f"Received request to delete api for gateway with name [{ name } ], but this name"
234270 f"is not associated with an existing PlugAPI. Ignoring..." )
@@ -242,8 +278,8 @@ async def _delayed_plug_remove(self, name, mac):
242278 del self .plugs [mac ]
243279 del self ._known_plug_names [name ]
244280 except asyncio .CancelledError :
245- # Task was cancelled because service came back
246- _LOGGER .info (f"Request to remove plug { mac } was canceled by request to update, add plug or new message." )
281+ # Task was canceled because service came back
282+ _LOGGER .info (f"Request to remove plug { mac } was cancelled by request to update, add plug or new message." )
247283
248284 # Either way were done with this task
249- self ._pending_removals .pop (name , None )
285+ self ._pending_removals .pop (name , None )
0 commit comments