ipcam: start porting to new config and multiserver scheme

This commit is contained in:
Evgeny Zinoviev 2023-06-11 05:03:43 +03:00
parent ba321657e0
commit 62ee71fdb0
9 changed files with 125 additions and 16 deletions

2
.gitignore vendored
View File

@ -9,7 +9,7 @@ __pycache__
/include/test/test_inverter_monitor.log /include/test/test_inverter_monitor.log
/youtrack-certificate /youtrack-certificate
/cpp /cpp
/include/test.py /test/test.py
/bin/test.py /bin/test.py
/arduino/ESP32CameraWebServer/wifi_password.h /arduino/ESP32CameraWebServer/wifi_password.h
cmake-build-* cmake-build-*

View File

@ -9,6 +9,7 @@ import __py_include
import homekit.telegram.aio as telegram import homekit.telegram.aio as telegram
from argparse import ArgumentParser
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
from asyncio import Lock from asyncio import Lock
@ -53,8 +54,8 @@ def get_all_cams() -> list:
class IPCamServerDatabase(SQLiteBase): class IPCamServerDatabase(SQLiteBase):
SCHEMA = 4 SCHEMA = 4
def __init__(self): def __init__(self, path=None):
super().__init__() super().__init__(path=path)
def schema_init(self, version: int) -> None: def schema_init(self, version: int) -> None:
cursor = self.cursor() cursor = self.cursor()
@ -319,9 +320,9 @@ class IPCamWebServer(http.HTTPServer):
# other global stuff # other global stuff
# ------------------ # ------------------
def open_database(): def open_database(database_path: str):
global db global db
db = IPCamServerDatabase() db = IPCamServerDatabase(database_path)
# update cams list in database, if needed # update cams list in database, if needed
cams = db.get_all_timestamps().keys() cams = db.get_all_timestamps().keys()
@ -558,9 +559,12 @@ logger = logging.getLogger(__name__)
# -------------------- # --------------------
if __name__ == '__main__': 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() loop = asyncio.get_event_loop()

View File

@ -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'}
}
}

View File

@ -3,3 +3,15 @@ from enum import Enum
class CameraType(Enum): class CameraType(Enum):
ESP32 = 'esp32' ESP32 = 'esp32'
ALIEXPRESS_NONAME = 'ali'
HIKVISION = 'hik'
class VideoContainerType(Enum):
MP4 = 'mp4'
MOV = 'mov'
class VideoCodecType(Enum):
H264 = 'h264'
H265 = 'h265'

View File

@ -158,6 +158,9 @@ class ConfigUnit(BaseConfigUnit):
else: else:
normalized = v.validated(self._data, schema) normalized = v.validated(self._data, schema)
if not normalized:
raise cerberus.DocumentError(f'validation failed: {v.errors}')
self._data = normalized self._data = normalized
try: try:

View File

@ -15,10 +15,13 @@ def _get_database_path(name: str) -> str:
class SQLiteBase: class SQLiteBase:
SCHEMA = 1 SCHEMA = 1
def __init__(self, name=None, check_same_thread=False): def __init__(self, name=None, path=None, check_same_thread=False):
if name is None: if not path:
name = config.app_config['database_name'] if not name:
database_path = _get_database_path(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)): if not os.path.exists(os.path.dirname(database_path)):
os.makedirs(os.path.dirname(database_path)) os.makedirs(os.path.dirname(database_path))

View File

@ -0,0 +1,2 @@
LISTEN="0.0.0.0:8320"
DATABASE_PATH="/data1/ipcam_server.db"

View File

@ -1,5 +1,5 @@
[Unit] [Unit]
Description=HomeKit IPCam Server Description=Homekit IPCam Server
After=network-online.target After=network-online.target
[Service] [Service]
@ -7,7 +7,8 @@ User=user
Group=user Group=user
Restart=always Restart=always
RestartSec=10 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 WorkingDirectory=/home/user
[Install] [Install]

View File

@ -1,8 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
import __py_include import __py_include
from homekit.relay import RelayClient
from pprint import pprint
from homekit.camera.config import IpcamConfig
if __name__ == '__main__': if __name__ == '__main__':
c = RelayClient() c = IpcamConfig()
print(c, c._host) pprint(c.get())