Source code for miio.integrations.chuangmi.camera.chuangmi_camera

"""Xiaomi Chuangmi camera (chuangmi.camera.ipc009, ipc013, ipc019, 038a2) support."""

import enum
import ipaddress
import logging
import socket
from typing import Any, Dict
from urllib.parse import urlparse

import click

from miio.click_common import EnumType, command, format_output
from miio.device import Device, DeviceStatus

_LOGGER = logging.getLogger(__name__)


[docs] class Direction(enum.Enum): """Rotation direction.""" Left = 1 Right = 2 Up = 3 Down = 4
[docs] class MotionDetectionSensitivity(enum.IntEnum): """Motion detection sensitivity.""" High = 3 Low = 1
[docs] class HomeMonitoringMode(enum.IntEnum): """Home monitoring mode.""" Off = 0 AllDay = 1 Custom = 2
[docs] class NASState(enum.IntEnum): """NAS state.""" Off = 2 On = 3
[docs] class NASSyncInterval(enum.IntEnum): """NAS sync interval.""" Realtime = 300 Hour = 3600 Day = 86400
[docs] class NASVideoRetentionTime(enum.IntEnum): """NAS video retention time.""" Week = 604800 Month = 2592000 Quarter = 7776000 HalfYear = 15552000 Year = 31104000
CONST_HIGH_SENSITIVITY = [MotionDetectionSensitivity.High] * 32 CONST_LOW_SENSITIVITY = [MotionDetectionSensitivity.Low] * 32 SUPPORTED_MODELS = [ "chuangmi.camera.ipc009", "chuangmi.camera.ipc013", "chuangmi.camera.ipc019", "chuangmi.camera.021a04", "chuangmi.camera.038a2", ]
[docs] class CameraStatus(DeviceStatus): """Container for status reports from the Xiaomi Chuangmi Camera.""" def __init__(self, data: Dict[str, Any]) -> None: """ Request: ["power", "motion_record", "light", "full_color", "flip", "improve_program", "wdr", "track", "sdcard_status", "watermark", "max_client", "night_mode", "mini_level"] Response: ["on","on","on","on","off","on","on","off","0","off","0","0","1"] """ self.data = data @property def power(self) -> bool: """Camera power.""" return self.data["power"] == "on" @property def motion_record(self) -> bool: """Motion record status.""" return self.data["motion_record"] == "on" @property def light(self) -> bool: """Camera light status.""" return self.data["light"] == "on" @property def full_color(self) -> bool: """Full color with bad lighting conditions.""" return self.data["full_color"] == "on" @property def flip(self) -> bool: """Image 180 degrees flip status.""" return self.data["flip"] == "on" @property def improve_program(self) -> bool: """Customer experience improvement program status.""" return self.data["improve_program"] == "on" @property def wdr(self) -> bool: """Wide dynamic range status.""" return self.data["wdr"] == "on" @property def track(self) -> bool: """Tracking status.""" return self.data["track"] == "on" @property def watermark(self) -> bool: """Apply watermark to video.""" return self.data["watermark"] == "on" @property def sdcard_status(self) -> int: """SD card status.""" return self.data["sdcard_status"] @property def max_client(self) -> int: """Unknown.""" return self.data["max_client"] @property def night_mode(self) -> int: """Night mode.""" return self.data["night_mode"] @property def mini_level(self) -> int: """Unknown.""" return self.data["mini_level"]
[docs] class ChuangmiCamera(Device): """Main class representing the Xiaomi Chuangmi Camera.""" _supported_models = SUPPORTED_MODELS
[docs] @command( default_output=format_output( "", "Power: {result.power}\n" "Motion record: {result.motion_record}\n" "Light: {result.light}\n" "Full color: {result.full_color}\n" "Flip: {result.flip}\n" "Improve program: {result.improve_program}\n" "Wdr: {result.wdr}\n" "Track: {result.track}\n" "SD card status: {result.sdcard_status}\n" "Watermark: {result.watermark}\n" "Max client: {result.max_client}\n" "Night mode: {result.night_mode}\n" "Mini level: {result.mini_level}\n" "\n", ) ) def status(self) -> CameraStatus: """Retrieve properties.""" properties = [ "power", "motion_record", "light", "full_color", "flip", "improve_program", "wdr", "track", "sdcard_status", "watermark", "max_client", "night_mode", "mini_level", ] values = self.get_properties(properties) return CameraStatus(dict(zip(properties, values)))
[docs] @command(default_output=format_output("Power on")) def on(self): """Power on.""" return self.send("set_power", ["on"])
[docs] @command(default_output=format_output("Power off")) def off(self): """Power off.""" return self.send("set_power", ["off"])
[docs] @command(default_output=format_output("MotionRecord on")) def motion_record_on(self): """Start recording when motion detected.""" return self.send("set_motion_record", ["on"])
[docs] @command(default_output=format_output("MotionRecord off")) def motion_record_off(self): """Motion record off, always record video.""" return self.send("set_motion_record", ["off"])
[docs] @command(default_output=format_output("MotionRecord stop")) def motion_record_stop(self): """Motion record off, video recording stopped.""" return self.send("set_motion_record", ["stop"])
[docs] @command(default_output=format_output("Light on")) def light_on(self): """Light on.""" return self.send("set_light", ["on"])
[docs] @command(default_output=format_output("Light off")) def light_off(self): """Light off.""" return self.send("set_light", ["off"])
[docs] @command(default_output=format_output("FullColor on")) def full_color_on(self): """Full color on.""" return self.send("set_full_color", ["on"])
[docs] @command(default_output=format_output("FullColor off")) def full_color_off(self): """Full color off.""" return self.send("set_full_color", ["off"])
[docs] @command(default_output=format_output("Flip on")) def flip_on(self): """Flip image 180 degrees on.""" return self.send("set_flip", ["on"])
[docs] @command(default_output=format_output("Flip off")) def flip_off(self): """Flip image 180 degrees off.""" return self.send("set_flip", ["off"])
[docs] @command(default_output=format_output("ImproveProgram on")) def improve_program_on(self): """Improve program on.""" return self.send("set_improve_program", ["on"])
[docs] @command(default_output=format_output("ImproveProgram off")) def improve_program_off(self): """Improve program off.""" return self.send("set_improve_program", ["off"])
[docs] @command(default_output=format_output("Watermark on")) def watermark_on(self): """Watermark on.""" return self.send("set_watermark", ["on"])
[docs] @command(default_output=format_output("Watermark off")) def watermark_off(self): """Watermark off.""" return self.send("set_watermark", ["off"])
[docs] @command(default_output=format_output("WideDynamicRange on")) def wdr_on(self): """Wide dynamic range on.""" return self.send("set_wdr", ["on"])
[docs] @command(default_output=format_output("WideDynamicRange off")) def wdr_off(self): """Wide dynamic range off.""" return self.send("set_wdr", ["off"])
[docs] @command(default_output=format_output("NightMode auto")) def night_mode_auto(self): """Auto switch to night mode.""" return self.send("set_night_mode", [0])
[docs] @command(default_output=format_output("NightMode off")) def night_mode_off(self): """Night mode off.""" return self.send("set_night_mode", [1])
[docs] @command(default_output=format_output("NightMode on")) def night_mode_on(self): """Night mode always on.""" return self.send("set_night_mode", [2])
[docs] @command( click.argument("direction", type=EnumType(Direction)), default_output=format_output("Rotating to direction '{direction.name}'"), ) def rotate(self, direction: Direction): """Rotate camera to given direction (left, right, up, down).""" return self.send("set_motor", {"operation": direction.value})
[docs] @command() def alarm(self): """Sound a loud alarm for 10 seconds.""" return self.send("alarm_sound")
[docs] @command( click.argument("sensitivity", type=EnumType(MotionDetectionSensitivity)), default_output=format_output("Setting motion sensitivity '{sensitivity.name}'"), ) def set_motion_sensitivity(self, sensitivity: MotionDetectionSensitivity): """Set motion sensitivity (high, low).""" return self.send( "set_motion_region", ( CONST_HIGH_SENSITIVITY if sensitivity == MotionDetectionSensitivity.High else CONST_LOW_SENSITIVITY ), )
[docs] @command( click.argument("mode", type=EnumType(HomeMonitoringMode)), click.argument("start-hour", default=10), click.argument("start-minute", default=0), click.argument("end-hour", default=17), click.argument("end-minute", default=0), click.argument("notify", default=1), click.argument("interval", default=5), default_output=format_output("Setting alarm config to '{mode.name}'"), ) def set_home_monitoring_config( self, mode: HomeMonitoringMode = HomeMonitoringMode.AllDay, start_hour: int = 10, start_minute: int = 0, end_hour: int = 17, end_minute: int = 0, notify: int = 1, interval: int = 5, ): """Set home monitoring configuration.""" return self.send( "setAlarmConfig", [mode, start_hour, start_minute, end_hour, end_minute, notify, interval], )
[docs] @command(default_output=format_output("Clearing NAS directory")) def clear_nas_dir(self): """Clear NAS directory.""" return self.send("nas_clear_dir", [[]])
[docs] @command(default_output=format_output("Getting NAS config info")) def get_nas_config(self): """Get NAS config info.""" return self.send("nas_get_config", {})
[docs] @command( click.argument("state", type=EnumType(NASState)), click.argument("share", type=str), click.argument("sync-interval", type=EnumType(NASSyncInterval)), click.argument("video-retention-time", type=EnumType(NASVideoRetentionTime)), default_output=format_output("Setting NAS config to '{state.name}'"), ) def set_nas_config( self, state: NASState, share=None, sync_interval: NASSyncInterval = NASSyncInterval.Realtime, video_retention_time: NASVideoRetentionTime = NASVideoRetentionTime.Week, ): """Set NAS configuration.""" params: Dict[str, Any] = { "state": state, "sync_interval": sync_interval, "video_retention_time": video_retention_time, } share = urlparse(share) if share.scheme == "smb": ip = socket.gethostbyname(share.hostname) reversed_ip = ".".join(reversed(ip.split("."))) addr = int(ipaddress.ip_address(reversed_ip)) params["share"] = { "type": 1, "name": share.hostname, "addr": addr, "dir": share.path.lstrip("/"), "group": "WORKGROUP", "user": share.username, "pass": share.password, } return self.send("nas_set_config", params)