def test_end_to_end_group_write_binary_off(self): """Test parsing and streaming CEMIFrame KNX/IP packet, switch off light in my kitchen.""" # Switch off Kitchen-L1 raw = bytes.fromhex("0610053000112900BCD0FFF90149010080") xknx = XKNX() knxipframe = KNXIPFrame(xknx) knxipframe.from_knx(raw) telegram = knxipframe.body.cemi.telegram assert telegram == Telegram( destination_address=GroupAddress("329"), payload=GroupValueWrite(DPTBinary(0)), source_address=IndividualAddress("15.15.249"), ) cemi = CEMIFrame(xknx, src_addr=IndividualAddress("15.15.249")) cemi.telegram = telegram cemi.set_hops(5) routing_indication = RoutingIndication(xknx, cemi=cemi) knxipframe2 = KNXIPFrame.init_from_body(routing_indication) assert knxipframe2.header.to_knx() == list(raw[0:6]) assert knxipframe2.body.to_knx() == list(raw[6:]) assert knxipframe2.to_knx() == list(raw)
async def service_send_to_knx_bus(self, call): """Service for sending an arbitrary KNX message to the KNX bus.""" attr_address = call.data.get(KNX_ADDRESS) attr_payload = call.data.get(SERVICE_KNX_ATTR_PAYLOAD) attr_type = call.data.get(SERVICE_KNX_ATTR_TYPE) payload: DPTBinary | DPTArray if attr_type is not None: transcoder = DPTBase.parse_transcoder(attr_type) if transcoder is None: raise ValueError(f"Invalid type for knx.send service: {attr_type}") payload = DPTArray(transcoder.to_knx(attr_payload)) elif isinstance(attr_payload, int): payload = DPTBinary(attr_payload) else: payload = DPTArray(attr_payload) for address in attr_address: telegram = Telegram( destination_address=GroupAddress(address), payload=GroupValueWrite(payload), ) await self.xknx.telegrams.put(telegram)
def test_callback_negative_address_filters(self): """Test telegram_received_callback after state of switch was changed.""" xknx = XKNX() async_telegram_received_callback = AsyncMock() xknx.telegram_queue.register_telegram_received_cb( async_telegram_received_callback, address_filters=[ AddressFilter("2/4-8/*"), AddressFilter("1/2/8-") ], ) telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram) self.loop.run_until_complete( xknx.telegram_queue._process_all_telegrams()) async_telegram_received_callback.assert_not_called()
def test_to_process_error(self): """Test process errornous telegram.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueDpt2ByteUnsigned( xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram(group_address=GroupAddress("1/2/3"), payload=DPTBinary(1)) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram(group_address=GroupAddress("1/2/3"), payload=DPTArray((0x64, ))) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( group_address=GroupAddress("1/2/3"), payload=DPTArray(( 0x64, 0x53, 0x42, )), ) self.loop.run_until_complete(remote_value.process(telegram))
def test_maximum_apci(self): """Test parsing and streaming CEMIFrame KNX/IP packet, testing maximum APCI.""" telegram = Telegram( destination_address=GroupAddress(337), payload=GroupValueWrite(DPTBinary(DPTBinary.APCI_MAX_VALUE)), source_address=IndividualAddress("1.3.1"), ) cemi = CEMIFrame(src_addr=IndividualAddress("1.3.1")) cemi.telegram = telegram cemi.set_hops(5) routing_indication = RoutingIndication(cemi=cemi) knxipframe = KNXIPFrame.init_from_body(routing_indication) raw = bytes.fromhex( "06 10 05 30 00 11 29 00 bc d0 13 01 01 51 01 00 bf") assert knxipframe.to_knx() == raw knxipframe2 = KNXIPFrame() knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION) knxipframe2.from_knx(knxipframe.to_knx()) assert knxipframe2.body.cemi.telegram == telegram
def test_end_to_end_group_response(self): """Test parsing and streaming CEMIFrame KNX/IP packet, group response.""" # Incoming state raw = bytes.fromhex( "06 10 05 30 00 11 29 00 bc d0 13 01 01 88 01 00 41") knxipframe = KNXIPFrame() knxipframe.from_knx(raw) telegram = knxipframe.body.cemi.telegram assert telegram == Telegram( destination_address=GroupAddress("392"), payload=GroupValueResponse(DPTBinary(1)), source_address=IndividualAddress("1.3.1"), ) cemi = CEMIFrame(src_addr=IndividualAddress("1.3.1")) cemi.telegram = telegram cemi.set_hops(5) routing_indication = RoutingIndication(cemi=cemi) knxipframe2 = KNXIPFrame.init_from_body(routing_indication) assert knxipframe2.header.to_knx() == raw[0:6] assert knxipframe2.body.to_knx() == raw[6:] assert knxipframe2.to_knx() == raw
async def test_to_process_error(self): """Test process errornous telegram.""" xknx = XKNX() remote_value = RemoteValueDpt2ByteUnsigned( xknx, group_address=GroupAddress("1/2/3")) with pytest.raises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTBinary(1)), ) await remote_value.process(telegram) with pytest.raises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray((0x64, ))), ) await remote_value.process(telegram) with pytest.raises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray((0x64, 0x53, 0x42))), ) await remote_value.process(telegram)
async def test_process_reset_after(self): """Test process / reading telegrams from telegram queue.""" xknx = XKNX() reset_after_sec = 0.001 async_after_update_callback = AsyncMock() binaryinput = BinarySensor( xknx, "TestInput", "1/2/3", reset_after=reset_after_sec, device_updated_cb=async_after_update_callback, ) telegram_on = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTBinary(1)), ) await binaryinput.process(telegram_on) assert binaryinput.state await asyncio.sleep(reset_after_sec * 1.2) assert not binaryinput.state # once for 'on' and once for 'off' assert async_after_update_callback.call_count == 2 async_after_update_callback.reset_mock() # multiple telegrams during reset_after time period shall reset timer await binaryinput.process(telegram_on) async_after_update_callback.assert_called_once() await binaryinput.process(telegram_on) await binaryinput.process(telegram_on) # second and third telegram resets timer but doesn't run callback async_after_update_callback.assert_called_once() assert binaryinput.state await asyncio.sleep(reset_after_sec * 1.2) assert not binaryinput.state # once for 'on' and once for 'off' assert async_after_update_callback.call_count == 2
def test_maximum_apci(self): """Test parsing and streaming CEMIFrame KNX/IP packet, testing maximum APCI.""" telegram = Telegram(group_address=GroupAddress(337), payload=DPTBinary(DPTBinary.APCI_MAX_VALUE)) xknx = XKNX(loop=self.loop) knxipframe = KNXIPFrame(xknx) knxipframe.init(KNXIPServiceType.ROUTING_INDICATION) knxipframe.body.cemi.src_addr = PhysicalAddress("1.3.1") knxipframe.body.cemi.telegram = telegram knxipframe.body.cemi.set_hops(5) knxipframe.normalize() raw = ( 0x06, 0x10, 0x05, 0x30, 0x00, 0x11, 0x29, 0x00, 0xBC, 0xD0, 0x13, 0x01, 0x01, 0x51, 0x01, 0x00, 0xBF, ) self.assertEqual(knxipframe.to_knx(), list(raw)) knxipframe2 = KNXIPFrame(xknx) knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION) knxipframe2.from_knx(knxipframe.to_knx()) self.assertEqual(knxipframe2.body.cemi.telegram, telegram)
async def main() -> None: """Test connection with secure.""" connection_config = ConnectionConfig( connection_type=ConnectionType.TUNNELING_TCP_SECURE, gateway_ip="192.168.1.188", secure_config=SecureConfig( user_id=4, knxkeys_file_path="/home/marvin/testcase.knxkeys", knxkeys_password="******", ), ) xknx = XKNX(connection_config=connection_config) await xknx.start() await xknx.knxip_interface.send_telegram( Telegram( GroupAddress("1/0/32"), TelegramDirection.OUTGOING, GroupValueWrite(DPTBinary(1)), )) await asyncio.sleep(5) print("Tunnel connected") await xknx.stop()
async def test_process_callback(self): """Test after_update_callback after state of switch was changed.""" xknx = XKNX() switch = BinarySensor(xknx, "TestInput", group_address_state="1/2/3", ignore_internal_state=False) async_after_update_callback = AsyncMock() switch.register_device_updated_cb(async_after_update_callback) telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTBinary(1)), ) await switch.process(telegram) # no _context_task started because ignore_internal_state is False assert switch._context_task is None async_after_update_callback.assert_called_once_with(switch) async_after_update_callback.reset_mock() # send same telegram again await switch.process(telegram) async_after_update_callback.assert_not_called()
def test_value_reader_read_success(self, timeout_mock): """Test value reader: successfull read.""" xknx = XKNX() test_group_address = GroupAddress("0/0/0") response_telegram = Telegram( group_address=test_group_address, telegramtype=TelegramType.GROUP_RESPONSE, direction=TelegramDirection.INCOMING, payload=DPTBinary(1), ) value_reader = ValueReader(xknx, test_group_address) # Create a task for read() (3.5 compatible) read_task = asyncio.ensure_future(value_reader.read()) # receive the response self.loop.run_until_complete( value_reader.telegram_received(response_telegram)) # and yield the result successfull_read = self.loop.run_until_complete( asyncio.gather(read_task))[0] # GroupValueRead telegram is still in the queue because we are not actually processing it self.assertEqual(xknx.telegrams.qsize(), 1) # Callback was removed again self.assertEqual(xknx.telegram_queue.telegram_received_cbs, []) # Timeout handle was cancelled (cancelled method requires Python 3.7) event_has_cancelled = getattr(value_reader.timeout_handle, "cancelled", None) if callable(event_has_cancelled): self.assertTrue(value_reader.timeout_handle.cancelled()) # timeout() was never called because there was no timeout timeout_mock.assert_not_called() # Telegram was received self.assertEqual(value_reader.received_telegram, response_telegram) # Successfull read() returns the telegram self.assertEqual(successfull_read, response_telegram)
def test_value_reader_read_success(self): """Test value reader: successfull read.""" xknx = XKNX() test_group_address = GroupAddress("0/0/0") response_telegram = Telegram( destination_address=test_group_address, direction=TelegramDirection.INCOMING, payload=GroupValueResponse(DPTBinary(1)), ) value_reader = ValueReader(xknx, test_group_address) # receive the response self.loop.run_until_complete(value_reader.telegram_received(response_telegram)) # and yield the result successfull_read = self.loop.run_until_complete(value_reader.read()) # GroupValueRead telegram is still in the queue because we are not actually processing it self.assertEqual(xknx.telegrams.qsize(), 1) # Callback was removed again self.assertEqual(xknx.telegram_queue.telegram_received_cbs, []) # Telegram was received self.assertEqual(value_reader.received_telegram, response_telegram) # Successfull read() returns the telegram self.assertEqual(successfull_read, response_telegram)
async def test_value_reader_read_success(self): """Test value reader: successfull read.""" xknx = XKNX() test_group_address = GroupAddress("0/0/0") response_telegram = Telegram( destination_address=test_group_address, direction=TelegramDirection.INCOMING, payload=GroupValueResponse(DPTBinary(1)), ) value_reader = ValueReader(xknx, test_group_address) # receive the response await value_reader.telegram_received(response_telegram) # and yield the result successfull_read = await value_reader.read() # GroupValueRead telegram is still in the queue because we are not actually processing it assert xknx.telegrams.qsize() == 1 # Callback was removed again assert not xknx.telegram_queue.telegram_received_cbs # Telegram was received assert value_reader.received_telegram == response_telegram # Successfull read() returns the telegram assert successfull_read == response_telegram
def test_position_without_position_address_uninitialized_down(self): """Test moving uninitialized cover to absolute position - with no absolute positioning supported.""" xknx = XKNX() cover = Cover( xknx, "TestCover", group_address_long="1/2/1", group_address_short="1/2/2", group_address_position_state="1/2/4", ) with patch("logging.Logger.warning") as mock_warn: self.loop.run_until_complete(cover.set_position(50)) self.assertEqual(xknx.telegrams.qsize(), 0) mock_warn.assert_called_with( "Current position unknown. Initialize cover by moving to end position." ) self.loop.run_until_complete(cover.set_position(100)) print(cover.travelcalculator.position_closed) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram, Telegram(GroupAddress("1/2/1"), payload=DPTBinary(1)))
def test_to_process_error(self): """Test process errornous telegram.""" xknx = XKNX() remote_value = RemoteValueColorRGBW( xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x66))), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite( DPTArray((0x00, 0x00, 0x0F, 0x64, 0x65, 0x66, 0x67))), ) self.loop.run_until_complete(remote_value.process(telegram))
def test_process_exception(self, process_tg_in_mock, logging_error_mock): """Test process_telegram exception handling.""" # pylint: disable=no-self-use xknx = XKNX(loop=self.loop) async def process_exception(): raise CouldNotParseTelegram( "Something went wrong when receiving the telegram." "") process_tg_in_mock.return_value = asyncio.ensure_future( process_exception()) telegram = Telegram(direction=TelegramDirection.INCOMING, payload=DPTBinary(1), group_address=GroupAddress("1/2/3")) self.loop.run_until_complete( asyncio.Task(xknx.telegram_queue.process_telegram(telegram))) logging_error_mock.assert_called_once_with( "Error while processing telegram %s", CouldNotParseTelegram( "Something went wrong when receiving the telegram." ""))
def test_no_filters(self): """Test telegram_received_callback after state of switch was changed.""" # pylint: disable=no-self-use xknx = XKNX() telegram_received_callback = Mock() async def async_telegram_received_cb(device): """Async callback.""" telegram_received_callback(device) xknx.telegram_queue.register_telegram_received_cb( async_telegram_received_cb) telegram = Telegram( direction=TelegramDirection.INCOMING, payload=DPTBinary(1), group_address=GroupAddress("1/2/3"), ) xknx.telegrams.put_nowait(telegram) self.loop.run_until_complete( xknx.telegram_queue._process_all_telegrams()) telegram_received_callback.assert_called_with(telegram)
def test_to_process_error_operation_mode(self): """Test process errornous telegram.""" xknx = XKNX() remote_value = RemoteValueOperationMode( xknx, group_address=GroupAddress("1/2/3"), climate_mode_type=RemoteValueOperationMode.ClimateModeType. HVAC_MODE, ) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray(( 0x64, 0x65, ))), ) self.loop.run_until_complete(remote_value.process(telegram))
def test_process_to_callback(self, devices_by_ga_mock): """Test process_telegram_incoming for returning after processing callback.""" # pylint: disable=no-self-use xknx = XKNX() telegram_received_callback = Mock() async def async_telegram_received_cb(device): """Async callback. Returning 'True'.""" telegram_received_callback(device) return True xknx.telegram_queue.register_telegram_received_cb( async_telegram_received_cb) telegram = Telegram( direction=TelegramDirection.INCOMING, payload=DPTBinary(1), group_address=GroupAddress("1/2/3"), ) self.loop.run_until_complete( xknx.telegram_queue.process_telegram_incoming(telegram)) telegram_received_callback.assert_called_once_with(telegram) devices_by_ga_mock.assert_not_called()
def test_dpt_binary(self): """Test string representation of DPTBinary.""" dpt_binary = DPTBinary(7) self.assertEqual(str(dpt_binary), '<DPTBinary value="7" />')
def calculate_payload(attr_payload): """Calculate payload depending on type of attribute.""" if isinstance(attr_payload, int): return DPTBinary(attr_payload) return DPTArray(attr_payload)
def test_compare_binary(self): """Test comparison of DPTBinary objects.""" self.assertEqual(DPTBinary(0), DPTBinary(0)) self.assertEqual(DPTBinary(0), DPTBinary(False)) self.assertEqual(DPTBinary(1), DPTBinary(True)) self.assertEqual(DPTBinary(2), DPTBinary(2)) self.assertNotEqual(DPTBinary(1), DPTBinary(4)) self.assertNotEqual(DPTBinary(2), DPTBinary(0)) self.assertNotEqual(DPTBinary(0), DPTBinary(2))
async def test_stop(self): """Test stopping cover.""" xknx = XKNX() cover_short_stop = Cover( xknx, "TestCover", group_address_long="1/2/1", group_address_short="1/2/2", group_address_position="1/2/3", group_address_position_state="1/2/4", ) # Attempt stopping while not actually moving await cover_short_stop.stop() assert xknx.telegrams.qsize() == 0 # Attempt stopping while moving down cover_short_stop.travelcalculator.set_position(0) await cover_short_stop.set_down() await cover_short_stop.stop() assert xknx.telegrams.qsize() == 2 telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("1/2/1"), payload=GroupValueWrite(DPTBinary(1)), ) telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("1/2/2"), payload=GroupValueWrite(DPTBinary(1)), ) # Attempt stopping while moving up cover_short_stop.travelcalculator.set_position(100) await cover_short_stop.set_up() await cover_short_stop.stop() assert xknx.telegrams.qsize() == 2 telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("1/2/1"), payload=GroupValueWrite(DPTBinary(0)), ) telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("1/2/2"), payload=GroupValueWrite(DPTBinary(0)), ) cover_manual_stop = Cover( xknx, "TestCover", group_address_long="1/2/1", group_address_short="1/2/2", group_address_stop="1/2/0", group_address_position="1/2/3", group_address_position_state="1/2/4", ) await cover_manual_stop.stop() assert xknx.telegrams.qsize() == 1 telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("1/2/0"), payload=GroupValueWrite(DPTBinary(1)), )
def _payload_value(payload: int | tuple[int, ...]) -> DPTArray | DPTBinary: """Prepare payload value for GroupValueWrite or GroupValueResponse.""" if isinstance(payload, int): return DPTBinary(payload) return DPTArray(payload)
def test_dpt_binary_assign_limit_exceeded(self): """Test initialization of DPTBinary objects with wrong value (value exceeded).""" with self.assertRaises(ConversionError): DPTBinary(DPTBinary.APCI_MAX_VALUE + 1)
def test_dpt_binary_assign(self): """Test initialization of DPTBinary objects.""" self.assertEqual(DPTBinary(8).value, 8)
def test_dpt_representation(self): """Test representation of DPTBinary and DPTArray.""" assert DPTBinary(True).__repr__() == "DPTBinary(0x1)" assert DPTArray((5, 15)).__repr__() == "DPTArray((0x5, 0xf))"
def test_dpt_init_with_string(self): """Teest initialization of DPTBinary object with wrong value (wrong type).""" with self.assertRaises(TypeError): DPTBinary("bla")
def test_compare_binary(self): """Test comparison of DPTBinary objects.""" assert DPTBinary(0) == DPTBinary(0) assert DPTBinary(0) == DPTBinary(False) assert DPTBinary(1) == DPTBinary(True) assert DPTBinary(2) == DPTBinary(2) assert DPTBinary(1) != DPTBinary(4) assert DPTBinary(2) != DPTBinary(0) assert DPTBinary(0) != DPTBinary(2)