"""Tests for the Device file."""

import pytest

from pysmartthings.capability import Attribute, Capability
from pysmartthings.device import Device, DeviceEntity, DeviceStatus, DeviceType

from .conftest import DEVICE_ID, LOCATION_ID, ROOM_ID
from .utilities import get_json


class TestDevice:
    """Tests for the Device class."""

    @staticmethod
    def test_init():
        """Tests whether the Device class initializes correctly."""
        # Arrange/Act
        device = Device()
        # Assert
        assert device.type == DeviceType.UNKNOWN
        assert device.capabilities == []
        assert device.components == {}

    @staticmethod
    def test_apply_data():
        """Tests the apply data method."""
        # Arrange
        data = get_json('device.json')
        device = Device()
        # Act
        device.apply_data(data)
        # Assert
        assert device.device_id == DEVICE_ID
        assert device.name == 'GE In-Wall Smart Dimmer'
        assert device.label == 'Front Porch Lights'
        assert device.location_id == LOCATION_ID
        assert device.room_id == ROOM_ID
        assert device.type is DeviceType.DTH
        assert device.device_type_id == '8a9d4b1e3b9b1fe3013b9b206a7f000d'
        assert device.device_type_name == 'Dimmer Switch'
        assert device.device_type_network == 'ZWAVE'
        assert device.capabilities == [
            'switch', 'switchLevel', 'refresh', 'indicator', 'sensor',
            'actuator', 'healthCheck', 'light']
        assert device.components == {
            "bottomButton": ['button'],
            "topButton": ['button']
        }

    @staticmethod
    def test_get_capability():
        """Test the capability retrieval method."""
        # Arrange
        data = get_json('device.json')
        device = Device()
        # Act
        device.apply_data(data)
        # Assert
        assert device.get_capability('switch', 'light') == 'switch'
        assert device.get_capability('foo', 'switch') == 'switch'
        assert device.get_capability('foo', 'bar') is None


class TestDeviceEntity:
    """Tests for the DeviceEntity class."""

    @staticmethod
    @pytest.mark.asyncio
    async def test_refresh(api):
        """Tests the refresh method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        await device.refresh()
        # Assert
        assert device.label == 'Front Porch Lights'

    @staticmethod
    @pytest.mark.asyncio
    async def test_save(api):
        """Tests the save method."""
        # Arrange
        device = DeviceEntity(api)
        # Act/Assert
        with pytest.raises(NotImplementedError):
            await device.save()

    @staticmethod
    @pytest.mark.asyncio
    async def test_switch_on(api):
        """Tests the switch_on method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.switch_on()
        # Assert
        assert result
        assert not device.status.switch

    @staticmethod
    @pytest.mark.asyncio
    async def test_switch_on_update(api):
        """Tests the switch_on method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.switch_on(True)
        # Assert
        assert result
        assert device.status.switch

    @staticmethod
    @pytest.mark.asyncio
    async def test_switch_off(api):
        """Tests the switch_on method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.status.switch = True
        # Act
        result = await device.switch_off()
        # Assert
        assert result
        assert device.status.switch

    @staticmethod
    @pytest.mark.asyncio
    async def test_switch_off_update(api):
        """Tests the switch_on method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.status.switch = True
        # Act
        result = await device.switch_off(True)
        # Assert
        assert result
        assert not device.status.switch

    @staticmethod
    @pytest.mark.asyncio
    async def test_lock(api):
        """Tests the lock method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.lock()
        # Assert
        assert result
        assert not device.status.lock

    @staticmethod
    @pytest.mark.asyncio
    async def test_lock_update(api):
        """Tests the lock method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.lock(True)
        # Assert
        assert result
        assert device.status.lock == 'locked'

    @staticmethod
    @pytest.mark.asyncio
    async def test_unlock(api):
        """Tests the unlock method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.unlock()
        # Assert
        assert result
        assert not device.status.lock

    @staticmethod
    @pytest.mark.asyncio
    async def test_unlock_update(api):
        """Tests the unlock method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.unlock(True)
        # Assert
        assert result
        assert device.status.lock == 'unlocked'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_level(api):
        """Tests the set_level method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_level(75, 2)
        # Assert
        assert result
        assert device.status.level == 0
        assert not device.status.switch

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_level_invalid(api):
        """Tests the set_level method invalid values."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Assert level
        levels = [-1, 101]
        for level in levels:
            with pytest.raises(ValueError):
                await device.set_level(level)
        # Assert duration
        with pytest.raises(ValueError):
            await device.set_level(100, -1)

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_level_update(api):
        """Tests the set_level method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_level(75, 2, True)
        # Assert
        assert result
        assert device.status.level == 75
        assert device.status.switch

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_fan_speed(api):
        """Tests the set_fan_speed method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_fan_speed(66)
        # Assert
        assert result
        assert device.status.level == 0
        assert not device.status.switch

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_fan_speed_invalid(api):
        """Tests the set_fan_speed method invalid values."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Assert
        with pytest.raises(ValueError):
            await device.set_fan_speed(-1)

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_fan_speed_update(api):
        """Tests the set_fan_speed method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_fan_speed(66, True)
        # Assert
        assert result
        assert device.status.fan_speed == 66
        assert device.status.switch

    @staticmethod
    def test_status():
        """Tests the set_level method."""
        # Arrange
        device = DeviceEntity(None, device_id=DEVICE_ID)
        # Act
        status = device.status
        # Assert
        assert status.device_id == DEVICE_ID

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color_temperature(api):
        """Tests the set_color_temperature method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_color_temperature(3000)
        # Assert
        assert result
        assert device.status.color_temperature == 1

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color_temperature_invalid(api):
        """Tests the set_color_temperature method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        values = [0, 30001]
        for value in values:
            with pytest.raises(ValueError):
                await device.set_color_temperature(value)

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color_temperature_update(api):
        """Tests the set_color_temperature method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_color_temperature(3000, True)
        # Assert
        assert result
        assert device.status.color_temperature == 3000

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_hue(api):
        """Tests the set_hue method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_hue(75)
        # Assert
        assert result
        assert device.status.hue == 0

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_hue_invalid(api):
        """Tests the set_hue method invalid values."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Assert
        levels = [-1, 101]
        for level in levels:
            with pytest.raises(ValueError):
                await device.set_hue(level)

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_hue_update(api):
        """Tests the set_hue method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_hue(75, True)
        # Assert
        assert result
        assert device.status.hue == 75

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_saturation(api):
        """Tests the set_saturation method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_saturation(75)
        # Assert
        assert result
        assert device.status.saturation == 0

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_saturation_invalid(api):
        """Tests the set_saturation method invalid values."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Assert
        levels = [-1, 101]
        for level in levels:
            with pytest.raises(ValueError):
                await device.set_saturation(level)

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_saturation_update(api):
        """Tests the set_saturation method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_saturation(75, True)
        # Assert
        assert result
        assert device.status.saturation == 75

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color(api):
        """Tests the set_color method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_color(25, 50)
        # Assert
        assert result
        assert device.status.hue == 0
        assert device.status.saturation == 0
        assert device.status.color is None

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color_invalid(api):
        """Tests the set_saturation method invalid values."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Assert
        values = [-1, 101]
        for value in values:
            with pytest.raises(ValueError):
                await device.set_color(value, 0)
            with pytest.raises(ValueError):
                await device.set_color(0, value)

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color_update(api):
        """Tests the set_saturation method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_color(25, 50, set_status=True)
        # Assert
        assert result
        assert device.status.hue == 25
        assert device.status.saturation == 50
        assert device.status.color == '#4B6432'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color_hex(api):
        """Tests the set_color method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_color(color_hex='#4B6432')
        # Assert
        assert result
        assert device.status.hue == 0
        assert device.status.saturation == 0
        assert device.status.color is None

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color_hex_invalid(api):
        """Tests the set_color method invalid values."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Assert
        values = ['000000', '#00000', '#G00000']
        for value in values:
            with pytest.raises(ValueError):
                await device.set_color(color_hex=value)

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_color_update_hex(api):
        """Tests the set_saturation method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act
        result = await device.set_color(color_hex='#4B6432', set_status=True)
        # Assert
        assert result
        assert device.status.hue == 25
        assert device.status.saturation == 50
        assert device.status.color == '#4B6432'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_thermostat_fan_mode_legacy(api):
        """Tests the set_saturation method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat)
        device.status.thermostat_fan_mode = 'on'
        # Act
        result = await device.set_thermostat_fan_mode('auto')
        # Assert
        assert result
        assert device.status.thermostat_fan_mode != 'auto'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_thermostat_fan_mode(api):
        """Tests the set_saturation method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat_fan_mode)
        device.status.thermostat_fan_mode = 'on'
        # Act
        result = await device.set_thermostat_fan_mode('auto')
        # Assert
        assert result
        assert device.status.thermostat_fan_mode != 'auto'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_thermostat_fan_mode_update(api):
        """Tests the set_saturation method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat_fan_mode)
        device.status.thermostat_fan_mode = 'on'
        # Act
        result = await device.set_thermostat_fan_mode('auto', set_status=True)
        # Assert
        assert result
        assert device.status.thermostat_fan_mode == 'auto'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_thermostat_mode_legacy(api):
        """Tests the set_thermostat_mode method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat)
        device.status.thermostat_mode = 'heat'
        # Act
        result = await device.set_thermostat_mode('auto')
        # Assert
        assert result
        assert device.status.thermostat_mode != 'auto'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_thermostat_mode(api):
        """Tests the set_thermostat_mode method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat_mode)
        device.status.thermostat_mode = 'heat'
        # Act
        result = await device.set_thermostat_mode('auto')
        # Assert
        assert result
        assert device.status.thermostat_mode != 'auto'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_thermostat_mode_update(api):
        """Tests the set_thermostat_mode method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat_mode)
        device.status.thermostat_mode = 'heat'
        # Act
        result = await device.set_thermostat_mode('auto', set_status=True)
        # Assert
        assert result
        assert device.status.thermostat_mode == 'auto'

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_cooling_setpoint_legacy(api):
        """Tests the set_cooling_setpoint method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat)
        device.status.cooling_setpoint = 70
        # Act
        result = await device.set_cooling_setpoint(76)
        # Assert
        assert result
        assert device.status.cooling_setpoint != 76

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_cooling_setpoint_mode(api):
        """Tests the set_cooling_setpoint method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat_cooling_setpoint)
        device.status.cooling_setpoint = 70
        # Act
        result = await device.set_cooling_setpoint(76)
        # Assert
        assert result
        assert device.status.cooling_setpoint != 76

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_cooling_setpoint_update(api):
        """Tests the set_cooling_setpoint method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat_cooling_setpoint)
        device.status.cooling_setpoint = 70
        # Act
        result = await device.set_cooling_setpoint(76, set_status=True)
        # Assert
        assert result
        assert device.status.cooling_setpoint == 76

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_heating_setpoint_legacy(api):
        """Tests the set_heating_setpoint method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat)
        device.status.heating_setpoint = 70
        # Act
        result = await device.set_heating_setpoint(76)
        # Assert
        assert result
        assert device.status.heating_setpoint != 76

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_heating_setpoint_mode(api):
        """Tests the set_heating_setpoint method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat_heating_setpoint)
        device.status.heating_setpoint = 70
        # Act
        result = await device.set_heating_setpoint(76)
        # Assert
        assert result
        assert device.status.heating_setpoint != 76

    @staticmethod
    @pytest.mark.asyncio
    async def test_set_heating_setpoint_update(api):
        """Tests the set_heating_setpoint method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.thermostat_heating_setpoint)
        device.status.heating_setpoint = 70
        # Act
        result = await device.set_heating_setpoint(76, set_status=True)
        # Assert
        assert result
        assert device.status.heating_setpoint == 76

    @staticmethod
    @pytest.mark.asyncio
    async def test_open(api):
        """Tests the open method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.door_control)
        # Act/Assert
        assert await device.open()
        assert device.status.door is None
        assert await device.open(set_status=True)
        assert device.status.door == 'opening'

    @staticmethod
    @pytest.mark.asyncio
    async def test_open_legacy(api):
        """Tests the open method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.garage_door_control)
        # Act/Assert
        assert await device.open()
        assert device.status.door is None
        assert await device.open(set_status=True)
        assert device.status.door == 'opening'

    @staticmethod
    @pytest.mark.asyncio
    async def test_close(api):
        """Tests the close method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.door_control)
        # Act/Assert
        assert await device.close()
        assert device.status.door is None
        assert await device.close(set_status=True)
        assert device.status.door == 'closing'

    @staticmethod
    @pytest.mark.asyncio
    async def test_close_legacy(api):
        """Tests the close method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.garage_door_control)
        # Act/Assert
        assert await device.close()
        assert device.status.door is None
        assert await device.close(set_status=True)
        assert device.status.door == 'closing'

    @staticmethod
    @pytest.mark.asyncio
    async def test_open_window_shade(api):
        """Tests the open method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.window_shade)
        # Act/Assert
        assert await device.open()
        assert device.status.window_shade is None
        assert await device.open(set_status=True)
        assert device.status.window_shade == 'opening'

    @staticmethod
    @pytest.mark.asyncio
    async def test_close_window_shade(api):
        """Tests the close method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        device.capabilities.append(Capability.window_shade)
        # Act/Assert
        assert await device.close()
        assert device.status.window_shade is None
        assert await device.close(set_status=True)
        assert device.status.window_shade == 'closing'

    @staticmethod
    @pytest.mark.asyncio
    async def test_preset_position(api):
        """Tests the preset_position method."""
        # Arrange
        device = DeviceEntity(api, device_id=DEVICE_ID)
        # Act/Assert
        assert await device.preset_position()


class TestDeviceStatus:
    """Tests for the DeviceStatus class."""

    @staticmethod
    def test_init():
        """Tests the init method."""
        # Arrange/Act
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Assert
        assert status.device_id == DEVICE_ID
        assert status.attributes == {}
        assert not status.switch
        assert not status.motion
        assert status.level == 0
        assert status.component_id == 'main'

    @staticmethod
    def test_apply_data():
        """Tests the apply_data method."""
        # Arrange
        data = get_json('device_status.json')
        # Act
        status = DeviceStatus(None, DEVICE_ID, data)
        # Assert
        assert len(status.attributes) == 9
        assert status.switch
        assert status.level == 100
        assert len(status.components) == 2
        assert len(status.components['topButton'].attributes) == 3
        assert len(status.components['bottomButton'].attributes) == 3

    @staticmethod
    def test_apply_attribute_update():
        """Tests the apply_attribute_update method."""
        # Arrange
        data = get_json('device_status.json')
        status = DeviceStatus(None, DEVICE_ID, data)
        # Act
        status.apply_attribute_update('main', 'switchLevel', 'level', 50)
        # Assert
        assert status.level == 50

    @staticmethod
    def test_apply_attribute_update_child_status():
        """Tests the apply_attribute_update method to a child status."""
        # Arrange
        data = get_json('device_status.json')
        status = DeviceStatus(None, DEVICE_ID, data)
        # Act
        status.apply_attribute_update(
            'bottomButton', 'switchLevel', 'level', 50)
        # Assert
        assert status.components['bottomButton'].level == 50

    @staticmethod
    def test_values():
        """Test the values property."""
        # Arrange
        data = get_json('device_status.json')
        status = DeviceStatus(None, DEVICE_ID, data)
        # Act/Assert
        assert status.values == {
            'button': None,
            'numberOfButtons': None,
            'supportedButtonValues': None,
            'indicatorStatus': 'when off',
            'switch': 'on',
            'checkInterval': 1920,
            'healthStatus': None,
            'DeviceWatch-DeviceStatus': None,
            'level': 100
        }

    @staticmethod
    def test_attributes():
        """Test the attributes property."""
        # Arrange
        data = get_json('device_status.json')
        status = DeviceStatus(None, DEVICE_ID, data)
        # Act/Assert
        assert status.attributes == {
            'button': (None, None, None),
            'numberOfButtons': (None, None, None),
            'supportedButtonValues': (None, None, None),
            'indicatorStatus': ('when off', None, None),
            'switch': ('on', None, None),
            'checkInterval': (1920, 's', {"protocol": "zwave",
                                          "hubHardwareId": "000F"}),
            'healthStatus': (None, None, {}),
            'DeviceWatch-DeviceStatus': (None, None, {}),
            'level': (100, '%', None)
        }

    @staticmethod
    def test_attributes_default():
        """Test the attributes property."""
        # Arrange
        data = get_json('device_status.json')
        status = DeviceStatus(None, DEVICE_ID, data)
        # Act/Assert
        assert status.attributes['thermostatSetpoint'] == (None, None, None)

    @staticmethod
    @pytest.mark.asyncio
    async def test_refresh(api):
        """Tests the refresh method."""
        # Arrange
        status = DeviceStatus(api, device_id=DEVICE_ID)
        # Act
        await status.refresh()
        # Assert
        assert len(status.attributes) == 9

    @staticmethod
    def test_switch():
        """Tests the switch property."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act
        status.switch = True
        # Assert
        assert status.switch

    @staticmethod
    def test_level():
        """Tests the level property."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act
        status.level = 50
        # Assert
        assert status.level == 50

    @staticmethod
    def test_level_range():
        """Tests the level property's range."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act/Assert
        values = [-1, 101]
        for value in values:
            with pytest.raises(ValueError):
                status.level = value

    @staticmethod
    def test_fan_speed():
        """Tests the fan_speed property."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act
        status.fan_speed = 50
        # Assert
        assert status.fan_speed == 50

    @staticmethod
    def test_fan_speed_range():
        """Tests the fan_speed property's range."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act/Assert
        with pytest.raises(ValueError):
            status.fan_speed = -1

    @staticmethod
    def test_hue_range():
        """Tests the hue property's range."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act/Assert
        values = [-1, 101]
        for value in values:
            with pytest.raises(ValueError):
                status.hue = value

    @staticmethod
    def test_saturation_range():
        """Tests the hue property's range."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act/Assert
        values = [-1, 101]
        for value in values:
            with pytest.raises(ValueError):
                status.saturation = value

    @staticmethod
    def test_color_temperature_range():
        """Tests the hue property's range."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act/Assert
        values = [0, 30001]
        for value in values:
            with pytest.raises(ValueError):
                status.color_temperature = value

    @staticmethod
    def test_color_format():
        """Tests the color property's validation."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        # Act/Assert
        values = ['000000', '#00000', '#HH2000']
        for value in values:
            with pytest.raises(ValueError):
                status.color = value

    @staticmethod
    def test_is_on():
        """Tests the is_on method."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        status.update_attribute_value(Attribute.acceleration, 'active')
        status.level = 100
        # Act/Assert
        assert status.is_on(Attribute.acceleration)
        assert status.is_on(Attribute.level)
        assert not status.is_on(Attribute.switch)

    @staticmethod
    def test_well_known_attributes():
        """Tests the humidity property."""
        # Arrange
        status = DeviceStatus(None, device_id=DEVICE_ID)
        status.update_attribute_value(Attribute.humidity, 50)
        status.update_attribute_value(Attribute.temperature, 55)
        status.update_attribute_value(
            Attribute.thermostat_operating_state, 'on')
        status.update_attribute_value(
            Attribute.supported_thermostat_fan_modes, ['on', 'off'])
        status.update_attribute_value(
            Attribute.supported_thermostat_modes, ['auto', 'off'])
        status.update_attribute_value(Attribute.lock, 'locked')
        status.update_attribute_value(Attribute.door, 'open')
        status.update_attribute_value(Attribute.window_shade, 'closed')
        # Act/Assert
        assert status.humidity == 50
        assert status.temperature == 55
        assert status.thermostat_operating_state == 'on'
        assert status.supported_thermostat_fan_modes == ['on', 'off']
        assert status.supported_thermostat_modes == ['auto', 'off']
        assert status.lock == 'locked'
        assert status.door == 'open'
        assert status.window_shade == 'closed'
