Lock updates to tracked objects, current frame time, motion boxes, and
regions on `update()`.
Directly create Counters using counted values.
Don't convert removed_ids, new_ids, or updated_ids sets to lists.
Update defaultdict's to remove un-necessary lambdas when possible.
When possible, drop un-necessay list comprehensions, such as when
calling `any`.
Use set comprehension, rather than passing a list comprehension into
`set()`.
Do the slightly more pythonic `x not in y` rather than `not x in y` to
check list inclusion.
Use config data classes to eliminate some of the boilerplate associated
with setting up the configuration. In particular, using dataclasses
removes a lot of the boilerplate around assigning properties to the
object and allows these to be easily immutable by freezing them. In the
case of simple, non-nested dataclasses, this also provides more
convenient `asdict` helpers.
To set this up, where previously the objects would be parsed from the
config via the `__init__` method, create a `build` classmethod that does
this and calls the dataclass initializer.
Some of the objects are mutated at runtime, in particular some of the
zones are mutated to set the color (this might be able to be refactored
out) and some of the camera functionality can be enabled/disabled. Some
of the configs with `enabled` properties don't seem to have mqtt hooks
to be able to toggle this, in particular, the clips, snapshots, and
detect can be toggled but rtmp and record configs do not, but all of
these configs are still not frozen in case there is some other
functionality I am missing.
There are a couple other minor fixes here, one that was introduced
by me recently where `max_seconds` was not defined, the other to
properly `get()` the message payload when handling publishing mqtt
messages sent via websocket.
Use `np.unique` to determine the correct set of row/col pairs to iterate
over when doing the object matching without needing to track which rows
or columns have already been seen. Add to some of the accompanying
documentation to clarify this algorithm.
Also fix what looks to be an erroneous early return, and change this to
a continue.
Generally eliminate the `while True` loops while waiting for a stop
event and prefer to condition the loops on if the stop event is set,
blocking on that where it makes sense. This generally comes in 3
flavors. First and simplest, when there is a sleep and the stop event
is the only thing the loop blocks on, instead do a check using
`stop_event.wait(timeout)` to instead block on the stop event for the
designated amount of time. Second, when there is a different event that
is blocking in the loop, condition the loop on `stop_event.is_set()`
rather than breaking when it is set. Finally, when there is a separate
internal condition that requires a counter, have the loop iterate over
the counter and use `if stop_event.wait(timeout)` internal to the loop.
When running ffprobe, use `subprocess.run` rather than
`subprocess.Popen`. This simplifies the handling that is needed to run
and process the outputs. Here, filename parsing is also simplified by
explicitly removing the file extension with `os.path.splitext` and
forcing a single split into the camera name and the formatted date.
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.