def test_read_only_flag(self): """Test an int read only flag.""" ex_prop = ExtendedProperty(random_address(), "ext_prop", int, False, True) op_flag = OperatingFlag(random_address(), "op_flag", int, False, True) for flag in [ex_prop, op_flag]: orig = randint(0, 100) mod = randint(101, 255) assert not flag.is_loaded flag.load(orig) assert flag.is_loaded assert flag.value == orig assert not flag.is_dirty flag.new_value = mod assert flag.value == orig assert flag.new_value is None assert not flag.is_dirty flag.new_value = None assert not flag.is_dirty assert not flag.is_reversed assert flag.is_read_only
def __init__(self): """Init the MockDevice class.""" self.address = random_address() self.aldb = MockAldb(ALDBStatus.LOADED) self.is_battery = True self.cat = 0x01 self.subcat = 0x02
async def test_off_command(self): """Test an OFF message.""" state_values = {} def state_updated(value, group, name, address): """Run when the state is updated.""" nonlocal state_values state_values[group] = value address = random_address() device = DimmableLightingControl_KeypadLinc_8(address, 0x01, 0x02, 0x03, "Test", "KPL") for button in device.groups: device.groups[button].set_value(255) device.groups[button].subscribe(state_updated) cmd1 = 0x13 cmd2 = 0x00 target = device.address user_data = None ack = "ack.{}.1.{}.direct".format(device.address.id, OFF) direct_ack = "{}.{}.direct_ack".format(device.address.id, OFF) responses = [ TopicItem(ack, cmd_kwargs(cmd1, cmd2, user_data), 0.25), TopicItem(direct_ack, cmd_kwargs(cmd1, cmd2, user_data, target), 0.25), ] send_topics(responses) response = await device.async_off(fast=False) assert response == ResponseStatus.SUCCESS assert state_values.get(1) == cmd2 for button in device.groups: if button == 1: continue assert state_values.get(2) is None
async def _execute_command(self, device_type, command, config): address = random_address() params = config["params"] for param in params: params[param] = convert_to_int(params[param]) if config.get("response"): topic_item = convert_response(config["response"], address) else: topic_item = None device_class = getattr(device_types, device_type) try: device = device_class(address=address, cat=0x01, subcat=0x02, description=device_type) method = getattr(device, command) if topic_item: send_topics([topic_item]) result = await method(**params) assert int(result) == 1 # pylint: disable=broad-except except Exception as ex: _LOGGER.error("Failed: device: %s command: %s", device_type, command) _LOGGER.error(ex) assert False
async def test_send_on_topic(self): """Test sending the ON command.""" async with async_protocol_manager(): received_topic = "" def expected_topic_received(cmd1, cmd2, user_data, topic=pub.AUTO_TOPIC): nonlocal received_topic received_topic = topic.name address = random_address() on_topic = "send.{}.1.direct".format(ON) topics = [ TopicItem(on_topic, { "address": address, "on_level": 0xFF, "group": 0 }, 0) ] self._last_topic = None expected_topic = "ack.{}.1.on.direct".format(address.id) pub.subscribe(expected_topic_received, expected_topic) send_topics(topics) await asyncio.sleep(0.05) assert received_topic == expected_topic
async def test_message_to_topic(self): """Test converting a message to a topic.""" async with async_protocol_manager() as protocol: tests = await import_commands() for test_info in tests: self._topic = None address = repr(random_address()) curr_test = tests[test_info] if curr_test.get("address") is not None: curr_test["address"] = address msgs = [create_message(curr_test)] curr_topic = curr_test["topic"].format(address) subscribe_topic(self.capture_topic, curr_topic) send_data(msgs, protocol.read_queue) try: await asyncio.sleep(0.1) assert self._topic.name == curr_topic except asyncio.TimeoutError: raise AssertionError( "Failed timed out {} with test topic {}".format( test_info, curr_test.get("topic") ) ) except (AssertionError, AttributeError): raise AssertionError( "Failed test {} with test topic {}".format( test_info, curr_test.get("topic") ) ) finally: unsubscribe_topic(self.capture_topic, curr_topic)
async def test_inbound(self): """Test direct command.""" async with async_protocol_manager(auto_ack=False) as protocol: tests = await import_commands() subscribe_topic(self.async_validate_values, pub.ALL_TOPICS) for test_info in tests: self._current_test = test_info test_command = tests[test_info] cmd_class = test_command.get("command") params = test_command.get("params") inbound_message = test_command.get("message") if params.get("address") == "": params["address"] = random_address() inbound_message["address"] = params["address"] self._assert_tests = test_command.get("assert_tests") obj = get_class_or_method(commands, cmd_class) cmd = obj(**params) inbound_data_item = create_message(inbound_message, 0) self._call_count = 0 send_data([inbound_data_item], protocol.read_queue) await sleep(0.1) assert self._call_count == 1 assert self._assert_result
def test_set_toggle_mode(self): """Test setting toggle modes.""" address = random_address() device = DimmableLightingControl_KeypadLinc_8(address, 0x01, 0x02, 0x03, "Test", "KPL") device.properties[NON_TOGGLE_MASK].load(0) device.properties[NON_TOGGLE_ON_OFF_MASK].load(0) masks = { 1: [0, 0], 2: [0, 0], 3: [1, 1], 4: [1, 0], 5: [0, 0], 6: [0, 0], 7: [0, 0], 8: [0, 0], } device.set_toggle_mode(3, 1) device.set_toggle_mode(4, 2) for button in range(1, 9): non_toggle_mask = device.properties[NON_TOGGLE_MASK] non_toggle_on_off_mask = device.properties[NON_TOGGLE_ON_OFF_MASK] assert bit_is_set(non_toggle_mask.new_value, button - 1) == bool(masks[button][0]) assert bit_is_set(non_toggle_on_off_mask.new_value, button - 1) == bool(masks[button][1])
def test_clear_radio_buttons(self): """Test the `set_radio_buttons` feature.""" address = random_address() device = DimmableLightingControl_KeypadLinc_8(address, 0x01, 0x02, 0x03, "Test", "KPL") for button in range(1, 9): button_str = f"_{button}" if button != 1 else "" on_mask = device.properties[f"{ON_MASK}{button_str}"] off_mask = device.properties[f"{OFF_MASK}{button_str}"] on_mask.load(0) off_mask.load(0) device.set_radio_buttons([3, 4, 5, 6]) device.clear_radio_buttons([4, 5]) masks = { 1: None, 2: None, 3: int("00100000", 2), 4: None, 5: None, 6: int("00000100", 2), 7: None, 8: None, } for button in range(1, 9): button_str = f"_{button}" if button != 1 else "" on_mask = device.properties[f"{ON_MASK}{button_str}"] off_mask = device.properties[f"{OFF_MASK}{button_str}"] assert on_mask.new_value == masks[button] assert off_mask.new_value == masks[button]
async def test_create_devices(self): """Test device creation.""" ipdb = IPDB() failed = False for prod in ipdb: addr = random_address() try: prod.deviceclass( address=addr, cat=prod.cat, subcat=prod.subcat, description=prod.description, model=prod.model, ) # pylint: disable=broad-except except Exception as e: cat = None if prod.cat is None else f"{int(prod.cat):02x}" subcat = None if prod.subcat is None else f"{int(prod.subcat):02x}" _LOGGER.error("Failed with cat %s subcat %s", cat, subcat) _LOGGER.error(e) _LOGGER.debug(traceback.format_exc()) failed = True if failed: assert False
async def test_receive_on_msg(self): """Test receiving an ON message.""" async with async_protocol_manager() as protocol: last_topic = None def topic_received(cmd1, cmd2, target, user_data, hops_left, topic=pub.AUTO_TOPIC): """Receive the OFF topic for a device.""" nonlocal last_topic last_topic = topic.name address = random_address() byte_data = create_std_ext_msg(address, 0x80, 0x11, 0xFF, target=Address("000001")) expected_topic = "{}.{}.on.broadcast".format(address.id, 1) pub.subscribe(topic_received, expected_topic) on_cmd = DataItem(byte_data, 0) data = [on_cmd] send_data(data, protocol.read_queue) await asyncio.sleep(0.05) assert last_topic == expected_topic
async def async_send(self): """Send the mock command.""" await asyncio.sleep(0.1) self._call_subscribers(address=random_address(), cat=0x03, subcat=0x01, firmware=0x02) return ResponseStatus.SUCCESS
def setUp(self): """Set up the test.""" self.call_count = 0 self.address = repr(random_address()) set_log_levels( logger="info", logger_pyinsteon="info", logger_messages="info", logger_topics=True, )
async def test_on_with_fast_direct_ack(self): """Test ON with fast direct ack response.""" response_called = False def handle_on_response(on_level): """Handle the ON command response.""" nonlocal response_called response_called = True set_log_levels(logger_topics=True) address = random_address() target = random_address() group = randint(0, 10) cmd = OnLevelCommand(address, group) cmd.subscribe(handle_on_response) ack_topic = build_topic(ON, "ack", address, group, MessageFlagType.DIRECT) direct_ack_topic = build_topic(ON, None, address, None, MessageFlagType.DIRECT_ACK) ack_topic_item = TopicItem( topic=ack_topic, kwargs={ "cmd1": 0x11, "cmd2": 0xFF, "user_data": None }, delay=0.1, ) direct_ack_topic_item = TopicItem( topic=direct_ack_topic, kwargs={ "cmd1": 0x11, "cmd2": 0xFF, "target": target, "user_data": None, "hops_left": 0, }, delay=0, ) send_topics([ack_topic_item, direct_ack_topic_item]) await asyncio.sleep(0.2) assert response_called
async def test_status_with_fast_direct_ack(self): """Test STATUS REQUEST with fast direct ack response.""" response_called = False def handle_status_response(db_version, status): """Handle the ON command response.""" nonlocal response_called response_called = True set_log_levels(logger_topics=True) address = random_address() target = random_address() status_type = randint(0, 10) cmd = StatusRequestCommand(address, status_type) cmd.subscribe(handle_status_response) ack_topic = build_topic(STATUS_REQUEST, "ack", address, None, MessageFlagType.DIRECT) direct_ack_topic = build_topic(ON, None, address, None, MessageFlagType.DIRECT_ACK) ack_topic_item = TopicItem( topic=ack_topic, kwargs={ "cmd1": 0x19, "cmd2": status_type, "user_data": None }, delay=0.1, ) direct_ack_topic_item = TopicItem( topic=direct_ack_topic, kwargs={ "cmd1": 0x11, "cmd2": 0x02, "target": target, "user_data": None, "hops_left": 0, }, delay=0, ) send_topics([ack_topic_item, direct_ack_topic_item]) await asyncio.sleep(0.2) assert response_called
def setUp(self): """Set up the test.""" set_log_levels( logger="debug", logger_pyinsteon="info", logger_messages="info", logger_topics=False, ) _LOGGER.debug("Running setUp") self.record = 0 addr = random_address() self.topics = [ TopicItem( ALL_LINK_RECORD_RESPONSE, fill_rec(0x2E, 0x01, addr.id, 0x02, 0x03, 0x04), 0.2, ), TopicItem( ALL_LINK_RECORD_RESPONSE, fill_rec(0x2E, 0x02, addr.id, 0x02, 0x03, 0x04), 0.2, ), TopicItem( ALL_LINK_RECORD_RESPONSE, fill_rec(0x2E, 0x03, addr.id, 0x02, 0x03, 0x04), 0.2, ), TopicItem( ALL_LINK_RECORD_RESPONSE, fill_rec(0x2E, 0x04, addr.id, 0x02, 0x03, 0x04), 0.2, ), TopicItem( ALL_LINK_RECORD_RESPONSE, fill_rec(0x2E, 0x05, addr.id, 0x02, 0x03, 0x04), 0.2, ), TopicItem( ALL_LINK_RECORD_RESPONSE, fill_rec(0x2E, 0x06, addr.id, 0x02, 0x03, 0x04), 0.2, ), TopicItem( ALL_LINK_RECORD_RESPONSE, fill_rec(0x2E, 0x07, addr.id, 0x02, 0x03, 0x04), 0.2, ), TopicItem( ALL_LINK_RECORD_RESPONSE, fill_rec(0x2E, 0x08, addr.id, 0x02, 0x03, 0x04), 0.2, ), ]
async def test_command(self): """Test direct command.""" async with async_protocol_manager() as protocol: msgs = [] def listen_for_ack(): send_data(msgs, protocol.read_queue) tests = await import_commands() subscribe_topic(self.validate_values, pub.ALL_TOPICS) subscribe_topic(listen_for_ack, "ack") for test_info in tests: address = random_address() self._current_test = test_info test_command = tests[test_info] command = test_command.get("command") cmd_class = command.get("class") params = command.get("params") if params.get("address"): params["address"] = address send_params = command.get("send_params") test_response = test_command.get("response") obj = get_class_or_method(commands, cmd_class) cmd = obj(**params) messages = test_command.get("messages") msgs = [] for msg_dict in messages: #msg_dict = messages[message] msg_dict["address"] = address msgs.append(create_message(msg_dict)) self._assert_tests = test_command.get("assert_tests") self._call_count = 0 try: response = await cmd.async_send(**send_params) except Exception as ex: raise Exception("Failed test {} with error: {}".format( self._current_test, str(ex))) try: if test_response: assert int(response) == test_response if self._assert_tests: call_count = test_command.get("call_count", 1) assert self._call_count == call_count except AssertionError: raise AssertionError( "Failed test: {} command response: {} call count {}". format(self._current_test, response, self._call_count)) await sleep(0.1) assert self._assert_result
def test_reverse_bool_flag(self): """Test a reversed bool writable flag.""" ex_prop = ExtendedProperty(random_address(), "ext_prop", bool, True, False) op_flag = OperatingFlag(random_address(), "op_flag", bool, True, False) for flag in [ex_prop, op_flag]: assert not flag.is_loaded for orig in [True, False]: reverse = not orig flag.load(orig) assert flag.is_loaded assert flag.value == reverse assert not flag.is_dirty flag.new_value = reverse assert flag.value == reverse assert flag.new_value == orig assert flag.is_dirty assert flag.is_reversed assert not flag.is_read_only
def test_is_dirty(self): """Test is_dirty property.""" ex_prop = ExtendedProperty(random_address(), "ext_prop", int, False, False) op_flag = OperatingFlag(random_address(), "op_flag", int, False, False) for flag in [ex_prop, op_flag]: # load(orig) -> not is_dirty # new_value = mod -> is_dirty # new_value = orig => not is_dirty # new_value = None => not is_dirty orig = randint(0, 100) mod = randint(101, 255) assert not flag.is_loaded flag.load(orig) assert flag.is_loaded assert flag.value == orig assert not flag.is_dirty flag.new_value = mod assert flag.value == orig assert flag.new_value == mod assert flag.is_dirty flag.new_value = orig assert flag.value == orig assert flag.new_value is None assert not flag.is_dirty flag.new_value = mod assert flag.is_dirty flag.new_value = None assert flag.value == orig assert flag.new_value is None assert not flag.is_dirty
def __init__(self): """Init the MockDevices class.""" self._devices = {} self.devices_to_add = [] self.modem = create_device(PLM, "11.22.33", 0x03, 0x01) self._devices[self.modem.address] = self.modem device1 = create_device(DimmableLightingControl, random_address(), 0x01, 0x02) self._devices[device1.address] = device1 device2 = create_device(DimmableLightingControl_KeypadLinc_8, random_address(), 0x01, 0x03) self._devices[device2.address] = device2 device3 = create_device(SensorsActuators_IOLink, random_address(), 0x02, 0x04) self._devices[device3.address] = device3 device4 = create_device(ClimateControl_Thermostat, random_address(), 0x07, 0x05) self._devices[device4.address] = device4 # Do not set methods to AyncMock if python version < 3.8 if sys.version_info[0:2] < (3, 8): return for addr in self._devices: self._devices[addr].aldb.async_load = AsyncMock() self.async_save = AsyncMock() self.async_load = AsyncMock() self.set_id = MagicMock() self.async_remove_device = AsyncMock() self.add_x10_device = MagicMock()
async def test_load_empty(self): """Test loading an empty modem ALDB.""" async with LOCK: mgr = pub.getDefaultTopicMgr() mgr.delTopic(ALL_LINK_RECORD_RESPONSE) aldb = ModemALDB(random_address()) pub.subscribe(send_nak_response, SEND_FIRST_TOPIC) response = await aldb.async_load() _LOGGER.debug("Done LOAD function.") _LOGGER.debug("Status: %s", response.name) assert aldb.is_loaded _LOGGER.debug("ALDB Record Count: %d", len(aldb)) assert len(aldb) == 0 pub.unsubscribe(send_nak_response, SEND_FIRST_TOPIC)
def setUp(self): """Set up the test.""" self._address = random_address() self._db_version = None self._status = None self._db_version_1 = None self._status_1 = None self.ack_topic = f"ack.{self._address.id}.status_request.direct" self.direct_ack_topic = f"{self._address.id}.any_topic.direct_ack" set_log_levels( logger="debug", logger_pyinsteon="info", logger_messages="info", logger_topics=True, )
def setUp(self): """Set up the test.""" self.address = random_address() self.group = 6 self.target = Address(f"0000{self.group:02d}") self.on_level_manager = OnLevelManager(self.address, self.group) self.on_level_manager.subscribe(self.handle_on_off) self.on_level = None self.call_count = 0 set_log_levels( logger="info", logger_pyinsteon="info", logger_messages="info", logger_topics=True, )
def setUp(self): """Set up the test.""" self._address = random_address() self._db_version = None self._status = None self._db_version_1 = None self._status_1 = None self.status_command = StatusRequestCommand(self._address, status_type=0) self.status_command.subscribe(self.set_status) self.ack_topic = "ack.{}.status_request.direct".format(self._address.id) self.direct_ack_topic = "{}.any_topic.direct_ack".format(self._address.id) set_log_levels( logger="info", logger_pyinsteon="info", logger_messages="info", logger_topics=False, )
async def test_load_8_records_eeprom(self): """Test loading 8 records into the modem ALDB.""" async with LOCK: mgr = pub.getDefaultTopicMgr() mgr.delTopic(ALL_LINK_RECORD_RESPONSE) pub.subscribe(self.send_eeprom_response, SEND_READ_EEPROM_TOPIC) aldb = ModemALDB(random_address()) aldb.read_write_mode = ReadWriteMode.EEPROM response = await aldb.async_load() await asyncio.sleep(0.01) _LOGGER.debug("Done LOAD function.") _LOGGER.debug("Status: %s", response.name) assert aldb.is_loaded _LOGGER.debug("ALDB Record Count: %d", len(aldb)) assert len(aldb) == 9 # Includes HWM record pub.unsubscribe(self.send_standard_response, SEND_READ_EEPROM_TOPIC)
def test_clear_radio_buttons_when_preset(self): """Test clearing an existing radio button group.""" address = random_address() device = DimmableLightingControl_KeypadLinc_8( address, 0x01, 0x02, 0x03, "Test", "KPL" ) preset_masks = { 1: 0, 2: 0, 3: int("00111000", 2), 4: int("00110100", 2), 5: int("00101100", 2), 6: int("00011100", 2), 7: 0, 8: 0, } for button in range(1, 9): button_str = f"_{button}" if button != 1 else "" on_mask = device.properties[f"{ON_MASK}{button_str}"] off_mask = device.properties[f"{OFF_MASK}{button_str}"] on_mask.load(preset_masks[button]) off_mask.load(preset_masks[button]) device.clear_radio_buttons([4, 5]) masks = { 1: None, 2: None, 3: int("00100000", 2), 4: 0, 5: 0, 6: int("00000100", 2), 7: None, 8: None, } for button in range(1, 9): button_str = f"_{button}" if button != 1 else "" on_mask = device.properties[f"{ON_MASK}{button_str}"] off_mask = device.properties[f"{OFF_MASK}{button_str}"] assert on_mask.new_value == masks[button] assert off_mask.new_value == masks[button]
async def test_dup_increase_hops_lt_MAX_DUP_sec(self): """Test two messages increase hops within 2 seconds => 2 calls.""" with patch.object(pyinsteon.handlers.from_device.broadcast_command, "MIN_DUP", 0.3), patch.object( pyinsteon.handlers.from_device.broadcast_command, "MAX_DUP", 0.5): for topic, command in COMMANDS.items(): group = randint(1, 9) address = random_address() if command in NO_GROUP_CMDS: handler = command(address) else: handler = command(address, group) self.call_count = 0 topics = [ create_topic(topic, address, group, 2, 0.0), create_topic(topic, address, group, 3, 0.4), ] send_topics(topics) await sleep(0.5) assert self.call_count == 2
async def test_dup(self): """Test two messages with Hops reduction within MAX_DUP seconds => 1 call.""" with patch.object(pyinsteon.handlers.from_device.broadcast_command, "MIN_DUP", 0.3), patch.object( pyinsteon.handlers.from_device.broadcast_command, "MAX_DUP", 0.5): for topic, command in COMMANDS.items(): group = randint(1, 9) address = random_address() if command in NO_GROUP_CMDS: handler = command(address) else: handler = command(address, group) topics = [ create_topic(topic, address, group, 3, 0), create_topic(topic, address, group, 2, 0.4), ] self.call_count = 0 send_topics(topics) await sleep(.5) assert self.call_count == 1
def setUp(self): """Set up the test.""" set_log_levels( logger="info", logger_pyinsteon="info", logger_messages="info", logger_topics=False, ) self._address = random_address() self._group = 4 self._hb_mgr = pyinsteon.managers.heartbeat_manager.HeartbeatManager( self._address, self._group) self._heartbeat = None self._heartbeat_on = None self._heartbeat_off = None self._hb_mgr.subscribe(self.heartbeat) self._hb_mgr.subscribe_on(self.heartbeat_on) self._hb_mgr.subscribe_off(self.heartbeat_off) self._on_topic = build_topic(ON, None, self._address, self._group, MessageFlagType.ALL_LINK_BROADCAST) self._off_topic = build_topic(OFF, None, self._address, self._group, MessageFlagType.ALL_LINK_BROADCAST)
def test_class_properties(self): """Test the ExtendedProperty class properties.""" address = random_address() name = "prpp_name" value_type = int is_reversed = bool(random.randint(0, 1)) is_read_only = bool(random.randint(0, 1)) prop_type = PropertyType(random.randint(0, 2)) prop = ExtendedProperty(address, name, value_type, is_reversed, is_read_only, prop_type) assert prop.topic == f"{address.id}.property.{name}" assert prop.name == name assert prop.value_type == value_type assert prop.is_reversed == is_reversed assert prop.is_read_only == is_read_only assert prop.property_type == prop_type prop2 = ExtendedProperty(address, name, value_type, not is_reversed, not is_read_only, prop_type) assert prop2.is_reversed != is_reversed assert prop2.is_read_only != is_read_only