-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathfrigate_manager.py
More file actions
133 lines (111 loc) · 5.2 KB
/
Copy pathfrigate_manager.py
File metadata and controls
133 lines (111 loc) · 5.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import requests
import io
import time
import threading
from PIL import Image
import pygame
# Global state dictionaries for inter-thread communication
# These must be managed with a lock.
current_images = {}
camera_last_updated = {}
camera_intervals = {}
camera_motion_states = {}
image_lock = threading.Lock()
api_token = None
token_refresh_time = 0
# This is a placeholder; its value is set dynamically by main.py after reading the config.
is_mqtt_configured = False
# Wake-up events for each camera to interrupt sleep when motion state changes
camera_wake_events = {}
def wake_camera_thread(camera_name):
"""
Wake up a specific camera's image fetching thread immediately.
Used when motion state changes to avoid delays.
"""
if camera_name in camera_wake_events:
camera_wake_events[camera_name].set()
def get_frigate_token(config, session):
"""
Fetches an auth token from the Frigate API.
Args:
config (dict): The application configuration.
session (requests.Session): The session object to use for the request.
Returns:
bool: True if the token was successfully fetched, False otherwise.
"""
global api_token, token_refresh_time
frigate_base_url = config['frigate']['base_url']
username = config['frigate']['username']
password = config['frigate']['password']
login_url = f"{frigate_base_url}/api/login"
payload = {"user": username, "password": password}
headers = {"Content-Type": "application/json"}
try:
response = session.post(login_url, json=payload, headers=headers, verify=False, timeout=10)
response.raise_for_status()
frigate_cookie = response.cookies.get('frigate_token')
if frigate_cookie:
with image_lock:
api_token = frigate_cookie
token_refresh_time = time.time()
return True
else:
return False
except requests.exceptions.RequestException as e:
print(f"Error getting Frigate API token: {e}")
if response and response.status_code == 401:
print("Login credentials likely incorrect for /api/login.")
return False
except Exception as e:
print(f"An unexpected error occurred in get_frigate_token: {e}")
return False
def fetch_and_process_image(config, session, camera_name, target_image_size):
"""
Continuously fetches the latest image for a camera and updates the display surface.
Args:
config (dict): The application configuration.
session (requests.Session): The session object for the request.
camera_name (str): The name of the camera to fetch.
target_image_size (tuple): The desired size for the image surface.
"""
global api_token, image_lock, camera_wake_events
last_fetch_time = time.time()
# Create wake-up event for this camera
if camera_name not in camera_wake_events:
camera_wake_events[camera_name] = threading.Event()
frigate_base_url = config['frigate']['base_url']
image_resampling_method = getattr(Image.Resampling, config.get('advanced', {}).get('image_resampling', 'BICUBIC').upper(), Image.Resampling.BICUBIC)
token_lifetime_seconds = config.get('advanced', {}).get('token_lifetime_seconds', 3600)
while True:
try:
# Re-obtain token if expired or not yet set
with image_lock:
if not api_token or (time.time() - token_refresh_time > token_lifetime_seconds):
get_frigate_token(config, session)
headers = {"Authorization": f"Bearer {api_token}"}
current_interval = camera_intervals.get(camera_name, config['display']['interval_rate_min'])
# Calculate how long to wait, but allow wake-up events to interrupt
time_to_wait = current_interval - (time.time() - last_fetch_time)
if time_to_wait > 0:
# Use event.wait() with timeout instead of time.sleep()
# This allows the thread to be woken up immediately when motion state changes
camera_wake_events[camera_name].wait(timeout=time_to_wait)
# Clear the event for next time
camera_wake_events[camera_name].clear()
last_fetch_time = time.time()
image_url = f"{frigate_base_url}/api/{camera_name}/latest.jpg"
response = session.get(image_url, headers=headers, stream=True, verify=False, timeout=10)
response.raise_for_status()
image_data = io.BytesIO(response.content)
pil_image = Image.open(image_data).convert("RGB")
pil_image = pil_image.resize(target_image_size, image_resampling_method)
pygame_image = pygame.image.fromstring(pil_image.tobytes(), pil_image.size, pil_image.mode)
with image_lock:
current_images[camera_name] = pygame_image
camera_last_updated[camera_name] = time.time()
except requests.exceptions.RequestException as e:
print(f"Error fetching image for {camera_name}: {e}")
time.sleep(5)
except Exception as e:
print(f"CRITICAL ERROR in {camera_name} fetching loop: {e}")
time.sleep(5)