77from typing import Any , Dict , List , Optional
88
99from octoeverywhere .compat import Compat
10+ from octoeverywhere .repeattimer import RepeatTimer
1011from octoeverywhere .sentry import Sentry
1112from octoeverywhere .websocketimpl import Client
1213from octoeverywhere .octohttprequest import OctoHttpRequest
@@ -184,7 +185,7 @@ def IsDisconnectDueToTooManyClients(self) -> bool:
184185
185186 # Sends a command to the printer to enable the webcam.
186187 def SendEnableWebcamCommand (self , waitForResponse :bool = True ) -> ResponseMsg :
187- return ElegooClient . Get () .SendRequest (386 , {"Enable" :1 }, waitForResponse = waitForResponse )
188+ return self .SendRequest (386 , {"Enable" :1 }, waitForResponse = waitForResponse )
188189
189190
190191 # Sends a command to all connected frontends to show a popup message.
@@ -205,6 +206,8 @@ def SendFrontendPopupMsg(self, title:str, text:str, msgType:str, actionText:Opti
205206
206207 # Sends a request to the printer and waits for a response.
207208 # Always returns a ResponseMsg, with various error codes.
209+ # Full protocol:
210+ # https://github.com/cbd-tech/SDCP-Smart-Device-Control-Protocol-V3.0.0/blob/main/SDCP(Smart%20Device%20Control%20Protocol)_V3.0.0_EN.md
208211 def SendRequest (self , cmdId :int , data :Optional [Dict [str , Any ]]= None , waitForResponse :bool = True , timeoutSec :Optional [float ]= None ) -> ResponseMsg :
209212 # Generate a request id, which is a 32 char lowercase letter and number string
210213 requestId = '' .join (random .choices (string .ascii_lowercase + string .digits , k = 32 ))
@@ -330,11 +333,15 @@ def _ClientWorker(self):
330333 self .WebSocket = Client (url , onWsOpen = self ._OnWsConnect , onWsClose = self ._OnWsClose , onWsError = self ._OnWsError , onWsData = self ._OnWsData )
331334 self .WebSocket .SetDisableCertCheck (True )
332335
333- # Connect to the server
334- with self .WebSocket :
335- # Use a more aggressive ping timeout because if the printer power cycles, we don't get the TCP close message.
336- # Time ping timeout must be less than the ping interval.
337- self .WebSocket .RunUntilClosed (pingIntervalSec = 30 )
336+ # Important! The connection to the print will close after 1 minute if we don't send any messages.
337+ # Even if the websocket sends the ws ping message, it doesn't seem to reset the idle timer.
338+ # So, we will use a repeat timer to send the SDCP protocol ping message every 50 seconds.
339+ with RepeatTimer (self .Logger , "ElegooClientWsMsgKeepalive" , 50.0 , self ._RepeatTimerKeepaliveTick ) as t :
340+ t .start ()
341+ # Connect to the server
342+ with self .WebSocket :
343+ # We use a ping payload of "ping" because it's what the web portal uses.
344+ self .WebSocket .RunUntilClosed (pingPayload = "ping" )
338345 except Exception as e :
339346 Sentry .OnException ("Elegoo client exception in main WS loop." , e )
340347
@@ -357,6 +364,16 @@ def _ClientWorker(self):
357364 self .SleepEvent .clear ()
358365
359366
367+ def _RepeatTimerKeepaliveTick (self ):
368+ # Any message works, but this is the lightest weight.
369+ # We don't care about the result
370+ if self ._WebSocketSend (Buffer ("ping" .encode ("utf-8" ))) is False :
371+ localWs = self .WebSocket
372+ if localWs is not None and self .WebSocketConnected is True :
373+ self .Logger .info ("Elegoo client - keepalive tick failed to send ping message, closing the websocket." )
374+ localWs .Close ()
375+
376+
360377 # Fired whenever the client is disconnected, we need to clean up the state since it's now unknown.
361378 def _CleanupStateOnDisconnect (self ):
362379 self .State = None
@@ -368,7 +385,7 @@ def _CleanupStateOnDisconnect(self):
368385
369386 # Fired when the websocket is connected.
370387 def _OnWsConnect (self , ws :IWebSocketClient ):
371- self .Logger .info ( "Connection to the Elegoo printer established! " )
388+ self .Logger .debug ( " Elegoo websocket established" )
372389 LocalWebApi .Get ().SetPrinterConnectionState (True )
373390
374391 # Set the connected flag now, so we can send messages.
@@ -519,7 +536,7 @@ def _HandleAttributesUpdate(self, attributes:Dict[str, Any]):
519536 s = PrinterAttributes (self .Logger )
520537 s .OnUpdate (attributes )
521538 self .Attributes = s
522- self .Logger .info ("Elegoo printer attributes object created." )
539+ self .Logger .debug ("Elegoo printer attributes object created." )
523540 else :
524541 self .Attributes .OnUpdate (attributes )
525542 except Exception as e :
@@ -573,7 +590,7 @@ def _HandleAttributesUpdate(self, attributes:Dict[str, Any]):
573590
574591 # We have a match, so we are good.
575592 self .Config .SetStr (Config .SectionCompanion , Config .CompanionKeyIpOrHostname , wsConIp )
576- self .Logger .info ("Elegoo client connection finalized ." )
593+ self .Logger .info ("Elegoo client connection fully connected ." )
577594
578595
579596 def _HandleStatusUpdate (self , status :Dict [str , Any ]):
@@ -585,7 +602,7 @@ def _HandleStatusUpdate(self, status:Dict[str, Any]):
585602 s = PrinterState (self .Logger )
586603 s .OnUpdate (status )
587604 self .State = s
588- self .Logger .info ("Elegoo printer state object created." )
605+ self .Logger .debug ("Elegoo printer state object created." )
589606 else :
590607 self .State .OnUpdate (status )
591608 except Exception as e :
0 commit comments