def test_receive_encapsulated_command(self, rx, tx): rx('MULTI_CMD_ENCAP', commands=[ make_object(command=[0x72, 0x04]), make_object(command=[0x5E, 0x01]) ]) tx('MULTI_CMD_ENCAP', commands=[ make_object(command=[0x72, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03]), make_object(command=[0x5E, 0x02, 0x01, 0x02, 0x03, 0x00, 0x04, 0x00, 0x05]) ])
def test_version_get(self, rx, tx): rx('VERSION_GET') tx('VERSION_REPORT', protocol_library_type=0x06, protocol_version=make_object(major=1, minor=2), application_version=make_object(major=3, minor=4), hardware_version=5, firmware_versions=[ make_object(major=6, minor=7), make_object(major=8, minor=9) ])
async def test_handle_long_command(self, rx_encrypted, tx_encrypted, bootstrap): await bootstrap() rx_encrypted('MULTI_CMD_ENCAP', Version1.class_id, commands=[make_object(command=[0x86, 0x13, 0x98])] * 8) await tx_encrypted( 'MULTI_CMD_ENCAP', MultiCmd1.class_id, commands=[make_object(command=[0x86, 0x14, 0x98, 0x01])] * 8)
def test_set(self, rx, tx, tx_client, assert_observed, channel, lifeline, association_group_2): rx('MULTI_CHANNEL_ASSOCIATION_SET', group_id=1, node_ids=[4, 5], multi_channel_destinations=[ make_object(node_id=3, bit_address=False, endpoint=4), make_object(node_id=5, bit_address=True, endpoint=0b00000101) ]) assert_observed(channel) assert lifeline.targets == {1: {0, 1}, 3: {1, 2, 4}, 4: {0, 3}, 5: {0, 1, 3}} assert association_group_2.targets == {3: {0, 1}, 4: {1, 2}, 5: {8}}
def test_get(self, rx, tx, lifeline, association_group_2): rx('MULTI_CHANNEL_ASSOCIATION_GET', group_id=2) tx('MULTI_CHANNEL_ASSOCIATION_REPORT', group_id=2, max_nodes_supported=255, reports_to_follow=0, node_ids=[3], multi_channel_destinations=[ make_object(node_id=3, endpoint=0b00000001, bit_address=True), make_object(node_id=4, endpoint=0b00000011, bit_address=True), make_object(node_id=5, endpoint=8, bit_address=False) ])
async def test_send_long_response(self, rx_encrypted, tx_encrypted, bootstrap): await bootstrap() rx_encrypted('MULTI_CMD_ENCAP', MultiCmd1.class_id, commands=[make_object(command=[0x72, 0x04])] * 3) await tx_encrypted( 'MULTI_CMD_ENCAP', MultiCmd1.class_id, commands=[ make_object( command=[0x72, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03]) ] * 3)
def send_encapsulated_command(self, context: Context, command: List[int]): if len(command) <= MAX_SECURITY_FRAME_LENGTH: payload = make_object(sequenced=False, second=False, sequence_counter=0, command=command) async def flow(): try: self.send_nonce_get(context) nonce = await self.external_nonce_table[context.node_id ].get() await self.encrypt_and_send(context, payload, nonce) except asyncio.TimeoutError: pass asyncio.create_task(flow()) else: assert len(command) <= MAX_SECURITY_FRAME_LENGTH * 2 sequence = next(self.sequence_counter) first_command = command[:MAX_SECURITY_FRAME_LENGTH] first_payload = make_object(sequenced=True, second=False, sequence_counter=sequence, command=first_command) second_command = command[MAX_SECURITY_FRAME_LENGTH:] second_payload = make_object(sequenced=True, second=True, sequence_counter=sequence, command=second_command) async def flow(): try: self.send_nonce_get(context) nonce = await self.external_nonce_table[context.node_id ].get() await self.encrypt_and_send(context, first_payload, nonce) nonce = await self.external_nonce_table[context.node_id ].get() await self.encrypt_and_send(context, second_payload, nonce) except asyncio.TimeoutError: pass asyncio.create_task(flow())
async def start(self): yield AddNodeStatus.LEARN_READY, 0, None self.network_controller.broadcast_message('ADD_NODE_STARTED', {}) try: old_home_id, old_node_id, node_info = await self.node_info yield AddNodeStatus.NODE_FOUND, 0, None if old_home_id == self.network_controller.home_id: yield AddNodeStatus.FAILED, 0, None return self.new_node_id = self.generate_node_id() self.network_controller.node_infos.add( self.new_node_id, make_object( basic=node_info.basic, generic=node_info.generic, specific=node_info.specific, # Controller doesn't store command classes, but this field is still required for serialization command_class_ids=[])) yield AddNodeStatus.ADDING_SLAVE, self.new_node_id, node_info self.network_controller.send_message( old_home_id, old_node_id, 'ADD_TO_NETWORK', {'newNodeId': self.new_node_id}) yield AddNodeStatus.PROTOCOL_DONE, self.new_node_id, None except CancelledError: pass
def included_node(nif, node_info_repository): node_info = make_object(basic=0x04, generic=nif.generic, specific=nif.specific, command_class_ids=[]) node_info_repository.add(2, node_info) yield node_info
def inner(command: List[int]): rx('SECURITY_NONCE_GET') tx('SECURITY_NONCE_REPORT', nonce=get_last_nonce()) rx_payload( 'SECURITY_MESSAGE_ENCAPSULATION_NONCE_GET', make_object(sequenced=True, second=False, sequence_counter=0, command=command[:26])) tx('SECURITY_NONCE_REPORT', nonce=get_last_nonce()) rx_payload( 'SECURITY_MESSAGE_ENCAPSULATION', make_object(sequenced=True, second=True, sequence_counter=0, command=command[26:]))
def get_destinations(self, group_id: int) -> Tuple[List[int], List[Object]]: node_ids = [] multi_channel_destinations = [] for node_id, endpoints in self.channel.associations.get_destinations(group_id): # Note: endpoints are sorted it = RangeIterator(endpoints) # If there's a 0 endpoint, add to node_ids for _ in it.takewhile(lambda channel: channel == 0, peek_last=True): node_ids.append(node_id) # Add endpoints 1 to 7 into a single destination via bitmask if (mask := create_mask(it.takewhile(lambda channel: channel <= 7, peek_last=True), start=1)) != 0: multi_channel_destinations.append(make_object(node_id=node_id, endpoint=mask, bit_address=True)) # Add the rest of endpoints as separate destinations multi_channel_destinations.extend(make_object(node_id=node_id, endpoint=endpoint, bit_address=False) for endpoint in it)
def get_node_info(self) -> Object: return make_object( generic=self.root_channel.generic, specific=self.root_channel.specific, command_class_ids=[ cc.class_id for cc in self.root_channel.command_classes.values() if cc.advertise_in_nif and cc.supported_non_securely ])
def test_list_of_composites_field(from_bytes_converter, to_bytes_converter): schema = Schema("", [ NumberOfField(field_name="commands"), ListField(name="commands", element_type=Schema("_", [ LengthOfField(field_name="command"), ListField(name="command") ])) ]) data = [0x03, 0x01, 0x0A, 0x03, 0x0A, 0x0B, 0x0C, 0x02, 0x0A, 0x0B] packet = from_bytes_converter.convert(schema, data) assert packet.commands == [ make_object(command=[0x0A]), make_object(command=[0x0A, 0x0B, 0x0C]), make_object(command=[0x0A, 0x0B]), ] assert to_bytes_converter.convert(schema, packet) == data
def test_remove_from_group(self, rx, tx, assert_observed, channel, lifeline, association_group_2): rx('MULTI_CHANNEL_ASSOCIATION_REMOVE', group_id=1, node_ids=[1], multi_channel_destinations=[ make_object(node_id=4, endpoint=3, bit_address=False) ]) assert_observed(channel) assert lifeline.targets == {1: {1}, 3: {1, 2}} assert association_group_2.targets == {3: {0, 1}, 4: {1, 2}, 5: {8}}
def send_group_command_list_report(self, context: Context, group_id: int): group = self.channel.associations.get_group(group_id) commands = [ make_object(class_id=class_id, command_id=command_id) for class_id, command_id in group.commands ] self.send_command(context, 'ASSOCIATION_GROUP_COMMAND_LIST_REPORT', group_id=group_id, commands=commands)
async def inner(command: List[int]): await asyncio.sleep(0) tx('SECURITY_NONCE_GET') await rx_nonce('SECURITY_NONCE_REPORT') tx_payload( 'SECURITY_MESSAGE_ENCAPSULATION_NONCE_GET', make_object(sequenced=True, second=False, sequence_counter=0, command=command[:26])) await rx_nonce('SECURITY_NONCE_REPORT') tx_payload( 'SECURITY_MESSAGE_ENCAPSULATION', make_object(sequenced=True, second=True, sequence_counter=0, command=command[26:]))
def test_software_get(self, rx, tx): rx('VERSION_ZWAVE_SOFTWARE_GET') tx('VERSION_ZWAVE_SOFTWARE_REPORT', sdk_version=make_object(major=1, minor=2, patch=3), zwave_application_framework=make_object(api_version=make_object( major=2, minor=3, patch=4), build_number=1), host_interface=make_object(api_version=make_object(major=3, minor=4, patch=5), build_number=2), zwave_protocol=make_object(api_version=make_object(major=4, minor=5, patch=6), build_number=3), application=make_object(api_version=make_object(major=5, minor=6, patch=7), build_number=4))
async def test_application_node_information(rx_network, tx_req): rx_network( 'APPLICATION_NODE_INFORMATION', { 'source': { 'homeId': 0xC0000000, 'nodeId': 2 }, 'nodeInfo': { 'generic': 0x10, 'specific': 0x01, 'commandClassIds': [0x72, 0x5E] } }) await tx_req('APPLICATION_SLAVE_UPDATE', status=UpdateStatus.NODE_INFO_RECEIVED, node_id=2, node_info=make_object(basic=0x04, generic=0x10, specific=0x01, command_class_ids=[0x72, 0x5E]))
def send_encapsulated_command(self, context: Context, commands: List[List[int]]): self.send_command( context, 'MULTI_CMD_ENCAP', commands=[make_object(command=command) for command in commands])
def test_extra_field(to_bytes_converter): schema = Schema("", [IntField(name="hello")]) data = [0x11] packet = make_object(hello=0x11, bye=0x22) assert to_bytes_converter.convert(schema, packet) == data
def test_version_get(self, rx, tx): rx('VERSION_GET') tx('VERSION_REPORT', protocol_library_type=0x06, protocol_version=make_object(major=1, minor=2), application_version=make_object(major=3, minor=4))
def to_major_minor_patch(cls, data: MajorMinorPatch) -> Object: return make_object(major=data[0], minor=data[1], patch=data[2])
def to_major_minor(cls, data: MajorMinor) -> Object: return make_object(major=data[0], minor=data[1])
make_command(0x59, 'ASSOCIATION_GROUP_INFO_GET', refresh_cache=True, list_mode=False, group_id=0x02)), ([ 0x59, 0x04, 0x82, 0x02, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x07, 0x00, 0x00, 0x00 ], make_command(0x59, 'ASSOCIATION_GROUP_INFO_REPORT', list_mode=True, dynamic_info=False, groups=[ make_object(group_id=0x02, profile=make_object(generic=0x03, specific=0x04)), make_object(group_id=0x05, profile=make_object(generic=0x06, specific=0x07)) ])), ([0x59, 0x05, 0x80, 0x02], make_command(0x59, 'ASSOCIATION_GROUP_COMMAND_LIST_GET', allow_cache=True, group_id=0x02)), ([0x59, 0x06, 0x01, 0x04, 0x03, 0x04, 0x05, 0x06], make_command(0x59, 'ASSOCIATION_GROUP_COMMAND_LIST_REPORT', group_id=0x01, commands=[
def to_software_info(cls, data: MajorMinorPatch, build_number: int) -> Object: return make_object(api_version=cls.to_major_minor_patch(data), build_number=build_number)
), ( [0x13, 0x01, 0x02], make_packet('SEND_DATA', function_id=0x01, tx_status=0x02) ), ( [0x42, 0x01], make_packet('SET_DEFAULT', function_id=0x01) ), ( [0x49, 0x01, 0x02, 0x05, 0x04, 0x05, 0x06, 0x07, 0x08], make_packet('APPLICATION_SLAVE_UPDATE', status=0x01, node_id=0x02, node_info=make_object(basic=0x04, generic=0x05, specific=0x06, command_class_ids=[0x07, 0x08])) ), ( [0x4A, 0x00, 0x01, 0x02, 0x05, 0x04, 0x05, 0x06, 0x07, 0x08], make_packet('ADD_NODE_TO_NETWORK', function_id=0x00, status=0x01, source=0x02, node_info=make_object(basic=0x04, generic=0x05, specific=0x06, command_class_ids=[0x07, 0x08])) ), ( [0x4B, 0x00, 0x01, 0x02, 0x05, 0x04, 0x05, 0x06, 0x07, 0x08],
def nif(): yield make_object(generic=2, specific=3, command_class_ids=[0x72, 0x5E, 0x20])
make_command(0x60, 'MULTI_CHANNEL_CMD_ENCAP', 3, source_endpoint=0x01, bit_address=False, destination=0x02, command=[0x03, 0x04])) ] COMMAND_CLASS_MULTI_CMD_1 = [ ([0x8F, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x02, 0x04, 0x05], make_command(0x60, 'MULTI_CMD_ENCAP', 1, commands=[ make_object(command=[0x01, 0x02, 0x03]), make_object(command=[0x04, 0x05]) ])) ] COMMAND_CLASS_SECURITY_1 = [ ([0x98, 0x02], make_command(0x98, 'SECURITY_COMMANDS_SUPPORTED_GET', 1)), ([0x98, 0x03, 0x01, 0x02, 0x03], make_command(0x98, 'SECURITY_COMMANDS_SUPPORTED_REPORT', 1, reports_to_follow=0x01, command_class_ids=[0x02, 0x03])), ([0x98, 0x04, 0x01], make_command(0x98, 'SECURITY_SCHEME_GET',
def visit_list_field(self, field: ListField, obj: Object): for value in getattr(obj, field.name): proxy = make_object(_=value) yield from self.visit(field.element_type, proxy)
def get_group_info(self, group_id: int): group = self.channel.associations.get_group(group_id) return make_object(group_id=group_id, profile=make_object(generic=group.profile[0], specific=group.profile[1]))