fix issue with vertex2 reconnection after HA reboot

This commit is contained in:
Pierre committed 2026-09-27 08:52:03 +02:00
1 parent acdd14aaeb
commit 00f6b28342
1 file changed
+24 -6
+24 -6
View File
@@ -11,6 +11,8 @@ from urllib.parse import urlparse
class Vertex2(SmartObject): class Vertex2(SmartObject):
def on_initialize_smart_object(self): def on_initialize_smart_object(self):
# HASS can call initialize() again without calling terminate() first.
self._stop_interrupt_listener()
self._interrupt_stop = threading.Event() self._interrupt_stop = threading.Event()
self._status_requested = threading.Event() self._status_requested = threading.Event()
self._tx0_recovery_attempted = False self._tx0_recovery_attempted = False
@@ -143,22 +145,25 @@ class Vertex2(SmartObject):
self.vertex2_tcp_port = int(self.args.get('vertex2_tcp_port', 2220)) self.vertex2_tcp_port = int(self.args.get('vertex2_tcp_port', 2220))
self._interrupt_thread = threading.Thread( self._interrupt_thread = threading.Thread(
target=self._interrupt_listener, target=self._interrupt_listener,
args=(self._interrupt_stop,),
name=f'{self.name}_interrupt_listener', name=f'{self.name}_interrupt_listener',
daemon=True, daemon=True,
) )
self.update_vertex2_states() self.update_vertex2_states()
self._interrupt_thread.start() self._interrupt_thread.start()
def _interrupt_listener(self): def _interrupt_listener(self, interrupt_stop):
address = urlparse(self.args['vertex2_address']) address = urlparse(self.args['vertex2_address'])
host = address.hostname host = address.hostname
status_fields = ('rx0', 'rx1', 'tx0', 'tx1', 'tx0sink', 'tx1sink', 'aud0', 'aud1', 'audout', 'spd0', 'spd1') status_fields = ('rx0', 'rx1', 'tx0', 'tx1', 'tx0sink', 'tx1sink', 'aud0', 'aud1', 'audout', 'spd0', 'spd1')
commands = tuple(f'get status {field}' for field in status_fields) + ('get insel',) commands = tuple(f'get status {field}' for field in status_fields) + ('get insel',)
expected = set(status_fields) | {'insel'} expected = set(status_fields) | {'insel'}
while not self._interrupt_stop.is_set(): while not interrupt_stop.is_set():
try: try:
with socket.create_connection((host, self.vertex2_tcp_port), timeout=10) as connection: with socket.create_connection((host, self.vertex2_tcp_port), timeout=10) as connection:
if interrupt_stop.is_set():
break
# Detect a silent reboot/network loss without polling HDMI status. # Detect a silent reboot/network loss without polling HDMI status.
connection.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) connection.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 10) connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 10)
@@ -178,7 +183,7 @@ class Vertex2(SmartObject):
previous_snapshot = None previous_snapshot = None
previous_snapshot_generation = None previous_snapshot_generation = None
while not self._interrupt_stop.is_set(): while not interrupt_stop.is_set():
if status_started is None and self._status_requested.is_set(): if status_started is None and self._status_requested.is_set():
self._status_requested.clear() self._status_requested.clear()
status = {} status = {}
@@ -195,6 +200,8 @@ class Vertex2(SmartObject):
raise OSError('Vertex2 TCP status response timed out') raise OSError('Vertex2 TCP status response timed out')
continue continue
if interrupt_stop.is_set():
break
if not data: if not data:
raise OSError('Vertex2 closed the IP INTERRUPT connection') raise OSError('Vertex2 closed the IP INTERRUPT connection')
@@ -251,7 +258,7 @@ class Vertex2(SmartObject):
raise OSError('Vertex2 TCP status response timed out') raise OSError('Vertex2 TCP status response timed out')
except (OSError, ValueError) as error: except (OSError, ValueError) as error:
if self._interrupt_stop.is_set(): if interrupt_stop.is_set():
break break
self.unreachable_sensor.set_state(state='on') self.unreachable_sensor.set_state(state='on')
self.video_tx_signal_sensor.set_state(state='unavailable') self.video_tx_signal_sensor.set_state(state='unavailable')
@@ -262,7 +269,7 @@ class Vertex2(SmartObject):
sensor.set_state(state='unavailable') sensor.set_state(state='unavailable')
self.audio_out_signal_sensor.set_state(state='unavailable') self.audio_out_signal_sensor.set_state(state='unavailable')
self.log(f'Vertex2 IP INTERRUPT unavailable: {error}', level='WARNING') self.log(f'Vertex2 IP INTERRUPT unavailable: {error}', level='WARNING')
self._interrupt_stop.wait(5) interrupt_stop.wait(5)
def trigger_tx0_stuck_workaround(self, kwargs): def trigger_tx0_stuck_workaround(self, kwargs):
if self._tx0_recovery_attempted: if self._tx0_recovery_attempted:
@@ -288,13 +295,24 @@ class Vertex2(SmartObject):
self.log_info('Vertex2 TX0 +5V workaround: restoring TX0 +5V FORCE') self.log_info('Vertex2 TX0 +5V workaround: restoring TX0 +5V FORCE')
self.force_tx0_plus5(True) self.force_tx0_plus5(True)
def terminate(self): def _stop_interrupt_listener(self):
interrupt_stop = getattr(self, '_interrupt_stop', None) interrupt_stop = getattr(self, '_interrupt_stop', None)
if interrupt_stop is not None: if interrupt_stop is not None:
interrupt_stop.set() interrupt_stop.set()
interrupt_thread = getattr(self, '_interrupt_thread', None)
if interrupt_thread is not None:
# Allow the 10s connect timeout or 1s recv timeout to finish.
interrupt_thread.join(timeout=12)
if interrupt_thread.is_alive():
raise RuntimeError('Previous Vertex2 IP INTERRUPT listener did not stop')
self._interrupt_thread = None
restore_handle = getattr(self, '_tx0_recovery_restore_handle', None) restore_handle = getattr(self, '_tx0_recovery_restore_handle', None)
if restore_handle is not None: if restore_handle is not None:
self.cancel_timer(restore_handle) self.cancel_timer(restore_handle)
self._tx0_recovery_restore_handle = None
def terminate(self):
self._stop_interrupt_listener()
super().terminate() super().terminate()
def generate_input_selected_sensor_name(self,i): def generate_input_selected_sensor_name(self,i):