summaryrefslogtreecommitdiff
path: root/include/py/homekit/camera/config.py
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2023-09-27 00:54:57 +0300
committerEvgeny Zinoviev <me@ch1p.io>2023-09-27 00:54:57 +0300
commitd3a295872c49defb55fc8e4e43e55550991e0927 (patch)
treeb9dca15454f9027d5a9dad0d4443a20de04dbc5d /include/py/homekit/camera/config.py
parentb7cbc2571c1870b4582ead45277d0aa7f961bec8 (diff)
parentbdbb296697f55f4c3a07af43c9aaf7a9ea86f3d0 (diff)
Merge branch 'master' of ch1p.io:homekit
Diffstat (limited to 'include/py/homekit/camera/config.py')
-rw-r--r--include/py/homekit/camera/config.py130
1 files changed, 130 insertions, 0 deletions
diff --git a/include/py/homekit/camera/config.py b/include/py/homekit/camera/config.py
new file mode 100644
index 0000000..c7dbc38
--- /dev/null
+++ b/include/py/homekit/camera/config.py
@@ -0,0 +1,130 @@
+import socket
+
+from ..config import ConfigUnit, LinuxBoardsConfig
+from typing import Optional
+from .types import CameraType, VideoContainerType, VideoCodecType
+
+_lbc = LinuxBoardsConfig()
+
+
+def _validate_roi_line(field, value, error) -> bool:
+ p = value.split(' ')
+ if len(p) != 4:
+ error(field, f'{field}: must contain four coordinates separated by space')
+ for n in p:
+ if not n.isnumeric():
+ error(field, f'{field}: invalid coordinates (not a number)')
+ return True
+
+
+class IpcamConfig(ConfigUnit):
+ NAME = 'ipcam'
+
+ @classmethod
+ def schema(cls) -> Optional[dict]:
+ return {
+ 'cams': {
+ 'type': 'dict',
+ 'keysrules': {'type': ['string', 'integer']},
+ 'valuesrules': {
+ 'type': 'dict',
+ 'schema': {
+ 'type': {'type': 'string', 'allowed': [t.value for t in CameraType], 'required': True},
+ 'codec': {'type': 'string', 'allowed': [t.value for t in VideoCodecType], 'required': True},
+ 'container': {'type': 'string', 'allowed': [t.value for t in VideoContainerType], 'required': True},
+ 'server': {'type': 'string', 'allowed': list(_lbc.get().keys()), 'required': True},
+ 'disk': {'type': 'integer', 'required': True},
+ 'motion': {
+ 'type': 'dict',
+ 'schema': {
+ 'threshold': {'type': ['float', 'integer']},
+ 'roi': {
+ 'type': 'list',
+ 'schema': {'type': 'string', 'check_with': _validate_roi_line}
+ }
+ }
+ },
+ 'rtsp_tcp': {'type': 'boolean'}
+ }
+ }
+ },
+ 'motion_padding': {'type': 'integer', 'required': True},
+ 'motion_telegram': {'type': 'boolean', 'required': True},
+ 'fix_interval': {'type': 'integer', 'required': True},
+ 'fix_enabled': {'type': 'boolean', 'required': True},
+ 'cleanup_min_gb': {'type': 'integer', 'required': True},
+ 'cleanup_interval': {'type': 'integer', 'required': True},
+
+ # TODO FIXME
+ 'fragment_url_templates': cls._url_templates_schema(),
+ 'original_file_url_templates': cls._url_templates_schema(),
+
+ 'hls_path': {'type': 'string', 'required': True},
+ 'motion_processing_tmpfs_path': {'type': 'string', 'required': True},
+
+ 'rtsp_creds': {
+ 'required': True,
+ 'type': 'dict',
+ 'schema': {
+ 'login': {'type': 'string', 'required': True},
+ 'password': {'type': 'string', 'required': True},
+ }
+ }
+ }
+
+ @staticmethod
+ def custom_validator(data):
+ for n, cam in data['cams'].items():
+ linux_box = _lbc[cam['server']]
+ if 'ext_hdd' not in linux_box:
+ raise ValueError(f'cam-{n}: linux box {cam["server"]} must have ext_hdd defined')
+ disk = cam['disk']-1
+ if disk < 0 or disk >= len(linux_box['ext_hdd']):
+ raise ValueError(f'cam-{n}: invalid disk index for linux box {cam["server"]}')
+
+ @classmethod
+ def _url_templates_schema(cls) -> dict:
+ return {
+ 'type': 'list',
+ 'empty': False,
+ 'schema': {
+ 'type': 'list',
+ 'empty': False,
+ 'schema': {'type': 'string'}
+ }
+ }
+
+ def get_all_cam_names(self,
+ filter_by_server: Optional[str] = None,
+ filter_by_disk: Optional[int] = None) -> list[int]:
+ cams = []
+ if filter_by_server is not None and filter_by_server not in _lbc:
+ raise ValueError(f'invalid filter_by_server: {filter_by_server} not found in {_lbc.__class__.__name__}')
+ for cam, params in self['cams'].items():
+ if filter_by_server is None or params['server'] == filter_by_server:
+ if filter_by_disk is None or params['disk'] == filter_by_disk:
+ cams.append(int(cam))
+ return cams
+
+ def get_all_cam_names_for_this_server(self,
+ filter_by_disk: Optional[int] = None):
+ return self.get_all_cam_names(filter_by_server=socket.gethostname(),
+ filter_by_disk=filter_by_disk)
+
+ def get_cam_server_and_disk(self, cam: int) -> tuple[str, int]:
+ return self['cams'][cam]['server'], self['cams'][cam]['disk']
+
+ def get_camera_container(self, cam: int) -> VideoContainerType:
+ return VideoContainerType(self['cams'][cam]['container'])
+
+ def get_camera_type(self, cam: int) -> CameraType:
+ return CameraType(self['cams'][cam]['type'])
+
+ def get_rtsp_creds(self) -> tuple[str, str]:
+ return self['rtsp_creds']['login'], self['rtsp_creds']['password']
+
+ def should_use_tcp_for_rtsp(self, cam: int) -> bool:
+ return 'rtsp_tcp' in self['cams'][cam] and self['cams'][cam]['rtsp_tcp']
+
+ def get_camera_ip(self, camera: int) -> str:
+ return f'192.168.5.{camera}'