Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
import base64
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
import dataclasses
|
2020-11-03 15:15:58 +01:00
|
|
|
import json
|
2020-12-21 15:03:27 +01:00
|
|
|
import logging
|
2020-11-03 15:15:58 +01:00
|
|
|
import os
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
2020-11-03 15:15:58 +01:00
|
|
|
|
|
|
|
import cv2
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
import numpy as np
|
2020-11-01 13:17:44 +01:00
|
|
|
import voluptuous as vol
|
2020-11-04 13:31:25 +01:00
|
|
|
import yaml
|
2020-11-01 13:17:44 +01:00
|
|
|
|
2021-06-07 03:24:36 +02:00
|
|
|
from frigate.const import BASE_DIR, RECORD_DIR, CLIPS_DIR, CACHE_DIR
|
2021-01-14 14:19:12 +01:00
|
|
|
from frigate.util import create_mask
|
2020-12-01 14:22:23 +01:00
|
|
|
|
2020-12-21 15:03:27 +01:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
DEFAULT_TRACKED_OBJECTS = ["person"]
|
2021-01-16 14:39:08 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
DEFAULT_DETECTORS = {"coral": {"type": "edgetpu", "device": "usb"}}
|
2020-11-01 13:17:44 +01:00
|
|
|
DETECTORS_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Required(str): {
|
2021-02-17 14:23:32 +01:00
|
|
|
vol.Required("type", default="edgetpu"): vol.In(["cpu", "edgetpu"]),
|
|
|
|
vol.Optional("device", default="usb"): str,
|
|
|
|
vol.Optional("num_threads", default=3): int,
|
2020-11-01 13:17:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class DetectorConfig:
|
|
|
|
type: str
|
|
|
|
device: str
|
|
|
|
num_threads: int
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> DetectorConfig:
|
|
|
|
return DetectorConfig(config["type"], config["device"], config["num_threads"])
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
|
|
|
|
2020-11-01 13:17:44 +01:00
|
|
|
|
|
|
|
MQTT_SCHEMA = vol.Schema(
|
|
|
|
{
|
2021-02-17 14:23:32 +01:00
|
|
|
vol.Required("host"): str,
|
|
|
|
vol.Optional("port", default=1883): int,
|
|
|
|
vol.Optional("topic_prefix", default="frigate"): str,
|
|
|
|
vol.Optional("client_id", default="frigate"): str,
|
|
|
|
vol.Optional("stats_interval", default=60): int,
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
vol.Inclusive("user", "auth"): str,
|
|
|
|
vol.Inclusive("password", "auth"): str,
|
2021-06-06 00:52:50 +02:00
|
|
|
vol.Optional("tls_ca_certs"): str,
|
|
|
|
vol.Optional("tls_client_cert"): str,
|
|
|
|
vol.Optional("tls_client_key"): str,
|
|
|
|
vol.Optional("tls_insecure"): bool,
|
2020-11-01 13:17:44 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class MqttConfig:
|
|
|
|
host: str
|
|
|
|
port: int
|
|
|
|
topic_prefix: str
|
|
|
|
client_id: str
|
|
|
|
stats_interval: int
|
|
|
|
user: Optional[str]
|
|
|
|
password: Optional[str]
|
2021-06-06 00:52:50 +02:00
|
|
|
tls_ca_certs: Optional[str]
|
|
|
|
tls_client_cert: Optional[str]
|
|
|
|
tls_client_key: Optional[str]
|
|
|
|
tls_insecure: Optional[bool]
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> MqttConfig:
|
|
|
|
return MqttConfig(
|
|
|
|
config["host"],
|
|
|
|
config["port"],
|
|
|
|
config["topic_prefix"],
|
|
|
|
config["client_id"],
|
|
|
|
config["stats_interval"],
|
|
|
|
config.get("user"),
|
|
|
|
config.get("password"),
|
2021-06-06 00:52:50 +02:00
|
|
|
config.get("tls_ca_certs"),
|
|
|
|
config.get("tls_client_cert"),
|
|
|
|
config.get("tls_client_key"),
|
|
|
|
config.get("tls_insecure"),
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
|
|
|
|
|
|
|
|
2021-01-13 13:49:05 +01:00
|
|
|
RETAIN_SCHEMA = vol.Schema(
|
2021-02-17 14:23:32 +01:00
|
|
|
{vol.Required("default", default=10): int, "objects": {str: int}}
|
2020-11-23 15:25:46 +01:00
|
|
|
)
|
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class RetainConfig:
|
|
|
|
default: int
|
|
|
|
objects: Dict[str, int]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config, global_config={}) -> RetainConfig:
|
|
|
|
return RetainConfig(
|
|
|
|
config.get("default", global_config.get("default")),
|
|
|
|
config.get("objects", global_config.get("objects", {})),
|
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
|
|
|
|
|
|
|
|
2020-12-23 14:16:37 +01:00
|
|
|
CLIPS_SCHEMA = vol.Schema(
|
2020-11-01 13:17:44 +01:00
|
|
|
{
|
2021-02-17 14:23:32 +01:00
|
|
|
vol.Optional("max_seconds", default=300): int,
|
|
|
|
vol.Optional("retain", default={}): RETAIN_SCHEMA,
|
2020-11-01 13:17:44 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class ClipsConfig:
|
|
|
|
max_seconds: int
|
|
|
|
retain: RetainConfig
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> ClipsConfig:
|
|
|
|
return ClipsConfig(
|
|
|
|
config["max_seconds"],
|
|
|
|
RetainConfig.build(config["retain"]),
|
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return {
|
|
|
|
"max_seconds": self.max_seconds,
|
|
|
|
"retain": self.retain.to_dict(),
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MOTION_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
"mask": vol.Any(str, [str]),
|
|
|
|
"threshold": vol.Range(min=1, max=255),
|
|
|
|
"contour_area": int,
|
|
|
|
"delta_alpha": float,
|
|
|
|
"frame_alpha": float,
|
|
|
|
"frame_height": int,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class MotionConfig:
|
|
|
|
raw_mask: Union[str, List[str]]
|
|
|
|
mask: np.ndarray
|
|
|
|
threshold: int
|
|
|
|
contour_area: int
|
|
|
|
delta_alpha: float
|
|
|
|
frame_alpha: float
|
|
|
|
frame_height: int
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config, global_config, frame_shape) -> MotionConfig:
|
|
|
|
raw_mask = config.get("mask")
|
|
|
|
if raw_mask:
|
|
|
|
mask = create_mask(frame_shape, raw_mask)
|
|
|
|
else:
|
|
|
|
mask = np.zeros(frame_shape, np.uint8)
|
|
|
|
mask[:] = 255
|
|
|
|
|
|
|
|
return MotionConfig(
|
|
|
|
raw_mask,
|
|
|
|
mask,
|
|
|
|
config.get("threshold", global_config.get("threshold", 25)),
|
|
|
|
config.get("contour_area", global_config.get("contour_area", 100)),
|
|
|
|
config.get("delta_alpha", global_config.get("delta_alpha", 0.2)),
|
|
|
|
config.get("frame_alpha", global_config.get("frame_alpha", 0.2)),
|
|
|
|
config.get(
|
|
|
|
"frame_height", global_config.get("frame_height", frame_shape[0] // 6)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return {
|
|
|
|
"mask": self.raw_mask,
|
|
|
|
"threshold": self.threshold,
|
|
|
|
"contour_area": self.contour_area,
|
|
|
|
"delta_alpha": self.delta_alpha,
|
|
|
|
"frame_alpha": self.frame_alpha,
|
|
|
|
"frame_height": self.frame_height,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GLOBAL_DETECT_SCHEMA = vol.Schema({"max_disappeared": int})
|
|
|
|
DETECT_SCHEMA = GLOBAL_DETECT_SCHEMA.extend(
|
|
|
|
{vol.Optional("enabled", default=True): bool}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
class DetectConfig:
|
|
|
|
enabled: bool
|
|
|
|
max_disappeared: int
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config, global_config, camera_fps) -> DetectConfig:
|
|
|
|
return DetectConfig(
|
|
|
|
config["enabled"],
|
|
|
|
config.get(
|
|
|
|
"max_disappeared", global_config.get("max_disappeared", camera_fps * 5)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return {
|
|
|
|
"enabled": self.enabled,
|
|
|
|
"max_disappeared": self.max_disappeared,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZONE_FILTER_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
str: {
|
|
|
|
"min_area": int,
|
|
|
|
"max_area": int,
|
|
|
|
"threshold": float,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
FILTER_SCHEMA = ZONE_FILTER_SCHEMA.extend(
|
|
|
|
{
|
|
|
|
str: {
|
|
|
|
"min_score": float,
|
|
|
|
"mask": vol.Any(str, [str]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class FilterConfig:
|
|
|
|
min_area: int
|
|
|
|
max_area: int
|
|
|
|
threshold: float
|
|
|
|
min_score: float
|
|
|
|
mask: Optional[np.ndarray]
|
|
|
|
raw_mask: Union[str, List[str]]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(
|
|
|
|
cls, config, global_config={}, global_mask=None, frame_shape=None
|
|
|
|
) -> FilterConfig:
|
|
|
|
raw_mask = []
|
|
|
|
if global_mask:
|
|
|
|
if isinstance(global_mask, list):
|
|
|
|
raw_mask += global_mask
|
|
|
|
elif isinstance(global_mask, str):
|
|
|
|
raw_mask += [global_mask]
|
|
|
|
|
|
|
|
config_mask = config.get("mask")
|
|
|
|
if config_mask:
|
|
|
|
if isinstance(config_mask, list):
|
|
|
|
raw_mask += config_mask
|
|
|
|
elif isinstance(config_mask, str):
|
|
|
|
raw_mask += [config_mask]
|
|
|
|
|
|
|
|
mask = create_mask(frame_shape, raw_mask) if raw_mask else None
|
|
|
|
|
|
|
|
return FilterConfig(
|
|
|
|
min_area=config.get("min_area", global_config.get("min_area", 0)),
|
|
|
|
max_area=config.get("max_area", global_config.get("max_area", 24000000)),
|
|
|
|
threshold=config.get("threshold", global_config.get("threshold", 0.7)),
|
|
|
|
min_score=config.get("min_score", global_config.get("min_score", 0.5)),
|
|
|
|
mask=mask,
|
|
|
|
raw_mask=raw_mask,
|
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return {
|
|
|
|
"min_area": self.min_area,
|
|
|
|
"max_area": self.max_area,
|
|
|
|
"threshold": self.threshold,
|
|
|
|
"min_score": self.min_score,
|
|
|
|
"mask": self.raw_mask,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZONE_SCHEMA = {
|
|
|
|
str: {
|
|
|
|
vol.Required("coordinates"): vol.Any(str, [str]),
|
|
|
|
vol.Optional("filters", default={}): ZONE_FILTER_SCHEMA,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-23 18:39:55 +02:00
|
|
|
@dataclasses.dataclass(frozen=True)
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
class ZoneConfig:
|
|
|
|
filters: Dict[str, FilterConfig]
|
|
|
|
coordinates: Union[str, List[str]]
|
|
|
|
contour: np.ndarray
|
|
|
|
color: Tuple[int, int, int]
|
|
|
|
|
|
|
|
@classmethod
|
2021-05-23 18:39:55 +02:00
|
|
|
def build(cls, config, color: Tuple[int, int, int]) -> ZoneConfig:
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
coordinates = config["coordinates"]
|
|
|
|
|
|
|
|
if isinstance(coordinates, list):
|
|
|
|
contour = np.array(
|
|
|
|
[[int(p.split(",")[0]), int(p.split(",")[1])] for p in coordinates]
|
|
|
|
)
|
|
|
|
elif isinstance(coordinates, str):
|
|
|
|
points = coordinates.split(",")
|
|
|
|
contour = np.array(
|
|
|
|
[[int(points[i]), int(points[i + 1])] for i in range(0, len(points), 2)]
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
print(f"Unable to parse zone coordinates for {name}")
|
|
|
|
contour = np.array([])
|
|
|
|
|
|
|
|
return ZoneConfig(
|
|
|
|
{name: FilterConfig.build(c) for name, c in config["filters"].items()},
|
|
|
|
coordinates,
|
|
|
|
contour,
|
2021-05-23 18:39:55 +02:00
|
|
|
color=color,
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return {
|
|
|
|
"filters": {k: f.to_dict() for k, f in self.filters.items()},
|
|
|
|
"coordinates": self.coordinates,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def filters_for_all_tracked_objects(object_config):
|
|
|
|
for tracked_object in object_config.get("track", DEFAULT_TRACKED_OBJECTS):
|
|
|
|
if not "filters" in object_config:
|
|
|
|
object_config["filters"] = {}
|
|
|
|
if not tracked_object in object_config["filters"]:
|
|
|
|
object_config["filters"][tracked_object] = {}
|
|
|
|
return object_config
|
|
|
|
|
|
|
|
|
|
|
|
OBJECTS_SCHEMA = vol.Schema(
|
|
|
|
vol.All(
|
|
|
|
filters_for_all_tracked_objects,
|
|
|
|
{
|
|
|
|
"track": [str],
|
|
|
|
"mask": vol.Any(str, [str]),
|
|
|
|
vol.Optional("filters", default={}): FILTER_SCHEMA,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class ObjectConfig:
|
|
|
|
track: List[str]
|
|
|
|
filters: Dict[str, FilterConfig]
|
|
|
|
raw_mask: Optional[Union[str, List[str]]]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config, global_config, frame_shape) -> ObjectConfig:
|
|
|
|
track = config.get("track", global_config.get("track", DEFAULT_TRACKED_OBJECTS))
|
|
|
|
raw_mask = config.get("mask")
|
|
|
|
return ObjectConfig(
|
|
|
|
track,
|
|
|
|
{
|
|
|
|
name: FilterConfig.build(
|
|
|
|
config["filters"].get(name, {}),
|
|
|
|
global_config["filters"].get(name, {}),
|
|
|
|
raw_mask,
|
|
|
|
frame_shape,
|
|
|
|
)
|
|
|
|
for name in track
|
|
|
|
},
|
|
|
|
raw_mask,
|
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return {
|
|
|
|
"track": self.track,
|
|
|
|
"mask": self.raw_mask,
|
|
|
|
"filters": {k: f.to_dict() for k, f in self.filters.items()},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
FFMPEG_GLOBAL_ARGS_DEFAULT = ["-hide_banner", "-loglevel", "warning"]
|
|
|
|
FFMPEG_INPUT_ARGS_DEFAULT = [
|
|
|
|
"-avoid_negative_ts",
|
|
|
|
"make_zero",
|
|
|
|
"-fflags",
|
|
|
|
"+genpts+discardcorrupt",
|
|
|
|
"-rtsp_transport",
|
|
|
|
"tcp",
|
|
|
|
"-stimeout",
|
|
|
|
"5000000",
|
|
|
|
"-use_wallclock_as_timestamps",
|
|
|
|
"1",
|
|
|
|
]
|
|
|
|
DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT = ["-f", "rawvideo", "-pix_fmt", "yuv420p"]
|
2020-11-29 22:55:53 +01:00
|
|
|
RTMP_FFMPEG_OUTPUT_ARGS_DEFAULT = ["-c", "copy", "-f", "flv"]
|
2021-02-17 14:23:32 +01:00
|
|
|
SAVE_CLIPS_FFMPEG_OUTPUT_ARGS_DEFAULT = [
|
|
|
|
"-f",
|
|
|
|
"segment",
|
|
|
|
"-segment_time",
|
|
|
|
"10",
|
|
|
|
"-segment_format",
|
|
|
|
"mp4",
|
|
|
|
"-reset_timestamps",
|
|
|
|
"1",
|
|
|
|
"-strftime",
|
|
|
|
"1",
|
|
|
|
"-c",
|
|
|
|
"copy",
|
|
|
|
"-an",
|
|
|
|
]
|
|
|
|
RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT = [
|
|
|
|
"-f",
|
|
|
|
"segment",
|
|
|
|
"-segment_time",
|
|
|
|
"60",
|
|
|
|
"-segment_format",
|
|
|
|
"mp4",
|
|
|
|
"-reset_timestamps",
|
|
|
|
"1",
|
|
|
|
"-strftime",
|
|
|
|
"1",
|
|
|
|
"-c",
|
|
|
|
"copy",
|
|
|
|
"-an",
|
|
|
|
]
|
2020-11-01 13:17:44 +01:00
|
|
|
|
|
|
|
GLOBAL_FFMPEG_SCHEMA = vol.Schema(
|
2021-01-09 18:26:46 +01:00
|
|
|
{
|
2021-02-17 14:23:32 +01:00
|
|
|
vol.Optional("global_args", default=FFMPEG_GLOBAL_ARGS_DEFAULT): vol.Any(
|
|
|
|
str, [str]
|
|
|
|
),
|
|
|
|
vol.Optional("hwaccel_args", default=[]): vol.Any(str, [str]),
|
|
|
|
vol.Optional("input_args", default=FFMPEG_INPUT_ARGS_DEFAULT): vol.Any(
|
|
|
|
str, [str]
|
|
|
|
),
|
|
|
|
vol.Optional("output_args", default={}): {
|
|
|
|
vol.Optional("detect", default=DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT): vol.Any(
|
|
|
|
str, [str]
|
|
|
|
),
|
|
|
|
vol.Optional("record", default=RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT): vol.Any(
|
|
|
|
str, [str]
|
|
|
|
),
|
|
|
|
vol.Optional(
|
|
|
|
"clips", default=SAVE_CLIPS_FFMPEG_OUTPUT_ARGS_DEFAULT
|
|
|
|
): vol.Any(str, [str]),
|
|
|
|
vol.Optional("rtmp", default=RTMP_FFMPEG_OUTPUT_ARGS_DEFAULT): vol.Any(
|
|
|
|
str, [str]
|
|
|
|
),
|
|
|
|
},
|
2020-11-01 13:17:44 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-11-29 22:55:53 +01:00
|
|
|
def each_role_used_once(inputs):
|
2021-02-17 14:23:32 +01:00
|
|
|
roles = [role for i in inputs for role in i["roles"]]
|
2020-11-29 22:55:53 +01:00
|
|
|
roles_set = set(roles)
|
|
|
|
if len(roles) > len(roles_set):
|
|
|
|
raise ValueError
|
|
|
|
return inputs
|
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
|
2021-01-16 05:01:24 +01:00
|
|
|
def detect_is_required(inputs):
|
2021-02-17 14:23:32 +01:00
|
|
|
roles = [role for i in inputs for role in i["roles"]]
|
|
|
|
if not "detect" in roles:
|
2021-01-16 05:01:24 +01:00
|
|
|
raise ValueError
|
|
|
|
return inputs
|
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
|
2020-11-01 13:17:44 +01:00
|
|
|
CAMERA_FFMPEG_SCHEMA = vol.Schema(
|
2021-01-09 18:26:46 +01:00
|
|
|
{
|
2021-02-17 14:23:32 +01:00
|
|
|
vol.Required("inputs"): vol.All(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
vol.Required("path"): str,
|
|
|
|
vol.Required("roles"): ["detect", "clips", "record", "rtmp"],
|
|
|
|
"global_args": vol.Any(str, [str]),
|
|
|
|
"hwaccel_args": vol.Any(str, [str]),
|
|
|
|
"input_args": vol.Any(str, [str]),
|
|
|
|
}
|
|
|
|
],
|
|
|
|
vol.Msg(each_role_used_once, msg="Each input role may only be used once"),
|
|
|
|
vol.Msg(detect_is_required, msg="The detect role is required"),
|
|
|
|
),
|
|
|
|
"global_args": vol.Any(str, [str]),
|
|
|
|
"hwaccel_args": vol.Any(str, [str]),
|
|
|
|
"input_args": vol.Any(str, [str]),
|
|
|
|
"output_args": {
|
|
|
|
vol.Optional("detect", default=DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT): vol.Any(
|
|
|
|
str, [str]
|
|
|
|
),
|
|
|
|
vol.Optional("record", default=RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT): vol.Any(
|
|
|
|
str, [str]
|
|
|
|
),
|
|
|
|
vol.Optional(
|
|
|
|
"clips", default=SAVE_CLIPS_FFMPEG_OUTPUT_ARGS_DEFAULT
|
|
|
|
): vol.Any(str, [str]),
|
|
|
|
vol.Optional("rtmp", default=RTMP_FFMPEG_OUTPUT_ARGS_DEFAULT): vol.Any(
|
|
|
|
str, [str]
|
|
|
|
),
|
|
|
|
},
|
2020-11-01 13:17:44 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class CameraFfmpegConfig:
|
|
|
|
inputs: List[CameraInput]
|
|
|
|
output_args: Dict[str, List[str]]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(self, config, global_config):
|
|
|
|
output_args = config.get("output_args", global_config["output_args"])
|
|
|
|
output_args = {
|
|
|
|
k: v if isinstance(v, list) else v.split(" ")
|
|
|
|
for k, v in output_args.items()
|
|
|
|
}
|
|
|
|
return CameraFfmpegConfig(
|
|
|
|
[CameraInput.build(i, config, global_config) for i in config["inputs"]],
|
|
|
|
output_args,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class CameraInput:
|
|
|
|
path: str
|
|
|
|
roles: List[str]
|
|
|
|
global_args: List[str]
|
|
|
|
hwaccel_args: List[str]
|
|
|
|
input_args: List[str]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, ffmpeg_input, camera_config, global_config) -> CameraInput:
|
|
|
|
return CameraInput(
|
|
|
|
ffmpeg_input["path"],
|
|
|
|
ffmpeg_input["roles"],
|
|
|
|
CameraInput._extract_args(
|
|
|
|
"global_args", ffmpeg_input, camera_config, global_config
|
|
|
|
),
|
|
|
|
CameraInput._extract_args(
|
|
|
|
"hwaccel_args", ffmpeg_input, camera_config, global_config
|
|
|
|
),
|
|
|
|
CameraInput._extract_args(
|
|
|
|
"input_args", ffmpeg_input, camera_config, global_config
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _extract_args(name, ffmpeg_input, camera_config, global_config):
|
|
|
|
args = ffmpeg_input.get(name, camera_config.get(name, global_config[name]))
|
|
|
|
return args if isinstance(args, list) else args.split(" ")
|
|
|
|
|
|
|
|
|
2020-11-30 02:00:46 +01:00
|
|
|
def ensure_zones_and_cameras_have_different_names(cameras):
|
2021-02-17 14:23:32 +01:00
|
|
|
zones = [zone for camera in cameras.values() for zone in camera["zones"].keys()]
|
2020-11-30 02:00:46 +01:00
|
|
|
for zone in zones:
|
|
|
|
if zone in cameras.keys():
|
|
|
|
raise ValueError
|
|
|
|
return cameras
|
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
|
|
|
|
CAMERAS_SCHEMA = vol.Schema(
|
|
|
|
vol.All(
|
|
|
|
{
|
|
|
|
str: {
|
|
|
|
vol.Required("ffmpeg"): CAMERA_FFMPEG_SCHEMA,
|
|
|
|
vol.Required("height"): int,
|
|
|
|
vol.Required("width"): int,
|
|
|
|
"fps": int,
|
|
|
|
vol.Optional("best_image_timeout", default=60): int,
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
vol.Optional("zones", default={}): ZONE_SCHEMA,
|
2021-02-17 14:23:32 +01:00
|
|
|
vol.Optional("clips", default={}): {
|
|
|
|
vol.Optional("enabled", default=False): bool,
|
|
|
|
vol.Optional("pre_capture", default=5): int,
|
|
|
|
vol.Optional("post_capture", default=5): int,
|
|
|
|
vol.Optional("required_zones", default=[]): [str],
|
|
|
|
"objects": [str],
|
|
|
|
vol.Optional("retain", default={}): RETAIN_SCHEMA,
|
|
|
|
},
|
|
|
|
vol.Optional("record", default={}): {
|
|
|
|
"enabled": bool,
|
|
|
|
"retain_days": int,
|
|
|
|
},
|
|
|
|
vol.Optional("rtmp", default={}): {
|
|
|
|
vol.Required("enabled", default=True): bool,
|
|
|
|
},
|
|
|
|
vol.Optional("snapshots", default={}): {
|
|
|
|
vol.Optional("enabled", default=False): bool,
|
|
|
|
vol.Optional("timestamp", default=False): bool,
|
|
|
|
vol.Optional("bounding_box", default=False): bool,
|
|
|
|
vol.Optional("crop", default=False): bool,
|
|
|
|
vol.Optional("required_zones", default=[]): [str],
|
|
|
|
"height": int,
|
|
|
|
vol.Optional("retain", default={}): RETAIN_SCHEMA,
|
|
|
|
},
|
|
|
|
vol.Optional("mqtt", default={}): {
|
|
|
|
vol.Optional("enabled", default=True): bool,
|
|
|
|
vol.Optional("timestamp", default=True): bool,
|
|
|
|
vol.Optional("bounding_box", default=True): bool,
|
|
|
|
vol.Optional("crop", default=True): bool,
|
|
|
|
vol.Optional("height", default=270): int,
|
|
|
|
vol.Optional("required_zones", default=[]): [str],
|
|
|
|
},
|
|
|
|
vol.Optional("objects", default={}): OBJECTS_SCHEMA,
|
|
|
|
vol.Optional("motion", default={}): MOTION_SCHEMA,
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
vol.Optional("detect", default={}): DETECT_SCHEMA,
|
2021-02-17 14:23:32 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
vol.Msg(
|
|
|
|
ensure_zones_and_cameras_have_different_names,
|
|
|
|
msg="Zones cannot share names with cameras",
|
|
|
|
),
|
|
|
|
)
|
2020-11-01 13:17:44 +01:00
|
|
|
)
|
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
@dataclasses.dataclass
|
|
|
|
class CameraSnapshotsConfig:
|
|
|
|
enabled: bool
|
|
|
|
timestamp: bool
|
|
|
|
bounding_box: bool
|
|
|
|
crop: bool
|
|
|
|
required_zones: List[str]
|
|
|
|
height: Optional[int]
|
|
|
|
retain: RetainConfig
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(self, config, global_config) -> CameraSnapshotsConfig:
|
|
|
|
return CameraSnapshotsConfig(
|
|
|
|
enabled=config["enabled"],
|
|
|
|
timestamp=config["timestamp"],
|
|
|
|
bounding_box=config["bounding_box"],
|
|
|
|
crop=config["crop"],
|
|
|
|
required_zones=config["required_zones"],
|
|
|
|
height=config.get("height"),
|
|
|
|
retain=RetainConfig.build(
|
|
|
|
config["retain"], global_config["snapshots"]["retain"]
|
|
|
|
),
|
|
|
|
)
|
2021-02-17 14:23:32 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return {
|
|
|
|
"enabled": self.enabled,
|
|
|
|
"timestamp": self.timestamp,
|
|
|
|
"bounding_box": self.bounding_box,
|
|
|
|
"crop": self.crop,
|
|
|
|
"height": self.height,
|
|
|
|
"retain": self.retain.to_dict(),
|
|
|
|
"required_zones": self.required_zones,
|
|
|
|
}
|
2020-12-13 17:04:55 +01:00
|
|
|
|
2021-01-09 18:26:46 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
@dataclasses.dataclass
|
|
|
|
class CameraMqttConfig:
|
|
|
|
enabled: bool
|
|
|
|
timestamp: bool
|
|
|
|
bounding_box: bool
|
|
|
|
crop: bool
|
|
|
|
height: int
|
|
|
|
required_zones: List[str]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> CameraMqttConfig:
|
|
|
|
return CameraMqttConfig(
|
|
|
|
config["enabled"],
|
|
|
|
config["timestamp"],
|
|
|
|
config["bounding_box"],
|
|
|
|
config["crop"],
|
|
|
|
config.get("height"),
|
|
|
|
config["required_zones"],
|
|
|
|
)
|
2020-12-13 17:04:55 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
2020-11-08 23:05:15 +01:00
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
@dataclasses.dataclass
|
2021-02-17 14:23:32 +01:00
|
|
|
class CameraClipsConfig:
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
enabled: bool
|
|
|
|
pre_capture: int
|
|
|
|
post_capture: int
|
|
|
|
required_zones: List[str]
|
|
|
|
objects: Optional[List[str]]
|
|
|
|
retain: RetainConfig
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config, global_config) -> CameraClipsConfig:
|
|
|
|
return CameraClipsConfig(
|
|
|
|
enabled=config["enabled"],
|
|
|
|
pre_capture=config["pre_capture"],
|
|
|
|
post_capture=config["post_capture"],
|
|
|
|
required_zones=config["required_zones"],
|
|
|
|
objects=config.get("objects"),
|
|
|
|
retain=RetainConfig.build(
|
|
|
|
config["retain"],
|
|
|
|
global_config["clips"]["retain"],
|
|
|
|
),
|
|
|
|
)
|
2021-02-05 04:44:44 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
2020-11-18 04:11:19 +01:00
|
|
|
return {
|
2021-02-17 14:23:32 +01:00
|
|
|
"enabled": self.enabled,
|
|
|
|
"pre_capture": self.pre_capture,
|
|
|
|
"post_capture": self.post_capture,
|
|
|
|
"objects": self.objects,
|
|
|
|
"retain": self.retain.to_dict(),
|
|
|
|
"required_zones": self.required_zones,
|
2020-11-18 04:11:19 +01:00
|
|
|
}
|
2020-11-29 22:55:53 +01:00
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
@dataclasses.dataclass
|
2021-02-17 14:23:32 +01:00
|
|
|
class CameraRtmpConfig:
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
enabled: bool
|
2020-11-03 15:15:58 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> CameraRtmpConfig:
|
|
|
|
return CameraRtmpConfig(config["enabled"])
|
2021-02-17 14:23:32 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
2020-11-03 15:15:58 +01:00
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
@dataclasses.dataclass(frozen=True)
|
2021-02-17 14:23:32 +01:00
|
|
|
class CameraConfig:
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
name: str
|
|
|
|
ffmpeg: CameraFfmpegConfig
|
|
|
|
height: int
|
|
|
|
width: int
|
|
|
|
fps: Optional[int]
|
|
|
|
best_image_timeout: int
|
|
|
|
zones: Dict[str, ZoneConfig]
|
|
|
|
clips: CameraClipsConfig
|
|
|
|
record: RecordConfig
|
|
|
|
rtmp: CameraRtmpConfig
|
|
|
|
snapshots: CameraSnapshotsConfig
|
|
|
|
mqtt: CameraMqttConfig
|
|
|
|
objects: ObjectConfig
|
|
|
|
motion: MotionConfig
|
|
|
|
detect: DetectConfig
|
|
|
|
|
|
|
|
@property
|
|
|
|
def frame_shape(self) -> Tuple[int, int]:
|
|
|
|
return self.height, self.width
|
|
|
|
|
|
|
|
@property
|
|
|
|
def frame_shape_yuv(self) -> Tuple[int, int]:
|
|
|
|
return self.height * 3 // 2, self.width
|
|
|
|
|
|
|
|
@property
|
|
|
|
def ffmpeg_cmds(self) -> List[Dict[str, List[str]]]:
|
|
|
|
ffmpeg_cmds = []
|
|
|
|
for ffmpeg_input in self.ffmpeg.inputs:
|
2020-12-07 15:07:35 +01:00
|
|
|
ffmpeg_cmd = self._get_ffmpeg_cmd(ffmpeg_input)
|
|
|
|
if ffmpeg_cmd is None:
|
|
|
|
continue
|
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
ffmpeg_cmds.append({"roles": ffmpeg_input.roles, "cmd": ffmpeg_cmd})
|
|
|
|
return ffmpeg_cmds
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, name, config, global_config) -> CameraConfig:
|
2021-05-23 18:39:55 +02:00
|
|
|
colors = plt.cm.get_cmap("tab10", len(config["zones"]))
|
|
|
|
zones = {
|
|
|
|
name: ZoneConfig.build(z, tuple(round(255 * c) for c in colors(idx)[:3]))
|
|
|
|
for idx, (name, z) in enumerate(config["zones"].items())
|
|
|
|
}
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
|
|
|
|
frame_shape = config["height"], config["width"]
|
|
|
|
|
|
|
|
return CameraConfig(
|
|
|
|
name=name,
|
|
|
|
ffmpeg=CameraFfmpegConfig.build(config["ffmpeg"], global_config["ffmpeg"]),
|
|
|
|
height=config["height"],
|
|
|
|
width=config["width"],
|
|
|
|
fps=config.get("fps"),
|
|
|
|
best_image_timeout=config["best_image_timeout"],
|
|
|
|
zones=zones,
|
|
|
|
clips=CameraClipsConfig.build(config["clips"], global_config),
|
|
|
|
record=RecordConfig.build(config["record"], global_config["record"]),
|
|
|
|
rtmp=CameraRtmpConfig.build(config["rtmp"]),
|
|
|
|
snapshots=CameraSnapshotsConfig.build(config["snapshots"], global_config),
|
|
|
|
mqtt=CameraMqttConfig.build(config["mqtt"]),
|
|
|
|
objects=ObjectConfig.build(
|
|
|
|
config.get("objects", {}), global_config["objects"], frame_shape
|
|
|
|
),
|
|
|
|
motion=MotionConfig.build(
|
|
|
|
config["motion"], global_config["motion"], frame_shape
|
|
|
|
),
|
|
|
|
detect=DetectConfig.build(
|
|
|
|
config["detect"], global_config["detect"], config.get("fps", 5)
|
|
|
|
),
|
|
|
|
)
|
2020-11-03 15:15:58 +01:00
|
|
|
|
2020-12-01 14:22:23 +01:00
|
|
|
def _get_ffmpeg_cmd(self, ffmpeg_input):
|
2020-11-29 22:55:53 +01:00
|
|
|
ffmpeg_output_args = []
|
2021-02-17 14:23:32 +01:00
|
|
|
if "detect" in ffmpeg_input.roles:
|
|
|
|
ffmpeg_output_args = (
|
|
|
|
self.ffmpeg.output_args["detect"] + ffmpeg_output_args + ["pipe:"]
|
|
|
|
)
|
2020-11-29 22:55:53 +01:00
|
|
|
if self.fps:
|
|
|
|
ffmpeg_output_args = ["-r", str(self.fps)] + ffmpeg_output_args
|
2021-02-17 14:23:32 +01:00
|
|
|
if "rtmp" in ffmpeg_input.roles and self.rtmp.enabled:
|
|
|
|
ffmpeg_output_args = (
|
|
|
|
self.ffmpeg.output_args["rtmp"]
|
|
|
|
+ [f"rtmp://127.0.0.1/live/{self.name}"]
|
|
|
|
+ ffmpeg_output_args
|
|
|
|
)
|
|
|
|
if "clips" in ffmpeg_input.roles:
|
|
|
|
ffmpeg_output_args = (
|
|
|
|
self.ffmpeg.output_args["clips"]
|
|
|
|
+ [f"{os.path.join(CACHE_DIR, self.name)}-%Y%m%d%H%M%S.mp4"]
|
|
|
|
+ ffmpeg_output_args
|
|
|
|
)
|
|
|
|
if "record" in ffmpeg_input.roles and self.record.enabled:
|
|
|
|
ffmpeg_output_args = (
|
|
|
|
self.ffmpeg.output_args["record"]
|
|
|
|
+ [f"{os.path.join(RECORD_DIR, self.name)}-%Y%m%d%H%M%S.mp4"]
|
|
|
|
+ ffmpeg_output_args
|
|
|
|
)
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-12-07 15:07:35 +01:00
|
|
|
# if there arent any outputs enabled for this input
|
|
|
|
if len(ffmpeg_output_args) == 0:
|
|
|
|
return None
|
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
cmd = (
|
|
|
|
["ffmpeg"]
|
|
|
|
+ ffmpeg_input.global_args
|
|
|
|
+ ffmpeg_input.hwaccel_args
|
|
|
|
+ ffmpeg_input.input_args
|
|
|
|
+ ["-i", ffmpeg_input.path]
|
|
|
|
+ ffmpeg_output_args
|
|
|
|
)
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
return [part for part in cmd if part != ""]
|
2021-01-09 18:26:46 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
2020-11-18 04:11:19 +01:00
|
|
|
return {
|
2021-02-17 14:23:32 +01:00
|
|
|
"name": self.name,
|
|
|
|
"height": self.height,
|
|
|
|
"width": self.width,
|
|
|
|
"fps": self.fps,
|
|
|
|
"best_image_timeout": self.best_image_timeout,
|
|
|
|
"zones": {k: z.to_dict() for k, z in self.zones.items()},
|
|
|
|
"clips": self.clips.to_dict(),
|
|
|
|
"record": self.record.to_dict(),
|
|
|
|
"rtmp": self.rtmp.to_dict(),
|
|
|
|
"snapshots": self.snapshots.to_dict(),
|
|
|
|
"mqtt": self.mqtt.to_dict(),
|
|
|
|
"objects": self.objects.to_dict(),
|
|
|
|
"motion": self.motion.to_dict(),
|
|
|
|
"detect": self.detect.to_dict(),
|
|
|
|
"frame_shape": self.frame_shape,
|
|
|
|
"ffmpeg_cmds": [
|
|
|
|
{"roles": c["roles"], "cmd": " ".join(c["cmd"])}
|
|
|
|
for c in self.ffmpeg_cmds
|
|
|
|
],
|
2020-11-18 04:11:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
FRIGATE_CONFIG_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Optional("database", default={}): {
|
2021-06-07 03:24:36 +02:00
|
|
|
vol.Optional("path", default=os.path.join(BASE_DIR, "frigate.db")): str
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
},
|
|
|
|
vol.Optional("model", default={"width": 320, "height": 320}): {
|
|
|
|
vol.Required("width"): int,
|
|
|
|
vol.Required("height"): int,
|
|
|
|
},
|
|
|
|
vol.Optional("detectors", default=DEFAULT_DETECTORS): DETECTORS_SCHEMA,
|
|
|
|
"mqtt": MQTT_SCHEMA,
|
|
|
|
vol.Optional("logger", default={}): {
|
|
|
|
vol.Optional("default", default="info"): vol.In(
|
|
|
|
["info", "debug", "warning", "error", "critical"]
|
|
|
|
),
|
|
|
|
vol.Optional("logs", default={}): {
|
|
|
|
str: vol.In(["info", "debug", "warning", "error", "critical"])
|
|
|
|
},
|
|
|
|
},
|
|
|
|
vol.Optional("snapshots", default={}): {
|
|
|
|
vol.Optional("retain", default={}): RETAIN_SCHEMA
|
|
|
|
},
|
|
|
|
vol.Optional("clips", default={}): CLIPS_SCHEMA,
|
|
|
|
vol.Optional("record", default={}): {
|
|
|
|
vol.Optional("enabled", default=False): bool,
|
|
|
|
vol.Optional("retain_days", default=30): int,
|
|
|
|
},
|
|
|
|
vol.Optional("ffmpeg", default={}): GLOBAL_FFMPEG_SCHEMA,
|
|
|
|
vol.Optional("objects", default={}): OBJECTS_SCHEMA,
|
|
|
|
vol.Optional("motion", default={}): MOTION_SCHEMA,
|
|
|
|
vol.Optional("detect", default={}): GLOBAL_DETECT_SCHEMA,
|
|
|
|
vol.Required("cameras", default={}): CAMERAS_SCHEMA,
|
|
|
|
vol.Optional("environment_vars", default={}): {str: str},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class DatabaseConfig:
|
|
|
|
path: str
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> DatabaseConfig:
|
|
|
|
return DatabaseConfig(config["path"])
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class ModelConfig:
|
|
|
|
width: int
|
|
|
|
height: int
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> ModelConfig:
|
|
|
|
return ModelConfig(config["width"], config["height"])
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class LoggerConfig:
|
|
|
|
default: str
|
|
|
|
logs: Dict[str, str]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> LoggerConfig:
|
|
|
|
return LoggerConfig(
|
|
|
|
config["default"].upper(),
|
|
|
|
{k: v.upper() for k, v in config["logs"].items()},
|
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class SnapshotsConfig:
|
|
|
|
retain: RetainConfig
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config) -> SnapshotsConfig:
|
|
|
|
return SnapshotsConfig(RetainConfig.build(config["retain"]))
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return {"retain": self.retain.to_dict()}
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
class RecordConfig:
|
|
|
|
enabled: bool
|
|
|
|
retain_days: int
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def build(cls, config, global_config) -> RecordConfig:
|
|
|
|
return RecordConfig(
|
|
|
|
config.get("enabled", global_config["enabled"]),
|
|
|
|
config.get("retain_days", global_config["retain_days"]),
|
|
|
|
)
|
|
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
return dataclasses.asdict(self)
|
|
|
|
|
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
class FrigateConfig:
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def __init__(self, config_file=None, config=None) -> None:
|
2020-11-03 15:15:58 +01:00
|
|
|
if config is None and config_file is None:
|
2021-02-17 14:23:32 +01:00
|
|
|
raise ValueError("config or config_file must be defined")
|
2020-11-03 15:15:58 +01:00
|
|
|
elif not config_file is None:
|
|
|
|
config = self._load_file(config_file)
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
config = FRIGATE_CONFIG_SCHEMA(config)
|
|
|
|
|
|
|
|
config = self._sub_env_vars(config)
|
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
self._database = DatabaseConfig.build(config["database"])
|
|
|
|
self._model = ModelConfig.build(config["model"])
|
2021-02-17 14:23:32 +01:00
|
|
|
self._detectors = {
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
name: DetectorConfig.build(d) for name, d in config["detectors"].items()
|
2021-02-17 14:23:32 +01:00
|
|
|
}
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
self._mqtt = MqttConfig.build(config["mqtt"])
|
|
|
|
self._clips = ClipsConfig.build(config["clips"])
|
|
|
|
self._snapshots = SnapshotsConfig.build(config["snapshots"])
|
2021-02-17 14:23:32 +01:00
|
|
|
self._cameras = {
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
name: CameraConfig.build(name, c, config)
|
|
|
|
for name, c in config["cameras"].items()
|
2021-02-17 14:23:32 +01:00
|
|
|
}
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
self._logger = LoggerConfig.build(config["logger"])
|
2021-02-17 14:23:32 +01:00
|
|
|
self._environment_vars = config["environment_vars"]
|
2020-11-03 15:15:58 +01:00
|
|
|
|
|
|
|
def _sub_env_vars(self, config):
|
2021-02-17 14:23:32 +01:00
|
|
|
frigate_env_vars = {
|
|
|
|
k: v for k, v in os.environ.items() if k.startswith("FRIGATE_")
|
|
|
|
}
|
2020-11-03 15:15:58 +01:00
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
if "password" in config["mqtt"]:
|
|
|
|
config["mqtt"]["password"] = config["mqtt"]["password"].format(
|
|
|
|
**frigate_env_vars
|
|
|
|
)
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2021-02-17 14:23:32 +01:00
|
|
|
for camera in config["cameras"].values():
|
|
|
|
for i in camera["ffmpeg"]["inputs"]:
|
|
|
|
i["path"] = i["path"].format(**frigate_env_vars)
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
return config
|
|
|
|
|
|
|
|
def _load_file(self, config_file):
|
|
|
|
with open(config_file) as f:
|
|
|
|
raw_config = f.read()
|
2021-01-09 18:26:46 +01:00
|
|
|
|
|
|
|
if config_file.endswith(".yml"):
|
2020-11-03 15:15:58 +01:00
|
|
|
config = yaml.safe_load(raw_config)
|
|
|
|
elif config_file.endswith(".json"):
|
|
|
|
config = json.loads(raw_config)
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
return config
|
2021-01-09 18:26:46 +01:00
|
|
|
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
2020-11-18 04:11:19 +01:00
|
|
|
return {
|
2021-02-17 14:23:32 +01:00
|
|
|
"database": self.database.to_dict(),
|
|
|
|
"model": self.model.to_dict(),
|
|
|
|
"detectors": {k: d.to_dict() for k, d in self.detectors.items()},
|
|
|
|
"mqtt": self.mqtt.to_dict(),
|
|
|
|
"clips": self.clips.to_dict(),
|
|
|
|
"snapshots": self.snapshots.to_dict(),
|
|
|
|
"cameras": {k: c.to_dict() for k, c in self.cameras.items()},
|
|
|
|
"logger": self.logger.to_dict(),
|
|
|
|
"environment_vars": self._environment_vars,
|
2020-11-18 04:11:19 +01:00
|
|
|
}
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-12-13 17:04:55 +01:00
|
|
|
@property
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def database(self) -> DatabaseConfig:
|
2020-12-13 17:04:55 +01:00
|
|
|
return self._database
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-12-09 02:39:46 +01:00
|
|
|
@property
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def model(self) -> ModelConfig:
|
2020-12-09 02:39:46 +01:00
|
|
|
return self._model
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
@property
|
|
|
|
def detectors(self) -> Dict[str, DetectorConfig]:
|
|
|
|
return self._detectors
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-12-04 13:59:03 +01:00
|
|
|
@property
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def logger(self) -> LoggerConfig:
|
2020-12-04 13:59:03 +01:00
|
|
|
return self._logger
|
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
@property
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def mqtt(self) -> MqttConfig:
|
2020-11-03 15:15:58 +01:00
|
|
|
return self._mqtt
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
@property
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def clips(self) -> ClipsConfig:
|
2020-12-23 14:16:37 +01:00
|
|
|
return self._clips
|
2020-11-03 15:15:58 +01:00
|
|
|
|
2021-01-14 13:38:13 +01:00
|
|
|
@property
|
Use dataclasses for config handling
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.
2021-05-23 00:28:15 +02:00
|
|
|
def snapshots(self) -> SnapshotsConfig:
|
2021-01-14 13:38:13 +01:00
|
|
|
return self._snapshots
|
|
|
|
|
2020-11-03 15:15:58 +01:00
|
|
|
@property
|
|
|
|
def cameras(self) -> Dict[str, CameraConfig]:
|
2020-11-04 13:31:25 +01:00
|
|
|
return self._cameras
|
2021-01-16 04:33:53 +01:00
|
|
|
|
|
|
|
@property
|
|
|
|
def environment_vars(self):
|
|
|
|
return self._environment_vars
|