aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2023-06-11 05:03:43 +0300
committerEvgeny Zinoviev <me@ch1p.io>2023-06-11 05:03:43 +0300
commit62ee71fdb0eb07adbf0071103617aa96c993fe22 (patch)
tree07a5d7ec63c66efcf3162a388f22f371367000f7
parentba321657e0e724082df206857f80ca08c4d999dc (diff)
ipcam: start porting to new config and multiserver scheme
-rw-r--r--.gitignore2
-rwxr-xr-xbin/ipcam_server.py16
-rw-r--r--include/py/homekit/camera/config.py82
-rw-r--r--include/py/homekit/camera/types.py12
-rw-r--r--include/py/homekit/config/config.py3
-rw-r--r--include/py/homekit/database/sqlite.py11
-rw-r--r--misc/home_linux_boards/etc/default/homekit_ipcam_server2
-rw-r--r--systemd/ipcam_server.service5
-rwxr-xr-xtest/test.py8
9 files changed, 125 insertions, 16 deletions
diff --git a/.gitignore b/.gitignore
index 9a32ecc..b113ef6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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