Source code for platypush.plugins.camera.android.ipcam

import json
import os
import requests

from requests.auth import HTTPBasicAuth
from typing import Optional, Sequence, Union, Dict, List, Any

from platypush.plugins import Plugin, action
from platypush.schemas.camera.android.ipcam import CameraStatusSchema


[docs] class AndroidIpcam: """ IPCam camera configuration. """ args = {}
[docs] def __init__( self, name: str, host: str, port: int = 8080, username: Optional[str] = None, password: Optional[str] = None, timeout: int = 10, ssl: bool = True, ): self.args = { 'name': name, 'host': host, 'port': port, 'username': username, 'password': password, 'timeout': timeout, 'ssl': ssl, } self.auth = None if username: self.auth = HTTPBasicAuth(self.username, self.password)
def __getattr__(self, item): if item in self.args: return super().__getattribute__('args').get(item) return super().__getattribute__(item) def __setattr__(self, key, value): if key not in self.args: super().__setattr__(key, value) else: self.args[key] = value def __str__(self): return json.dumps(self.args or {}) @property def base_url(self) -> str: return 'http{ssl}://{host}:{port}/'.format( ssl=('s' if self.ssl else ''), host=self.host, port=self.port ) @property def stream_url(self) -> str: return self.base_url + 'video' @property def audio_url(self) -> str: return self.base_url + 'audio.wav' @property def image_url(self) -> str: return self.base_url + 'photo.jpg'
[docs] class CameraAndroidIpcamPlugin(Plugin): """ Plugin to control remote Android cameras over `IPCam <https://play.google.com/store/apps/details?id=com.pas.webcam>`_. """
[docs] def __init__( self, name: Optional[str] = None, host: Optional[str] = None, port: int = 8080, username: Optional[str] = None, password: Optional[str] = None, timeout: int = 10, ssl: bool = True, cameras: Optional[Sequence[dict]] = None, **kwargs, ): """ :param name: Custom name for the default camera (default: IP/hostname) :param host: Camera host name or address :param port: Camera port :param username: Camera username, if set :param password: Camera password, if set :param timeout: Connection timeout :param ssl: Use HTTPS instead of HTTP :param cameras: Alternatively, you can specify a list of IPCam cameras as a list of objects with ``name``, ``host``, ``port``, ``username``, ``password``, ``timeout`` and ``ssl`` attributes. """ super().__init__(**kwargs) self.cameras: List[AndroidIpcam] = [] self._camera_name_to_idx: Dict[str, int] = {} if not cameras: assert host, 'You need to specify at least one camera' name = name or host camera = AndroidIpcam( name=name, host=host, port=port, username=username, password=password, timeout=timeout, ssl=ssl, ) self.cameras.append(camera) self._camera_name_to_idx[name] = 0 else: for camera in cameras: assert 'host' in camera, 'You need to specify the host for each camera' name = camera.get('name', camera['host']) camera = AndroidIpcam( name=name, host=camera['host'], port=camera.get('port', port), username=camera.get('username'), password=camera.get('password'), timeout=camera.get('timeout', timeout), ssl=camera.get('ssl', ssl), ) self._camera_name_to_idx[name] = len(self.cameras) self.cameras.append(camera)
def _get_camera(self, camera: Optional[Union[int, str]] = None) -> AndroidIpcam: if not camera: camera = 0 if isinstance(camera, int): return self.cameras[camera] return self.cameras[self._camera_name_to_idx[camera]] def _exec( self, url: str, *args, camera: Optional[Union[int, str]] = None, **kwargs ) -> Union[Dict[str, Any], bool]: cam = self._get_camera(camera) url = cam.base_url + url response = requests.get( url, auth=cam.auth, timeout=cam.timeout, verify=False, *args, **kwargs ) response.raise_for_status() if response.headers.get('content-type') == 'application/json': return response.json() return response.text.find('Ok') != -1 def _change_setting( self, key: str, value: Union[str, int, bool], camera: Optional[Union[int, str]] = None, ) -> bool: if isinstance(value, bool): payload = "on" if value else "off" else: payload = value return bool( self._exec( "settings/{key}?set={payload}".format(key=key, payload=payload), camera=camera, ) )
[docs] @action def change_setting( self, key: str, value: Union[str, int, bool], camera: Optional[Union[int, str]] = None, ) -> bool: """ Change a setting. :param key: Setting name :param value: Setting value :param camera: Camera index or configured name :return: True on success, False otherwise """ return self._change_setting(key, value, camera=camera)
[docs] @action def status(self, camera: Optional[Union[int, str]] = None) -> List[dict]: """ :param camera: Camera index or name (default: status of all the cameras) :return: .. schema:: camera.android.ipcam.CameraStatusSchema(many=True) """ cameras = self._camera_name_to_idx.keys() if camera is None else [camera] statuses = [] for c in cameras: try: print('****** HERE ******') print(self._camera_name_to_idx) if isinstance(c, int): cam = self.cameras[c] else: cam = self.cameras[self._camera_name_to_idx[c]] response = self._exec( 'status.json', params={'show_avail': 1}, camera=cam.name ) assert isinstance(response, dict), f'Invalid response: {response}' status_data = response.get('curvals', {}) status = CameraStatusSchema().dump( { 'name': cam.name, 'stream_url': cam.stream_url, 'image_url': cam.image_url, 'audio_url': cam.audio_url, **status_data, } ) statuses.append(status) except Exception as e: self.logger.warning( 'Could not get the status of %s: %s: %s', c, type(e), e ) return statuses
[docs] @action def set_front_facing_camera( self, activate: bool = True, camera: Optional[Union[int, str]] = None ) -> bool: """Enable/disable the front-facing camera.""" return self._change_setting('ffc', activate, camera=camera)
[docs] @action def set_torch( self, activate: bool = True, camera: Optional[Union[int, str]] = None ) -> bool: """Enable/disable the torch.""" url = 'enabletorch' if activate else 'disabletorch' return bool(self._exec(url, camera=camera))
[docs] @action def set_focus( self, activate: bool = True, camera: Optional[Union[int, str]] = None ) -> bool: """Enable/disable the focus.""" url = 'focus' if activate else 'nofocus' return bool(self._exec(url, camera=camera))
[docs] @action def start_recording( self, tag: Optional[str] = None, camera: Optional[Union[int, str]] = None ) -> bool: """Start recording.""" params = { 'force': 1, **({'tag': tag} if tag else {}), } return bool(self._exec('startvideo', params=params, camera=camera))
[docs] @action def stop_recording(self, camera: Optional[Union[int, str]] = None) -> bool: """Stop recording.""" return bool(self._exec('stopvideo', params={'force': 1}, camera=camera))
[docs] @action def take_picture( self, image_file: str, camera: Optional[Union[int, str]] = None ) -> dict: """ Take a picture and save it on the local device. :return: dict .. code-block:: json { "image_file": "/path/to/image.jpg" } """ cam = self._get_camera(camera) image_file = os.path.abspath(os.path.expanduser(image_file)) os.makedirs(os.path.dirname(image_file), exist_ok=True) response = requests.get(cam.image_url, auth=cam.auth, verify=False) response.raise_for_status() with open(image_file, 'wb') as f: f.write(response.content) return {'image_file': image_file}
[docs] @action def set_night_vision( self, activate: bool = True, camera: Optional[Union[int, str]] = None ) -> bool: """Enable/disable night vision.""" return self._change_setting('night_vision', activate, camera=camera)
[docs] @action def set_overlay( self, activate: bool = True, camera: Optional[Union[int, str]] = None ) -> bool: """Enable/disable video overlay.""" return self._change_setting('overlay', activate, camera=camera)
[docs] @action def set_gps( self, activate: bool = True, camera: Optional[Union[int, str]] = None ) -> bool: """Enable/disable GPS.""" return self._change_setting('gps_active', activate, camera=camera)
[docs] @action def set_quality( self, quality: int = 100, camera: Optional[Union[int, str]] = None ) -> bool: """Set video quality.""" return self._change_setting('quality', int(quality), camera=camera)
[docs] @action def set_motion_detect( self, activate: bool = True, camera: Optional[Union[int, str]] = None ) -> bool: """Enable/disable motion detect.""" return self._change_setting('motion_detect', activate, camera=camera)
[docs] @action def set_orientation( self, orientation: str = 'landscape', camera: Optional[Union[int, str]] = None ) -> bool: """Set video orientation.""" return self._change_setting('orientation', orientation, camera=camera)
[docs] @action def set_zoom(self, zoom: float, camera: Optional[Union[int, str]] = None) -> bool: """Set the zoom level.""" return bool( self._exec('settings/ptz', params={'zoom': float(zoom)}, camera=camera) )
[docs] @action def set_scenemode( self, scenemode: str = 'auto', camera: Optional[Union[int, str]] = None ) -> bool: """Set video orientation.""" return self._change_setting('scenemode', scenemode, camera=camera)
# vim:sw=4:ts=4:et: