python-aidot-cameras 0.9.0__tar.gz → 0.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. {python_aidot_cameras-0.9.0/src/python_aidot_cameras.egg-info → python_aidot_cameras-0.9.1}/PKG-INFO +5 -5
  2. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/pyproject.toml +10 -5
  3. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/client.py +19 -19
  4. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/models.py +3 -3
  5. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/playback.py +2 -2
  6. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/protocol.py +16 -16
  7. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/sdes.py +7 -7
  8. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/sdes_open.py +43 -43
  9. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/tutk.py +2 -2
  10. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/webrtc.py +4 -4
  11. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/webrtc_open.py +9 -9
  12. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1/src/python_aidot_cameras.egg-info}/PKG-INFO +5 -5
  13. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/python_aidot_cameras.egg-info/requires.txt +4 -4
  14. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/LICENSE +0 -0
  15. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/MANIFEST.in +0 -0
  16. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/README.md +0 -0
  17. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/setup.cfg +0 -0
  18. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/__init__.py +0 -0
  19. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/aes_utils.py +0 -0
  20. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/__init__.py +0 -0
  21. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/constants.py +0 -0
  22. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/controls.py +0 -0
  23. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/go2rtc.py +0 -0
  24. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/camera/lan_control.py +0 -0
  25. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/client.py +0 -0
  26. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/const.py +0 -0
  27. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/credentials.py +0 -0
  28. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/device_client.py +0 -0
  29. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/discover.py +0 -0
  30. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/exceptions.py +0 -0
  31. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/g711.py +0 -0
  32. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/login_const.py +0 -0
  33. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/models/__init__.py +0 -0
  34. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/models/device_client_model.py +0 -0
  35. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/models/device_model.py +0 -0
  36. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/models/discover_model.py +0 -0
  37. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/aidot/py.typed +0 -0
  38. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
  39. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  40. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/src/python_aidot_cameras.egg-info/top_level.txt +0 -0
  41. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_aioice_compat.py +0 -0
  42. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_alarm_event.py +0 -0
  43. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_backoff.py +0 -0
  44. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_device_login_guard.py +0 -0
  45. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_go2rtc.py +0 -0
  46. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_highport_nomination.py +0 -0
  47. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_ice_config_cache.py +0 -0
  48. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_lan_control.py +0 -0
  49. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_live_stream_param.py +0 -0
  50. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_motion_poll.py +0 -0
  51. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_no_undefined_names.py +0 -0
  52. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_persistent_mqtt.py +0 -0
  53. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_persistent_mqtt_robustness.py +0 -0
  54. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_post_merge_hardening.py +0 -0
  55. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_sdes_adaptive.py +0 -0
  56. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_sdes_fast_liveplay.py +0 -0
  57. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_sdes_idle_release.py +0 -0
  58. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_sdes_serve_audio.py +0 -0
  59. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_sdes_serve_cmd.py +0 -0
  60. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_sdes_sprop.py +0 -0
  61. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_sdes_talk.py +0 -0
  62. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_sdes_watchdog.py +0 -0
  63. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_serve_relay.py +0 -0
  64. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_session_stats.py +0 -0
  65. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_speak.py +0 -0
  66. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_stream_cap.py +0 -0
  67. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_stream_idle.py +0 -0
  68. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_stream_teardown.py +0 -0
  69. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_talk.py +0 -0
  70. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_terminal_ack.py +0 -0
  71. {python_aidot_cameras-0.9.0 → python_aidot_cameras-0.9.1}/tests/test_token_refresh.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
5
5
  Author-email: cbrightly <chris.brightly@gmail.com>
6
6
  License-Expression: MIT
@@ -11,11 +11,11 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.11
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: aiohttp
14
+ Requires-Dist: aiohttp>=3.9
15
15
  Requires-Dist: paho-mqtt>=2.0
16
- Requires-Dist: cryptography
17
- Requires-Dist: pycryptodome
18
- Requires-Dist: dacite
16
+ Requires-Dist: cryptography>=42.0
17
+ Requires-Dist: pycryptodome>=3.20
18
+ Requires-Dist: dacite>=1.8
19
19
  Provides-Extra: webrtc
20
20
  Requires-Dist: aiortc>=1.9.0; extra == "webrtc"
21
21
  Requires-Dist: aioice<0.12,>=0.9.0; extra == "webrtc"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-aidot-cameras"
7
- version = "0.9.0"
7
+ version = "0.9.1"
8
8
  description = "Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -15,12 +15,17 @@ classifiers = [
15
15
  "Programming Language :: Python :: 3",
16
16
  "Operating System :: OS Independent",
17
17
  ]
18
+ # Conservative lower bounds only - no upper caps. When installed under Home
19
+ # Assistant, HA core pins aiohttp/cryptography itself; an upper cap here would
20
+ # fight HA's resolver on the box. Floors just exclude ancient releases while
21
+ # leaving the consumer (or HA) free to use a newer version. Bump a floor only
22
+ # after re-validating against it.
18
23
  dependencies = [
19
- "aiohttp",
24
+ "aiohttp>=3.9",
20
25
  "paho-mqtt>=2.0",
21
- "cryptography",
22
- "pycryptodome",
23
- "dacite",
26
+ "cryptography>=42.0",
27
+ "pycryptodome>=3.20",
28
+ "dacite>=1.8",
24
29
  ]
25
30
 
26
31
  [project.optional-dependencies]
@@ -1467,7 +1467,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
1467
1467
  try:
1468
1468
  self.status.update_from_camera_attributes({attr: value})
1469
1469
  except Exception:
1470
- _LOGGER.debug("camera %s: swallowed exception", 'async_set_device_attribute', exc_info=True)
1470
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), 'async_set_device_attribute', exc_info=True)
1471
1471
  return ok
1472
1472
 
1473
1473
  async def async_trigger_device_action(
@@ -1542,7 +1542,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
1542
1542
  try:
1543
1543
  await self.async_wake_camera()
1544
1544
  except Exception:
1545
- _LOGGER.debug("camera %s: swallowed exception", 'async_get_camera_attributes', exc_info=True)
1545
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), 'async_get_camera_attributes', exc_info=True)
1546
1546
 
1547
1547
  smarthome_auth = await self._async_get_smarthome_auth()
1548
1548
  mqtt_user = (smarthome_auth or {}).get("mqttUser") or str(self.user_id)
@@ -1929,7 +1929,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
1929
1929
  try:
1930
1930
  _os.unlink(_tmp_ts)
1931
1931
  except Exception:
1932
- _LOGGER.debug("camera %s: swallowed exception", 'async_snapshot', exc_info=True)
1932
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), 'async_snapshot', exc_info=True)
1933
1933
 
1934
1934
  # ── DTLS path: on_frame callback delivers frames from aiortc ─────── #
1935
1935
  frame_event = _asyncio.Event()
@@ -2002,21 +2002,21 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2002
2002
  try:
2003
2003
  await g_task
2004
2004
  except (asyncio.CancelledError, Exception):
2005
- _LOGGER.debug("camera %s: swallowed exception", 'async_stop_streaming', exc_info=True)
2005
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), 'async_stop_streaming', exc_info=True)
2006
2006
  await self._deregister_go2rtc()
2007
2007
  session, self._stream_session = self._stream_session, None
2008
2008
  if session is not None:
2009
2009
  try:
2010
2010
  await session.stop()
2011
2011
  except Exception:
2012
- _LOGGER.debug("camera %s: swallowed exception", 'async_stop_streaming', exc_info=True)
2012
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), 'async_stop_streaming', exc_info=True)
2013
2013
  task, self._stream_task = self._stream_task, None
2014
2014
  if task is not None and not task.done():
2015
2015
  task.cancel()
2016
2016
  try:
2017
2017
  await task
2018
2018
  except (asyncio.CancelledError, Exception):
2019
- _LOGGER.debug("camera %s: swallowed exception", 'async_stop_streaming', exc_info=True)
2019
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), 'async_stop_streaming', exc_info=True)
2020
2020
  # Reap a persistent-MQTT stream drain that no session stopped (e.g. an
2021
2021
  # open cancelled mid-handshake) so its handler is removed from the shared
2022
2022
  # connection and its blocked executor thread is released.
@@ -2041,13 +2041,13 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2041
2041
  try:
2042
2042
  outq.put_nowait(None) # release the executor thread in outgoing_q.get
2043
2043
  except Exception:
2044
- _LOGGER.debug("camera %s: swallowed exception", '_reap_stream_drain', exc_info=True)
2044
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_reap_stream_drain', exc_info=True)
2045
2045
  if not drain.done():
2046
2046
  drain.cancel()
2047
2047
  try:
2048
2048
  await drain
2049
2049
  except (asyncio.CancelledError, Exception):
2050
- _LOGGER.debug("camera %s: swallowed exception", '_reap_stream_drain', exc_info=True)
2050
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_reap_stream_drain', exc_info=True)
2051
2051
 
2052
2052
  async def async_start_motion_polling(
2053
2053
  self, callback: Callable, interval: float = 30.0, lookback_s: int = 600,
@@ -2087,7 +2087,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2087
2087
  try:
2088
2088
  await task
2089
2089
  except (asyncio.CancelledError, Exception):
2090
- _LOGGER.debug("camera %s: swallowed exception", 'async_stop_motion_polling', exc_info=True)
2090
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), 'async_stop_motion_polling', exc_info=True)
2091
2091
 
2092
2092
  async def _motion_poll_loop(self, lookback_s: int) -> None:
2093
2093
  """Background: poll the cloud event list; fire callback on newly-recorded events."""
@@ -2241,7 +2241,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2241
2241
  try:
2242
2242
  await self.async_wait_serve_ready(timeout=40.0)
2243
2243
  except Exception:
2244
- _LOGGER.debug("camera %s: swallowed exception", '_register_with_go2rtc', exc_info=True)
2244
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_register_with_go2rtc', exc_info=True)
2245
2245
  if not (self._streaming_active and self._go2rtc_url and self._keepalive_rtsp_url):
2246
2246
  return
2247
2247
  name = f"aidot_{self.device_id[:12]}"
@@ -2254,7 +2254,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2254
2254
  _LOGGER.info(
2255
2255
  "camera %s: preferring go2rtc stream -> %s", self.device_id, url)
2256
2256
  except Exception:
2257
- _LOGGER.debug("camera %s: swallowed exception", '_register_with_go2rtc', exc_info=True)
2257
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_register_with_go2rtc', exc_info=True)
2258
2258
 
2259
2259
  async def _deregister_go2rtc(self) -> None:
2260
2260
  """Remove this camera's stream from go2rtc (best-effort)."""
@@ -2269,7 +2269,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2269
2269
  async with aiohttp.ClientSession() as _s2:
2270
2270
  await Go2rtcClient(_s2, base).remove_stream(name)
2271
2271
  except Exception:
2272
- _LOGGER.debug("camera %s: swallowed exception", '_deregister_go2rtc', exc_info=True)
2272
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_deregister_go2rtc', exc_info=True)
2273
2273
 
2274
2274
  async def async_wait_serve_ready(self, timeout: float = 20.0) -> bool:
2275
2275
  """Wait until the DTLS serve is bound + serving (or ``timeout``).
@@ -2520,7 +2520,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2520
2520
  try:
2521
2521
  await session.stop()
2522
2522
  except Exception:
2523
- _LOGGER.debug("camera %s: swallowed exception", '_sdes_keepalive_loop', exc_info=True)
2523
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_sdes_keepalive_loop', exc_info=True)
2524
2524
  self._streaming_active = False
2525
2525
  self._keepalive_rtsp_url = None
2526
2526
  self._serve_ready.clear()
@@ -2540,7 +2540,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2540
2540
  try:
2541
2541
  await session.stop()
2542
2542
  except Exception:
2543
- _LOGGER.debug("camera %s: swallowed exception", '_sdes_keepalive_loop', exc_info=True)
2543
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_sdes_keepalive_loop', exc_info=True)
2544
2544
 
2545
2545
  # Adaptive bookkeeping: a fast attempt that never delivered media
2546
2546
  # latches the loop onto the full relay path for its remaining opens.
@@ -2651,7 +2651,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2651
2651
  try:
2652
2652
  await session.stop()
2653
2653
  except Exception:
2654
- _LOGGER.debug("camera %s: swallowed exception", '_on_frame', exc_info=True)
2654
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_on_frame', exc_info=True)
2655
2655
 
2656
2656
  if self._streaming_active:
2657
2657
  # Reset backoff if this session produced frames (a normal drop
@@ -2707,7 +2707,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
2707
2707
  except Exception:
2708
2708
  pass # full -> drop (PLI re-arms a GOP)
2709
2709
  except Exception:
2710
- _LOGGER.debug("camera %s: swallowed exception", '_tap_put', exc_info=True)
2710
+ _LOGGER.debug("swallowed exception in %s", '_tap_put', exc_info=True)
2711
2711
  return _orig_put(task, *a, **k)
2712
2712
 
2713
2713
  _qd.put = _tap_put
@@ -3229,7 +3229,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
3229
3229
  try:
3230
3230
  wfile.close()
3231
3231
  except Exception:
3232
- _LOGGER.debug("camera %s: swallowed exception", '_pc_dead', exc_info=True)
3232
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_pc_dead', exc_info=True)
3233
3233
  wfile = None
3234
3234
  mux_thread.join(timeout=2.0)
3235
3235
  mux_thread = stop_flag = None
@@ -3248,7 +3248,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
3248
3248
  try:
3249
3249
  wfile.close()
3250
3250
  except Exception:
3251
- _LOGGER.debug("camera %s: swallowed exception", '_pc_dead', exc_info=True)
3251
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_pc_dead', exc_info=True)
3252
3252
  if mux_thread is not None:
3253
3253
  mux_thread.join(timeout=2.0)
3254
3254
  _terminate_proc(proc) # never orphan ffmpeg on teardown
@@ -3256,7 +3256,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
3256
3256
  try:
3257
3257
  await session.stop()
3258
3258
  except Exception:
3259
- _LOGGER.debug("camera %s: swallowed exception", '_pc_dead', exc_info=True)
3259
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_pc_dead', exc_info=True)
3260
3260
 
3261
3261
  if cancelled:
3262
3262
  return
@@ -102,7 +102,7 @@ class CameraStatusData(DeviceStatusData):
102
102
  try:
103
103
  self.battery_remaining = int(v)
104
104
  except (ValueError, TypeError):
105
- _LOGGER.debug("camera %s: swallowed exception", 'update', exc_info=True)
105
+ _LOGGER.debug("swallowed exception in %s", 'update', exc_info=True)
106
106
  if (v := attr.get("Occupancy")) is not None:
107
107
  self.occupancy = bool(int(v))
108
108
  if (v := attr.get("SDcardStatus")) is not None:
@@ -111,7 +111,7 @@ class CameraStatusData(DeviceStatusData):
111
111
  try:
112
112
  self.wifi_rssi = int(v)
113
113
  except (ValueError, TypeError):
114
- _LOGGER.debug("camera %s: swallowed exception", 'update', exc_info=True)
114
+ _LOGGER.debug("swallowed exception in %s", 'update', exc_info=True)
115
115
 
116
116
  # Cloud "properties" keys that belong to lights, not cameras. A camera's
117
117
  # image "Dimming" must not be read as a light brightness (and could TypeError
@@ -163,7 +163,7 @@ class CameraDeviceInformation(DeviceInformation):
163
163
  if isinstance(codes, list):
164
164
  self.ptz_directions = [int(c) for c in codes]
165
165
  except Exception:
166
- _LOGGER.debug("camera %s: swallowed exception", '__init__', exc_info=True)
166
+ _LOGGER.debug("swallowed exception in %s", '__init__', exc_info=True)
167
167
 
168
168
 
169
169
  @dataclass
@@ -291,7 +291,7 @@ class CloudPlaybackSession:
291
291
  self._writer.close()
292
292
  await self._writer.wait_closed()
293
293
  except Exception:
294
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
294
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
295
295
  self._writer = None
296
296
  self._reader = None
297
297
 
@@ -480,6 +480,6 @@ class LiveStreamSession:
480
480
  self._writer.close()
481
481
  await self._writer.wait_closed()
482
482
  except Exception:
483
- _LOGGER.debug("camera %s: swallowed exception", '_cleanup', exc_info=True)
483
+ _LOGGER.debug("swallowed exception in %s", '_cleanup', exc_info=True)
484
484
  self._writer = None
485
485
  self._reader = None
@@ -302,13 +302,13 @@ def _compress_sdp_for_camera(sdp: str) -> str:
302
302
  try:
303
303
  seen["H264/90000_pt"] = ln.split(":")[1].split(" ")[0]
304
304
  except Exception:
305
- _LOGGER.debug("camera %s: swallowed exception", 'keep', exc_info=True)
305
+ _LOGGER.debug("swallowed exception in %s", 'keep', exc_info=True)
306
306
  elif "H265/90000" in ln and seen.get("H265/90000") is None:
307
307
  keep(ln, "H265/90000")
308
308
  try:
309
309
  seen["H265/90000_pt"] = ln.split(":")[1].split(" ")[0]
310
310
  except Exception:
311
- _LOGGER.debug("camera %s: swallowed exception", 'keep', exc_info=True)
311
+ _LOGGER.debug("swallowed exception in %s", 'keep', exc_info=True)
312
312
  elif "apt=" in ln:
313
313
  try:
314
314
  apt = ln.split("apt=")[1].strip()
@@ -499,7 +499,7 @@ def _terminate_proc(proc) -> None:
499
499
  if proc.returncode is None:
500
500
  proc.terminate()
501
501
  except Exception:
502
- _LOGGER.debug("camera %s: swallowed exception", '_terminate_proc', exc_info=True)
502
+ _LOGGER.debug("swallowed exception in %s", '_terminate_proc', exc_info=True)
503
503
 
504
504
 
505
505
  _XDG_CONFIG_HOME = os.environ.get("XDG_CONFIG_HOME") or os.path.join(
@@ -918,7 +918,7 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
918
918
  out.mux(pkt)
919
919
  progress[0] = _t.monotonic()
920
920
  except Exception:
921
- _LOGGER.debug("camera %s: swallowed exception", '_flush_video', exc_info=True)
921
+ _LOGGER.debug("swallowed exception in %s", '_flush_video', exc_info=True)
922
922
 
923
923
  def _flush_audio(drain=False):
924
924
  if not have_audio:
@@ -961,11 +961,11 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
961
961
  _g.sample_rate = 8000
962
962
  fr = _g
963
963
  except Exception:
964
- _LOGGER.debug("camera %s: swallowed exception", '_flush_audio', exc_info=True)
964
+ _LOGGER.debug("swallowed exception in %s", '_flush_audio', exc_info=True)
965
965
  for rfr in resampler.resample(fr): # 8k PCMA -> 48k fltp
966
966
  fifo.write(rfr)
967
967
  except Exception:
968
- _LOGGER.debug("camera %s: swallowed exception", '_flush_audio', exc_info=True)
968
+ _LOGGER.debug("swallowed exception in %s", '_flush_audio', exc_info=True)
969
969
  while True:
970
970
  fr = fifo.read(1024) # AAC wants 1024-sample frames
971
971
  if fr is None:
@@ -1009,7 +1009,7 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
1009
1009
  out.mux(opkt)
1010
1010
  progress[0] = _t.monotonic()
1011
1011
  except Exception:
1012
- _LOGGER.debug("camera %s: swallowed exception", '_flush_audio', exc_info=True)
1012
+ _LOGGER.debug("swallowed exception in %s", '_flush_audio', exc_info=True)
1013
1013
 
1014
1014
  while not stop_flag.is_set():
1015
1015
  _flush_video()
@@ -1022,11 +1022,11 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
1022
1022
  for opkt in aenc.encode(None): # flush
1023
1023
  out.mux(opkt)
1024
1024
  except Exception:
1025
- _LOGGER.debug("camera %s: swallowed exception", '_dtls_av_mux_run', exc_info=True)
1025
+ _LOGGER.debug("swallowed exception in %s", '_dtls_av_mux_run', exc_info=True)
1026
1026
  try:
1027
1027
  out.close()
1028
1028
  except Exception:
1029
- _LOGGER.debug("camera %s: swallowed exception", '_dtls_av_mux_run', exc_info=True)
1029
+ _LOGGER.debug("swallowed exception in %s", '_dtls_av_mux_run', exc_info=True)
1030
1030
 
1031
1031
 
1032
1032
  def _h264_has_keyframe(data: bytes) -> bool:
@@ -1170,7 +1170,7 @@ def _mqtt_session_sync(
1170
1170
  try:
1171
1171
  client.disconnect()
1172
1172
  except Exception:
1173
- _LOGGER.debug("camera %s: swallowed exception", '_on_log', exc_info=True)
1173
+ _LOGGER.debug("swallowed exception in %s", '_on_log', exc_info=True)
1174
1174
  return [], status
1175
1175
 
1176
1176
  if not status["connected"]:
@@ -1182,7 +1182,7 @@ def _mqtt_session_sync(
1182
1182
  try:
1183
1183
  client.disconnect()
1184
1184
  except Exception:
1185
- _LOGGER.debug("camera %s: swallowed exception", '_on_log', exc_info=True)
1185
+ _LOGGER.debug("swallowed exception in %s", '_on_log', exc_info=True)
1186
1186
  return [], status
1187
1187
 
1188
1188
  _LOGGER.info("_mqtt_session: connected to %s:%d clientId=%s", hostname, port, client_id)
@@ -1199,7 +1199,7 @@ def _mqtt_session_sync(
1199
1199
  try:
1200
1200
  on_ready(status)
1201
1201
  except Exception:
1202
- _LOGGER.debug("camera %s: swallowed exception", '_on_log', exc_info=True)
1202
+ _LOGGER.debug("swallowed exception in %s", '_on_log', exc_info=True)
1203
1203
 
1204
1204
  collected = []
1205
1205
  deadline = _time.monotonic() + duration
@@ -1222,7 +1222,7 @@ def _mqtt_session_sync(
1222
1222
  try:
1223
1223
  client.disconnect()
1224
1224
  except Exception:
1225
- _LOGGER.debug("camera %s: swallowed exception", '_on_log', exc_info=True)
1225
+ _LOGGER.debug("swallowed exception in %s", '_on_log', exc_info=True)
1226
1226
  return collected, status
1227
1227
  pub_topic, pub_payload = out
1228
1228
  client.publish(pub_topic, pub_payload)
@@ -1235,13 +1235,13 @@ def _mqtt_session_sync(
1235
1235
  try:
1236
1236
  on_message(*item)
1237
1237
  except Exception:
1238
- _LOGGER.debug("camera %s: swallowed exception", '_on_log', exc_info=True)
1238
+ _LOGGER.debug("swallowed exception in %s", '_on_log', exc_info=True)
1239
1239
 
1240
1240
  client.loop_stop()
1241
1241
  try:
1242
1242
  client.disconnect()
1243
1243
  except Exception:
1244
- _LOGGER.debug("camera %s: swallowed exception", '_mqtt_session_sync', exc_info=True)
1244
+ _LOGGER.debug("swallowed exception in %s", '_mqtt_session_sync', exc_info=True)
1245
1245
  return collected, status
1246
1246
 
1247
1247
 
@@ -1566,7 +1566,7 @@ async def _mqtt_get_playback_server_info(
1566
1566
  pl["serverIP"] = pl.get("serverIP") or pl.get("serverIp")
1567
1567
  result_holder.append(pl)
1568
1568
  except Exception:
1569
- _LOGGER.debug("camera %s: swallowed exception", '_check', exc_info=True)
1569
+ _LOGGER.debug("swallowed exception in %s", '_check', exc_info=True)
1570
1570
 
1571
1571
  await _mqtt_session(
1572
1572
  mqtt_url, mqtt_user, mqtt_pwd, client_id,
@@ -192,7 +192,7 @@ class SdesSession:
192
192
  try:
193
193
  await _stop_loop.run_in_executor(None, lambda: self._proc.wait(5))
194
194
  except Exception:
195
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
195
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
196
196
  # Read drained stderr in the executor with a hard timeout: proc.stderr.read()
197
197
  # blocks until EOF, which never arrives if the killed process is still a
198
198
  # zombie / stuck in uninterruptible I/O - doing it inline would hang the
@@ -205,32 +205,32 @@ class SdesSession:
205
205
  timeout=2.0,
206
206
  )
207
207
  except Exception: # incl. asyncio.TimeoutError - never let teardown hang here
208
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
208
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
209
209
  # On timeout the executor thread is still blocked in stderr.read() on
210
210
  # a wedged ffmpeg; close the pipe so that read returns instead of
211
211
  # pinning a default-pool thread for the life of the process.
212
212
  try:
213
213
  self._proc.stderr.close()
214
214
  except Exception:
215
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
215
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
216
216
  if stderr_bytes:
217
217
  _LOGGER.warning("ffmpeg SDES stderr:\n%s", stderr_bytes.decode(errors="replace"))
218
218
  import os
219
219
  try:
220
220
  os.unlink(self._sdp_path)
221
221
  except Exception:
222
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
222
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
223
223
  for _sock in (self._audio_sock, self._video_sock):
224
224
  if _sock is not None:
225
225
  try:
226
226
  _sock.close()
227
227
  except Exception:
228
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
228
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
229
229
  self._outgoing_q.put_nowait(None)
230
230
  try:
231
231
  await asyncio.wait_for(self._mqtt_fut, timeout=5.0)
232
232
  except Exception:
233
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
233
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
234
234
 
235
235
 
236
236
  def _run_sdes_talk_pump(state: dict) -> None:
@@ -297,7 +297,7 @@ def _run_sdes_talk_pump(state: dict) -> None:
297
297
  try:
298
298
  _sock.sendto(_tx.protect(_hdr + _alaw), _src)
299
299
  except Exception:
300
- _LOGGER.debug("camera %s: swallowed exception", '_run_sdes_talk_pump', exc_info=True)
300
+ _LOGGER.debug("swallowed exception in %s", '_run_sdes_talk_pump', exc_info=True)
301
301
  _seq = (_seq + 1) & 0xFFFF
302
302
  _ts = (_ts + len(_alaw)) & 0xFFFFFFFF
303
303
  # Active talk: hold 20 ms pacing for the audio cadence.
@@ -151,7 +151,7 @@ class _SdesOpenMixin:
151
151
  if len(_p) == 4 and all(x.isdigit() and 0 <= int(x) <= 255 for x in _p):
152
152
  _public_ip = _cand_pub
153
153
  except Exception:
154
- _LOGGER.debug("camera %s: swallowed exception", '_open_sdes_stream', exc_info=True)
154
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_open_sdes_stream', exc_info=True)
155
155
 
156
156
  # Build TURN server list for _sdes_ice_server_list from ice_config if
157
157
  # available. The camera's ICE agent uses these to gather its own relay
@@ -192,7 +192,7 @@ class _SdesOpenMixin:
192
192
  "Password": _entry.get("credential") or "",
193
193
  })
194
194
  except Exception:
195
- _LOGGER.debug("camera %s: swallowed exception", '_open_sdes_stream', exc_info=True)
195
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_open_sdes_stream', exc_info=True)
196
196
 
197
197
  # --- TURN relay allocation helper ------------------------------------ #
198
198
  # Defined BEFORE the offer so relay IP/port can be embedded in the
@@ -781,13 +781,13 @@ class _SdesOpenMixin:
781
781
  try:
782
782
  _seen["H264/90000_pt"] = _ln.split(":")[1].split(" ")[0]
783
783
  except Exception:
784
- _LOGGER.debug("camera %s: swallowed exception", '_k', exc_info=True)
784
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_k', exc_info=True)
785
785
  elif "H265/90000" in _ln and _seen.get("H265/90000") is None:
786
786
  _k(_ln, "H265/90000")
787
787
  try:
788
788
  _seen["H265/90000_pt"] = _ln.split(":")[1].split(" ")[0]
789
789
  except Exception:
790
- _LOGGER.debug("camera %s: swallowed exception", '_k', exc_info=True)
790
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_k', exc_info=True)
791
791
  elif "apt=" in _ln:
792
792
  try:
793
793
  _apt = _ln.split("apt=")[1].strip()
@@ -878,7 +878,7 @@ class _SdesOpenMixin:
878
878
  "Password": str(_e.get("Password") or ""),
879
879
  })
880
880
  except Exception:
881
- _LOGGER.debug("camera %s: swallowed exception", '_k', exc_info=True)
881
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_k', exc_info=True)
882
882
  # Allocate TURN relay if not already done before offer build.
883
883
  # When ice_config provided TURN entries, pre-allocation already ran
884
884
  # and _relay_addrs is populated - skip to avoid double-allocation.
@@ -1193,13 +1193,13 @@ class _SdesOpenMixin:
1193
1193
  _hp_host = _m_hp.group(1)
1194
1194
  _hp_port = int(_m_hp.group(2) or 3478)
1195
1195
  except Exception:
1196
- _LOGGER.debug("camera %s: swallowed exception", '_open_sdes_stream', exc_info=True)
1196
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_open_sdes_stream', exc_info=True)
1197
1197
  _hp_stun = b'\x00\x01\x00\x00\x21\x12\xa4\x42' + os.urandom(12)
1198
1198
  for _hp_sock in (_audio_sock, _video_sock):
1199
1199
  try:
1200
1200
  _hp_sock.sendto(_hp_stun, (_hp_host, _hp_port))
1201
1201
  except Exception:
1202
- _LOGGER.debug("camera %s: swallowed exception", '_send_sdes_ice_cand', exc_info=True)
1202
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_send_sdes_ice_cand', exc_info=True)
1203
1203
  # Punch to TURN allocation port (5349) as well so port-restricted NAT
1204
1204
  # allows traffic from either TURN port (3478 STUN or 5349 allocation).
1205
1205
  _hp_port2 = 5349
@@ -1208,7 +1208,7 @@ class _SdesOpenMixin:
1208
1208
  try:
1209
1209
  _hp_sock.sendto(_hp_stun, (_hp_host, _hp_port2))
1210
1210
  except Exception:
1211
- _LOGGER.debug("camera %s: swallowed exception", '_send_sdes_ice_cand', exc_info=True)
1211
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_send_sdes_ice_cand', exc_info=True)
1212
1212
  _status(
1213
1213
  f"NAT hole-punch: sent from audio={audio_port}"
1214
1214
  f" video={video_port} → {_hp_host}:{_hp_port}"
@@ -1260,7 +1260,7 @@ class _SdesOpenMixin:
1260
1260
  try:
1261
1261
  _rsock.setblocking(False)
1262
1262
  except Exception:
1263
- _LOGGER.debug("camera %s: swallowed exception", '_is_self_peer_ip', exc_info=True)
1263
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_is_self_peer_ip', exc_info=True)
1264
1264
  while time.monotonic() < _stun_deadline:
1265
1265
  # Idle-exit: threshold depends on whether we've seen any STUN yet
1266
1266
  idle = time.monotonic() - _last_pkt_t
@@ -1398,7 +1398,7 @@ class _SdesOpenMixin:
1398
1398
  _sock.sendto(_resp, _src)
1399
1399
  _stun_count += 1
1400
1400
  except Exception:
1401
- _LOGGER.debug("camera %s: swallowed exception", '_is_self_peer_ip', exc_info=True)
1401
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_is_self_peer_ip', exc_info=True)
1402
1402
  else:
1403
1403
  # Non-STUN packet = SRTP arriving - ICE is done, hand off to ffmpeg now
1404
1404
  _srtp_detected = True
@@ -1409,7 +1409,7 @@ class _SdesOpenMixin:
1409
1409
  try:
1410
1410
  _rsock.setblocking(True)
1411
1411
  except Exception:
1412
- _LOGGER.debug("camera %s: swallowed exception", '_is_self_peer_ip', exc_info=True)
1412
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_is_self_peer_ip', exc_info=True)
1413
1413
  if _stun_count:
1414
1414
  _status(f"ICE: responded to {_stun_count} STUN binding request(s)")
1415
1415
  elif not _srtp_detected:
@@ -1479,7 +1479,7 @@ class _SdesOpenMixin:
1479
1479
  try:
1480
1480
  _hp_sock_r.sendto(_hp_stun, (_hp_host, _hp_port))
1481
1481
  except Exception:
1482
- _LOGGER.debug("camera %s: swallowed exception", '_is_self_peer_ip', exc_info=True)
1482
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_is_self_peer_ip', exc_info=True)
1483
1483
  # Retry STUN window (8 s)
1484
1484
  _stun_deadline = time.monotonic() + 8.0
1485
1485
  _last_pkt_t = time.monotonic()
@@ -1518,7 +1518,7 @@ class _SdesOpenMixin:
1518
1518
  _sk_r.sendto(_resp_r, _src_r)
1519
1519
  _stun_count += 1
1520
1520
  except Exception:
1521
- _LOGGER.debug("camera %s: swallowed exception", '_is_self_peer_ip', exc_info=True)
1521
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_is_self_peer_ip', exc_info=True)
1522
1522
  else:
1523
1523
  _srtp_detected = True
1524
1524
  break
@@ -1554,7 +1554,7 @@ class _SdesOpenMixin:
1554
1554
  _pre_ans = answer_fut.result()
1555
1555
  _pre_launch_answer_sdp = (_pre_ans or {}).get("sdp", "")
1556
1556
  except Exception:
1557
- _LOGGER.debug("camera %s: swallowed exception", '_is_self_peer_ip', exc_info=True)
1557
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_is_self_peer_ip', exc_info=True)
1558
1558
  if _pre_launch_answer_sdp:
1559
1559
  _LOGGER.debug(
1560
1560
  "_open_sdes_stream: camera webrtcResp answer SDP (len=%d)",
@@ -1873,7 +1873,7 @@ class _SdesOpenMixin:
1873
1873
  try:
1874
1874
  sock.sendto(_req, cam_addr)
1875
1875
  except Exception:
1876
- _LOGGER.debug("camera %s: swallowed exception", '_send_use_candidate', exc_info=True)
1876
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_send_use_candidate', exc_info=True)
1877
1877
 
1878
1878
  if _cam_ice_ufrag and _cam_ice_pwd and _cam_ice_cands:
1879
1879
  for _c_ip, _c_port in _cam_ice_cands:
@@ -2100,14 +2100,14 @@ class _SdesOpenMixin:
2100
2100
  _talk_state["speaker_on"] = True
2101
2101
  _status("SDES talk: sent SPEAKERSTART(848) (bridge thread)")
2102
2102
  except Exception:
2103
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2103
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2104
2104
  elif _talk_state.get("speaker_on") and not _talk_state.get("want_speaker"):
2105
2105
  try:
2106
2106
  _cmd_chan[0](849, b'\x00' * 8) # SPEAKERSTOP
2107
2107
  _talk_state["speaker_on"] = False
2108
2108
  _status("SDES talk: sent SPEAKERSTOP(849) (bridge thread)")
2109
2109
  except Exception:
2110
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2110
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2111
2111
 
2112
2112
  # RTCP PLI (Picture Loss Indication) - forces camera to
2113
2113
  # resend IDR + VPS/SPS/PPS so ffmpeg gets codec params.
@@ -2177,13 +2177,13 @@ class _SdesOpenMixin:
2177
2177
  )
2178
2178
  _pli_sent = True
2179
2179
  except Exception:
2180
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2180
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2181
2181
  if not _pli_sent:
2182
2182
  try:
2183
2183
  _bridge_fn._cam_srtp_sock.sendto(
2184
2184
  _pli_raw, _bridge_fn._cam_srtp_src)
2185
2185
  except Exception:
2186
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2186
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2187
2187
  _bridge_fn._last_pli_ts = _time_br.time()
2188
2188
  _pli_n = getattr(_bridge_fn, '_pli_count', 0) + 1
2189
2189
  _bridge_fn._pli_count = _pli_n
@@ -2245,7 +2245,7 @@ class _SdesOpenMixin:
2245
2245
  _csrc,
2246
2246
  )
2247
2247
  except Exception:
2248
- _LOGGER.debug("camera %s: swallowed exception", '_persistent_sdes_cmd', exc_info=True)
2248
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_persistent_sdes_cmd', exc_info=True)
2249
2249
 
2250
2250
  _cmd_chan[0] = _persistent_sdes_cmd
2251
2251
  except Exception as _dw_e:
@@ -2275,7 +2275,7 @@ class _SdesOpenMixin:
2275
2275
  _re_key, _AES_re2.MODE_CBC, _re_iv
2276
2276
  ).encrypt(_pad_re2(_re_plain, 16))
2277
2277
  except Exception:
2278
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2278
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2279
2279
  for _rp in [_re_enc, _re_plain]:
2280
2280
  if _rp is None:
2281
2281
  continue
@@ -2286,7 +2286,7 @@ class _SdesOpenMixin:
2286
2286
  _trigger_bsrc,
2287
2287
  )
2288
2288
  except Exception:
2289
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2289
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2290
2290
  _last_trigger_ts = _time_br.time()
2291
2291
 
2292
2292
  for _bs in _rl:
@@ -2439,14 +2439,14 @@ class _SdesOpenMixin:
2439
2439
  (_br_ci, _br_cp),
2440
2440
  )
2441
2441
  except Exception:
2442
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2442
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2443
2443
  _status(
2444
2444
  f"bridge: late USE-CANDIDATE sent (audio+video) to"
2445
2445
  f" {len(_bridge_uc_info['cands'])} camera candidate(s)"
2446
2446
  " (answer arrived after bridge started)"
2447
2447
  )
2448
2448
  except Exception:
2449
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2449
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2450
2450
  elif len(_bpkt) >= 20 and _bpkt[4:8] == _STUN_MAGIC_BR:
2451
2451
  # STUN BindingSuccess (0x0101) from camera: ICE complete.
2452
2452
  # Send AES-128-CBC encrypted SESSION_MODE_REQ (AVIO LIVING).
@@ -2503,7 +2503,7 @@ class _SdesOpenMixin:
2503
2503
  f" → {_bsrc[0]}:{_bsrc[1]}"
2504
2504
  )
2505
2505
  except Exception:
2506
- _LOGGER.debug("camera %s: swallowed exception", '_persistent_sdes_cmd', exc_info=True)
2506
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_persistent_sdes_cmd', exc_info=True)
2507
2507
  _avio_living_sent = True
2508
2508
  _last_trigger_ts = _time_br.time()
2509
2509
  _trigger_bs = _bs
@@ -2880,12 +2880,12 @@ class _SdesOpenMixin:
2880
2880
  _bs.sendto(_rr_sess.protect_rtcp(_rr_pkt), _bsrc)
2881
2881
  _rtcp_sent = True
2882
2882
  except Exception:
2883
- _LOGGER.debug("camera %s: swallowed exception", '_persistent_sdes_cmd', exc_info=True)
2883
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_persistent_sdes_cmd', exc_info=True)
2884
2884
  if not _rtcp_sent:
2885
2885
  try:
2886
2886
  _bs.sendto(_rr_pkt, _bsrc)
2887
2887
  except Exception:
2888
- _LOGGER.debug("camera %s: swallowed exception", '_enc_c8_sctp', exc_info=True)
2888
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_enc_c8_sctp', exc_info=True)
2889
2889
  if _bridge_fn._tutk_count == 1:
2890
2890
  _status(
2891
2891
  f"SDES: sent RTCP RR to camera"
@@ -2917,7 +2917,7 @@ class _SdesOpenMixin:
2917
2917
  f" ({len(_pd_plain)}B PCMA)"
2918
2918
  )
2919
2919
  except Exception:
2920
- _LOGGER.debug("camera %s: swallowed exception", '_enc_c8_sctp', exc_info=True)
2920
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_enc_c8_sctp', exc_info=True)
2921
2921
  continue
2922
2922
 
2923
2923
  # Standard SRTP/SRTCP demux by RTP payload type.
@@ -2955,11 +2955,11 @@ class _SdesOpenMixin:
2955
2955
  try:
2956
2956
  _lo_a.sendto(_bpkt, ('127.0.0.1', _lo_audio_port))
2957
2957
  except Exception:
2958
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2958
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2959
2959
  try:
2960
2960
  _lo_v.sendto(_bpkt, ('127.0.0.1', _lo_video_port))
2961
2961
  except Exception:
2962
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
2962
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
2963
2963
  continue
2964
2964
  _pt = _pt_byte & 0x7F
2965
2965
  if _pt in (96, 97, 98):
@@ -3138,7 +3138,7 @@ class _SdesOpenMixin:
3138
3138
  )
3139
3139
  _media_progress[0] = _time_br.monotonic()
3140
3140
  except Exception:
3141
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3141
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3142
3142
  # Periodic ICE controlling check: re-send USE-CANDIDATE every 2.5 s.
3143
3143
  # Keeps the camera in ICE "Completed" state and satisfies consent
3144
3144
  # refresh (RFC 7675). Also handles the case where the initial
@@ -3159,11 +3159,11 @@ class _SdesOpenMixin:
3159
3159
  try:
3160
3160
  _lo_a.close()
3161
3161
  except Exception:
3162
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3162
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3163
3163
  try:
3164
3164
  _lo_v.close()
3165
3165
  except Exception:
3166
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3166
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3167
3167
 
3168
3168
  _br_first_di_logged = False
3169
3169
  _br_first_srtp_logged = False
@@ -3222,11 +3222,11 @@ class _SdesOpenMixin:
3222
3222
  try:
3223
3223
  _rsock.close()
3224
3224
  except Exception:
3225
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3225
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3226
3226
  try:
3227
3227
  os.unlink(sdp_path)
3228
3228
  except Exception:
3229
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3229
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3230
3230
  outgoing_q.put_nowait(None) # stop MQTT thread
3231
3231
  raise CameraMixin._SdesNoAnswerError()
3232
3232
  elif (_cam_echo_received
@@ -3309,7 +3309,7 @@ class _SdesOpenMixin:
3309
3309
  f"SDES: narrowed ffmpeg SDP to video pt={_vpt}"
3310
3310
  f" ({'H265' if int(_vpt) == 97 else 'H264'})")
3311
3311
  except Exception:
3312
- _LOGGER.debug("camera %s: swallowed exception", '_open_sdes_stream', exc_info=True)
3312
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_open_sdes_stream', exc_info=True)
3313
3313
  _LOGGER.info("SDES ffmpeg cmd: %s", " ".join(cmd))
3314
3314
  if _ffmpeg_path() is None:
3315
3315
  # ffmpeg is not installed - clean up and surface a clear error
@@ -3318,11 +3318,11 @@ class _SdesOpenMixin:
3318
3318
  try:
3319
3319
  _rsock.close()
3320
3320
  except Exception:
3321
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3321
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3322
3322
  try:
3323
3323
  os.unlink(sdp_path)
3324
3324
  except Exception:
3325
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3325
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3326
3326
  outgoing_q.put_nowait(None) # stop MQTT thread
3327
3327
  raise RuntimeError(
3328
3328
  "ffmpeg not found - install ffmpeg to stream SDES-SRTP cameras.\n"
@@ -3343,11 +3343,11 @@ class _SdesOpenMixin:
3343
3343
  try:
3344
3344
  _rsock.close()
3345
3345
  except Exception:
3346
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3346
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3347
3347
  try:
3348
3348
  os.unlink(sdp_path)
3349
3349
  except Exception:
3350
- _LOGGER.debug("camera %s: swallowed exception", '_bridge_fn', exc_info=True)
3350
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_bridge_fn', exc_info=True)
3351
3351
  outgoing_q.put_nowait(None) # stop MQTT thread
3352
3352
  raise RuntimeError(
3353
3353
  "ffmpeg not found - install ffmpeg to stream SDES-SRTP cameras.\n"
@@ -3591,11 +3591,11 @@ class _SdesOpenMixin:
3591
3591
  try:
3592
3592
  _rsock.close()
3593
3593
  except Exception:
3594
- _LOGGER.debug("camera %s: swallowed exception", '_udp_port_bound', exc_info=True)
3594
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_udp_port_bound', exc_info=True)
3595
3595
  try:
3596
3596
  os.unlink(sdp_path)
3597
3597
  except Exception:
3598
- _LOGGER.debug("camera %s: swallowed exception", '_udp_port_bound', exc_info=True)
3598
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_udp_port_bound', exc_info=True)
3599
3599
  outgoing_q.put_nowait(None) # signal MQTT thread to exit
3600
3600
  raise CameraMixin._SdesNoAnswerError()
3601
3601
  else:
@@ -3654,7 +3654,7 @@ class _SdesOpenMixin:
3654
3654
  + (" [m=app]" if _dc_answer_has_app else "")
3655
3655
  )
3656
3656
  except Exception:
3657
- _LOGGER.debug("camera %s: swallowed exception", '_late_second_answer_task', exc_info=True)
3657
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_late_second_answer_task', exc_info=True)
3658
3658
  _spawn_bg(_late_second_answer_task())
3659
3659
 
3660
3660
  return SdesSession(
@@ -249,12 +249,12 @@ class TutkStreamSession:
249
249
  try:
250
250
  av.avClientStop(self._av_index)
251
251
  except Exception:
252
- _LOGGER.debug("camera %s: swallowed exception", '_recv_loop', exc_info=True)
252
+ _LOGGER.debug("swallowed exception in %s", '_recv_loop', exc_info=True)
253
253
  if self._sid >= 0:
254
254
  try:
255
255
  iotc.IOTC_Session_Close(self._sid)
256
256
  except Exception:
257
- _LOGGER.debug("camera %s: swallowed exception", '_recv_loop', exc_info=True)
257
+ _LOGGER.debug("swallowed exception in %s", '_recv_loop', exc_info=True)
258
258
  _LOGGER.debug("TUTK: recv loop exited")
259
259
 
260
260
  async def stop(self) -> None:
@@ -106,7 +106,7 @@ class WebRTCSession:
106
106
  if self._audio_sender is not None:
107
107
  self._audio_sender.replaceTrack(None)
108
108
  except Exception:
109
- _LOGGER.debug("camera %s: swallowed exception", 'async_stop_talk', exc_info=True)
109
+ _LOGGER.debug("swallowed exception in %s", 'async_stop_talk', exc_info=True)
110
110
  self._talk_holder["provider"] = None
111
111
  return True
112
112
 
@@ -185,18 +185,18 @@ class WebRTCSession:
185
185
  self._talk_holder["was_active"] = False
186
186
  await asyncio.sleep(0.4) # let the DataChannel deliver SPEAKERSTOP
187
187
  except Exception:
188
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
188
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
189
189
  for task in self._track_tasks:
190
190
  task.cancel()
191
191
  if self._recorder is not None:
192
192
  try:
193
193
  await self._recorder.stop()
194
194
  except Exception:
195
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
195
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
196
196
  # Send None sentinel to stop the MQTT session in its thread
197
197
  self._outgoing_q.put_nowait(None)
198
198
  await self._pc.close()
199
199
  try:
200
200
  await asyncio.wait_for(self._mqtt_fut, timeout=5.0)
201
201
  except Exception:
202
- _LOGGER.debug("camera %s: swallowed exception", 'stop', exc_info=True)
202
+ _LOGGER.debug("swallowed exception in %s", 'stop', exc_info=True)
@@ -193,7 +193,7 @@ class _WebRTCOpenMixin:
193
193
  try:
194
194
  await self.async_wake_camera()
195
195
  except Exception:
196
- _LOGGER.debug("camera %s: swallowed exception", '_async_open_webrtc_stream_impl', exc_info=True)
196
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_async_open_webrtc_stream_impl', exc_info=True)
197
197
 
198
198
  if not use_sdes:
199
199
  try:
@@ -1105,7 +1105,7 @@ class _WebRTCOpenMixin:
1105
1105
  except RuntimeError:
1106
1106
  raise
1107
1107
  except Exception:
1108
- _LOGGER.debug("camera %s: swallowed exception", '_http_keepalive', exc_info=True)
1108
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_http_keepalive', exc_info=True)
1109
1109
 
1110
1110
  # ------------------------------------------------------------------ #
1111
1111
  # Branch: SDES-SRTP cameras use ffmpeg; DTLS cameras use aiortc
@@ -1441,7 +1441,7 @@ class _WebRTCOpenMixin:
1441
1441
  _hdr = struct.pack("<IIqII4x", _seq, 5156, _ts_ms, 0, 0)
1442
1442
  _dc_ref.send(_hdr)
1443
1443
  except Exception:
1444
- _LOGGER.debug("camera %s: swallowed exception", '_send_avio_heartbeat', exc_info=True)
1444
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_send_avio_heartbeat', exc_info=True)
1445
1445
 
1446
1446
  def _send_avio_audiostart(_dc_ref) -> None:
1447
1447
  # IOTYPE_USER_IPCAM_AUDIOSTART = 768 (AVIOCTRLDEFs.java:154).
@@ -1460,7 +1460,7 @@ class _WebRTCOpenMixin:
1460
1460
  _hdr = struct.pack("<IIqII4x", _seq, 768, _ts_ms, len(_payload), 0)
1461
1461
  _dc_ref.send(_hdr + _payload)
1462
1462
  except Exception:
1463
- _LOGGER.debug("camera %s: swallowed exception", '_send_avio_audiostart', exc_info=True)
1463
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_send_avio_audiostart', exc_info=True)
1464
1464
 
1465
1465
  def _send_avio_speaker(_dc_ref, start: bool) -> None:
1466
1466
  # IOTYPE_USER_IPCAM_SPEAKERSTART = 848 / SPEAKERSTOP = 849
@@ -1507,7 +1507,7 @@ class _WebRTCOpenMixin:
1507
1507
  else:
1508
1508
  _status(f"DC[remote:{channel.label}] RX text {message!r}")
1509
1509
  except Exception:
1510
- _LOGGER.debug("camera %s: swallowed exception", '_on_remote_dc_message', exc_info=True)
1510
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_on_remote_dc_message', exc_info=True)
1511
1511
 
1512
1512
  track_tasks: list = []
1513
1513
  _kvs_dc = None
@@ -1617,7 +1617,7 @@ class _WebRTCOpenMixin:
1617
1617
  else:
1618
1618
  _status(f"DC[{_dc_label}] RX text {message!r}")
1619
1619
  except Exception:
1620
- _LOGGER.debug("camera %s: swallowed exception", '_on_kvs_dc_message', exc_info=True)
1620
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_on_kvs_dc_message', exc_info=True)
1621
1621
 
1622
1622
  # Periodic readyState diagnostic - last run had no "DC OPEN"
1623
1623
  # log line, so we want to see whether readyState ever transitions
@@ -1679,7 +1679,7 @@ class _WebRTCOpenMixin:
1679
1679
  while True:
1680
1680
  await track.recv()
1681
1681
  except Exception:
1682
- _LOGGER.debug("camera %s: swallowed exception", '_drain_audio', exc_info=True)
1682
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_drain_audio', exc_info=True)
1683
1683
  t = asyncio.ensure_future(_drain_audio())
1684
1684
  track_tasks.append(t)
1685
1685
  elif track.kind == "video":
@@ -1767,7 +1767,7 @@ class _WebRTCOpenMixin:
1767
1767
  _LOGGER.debug(
1768
1768
  "highport-fix: scoped to this DTLS camera connection")
1769
1769
  except Exception:
1770
- _LOGGER.debug("camera %s: swallowed exception", '_on_track', exc_info=True)
1770
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_on_track', exc_info=True)
1771
1771
 
1772
1772
 
1773
1773
  _sdp = pc.localDescription.sdp
@@ -2723,7 +2723,7 @@ class _WebRTCOpenMixin:
2723
2723
  _NPFp(algorithm="sha-256", value=_real_fp)
2724
2724
  ]
2725
2725
  except Exception:
2726
- _LOGGER.debug("camera %s: swallowed exception", '_np_accept_cam_cert', exc_info=True)
2726
+ _LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_np_accept_cam_cert', exc_info=True)
2727
2727
 
2728
2728
  # Diag: log PC/ICE state at patch-application time so we
2729
2729
  # can see whether DTLS handshake has *already* started by
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
5
5
  Author-email: cbrightly <chris.brightly@gmail.com>
6
6
  License-Expression: MIT
@@ -11,11 +11,11 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.11
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: aiohttp
14
+ Requires-Dist: aiohttp>=3.9
15
15
  Requires-Dist: paho-mqtt>=2.0
16
- Requires-Dist: cryptography
17
- Requires-Dist: pycryptodome
18
- Requires-Dist: dacite
16
+ Requires-Dist: cryptography>=42.0
17
+ Requires-Dist: pycryptodome>=3.20
18
+ Requires-Dist: dacite>=1.8
19
19
  Provides-Extra: webrtc
20
20
  Requires-Dist: aiortc>=1.9.0; extra == "webrtc"
21
21
  Requires-Dist: aioice<0.12,>=0.9.0; extra == "webrtc"
@@ -1,8 +1,8 @@
1
- aiohttp
1
+ aiohttp>=3.9
2
2
  paho-mqtt>=2.0
3
- cryptography
4
- pycryptodome
5
- dacite
3
+ cryptography>=42.0
4
+ pycryptodome>=3.20
5
+ dacite>=1.8
6
6
 
7
7
  [webrtc]
8
8
  aiortc>=1.9.0