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 { 'cameras': { 'type': 'dict', 'keysrules': {'type': ['string', 'integer']}, 'valuesrules': { 'type': 'dict', 'schema': { 'type': {'type': 'string', 'allowed': [t.value for t in CameraType], 'required': True}, 'enabled': {'type': 'boolean'}, 'motion': { 'type': 'dict', 'schema': { 'threshold': {'type': ['float', 'integer']}, 'roi': { 'type': 'list', 'schema': {'type': 'string', 'check_with': _validate_roi_line} } } }, } } }, 'areas': { 'type': 'dict', 'keysrules': {'type': 'string'}, 'valuesrules': { 'type': 'list', 'schema': {'type': ['string', 'integer']} # same type as for 'cameras' keysrules } }, 'camera_ip_template': {'type': 'string', 'required': True}, '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}, } }, 'web_creds': { 'required': True, 'type': 'dict', 'schema': { 'login': {'type': 'string', 'required': True}, 'password': {'type': 'string', 'required': True}, } } } @staticmethod def custom_validator(data): pass # FIXME rewrite or delete, looks kinda obsolete # for n, cam in data['cameras'].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'} } } # FIXME 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['cameras'].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['cameras'][cam]['server'], self['cameras'][cam]['disk'] def get_camera_container(self, camera: int) -> VideoContainerType: return self.get_camera_type(camera).get_container() def get_camera_type(self, camera: int) -> CameraType: return CameraType(self['cameras'][camera]['type']) def get_rtsp_creds(self) -> tuple[str, str]: return self['rtsp_creds']['login'], self['rtsp_creds']['password'] def get_camera_ip(self, camera: int) -> str: return self['camera_ip_template'] % (str(camera),)