mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 11:45:24 +03:00
Add api and data
This commit is contained in:
parent
08ca024426
commit
afb9997c7d
@ -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):
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user