Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

aroundthecode / python-miio   python

Repository URL to install this package:

Version: 0.4.4 

/ tests / test_airconditioningcompanion.py

import string
import json
import os
from unittest import TestCase

import pytest

from miio import AirConditioningCompanion, AirConditioningCompanionV3
from miio.airconditioningcompanion import (OperationMode, FanSpeed, Power,
                                           SwingMode, Led,
                                           AirConditioningCompanionStatus,
                                           AirConditioningCompanionException,
                                           STORAGE_SLOT_ID,
                                           MODEL_ACPARTNER_V3,
                                           )

STATE_ON = ['on']
STATE_OFF = ['off']

PUBLIC_ENUMS = {
    'OperationMode': OperationMode,
    'FanSpeed': FanSpeed,
    'Power': Power,
    'SwingMode': SwingMode,
    'Led': Led,
}


def as_enum(d):
    if "__enum__" in d:
        name, member = d["__enum__"].split(".")
        return getattr(PUBLIC_ENUMS[name], member)
    else:
        return d


with open(os.path.join(os.path.dirname(__file__),
                       'test_airconditioningcompanion.json')) as inp:
    test_data = json.load(inp, object_hook=as_enum)


class EnumEncoder(json.JSONEncoder):
    def default(self, obj):
        if type(obj) in PUBLIC_ENUMS.values():
            return {"__enum__": str(obj)}
        return json.JSONEncoder.default(self, obj)


class DummyAirConditioningCompanion(AirConditioningCompanion):
    def __init__(self, *args, **kwargs):
        self.state = ['010500978022222102', '01020119A280222221', '2']
        self.last_ir_played = None

        self.return_values = {
            'get_model_and_state': self._get_state,
            'start_ir_learn': lambda x: True,
            'end_ir_learn': lambda x: True,
            'get_ir_learn_result': lambda x: True,
            'send_ir_code': lambda x: self._send_input_validation(x),
            'send_cmd': lambda x: self._send_input_validation(x),
            'set_power': lambda x: self._set_power(x),
        }
        self.start_state = self.state.copy()

    def send(self, command: str, parameters=None, retry_count=3):
        """Overridden send() to return values from `self.return_values`."""
        return self.return_values[command](parameters)

    def _reset_state(self):
        """Revert back to the original state."""
        self.state = self.start_state.copy()

    def _get_state(self, props):
        """Return the requested data"""
        return self.state

    def _set_power(self, value: str):
        """Set the requested power state"""
        if value == STATE_ON:
            self.state[1] = self.state[1][:2] + '1' + self.state[1][3:]

        if value == STATE_OFF:
            self.state[1] = self.state[1][:2] + '0' + self.state[1][3:]

    @staticmethod
    def _hex_input_validation(payload):
        return all(c in string.hexdigits for c in payload[0])

    def _send_input_validation(self, payload):
        if self._hex_input_validation(payload[0]):
            self.last_ir_played = payload[0]
            return True

        return False

    def get_last_ir_played(self):
        return self.last_ir_played


@pytest.fixture(scope="class")
def airconditioningcompanion(request):
    request.cls.device = DummyAirConditioningCompanion()
    # TODO add ability to test on a real device


@pytest.mark.usefixtures("airconditioningcompanion")
class TestAirConditioningCompanion(TestCase):
    def is_on(self):
        return self.device.status().is_on

    def state(self):
        return self.device.status()

    def test_on(self):
        self.device.off()  # ensure off
        assert self.is_on() is False

        self.device.on()
        assert self.is_on() is True

    def test_off(self):
        self.device.on()  # ensure on
        assert self.is_on() is True

        self.device.off()
        assert self.is_on() is False

    def test_status(self):
        self.device._reset_state()

        assert repr(self.state()) == repr(AirConditioningCompanionStatus(dict(
            model_and_state=self.device.start_state)))

        assert self.is_on() is False
        assert self.state().power_socket is None
        assert self.state().load_power == 2
        assert self.state().air_condition_model == \
            bytes.fromhex('010500978022222102')
        assert self.state().model_format == 1
        assert self.state().device_type == 5
        assert self.state().air_condition_brand == 97
        assert self.state().air_condition_remote == 80222221
        assert self.state().state_format == 2
        assert self.state().air_condition_configuration == '020119A2'
        assert self.state().target_temperature == 25
        assert self.state().swing_mode == SwingMode.Off
        assert self.state().fan_speed == FanSpeed.Low
        assert self.state().mode == OperationMode.Auto
        assert self.state().led is False

    def test_status_without_target_temperature(self):
        self.device._reset_state()
        self.device.state[1] = None
        assert self.state().target_temperature is None

    def test_status_without_swing_mode(self):
        self.device._reset_state()
        self.device.state[1] = None
        assert self.state().swing_mode is None

    def test_status_without_mode(self):
        self.device._reset_state()
        self.device.state[1] = None
        assert self.state().mode is None

    def test_status_without_fan_speed(self):
        self.device._reset_state()
        self.device.state[1] = None
        assert self.state().fan_speed is None

    def test_learn(self):
        assert self.device.learn(STORAGE_SLOT_ID) is True
        assert self.device.learn() is True

    def test_learn_result(self):
        assert self.device.learn_result() is True

    def test_learn_stop(self):
        assert self.device.learn_stop(STORAGE_SLOT_ID) is True
        assert self.device.learn_stop() is True

    def test_send_ir_code(self):
        for args in test_data['test_send_ir_code_ok']:
            with self.subTest():
                self.device._reset_state()
                self.assertTrue(self.device.send_ir_code(*args['in']))
                self.assertSequenceEqual(
                    self.device.get_last_ir_played(),
                    args['out']
                )

        for args in test_data['test_send_ir_code_exception']:
            with pytest.raises(AirConditioningCompanionException):
                self.device.send_ir_code(*args['in'])

    def test_send_command(self):
        assert self.device.send_command('0000000') is True

    def test_send_configuration(self):

        for args in test_data['test_send_configuration_ok']:
            with self.subTest():
                self.device._reset_state()
                self.assertTrue(self.device.send_configuration(*args['in']))
                self.assertSequenceEqual(
                    self.device.get_last_ir_played(),
                    args['out']
                )


class DummyAirConditioningCompanionV3(AirConditioningCompanionV3):
    def __init__(self, *args, **kwargs):
        self.state = ['010507950000257301', '011001160100002573', '807']
        self.device_prop = {'lumi.0': {'plug_state': 'on'}}
        self.model = MODEL_ACPARTNER_V3
        self.last_ir_played = None

        self.return_values = {
            'get_model_and_state': self._get_state,
            'get_device_prop': self._get_device_prop,
            'toggle_plug': self._toggle_plug,
        }
        self.start_state = self.state.copy()
        self.start_device_prop = self.device_prop.copy()

    def send(self, command: str, parameters=None, retry_count=3):
        """Overridden send() to return values from `self.return_values`."""
        return self.return_values[command](parameters)

    def _reset_state(self):
        """Revert back to the original state."""
        self.state = self.start_state.copy()

    def _get_state(self, props):
        """Return the requested data"""
        return self.state

    def _get_device_prop(self, props):
        """Return the requested data"""
        return self.device_prop[props[0]][props[1]]

    def _toggle_plug(self, props):
        """Toggle the lumi.0 plug state"""
        self.device_prop['lumi.0']['plug_state'] = props.pop()


@pytest.fixture(scope="class")
def airconditioningcompanionv3(request):
    request.cls.device = DummyAirConditioningCompanionV3()
    # TODO add ability to test on a real device


@pytest.mark.usefixtures("airconditioningcompanionv3")
class TestAirConditioningCompanionV3(TestCase):
    def state(self):
        return self.device.status()

    def is_on(self):
        return self.device.status().is_on

    def test_socket_on(self):
        self.device.socket_off()  # ensure off
        assert self.state().power_socket == 'off'

        self.device.socket_on()
        assert self.state().power_socket == 'on'

    def test_socket_off(self):
        self.device.socket_on()  # ensure on
        assert self.state().power_socket == 'on'

        self.device.socket_off()
        assert self.state().power_socket == 'off'

    def test_status(self):
        self.device._reset_state()

        assert repr(self.state()) == repr(AirConditioningCompanionStatus(dict(
            model_and_state=self.device.start_state,
            power_socket=self.device.start_device_prop['lumi.0']['plug_state'])
        ))

        assert self.is_on() is True
        assert self.state().power_socket == 'on'
        assert self.state().load_power == 807
        assert self.state().air_condition_model == \
            bytes.fromhex('010507950000257301')
        assert self.state().model_format == 1
        assert self.state().device_type == 5
        assert self.state().air_condition_brand == 795
        assert self.state().air_condition_remote == 2573
        assert self.state().state_format == 1
        assert self.state().air_condition_configuration == '10011601'
        assert self.state().target_temperature == 22
        assert self.state().swing_mode == SwingMode.Off
        assert self.state().fan_speed == FanSpeed.Low
        assert self.state().mode == OperationMode.Heat
        assert self.state().led is True