Compare commits

..

213 Commits

Author SHA1 Message Date
Paul Armstrong
2ec921593e refactor(web): responsive images on content size, throttle AutoUpdatingCameraImage 2021-01-26 21:40:33 -06:00
Paul Armstrong
75a01f657e feat(web): make it possible to add to object masks 2021-01-26 21:40:33 -06:00
Paul Armstrong
d4e512c1fc fix(web): object mask editing not showing points 2021-01-26 21:40:33 -06:00
Paul Armstrong
26e7d34f18 fix(web): ensure all links on events page include pathname 2021-01-26 21:40:33 -06:00
Blake Blackshear
15b5ffddd4 updating for docusaurus2 docs 2021-01-26 21:40:33 -06:00
Paul Armstrong
f0f3764992 fix(web): make camera latest.jpg responsive 2021-01-26 21:40:33 -06:00
Blake Blackshear
2beb44b591 add search to docs 2021-01-26 21:40:33 -06:00
Blake Blackshear
27b659dde1 tweaking the docs 2021-01-26 21:40:33 -06:00
Blake Blackshear
630c2ee6f6 use sqlitequeuedb 2021-01-26 21:40:33 -06:00
James Carlos
600477c487 Update documentation link in sidebar to new docs 2021-01-26 21:40:33 -06:00
Blake Blackshear
d31c295598 add debug log when cache is cleaned up 2021-01-26 21:40:33 -06:00
Blake Blackshear
a7bb0931c4 if detection stopped, assume the container needs a restart 2021-01-26 21:40:33 -06:00
Blake Blackshear
ff99a01423 fix table in docs 2021-01-26 21:40:33 -06:00
Blake Blackshear
ea6e311318 readme update 2021-01-26 21:40:33 -06:00
Paul Armstrong
6790467bbc docs: move docs to docusaurus 2021-01-26 21:40:33 -06:00
Blake Blackshear
d315dbea22 rate limit tracked object updates to every 5 seconds 2021-01-26 21:40:33 -06:00
Blake Blackshear
8db7ab6724 add snapshot endpoint that works during the event fixes #575 2021-01-26 21:40:33 -06:00
Blake Blackshear
9a2c034ae8 get the thumbnail instead of the full frame 2021-01-26 21:40:33 -06:00
Blake Blackshear
2885b80a13 dont wait forever for the cache 2021-01-26 21:40:33 -06:00
Blake Blackshear
4a85156e87 fix initial switch state 2021-01-26 21:40:33 -06:00
Blake Blackshear
1785c69e1b handle exception when frame isnt in cache 2021-01-26 21:40:33 -06:00
Paul Armstrong
a862ba8348 feat(web): AutoUpdatingCameraImage to replace MJPEG feed 2021-01-26 21:40:33 -06:00
Paul Armstrong
633d45d02f fix(web): set default path to cameras view 2021-01-26 21:40:33 -06:00
Blake Blackshear
7f4e042dfa update index.js to use baseUrl 2021-01-26 21:40:33 -06:00
Blake Blackshear
507ec13848 first pass at subfilter for ingress support 2021-01-26 21:40:33 -06:00
Paul Armstrong
2132352639 fix(web): dark mode text color fixes
fixes #544
2021-01-26 21:40:33 -06:00
Blake Blackshear
11016b8486 ensure error message with missing config is printed 2021-01-26 21:40:33 -06:00
Blake Blackshear
8615f14407 update notification example 2021-01-26 21:40:33 -06:00
Blake Blackshear
1e84f08018 fix mqtt switch handling 2021-01-26 21:40:33 -06:00
Blake Blackshear
7f663328dc initialize detection correctly from config 2021-01-26 21:40:33 -06:00
Blake Blackshear
ea53068432 update wheels version 2021-01-26 21:40:33 -06:00
Blake Blackshear
144aff9b4e pin numpy 2021-01-26 21:40:33 -06:00
Paul Armstrong
18db6daf0a feat(web): layout & auto-update debug page 2021-01-26 21:40:33 -06:00
Paul Armstrong
26ba29b538 fix(web): ensure button bg colors show in prod builds 2021-01-26 21:40:33 -06:00
Blake Blackshear
70167a34b6 fix zone config 2021-01-26 21:40:33 -06:00
Blake Blackshear
ccb668a1b6 no longer need special aarch64 wheels build 2021-01-26 21:40:33 -06:00
Blake Blackshear
0989c64eab versioning wheels image 2021-01-26 21:40:33 -06:00
Blake Blackshear
c082fc5cb2 move wheels to build container 2021-01-26 21:40:33 -06:00
Paul Armstrong
d39111a294 fix(web): mask zone editor to handle object filter masks
Includes additional handlers for adding/removing masks, as well as click to copy configs

fixes #523
2021-01-26 21:40:33 -06:00
Paul Armstrong
3c072f94b0 feat(web): hash build files to avoid cache issues 2021-01-26 21:40:33 -06:00
Paul Armstrong
7f8ae2ce5c fix(web): ensure mask editing works in firefox 2021-01-26 21:40:33 -06:00
Blake Blackshear
d84b75168c docs updates for notification changes 2021-01-26 21:40:33 -06:00
Blake Blackshear
eb0a5e1c55 rename snapshot endpoint to thumbnail 2021-01-26 21:40:33 -06:00
Blake Blackshear
47ac77dbb0 mqtt tweaks for switches 2021-01-26 21:40:33 -06:00
Blake Blackshear
ec84847be7 allow summary data to be filtered 2021-01-26 21:40:33 -06:00
Blake Blackshear
e7839bfd40 update readme 2021-01-26 21:40:33 -06:00
Blake Blackshear
8762da627b snapshots config typo 2021-01-26 21:40:33 -06:00
Blake Blackshear
3fab321045 update object filters to inherit like motion settings 2021-01-26 21:40:33 -06:00
Blake Blackshear
9451048574 remove support for image masks 2021-01-26 21:40:33 -06:00
Blake Blackshear
46c002038b don't fallback to the CPU
fixes #381
2021-01-26 21:40:33 -06:00
Blake Blackshear
d1d833ea9a add change type to events topic
#476
2021-01-26 21:40:33 -06:00
Blake Blackshear
c1f0750526 ensure each camera has a detect role set 2021-01-26 21:40:33 -06:00
Blake Blackshear
89e02b6956 add detection enable to config
fixes #482
2021-01-26 21:40:33 -06:00
Blake Blackshear
97e8258288 add env vars to config
fixes #509
2021-01-26 21:40:33 -06:00
Blake Blackshear
39040c1874 enable and disable detection via mqtt 2021-01-26 21:40:33 -06:00
Blake Blackshear
c709851888 move setproctitle to prebuilt wheel location 2021-01-26 21:40:33 -06:00
Blake Blackshear
b022bec1fa switch to docker based web builds 2021-01-26 21:40:33 -06:00
Blake Blackshear
bca0531963 handle null thumbnail data 2021-01-26 21:40:33 -06:00
Blake Blackshear
b2c7fc8f5b add mask as object filter 2021-01-26 21:40:33 -06:00
Blake Blackshear
96ac2c29d6 add object masks and move moton mask 2021-01-26 21:40:33 -06:00
Blake Blackshear
14a5118b4d add missing global shapshots config 2021-01-26 21:40:33 -06:00
Patrick Decat
232fa1ffe8 Add missing migrations in docker images 2021-01-26 21:40:33 -06:00
Paul Armstrong
d2e91754e9 fix(web): ensure postcss and postcss-cli are marked as deps 2021-01-26 21:40:33 -06:00
Patrick Decat
4d9066a58d Fix Makefile to ignore gpg signatures in commits 2021-01-26 21:40:33 -06:00
Paul Armstrong
c618867941 feat!: web user interface 2021-01-26 21:40:33 -06:00
Blake Blackshear
5ad4017510 try to cleanup some migration logging 2021-01-26 21:40:33 -06:00
Blake Blackshear
63e14a98f9 add retention settings for snapshots 2021-01-26 21:40:33 -06:00
Blake Blackshear
25e3fe8eab init variables on camera state 2021-01-26 21:40:33 -06:00
Blake Blackshear
840f046572 handle process exit exceptions 2021-01-26 21:40:33 -06:00
Blake Blackshear
89e3c2e4b1 store has_clip and has_snapshot on events 2021-01-26 21:40:33 -06:00
Blake Blackshear
c770470b58 add database migrations 2021-01-26 21:40:33 -06:00
Nat Morris
4619836122 Set titles for forked processes 2021-01-26 21:40:33 -06:00
Nat Morris
76403bba8e New stats module, refactor stats generation out of http module.
StatsEmitter thread to send stats to MQTT every 60 seconds by default, optional stats_interval config value.

New service stats attribute, containing uptime in seconds and version.
2021-01-26 21:40:33 -06:00
Blake Blackshear
a9afa303a2 turn off snapshots via mqtt 2021-01-26 21:40:33 -06:00
Blake Blackshear
e5399ae07a enable turning clips on and off via mqtt 2021-01-26 21:40:33 -06:00
Blake Blackshear
80a5a7b129 cleanup save_Clips/clips inconsistency 2021-01-26 21:40:33 -06:00
Blake Blackshear
9dc97d4b6b add jpg snapshots to disk and clean up config 2021-01-26 21:40:33 -06:00
Paul Armstrong
d8c9169af2 fix: ensure timestamp is drawn above mask 2021-01-26 21:40:33 -06:00
Leonardo Merza
ec256f7130 add notes for Blue Iris RTSP support 2021-01-26 21:40:33 -06:00
yllar
19bbfce4ed Update README.md
change tmpfs size from 100MB to 1GB
2021-01-26 21:40:33 -06:00
kluszczyn
b0b2d9d972 Recordings - fix expire_file 2021-01-26 21:40:33 -06:00
Blake Blackshear
6f5f5c9461 add clips endpoint to readme 2021-01-26 21:40:33 -06:00
Blake Blackshear
fc04bc6046 better mask error handling 2021-01-26 21:40:33 -06:00
Blake Blackshear
9f504253fb fix tmpfs 2021-01-26 21:40:33 -06:00
Blake Blackshear
961997e078 remove redundant error output 2021-01-26 21:40:33 -06:00
Blake Blackshear
363594a9a2 use CACHE_DIR constant 2021-01-26 21:40:33 -06:00
Blake Blackshear
247e2677f3 enable mounting tmpfs volume on start 2021-01-26 21:40:33 -06:00
Blake Blackshear
5b5159f4dd docs and issue template 2021-01-26 21:40:33 -06:00
Blake Blackshear
bc8b85860c update process clip for latest changes 2021-01-26 21:40:33 -06:00
Blake Blackshear
44d45c5880 publish event updates on zone change 2021-01-26 21:40:33 -06:00
Blake Blackshear
a6d8e4fc3f readme updates 2021-01-26 21:40:33 -06:00
Blake Blackshear
2cc9a15f6a handle scenario with empty cache 2021-01-26 21:40:33 -06:00
Blake Blackshear
151f9fb2ee add qsv support to amd64 image 2021-01-26 21:40:33 -06:00
Blake Blackshear
32fb76b3d1 add num_threads fixes #322 2021-01-26 21:40:33 -06:00
Blake Blackshear
8d52e2635a optimize clips fixes #299 2021-01-26 21:40:33 -06:00
Blake Blackshear
f20e1f20a6 add post_capture option 2021-01-26 21:40:33 -06:00
Blake Blackshear
af8594c5c6 re-crop to the object rather than the region 2021-01-26 21:40:33 -06:00
Blake Blackshear
899d41f361 allow runtime drawing settings for mjpeg and latest 2021-01-26 21:40:33 -06:00
Blake Blackshear
7dc6382c90 allow the mask to be a list of masks 2021-01-26 21:40:33 -06:00
Blake Blackshear
e8009c2d26 adding version endpoint 2021-01-26 21:40:33 -06:00
Blake Blackshear
3bc7cdaab6 configurable motion and detect settings 2021-01-26 21:40:33 -06:00
Blake Blackshear
724d8187c6 update gitignore 2021-01-26 21:40:33 -06:00
Blake Blackshear
8f68df60c7 fix test 2021-01-26 21:40:33 -06:00
Blake Blackshear
6af3cb6134 switch default threshold to .7 2021-01-26 21:40:33 -06:00
Blake Blackshear
2ff0c3907f allow process clips to output a csv of scores 2021-01-26 21:40:33 -06:00
Blake Blackshear
dd102ff01d allow db path to be customized 2021-01-26 21:40:33 -06:00
Blake Blackshear
f20b1d75e6 add telegram example 2021-01-26 21:40:33 -06:00
Blake Blackshear
a4b88ac4a7 fix process clip 2021-01-26 21:40:33 -06:00
Blake Blackshear
93b9d586d2 handle empty string args 2021-01-26 21:40:33 -06:00
Blake Blackshear
41dd4447cc allow region to extend beyond the frame 2021-01-26 21:40:33 -06:00
tubalainen
8b9c8a2e80 Updated file
ref: https://github.com/blakeblackshear/frigate/issues/373
2021-01-26 21:40:33 -06:00
Blake Blackshear
a63ff1bb99 swap width and height to reduce confusion 2021-01-26 21:40:33 -06:00
Blake Blackshear
d5e3b59245 updating compose example to reduce confusion 2021-01-26 21:40:33 -06:00
Blake Blackshear
d0470fffcc allow defining model shape and switch to mobiledet as default model 2021-01-26 21:40:33 -06:00
Blake Blackshear
5053305e17 add model dimensions to config 2021-01-26 21:40:33 -06:00
Patrick Decat
9c79392060 Document beta addon host 2021-01-26 21:40:33 -06:00
Blake Blackshear
708c3278bf make shm consistent with compose 2021-01-26 21:40:33 -06:00
tubalainen
c0249f6e59 Updated docker command line...
...to correspond with 0.8.0 feature set.
2021-01-26 21:40:33 -06:00
Blake Blackshear
afd8aefac2 readme cleanup fixes #332 2021-01-26 21:40:33 -06:00
Blake Blackshear
3c07767138 handle and warn if roles dont match enabled features 2021-01-26 21:40:33 -06:00
Blake Blackshear
953c442f13 camera recommendations 2021-01-26 21:40:33 -06:00
Blake Blackshear
e147852878 catch all psutil errors 2021-01-26 21:40:33 -06:00
Blake Blackshear
db7ee6cfb3 clarify height width and fps 2021-01-26 21:40:33 -06:00
Blake Blackshear
3b41b6cc33 readme updates 2021-01-26 21:40:33 -06:00
Blake Blackshear
5e79888370 tweak screenshots 2021-01-26 21:40:33 -06:00
Blake Blackshear
a6aa9bdd59 readme updates 2021-01-26 21:40:33 -06:00
Blake Blackshear
d78b7cc110 set ffmpeg image versions 2021-01-26 21:40:33 -06:00
Blake Blackshear
e7cdace0ab comment you zeroconf 2021-01-26 21:40:33 -06:00
Blake Blackshear
f60eb4e977 fix flask logger config 2021-01-26 21:40:33 -06:00
Blake Blackshear
7aecf6c6de fix graceful exits 2021-01-26 21:40:33 -06:00
Blake Blackshear
75d62096a6 better exception handling 2021-01-26 21:40:33 -06:00
Blake Blackshear
7c44994070 fix default args 2021-01-26 21:40:33 -06:00
Blake Blackshear
f49f3fd9c3 fix fontconfig issue 2021-01-26 21:40:33 -06:00
Blake Blackshear
5ea86d636c doc updates 2021-01-26 21:40:33 -06:00
Blake Blackshear
4c6e90717a update some default config values 2021-01-26 21:40:33 -06:00
Blake Blackshear
d60ca9d783 log level configuration 2021-01-26 21:40:33 -06:00
Blake Blackshear
d304718ea0 no need to write jpg disk 2021-01-26 21:40:33 -06:00
Blake Blackshear
c787c8948e dont delete the recordings directory 2021-01-26 21:40:33 -06:00
Blake Blackshear
62728ef7fb default save_clips objects 2021-01-26 21:40:33 -06:00
Blake Blackshear
47e256f03d add logging for directory creation 2021-01-26 21:40:33 -06:00
Blake Blackshear
527db52d5e exit on config errors 2021-01-26 21:40:33 -06:00
Blake Blackshear
f78b2c48a7 add zeroconf discovery 2021-01-26 21:40:33 -06:00
Blake Blackshear
90c965a32a optional android notification aspect ratio 2021-01-26 21:40:33 -06:00
Blake Blackshear
d4afcde6c9 reduce min timestamp size 2021-01-26 21:40:33 -06:00
Blake Blackshear
257de89ce4 publish object counts rather than on/off 2021-01-26 21:40:33 -06:00
Blake Blackshear
735cc3962b make directories constants 2021-01-26 21:40:33 -06:00
Blake Blackshear
feb42181de cleanup empty directories 2021-01-26 21:40:33 -06:00
Blake Blackshear
f5c4bfa7b4 serve up recordings with nginx 2021-01-26 21:40:33 -06:00
Blake Blackshear
65ddd91855 add recording maintenance 2021-01-26 21:40:33 -06:00
Blake Blackshear
6d7d838613 add record settings to config 2021-01-26 21:40:33 -06:00
Blake Blackshear
5edf7b7f00 fix log timeout 2021-01-26 21:40:33 -06:00
Blake Blackshear
117569830d ensure zones dont have the same name as a camera 2021-01-26 21:40:33 -06:00
Blake Blackshear
d62aec7287 graceful exit of subprocesses 2021-01-26 21:40:33 -06:00
Blake Blackshear
4e0cf3681e add multiple streams per camera 2021-01-26 21:40:33 -06:00
Blake Blackshear
d98751102a fix fontconfig error 2021-01-26 21:40:33 -06:00
Blake Blackshear
1acbeb813e add support for rebroadcasting as rtmp 2021-01-26 21:40:33 -06:00
Blake Blackshear
b87ec752cf avoid null error 2021-01-26 21:40:33 -06:00
Blake Blackshear
753df31fa6 minimize logging 2021-01-26 21:40:33 -06:00
Blake Blackshear
bd77b74689 oops 2021-01-26 21:40:33 -06:00
Blake Blackshear
810c23d8ee only publish end events for true positives 2021-01-26 21:40:33 -06:00
Blake Blackshear
34d9b2983e ensure all events are cleaned up 2021-01-26 21:40:33 -06:00
Blake Blackshear
63c5c8412a publish events like a change feed 2021-01-26 21:40:33 -06:00
Blake Blackshear
60207723d1 pull from memory if event in progress 2021-01-26 21:40:33 -06:00
Blake Blackshear
f4117ad096 add endpoint for event thumbnail 2021-01-26 21:40:33 -06:00
Blake Blackshear
8bed4e9970 add service to get by id 2021-01-26 21:40:33 -06:00
Blake Blackshear
f72eaf781c add zones to summary data 2021-01-26 21:40:33 -06:00
Blake Blackshear
e9ecc20a36 sleep in the right place 2021-01-26 21:40:33 -06:00
Blake Blackshear
addfa2a32d manage events for unlisted cameras 2021-01-26 21:40:33 -06:00
Blake Blackshear
0dad9bc393 add event cleanup thread 2021-01-26 21:40:33 -06:00
Blake Blackshear
5155875a72 add clip retention to config 2021-01-26 21:40:33 -06:00
Blake Blackshear
4ed1217366 use localtime in group by 2021-01-26 21:40:33 -06:00
Blake Blackshear
50e898a684 new http endpoints 2021-01-26 21:40:33 -06:00
Blake Blackshear
251c7fa982 add parameters to event query 2021-01-26 21:40:33 -06:00
Blake Blackshear
00c75e9f98 only save events when a clip is created 2021-01-26 21:40:33 -06:00
Blake Blackshear
1b5b02d286 add bas64 encoded thumbnail to the database 2021-01-26 21:40:33 -06:00
Blake Blackshear
946d655cee check for None value thumbnail_data 2021-01-26 21:40:33 -06:00
Blake Blackshear
d56710b0b5 only set thumbnail data if object is a true positive 2021-01-26 21:40:33 -06:00
Blake Blackshear
0cf78277b5 add some debug logging to frame cache 2021-01-26 21:40:33 -06:00
Blake Blackshear
ce2a583ff9 dont use a property 2021-01-26 21:40:33 -06:00
Blake Blackshear
84bddad30e attempt to fix missing thumbs 2021-01-26 21:40:33 -06:00
Blake Blackshear
0ff682504a better frame handling for best images 2021-01-26 21:40:33 -06:00
Blake Blackshear
5d5984166f cleanup false_positive attribute 2021-01-26 21:40:33 -06:00
Blake Blackshear
b825eb44fe ensure some valid thumbnail is available 2021-01-26 21:40:33 -06:00
Blake Blackshear
7015eb66f2 don't save thumbnails for false positives 2021-01-26 21:40:33 -06:00
Blake Blackshear
494eeb16a5 cleanup 2021-01-26 21:40:33 -06:00
Blake Blackshear
692fdc8d5d reduce logging 2021-01-26 21:40:33 -06:00
Blake Blackshear
ec1a8ebd4a fixes 2021-01-26 21:40:33 -06:00
Blake Blackshear
59daa6597b update nginx config 2021-01-26 21:40:33 -06:00
Blake Blackshear
3941ce4ad1 stop writing json file to disk 2021-01-26 21:40:33 -06:00
Blake Blackshear
aff87d4372 create tracked object class and save thumbnails 2021-01-26 21:40:33 -06:00
Blake Blackshear
373ca87887 maintain thumbnail frames for tracked objects 2021-01-26 21:40:33 -06:00
Blake Blackshear
03c855ecbe sort imports 2021-01-26 21:40:33 -06:00
Blake Blackshear
3a3cb24631 naming threads and processes for logs 2021-01-26 21:40:33 -06:00
Blake Blackshear
4c3fea25a5 use a queue for logging 2021-01-26 21:40:33 -06:00
Blake Blackshear
af303cbf2a create typed config classes 2021-01-26 21:40:33 -06:00
Blake Blackshear
b7c09a9b38 add nginx and change default file locations 2021-01-26 21:40:33 -06:00
Blake Blackshear
eced06eea8 config setup 2021-01-26 21:40:33 -06:00
Blake Blackshear
15d989255c add watchdog 2021-01-26 21:40:33 -06:00
Blake Blackshear
095566b9c2 add back all endpoints 2021-01-26 21:40:33 -06:00
Blake Blackshear
b77a65d446 add event processor 2021-01-26 21:40:33 -06:00
Blake Blackshear
9778a748fc add capture processes 2021-01-26 21:40:33 -06:00
Blake Blackshear
a89dddcafa add camera processors 2021-01-26 21:40:33 -06:00
Blake Blackshear
75973fd4c0 add detected_frames_processor 2021-01-26 21:40:33 -06:00
Blake Blackshear
514036f9d1 add detector processes 2021-01-26 21:40:33 -06:00
Blake Blackshear
36fbedab20 init db/http/mqtt 2021-01-26 21:40:33 -06:00
Blake Blackshear
180baeba50 app container and config schema 2021-01-26 21:40:33 -06:00
Blake Blackshear
cce82fe2a5 move primary script into the module 2021-01-26 21:40:33 -06:00
Blake Blackshear
5512bb2e06 saving events and simple endpoint 2021-01-26 21:40:33 -06:00
Blake Blackshear
be1fcbbdf8 basic database model and api endpoint 2021-01-26 21:40:33 -06:00
Blake Blackshear
422cd52049 store events in tinydb 2021-01-26 21:40:33 -06:00
Blake Blackshear
d67a56d37e update events model 2021-01-26 21:40:33 -06:00
Marc Seeger
070c9721b6 Add support for AMD Ryzen iGPU (fixes #311)
This package will add support for the iGPU of AMD Ryzen and presumably a few more AMD cards.
See details of the package here: https://packages.ubuntu.com/focal/mesa-va-drivers
It also adds support for the open source Nvidia Nouveau driver according to https://wiki.debian.org/HardwareVideoAcceleration
2021-01-26 21:40:33 -06:00
Michael Wei
0219834dd1 Use cv2.bitwise_and instead of numpy.where 2021-01-26 21:40:33 -06:00
15 changed files with 245 additions and 94 deletions

View File

@@ -14,9 +14,25 @@ Use of a [Google Coral Accelerator](https://coral.ai/products/) is optional, but
- Uses a very low overhead motion detection to determine where to run object detection - Uses a very low overhead motion detection to determine where to run object detection
- Object detection with TensorFlow runs in separate processes for maximum FPS - Object detection with TensorFlow runs in separate processes for maximum FPS
- Communicates over MQTT for easy integration into other systems - Communicates over MQTT for easy integration into other systems
- Records video clips of detected objects
- 24/7 recording - 24/7 recording
- Re-streaming via RTMP to reduce the number of connections to your camera - Re-streaming via RTMP to reduce the number of connections to your camera
## Documentation ## Documentation
View the documentation at https://blakeblackshear.github.io/frigate View the documentation at https://blakeblackshear.github.io/frigate
## Screenshots
Integration into HomeAssistant
<div>
<a href="docs/static/img/media_browser.png"><img src="docs/static/img/media_browser.png" height=400></a>
<a href="docs/static/img/notification.png"><img src="docs/static/img/notification.png" height=400></a>
</div>
Also comes with a builtin UI:
<div>
<a href="docs/static/img/home-ui.png"><img src="docs/static/img/home-ui.png" height=400></a>
<a href="docs/static/img/camera-ui.png"><img src="docs/static/img/camera-ui.png" height=400></a>
</div>
![Events](docs/static/img/events-ui.png)

View File

@@ -21,6 +21,7 @@ HassOS users can install via the addon repository. Frigate requires an MQTT serv
## Docker ## Docker
Make sure you choose the right image for your architecture: Make sure you choose the right image for your architecture:
|Arch|Image Name| |Arch|Image Name|
|-|-| |-|-|
|amd64|blakeblackshear/frigate:stable-amd64| |amd64|blakeblackshear/frigate:stable-amd64|

View File

@@ -6,7 +6,6 @@ title: Troubleshooting
### My mjpeg stream or snapshots look green and crazy ### My mjpeg stream or snapshots look green and crazy
This almost always means that the width/height defined for your camera are not correct. Double check the resolution with vlc or another player. Also make sure you don't have the width and height values backwards. This almost always means that the width/height defined for your camera are not correct. Double check the resolution with vlc or another player. Also make sure you don't have the width and height values backwards.
Example:
![mismatched-resolution](/img/mismatched-resolution.jpg) ![mismatched-resolution](/img/mismatched-resolution.jpg)
## "[mov,mp4,m4a,3gp,3g2,mj2 @ 0x5639eeb6e140] moov atom not found" ## "[mov,mp4,m4a,3gp,3g2,mj2 @ 0x5639eeb6e140] moov atom not found"

View File

@@ -9,6 +9,10 @@ module.exports = {
organizationName: 'blakeblackshear', organizationName: 'blakeblackshear',
projectName: 'frigate', projectName: 'frigate',
themeConfig: { themeConfig: {
algolia: {
apiKey: '81ec882db78f7fed05c51daf973f0362',
indexName: 'frigate'
},
navbar: { navbar: {
title: 'Frigate', title: 'Frigate',
logo: { logo: {

BIN
docs/static/img/camera-ui.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB

BIN
docs/static/img/events-ui.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
docs/static/img/home-ui.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@@ -26,7 +26,7 @@ export default function App() {
<Config.Provider value={config}> <Config.Provider value={config}>
<div className="md:flex flex-col md:flex-row md:min-h-screen w-full bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white"> <div className="md:flex flex-col md:flex-row md:min-h-screen w-full bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white">
<Sidebar /> <Sidebar />
<div className="p-4 min-w-0"> <div className="flex-auto p-4 lg:pl-8 lg:pr-8 min-w-0">
<Router> <Router>
<CameraMap path="/cameras/:camera/editor" /> <CameraMap path="/cameras/:camera/editor" />
<Camera path="/cameras/:camera" /> <Camera path="/cameras/:camera" />
@@ -39,5 +39,4 @@ export default function App() {
</div> </div>
</Config.Provider> </Config.Provider>
); );
return;
} }

View File

@@ -27,14 +27,26 @@ export default function CameraMasks({ camera, url }) {
zones, zones,
} = cameraConfig; } = cameraConfig;
const resizeObserver = useMemo(
() =>
new ResizeObserver((entries) => {
window.requestAnimationFrame(() => {
if (Array.isArray(entries) && entries.length) {
const scaledWidth = entries[0].contentRect.width;
const scale = scaledWidth / width;
setImageScale(scale);
}
});
}),
[camera, width, setImageScale]
);
useEffect(() => { useEffect(() => {
if (!imageRef.current) { if (!imageRef.current) {
return; return;
} }
const scaledWidth = imageRef.current.width; resizeObserver.observe(imageRef.current);
const scale = scaledWidth / width; }, [resizeObserver, imageRef.current]);
setImageScale(scale);
}, [imageRef.current, setImageScale]);
const [motionMaskPoints, setMotionMaskPoints] = useState( const [motionMaskPoints, setMotionMaskPoints] = useState(
Array.isArray(motionMask) Array.isArray(motionMask)
@@ -177,7 +189,7 @@ ${Object.keys(zonePoints)
const handleAddObjectMask = useCallback(() => { const handleAddObjectMask = useCallback(() => {
const n = Object.keys(objectMaskPoints).filter((name) => name.startsWith('object_')).length; const n = Object.keys(objectMaskPoints).filter((name) => name.startsWith('object_')).length;
const newObjectName = `object_${n}`; const newObjectName = `object_${n}`;
const newObjectMaskPoints = { ...objectMaskPoints, [newObjectName]: [] }; const newObjectMaskPoints = { ...objectMaskPoints, [newObjectName]: [[]] };
setObjectMaskPoints(newObjectMaskPoints); setObjectMaskPoints(newObjectMaskPoints);
setEditing({ set: newObjectMaskPoints, key: newObjectName, subkey: 0, fn: setObjectMaskPoints }); setEditing({ set: newObjectMaskPoints, key: newObjectName, subkey: 0, fn: setObjectMaskPoints });
}, [objectMaskPoints, setObjectMaskPoints, setEditing]); }, [objectMaskPoints, setObjectMaskPoints, setEditing]);
@@ -185,7 +197,7 @@ ${Object.keys(zonePoints)
const handleRemoveObjectMask = useCallback( const handleRemoveObjectMask = useCallback(
(key, subkey) => { (key, subkey) => {
const newObjectMaskPoints = { ...objectMaskPoints }; const newObjectMaskPoints = { ...objectMaskPoints };
delete newObjectMaskPoints[key]; delete newObjectMaskPoints[key][subkey];
setObjectMaskPoints(newObjectMaskPoints); setObjectMaskPoints(newObjectMaskPoints);
}, },
[objectMaskPoints, setObjectMaskPoints] [objectMaskPoints, setObjectMaskPoints]
@@ -205,6 +217,20 @@ ${Object.keys(objectMaskPoints)
.join('\n')}`); .join('\n')}`);
}, [objectMaskPoints]); }, [objectMaskPoints]);
const handleAddToObjectMask = useCallback(
(key) => {
const newObjectMaskPoints = { ...objectMaskPoints, [key]: [...objectMaskPoints[key], []] };
setObjectMaskPoints(newObjectMaskPoints);
setEditing({
set: newObjectMaskPoints,
key,
subkey: newObjectMaskPoints[key].length - 1,
fn: setObjectMaskPoints,
});
},
[objectMaskPoints, setObjectMaskPoints, setEditing]
);
const handleChangeSnap = useCallback( const handleChangeSnap = useCallback(
(id, value) => { (id, value) => {
setSnap(value); setSnap(value);
@@ -226,10 +252,10 @@ ${Object.keys(objectMaskPoints)
<Box className="space-y-4"> <Box className="space-y-4">
<div className="relative"> <div className="relative">
<img ref={imageRef} className="w-full" src={`${apiHost}/api/${camera}/latest.jpg`} /> <img ref={imageRef} src={`${apiHost}/api/${camera}/latest.jpg`} />
<EditableMask <EditableMask
onChange={handleUpdateEditable} onChange={handleUpdateEditable}
points={editing.subkey ? editing.set[editing.key][editing.subkey] : editing.set[editing.key]} points={'subkey' in editing ? editing.set[editing.key][editing.subkey] : editing.set[editing.key]}
scale={imageScale} scale={imageScale}
snap={snap} snap={snap}
width={width} width={width}
@@ -268,6 +294,7 @@ ${Object.keys(objectMaskPoints)
isMulti isMulti
editing={editing} editing={editing}
title="Object masks" title="Object masks"
onAdd={handleAddToObjectMask}
onCopy={handleCopyObjectMasks} onCopy={handleCopyObjectMasks}
onCreate={handleAddObjectMask} onCreate={handleAddObjectMask}
onEdit={handleEditObjectMask} onEdit={handleEditObjectMask}
@@ -397,6 +424,7 @@ function MaskValues({
isMulti = false, isMulti = false,
editing, editing,
title, title,
onAdd,
onCopy, onCopy,
onCreate, onCreate,
onEdit, onEdit,
@@ -438,6 +466,14 @@ function MaskValues({
[onRemove] [onRemove]
); );
const handleAdd = useCallback(
(event) => {
const { key } = event.target.dataset;
onAdd(key);
},
[onAdd]
);
return ( return (
<Box className="overflow-hidden" onmouseover={handleMousein} onmouseout={handleMouseout}> <Box className="overflow-hidden" onmouseover={handleMousein} onmouseout={handleMouseout}>
<div class="flex space-x-4"> <div class="flex space-x-4">
@@ -454,15 +490,20 @@ function MaskValues({
return ( return (
<div> <div>
{` ${mainkey}:\n mask:\n`} {` ${mainkey}:\n mask:\n`}
{onAdd && showButtons ? (
<Button className="absolute -mt-12 right-0 font-sans" data-key={mainkey} onClick={handleAdd}>
{`Add to ${mainkey}`}
</Button>
) : null}
{points[mainkey].map((item, subkey) => ( {points[mainkey].map((item, subkey) => (
<Item <Item
mainkey={mainkey} mainkey={mainkey}
subkey={subkey} subkey={subkey}
editing={editing} editing={editing}
handleEdit={handleEdit} handleEdit={handleEdit}
handleRemove={handleRemove}
points={item} points={item}
showButtons={showButtons} showButtons={showButtons}
handleRemove={handleRemove}
yamlKeyPrefix={yamlKeyPrefix} yamlKeyPrefix={yamlKeyPrefix}
/> />
))} ))}
@@ -473,10 +514,11 @@ function MaskValues({
<Item <Item
mainkey={mainkey} mainkey={mainkey}
editing={editing} editing={editing}
handleAdd={onAdd ? handleAdd : undefined}
handleEdit={handleEdit} handleEdit={handleEdit}
handleRemove={handleRemove}
points={points[mainkey]} points={points[mainkey]}
showButtons={showButtons} showButtons={showButtons}
handleRemove={handleRemove}
yamlKeyPrefix={yamlKeyPrefix} yamlKeyPrefix={yamlKeyPrefix}
/> />
); );
@@ -487,7 +529,7 @@ function MaskValues({
); );
} }
function Item({ mainkey, subkey, editing, handleEdit, points, showButtons, handleRemove, yamlKeyPrefix }) { function Item({ mainkey, subkey, editing, handleEdit, points, showButtons, handleAdd, handleRemove, yamlKeyPrefix }) {
return ( return (
<span <span
data-key={mainkey} data-key={mainkey}

View File

@@ -1,5 +1,6 @@
import { h } from 'preact'; import { h } from 'preact';
import Box from './components/Box'; import Box from './components/Box';
import CameraImage from './components/CameraImage';
import Events from './Events'; import Events from './Events';
import Heading from './components/Heading'; import Heading from './components/Heading';
import { route } from 'preact-router'; import { route } from 'preact-router';
@@ -23,7 +24,6 @@ export default function Cameras() {
} }
function Camera({ name }) { function Camera({ name }) {
const apiHost = useContext(ApiHost);
const href = `/cameras/${name}`; const href = `/cameras/${name}`;
return ( return (
@@ -32,7 +32,7 @@ function Camera({ name }) {
href={href} href={href}
> >
<Heading size="base">{name}</Heading> <Heading size="base">{name}</Heading>
<img className="w-full" src={`${apiHost}/api/${name}/latest.jpg`} /> <CameraImage camera={name} />
</Box> </Box>
); );
} }

View File

@@ -1,4 +1,6 @@
import { h } from 'preact'; import { h } from 'preact';
import Box from './components/Box';
import Button from './components/Button';
import Heading from './components/Heading'; import Heading from './components/Heading';
import Link from './components/Link'; import Link from './components/Link';
import { ApiHost, Config } from './context'; import { ApiHost, Config } from './context';
@@ -39,59 +41,73 @@ export default function Debug() {
const cameraNames = Object.keys(cameras); const cameraNames = Object.keys(cameras);
const cameraDataKeys = Object.keys(cameras[cameraNames[0]]); const cameraDataKeys = Object.keys(cameras[cameraNames[0]]);
const handleCopyConfig = useCallback(async () => {
await window.navigator.clipboard.writeText(JSON.stringify(config, null, 2));
}, [config]);
return ( return (
<div> <div class="space-y-4">
<Heading> <Heading>
Debug <span className="text-sm">{service.version}</span> Debug <span className="text-sm">{service.version}</span>
</Heading> </Heading>
<Table className="w-full">
<Thead> <Box>
<Tr> <Table className="w-full">
<Th>detector</Th> <Thead>
{detectorDataKeys.map((name) => ( <Tr>
<Th>{name.replace('_', ' ')}</Th> <Th>detector</Th>
))}
</Tr>
</Thead>
<Tbody>
{detectorNames.map((detector, i) => (
<Tr index={i}>
<Td>{detector}</Td>
{detectorDataKeys.map((name) => ( {detectorDataKeys.map((name) => (
<Td key={`${name}-${detector}`}>{detectors[detector][name]}</Td> <Th>{name.replace('_', ' ')}</Th>
))} ))}
</Tr> </Tr>
))} </Thead>
</Tbody> <Tbody>
</Table> {detectorNames.map((detector, i) => (
<Tr index={i}>
<Table className="w-full"> <Td>{detector}</Td>
<Thead> {detectorDataKeys.map((name) => (
<Tr> <Td key={`${name}-${detector}`}>{detectors[detector][name]}</Td>
<Th>camera</Th> ))}
{cameraDataKeys.map((name) => ( </Tr>
<Th>{name.replace('_', ' ')}</Th>
))} ))}
</Tr> </Tbody>
</Thead> </Table>
<Tbody> </Box>
{cameraNames.map((camera, i) => (
<Tr index={i}> <Box>
<Td> <Table className="w-full">
<Link href={`/cameras/${camera}`}>{camera}</Link> <Thead>
</Td> <Tr>
<Th>camera</Th>
{cameraDataKeys.map((name) => ( {cameraDataKeys.map((name) => (
<Td key={`${name}-${camera}`}>{cameras[camera][name]}</Td> <Th>{name.replace('_', ' ')}</Th>
))} ))}
</Tr> </Tr>
))} </Thead>
</Tbody> <Tbody>
</Table> {cameraNames.map((camera, i) => (
<Tr index={i}>
<Td>
<Link href={`/cameras/${camera}`}>{camera}</Link>
</Td>
{cameraDataKeys.map((name) => (
<Td key={`${name}-${camera}`}>{cameras[camera][name]}</Td>
))}
</Tr>
))}
</Tbody>
</Table>
</Box>
<Heading size="sm">Config</Heading> <Box className="relative">
<pre className="font-mono overflow-y-scroll overflow-x-scroll max-h-96 rounded bg-white dark:bg-gray-900"> <Heading size="sm">Config</Heading>
{JSON.stringify(config, null, 2)} <Button className="absolute top-4 right-8" onClick={handleCopyConfig}>
</pre> Copy to Clipboard
</Button>
<pre className="overflow-auto font-mono text-gray-900 dark:text-gray-100 rounded bg-gray-100 dark:bg-gray-800 p-2 max-h-96">
{JSON.stringify(config, null, 2)}
</pre>
</Box>
</div> </div>
); );
} }

View File

@@ -11,7 +11,7 @@ export default function Events({ url } = {}) {
const apiHost = useContext(ApiHost); const apiHost = useContext(ApiHost);
const [events, setEvents] = useState([]); const [events, setEvents] = useState([]);
const searchParams = new URL(`${window.location.protocol}//${window.location.host}${url || '/events'}`).searchParams; const { pathname, searchParams } = new URL(`${window.location.protocol}//${window.location.host}${url || '/events'}`);
const searchParamsString = searchParams.toString(); const searchParamsString = searchParams.toString();
useEffect(async () => { useEffect(async () => {
@@ -23,7 +23,7 @@ export default function Events({ url } = {}) {
const searchKeys = Array.from(searchParams.keys()); const searchKeys = Array.from(searchParams.keys());
return ( return (
<div className="space-y-4"> <div className="space-y-4 w-full">
<Heading>Events</Heading> <Heading>Events</Heading>
{searchKeys.length ? ( {searchKeys.length ? (
@@ -32,9 +32,10 @@ export default function Events({ url } = {}) {
<div className="flex flex-wrap space-x-2"> <div className="flex flex-wrap space-x-2">
{searchKeys.map((filterKey) => ( {searchKeys.map((filterKey) => (
<UnFilterable <UnFilterable
paramName={filterKey}
searchParams={searchParamsString}
name={`${filterKey}: ${searchParams.get(filterKey)}`} name={`${filterKey}: ${searchParams.get(filterKey)}`}
paramName={filterKey}
pathname={pathname}
searchParams={searchParamsString}
/> />
))} ))}
</div> </div>
@@ -42,7 +43,7 @@ export default function Events({ url } = {}) {
) : null} ) : null}
<Box className="min-w-0 overflow-auto"> <Box className="min-w-0 overflow-auto">
<Table> <Table className="w-full">
<Thead> <Thead>
<Tr> <Tr>
<Th></Th> <Th></Th>
@@ -71,17 +72,32 @@ export default function Events({ url } = {}) {
</a> </a>
</Td> </Td>
<Td> <Td>
<Filterable searchParams={searchParamsString} paramName="camera" name={camera} /> <Filterable
pathname={pathname}
searchParams={searchParamsString}
paramName="camera"
name={camera}
/>
</Td> </Td>
<Td> <Td>
<Filterable searchParams={searchParamsString} paramName="label" name={label} /> <Filterable
pathname={pathname}
searchParams={searchParamsString}
paramName="label"
name={label}
/>
</Td> </Td>
<Td>{(score * 100).toFixed(2)}%</Td> <Td>{(score * 100).toFixed(2)}%</Td>
<Td> <Td>
<ul> <ul>
{zones.map((zone) => ( {zones.map((zone) => (
<li> <li>
<Filterable searchParams={searchParamsString} paramName="zone" name={zone} /> <Filterable
pathname={pathname}
searchParams={searchParamsString}
paramName="zone"
name={zone}
/>
</li> </li>
))} ))}
</ul> </ul>
@@ -100,19 +116,19 @@ export default function Events({ url } = {}) {
); );
} }
function Filterable({ searchParams, paramName, name }) { function Filterable({ pathname, searchParams, paramName, name }) {
const params = new URLSearchParams(searchParams); const params = new URLSearchParams(searchParams);
params.set(paramName, name); params.set(paramName, name);
return <Link href={`?${params.toString()}`}>{name}</Link>; return <Link href={`${pathname}?${params.toString()}`}>{name}</Link>;
} }
function UnFilterable({ searchParams, paramName, name }) { function UnFilterable({ pathname, searchParams, paramName, name }) {
const params = new URLSearchParams(searchParams); const params = new URLSearchParams(searchParams);
params.delete(paramName); params.delete(paramName);
return ( return (
<a <a
className="bg-gray-700 text-white px-3 py-1 rounded-md hover:bg-gray-300 hover:text-gray-900 dark:bg-gray-300 dark:text-gray-900 dark:hover:bg-gray-700 dark:hover:text-white" className="bg-gray-700 text-white px-3 py-1 rounded-md hover:bg-gray-300 hover:text-gray-900 dark:bg-gray-300 dark:text-gray-900 dark:hover:bg-gray-700 dark:hover:text-white"
href={`?${params.toString()}`} href={`${pathname}?${params.toString()}`}
> >
{name} {name}
</a> </a>

View File

@@ -1,27 +1,29 @@
import { h } from 'preact'; import { h } from 'preact';
import CameraImage from './CameraImage';
import { ApiHost, Config } from '../context'; import { ApiHost, Config } from '../context';
import { useCallback, useEffect, useContext, useState } from 'preact/hooks'; import { useCallback, useState } from 'preact/hooks';
export default function AutoUpdatingCameraImage({ camera, searchParams }) { const MIN_LOAD_TIMEOUT_MS = 200;
const config = useContext(Config);
const apiHost = useContext(ApiHost);
const cameraConfig = config.cameras[camera];
export default function AutoUpdatingCameraImage({ camera, searchParams, showFps = true }) {
const [key, setKey] = useState(Date.now()); const [key, setKey] = useState(Date.now());
useEffect(() => { const [fps, setFps] = useState(0);
const timeoutId = setTimeout(() => {
setKey(Date.now()); const handleLoad = useCallback(() => {
}, 500); const loadTime = Date.now() - key;
return () => { setFps((1000 / Math.max(loadTime, MIN_LOAD_TIMEOUT_MS)).toFixed(1));
clearTimeout(timeoutId); setTimeout(
}; () => {
}, [key, searchParams]); setKey(Date.now());
},
loadTime > MIN_LOAD_TIMEOUT_MS ? 1 : MIN_LOAD_TIMEOUT_MS
);
}, [key, searchParams, setFps]);
return ( return (
<img <div>
className="w-full" <CameraImage camera={camera} onload={handleLoad} searchParams={`cache=${key}&${searchParams}`} />
src={`${apiHost}/api/${camera}/latest.jpg?cache=${key}&${searchParams}`} {showFps ? <span className="text-xs">Displaying at {fps}fps</span> : null}
alt={`Auto-updating ${camera} image`} </div>
/>
); );
} }

View File

@@ -0,0 +1,60 @@
import { h } from 'preact';
import { ApiHost, Config } from '../context';
import { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'preact/hooks';
export default function CameraImage({ camera, onload, searchParams = '' }) {
const config = useContext(Config);
const apiHost = useContext(ApiHost);
const [availableWidth, setAvailableWidth] = useState(0);
const [loadedSrc, setLoadedSrc] = useState(null);
const containerRef = useRef(null);
const { name, width, height } = config.cameras[camera];
const aspectRatio = width / height;
const resizeObserver = useMemo(() => {
return new ResizeObserver((entries) => {
window.requestAnimationFrame(() => {
if (Array.isArray(entries) && entries.length) {
setAvailableWidth(entries[0].contentRect.width);
}
});
});
}, [setAvailableWidth, width]);
useEffect(() => {
if (!containerRef.current) {
return;
}
resizeObserver.observe(containerRef.current);
}, [resizeObserver, containerRef.current]);
const scaledHeight = useMemo(() => Math.min(Math.ceil(availableWidth / aspectRatio), height), [
availableWidth,
aspectRatio,
height,
]);
const img = useMemo(() => new Image(), [camera]);
img.onload = useCallback(
(event) => {
const src = event.path[0].currentSrc;
setLoadedSrc(src);
onload && onload(event);
},
[searchParams, onload]
);
useEffect(() => {
if (!scaledHeight) {
return;
}
img.src = `${apiHost}/api/${name}/latest.jpg?h=${scaledHeight}${searchParams ? `&${searchParams}` : ''}`;
}, [apiHost, name, img, searchParams, scaledHeight]);
return (
<div ref={containerRef}>
{loadedSrc ? <img width={scaledHeight * aspectRatio} height={scaledHeight} src={loadedSrc} alt={name} /> : null}
</div>
);
}

View File

@@ -2,13 +2,9 @@ import { h } from 'preact';
import { useCallback, useState } from 'preact/hooks'; import { useCallback, useState } from 'preact/hooks';
export default function Switch({ checked, label, id, onChange }) { export default function Switch({ checked, label, id, onChange }) {
const handleChange = useCallback( const handleChange = useCallback(() => {
(event) => { onChange(id, !checked);
console.log(event.target.checked, !checked); }, [id, onChange, checked]);
onChange(id, !checked);
},
[id, onChange, checked]
);
return ( return (
<label for={id} className="flex items-center cursor-pointer"> <label for={id} className="flex items-center cursor-pointer">