Source code for miio.integrations.cgllc.airmonitor.airqualitymonitor_miot

import enum
import logging

import click

from miio.click_common import command, format_output
from miio.miot_device import DeviceStatus, MiotDevice

_LOGGER = logging.getLogger(__name__)

MODEL_AIRQUALITYMONITOR_CGDN1 = "cgllc.airm.cgdn1"

_MAPPINGS = {
    MODEL_AIRQUALITYMONITOR_CGDN1: {
        # Source https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-cgdn1:1
        # Environment
        "humidity": {"siid": 3, "piid": 1},  # [0, 100] step 1
        "pm25": {"siid": 3, "piid": 4},  # [0, 1000] step 1
        "pm10": {"siid": 3, "piid": 5},  # [0, 1000] step 1
        "temperature": {"siid": 3, "piid": 7},  # [-30, 100] step 0.00001
        "co2": {"siid": 3, "piid": 8},  # [0, 9999] step 1
        # Battery
        "battery": {"siid": 4, "piid": 1},  # [0, 100] step 1
        "charging_state": {
            "siid": 4,
            "piid": 2,
        },  # 1 - Charging, 2 - Not charging, 3 - Not chargeable
        "voltage": {"siid": 4, "piid": 3},  # [0, 65535] step 1
        # Settings
        "start_time": {"siid": 9, "piid": 2},  # [0, 2147483647] step 1
        "end_time": {"siid": 9, "piid": 3},  # [0, 2147483647] step 1
        "monitoring_frequency": {
            "siid": 9,
            "piid": 4,
        },  # 1, 60, 300, 600, 0; device accepts [0..600]
        "screen_off": {
            "siid": 9,
            "piid": 5,
        },  # 15, 30, 60, 300, 0; device accepts [0..300], 0 means never
        "device_off": {
            "siid": 9,
            "piid": 6,
        },  # 15, 30, 60, 0; device accepts [0..60], 0 means never
        "temperature_unit": {"siid": 9, "piid": 7},
    }
}


[docs] class ChargingState(enum.Enum): Unplugged = 0 # Not mentioned in the spec Charging = 1 NotCharging = 2 NotChargable = 3
[docs] class MonitoringFrequencyCGDN1(enum.Enum): # Official spec options Every1Second = 1 Every1Minute = 60 Every5Minutes = 300 Every10Minutes = 600 NotSet = 0
[docs] class ScreenOffCGDN1(enum.Enum): # Official spec options After15Seconds = 15 After30Seconds = 30 After1Minute = 60 After5Minutes = 300 Never = 0
[docs] class DeviceOffCGDN1(enum.Enum): # Official spec options After15Minutes = 15 After30Minutes = 30 After1Hour = 60 Never = 0
[docs] class DisplayTemperatureUnitCGDN1(enum.Enum): Celcius = "c" Fahrenheit = "f"
[docs] class AirQualityMonitorCGDN1Status(DeviceStatus): """Container of air quality monitor CGDN1 status. Example:: { 'humidity': 34, 'pm25': 18, 'pm10': 21, 'temperature': 22.8, 'co2': 468, 'battery': 37, 'charging_state': 0, 'voltage': 3564, 'start_time': 0, 'end_time': 0, 'monitoring_frequency': 1, 'screen_off': 300, 'device_off': 60, 'temperature_unit': 'c' } """ def __init__(self, data): self.data = data @property def humidity(self) -> int: """Return humidity value (0...100%).""" return self.data["humidity"] @property def pm25(self) -> int: """Return PM 2.5 value (0...1000ppm).""" return self.data["pm25"] @property def pm10(self) -> int: """Return PM 10 value (0...1000ppm).""" return self.data["pm10"] @property def temperature(self) -> float: """Return temperature value (-30...100°C).""" return self.data["temperature"] @property def co2(self) -> int: """Return co2 value (0...9999ppm).""" return self.data["co2"] @property def battery(self) -> int: """Return battery level (0...100%).""" return self.data["battery"] @property def charging_state(self) -> ChargingState: """Return charging state.""" return ChargingState(self.data["charging_state"]) @property def monitoring_frequency(self) -> int: """Return monitoring frequency time (0..600 s).""" return self.data["monitoring_frequency"] @property def screen_off(self) -> int: """Return screen off time (0..300 s).""" return self.data["screen_off"] @property def device_off(self) -> int: """Return device off time (0..60 min).""" return self.data["device_off"] @property def display_temperature_unit(self): """Return display temperature unit.""" return DisplayTemperatureUnitCGDN1(self.data["temperature_unit"])
[docs] class AirQualityMonitorCGDN1(MiotDevice): """Qingping Air Monitor Lite.""" _mappings = _MAPPINGS
[docs] @command( default_output=format_output( "", "Humidity: {result.humidity} %\n" "PM 2.5: {result.pm25} μg/m³\n" "PM 10: {result.pm10} μg/m³\n" "Temperature: {result.temperature} °C\n" "CO₂: {result.co2} μg/m³\n" "Battery: {result.battery} %\n" "Charging state: {result.charging_state.name}\n" "Monitoring frequency: {result.monitoring_frequency} s\n" "Screen off: {result.screen_off} s\n" "Device off: {result.device_off} min\n" "Display temperature unit: {result.display_temperature_unit.name}\n", ) ) def status(self) -> AirQualityMonitorCGDN1Status: """Retrieve properties.""" return AirQualityMonitorCGDN1Status( { prop["did"]: prop["value"] if prop["code"] == 0 else None for prop in self.get_properties_for_mapping() } )
[docs] @command( click.argument("duration", type=int), default_output=format_output("Setting monitoring frequency to {duration} s"), ) def set_monitoring_frequency_duration(self, duration): """Set monitoring frequency.""" if duration < 0 or duration > 600: raise ValueError( "Invalid duration: %s. Must be between 0 and 600" % duration ) return self.set_property("monitoring_frequency", duration)
[docs] @command( click.argument("duration", type=int), default_output=format_output("Setting device off duration to {duration} min"), ) def set_device_off_duration(self, duration): """Set device off duration.""" if duration < 0 or duration > 60: raise ValueError( "Invalid duration: %s. Must be between 0 and 60" % duration ) return self.set_property("device_off", duration)
[docs] @command( click.argument("duration", type=int), default_output=format_output("Setting screen off duration to {duration} s"), ) def set_screen_off_duration(self, duration): """Set screen off duration.""" if duration < 0 or duration > 300: raise ValueError( "Invalid duration: %s. Must be between 0 and 300" % duration ) return self.set_property("screen_off", duration)
[docs] @command( click.argument( "unit", type=click.Choice(DisplayTemperatureUnitCGDN1.__members__), callback=lambda c, p, v: getattr(DisplayTemperatureUnitCGDN1, v), ), default_output=format_output("Setting display temperature unit to {unit.name}"), ) def set_display_temperature_unit(self, unit: DisplayTemperatureUnitCGDN1): """Set display temperature unit.""" return self.set_property("temperature_unit", unit.value)