Add api and data

This commit is contained in:
Nick Mowen 2024-01-02 13:51:17 -07:00
parent 08ca024426
commit afb9997c7d
2 changed files with 84 additions and 1 deletions

View File

@ -8,6 +8,7 @@ import re
import subprocess as sp import subprocess as sp
import time import time
import traceback import traceback
from collections import defaultdict
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from functools import reduce from functools import reduce
from pathlib import Path from pathlib import Path
@ -620,7 +621,9 @@ def hourly_timeline():
after = request.args.get("after", type=float) after = request.args.get("after", type=float)
limit = request.args.get("limit", 200) limit = request.args.get("limit", 200)
tz_name = request.args.get("timezone", default="utc", type=str) tz_name = request.args.get("timezone", default="utc", type=str)
_, minute_modifier, _ = get_tz_modifiers(tz_name) _, minute_modifier, _ = get_tz_modifiers(tz_name)
minute_offset = int(minute_modifier.split(" ")[0])
clauses = [] clauses = []
@ -675,7 +678,7 @@ def hourly_timeline():
minute=0, second=0, microsecond=0 minute=0, second=0, microsecond=0
) )
+ timedelta( + timedelta(
minutes=int(minute_modifier.split(" ")[0]), minutes=minute_offset,
) )
).timestamp() ).timestamp()
if hour not in hours: if hour not in hours:
@ -693,6 +696,85 @@ def hourly_timeline():
) )
@bp.route("/<camera_name>/recording/hourly/activity")
def hourly_timeline_activity(camera_name: str):
"""Get hourly summary for timeline."""
if camera_name not in current_app.frigate_config.cameras:
return make_response(
jsonify({"success": False, "message": "Camera not found"}),
404,
)
before = request.args.get("before", type=float, default=datetime.now())
after = request.args.get(
"after", type=float, default=datetime.now() - timedelta(hours=1)
)
tz_name = request.args.get("timezone", default="utc", type=str)
_, minute_modifier, _ = get_tz_modifiers(tz_name)
minute_offset = int(minute_modifier.split(" ")[0])
all_recordings: list[Recordings] = (
Recordings.select(
Recordings.start_time,
Recordings.duration,
Recordings.objects,
Recordings.motion,
)
.where(Recordings.camera == camera_name)
.where(Recordings.motion > 0)
.where((Recordings.start_time > after) & (Recordings.end_time < before))
.iterator()
)
# data format is ex:
# {timestamp: [{ date: 1, count: 1, type: motion }]}] }}
hours: dict[int, list[dict[str, any]]] = defaultdict(list)
key = datetime.fromtimestamp(after).replace(second=0, microsecond=0) + timedelta(
minutes=minute_offset
)
check = (key + timedelta(hours=1)).timestamp()
for recording in all_recordings:
if recording.start_time > check:
key = key + timedelta(hours=1)
check = (key + timedelta(hours=1)).timestamp()
data_type = "motion" if recording.objects == 0 else "objects"
hours[int(key.timestamp())].append(
{
"date": recording.start_time + (recording.duration / 2),
"count": recording.motion,
"type": data_type,
}
)
# process data to make data counts relative
for hour, data in hours.items():
motion_values = np.asarray(list(map(lambda m: m["count"], data)))
avg = motion_values.mean()
std = motion_values.std()
for idx, motion in enumerate(motion_values):
if motion < (avg - (std * 2)):
value = 1
elif motion < (avg - std):
value = 2
elif motion < avg:
value = 3
elif motion < (avg + std):
value = 4
elif motion < (avg + (std * 2)):
value = 5
else:
value = 6
hours[hour][idx]["count"] = value
return jsonify(hours)
@bp.route("/<camera_name>/<label>/best.jpg") @bp.route("/<camera_name>/<label>/best.jpg")
@bp.route("/<camera_name>/<label>/thumbnail.jpg") @bp.route("/<camera_name>/<label>/thumbnail.jpg")
def label_thumbnail(camera_name, label): def label_thumbnail(camera_name, label):

View File

@ -14,6 +14,7 @@ export default function TimelineGraph({ id, data }: TimelineGraphProps) {
<Chart <Chart
type="bar" type="bar"
options={{ options={{
colors: ["#991b1b", "#06b6d4", "#ea580c"],
chart: { chart: {
id: id, id: id,
selection: { selection: {