diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rwxr-xr-x | bin/ipcam_server.py | 16 | ||||
-rw-r--r-- | include/py/homekit/camera/config.py | 82 | ||||
-rw-r--r-- | include/py/homekit/camera/types.py | 12 | ||||
-rw-r--r-- | include/py/homekit/config/config.py | 3 | ||||
-rw-r--r-- | include/py/homekit/database/sqlite.py | 11 | ||||
-rw-r--r-- | misc/home_linux_boards/etc/default/homekit_ipcam_server | 2 | ||||
-rw-r--r-- | systemd/ipcam_server.service | 5 | ||||
-rwxr-xr-x | test/test.py | 8 |
9 files changed, 125 insertions, 16 deletions
@@ -9,7 +9,7 @@ __pycache__ /include/test/test_inverter_monitor.log /youtrack-certificate /cpp -/include/test.py +/test/test.py /bin/test.py /arduino/ESP32CameraWebServer/wifi_password.h cmake-build-* diff --git a/bin/ipcam_server.py b/bin/ipcam_server.py index 211bc86..a9d6a0b 100755 --- a/bin/ipcam_server.py +++ b/bin/ipcam_server.py @@ -9,6 +9,7 @@ import __py_include import homekit.telegram.aio as telegram +from argparse import ArgumentParser from apscheduler.schedulers.asyncio import AsyncIOScheduler from asyncio import Lock @@ -53,8 +54,8 @@ def get_all_cams() -> list: class IPCamServerDatabase(SQLiteBase): SCHEMA = 4 - def __init__(self): - super().__init__() + def __init__(self, path=None): + super().__init__(path=path) def schema_init(self, version: int) -> None: cursor = self.cursor() @@ -319,9 +320,9 @@ class IPCamWebServer(http.HTTPServer): # other global stuff # ------------------ -def open_database(): +def open_database(database_path: str): global db - db = IPCamServerDatabase() + db = IPCamServerDatabase(database_path) # update cams list in database, if needed cams = db.get_all_timestamps().keys() @@ -558,9 +559,12 @@ logger = logging.getLogger(__name__) # -------------------- if __name__ == '__main__': - config.load_app('ipcam_server') + parser = ArgumentParser() + parser.add_argument('--listen', type=str, required=True) + parser.add_argument('--database-path', type=str, required=True) + arg = config.load_app(no_config=True, parser=parser) - open_database() + open_database(arg.database_path) loop = asyncio.get_event_loop() diff --git a/include/py/homekit/camera/config.py b/include/py/homekit/camera/config.py new file mode 100644 index 0000000..e0891a6 --- /dev/null +++ b/include/py/homekit/camera/config.py @@ -0,0 +1,82 @@ +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]: + lbc = LinuxBoardsConfig() + 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} + } + } + } + } + } + }, + '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() + } + + @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'} + } + }
\ No newline at end of file diff --git a/include/py/homekit/camera/types.py b/include/py/homekit/camera/types.py index de59022..0d3a384 100644 --- a/include/py/homekit/camera/types.py +++ b/include/py/homekit/camera/types.py @@ -3,3 +3,15 @@ from enum import Enum class CameraType(Enum): ESP32 = 'esp32' + ALIEXPRESS_NONAME = 'ali' + HIKVISION = 'hik' + + +class VideoContainerType(Enum): + MP4 = 'mp4' + MOV = 'mov' + + +class VideoCodecType(Enum): + H264 = 'h264' + H265 = 'h265' diff --git a/include/py/homekit/config/config.py b/include/py/homekit/config/config.py index 7344386..f2a3990 100644 --- a/include/py/homekit/config/config.py +++ b/include/py/homekit/config/config.py @@ -158,6 +158,9 @@ class ConfigUnit(BaseConfigUnit): else: normalized = v.validated(self._data, schema) + if not normalized: + raise cerberus.DocumentError(f'validation failed: {v.errors}') + self._data = normalized try: diff --git a/include/py/homekit/database/sqlite.py b/include/py/homekit/database/sqlite.py index 0af1f54..8b0c44c 100644 --- a/include/py/homekit/database/sqlite.py +++ b/include/py/homekit/database/sqlite.py @@ -15,10 +15,13 @@ def _get_database_path(name: str) -> str: class SQLiteBase: SCHEMA = 1 - def __init__(self, name=None, check_same_thread=False): - if name is None: - name = config.app_config['database_name'] - database_path = _get_database_path(name) + def __init__(self, name=None, path=None, check_same_thread=False): + if not path: + if not name: + name = config.app_config['database_name'] + database_path = _get_database_path(name) + else: + database_path = path if not os.path.exists(os.path.dirname(database_path)): os.makedirs(os.path.dirname(database_path)) diff --git a/misc/home_linux_boards/etc/default/homekit_ipcam_server b/misc/home_linux_boards/etc/default/homekit_ipcam_server new file mode 100644 index 0000000..e5ee2a3 --- /dev/null +++ b/misc/home_linux_boards/etc/default/homekit_ipcam_server @@ -0,0 +1,2 @@ +LISTEN="0.0.0.0:8320" +DATABASE_PATH="/data1/ipcam_server.db"
\ No newline at end of file diff --git a/systemd/ipcam_server.service b/systemd/ipcam_server.service index e6f8918..53e588d 100644 --- a/systemd/ipcam_server.service +++ b/systemd/ipcam_server.service @@ -1,5 +1,5 @@ [Unit] -Description=HomeKit IPCam Server +Description=Homekit IPCam Server After=network-online.target [Service] @@ -7,7 +7,8 @@ User=user Group=user Restart=always RestartSec=10 -ExecStart=/home/user/homekit/bin/ipcam_server.py +EnvironmentFile=/etc/default/homekit_ipcam_server +ExecStart=/home/user/homekit/bin/ipcam_server.py --listen "$LISTEN" --database-path "$DATABASE_PATH" WorkingDirectory=/home/user [Install] diff --git a/test/test.py b/test/test.py index 267a19f..0c4a347 100755 --- a/test/test.py +++ b/test/test.py @@ -1,8 +1,10 @@ #!/usr/bin/env python import __py_include -from homekit.relay import RelayClient + +from pprint import pprint +from homekit.camera.config import IpcamConfig if __name__ == '__main__': - c = RelayClient() - print(c, c._host)
\ No newline at end of file + c = IpcamConfig() + pprint(c.get())
\ No newline at end of file |