def test_EndTOEnd_group_write_2bytes(self): """Test parsing and streaming CEMIFrame KNX/IP packet, setting value of thermostat.""" # Incoming Temperature from thermostat raw = ((0x06, 0x10, 0x05, 0x30, 0x00, 0x13, 0x29, 0x00, 0xbc, 0xd0, 0x14, 0x02, 0x08, 0x01, 0x03, 0x00, 0x80, 0x07, 0xc1)) xknx = XKNX(loop=self.loop) knxipframe = KNXIPFrame(xknx) knxipframe.from_knx(raw) telegram = knxipframe.body.telegram self.assertEqual( telegram, Telegram(GroupAddress("2049"), payload=DPTArray(DPTTemperature().to_knx(19.85)))) knxipframe2 = KNXIPFrame(xknx) knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION) knxipframe2.body.src_addr = PhysicalAddress("1.4.2") knxipframe2.body.telegram = telegram knxipframe2.body.set_hops(5) knxipframe2.normalize() self.assertEqual(knxipframe2.header.to_knx(), list(raw[0:6])) self.assertEqual(knxipframe2.body.to_knx(), list(raw[6:])) self.assertEqual(knxipframe2.to_knx(), list(raw))
async def test_process_callback(self): """Test process / reading telegrams from telegram queue. Test if callback is called.""" xknx = XKNX() sensor = RawValue( xknx, "TestSensor", 2, group_address="1/2/3", ) after_update_callback = AsyncMock() sensor.register_device_updated_cb(after_update_callback) telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray((0x01, 0x02))), ) await sensor.process(telegram) after_update_callback.assert_called_with(sensor) assert sensor.last_telegram == telegram # consecutive telegrams with same payload shall only trigger one callback after_update_callback.reset_mock() await sensor.process(telegram) after_update_callback.assert_not_called()
async def service_send_to_knx_bus(self, call: ServiceCall) -> None: """Service for sending an arbitrary KNX message to the KNX bus.""" attr_address = call.data[KNX_ADDRESS] attr_payload = call.data[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=parse_device_group_address(address), payload=GroupValueWrite(payload), ) await self.xknx.telegrams.put(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_process(self): """Test process / reading telegrams from telegram queue.""" xknx = XKNX(loop=self.loop) binaryinput = BinarySensor(xknx, 'TestInput', '1/2/3') self.assertEqual(binaryinput.state, BinarySensorState.OFF) telegram_on = Telegram() telegram_on.payload = DPTBinary(1) self.loop.run_until_complete( asyncio.Task(binaryinput.process(telegram_on))) self.assertEqual(binaryinput.state, BinarySensorState.ON) telegram_off = Telegram() telegram_off.payload = DPTBinary(0) self.loop.run_until_complete( asyncio.Task(binaryinput.process(telegram_off))) self.assertEqual(binaryinput.state, BinarySensorState.OFF)
def test_telegram_not_equal(self): """Test not equals operator.""" self.assertNotEqual( Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), Telegram(GroupAddress("1/2/4"), payload=GroupValueRead()), ) self.assertNotEqual( Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), Telegram(GroupAddress("1/2/3"), payload=GroupValueWrite(DPTBinary(1))), ) self.assertNotEqual( Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), Telegram( GroupAddress("1/2/3"), TelegramDirection.INCOMING, payload=GroupValueRead(), ), )
def test_process_operation_mode(self): """Test process / reading telegrams from telegram queue. Test if setpoint is processed correctly.""" xknx = XKNX(loop=self.loop) climate_mode = ClimateMode(xknx, 'TestClimate', group_address_operation_mode='1/2/5', group_address_controller_status='1/2/3') for operation_mode in DPT_20102_MODES: telegram = Telegram(GroupAddress('1/2/5')) telegram.payload = DPTArray(DPTHVACMode.to_knx(operation_mode)) self.loop.run_until_complete( asyncio.Task(climate_mode.process(telegram))) self.assertEqual(climate_mode.operation_mode, operation_mode) for operation_mode in DPT_20102_MODES: if operation_mode == HVACOperationMode.AUTO: continue telegram = Telegram(GroupAddress('1/2/3')) telegram.payload = DPTArray( DPTControllerStatus.to_knx(operation_mode)) self.loop.run_until_complete( asyncio.Task(climate_mode.process(telegram))) self.assertEqual(climate_mode.operation_mode, operation_mode)
def test_sync_state_address(self): """Test sync function / sending group reads to KNX bus. Testing with a Light with dimm functionality.""" xknx = XKNX() light = Light( xknx, name="TestLight", group_address_switch="1/2/3", group_address_switch_state="1/2/4", group_address_brightness="1/2/5", group_address_brightness_state="1/2/6", group_address_color="1/2/7", group_address_color_state="1/2/8", group_address_tunable_white="1/2/9", group_address_tunable_white_state="1/2/10", group_address_color_temperature="1/2/11", group_address_color_temperature_state="1/2/12", group_address_rgbw="1/2/13", group_address_rgbw_state="1/2/14", ) self.loop.run_until_complete(light.sync()) self.assertEqual(xknx.telegrams.qsize(), 6) telegrams = [] for _ in range(6): telegrams.append(xknx.telegrams.get_nowait()) test_telegrams = [ Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ), Telegram(GroupAddress("1/2/6"), TelegramType.GROUP_READ), Telegram(GroupAddress("1/2/8"), TelegramType.GROUP_READ), Telegram(GroupAddress("1/2/10"), TelegramType.GROUP_READ), Telegram(GroupAddress("1/2/12"), TelegramType.GROUP_READ), Telegram(GroupAddress("1/2/14"), TelegramType.GROUP_READ), ] self.assertEqual(len(set(telegrams)), 6) self.assertEqual(set(telegrams), set(test_telegrams))
def test_process_action(self): """Test process / reading telegrams from telegram queue. Test if action is executed.""" xknx = XKNX(loop=self.loop) switch = Switch(xknx, 'TestOutlet', group_address='1/2/3') xknx.devices.add(switch) binary_sensor = BinarySensor(xknx, 'TestInput', group_address_state='1/2/3') action_on = Action(xknx, hook='on', target='TestOutlet', method='on') binary_sensor.actions.append(action_on) action_off = Action(xknx, hook='off', target='TestOutlet', method='off') binary_sensor.actions.append(action_off) xknx.devices.add(binary_sensor) self.assertEqual(xknx.devices['TestInput'].state, BinarySensorState.OFF) self.assertEqual(xknx.devices['TestOutlet'].state, False) telegram_on = Telegram() telegram_on.payload = DPTBinary(1) self.loop.run_until_complete( asyncio.Task(binary_sensor.process(telegram_on))) self.assertEqual(xknx.devices['TestInput'].state, BinarySensorState.ON) self.assertEqual(xknx.devices['TestOutlet'].state, True) telegram_off = Telegram() telegram_off.payload = DPTBinary(0) self.loop.run_until_complete( asyncio.Task(binary_sensor.process(telegram_off))) self.assertEqual(xknx.devices['TestInput'].state, BinarySensorState.OFF) self.assertEqual(xknx.devices['TestOutlet'].state, False)
def test_sync_state_address(self): """Test sync function / sending group reads to KNX bus. Testing with a Light with dimm functionality.""" xknx = XKNX(loop=self.loop) light = Light(xknx, name="TestLight", group_address_switch='1/2/3', group_address_switch_state='1/2/4', group_address_brightness='1/2/5', group_address_brightness_state='1/2/6', group_address_color='1/2/7', group_address_color_state='1/2/8', group_address_tunable_white='1/2/9', group_address_tunable_white_state='1/2/10', group_address_color_temperature='1/2/11', group_address_color_temperature_state='1/2/12', group_address_rgbw='1/2/13', group_address_rgbw_state='1/2/14') self.loop.run_until_complete(asyncio.Task(light.sync())) self.assertEqual(xknx.telegrams.qsize(), 6) telegrams = [] for _ in range(6): telegrams.append(xknx.telegrams.get_nowait()) test_telegrams = [ Telegram(GroupAddress('1/2/4'), TelegramType.GROUP_READ), Telegram(GroupAddress('1/2/6'), TelegramType.GROUP_READ), Telegram(GroupAddress('1/2/8'), TelegramType.GROUP_READ), Telegram(GroupAddress('1/2/10'), TelegramType.GROUP_READ), Telegram(GroupAddress('1/2/12'), TelegramType.GROUP_READ), Telegram(GroupAddress('1/2/14'), TelegramType.GROUP_READ) ] self.assertEqual(len(set(telegrams)), 6) self.assertEqual(set(telegrams), set(test_telegrams))
async def test_tunnelling(self): """Test tunnelling from KNX bus.""" communication_channel_id = 23 data_endpoint = ("192.168.1.2", 4567) udp_transport = UDPTransport(("192.168.1.1", 0), ("192.168.1.2", 1234)) cemi = CEMIFrame.init_from_telegram( Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray((0x1, 0x2, 0x3))), ) ) sequence_counter = 42 tunnelling = Tunnelling( udp_transport, data_endpoint, cemi, sequence_counter, communication_channel_id, ) tunnelling.timeout_in_seconds = 0 assert tunnelling.awaited_response_class == TunnellingAck assert tunnelling.communication_channel_id == communication_channel_id # Expected KNX/IP-Frame: tunnelling_request = TunnellingRequest( communication_channel_id=communication_channel_id, sequence_counter=sequence_counter, ) tunnelling_request.cemi = cemi exp_knxipframe = KNXIPFrame.init_from_body(tunnelling_request) with patch("xknx.io.transport.UDPTransport.send") as mock_udp_send, patch( "xknx.io.transport.UDPTransport.getsockname" ) as mock_udp_getsockname: mock_udp_getsockname.return_value = ("192.168.1.3", 4321) await tunnelling.start() mock_udp_send.assert_called_with(exp_knxipframe, addr=data_endpoint) # Response KNX/IP-Frame with wrong type wrong_knxipframe = KNXIPFrame() wrong_knxipframe.init(KNXIPServiceType.CONNECTIONSTATE_REQUEST) with patch("logging.Logger.warning") as mock_warning: tunnelling.response_rec_callback(wrong_knxipframe, HPAI(), None) mock_warning.assert_called_with("Could not understand knxipframe") # Response KNX/IP-Frame with error: err_knxipframe = KNXIPFrame() err_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK) err_knxipframe.body.status_code = ErrorCode.E_CONNECTION_ID with patch("logging.Logger.debug") as mock_warning: tunnelling.response_rec_callback(err_knxipframe, HPAI(), None) mock_warning.assert_called_with( "Error: KNX bus responded to request of type '%s' with error in '%s': %s", type(tunnelling).__name__, type(err_knxipframe.body).__name__, ErrorCode.E_CONNECTION_ID, ) # Correct Response KNX/IP-Frame: res_knxipframe = KNXIPFrame() res_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK) tunnelling.response_rec_callback(res_knxipframe, HPAI(), None) assert tunnelling.success
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 test_array_sensor_loop(self): """Test sensor and expose_sensor with different values.""" test_cases = [ ('angle', DPTArray((0x0B)), 16), ('brightness', DPTArray((0x27, 0x10)), 10000), ('color_temperature', DPTArray((0x0D, 0x48)), 3400), ('counter_pulses', DPTArray((0x9F)), -97), ('current', DPTArray((0x00, 0x03)), 3), ('delta_time_hrs', DPTArray((0x04, 0xD2)), 1234), ('delta_time_min', DPTArray((0xFB, 0x2E)), -1234), ('delta_time_ms', DPTArray((0x7D, 0x00)), 32000), ('delta_time_sec', DPTArray((0x83, 0x00)), -32000), ('electric_current', DPTArray((0x3D, 0xCC, 0xCC, 0xCD)), 0.1), ('electric_potential', DPTArray((0x43, 0xF0, 0xDE, 0xB8)), 481.74), ('energy', DPTArray((0x43, 0xE4, 0x00, 0x00)), 456), ('enthalpy', DPTArray((0xC1, 0x4E)), -4387.84), ('frequency', DPTArray((0x42, 0x46, 0xCC, 0xCD)), 49.7), ('heatflowrate', DPTArray((0x42, 0xAE, 0x00, 0x00)), 87), ('humidity', DPTArray((0x6C, 0xB6)), 98795.52), ('illuminance', DPTArray((0x2F, 0xE9)), 648), ('luminous_flux', DPTArray((0x43, 0x87, 0x40, 0x00)), 270.5), ('percent', DPTArray((0x26)), 15), ('percentU8', DPTArray((0xCD)), 205), ('percentV8', DPTArray((0x9A)), -102), ('percentV16', DPTArray((0xFF, 0xFF)), -1), ('phaseanglerad', DPTArray((0xC1, 0x10, 0x00, 0x00)), -9), ('phaseangledeg', DPTArray((0x43, 0x87, 0x40, 0x00)), 270.5), ('power', DPTArray((0x42, 0xA9, 0xBD, 0x71)), 84.87), ('powerfactor', DPTArray((0x42, 0xA9, 0x6B, 0x85)), 84.71), ('ppm', DPTArray((0x00, 0x03)), 0.03), ('pressure', DPTArray((0x42, 0xA9, 0x6B, 0x85)), 84.71), ('pressure_2byte', DPTArray((0x2E, 0xA9)), 545.6), ('pulse', DPTArray((0x11)), 17), ('rotation_angle', DPTArray((0xAE, 0xC0)), -20800), ('scene_number', DPTArray((0x00)), 1), ('speed', DPTArray((0x00, 0x00, 0x00, 0x00)), 0), ('speed_ms', DPTArray((0x0E, 0xA4)), 34), ('string', DPTArray((0x4B, 0x4E, 0x58, 0x20, 0x69, 0x73, 0x20, 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00)), "KNX is OK"), ('temperature', DPTArray((0x03, 0x12)), 7.86), ('voltage', DPTArray((0x07, 0x9A)), 19.46), # Generic DPT Without Min/Max and Unit. ('DPT-5', DPTArray((0x1F)), 31), ('1byte_unsigned', DPTArray((0x08)), 8), ('DPT-7', DPTArray((0xD4, 0x31)), 54321), ('2byte_unsigned', DPTArray((0x30, 0x39)), 12345), ('DPT-8', DPTArray((0x80, 0x44)), -32700), ('2byte_signed', DPTArray((0x00, 0x01)), 1), ('DPT-9', DPTArray((0x2E, 0xA9)), 545.6), ('DPT-12', DPTArray((0x07, 0x5B, 0xCD, 0x15)), 123456789), ('4byte_unsigned', DPTArray((0x00, 0x00, 0x00, 0x00)), 0), ('DPT-13', DPTArray((0x02, 0xE5, 0x5E, 0xF7)), 48586487), ('4byte_signed', DPTArray((0xFD, 0x1A, 0xA1, 0x09)), -48586487), ('DPT-14', DPTArray((0x47, 0xC0, 0xF7, 0x20)), 98798.25), ('4byte_float', DPTArray((0xC2, 0x09, 0xEE, 0xCC)), -34.4832), ] for value_type, test_payload, test_value in test_cases: with self.subTest(value_type=value_type): xknx = XKNX(loop=self.loop) sensor = Sensor(xknx, 'TestSensor_%s' % value_type, group_address_state='1/1/1', value_type=value_type) expose = ExposeSensor(xknx, 'TestExpose_%s' % value_type, group_address='2/2/2', value_type=value_type) incoming_telegram = Telegram( GroupAddress('1/1/1'), TelegramType.GROUP_WRITE, direction=TelegramDirection.INCOMING, payload=test_payload) self.loop.run_until_complete( asyncio.Task(sensor.process(incoming_telegram))) incoming_value = sensor.resolve_state() if isinstance(test_value, float): self.assertEqual(round(incoming_value, 4), test_value) else: self.assertEqual(incoming_value, test_value) # HA sends strings for new values stringified_value = str(test_value) self.loop.run_until_complete( asyncio.Task(expose.set(stringified_value))) self.assertEqual(xknx.telegrams.qsize(), 1) outgoing_telegram = xknx.telegrams.get_nowait() self.assertEqual( outgoing_telegram, Telegram(GroupAddress('2/2/2'), TelegramType.GROUP_WRITE, direction=TelegramDirection.OUTGOING, payload=test_payload))
def test_process_group_read(self): """Test if process_group_read. Nothing really to test here.""" xknx = XKNX() device = Device(xknx, "TestDevice") self.loop.run_until_complete(device.process_group_read(Telegram()))
async def send_group_read(self): """Send group read.""" telegram = Telegram(self.group_address, TelegramType.GROUP_READ) await self.xknx.telegrams.put(telegram)
def test_telegram_equal(self): """Test equals operator.""" self.assertEqual( Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), )
async def send_telegram(self, payload: APCI) -> None: """Send the telegram.""" await self.xknx.telegrams.put( Telegram(destination_address=self.address, payload=payload))
def test_set_invalid_telegram(frame): """Test for setting invalid telegram type""" tg = Telegram(telegramtype=None) with raises(TypeError): frame.telegram = tg
async def test_tunnel_connect_send_disconnect( self, time_travel, route_back, data_endpoint_addr, local_endpoint ): """Test initiating a tunnelling connection.""" local_addr = ("192.168.1.1", 12345) remote_addr = ("192.168.1.2", 3671) self.tunnel.route_back = route_back gateway_data_endpoint = ( HPAI(*data_endpoint_addr) if data_endpoint_addr else HPAI() ) self.tunnel.transport.connect = AsyncMock() self.tunnel.transport.getsockname = Mock(return_value=local_addr) self.tunnel.transport.send = Mock() self.tunnel.transport.stop = Mock() # Connect connect_request = ConnectRequest( control_endpoint=local_endpoint, data_endpoint=local_endpoint, ) connect_frame = KNXIPFrame.init_from_body(connect_request) connection_task = asyncio.create_task(self.tunnel.connect()) await time_travel(0) self.tunnel.transport.connect.assert_called_once() self.tunnel.transport.send.assert_called_once_with(connect_frame) connect_response_frame = KNXIPFrame.init_from_body( ConnectResponse( communication_channel=23, data_endpoint=gateway_data_endpoint, identifier=7, ) ) self.tunnel.transport.handle_knxipframe(connect_response_frame, remote_addr) await connection_task assert self.tunnel._data_endpoint_addr == data_endpoint_addr assert self.tunnel._src_address == IndividualAddress(7) # Send - use data endpoint self.tunnel.transport.send.reset_mock() test_telegram = Telegram(payload=GroupValueWrite(DPTArray((1,)))) test_telegram_frame = KNXIPFrame.init_from_body( TunnellingRequest( communication_channel_id=23, sequence_counter=0, cemi=CEMIFrame.init_from_telegram( test_telegram, code=CEMIMessageCode.L_DATA_REQ, src_addr=IndividualAddress(7), ), ) ) asyncio.create_task(self.tunnel.send_telegram(test_telegram)) await time_travel(0) self.tunnel.transport.send.assert_called_once_with( test_telegram_frame, addr=data_endpoint_addr ) # skip ack and confirmation # Disconnect self.tunnel.transport.send.reset_mock() disconnect_request = DisconnectRequest( communication_channel_id=23, control_endpoint=local_endpoint, ) disconnect_frame = KNXIPFrame.init_from_body(disconnect_request) disconnection_task = asyncio.create_task(self.tunnel.disconnect()) await time_travel(0) self.tunnel.transport.send.assert_called_once_with(disconnect_frame) disconnect_response_frame = KNXIPFrame.init_from_body( DisconnectResponse(communication_channel_id=23) ) self.tunnel.transport.handle_knxipframe(disconnect_response_frame, remote_addr) await disconnection_task assert self.tunnel._data_endpoint_addr is None self.tunnel.transport.stop.assert_called_once()
async def test_switch_on_off(self): """Test switching on/off of a Fan.""" xknx = XKNX() fan = Fan(xknx, name="TestFan", group_address_speed="1/2/3") # Turn the fan on via speed GA. First try without providing a speed, # which will set it to the default 50% percentage. await fan.turn_on() assert xknx.telegrams.qsize() == 1 telegram = xknx.telegrams.get_nowait() # 128 is 50% as byte (0...255) assert telegram == Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray(128)), ) # Try again, but this time with a speed provided await fan.turn_on(55) assert xknx.telegrams.qsize() == 1 telegram = xknx.telegrams.get_nowait() # 140 is 55% as byte (0...255) assert telegram == Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray(140)), ) # Turn the fan off via the speed GA await fan.turn_off() assert xknx.telegrams.qsize() == 1 telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray(0)), ) fan_with_switch = Fan( xknx, name="TestFanSwitch", group_address_speed="1/2/3", group_address_switch="4/5/6", ) # Turn the fan on via the switch GA, which should not adjust the speed await fan_with_switch.turn_on() assert xknx.telegrams.qsize() == 1 telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("4/5/6"), payload=GroupValueWrite(DPTBinary(1)), ) # Turn the fan off via the switch GA await fan_with_switch.turn_off() assert xknx.telegrams.qsize() == 1 telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("4/5/6"), payload=GroupValueWrite(DPTBinary(0)), ) # Turn the fan on again this time with a provided speed, which for a switch GA fan # should result in separate telegrams to switch on the fan and then set the speed. await fan_with_switch.turn_on(55) assert xknx.telegrams.qsize() == 2 telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("4/5/6"), payload=GroupValueWrite(DPTBinary(1)), ) telegram = xknx.telegrams.get_nowait() assert telegram == Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray(140)), )
def test_process_group_read(self): """Test if process_group_read. Nothing really to test here.""" xknx = XKNX(loop=self.loop) device = Device(xknx, 'TestDevice') self.loop.run_until_complete( asyncio.Task(device.process_group_read(Telegram())))
def test_telegram_unsupported_address(frame): """Test telegram conversion flags with an unsupported address""" with raises(TypeError): frame.telegram = Telegram(destination_address=object())
def test_telegram_individual_address(frame): """Test telegram conversion flags with a individual address""" frame.telegram = Telegram(destination_address=IndividualAddress(0)) assert (frame.flags & CEMIFlags.DESTINATION_INDIVIDUAL_ADDRESS ) == CEMIFlags.DESTINATION_INDIVIDUAL_ADDRESS
def test_telegram_group_address(frame): """Test telegram conversion flags with a group address""" frame.telegram = Telegram(destination_address=GroupAddress(0)) assert (frame.flags & CEMIFlags.DESTINATION_GROUP_ADDRESS ) == CEMIFlags.DESTINATION_GROUP_ADDRESS
def test_target_temperature_down(self): """Test decrease target temperature.""" # pylint: disable=no-self-use xknx = XKNX(loop=self.loop) climate = Climate(xknx, 'TestClimate', group_address_target_temperature='1/2/2', group_address_setpoint_shift='1/2/3') self.loop.run_until_complete( asyncio.Task(climate.set_setpoint_shift(1))) self.assertEqual(xknx.telegrams.qsize(), 1) self.assertEqual( xknx.telegrams.get_nowait(), # DEFAULT_SETPOINT_SHIFT_STEP is 0.5 -> payload = setpoint_shift * 2 Telegram(GroupAddress('1/2/3'), payload=DPTArray(2))) self.loop.run_until_complete( asyncio.Task(climate.target_temperature.set(23.00))) self.assertEqual(xknx.telegrams.qsize(), 1) self.assertEqual( xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/2'), payload=DPTArray(DPT2ByteFloat().to_knx(23.00)))) self.assertEqual(climate.base_temperature, 22.0) # First change self.loop.run_until_complete( asyncio.Task(climate.set_target_temperature(20.50))) self.assertEqual(xknx.telegrams.qsize(), 2) self.assertEqual(xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/3'), payload=DPTArray(0xFD))) # -3 self.assertEqual( xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/2'), payload=DPTArray(DPT2ByteFloat().to_knx(20.50)))) self.assertEqual(climate.target_temperature.value, 20.50) # Second change self.loop.run_until_complete( asyncio.Task(climate.set_target_temperature(19.00))) self.assertEqual(xknx.telegrams.qsize(), 2) self.assertEqual(xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/3'), payload=DPTArray(0xFA))) # -6 self.assertEqual( xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/2'), payload=DPTArray(DPT2ByteFloat().to_knx(19.00)))) self.assertEqual(climate.target_temperature.value, 19.00) # Test min target temperature # Base (22) - setpoint_shift_min (6) self.assertEqual(climate.target_temperature_min, 16.00) # third change - limit exceeded, setting to min self.loop.run_until_complete( asyncio.Task(climate.set_target_temperature(15.50))) self.assertEqual(climate.target_temperature_min, 16.00) self.assertEqual(climate.setpoint_shift, -6)
def test_telegram_unsupported_address(): """Test telegram conversion flags with an unsupported address.""" frame = CEMIFrame() with pytest.raises(TypeError): frame.telegram = Telegram(destination_address=object())
async def test_process_group_read(self): """Test if process_group_read. Nothing really to test here.""" xknx = XKNX() device = Device(xknx, "TestDevice") await device.process_group_read(Telegram())
async def test_binary_sensor_loop(self, value_type, test_payload, test_value): """Test binary_sensor and expose_sensor with binary values.""" xknx = XKNX() xknx.knxip_interface = AsyncMock() xknx.rate_limit = False telegram_callback = AsyncMock() xknx.telegram_queue.register_telegram_received_cb( telegram_callback, address_filters=[AddressFilter("i-test")], match_for_outgoing=True, ) await xknx.telegram_queue.start() expose = ExposeSensor( xknx, "TestExpose", group_address="i-test", value_type=value_type, ) assert expose.resolve_state() is None await expose.set(test_value) await xknx.telegrams.join() outgoing_telegram = Telegram( destination_address=InternalGroupAddress("i-test"), direction=TelegramDirection.OUTGOING, payload=GroupValueWrite(test_payload), ) # InternalGroupAddress isn't passed to knxip_interface xknx.knxip_interface.send_telegram.assert_not_called() telegram_callback.assert_called_with(outgoing_telegram) assert expose.resolve_state() == test_value bin_sensor = BinarySensor(xknx, "TestSensor", group_address_state="i-test") assert bin_sensor.state is None # read sensor state (from expose as it has the same GA) # wait_for_result so we don't have to await self.xknx.telegrams.join() await bin_sensor.sync(wait_for_result=True) read_telegram = Telegram( destination_address=InternalGroupAddress("i-test"), direction=TelegramDirection.OUTGOING, payload=GroupValueRead(), ) response_telegram = Telegram( destination_address=InternalGroupAddress("i-test"), direction=TelegramDirection.OUTGOING, payload=GroupValueResponse(test_payload), ) xknx.knxip_interface.send_telegram.assert_not_called() telegram_callback.assert_has_calls( [ call(read_telegram), call(response_telegram), ] ) # test if Sensor has successfully read from ExposeSensor assert bin_sensor.state == test_value assert expose.resolve_state() == bin_sensor.state await xknx.telegram_queue.stop()
def test_telegram_equal(self): """Test equals operator.""" self.assertEqual( Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ), Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ), )
def test_tunnelling(self): """Test tunnelling from KNX bus.""" # pylint: disable=too-many-locals xknx = XKNX() communication_channel_id = 23 udp_client = UDPClient(xknx, ("192.168.1.1", 0), ("192.168.1.2", 1234)) telegram = Telegram( destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray((0x1, 0x2, 0x3))), ) sequence_counter = 42 src_address = IndividualAddress("2.2.2") tunnelling = Tunnelling( xknx, udp_client, telegram, src_address, sequence_counter, communication_channel_id, ) tunnelling.timeout_in_seconds = 0 self.assertEqual(tunnelling.awaited_response_class, TunnellingAck) self.assertEqual(tunnelling.communication_channel_id, communication_channel_id) # Expected KNX/IP-Frame: tunnelling_request = TunnellingRequest( xknx, communication_channel_id=communication_channel_id, sequence_counter=sequence_counter, ) tunnelling_request.cemi.telegram = telegram tunnelling_request.cemi.src_addr = src_address exp_knxipframe = KNXIPFrame.init_from_body(tunnelling_request) with patch("xknx.io.UDPClient.send") as mock_udp_send, patch( "xknx.io.UDPClient.getsockname") as mock_udp_getsockname: mock_udp_getsockname.return_value = ("192.168.1.3", 4321) self.loop.run_until_complete(tunnelling.start()) mock_udp_send.assert_called_with(exp_knxipframe) # Response KNX/IP-Frame with wrong type wrong_knxipframe = KNXIPFrame(xknx) wrong_knxipframe.init(KNXIPServiceType.CONNECTIONSTATE_REQUEST) with patch("logging.Logger.warning") as mock_warning: tunnelling.response_rec_callback(wrong_knxipframe, None) mock_warning.assert_called_with("Could not understand knxipframe") # Response KNX/IP-Frame with error: err_knxipframe = KNXIPFrame(xknx) err_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK) err_knxipframe.body.status_code = ErrorCode.E_CONNECTION_ID with patch("logging.Logger.debug") as mock_warning: tunnelling.response_rec_callback(err_knxipframe, None) mock_warning.assert_called_with( "Error: KNX bus responded to request of type '%s' with error in '%s': %s", type(tunnelling).__name__, type(err_knxipframe.body).__name__, ErrorCode.E_CONNECTION_ID, ) # Correct Response KNX/IP-Frame: res_knxipframe = KNXIPFrame(xknx) res_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK) tunnelling.response_rec_callback(res_knxipframe, None) self.assertTrue(tunnelling.success)