summaryrefslogtreecommitdiff
path: root/src/home
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2022-05-17 10:38:27 +0300
committerEvgeny Zinoviev <me@ch1p.io>2022-05-17 23:50:21 +0300
commit6f965e85a633d3b7f7ab049076fb506c91275bea (patch)
tree5b3b7dff726483d86a74c7db4875f16c8ffb3fad /src/home
parentf1b52a92201e7240519a5fe23cf9a52df013a910 (diff)
initial camera support (only esp32-cam at the moment)
Diffstat (limited to 'src/home')
-rw-r--r--src/home/bot/wrapper.py3
-rw-r--r--src/home/camera/__init__.py0
-rw-r--r--src/home/camera/esp32.py166
-rw-r--r--src/home/config/config.py15
4 files changed, 178 insertions, 6 deletions
diff --git a/src/home/bot/wrapper.py b/src/home/bot/wrapper.py
index 8651e90..8ebde4f 100644
--- a/src/home/bot/wrapper.py
+++ b/src/home/bot/wrapper.py
@@ -271,6 +271,9 @@ class Wrapper:
text = exc2text(text)
self.updater.bot.send_message(chat_id=user_id, text=text, parse_mode='HTML')
+ def send_photo(self, user_id, **kwargs):
+ self.updater.bot.send_photo(chat_id=user_id, **kwargs)
+
def send_audio(self, user_id, **kwargs):
self.updater.bot.send_audio(chat_id=user_id, **kwargs)
diff --git a/src/home/camera/__init__.py b/src/home/camera/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/home/camera/__init__.py
diff --git a/src/home/camera/esp32.py b/src/home/camera/esp32.py
new file mode 100644
index 0000000..ef0ec53
--- /dev/null
+++ b/src/home/camera/esp32.py
@@ -0,0 +1,166 @@
+import logging
+import shutil
+import requests
+import json
+
+from typing import Union, Optional
+from time import sleep
+from enum import Enum
+from ..api.errors import ApiResponseError
+from ..util import Addr
+
+
+class FrameSize(Enum):
+ UXGA_1600x1200 = 13
+ SXGA_1280x1024 = 12
+ HD_1280x720 = 11
+ XGA_1024x768 = 10
+ SVGA_800x600 = 9
+ VGA_640x480 = 8
+ HVGA_480x320 = 7
+ CIF_400x296 = 6
+ QVGA_320x240 = 5
+ N_240x240 = 4
+ HQVGA_240x176 = 3
+ QCIF_176x144 = 2
+ QQVGA_160x120 = 1
+ N_96x96 = 0
+
+
+class WBMode(Enum):
+ AUTO = 0
+ SUNNY = 1
+ CLOUDY = 2
+ OFFICE = 3
+ HOME = 4
+
+
+def _assert_bounds(n: int, min: int, max: int):
+ if not min <= n <= max:
+ raise ValueError(f'value must be between {min} and {max}')
+
+
+class WebClient:
+ def __init__(self, addr: Addr):
+ self.endpoint = f'http://{addr[0]}:{addr[1]}'
+ self.logger = logging.getLogger(self.__class__.__name__)
+ self.delay = 0
+ self.isfirstrequest = True
+
+ def setdelay(self, delay: int):
+ self.delay = delay
+
+ def capture(self, save_to: str):
+ self._call('capture', save_to=save_to)
+
+ def getstatus(self):
+ return json.loads(self._call('status'))
+
+ def setflash(self, enable: bool):
+ self._control('flash', int(enable))
+
+ def setresolution(self, fs: FrameSize):
+ self._control('framesize', fs.value)
+
+ def sethmirror(self, enable: bool):
+ self._control('hmirror', int(enable))
+
+ def setvflip(self, enable: bool):
+ self._control('vflip', int(enable))
+
+ def setawb(self, enable: bool):
+ self._control('awb', int(enable))
+
+ def setawbgain(self, enable: bool):
+ self._control('awb_gain', int(enable))
+
+ def setwbmode(self, mode: WBMode):
+ self._control('wb_mode', mode.value)
+
+ def setaecsensor(self, enable: bool):
+ self._control('aec', int(enable))
+
+ def setaecdsp(self, enable: bool):
+ self._control('aec2', int(enable))
+
+ def setagc(self, enable: bool):
+ self._control('agc', int(enable))
+
+ def setagcgain(self, gain: int):
+ _assert_bounds(gain, 1, 31)
+ self._control('agc_gain', gain)
+
+ def setgainceiling(self, gainceiling: int):
+ _assert_bounds(gainceiling, 2, 128)
+ self._control('gainceiling', gainceiling)
+
+ def setbpc(self, enable: bool):
+ self._control('bpc', int(enable))
+
+ def setwpc(self, enable: bool):
+ self._control('wpc', int(enable))
+
+ def setrawgma(self, enable: bool):
+ self._control('raw_gma', int(enable))
+
+ def setlenscorrection(self, enable: bool):
+ self._control('lenc', int(enable))
+
+ def setdcw(self, enable: bool):
+ self._control('dcw', int(enable))
+
+ def setcolorbar(self, enable: bool):
+ self._control('colorbar', int(enable))
+
+ def setquality(self, q: int):
+ _assert_bounds(q, 4, 63)
+ self._control('quality', q)
+
+ def setbrightness(self, brightness: int):
+ _assert_bounds(brightness, -2, -2)
+ self._control('brightness', brightness)
+
+ def setcontrast(self, contrast: int):
+ _assert_bounds(contrast, -2, 2)
+ self._control('contrast', contrast)
+
+ def setsaturation(self, saturation: int):
+ _assert_bounds(saturation, -2, 2)
+ self._control('saturation', saturation)
+
+ def _control(self, var: str, value: Union[int, str]):
+ self._call('control', params={'var': var, 'val': value})
+
+ def _call(self,
+ method: str,
+ params: Optional[dict] = None,
+ save_to: Optional[str] = None):
+
+ if not self.isfirstrequest and self.delay > 0:
+ sleeptime = self.delay / 1000
+ self.logger.debug(f'sleeping for {sleeptime}')
+
+ sleep(sleeptime)
+
+ self.isfirstrequest = False
+
+ url = f'{self.endpoint}/{method}'
+ self.logger.debug(f'calling {url}, params: {params}')
+
+ kwargs = {}
+ if params:
+ kwargs['params'] = params
+ if save_to:
+ kwargs['stream'] = True
+
+ r = requests.get(url, **kwargs)
+ if r.status_code != 200:
+ raise ApiResponseError(status_code=r.status_code)
+
+ if save_to:
+ r.raise_for_status()
+ with open(save_to, 'wb') as f:
+ shutil.copyfileobj(r.raw, f)
+ return True
+
+ return r.text
diff --git a/src/home/config/config.py b/src/home/config/config.py
index 75cfc3a..40aa476 100644
--- a/src/home/config/config.py
+++ b/src/home/config/config.py
@@ -37,20 +37,23 @@ class ConfigStore:
log_default_fmt = False
log_file = None
log_verbose = False
+ no_config = name is False
path = None
if use_cli:
if parser is None:
parser = ArgumentParser()
- parser.add_argument('--config', type=str, required=name is None,
- help='Path to the config in TOML format')
- parser.add_argument('--verbose', action='store_true')
+ if not no_config:
+ parser.add_argument('-c', '--config', type=str, required=name is None,
+ help='Path to the config in TOML format')
+ parser.add_argument('-V', '--verbose', action='store_true')
parser.add_argument('--log-file', type=str)
parser.add_argument('--log-default-fmt', action='store_true')
args = parser.parse_args()
- if args.config:
+ if not no_config and args.config:
path = args.config
+
if args.verbose:
log_verbose = True
if args.log_file:
@@ -58,10 +61,10 @@ class ConfigStore:
if args.log_default_fmt:
log_default_fmt = args.log_default_fmt
- if name and path is None:
+ if not no_config and path is None:
path = _get_config_path(name)
- self.data = toml.load(path)
+ self.data = {} if no_config else toml.load(path)
if 'logging' in self:
if not log_file and 'file' in self['logging']: