2424
2525UPDATE_TOPIC = f"{ DOMAIN } _update"
2626ERROR_ITERVAL_MAPPING = [0 , 10 , 60 , 300 , 600 , 3000 , 6000 ]
27+ ERROR_RECONNECT_INTERVAL = 120
2728NOTIFICATION_ID = "ph803w_device_notification"
2829NOTIFICATION_TITLE = "PH-803W Device status"
2930
4041)
4142
4243
43- def setup (hass : HomeAssistant , base_config : ConfigType ) -> bool :
44+ async def async_setup (hass : HomeAssistant , base_config : ConfigType ) -> bool :
4445 """Set up waterfurnace platform."""
4546
4647 config = base_config [DOMAIN ]
4748
48- host = config [CONF_HOST ]
49-
50- device_client = device .Device (host )
51- try :
52- if not device_client .run (once = True ):
53- _LOGGER .error ("Device found but no measuremetn was received" )
54- return False
55- except TimeoutError :
56- _LOGGER .error ("Could no connect ot device" )
57- return False
58-
59- hass .data [DOMAIN ] = DeviceData (hass , device_client )
49+ hass .data [DOMAIN ] = DeviceData (hass , config )
6050 hass .data [DOMAIN ].start ()
6151
6252 discovery .load_platform (hass , Platform .SENSOR , DOMAIN , {}, config )
@@ -72,17 +62,33 @@ class DeviceData(threading.Thread):
7262 for every new data, could work for the pH and ORP data but for the
7363 switches a more direct feedback is wanted."""
7464
75- def __init__ (self , hass , device_client : device . Device ) -> None :
65+ def __init__ (self , hass , config ) -> None :
7666 super ().__init__ ()
7767 self .name = "Ph803wThread"
7868 self .hass = hass
79- self .device_client = device_client
80- self .device_client .register_callback (self .dispatcher_new_data )
81- self .device_client .register_callback (self .reset_fail_counter )
82- self .host = self .device_client .host
69+ self .host = config [CONF_HOST ]
70+ self .device_client = None
8371 self ._shutdown = False
8472 self ._fails = 0
8573
74+ def connected (self ):
75+ return self .device_client is not None
76+
77+ def passcode (self ):
78+ if self .device_client is not None :
79+ return self .device_client .passcode
80+ return None
81+
82+ def unique_name (self ):
83+ if self .device_client is not None :
84+ return self .device_client .get_unique_name ()
85+ return None
86+
87+ def measurement (self ):
88+ if self .device_client is not None :
89+ return self .device_client .get_latest_measurement ()
90+ return None
91+
8692 def run (self ):
8793 """Thread run loop."""
8894
@@ -92,9 +98,10 @@ def register():
9298
9399 def shutdown (event ):
94100 """Shutdown the thread."""
95- _LOGGER .debug ("Signaled to shutdown" )
101+ _LOGGER .info ("Signaled to shutdown" )
96102 self ._shutdown = True
97- self .device_client .abort ()
103+ if self .device_client is not None :
104+ self .device_client .abort ()
98105 self .join ()
99106
100107 self .hass .bus .async_listen_once (EVENT_HOMEASSISTANT_STOP , shutdown )
@@ -108,25 +115,53 @@ def shutdown(event):
108115 # least every 4 seconds the device side closes the
109116 # connection.
110117 while True :
111- if self ._shutdown :
112- _LOGGER .debug ("Graceful shutdown" )
113- return
118+ self .device_client = None
119+
120+ _LOGGER .info (f"Attempting to connect to device at { self .host } " )
121+ device_client = device .Device (self .host )
114122
115123 try :
116- self .device_client .run (once = False )
117- except (device .DeviceError , RecursionError , ConnectionError ):
118- _LOGGER .exception ("Failed to read data, attempting to recover" )
119- self .device_client .close ()
120- self ._fails += 1
121- error_mapping = self ._fails
122- if error_mapping >= len (ERROR_ITERVAL_MAPPING ):
123- error_mapping = len (ERROR_ITERVAL_MAPPING ) - 1
124- sleep_time = ERROR_ITERVAL_MAPPING [error_mapping ]
125- _LOGGER .debug (
126- "Sleeping for fail #%s, in %s seconds" , self ._fails , sleep_time
127- )
128- self .device_client .reset_socket ()
129- time .sleep (sleep_time )
124+ if not device_client .run (once = True ):
125+ _LOGGER .info (
126+ f"Device found but no measurement was received, reconnecting in { ERROR_RECONNECT_INTERVAL } seconds" )
127+ time .sleep (ERROR_RECONNECT_INTERVAL )
128+ continue
129+
130+ except Exception as e :
131+ _LOGGER .info (
132+ f"Error connecting to device at { self .host } : { str (e )} " )
133+ _LOGGER .info (
134+ f"Retrying connection in { ERROR_RECONNECT_INTERVAL } seconds" )
135+ time .sleep (ERROR_RECONNECT_INTERVAL )
136+ continue
137+
138+ self .device_client = device_client
139+ _LOGGER .debug ("Registering callbacks" )
140+ self .device_client .register_callback (self .dispatcher_new_data )
141+ self .device_client .register_callback (self .reset_fail_counter )
142+
143+ _LOGGER .info (f"Connected to { self .host } " )
144+
145+ while True :
146+ if self ._shutdown :
147+ _LOGGER .debug ("Graceful shutdown" )
148+ return
149+
150+ try :
151+ _LOGGER .info ("Starting device client loop" )
152+ self .device_client .run (once = False )
153+ except Exception as e :
154+ _LOGGER .exception (f"Failed to read data: { str (e )} " )
155+ self .device_client .close ()
156+ self ._fails += 1
157+ error_mapping = self ._fails
158+ if error_mapping >= len (ERROR_ITERVAL_MAPPING ):
159+ error_mapping = len (ERROR_ITERVAL_MAPPING ) - 1
160+ sleep_time = ERROR_ITERVAL_MAPPING [error_mapping ]
161+ _LOGGER .info (
162+ f"Sleeping { str (sleep_time )} s for failure #{ str (self ._fails )} " )
163+ self .device_client .reset_socket ()
164+ time .sleep (sleep_time )
130165
131166 @callback
132167 def reset_fail_counter (self ):
0 commit comments