COMMAND_LIGHT_OFF_0X13_0X00 = {"cmd1": 0x13, "cmd2": 0x00} COMMAND_LIGHT_ON_0X11_NONE = {"cmd1": 0x11, "cmd2": None} COMMAND_LIGHT_STATUS_REQUEST_0X19_0X00 = {"cmd1": 0x19, "cmd2": 0x00} COMMAND_LIGHT_STATUS_REQUEST_0X19_0X01 = {"cmd1": 0x19, "cmd2": 0x01} MESSAGE_ACK = 0x06 MESSAGE_TYPE_ALL_LINK_BROADCAST = 6 MESSAGE_TYPE_DIRECT_MESSAGE_ACK = 1 class StandardSend(Message):
    """Insteon Standard Length Message Send 0x62."""

    _code = MESSAGE_SEND_STANDARD_MESSAGE_0X62
    _sendSize = MESSAGE_SEND_STANDARD_MESSAGE_SIZE
    _receivedSize = MESSAGE_SEND_STANDARD_MESSAGE_RECEIVED_SIZE
    _description = "INSTEON Standard Message Send"

    def __init__(self, address, commandtuple, cmd2=None, flags=0x00, acknak=None):
        """Init the StandardSend message class."""
        if commandtuple.get("cmd1", None) is not None:
            cmd1 = commandtuple["cmd1"]
            cmd2out = commandtuple["cmd2"]
        else:
            raise ValueError

        if cmd2 is not None:
            cmd2out = cmd2

        if cmd2out is None:
            raise ValueError

        self._address = Address(address)
        self._messageFlags = MessageFlags(flags)
        self._messageFlags.extended = 0
        self._cmd1 = cmd1
        self._cmd2 = cmd2out

        self._acknak = self._setacknak(acknak)

    @classmethod
    def from_raw_message(cls, rawmessage):
        """Create a message from a raw byte stream."""
        if (rawmessage[5] & MESSAGE_FLAG_EXTENDED_0X10) == MESSAGE_FLAG_EXTENDED_0X10:
            if len(rawmessage) >= ExtendedSend.receivedSize:
                msg = ExtendedSend.from_raw_message(rawmessage)
            else:
                msg = None
        else:
            msg = StandardSend(
                rawmessage[2:5],
                {"cmd1": rawmessage[6], "cmd2": rawmessage[7]},
                flags=rawmessage[5],
                acknak=rawmessage[8:9],
            )
        return msg

    # pylint: disable=protected-access
    @classmethod
    def template(
        cls, address=None, commandtuple=None, cmd2=-1, flags=None, acknak=None
    ):
        """Create a message template for use in callbacks."""
        msgraw = bytearray([0x02, cls._code])
        msgraw.extend(bytes(cls._receivedSize))
        msg = StandardSend.from_raw_message(msgraw)

        if commandtuple:
            cmd1 = commandtuple.get("cmd1")
            cmd2out = commandtuple.get("cmd2")
        else:
            cmd1 = None
            cmd2out = None

        if cmd2 is not -1:
            cmd2out = cmd2

        msg._address = Address(address)
        msg._messageFlags = MessageFlags(flags)
        msg._cmd1 = cmd1
        msg._cmd2 = cmd2out
        msg._acknak = acknak
        return msg

    @property
    def address(self):
        """Return the address of the device."""
        return self._address

    @property
    def cmd1(self):
        """Return cmd1 property of the message."""
        return self._cmd1

    @property
    def cmd2(self):
        """Return cmd2 property of the message."""
        return self._cmd2

    @property
    def flags(self):
        """Return the message flags."""
        return self._messageFlags

    @property
    def acknak(self):
        """Return the ACK/NAK message flag."""
        return self._acknak

    @acknak.setter
    def acknak(self, val):
        """Set the ACK/NAK byte."""
        if val in [None, 0x06, 0x15]:
            self._acknak = val
        else:
            raise ValueError

    @property
    def isack(self):
        """Test if the message is a message ACK."""
        return self._acknak is not None and self._acknak == MESSAGE_ACK

    @property
    def isnak(self):
        """Test if the message is a message NAK."""
        return self._acknak is not None and self._acknak == MESSAGE_NAK

    def _message_properties(self):
        return [
            {"address": self._address},
            {"flags": self._messageFlags},
            {"cmd1": self._cmd1},
            {"cmd2": self._cmd2},
            {"acknak": self._acknak},
        ] class StandardReceive(Message):
    """Insteon Standard Length Message Received.

    Message type 0x50
    """

    _code = MESSAGE_STANDARD_MESSAGE_RECEIVED_0X50
    _sendSize = MESSAGE_STANDARD_MESSAGE_RECIEVED_SIZE
    _receivedSize = MESSAGE_STANDARD_MESSAGE_RECIEVED_SIZE
    _description = "INSTEON Standard Message Received"

    def __init__(self, address, target, commandtuple, cmd2=None, flags=0x00):
        """Init the StandardReceive message class."""
        if commandtuple.get("cmd1") is not None:
            cmd1 = commandtuple["cmd1"]
            cmd2out = commandtuple["cmd2"]
        else:
            raise ValueError

        if cmd2 is not None:
            cmd2out = cmd2

        if cmd2out is None:
            raise ValueError

        self._address = Address(address)
        self._target = Address(target)
        self._messageFlags = MessageFlags(flags)
        # self._messageFlags.extended = 0
        self._cmd1 = cmd1
        self._cmd2 = cmd2out

    @classmethod
    def from_raw_message(cls, rawmessage):
        """Create message from a raw byte stream."""
        return StandardReceive(
            rawmessage[2:5],
            rawmessage[5:8],
            {"cmd1": rawmessage[9], "cmd2": rawmessage[10]},
            flags=rawmessage[8],
        )

    # pylint: disable=protected-access
    @classmethod
    def template(
        cls, address=None, target=None, commandtuple=None, cmd2=-1, flags=None
    ):
        """Create a message template used for callbacks."""
        msgraw = bytearray([0x02, cls._code])
        msgraw.extend(bytes(cls._receivedSize))
        msg = StandardReceive.from_raw_message(msgraw)

        if commandtuple:
            cmd1 = commandtuple.get("cmd1")
            cmd2out = commandtuple.get("cmd2")
        else:
            cmd1 = None
            cmd2out = None

        if cmd2 is not -1:
            cmd2out = cmd2

        msg._address = Address(address)
        msg._target = Address(target)
        msg._messageFlags = MessageFlags(flags)
        msg._cmd1 = cmd1
        msg._cmd2 = cmd2out
        return msg

    @property
    def address(self):
        """Return the address of the device."""
        return self._address

    @property
    def target(self):
        """Return the address of the target device."""
        return self._target

    @property
    def cmd1(self):
        """Return the cmd1 property of the message."""
        return self._cmd1

    @property
    def cmd2(self):
        """Return the cmd2 property of the message."""
        return self._cmd2

    @property
    def flags(self):
        """Return the message flags."""
        return self._messageFlags

    @property
    def targetLow(self):
        """Return the low byte of the target message property.

        Used in All-Link Cleanup message types.
        """
        low_byte = None
        if self.target.addr is not None and self._messageFlags.isBroadcast:
            low_byte = self.target.bytes[0]
        return low_byte

    @property
    def targetMed(self):
        """Return the middle byte of the target message property.

        Used in All-Link Cleanup message types.
        """
        med_byte = None
        if self.target.addr is not None and self._messageFlags.isBroadcast:
            med_byte = self.target.bytes[1]
        return med_byte

    @property
    def targetHi(self):
        """Return the high byte of the target message property.

        Used in All-Link Cleanup message types.
        """
        hi_byte = None
        if self.target.addr is not None and self._messageFlags.isBroadcast:
            hi_byte = self.target.bytes[2]
        return hi_byte

    def _message_properties(self):
        return [
            {"address": self._address},
            {"target": self._target},
            {"flags": self._messageFlags},
            {"cmd1": self._cmd1},
            {"cmd2": self._cmd2},
        ] class ExtendedSend(Message):
    """Send an INSTEON Extended message.

    address: A device hex address in any form.
    cmd1: Required command in the cmd1 field
    cmd2: Required command in the cmd2 field
    **kwarg: otional userdata dictionary with the following keys:
        'd1' - User data byte 0 as byte or int
        'd2' - user data byte 1 as byte or int
        ...
        'd14' - user data byte 14 as byte or int
        'd1' to 'd14' are assumed to equal 0x00 unless explicitly set
    """

    _code = MESSAGE_SEND_EXTENDED_MESSAGE_0X62
    _sendSize = MESSAGE_SEND_EXTENDED_MESSAGE_SIZE
    _receivedSize = MESSAGE_SEND_EXTENDED_MESSAGE_RECEIVED_SIZE
    _description = "INSTEON Standard Message Send"

    def __init__(
        self, address, commandtuple, userdata, cmd2=None, flags=0x10, acknak=None
    ):
        """Init the ExtendedSend message class."""
        if commandtuple.get("cmd1", None) is not None:
            cmd1 = commandtuple["cmd1"]
            cmd2out = commandtuple["cmd2"]
        else:
            raise ValueError

        if cmd2 is not None:
            cmd2out = cmd2

        if cmd2out is None:
            raise ValueError

        self._address = Address(address)
        self._messageFlags = MessageFlags(flags)
        self._messageFlags.extended = 1
        self._cmd1 = cmd1
        self._cmd2 = cmd2out
        self._userdata = Userdata(userdata)
        self._acknak = self._setacknak(acknak)

    @classmethod
    def from_raw_message(cls, rawmessage):
        """Create a message from a raw byte stream."""
        userdata_dict = Userdata(rawmessage[8:22])
        return ExtendedSend(
            rawmessage[2:5],
            {"cmd1": rawmessage[6], "cmd2": rawmessage[7]},
            userdata_dict,
            flags=rawmessage[5],
            acknak=rawmessage[22:23],
        )

    # pylint: disable=protected-access
    @classmethod
    def template(
        cls,
        address=None,
        commandtuple=None,
        userdata=None,
        cmd2=-1,
        flags=None,
        acknak=None,
    ):
        """Create a message template used for callbacks."""
        msgraw = bytearray([0x02, cls._code])
        msgraw.extend(bytes(cls._receivedSize))
        msg = ExtendedSend.from_raw_message(msgraw)

        if commandtuple:
            cmd1 = commandtuple.get("cmd1")
            cmd2out = commandtuple.get("cmd2")
        else:
            cmd1 = None
            cmd2out = None

        if cmd2 is not -1:
            cmd2out = cmd2

        msg._address = Address(address)
        msg._messageFlags = MessageFlags(flags)
        msg._messageFlags.extended = 1
        msg._cmd1 = cmd1
        msg._cmd2 = cmd2out
        msg._userdata = Userdata.template(userdata)
        msg._acknak = acknak
        return msg

    @property
    def address(self):
        """Return the address of the device."""
        return self._address

    @property
    def cmd1(self):
        """Return the cmd1 property of the message."""
        return self._cmd1

    @property
    def cmd2(self):
        """Return the cmd2 property of the message."""
        return self._cmd2

    @property
    def userdata(self):
        """Return the extended message user data."""
        return self._userdata

    @property
    def flags(self):
        """Return the message flags."""
        return self._messageFlags

    @property
    def acknak(self):
        """Return the ACK/NAK byte."""
        return self._acknak

    @acknak.setter
    def acknak(self, val):
        """Set the ACK/NAK byte."""
        if val in [None, 0x06, 0x15]:
            self._acknak = val
        else:
            raise ValueError

    @property
    def isack(self):
        """Test if message is an ACK."""
        return self._acknak is not None and self._acknak == MESSAGE_ACK

    @property
    def isnak(self):
        """Test if message is a NAK."""
        return self._acknak is not None and self._acknak == MESSAGE_NAK

    def set_checksum(self):
        """Set byte 14 of the userdata to a checksum value."""
        data_sum = self.cmd1 + self.cmd2
        for i in range(1, 14):
            data_sum += self._userdata["d{:d}".format(i)]
        chksum = 0xFF - (data_sum & 0xFF) + 1
        self._userdata["d14"] = chksum & 0xff

    def set_crc(self):
        """Set Userdata[13] and Userdata[14] to the CRC value."""
        data = self.bytes[6:20]
        crc = int(0)
        for b in data:
            # pylint: disable=unused-variable
            for bit in range(0, 8):
                fb = b & 0x01
                fb = fb ^ 0x01 if (crc & 0x8000) else fb
                fb = fb ^ 0x01 if (crc & 0x4000) else fb
                fb = fb ^ 0x01 if (crc & 0x1000) else fb
                fb = fb ^ 0x01 if (crc & 0x0008) else fb
                crc = ((crc << 1) | fb) & 0xFFFF
                b = b >> 1
        self._userdata["d13"] = (crc >> 8) & 0xFF
        self._userdata["d14"] = crc & 0xFF

    def _message_properties(self):
        return [
            {"address": self._address},
            {"flags": self._messageFlags},
            {"cmd1": self._cmd1},
            {"cmd2": self._cmd2},
            {"userdata": self._userdata},
            {"acknak": self._acknak},
        ] class MessageFlags:
    """Message Flags class use in Standard and Extended messages."""

    def __init__(self, flags=0x00):
        """Init the MessageFlags class."""
        self._messageType = None
        self._extended = None
        self._hopsLeft = None
        self._hopsMax = None

        if flags is not None:
            self._set_properties(flags)

    def __repr__(self):
        """Representation of the message flags."""
        return self.hex

    def __str__(self):
        """Return a string representation of message flags."""
        return self.hex

    def __eq__(self, other):
        """Test for equality."""
        if hasattr(other, "messageType"):
            is_eq = self._messageType == other.messageType
            is_eq = is_eq and self._extended == other.extended
            return is_eq
        return False

    def __ne__(self, other):
        """Test for not equals."""
        if hasattr(other, "messageType"):
            return not self.__eq__(other)
        return True

    def matches_pattern(self, other):
        """Test if current message match a patterns or template."""
        if hasattr(other, "messageType"):
            messageTypeIsEqual = False
            if self.messageType is None or other.messageType is None:
                messageTypeIsEqual = True
            else:
                messageTypeIsEqual = self.messageType == other.messageType
            extendedIsEqual = False
            if self.extended is None or other.extended is None:
                extendedIsEqual = True
            else:
                extendedIsEqual = self.extended == other.extended
            return messageTypeIsEqual and extendedIsEqual
        return False

    @classmethod
    def get_properties(cls):
        """Get all properties of the MessageFlags class."""
        property_names = [p for p in dir(cls) if isinstance(getattr(cls, p), property)]
        return property_names

    @property
    def isBroadcast(self):
        """Test if the message is a broadcast message type."""
        return (
            self._messageType & MESSAGE_TYPE_BROADCAST_MESSAGE
            == MESSAGE_TYPE_BROADCAST_MESSAGE
        )

    @property
    def isDirect(self):
        """Test if the message is a direct message type."""
        direct = self._messageType == 0x00
        if self.isDirectACK or self.isDirectNAK:
            direct = True
        return direct

    @property
    def isDirectACK(self):
        """Test if the message is a direct ACK message type."""
        return self._messageType == MESSAGE_TYPE_DIRECT_MESSAGE_ACK

    @property
    def isDirectNAK(self):
        """Test if the message is a direct NAK message type."""
        return self._messageType == MESSAGE_TYPE_DIRECT_MESSAGE_NAK

    @property
    def isAllLinkBroadcast(self):
        """Test if the message is an ALl-Link broadcast message type."""
        return self._messageType == MESSAGE_TYPE_ALL_LINK_BROADCAST

    @property
    def isAllLinkCleanup(self):
        """Test if the message is a All-Link cleanup message type."""
        return self._messageType == MESSAGE_TYPE_ALL_LINK_CLEANUP

    @property
    def isAllLinkCleanupACK(self):
        """Test if the message is a All-LInk cleanup ACK message type."""
        return self._messageType == MESSAGE_TYPE_ALL_LINK_CLEANUP_ACK

    @property
    def isAllLinkCleanupNAK(self):
        """Test if the message is a All-Link cleanup NAK message type."""
        return self._messageType == MESSAGE_TYPE_ALL_LINK_CLEANUP_NAK

    @property
    def isExtended(self):
        """Test if the message is an extended message type."""
        return self._extended == 1

    @property
    def hopsLeft(self):
        """Return the number of hops left in message the trasport."""
        return self._hopsLeft

    @property
    def hopsMax(self):
        """Return the maximum number of hops allowed for this message."""
        return self._hopsMax

    @hopsMax.setter
    def hopsMax(self, val):
        """Set the maximum number of hops allowed for this message."""
        self._hopsMax = val

    @property
    def messageType(self):
        """Return the message type."""
        return self._messageType

    @messageType.setter
    def messageType(self, val):
        """Set the message type."""
        if val in range(0, 8):
            self._messageType = val
        else:
            raise ValueError

    @property
    def extended(self):
        """Return the extended flag."""
        return self._extended

    @extended.setter
    def extended(self, val):
        """Set the extended flag."""
        if val in [None, 0, 1]:
            self._extended = val
        else:
            raise ValueError

    # pylint: disable=protected-access
    @classmethod
    def create(cls, messageType, extended, hopsleft=3, hopsmax=3):
        """Create message flags.

        messageType: integter 0 to 7:
            MESSAGE_TYPE_DIRECT_MESSAGE = 0
            MESSAGE_TYPE_DIRECT_MESSAGE_ACK = 1
            MESSAGE_TYPE_ALL_LINK_CLEANUP = 2
            MESSAGE_TYPE_ALL_LINK_CLEANUP_ACK = 3
            MESSAGE_TYPE_BROADCAST_MESSAGE = 4
            MESSAGE_TYPE_DIRECT_MESSAGE_NAK = 5
            MESSAGE_TYPE_ALL_LINK_BROADCAST = 6
            MESSAGE_TYPE_ALL_LINK_CLEANUP_NAK = 7
        extended: 1 for extended, 0 for standard
        hopsleft: int  0 - 3
        hopsmax:  int  0 - 3
        """
        flags = MessageFlags(None)
        if messageType < 8:
            flags._messageType = messageType
        else:
            flags._messageType = messageType >> 5
        if extended in [0, 1, True, False]:
            if extended:
                flags._extended = 1
            else:
                flags._extended = 0
        else:
            flags._extended = extended >> 4
        flags._hopsLeft = hopsleft
        flags._hopsMax = hopsmax
        return flags

    @classmethod
    def template(cls, messageType=None, extended=None, hopsleft=None, hopsmax=None):
        """Create message flags template.

        messageType: integter 0 to 7 or None:
            MESSAGE_TYPE_DIRECT_MESSAGE = 0
            MESSAGE_TYPE_DIRECT_MESSAGE_ACK = 1
            MESSAGE_TYPE_ALL_LINK_CLEANUP = 2
            MESSAGE_TYPE_ALL_LINK_CLEANUP_ACK = 3
            MESSAGE_TYPE_BROADCAST_MESSAGE = 4
            MESSAGE_TYPE_DIRECT_MESSAGE_NAK = 5
            MESSAGE_TYPE_ALL_LINK_BROADCAST = 6
            MESSAGE_TYPE_ALL_LINK_CLEANUP_NAK = 7
        extended: 1 for extended, 0 for standard or None
        hopsleft: int  0 - 3
        hopsmax:  int  0 - 3
        """
        flags = MessageFlags(None)
        if messageType is None:
            flags._messageType = None
        elif messageType < 8:
            flags._messageType = messageType
        else:
            flags._messageType = messageType >> 5
        if extended is None:
            flags._extended = None
        elif extended in [0, 1, True, False]:
            if extended:
                flags._extended = 1
            else:
                flags._extended = 0
        else:
            flags._extended = extended >> 4
        flags._hopsLeft = hopsleft
        flags._hopsMax = hopsmax
        return flags

    @property
    def bytes(self):
        """Return a byte representation of the message flags."""
        flagByte = 0x00
        messageType = 0
        if self._messageType is not None:
            messageType = self._messageType << 5
        extendedBit = 0 if self._extended is None else self._extended << 4
        hopsMax = 0 if self._hopsMax is None else self._hopsMax
        hopsLeft = 0 if self._hopsLeft is None else (self._hopsLeft << 2)
        flagByte = flagByte | messageType | extendedBit | hopsLeft | hopsMax
        return bytes([flagByte])

    @property
    def hex(self):
        """Return a hexadecimal representation of the message flags."""
        return binascii.hexlify(self.bytes).decode()

    # pylint: disable=no-self-use
    def _normalize(self, flags):
        """Take any format of flags and turn it into a hex string."""
        norm = None
        if isinstance(flags, MessageFlags):
            norm = flags.bytes
        elif isinstance(flags, bytearray):
            norm = binascii.hexlify(flags)
        elif isinstance(flags, int):
            norm = bytes([flags])
        elif isinstance(flags, bytes):
            norm = binascii.hexlify(flags)
        elif isinstance(flags, str):
            flags = flags[0:2]
            norm = binascii.hexlify(binascii.unhexlify(flags.lower()))
        elif flags is None:
            norm = None
        else:
            _LOGGER.warning("MessageFlags with unknown type %s: %r", type(flags), flags)
        return norm

    def _set_properties(self, flags):
        """Set the properties of the message flags based on a byte input."""
        flagByte = self._normalize(flags)

        if flagByte is not None:
            self._messageType = (flagByte[0] & 0xE0) >> 5
            self._extended = (flagByte[0] & MESSAGE_FLAG_EXTENDED_0X10) >> 4
            self._hopsLeft = (flagByte[0] & 0x0C) >> 2
            self._hopsMax = flagByte[0] & 0x03
        else:
            self._messageType = None
            self._extended = None
            self._hopsLeft = None
            self._hopsMax = None class SwitchedLightingControl(Device):
    """Switched Lighting Control.

    Device Class 0x02 subcat Any

    Available device control options are:
        - light_on()
        - light_on_fast()
        - light_off()
        - light_off_fast()
    To monitor the state of the device subscribe to the state monitor:
         - lightOnLevel.connect(callback)
    where callback defined as:
        - callback(self, device_id, state, state_value)
    """

    def __init__(
        self, plm, address, cat, subcat, product_key=0x00, description="", model=""
    ):
        """Init the SwitchedLightingControl device class."""
        super().__init__(plm, address, cat, subcat, product_key, description, model)

        self._stateList[0x01] = OnOffSwitch(
            self._address,
            "lightOnOff",
            0x01,
            self._send_msg,
            self._message_callbacks,
            0x00,
        ) class SwitchedLightingControl_2663_222(Device):
    """On/Off outlet model 2663-222 Switched Lighting Control.

    Device Class 0x02 subcat 0x39

    Two separate INSTEON On/Off switch devices are created with ID
        - 'address': Top Outlet
        - 'address_2': Bottom Outlet

    Available device control options are:
        - light_on()
        - light_on_fast()
        - light_off()
        - light_off_fast()
    To monitor the state of the device subscribe to the state monitor:
         - lightOnLevel.connect(callback)
    where callback defined as:
        - callback(self, device_id, state, state_value)
    """

    def __init__(
        self, plm, address, cat, subcat, product_key=None, description=None, model=None
    ):
        """Init the SwitchedLightingControl_2663_222 device class."""
        super().__init__(plm, address, cat, subcat, product_key, description, model)

        self._stateList[0x01] = OnOffSwitch_OutletTop(
            self._address,
            "outletTopOnOff",
            0x01,
            self._send_msg,
            self._message_callbacks,
            0x00,
        )
        self._stateList[0x02] = OnOffSwitch_OutletBottom(
            self._address,
            "outletBottomOnOff",
            0x02,
            self._send_msg,
            self._message_callbacks,
            0x00,
        ) class MockPLM():
    """Mock PLM class for testing devices."""

    def __init__(self, loop=None):
        """Init the MockPLM class."""
        self.sentmessage = ''
        self._message_callbacks = MessageCallback()
        self.loop = loop
        self.devices = LinkedDevices()

    @property
    def message_callbacks(self):
        """Return the message callback list."""
        return self._message_callbacks

    # pylint: disable=unused-argument
    def send_msg(self, msg, wait_nak=True, wait_timeout=2):
        """Send a message mock routine."""
        _LOGGER.debug('TX: %s:%s', id(msg), msg)
        self.sentmessage = msg.hex

    def message_received(self, msg):
        """Fake a message being received by the PLM."""
        _LOGGER.debug('RX: %s:%s', id(msg), msg)
        if hasattr(msg, 'address'):
            device = self.devices[msg.address.id]
            if device:
                device.receive_message(msg)
            else:
                _LOGGER.info('Received message for unknown device %s',
                             msg.address)
        for callback in (
                self._message_callbacks.get_callbacks_from_message(msg)):
            callback(msg)

    # pylint: disable=unused-argument
    def start_all_linking(self, linkcode, group):
        """Fake start all linking."""
        self.sentmessage = b'02112233445566' class MockCallbacks():
    """Mock callback class to support device and state testing."""

    def __init__(self):
        """Init the MockCallbacks Class."""
        self.callbackvalue1 = None
        self.callbackvalue2 = None
        self.callbackvalue3 = None
        self.callbackvalue4 = None
        self.callbackvalue5 = None
        self.callbackvalue6 = None
        self.callbackvalue7 = None
        self.callbackvalue8 = None
        self.callbackvalue9 = None

    def callbackmethod1(self, addr, state, value):
        """Receive notice of callback method 1."""
        self._report_callback(1, addr, state, value)
        self.callbackvalue1 = value

    def callbackmethod2(self, addr, state, value):
        """Receive notice of callback method 2."""
        self._report_callback(2, addr, state, value)
        self.callbackvalue2 = value

    def callbackmethod3(self, addr, state, value):
        """Receive notice of callback method 3."""
        self._report_callback(3, addr, state, value)
        self.callbackvalue3 = value

    def callbackmethod4(self, addr, state, value):
        """Receive notice of callback method 5."""
        self._report_callback(4, addr, state, value)
        self.callbackvalue4 = value

    def callbackmethod5(self, addr, state, value):
        """Receive notice of callback method 5."""
        self._report_callback(5, addr, state, value)
        self.callbackvalue5 = value

    def callbackmethod6(self, addr, state, value):
        """Receive notice of callback method 6."""
        self._report_callback(6, addr, state, value)
        self.callbackvalue6 = value

    def callbackmethod7(self, addr, state, value):
        """Receive notice of callback method 7."""
        self._report_callback(7, addr, state, value)
        self.callbackvalue7 = value

    def callbackmethod8(self, addr, state, value):
        """Receive notice of callback method 8."""
        self._report_callback(8, addr, state, value)
        self.callbackvalue8 = value

    def callbackmethod9(self, addr, state, value):
        """Receive notice of callback method 9."""
        _LOGGER.debug('Called method 9 callback')
        self.callbackvalue9 = value

    @staticmethod
    def _report_callback(callback, addr, state, value):
        _LOGGER.debug('Called method %d for address %s group %s value %s',
                      callback, addr, state, value)
import asyncio
import logging
from insteonplm.constants import (COMMAND_LIGHT_OFF_0X13_0X00,
                                  COMMAND_LIGHT_ON_0X11_NONE,
                                  COMMAND_LIGHT_STATUS_REQUEST_0X19_0X00,
                                  COMMAND_LIGHT_STATUS_REQUEST_0X19_0X01,
                                  MESSAGE_ACK,
                                  MESSAGE_TYPE_ALL_LINK_BROADCAST,
                                  MESSAGE_TYPE_DIRECT_MESSAGE_ACK)
from insteonplm.messages.standardSend import StandardSend
from insteonplm.messages.standardReceive import StandardReceive
from insteonplm.messages.extendedSend import ExtendedSend
from insteonplm.messages.messageFlags import MessageFlags
from insteonplm.devices.switchedLightingControl import (
    SwitchedLightingControl, SwitchedLightingControl_2663_222)
from .mockPLM import MockPLM
from .mockCallbacks import MockCallbacks
            address, '000001', COMMAND_LIGHT_ON_0X11_NONE, cmd2=0x66,
            flags=MessageFlags.create(MESSAGE_TYPE_ALL_LINK_BROADCAST,
                                      0, 2, 3))
        plm.message_received(receivedmsg)
        await asyncio.sleep(.1, loop=loop)
        assert callbacks.callbackvalue1 == 0xff

        receivedmsg = StandardReceive(
            address, '000001', COMMAND_LIGHT_OFF_0X13_0X00,
            flags=MessageFlags.create(MESSAGE_TYPE_ALL_LINK_BROADCAST,
                                      0, 2, 3))
        plm.message_received(receivedmsg)
        await asyncio.sleep(.1, loop=loop)
        assert callbacks.callbackvalue1 == 0x00

    loop = asyncio.get_event_loop()
    loop.run_until_complete(run_test(loop))
    open_tasks = asyncio.Task.all_tasks(loop=loop)

    for task in open_tasks:
        if hasattr(task, 'name'):
            _LOGGING.error('Device: %s Task: %s', task.name, task)
        else:
            _LOGGING.error('Task: %s', task)
        if not task.done():
            loop.run_until_complete(task)


def test_switchedLightingControl_status():
    """Test SwitchedLightingControl status."""
    async def run_test(loop):
        """Asyncio test method."""
        plm = MockPLM(loop)
        address = '1a2b3c'
        target = '4d5e6f'
        cat = 0x02
        subcat = 0x0d
        product_key = None
        description = 'ToggleLinc Relay'
        model = '2466S'

        device = SwitchedLightingControl(plm, address, cat, subcat,
                                         product_key, description, model)
        plm.devices[address] = device
        assert device.address.hex == address
        assert device.cat == cat
        assert device.subcat == subcat
        assert device.product_key == 0x00  # Product key should not be None
        assert device.description == description
        assert device.model == model
        assert device.id == address

        callbacks = MockCallbacks()
        device.states[0x01].register_updates(callbacks.callbackmethod1)

        device.states[0x01].async_refresh_state()
        await asyncio.sleep(.1, loop=loop)
        receivedmsg = StandardSend(address,
                                   COMMAND_LIGHT_STATUS_REQUEST_0X19_0X00,
                                   acknak=MESSAGE_ACK)
        plm.message_received(receivedmsg)
        await asyncio.sleep(.1, loop=loop)
        receivedmsg = StandardReceive(
            address, target, {'cmd1': 0x09, 'cmd2': 0xff},
            flags=MessageFlags.create(MESSAGE_TYPE_DIRECT_MESSAGE_ACK,
                                      0, 2, 3))
        plm.message_received(receivedmsg)
        await asyncio.sleep(.1, loop=loop)
        sentmsg = StandardSend(address, COMMAND_LIGHT_STATUS_REQUEST_0X19_0X00)
        assert plm.sentmessage == sentmsg.hex
        assert callbacks.callbackvalue1 == 0xff

    loop = asyncio.get_event_loop()
    loop.run_until_complete(run_test(loop))
    open_tasks = asyncio.Task.all_tasks(loop=loop)

    for task in open_tasks:
        if hasattr(task, 'name'):
            _LOGGING.error('Device: %s Task: %s', task.name, task)
        else:
            _LOGGING.error('Task: %s', task)
        if not task.done():
            loop.run_until_complete(task)


# pylint: disable=too-many-statements
def test_switchedLightingControl_2663_222():
    """Test SwitchedLightingControl device 2663-222."""
    async def run_test(loop):
        """Asyncio test method."""
        plm = MockPLM(loop)
        callbacks = MockCallbacks()

        address = '1a2b3c'
        target = '4d5e6f'
        cat = 0x02
        subcat = 0x0d
        product_key = None
        description = 'ToggleLinc Relay'
        model = '2466S'

        device = SwitchedLightingControl_2663_222(
            plm, address, cat, subcat, product_key, description, model)
        plm.devices[address] = device
        assert device.address.hex == address
        assert device.cat == cat
        assert device.subcat == subcat
        assert device.product_key == 0x00  # Product key should not be None
        assert device.description == description
        assert device.model == model
        assert device.id == address

        device.states[0x01].register_updates(callbacks.callbackmethod1)
        device.states[0x02].register_updates(callbacks.callbackmethod2)

        device.states[0x01].on()
        await asyncio.sleep(.1, loop=loop)
        sentmsg = StandardSend(address, COMMAND_LIGHT_ON_0X11_NONE, cmd2=0xff)
        assert plm.sentmessage == sentmsg.hex
        receivedmsg = StandardSend(address, COMMAND_LIGHT_ON_0X11_NONE,

                                   cmd2=0xff, acknak=MESSAGE_ACK)