def test_userdata_basic(): """Test insteonplm User Data type class.""" userdata = { 'd1': 0x11, 'd2': 0x22, 'd3': 0x33, 'd4': 0x44, 'd5': 0x55, 'd6': 0x66, 'd7': 0x77, 'd8': 0x88, 'd9': 0x99, 'd10': 0xaa, 'd11': 0xbb, 'd12': 0xcc, 'd13': 0xdd, 'd14': 0xee } ud = Userdata(userdata) chk = Userdata.create_pattern(userdata) chk2 = Userdata.create_pattern({'d1': 0x11}) assert chk == ud assert ud.matches_pattern(chk2) assert chk2.matches_pattern(ud)
def test_message_callback_extended(): """Test message callback extended.""" callbacks = MessageCallback() callbacktest = "test callback" address = '1a2b3c' target = '4d5e6f' template_ext_on = ExtendedReceive.template( commandtuple=COMMAND_LIGHT_ON_0X11_NONE, userdata=Userdata({'d1': 0x02})) callbacks.add(template_ext_on, callbacktest) msg1 = ExtendedReceive(address, target, COMMAND_LIGHT_ON_0X11_NONE, Userdata({'d1': 0x02}), cmd2=0xff) msg2 = ExtendedReceive(address, target, COMMAND_LIGHT_ON_0X11_NONE, Userdata({ 'd1': 0x03, 'd2': 0x02 }), cmd2=0xff) callback1 = callbacks.get_callbacks_from_message(msg1) callback2 = callbacks.get_callbacks_from_message(msg2) assert callback1[0] == callbacktest assert not callback2
def _register_messages(self): mode_status_msg = StandardReceive.template( commandtuple=COMMAND_THERMOSTAT_MODE_STATUS_0X70_NONE, address=self._address, flags=MessageFlags.template(MESSAGE_TYPE_DIRECT_MESSAGE, None), ) mode_change_fan_on_ack = StandardReceive.template( commandtuple=COMMAND_THERMOSTAT_CONTROL_ON_FAN_0X6B_0X07, address=self._address, flags=MessageFlags.template(MESSAGE_TYPE_DIRECT_MESSAGE_ACK), ) mode_change_fan_auto_ack = StandardReceive.template( commandtuple=COMMAND_THERMOSTAT_CONTROL_OFF_FAN_0X6B_0X08, address=self._address, flags=MessageFlags.template(MESSAGE_TYPE_DIRECT_MESSAGE_ACK), ) mode_change_off_ack = StandardReceive.template( commandtuple=COMMAND_THERMOSTAT_CONTROL_OFF_ALL_0X6B_0X09, address=self._address, flags=MessageFlags.template(MESSAGE_TYPE_DIRECT_MESSAGE_ACK), ) ext_status_recd = ExtendedReceive.template( commandtuple=COMMAND_EXTENDED_GET_SET_0X2E_0X00, cmd2=0x02, userdata=Userdata.template({"d1": 0x01}), ) self._message_callbacks.add(mode_status_msg, self._status_received) self._message_callbacks.add(mode_change_fan_on_ack, self._mode_change_ack) self._message_callbacks.add(mode_change_fan_auto_ack, self._mode_change_ack) self._message_callbacks.add(mode_change_off_ack, self._mode_change_ack) self._message_callbacks.add(ext_status_recd, self._ext_status_received)
def _create_set_property_msg(self, prop, cmd, val): """Create an extended message to set a property. Create an extended message with: cmd1: 0x2e cmd2: 0x00 flags: Direct Extended d1: group d2: cmd d3: val d4 - d14: 0x00 Parameters: prop: Property name to update cmd: Command value 0x02: on mask 0x03: off mask 0x04: x10 house code 0x05: ramp rate 0x06: on level 0x07: LED brightness 0x08: Non-Toggle mask 0x09: LED bit mask (Do not use in this class. Use LED class) 0x0a: X10 All bit mask 0x0c: Trigger group bit mask val: New property value """ user_data = Userdata({'d1': self.group, 'd2': cmd, 'd3': val}) msg = ExtendedSend(self._address, COMMAND_EXTENDED_GET_SET_0X2E_0X00, user_data) msg.set_checksum() self._set_sent_property(prop, val) return msg
def __init__(self, address, target, commandtuple, userdata, cmd2=None, flags=0x10): """Initialize the ExtendedRecieve 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._target = Address(target) self._messageFlags = MessageFlags(flags) # self._messageFlags.extended = 1 self._cmd1 = cmd1 self._cmd2 = cmd2out self._userdata = Userdata(userdata)
def template( cls, address=None, target=None, commandtuple=None, userdata=None, cmd2=-1, flags=None, ): """Create message template for callbacks.""" msgraw = bytearray([0x02, cls._code]) msgraw.extend(bytes(cls._receivedSize)) msg = ExtendedReceive.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 msg._userdata = Userdata.create_pattern(userdata) return msg
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)
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
def set(self, mode): """Set the thermostat mode. Mode optons: OFF = 0x00, HEAT = 0x01, COOL = 0x02, AUTO = 0x03, FAN_AUTO = 0x04, FAN_ALWAYS_ON = 0x8 """ new_mode = None if mode == ThermostatMode.OFF: new_mode = COMMAND_THERMOSTAT_CONTROL_OFF_ALL_0X6B_0X09 elif mode == ThermostatMode.HEAT: new_mode = COMMAND_THERMOSTAT_CONTROL_ON_HEAT_0X6B_0X04 elif mode == ThermostatMode.COOL: new_mode = COMMAND_THERMOSTAT_CONTROL_ON_COOL_0X6B_0X05 elif mode == ThermostatMode.AUTO: new_mode = COMMAND_THERMOSTAT_CONTROL_ON_AUTO_0X6B_0X06 if new_mode: msg = ExtendedSend(address=self._address, commandtuple=new_mode, userdata=Userdata()) msg.set_checksum() self._send_method(msg, self._mode_change_ack)
def set(self, val): """Set the heat set point.""" msg = ExtendedSend( address=self._address, commandtuple=COMMAND_THERMOSTAT_SET_HEAT_SETPOINT_0X6D_NONE, cmd2=int(val * 2), userdata=Userdata()) msg.set_checksum() self._send_method(msg, self._set_heat_point_ack)
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])
def extended_status_request(self): """Send status request for group/button.""" self._status_received = False user_data = Userdata({'d1': self.group, 'd2': 0x00}) cmd = ExtendedSend(self._address, COMMAND_EXTENDED_GET_SET_0X2E_0X00, userdata=user_data) cmd.set_checksum() self._send_method(cmd, self._status_message_received, True)
def from_raw_message(cls, rawmessage): """Create message from raw byte stream.""" userdata = Userdata.from_raw_message(rawmessage[11:25]) return ExtendedReceive( rawmessage[2:5], rawmessage[5:8], {"cmd1": rawmessage[9], "cmd2": rawmessage[10]}, userdata, flags=rawmessage[8], )
async def _send_led_on_off_request(self, group, val): _LOGGER.debug("OnOffKeypadLed._send_led_on_off_request was called") await self._send_led_change_lock self._new_value = set_bit(self._value, group, bool(val)) user_data = Userdata({'d1': 0x01, 'd2': 0x09, 'd3': self._new_value}) msg = ExtendedSend(self._address, COMMAND_EXTENDED_GET_SET_0X2E_0X00, user_data) msg.set_checksum() self._send_method(msg, self._on_off_ack_received, True)
def _register_messages(self): temp_msg = StandardReceive.template( commandtuple=COMMAND_THERMOSTAT_TEMPERATURE_STATUS_0X6E_NONE, address=self._address, flags=MessageFlags.template(MESSAGE_TYPE_DIRECT_MESSAGE, None)) self._message_callbacks.add(temp_msg, self._temp_received) ext_status_recd = ExtendedReceive.template( commandtuple=COMMAND_EXTENDED_GET_SET_0X2E_0X00, cmd2=0x02, userdata=Userdata.template({"d1": 0x01})) self._message_callbacks.add(ext_status_recd, self._ext_status_received)
def _register_messages(self): cool_set_point_status = StandardReceive.template( address=self._address, commandtuple=COMMAND_THERMOSTAT_COOL_SET_POINT_STATUS_0X71_NONE, flags=MessageFlags.template(MESSAGE_TYPE_DIRECT_MESSAGE, False)) self._message_callbacks.add(cool_set_point_status, self._status_message_received) ext_status_recd = ExtendedReceive.template( commandtuple=COMMAND_EXTENDED_GET_SET_0X2E_0X00, cmd2=0x02, userdata=Userdata.template({"d1": 0x01})) self._message_callbacks.add(ext_status_recd, self._ext_status_received)
def async_refresh_state(self): """Request each state to provide status update.""" _LOGGER.debug('Setting up extended status') ext_status = ExtendedSend( address=self._address, commandtuple=COMMAND_EXTENDED_GET_SET_0X2E_0X00, cmd2=0x02, userdata=Userdata()) ext_status.set_crc() _LOGGER.debug('Sending ext status: %s', ext_status) self._send_msg(ext_status) _LOGGER.debug('Sending temp status request') self.temperature.async_refresh_state()
def to_userdata(self): """Return a Userdata dictionary.""" userdata = Userdata({ 'd3': self.memhi, 'd4': self.memlo, 'd6': self.control_flags, 'd7': self.group, 'd8': self.address[2], 'd9': self.address[1], 'd10': self.address[0], 'd11': self.data1, 'd12': self.data2, 'd13': self.data3 }) return userdata
def _register_messages(self): _LOGGER.debug('Starting HeatSetPoint register_messages') heat_set_point_status = StandardReceive.template( address=self._address, commandtuple=COMMAND_THERMOSTAT_HEAT_SET_POINT_STATUS_0X72_NONE, flags=MessageFlags.template(MESSAGE_TYPE_DIRECT_MESSAGE, False)) self._message_callbacks.add(heat_set_point_status, self._status_message_received) ext_status_recd = ExtendedReceive.template( commandtuple=COMMAND_EXTENDED_GET_SET_0X2E_0X00, cmd2=0x02, flags=MessageFlags.template(MESSAGE_TYPE_DIRECT_MESSAGE, True), userdata=Userdata.template({"d1": 0x01})) _LOGGER.debug('Reg Ext Status: %s', ext_status_recd) self._message_callbacks.add(ext_status_recd, self._ext_status_received)
def write_record(self, mem_addr: int, mode: str, group: int, target, data1=0x00, data2=0x00, data3=0x00): """Write an All-Link database record.""" if not (self._have_first_record() and self._have_last_record()): self.log.error('Must load the ALDB before writing to it') else: self._prior_status = self._status self._status = ALDBStatus.LOADING mem_hi = mem_addr >> 8 mem_lo = mem_addr & 0xff controller = True if mode == 'c' else False control_flag = ControlFlags(True, controller, True, False, False) addr = Address(target) addr_lo = addr.bytes[0] addr_mid = addr.bytes[1] addr_hi = addr.bytes[2] chksum = 0xff - ( (0x2f + 0x02 + mem_hi + mem_lo + 0x08 + control_flag.byte + addr_lo + addr_mid + addr_hi + group + data1 + data2 + data3) & 0xff) + 1 userdata = Userdata({ 'd1': 0, 'd2': 0x02, 'd3': mem_hi, 'd4': mem_lo, 'd5': 0x08, 'd6': control_flag.byte, 'd7': group, 'd8': addr_lo, 'd9': addr_mid, 'd10': addr_hi, 'd11': data1, 'd12': data2, 'd13': data3, 'd14': chksum }) msg = ExtendedSend(self._address, COMMAND_EXTENDED_READ_WRITE_ALDB_0X2F_0X00, userdata=userdata) self.log.info('writing message %s', msg) self._send_method(msg, self._handle_write_aldb_ack, True) self._load_action = LoadAction(mem_addr, 1, 0)
def scene_off(self): """Trigger group/scene to OFF level.""" user_data = Userdata({ 'd1': self._group, 'd2': 0x00, 'd3': 0x00, 'd4': 0x13, 'd5': 0x00, 'd6': 0x00 }) self._set_sent_property(DIMMABLE_KEYPAD_SCENE_ON_LEVEL, 0x00) cmd = ExtendedSend(self._address, COMMAND_EXTENDED_TRIGGER_ALL_LINK_0X30_0X00, user_data) cmd.set_checksum() self._send_method(cmd, self._received_scene_triggered)
def _register_messages(self): ext_msg_aldb_record = ExtendedReceive.template( address=self._address, commandtuple=COMMAND_EXTENDED_READ_WRITE_ALDB_0X2F_0X00, userdata=Userdata.template({'d2': 1}), flags=MessageFlags.template( messageType=MESSAGE_TYPE_DIRECT_MESSAGE, extended=1)) std_msg_pre_nak = StandardReceive.template(flags=MessageFlags.template( messageType=MESSAGE_FLAG_DIRECT_MESSAGE_NAK_0XA0), cmd2=0xfc) ext_msg_pre_nak = ExtendedReceive.template(flags=MessageFlags.template( messageType=MESSAGE_FLAG_DIRECT_MESSAGE_NAK_0XA0), cmd2=0xfc) self._message_callbacks.add(ext_msg_aldb_record, self._handle_aldb_record_received) self._message_callbacks.add(std_msg_pre_nak, self._handle_pre_nak) self._message_callbacks.add(ext_msg_pre_nak, self._handle_pre_nak)
def load(self, mem_addr=0x0000, rec_count=0, retry=0): """Read the device database and load.""" if self._version == ALDBVersion.Null: self._status = ALDBStatus.LOADED self.log.debug('Device has no ALDB') else: self._status = ALDBStatus.LOADING self.log.debug('Tring to lock from load') yield from self._rec_mgr_lock self.log.debug('load yielded lock') mem_hi = mem_addr >> 8 mem_lo = mem_addr & 0xff log_output = 'ALDB read' max_retries = 0 if rec_count: max_retries = ALDB_RECORD_RETRIES if mem_addr == 0x0000: log_output = '{:s} first record'.format(log_output) else: log_output = '{:s} record {:04x}'.format( log_output, mem_addr) else: max_retries = ALDB_ALL_RECORD_RETRIES log_output = '{:s} all records'.format(log_output) if retry: log_output = '{:s} retry {:d} of {:d}'.format( log_output, retry, max_retries) self.log.info(log_output) chksum = 0xff - ((0x2f + mem_hi + mem_lo + 1) & 0xff) + 1 userdata = Userdata({ 'd1': 0, 'd2': 0, 'd3': mem_hi, 'd4': mem_lo, 'd5': rec_count, 'd14': chksum }) msg = ExtendedSend(self._address, COMMAND_EXTENDED_READ_WRITE_ALDB_0X2F_0X00, userdata=userdata) self._send_method(msg, self._handle_read_aldb_ack, True) if not self._load_action: self._set_load_action(mem_addr, rec_count, -1, False)
def del_record(self, mem_addr: int): """Write an All-Link database record.""" record = self._records.get(mem_addr) if not record: self.log.error('Must load the ALDB record before deleting it') else: self._prior_status = self._status self._status = ALDBStatus.LOADING mem_hi = mem_addr >> 8 mem_lo = mem_addr & 0xff controller = record.control_flags.is_controller control_flag = ControlFlags(False, controller, True, False, False) addr = record.address addr_lo = addr.bytes[0] addr_mid = addr.bytes[1] addr_hi = addr.bytes[2] group = record.group data1 = record.data1 data2 = record.data2 data3 = record.data3 chksum = 0xff - ( (0x2f + 0x02 + mem_hi + mem_lo + 0x08 + control_flag.byte + addr_lo + addr_mid + addr_hi + group + data1 + data2 + data3) & 0xff) + 1 userdata = Userdata({ 'd1': 0, 'd2': 0x02, 'd3': mem_hi, 'd4': mem_lo, 'd5': 0x08, 'd6': control_flag.byte, 'd7': group, 'd8': addr_lo, 'd9': addr_mid, 'd10': addr_hi, 'd11': data1, 'd12': data2, 'd13': data3, 'd14': chksum }) msg = ExtendedSend(self._address, COMMAND_EXTENDED_READ_WRITE_ALDB_0X2F_0X00, userdata=userdata) self.log.info('writing message %s', msg) self._send_method(msg, self._handle_write_aldb_ack, True) self._load_action = LoadAction(mem_addr, 1, 0)
def scene_on(self): """Trigger group/scene to ON level.""" user_data = Userdata({ "d1": self._group, "d2": 0x00, "d3": 0x00, "d4": 0x11, "d5": 0xFF, "d6": 0x00, }) self._set_sent_property(DIMMABLE_KEYPAD_SCENE_ON_LEVEL, 0xFF) cmd = ExtendedSend(self._address, COMMAND_EXTENDED_TRIGGER_ALL_LINK_0X30_0X00, user_data) cmd.set_checksum() _LOGGER.debug("Calling scene_on and sending response to " "_received_scene_triggered") self._send_method(cmd, self._received_scene_triggered)
def scene_on(self): """Trigger group/scene to ON level.""" user_data = Userdata({ 'd1': self._group, 'd2': 0x00, 'd3': 0x00, 'd4': 0x11, 'd5': 0xff, 'd6': 0x00 }) self._set_sent_property(DIMMABLE_KEYPAD_SCENE_ON_LEVEL, 0xff) cmd = ExtendedSend(self._address, COMMAND_EXTENDED_TRIGGER_ALL_LINK_0X30_0X00, user_data) cmd.set_checksum() _LOGGER.debug('Calling scene_on and sending response to ' '_received_scene_triggered') self._send_method(cmd, self._received_scene_triggered)
def scene_level(self, level): """Trigger group/scene to input level.""" if level == 0: self.scene_off() else: user_data = Userdata({ "d1": self._group, "d2": 0x00, "d3": 0x00, "d4": 0x11, "d5": level, "d6": 0x00, }) self._set_sent_property(DIMMABLE_KEYPAD_SCENE_ON_LEVEL, level) cmd = ExtendedSend(self._address, COMMAND_EXTENDED_TRIGGER_ALL_LINK_0X30_0X00, user_data) cmd.set_checksum() self._send_method(cmd, self._received_scene_triggered)
async def run_test(loop): mockPLM = MockPLM(loop) linkcode = 0x01 group = 0x00 address = '112233' cat = 0x02 subcat = 0x39 firmware = 0x44 # Create OutletLinc device = insteonplm.devices.create(mockPLM, address, cat, subcat, firmware) # Start the process with an All-Link complete message with # the IM as a controller of Group 0x00 msg = AllLinkComplete(linkcode, group, address, cat, subcat, firmware) device.receive_message(msg) await asyncio.sleep(.1, loop=loop) # The device should start linking based on the groups in # self.states assert mockPLM.sentmessage == StandardSend( device.address, COMMAND_ENTER_LINKING_MODE_0X09_NONE, cmd2=0x01).hex msg = StandardSend(device.address, COMMAND_ENTER_LINKING_MODE_0X09_NONE, cmd2=0x01, acknak=MESSAGE_ACK) device.receive_message(msg) await asyncio.sleep(.1, loop=loop) # Confirm that the link attempt to group 0x01 completed msg = AllLinkComplete(0x00, 0x01, address, cat, subcat, firmware) device.receive_message(msg) await asyncio.sleep(.1, loop=loop) # The device should then start linking to group 0x02 assert mockPLM.sentmessage == StandardSend( device.address, COMMAND_ENTER_LINKING_MODE_0X09_NONE, cmd2=0x02).hex await asyncio.sleep(1, loop=loop) # Confirm that the link attempt to group 0x02 completed msg = AllLinkComplete(0x00, 0x01, address, cat, subcat, firmware) device.receive_message(msg) await asyncio.sleep(.1, loop=loop) # The device will now attempt to read the ALDB msg = ExtendedSend(address, COMMAND_EXTENDED_READ_WRITE_ALDB_0X2F_0X00, userdata=Userdata()) msg.set_checksum() assert mockPLM.sentmessage == msg.hex # Send a dummy ALDB record as a high water mark to end the process msg = ExtendedReceive( address, '111111', commandtuple=COMMAND_EXTENDED_READ_WRITE_ALDB_0X2F_0X00, userdata=Userdata({ 'd1': 0, 'd2': 0x01, 'd3': 0xff, 'd4': 0x77, 'd5': 0, 'd6': 0, 'd7': 0, 'd8': 0, 'd9': 0, 'd10': 0, 'd11': 0, 'd12': 0, 'd13': 0, 'd14': 0x3b })) device.receive_message(msg) await asyncio.sleep(1, loop=loop)