Skip to content

Commit c831705

Browse files
committed
feat: Add pinging to local client
1 parent 799a5c4 commit c831705

File tree

1 file changed

+29
-0
lines changed

1 file changed

+29
-0
lines changed

roborock/devices/local_channel.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
_LOGGER = logging.getLogger(__name__)
1818
_PORT = 58867
1919
_TIMEOUT = 5.0
20+
_PING_INTERVAL = 10
2021

2122

2223
@dataclass
@@ -58,6 +59,7 @@ def __init__(self, host: str, local_key: str):
5859
self._subscribers: CallbackList[RoborockMessage] = CallbackList(_LOGGER)
5960
self._is_connected = False
6061
self._local_protocol_version: LocalProtocolVersion | None = None
62+
self._keep_alive_task: asyncio.Task[None] | None = None
6163
self._update_encoder_decoder(
6264
LocalChannelParams(local_key=local_key, connect_nonce=get_next_int(10000, 32767), ack_nonce=None)
6365
)
@@ -132,6 +134,28 @@ async def _hello(self):
132134

133135
raise RoborockException("Failed to connect to device with any known protocol")
134136

137+
async def _ping(self) -> None:
138+
ping_message = RoborockMessage(
139+
protocol=RoborockMessageProtocol.PING_REQUEST, version=self.protocol_version.encode()
140+
)
141+
await self._send_message(
142+
roborock_message=ping_message,
143+
request_id=ping_message.seq,
144+
response_protocol=RoborockMessageProtocol.PING_RESPONSE,
145+
)
146+
147+
async def _keep_alive_loop(self) -> None:
148+
while self._is_connected:
149+
try:
150+
await asyncio.sleep(_PING_INTERVAL)
151+
if self._is_connected:
152+
await self._ping()
153+
except asyncio.CancelledError:
154+
break
155+
except Exception:
156+
_LOGGER.debug("Keep-alive ping failed", exc_info=True)
157+
# Retry next interval
158+
135159
@property
136160
def protocol_version(self) -> LocalProtocolVersion:
137161
"""Return the negotiated local protocol version, or a sensible default."""
@@ -166,6 +190,7 @@ async def connect(self) -> None:
166190
# Perform protocol negotiation
167191
try:
168192
await self._hello()
193+
self._keep_alive_task = asyncio.create_task(self._keep_alive_loop())
169194
except RoborockException:
170195
# If protocol negotiation fails, clean up the connection state
171196
self.close()
@@ -177,6 +202,8 @@ def _data_received(self, data: bytes) -> None:
177202

178203
def close(self) -> None:
179204
"""Disconnect from the device."""
205+
if self._keep_alive_task:
206+
self._keep_alive_task.cancel()
180207
if self._transport:
181208
self._transport.close()
182209
else:
@@ -187,6 +214,8 @@ def close(self) -> None:
187214
def _connection_lost(self, exc: Exception | None) -> None:
188215
"""Handle connection loss."""
189216
_LOGGER.warning("Connection lost to %s", self._host, exc_info=exc)
217+
if self._keep_alive_task:
218+
self._keep_alive_task.cancel()
190219
self._transport = None
191220
self._is_connected = False
192221

0 commit comments

Comments
 (0)