def try_parse(data: str) -> Optional[List[Input]]: """Try to parse the given input text. Will return a list of parsed Inputs. The list might be empty (but not null). :param data str: The input data received from LCN-PCHK :return: The parsed Inputs (never null) :rtype: List with instances of :class:`~pypck.input.Input` """ matcher_pos = PckParser.PATTERN_ACK_POS.match(data) if matcher_pos: addr = LcnAddr( int(matcher_pos.group("seg_id")), int(matcher_pos.group("mod_id")) ) return [ModAck(addr, -1)] matcher_neg = PckParser.PATTERN_ACK_NEG.match(data) if matcher_neg: addr = LcnAddr( int(matcher_neg.group("seg_id")), int(matcher_neg.group("mod_id")) ) return [ModAck(addr, int(matcher_neg.group("code")))] return None
async def test_groups_dynamic_membership_discovery(pchk_server, pypck_client): """Test module scan.""" await pypck_client.async_connect() module = pypck_client.get_address_conn(LcnAddr(0, 10, False)) task = asyncio.create_task(module.request_dynamic_groups()) assert await pchk_server.received(">M000010.GD") await pchk_server.send_message("=M000010.GD008011200051") assert await task == { LcnAddr(0, 11, True), LcnAddr(0, 200, True), LcnAddr(0, 51, True), }
async def test_fire_transmitter_event(hass, entry): """Test the transmitter event is fired.""" await init_integration(hass, entry) events = async_capture_events(hass, "lcn_transmitter") inp = ModStatusAccessControl( LcnAddr(0, 7, False), periphery=AccessControlPeriphery.TRANSMITTER, code="aabbcc", level=0, key=0, action=KeyAction.HIT, ) lcn_connection = MockPchkConnectionManager.return_value await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(events) == 1 assert events[0].event_type == "lcn_transmitter" assert events[0].data["code"] == "aabbcc" assert events[0].data["level"] == 0 assert events[0].data["key"] == 0 assert events[0].data["action"] == "hit"
def test_message_parsing_single_mod_input(message, expected): """Test if InputMod parses message correctly.""" exp = (expected[0])(LcnAddr(0, 10, False), *expected[1:]) inp = InputParser.parse(message) assert len(inp) == 1 assert type(inp[0]) == type(exp) # pylint: disable=unidiomatic-typecheck assert vars(inp[0]) == vars(exp)
async def test_pushed_relay_status_change(hass, entry, lcn_connection): """Test the relay light changes its state on status received.""" device_connection = get_device_connection(hass, (0, 7, False), entry) address = LcnAddr(0, 7, False) states = [False] * 8 # push status "on" states[0] = True inp = ModStatusRelays(address, states) await device_connection.async_process_input(inp) await hass.async_block_till_done() state = hass.states.get("light.light_relay1") assert state is not None assert state.state == STATE_ON # push status "off" states[0] = False inp = ModStatusRelays(address, states) await device_connection.async_process_input(inp) await hass.async_block_till_done() state = hass.states.get("light.light_relay1") assert state is not None assert state.state == STATE_OFF
def get_module_conn( self, addr: LcnAddr, request_serials: bool = True ) -> ModuleConnection: """Create and/or return the given LCN module. The ModuleConnection object is used for further communication with the module (e.g. sending commands). :param addr: The module's address :type addr: :class:`~LcnAddr` :returns: The address connection object (never null) :rtype: `~ModuleConnection` :Example: >>> address = LcnAddr(0, 7, False) >>> module = pchk_connection.get_module_conn(address) >>> module.toggle_output(0, 5) """ assert not addr.is_group if addr.seg_id == 0 and self.local_seg_id != -1: addr = LcnAddr(self.local_seg_id, addr.addr_id, addr.is_group) address_conn = self.address_conns.get(addr, None) if address_conn is None: address_conn = ModuleConnection(self, addr) if request_serials: self.task_registry.create_task(address_conn.request_serials()) self.address_conns[addr] = address_conn return address_conn
async def test_pushed_outputs_status_change(hass, entry, lcn_connection): """Test the outputs cover changes its state on status received.""" device_connection = get_device_connection(hass, (0, 7, False), entry) address = LcnAddr(0, 7, False) state = hass.states.get("cover.cover_outputs") state.state = STATE_CLOSED # push status "open" input = ModStatusOutput(address, 0, 100) await device_connection.async_process_input(input) await hass.async_block_till_done() state = hass.states.get("cover.cover_outputs") assert state is not None assert state.state == STATE_OPENING # push status "stop" input = ModStatusOutput(address, 0, 0) await device_connection.async_process_input(input) await hass.async_block_till_done() state = hass.states.get("cover.cover_outputs") assert state is not None assert state.state not in (STATE_OPENING, STATE_CLOSING) # push status "close" input = ModStatusOutput(address, 1, 100) await device_connection.async_process_input(input) await hass.async_block_till_done() state = hass.states.get("cover.cover_outputs") assert state is not None assert state.state == STATE_CLOSING
def try_parse(data: str) -> Optional[List[Input]]: """Try to parse the given input text. Will return a list of parsed Inputs. The list might be empty (but not null). :param data str: The input data received from LCN-PCHK :return: The parsed Inputs (never null) :rtype: List with instances of :class:`~pypck.input.Input` """ matcher = PckParser.PATTERN_SEND_KEYS_HOST.match(data) if matcher: addr = LcnAddr(int(matcher.group("seg_id")), int(matcher.group("mod_id"))) actions_value = int(matcher.group("actions")) keys_value = int(matcher.group("keys")) mapping = ( lcn_defs.SendKeyCommand.DONTSEND, lcn_defs.SendKeyCommand.HIT, lcn_defs.SendKeyCommand.MAKE, lcn_defs.SendKeyCommand.BREAK, ) actions = [] for idx in range(3): action = mapping[(actions_value >> 2 * idx) & 0x03] actions.append(action) keys = [bool(keys_value >> bit & 0x01) for bit in range(8)] return [ModSendKeysHost(addr, actions, keys)] return None
def try_parse(data: str) -> Optional[List[Input]]: """Try to parse the given input text. Will return a list of parsed Inputs. The list might be empty (but not null). :param data str: The input data received from LCN-PCHK :return: The parsed Inputs (never null) :rtype: List with instances of :class:`~pypck.input.Input` """ matcher = PckParser.PATTERN_STATUS_LEDSANDLOGICOPS.match(data) if matcher: addr = LcnAddr(int(matcher.group("seg_id")), int(matcher.group("mod_id"))) led_states = matcher.group("led_states").upper() states_leds = [lcn_defs.LedStatus(led_state) for led_state in led_states] logic_op_states = matcher.group("logic_op_states").upper() states_logic_ops = [ lcn_defs.LogicOpStatus(logic_op_state) for logic_op_state in logic_op_states ] return [ModStatusLedsAndLogicOps(addr, states_leds, states_logic_ops)] return None
async def test_fire_sendkeys_event(hass, lcn_connection): """Test the send_keys event is fired.""" events = async_capture_events(hass, "lcn_send_keys") inp = ModSendKeysHost( LcnAddr(0, 7, False), actions=[ SendKeyCommand.HIT, SendKeyCommand.MAKE, SendKeyCommand.DONTSEND ], keys=[True, True, False, False, False, False, False, False], ) await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(events) == 4 assert events[0].event_type == "lcn_send_keys" assert events[0].data["key"] == "a1" assert events[0].data["action"] == "hit" assert events[1].event_type == "lcn_send_keys" assert events[1].data["key"] == "a2" assert events[1].data["action"] == "hit" assert events[2].event_type == "lcn_send_keys" assert events[2].data["key"] == "b1" assert events[2].data["action"] == "make" assert events[3].event_type == "lcn_send_keys" assert events[3].data["key"] == "b2" assert events[3].data["action"] == "make"
async def module10( pypck_client: PchkConnectionManager, ) -> AsyncGenerator[ModuleConnection, None]: """Create test module with addr_id 10.""" lcn_addr = LcnAddr(0, 10, False) module = pypck_client.get_module_conn(lcn_addr) yield module await module.cancel_requests()
async def test_multiple_serial_requests(pchk_server, pypck_client): """Test module scan.""" await pypck_client.async_connect() pypck_client.get_address_conn(LcnAddr(0, 10, False)) pypck_client.get_address_conn(LcnAddr(0, 11, False)) pypck_client.get_address_conn(LcnAddr(0, 12, False)) assert await pchk_server.received(">M000010.SN") assert await pchk_server.received(">M000011.SN") assert await pchk_server.received(">M000012.SN") message = "=M000010.SN1AB20A123401FW190B11HW015" await pchk_server.send_message(message) assert await pypck_client.received(message) await pypck_client.async_close()
async def test_add_address_connections(pypck_client): """Test if new address connections are added on request.""" lcn_addr = LcnAddr(0, 10, False) assert lcn_addr not in pypck_client.address_conns addr_conn = pypck_client.get_address_conn(lcn_addr) assert isinstance(addr_conn, ModuleConnection) assert lcn_addr in pypck_client.address_conns
async def test_physical_to_logical_segment_id(pypck_client): """Test conversion from logical to physical segment id.""" pypck_client.local_seg_id = 20 module = pypck_client.get_address_conn(LcnAddr(20, 7, False)) module.async_process_input = AsyncMock() with patch("tests.conftest.MockPchkConnectionManager.is_ready", result=True): inp = ModInput(LcnAddr(20, 7, False)) await pypck_client.async_process_input(inp) inp = ModInput(LcnAddr(0, 7, False)) await pypck_client.async_process_input(inp) inp = ModInput(LcnAddr(4, 7, False)) await pypck_client.async_process_input(inp) assert module.async_process_input.await_count == 3
async def test_dyn_text(pchk_server, pypck_client, text, parts): """dyn_text.""" await pypck_client.async_connect() module = pypck_client.get_address_conn(LcnAddr(0, 10, False)) task = asyncio.create_task(module.dyn_text(3, text)) assert all([ await pchk_server.received(f">M000010!GTDT4{i+1:d}".encode() + part) for i, part in enumerate(parts) ]) task.cancel()
async def test_add_address_connections_by_message(pchk_server, pypck_client): """Test if new address connections are added by received message.""" await pypck_client.async_connect() lcn_addr = LcnAddr(0, 10, False) assert lcn_addr not in pypck_client.address_conns message = ":M000010A1050" await pchk_server.send_message(message) assert await pypck_client.received(message) assert lcn_addr in pypck_client.address_conns
async def test_if_fires_on_transmitter_event(hass, calls, entry): """Test for transmitter event triggers firing.""" await init_integration(hass, entry) address = (0, 7, False) device = get_device(hass, entry, address) assert await async_setup_component( hass, automation.DOMAIN, { automation.DOMAIN: [ { "trigger": { CONF_PLATFORM: "device", CONF_DOMAIN: DOMAIN, CONF_DEVICE_ID: device.id, CONF_TYPE: "transmitter", }, "action": { "service": "test.automation", "data_template": { "test": "test_trigger_transmitter", "code": "{{ trigger.event.data.code }}", "level": "{{ trigger.event.data.level }}", "key": "{{ trigger.event.data.key }}", "action": "{{ trigger.event.data.action }}", }, }, }, ] }, ) inp = ModStatusAccessControl( LcnAddr(*address), periphery=AccessControlPeriphery.TRANSMITTER, code="aabbcc", level=0, key=0, action=KeyAction.HIT, ) lcn_connection = MockPchkConnectionManager.return_value await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(calls) == 1 assert calls[0].data == { "test": "test_trigger_transmitter", "code": "aabbcc", "level": 0, "key": 0, "action": "hit", }
async def test_dont_fire_on_unknown_module(hass, lcn_connection): """Test for no event is fired if an input from an unknown module is received.""" inp = ModStatusAccessControl( LcnAddr(0, 10, False), # unknown module periphery=AccessControlPeriphery.FINGERPRINT, code="aabbcc", ) events = async_capture_events(hass, "lcn_fingerprint") await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(events) == 0
async def test_if_fires_on_send_keys_event(hass, calls, entry): """Test for send_keys event triggers firing.""" await init_integration(hass, entry) address = (0, 7, False) device = get_device(hass, entry, address) assert await async_setup_component( hass, automation.DOMAIN, { automation.DOMAIN: [ { "trigger": { CONF_PLATFORM: "device", CONF_DOMAIN: DOMAIN, CONF_DEVICE_ID: device.id, CONF_TYPE: "send_keys", }, "action": { "service": "test.automation", "data_template": { "test": "test_trigger_send_keys", "key": "{{ trigger.event.data.key }}", "action": "{{ trigger.event.data.action }}", }, }, }, ] }, ) inp = ModSendKeysHost( LcnAddr(*address), actions=[ SendKeyCommand.HIT, SendKeyCommand.DONTSEND, SendKeyCommand.DONTSEND ], keys=[True, False, False, False, False, False, False, False], ) lcn_connection = MockPchkConnectionManager.return_value await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(calls) == 1 assert calls[0].data == { "test": "test_trigger_send_keys", "key": "a1", "action": "hit", }
async def test_dyn_text(pypck_client, text, parts): """dyn_text.""" # await pypck_client.async_connect() module = pypck_client.get_address_conn(LcnAddr(0, 10, False)) with patch.object(ModuleConnection, "send_command") as send_command: await module.dyn_text(3, text) send_command.assert_awaited() await_args = (call.args for call in send_command.await_args_list) _, commands = zip(*await_args) for i, part in enumerate(parts): assert f"GTDT4{i+1:d}".encode() + part in commands
def physical_to_logical(self, addr: LcnAddr) -> LcnAddr: """Convert the physical segment id of an address to the logical one. :param addr: The module's/group's address :type addr: :class:`~LcnAddr` :returns: The module's/group's address :rtype: :class:`~LcnAddr` """ return LcnAddr( self.local_seg_id if addr.seg_id in (0, 4) else addr.seg_id, addr.addr_id, addr.is_group, )
async def test_module_sn_response(pchk_server, pypck_client): """Test module scan.""" await pypck_client.async_connect() module = pypck_client.get_address_conn(LcnAddr(0, 7, False)) message = "=M000007.SN1AB20A123401FW190B11HW015" await pchk_server.send_message(message) assert await pypck_client.received(message) assert await module.serial_known assert module.hardware_serial == 0x1AB20A1234 assert module.manu == 1 assert module.software_serial == 0x190B11 assert module.hardware_type.value == 15
def try_parse(data: str) -> Optional[List[Input]]: """Try to parse the given input text. Will return a list of parsed Inputs. The list might be empty (but not null). :param data str: The input data received from LCN-PCHK :return: The parsed Inputs (never null) :rtype: List with instances of :class:`~pypck.input.Input` """ matcher = PckParser.PATTERN_STATUS_GROUPS.match(data) if matcher: addr = LcnAddr(int(matcher.group("seg_id")), int(matcher.group("mod_id"))) dynamic = matcher.group("kind") == "D" max_groups = int(matcher.group("max_groups")) groups = [ LcnAddr(addr.seg_id, int(group), True) for group in matcher.groups()[4:] if group is not None ] return [ModStatusGroups(addr, dynamic, max_groups, groups)] return None
async def test_fire_transponder_event(hass, lcn_connection): """Test the transponder event is fired.""" events = async_capture_events(hass, "lcn_transponder") inp = ModStatusAccessControl( LcnAddr(0, 7, False), periphery=AccessControlPeriphery.TRANSPONDER, code="aabbcc", ) await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(events) == 1 assert events[0].event_type == "lcn_transponder" assert events[0].data["code"] == "aabbcc"
async def test_fire_fingerprint_event(hass, lcn_connection): """Test the fingerprint event is fired.""" events = async_capture_events(hass, "lcn_fingerprint") inp = ModStatusAccessControl( LcnAddr(0, 7, False), periphery=AccessControlPeriphery.FINGERPRINT, code="aabbcc", ) await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(events) == 1 assert events[0].event_type == "lcn_fingerprint" assert events[0].data["code"] == "aabbcc"
async def test_dont_fire_on_unknown_module(hass, entry): """Test for no event is fired if an input from an unknown module is received.""" await init_integration(hass, entry) inp = ModStatusAccessControl( LcnAddr(0, 10, False), # unknown module periphery=AccessControlPeriphery.FINGERPRINT, code="aabbcc", ) lcn_connection = MockPchkConnectionManager.return_value events = async_capture_events(hass, "lcn_transmitter") await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(events) == 0
def set_local_seg_id(self, local_seg_id: int) -> None: """Set the local segment id. :param int local_seg_id: The local segment_id. """ old_local_seg_id = self.local_seg_id self.local_seg_id = local_seg_id # replace all address_conns with current local_seg_id with new # local_seg_id for addr in list(self.address_conns): if addr.seg_id == old_local_seg_id: address_conn = self.address_conns.pop(addr) address_conn.addr = LcnAddr( self.local_seg_id, addr.addr_id, addr.is_group ) self.address_conns[address_conn.addr] = address_conn
async def test_if_fires_on_transponder_event(hass, calls, entry, lcn_connection): """Test for transponder event triggers firing.""" address = (0, 7, False) device = get_device(hass, entry, address) assert await async_setup_component( hass, automation.DOMAIN, { automation.DOMAIN: [ { "trigger": { CONF_PLATFORM: "device", CONF_DOMAIN: DOMAIN, CONF_DEVICE_ID: device.id, CONF_TYPE: "transponder", }, "action": { "service": "test.automation", "data_template": { "test": "test_trigger_transponder", "code": "{{ trigger.event.data.code }}", }, }, }, ] }, ) inp = ModStatusAccessControl( LcnAddr(*address), periphery=AccessControlPeriphery.TRANSPONDER, code="aabbcc", ) await lcn_connection.async_process_input(inp) await hass.async_block_till_done() assert len(calls) == 1 assert calls[0].data == { "test": "test_trigger_transponder", "code": "aabbcc", }
def try_parse(data: str) -> Optional[List[Input]]: """Try to parse the given input text. Will return a list of parsed Inputs. The list might be empty (but not null). :param data str: The input data received from LCN-PCHK :return: The parsed Inputs (never null) :rtype: List with instances of :class:`~pypck.input.Input` """ matcher = PckParser.PATTERN_SEND_COMMAND_HOST.match(data) if matcher: addr = LcnAddr(int(matcher.group("seg_id")), int(matcher.group("mod_id"))) parameters = tuple( int(param) for param in matcher.groups()[2:] if param is not None ) return [ModSendCommandHost(addr, parameters)] return None
async def test_pushed_output_status_change(hass, entry, lcn_connection): """Test the output switch changes its state on status received.""" device_connection = get_device_connection(hass, (0, 7, False), entry) address = LcnAddr(0, 7, False) # push status "on" inp = ModStatusOutput(address, 0, 100) await device_connection.async_process_input(inp) await hass.async_block_till_done() state = hass.states.get("switch.switch_output1") assert state.state == STATE_ON # push status "off" inp = ModStatusOutput(address, 0, 0) await device_connection.async_process_input(inp) await hass.async_block_till_done() state = hass.states.get("switch.switch_output1") assert state.state == STATE_OFF