pumaguard 21.post13__py3-none-any.whl → 21.post16__py3-none-any.whl

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.
@@ -12,6 +12,9 @@ import logging
12
12
  import socket
13
13
  import subprocess
14
14
  import threading
15
+ from collections.abc import (
16
+ Callable,
17
+ )
15
18
  from datetime import (
16
19
  datetime,
17
20
  timezone,
@@ -48,6 +51,7 @@ class CameraHeartbeat:
48
51
  tcp_port: int = 80,
49
52
  tcp_timeout: int = 3,
50
53
  icmp_timeout: int = 2,
54
+ status_change_callback: Callable[[str, dict], None] | None = None,
51
55
  ):
52
56
  """
53
57
  Initialize the camera heartbeat monitor.
@@ -61,6 +65,9 @@ class CameraHeartbeat:
61
65
  tcp_port: TCP port to check (default: 80 for HTTP)
62
66
  tcp_timeout: TCP connection timeout in seconds (default: 3)
63
67
  icmp_timeout: ICMP ping timeout in seconds (default: 2)
68
+ status_change_callback: Optional callback function to be called
69
+ when camera status changes. Signature:
70
+ callback(event_type: str, camera_data: dict)
64
71
  """
65
72
  self.webui = webui
66
73
  self.interval = interval
@@ -69,6 +76,7 @@ class CameraHeartbeat:
69
76
  self.tcp_port = tcp_port
70
77
  self.tcp_timeout = tcp_timeout
71
78
  self.icmp_timeout = icmp_timeout
79
+ self.status_change_callback = status_change_callback
72
80
 
73
81
  self._running = False
74
82
  self._thread: threading.Thread | None = None
@@ -171,6 +179,8 @@ class CameraHeartbeat:
171
179
  camera = self.webui.cameras[mac_address]
172
180
  timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
173
181
 
182
+ status_changed = False
183
+
174
184
  if is_reachable:
175
185
  # Camera is reachable - update status to connected
176
186
  if camera["status"] != "connected":
@@ -179,6 +189,7 @@ class CameraHeartbeat:
179
189
  camera["hostname"],
180
190
  camera["ip_address"],
181
191
  )
192
+ status_changed = True
182
193
  camera["status"] = "connected"
183
194
  camera["last_seen"] = timestamp
184
195
  else:
@@ -189,12 +200,27 @@ class CameraHeartbeat:
189
200
  camera["hostname"],
190
201
  camera["ip_address"],
191
202
  )
203
+ status_changed = True
192
204
  camera["status"] = "disconnected"
193
205
  # Don't update last_seen on failure - keep the last successful time
194
206
 
195
207
  # Persist changes to settings
196
208
  self._save_camera_list()
197
209
 
210
+ # Notify callback if status changed
211
+ if status_changed and self.status_change_callback:
212
+ try:
213
+ event_type = (
214
+ "camera_status_changed_online"
215
+ if is_reachable
216
+ else "camera_status_changed_offline"
217
+ )
218
+ self.status_change_callback(event_type, dict(camera))
219
+ except Exception as e: # pylint: disable=broad-except
220
+ logger.error(
221
+ "Error calling status change callback: %s", str(e)
222
+ )
223
+
198
224
  def _save_camera_list(self) -> None:
199
225
  """Save the camera list to settings file."""
200
226
  try: