ipcam: start porting to new config and multiserver scheme
This commit is contained in:
parent
ba321657e0
commit
62ee71fdb0
2
.gitignore
vendored
2
.gitignore
vendored
@ -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-*
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
82
include/py/homekit/camera/config.py
Normal file
82
include/py/homekit/camera/config.py
Normal 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'}
|
||||||
|
}
|
||||||
|
}
|
@ -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'
|
||||||
|
@ -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:
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
2
misc/home_linux_boards/etc/default/homekit_ipcam_server
Normal file
2
misc/home_linux_boards/etc/default/homekit_ipcam_server
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
LISTEN="0.0.0.0:8320"
|
||||||
|
DATABASE_PATH="/data1/ipcam_server.db"
|
@ -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]
|
||||||
|
@ -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())
|
Loading…
x
Reference in New Issue
Block a user