11import asyncio
22import base64
33import datetime
4+ import logging
5+ import os
46from typing import Optional , Literal , TypeVar
57
68from yarl import URL
@@ -24,42 +26,72 @@ class ClientCameraMixin(Client[_T]):
2426 _camera_pool : Optional [CameraPool ] = None
2527 _camera_uri : Optional [URL ] = None
2628 _camera_handle : Optional [CameraHandle ] = None
29+ _camera_status : Literal ["ok" , "new" , "err" ] = "ok"
2730 _camera_max_cache_age : Optional [datetime .timedelta ] = None
2831 _camera_pause_timeout : Optional [int ] = None
2932 _camera_debug : bool = False
33+ _camera_logger : logging .Logger = logging .getLogger (__name__ )
3034 _stream_lock : CancelableLock
3135 _stream_setup : asyncio .Event
3236 _request_count : int = 0
3337
3438 def initialize_camera_mixin (
35- self ,
36- camera_pool : Optional [CameraPool ] = None ,
37- pause_timeout : Optional [int ] = None ,
38- max_cache_age : Optional [datetime .timedelta ] = None ,
39- camera_debug = False ,
40- ** _kwargs ,
39+ self ,
40+ camera_pool : Optional [CameraPool ] = None ,
41+ pause_timeout : Optional [int ] = None ,
42+ max_cache_age : Optional [datetime .timedelta ] = None ,
43+ camera_debug : Optional [ bool ] = None ,
44+ ** _kwargs ,
4145 ):
4246 self ._camera_pool = camera_pool
4347 self ._stream_lock = CancelableLock ()
4448 self ._stream_setup = asyncio .Event ()
4549 self ._camera_pause_timeout = pause_timeout
4650 self ._camera_max_cache_age = max_cache_age
47- self ._camera_debug = camera_debug
51+ self ._camera_debug = (
52+ "SIMPLYPRINT_DEBUG_CAMERA" in os .environ
53+ if camera_debug is None
54+ else camera_debug
55+ )
56+ self ._camera_logger = self .logger .getChild ("camera" )
57+ self ._camera_logger .setLevel (logging .DEBUG if self ._camera_debug else logging .INFO )
58+
59+ @property
60+ def camera_status (self ) -> Literal ["ok" , "new" , "err" ]:
61+ """Get the camera status."""
62+ return self ._camera_status
63+
64+ @property
65+ def camera_uri (self ) -> Optional [URL ]:
66+ """Get the camera URI."""
67+ return self ._camera_uri
4868
49- def set_camera_uri (self , uri : Optional [URL ] = None ) -> Literal ["err" , "ok" , "new" ]:
69+ @camera_uri .setter
70+ def camera_uri (self , uri : Optional [URL ] = None ):
5071 """Returns whether it has changed the camera URI"""
5172 if self ._camera_pool is None :
52- return "err"
73+ self ._camera_status = "err"
74+ self ._camera_logger .debug (
75+ f"Dropped camera URI { uri } because no camera pool is available."
76+ )
77+ return
5378
5479 # If the camera URI is the same, don't recreate the camera.
5580 if self ._camera_uri == uri and self ._camera_handle :
56- return "ok"
81+ self ._camera_status = "ok"
82+ self ._camera_logger .debug (
83+ f"Camera URI { uri } is the same as the current one, not changing."
84+ )
85+ return
5786
5887 self ._camera_uri = uri
5988
60- # Clear out previous camera (if URI is different)
89+ # Clear out the previous camera (if URI is different)
6190 if self ._camera_handle :
6291 self ._camera_handle .stop ()
92+ self ._camera_logger .debug (
93+ f"Cleared previous camera handle ID { self ._camera_handle .id } ."
94+ )
6395 self ._camera_handle = None
6496 self .event_loop .call_soon_threadsafe (self ._stream_setup .clear )
6597
@@ -80,10 +112,13 @@ def set_camera_uri(self, uri: Optional[URL] = None) -> Literal["err", "ok", "new
80112 if not self .printer .webcam_info .connected :
81113 self .printer .webcam_info .connected = True
82114
83- return "new"
115+ self ._camera_status = "new"
116+ self ._camera_logger .debug (
117+ f"Set new camera URI to { uri } with handle ID { self ._camera_handle .id } , status is now { self ._camera_status } ."
118+ )
84119
85120 def __del__ (self ):
86- self .set_camera_uri ( None )
121+ self .camera_uri = None
87122
88123 async def on_stream_on (self ):
89124 if not self ._camera_handle :
@@ -109,10 +144,10 @@ def _before_webcam_snapshot(self, data: WebcamSnapshotDemandData):
109144 self ._request_count += 1
110145
111146 async def on_webcam_snapshot (
112- self ,
113- data : WebcamSnapshotDemandData = WebcamSnapshotDemandData (),
114- attempt = 0 ,
115- retry_timeout = 5 ,
147+ self ,
148+ data : WebcamSnapshotDemandData = WebcamSnapshotDemandData (),
149+ attempt = 0 ,
150+ retry_timeout = 5 ,
116151 ):
117152 if not self ._camera_handle :
118153 await self ._stream_setup .wait ()
@@ -130,27 +165,28 @@ async def on_webcam_snapshot(
130165 # Empty frame or none.
131166 if not frame :
132167 if attempt <= 3 :
133- self .logger .debug (
168+ self ._camera_logger .debug (
134169 f"Failed to get frame, retrying in { retry_timeout } seconds"
135170 )
136171 await asyncio .sleep (retry_timeout )
137172 await self .on_webcam_snapshot (data , attempt + 1 , retry_timeout )
138173 else :
139- self .logger .debug ("Failed to get frame, giving up." )
174+ self ._camera_logger .debug (
175+ f"Failed to get frame, giving up. Used camera handle id { self ._camera_handle .id } ."
176+ )
140177
141178 return
142179
143- if self ._camera_debug :
144- self .logger .debug (
145- f"Received frame from camera with size { len (frame ) if frame else 0 } bytes "
146- f"with an fps of { self ._camera_handle .fps or 'N/A' } in "
147- f"{ datetime .datetime .now () - st } ."
148- )
180+ self ._camera_logger .debug (
181+ f"Received frame from camera with size { len (frame ) if frame else 0 } bytes "
182+ f"with an fps of { self ._camera_handle .fps or 'N/A' } in "
183+ f"{ datetime .datetime .now () - st } from camera handle id { self ._camera_handle .id } ."
184+ )
149185
150186 # Capture snapshot events and send them to the API
151187 if is_snapshot_event :
152188 await SimplyPrintApi .post_snapshot (data .id , frame , endpoint = data .endpoint )
153- self .logger .debug (f"Posted snapshot to API with id { data .id } " )
189+ self ._camera_logger .debug (f"Posted snapshot to API with id { data .id } " )
154190 return
155191
156192 # Mark the webcam as connected if it's not already.
0 commit comments