9
9
from homeassistant .components .switch import DOMAIN as SWITCH_DOMAIN
10
10
from homeassistant .components .water_heater import DOMAIN as WATER_HEATER_DOMAIN
11
11
from homeassistant .config_entries import ConfigEntry
12
- from homeassistant .const import CONF_HOST , EVENT_HOMEASSISTANT_STOP
12
+ from homeassistant .const import (
13
+ CONF_HOST ,
14
+ EVENT_HOMEASSISTANT_STOP ,
15
+ TEMP_CELSIUS ,
16
+ TEMP_FAHRENHEIT ,
17
+ )
13
18
from homeassistant .core import HomeAssistant , callback
14
19
from homeassistant .exceptions import ConfigEntryNotReady
15
20
from homeassistant .helpers import dispatcher
16
21
from homeassistant .helpers .entity import Entity
17
22
from homeassistant .helpers .typing import ConfigType
18
23
19
24
from .const import DOMAIN
20
- from .pyintellicenter import ConnectionHandler , ModelController , PoolModel
25
+ from .pyintellicenter import ConnectionHandler , ModelController , PoolModel , PoolObject
21
26
22
27
_LOGGER = logging .getLogger (__name__ )
23
28
@@ -39,24 +44,37 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
39
44
async def async_setup_entry (hass : HomeAssistant , entry : ConfigEntry ) -> bool :
40
45
"""Set up IntelliCenter integration from a config entry."""
41
46
42
- # we are only interested in a subset of all objects
43
- def filterFunc (object ):
44
- """Return true for the objects we care about."""
45
- return object .status and object .objtype in [
46
- "BODY" ,
47
- "SENSE" ,
48
- "PUMP" ,
49
- "HEATER" ,
50
- "CIRCUIT" ,
51
- ]
47
+ # we don't need some of the system objects
48
+ def ignoreFunc (object ):
49
+ """Return False for the objects we want to ignore."""
50
+
51
+ return (
52
+ object .objtype
53
+ in [
54
+ "PANEL" ,
55
+ "MODULE" ,
56
+ "PERMIT" ,
57
+ "SYSTIM" ,
58
+ ]
59
+ or object .subtype in ["LEGACY" ]
60
+ )
52
61
53
- model = PoolModel (filterFunc )
62
+ attributes_map = {
63
+ "BODY" : {"SNAME" , "HEATER" , "HTMODE" , "LOTMP" , "LSTTMP" , "STATUS" },
64
+ "CIRCUIT" : {"SNAME" , "STATUS" , "USE" , "SUBTYPE" , "FEATR" },
65
+ "CIRCGRP" : {"CIRCUIT" },
66
+ "HEATER" : {"SNAME" , "BODY" },
67
+ "PUMP" : {"SNAME" , "STATUS" , "PWR" , "RPM" , "GPM" },
68
+ "SENSE" : {"SNAME" , "SOURCE" },
69
+ }
70
+ model = PoolModel (attributes_map )
54
71
55
72
controller = ModelController (entry .data [CONF_HOST ], model , loop = hass .loop )
56
73
57
74
class Handler (ConnectionHandler ):
58
75
59
76
UPDATE_SIGNAL = DOMAIN + "_UPDATE_" + entry .entry_id
77
+ CONNECTION_SIGNAL = DOMAIN + "_CONNECTION_" + entry .entry_id
60
78
61
79
def started (self , controller ):
62
80
@@ -74,32 +92,29 @@ async def setup_platforms():
74
92
]
75
93
)
76
94
95
+ # dispatcher.async_dispatcher_send(hass, self.CONNECTION_SIGNAL, True)
96
+
77
97
hass .async_create_task (setup_platforms ())
78
98
79
99
@callback
80
100
def reconnected (self , controller ):
81
101
"""Handle reconnection from the Pentair system."""
82
102
_LOGGER .info (f"reconnected to system: '{ controller .systemInfo .propName } '" )
83
- dispatcher .async_dispatcher_send (
84
- hass , self .UPDATE_SIGNAL , controller .model .objectList
85
- )
103
+ dispatcher .async_dispatcher_send (hass , self .CONNECTION_SIGNAL , True )
86
104
87
105
@callback
88
106
def disconnected (self , controller , exc ):
89
107
"""Handle updates from the Pentair system."""
90
108
_LOGGER .info (
91
109
f"disconnected from system: '{ controller .systemInfo .propName } '"
92
110
)
93
- dispatcher .async_dispatcher_send (
94
- hass , DOMAIN + "_DISCONNECT_" + entry .entry_id
95
- )
111
+ dispatcher .async_dispatcher_send (hass , self .CONNECTION_SIGNAL , False )
96
112
97
113
@callback
98
- def updated (self , controller , changes ):
114
+ def updated (self , controller , updates : Dict [ str , PoolObject ] ):
99
115
"""Handle updates from the Pentair system."""
100
- for object in changes :
101
- _LOGGER .debug (f"received update for { object } " )
102
- dispatcher .async_dispatcher_send (hass , self .UPDATE_SIGNAL , changes )
116
+ _LOGGER .debug (f"received update for { len (updates )} pool objects" )
117
+ dispatcher .async_dispatcher_send (hass , self .UPDATE_SIGNAL , updates )
103
118
104
119
try :
105
120
@@ -155,13 +170,22 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
155
170
class PoolEntity (Entity ):
156
171
"""Representation of an Pool entity linked to an pool object."""
157
172
158
- def __init__ (self , entry : ConfigEntry , controller , poolObject ):
173
+ def __init__ (
174
+ self ,
175
+ entry : ConfigEntry ,
176
+ controller : ModelController ,
177
+ poolObject : PoolObject ,
178
+ attribute_key = "STATUS" ,
179
+ name_suffix = "" ,
180
+ ):
159
181
"""Initialize a Pool entity."""
160
182
self ._entry_id = entry .entry_id
161
183
self ._controller = controller
162
184
self ._poolObject = poolObject
163
185
self ._available = True
164
186
self ._extraStateAttributes = []
187
+ self ._name_suffix = name_suffix
188
+ self ._attribute_key = attribute_key
165
189
166
190
_LOGGER .debug (f"mapping { poolObject } " )
167
191
@@ -176,8 +200,8 @@ async def async_added_to_hass(self):
176
200
self .async_on_remove (
177
201
dispatcher .async_dispatcher_connect (
178
202
self .hass ,
179
- DOMAIN + "_DISCONNECT_ " + self ._entry_id ,
180
- self ._disconnect_callback ,
203
+ DOMAIN + "_CONNECTION_ " + self ._entry_id ,
204
+ self ._connection_callback ,
181
205
)
182
206
)
183
207
@@ -193,12 +217,18 @@ def available(self):
193
217
@property
194
218
def name (self ):
195
219
"""Return the name of the entity."""
196
- return self ._poolObject .sname
220
+ name = self ._poolObject .sname
221
+ if self ._name_suffix :
222
+ name += " " + self ._name_suffix
223
+ return name
197
224
198
225
@property
199
226
def unique_id (self ):
200
227
"""Return a unique ID."""
201
- return self ._entry_id + self ._poolObject .objnam
228
+ my_id = self ._entry_id + self ._poolObject .objnam
229
+ if self ._attribute_key != "STATUS" :
230
+ my_id += self ._attribute_key
231
+ return my_id
202
232
203
233
@property
204
234
def should_poll (self ):
@@ -250,15 +280,29 @@ def requestChanges(self, changes: dict) -> None:
250
280
)
251
281
252
282
@callback
253
- def _update_callback (self , changes ):
283
+ def _update_callback (self , updates : Dict [ str , Dict [ str , str ]] ):
254
284
"""Update the entity if its underlying pool object has changed."""
255
- for object in changes :
256
- if object .objnam == self ._poolObject .objnam :
257
- self ._available = True
258
- self .async_write_ha_state ()
285
+
286
+ if self ._attribute_key in updates .get (self ._poolObject .objnam , {}):
287
+ self ._available = True
288
+ my_updates = updates .get (self ._poolObject .objnam )
289
+ _LOGGER .debug (f"updating { self } from { my_updates } " )
290
+ self .async_write_ha_state ()
259
291
260
292
@callback
261
- def _disconnect_callback (self ):
293
+ def _connection_callback (self , is_connected ):
262
294
"""Mark the entity as unavailable after being disconnected from the server."""
263
- self ._available = False
295
+ if is_connected :
296
+ self ._poolObject = self ._controller .model [self ._poolObject .objnam ]
297
+ if not self ._poolObject :
298
+ # this is for the rare case where the object the entity is mapped to
299
+ # had been removed from the Pentair system while we were disconnected
300
+ return
301
+ self ._available = is_connected
264
302
self .async_write_ha_state ()
303
+
304
+ def pentairTemperatureSettings (self ):
305
+ """Return the temperature units from the Pentair system."""
306
+ return (
307
+ TEMP_CELSIUS if self ._controller .systemInfo .usesMetric else TEMP_FAHRENHEIT
308
+ )
0 commit comments