def test_expose_sensor(self): """Test default state mapping.""" xknx = XKNX() Weather( name="weather", xknx=xknx, group_address_brightness_east="1/3/5", group_address_brightness_south="1/3/6", group_address_brightness_west="1/3/7", ) self.assertEqual(len(xknx.devices), 1) Weather( name="weather", xknx=xknx, group_address_brightness_east="1/3/5", group_address_brightness_south="1/3/6", group_address_brightness_west="1/3/7", group_address_wind_alarm="1/5/4", expose_sensors=True, ) self.assertEqual(len(xknx.devices), 6)
async def main(): """Connect to KNX/IP device and read the value of a temperature and a motion sensor.""" xknx = XKNX() await xknx.start() sensor1 = BinarySensor( xknx, "DiningRoom.Motion.Sensor", group_address_state="6/0/2", ) await sensor1.sync(wait_for_result=True) print(sensor1) sensor2 = Sensor( xknx, "DiningRoom.Temperature.Sensor", group_address_state="6/2/1", value_type="temperature", ) await sensor2.sync(wait_for_result=True) print(sensor2) await xknx.stop()
def test_process_callback(self): """Test process / reading telegrams from telegram queue. Test if callback is executed.""" # pylint: disable=no-self-use xknx = XKNX(loop=self.loop) cover = 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') after_update_callback = Mock() @asyncio.coroutine def async_after_update_callback(device): """Async callback.""" after_update_callback(device) cover.register_device_updated_cb(async_after_update_callback) telegram = Telegram(Address('1/2/4'), payload=DPTArray(42)) self.loop.run_until_complete(asyncio.Task(cover.process(telegram))) after_update_callback.assert_called_with(cover)
def test_tracker_parser_invalid_options(self, logging_warning_mock): """Test parsing invalid tracker options.""" xknx = XKNX() def _get_only_tracker() -> _StateTracker: # _workers is unordered so it just works with 1 item assert len(xknx.state_updater._workers) == 1 _tracker = next(iter(xknx.state_updater._workers.values())) return _tracker # INVALID string remote_value_invalid = RemoteValue( xknx, sync_state="invalid", group_address_state=GroupAddress("1/1/1") ) logging_warning_mock.assert_called_once_with( 'Could not parse StateUpdater tracker_options "%s" for %s. Using default %s %s minutes.', "invalid", remote_value_invalid, StateTrackerType.EXPIRE, 60, ) assert _get_only_tracker().tracker_type == StateTrackerType.EXPIRE assert _get_only_tracker().update_interval == 60 * 60 remote_value_invalid.__del__() logging_warning_mock.reset_mock() # intervall too long remote_value_long = RemoteValue( xknx, sync_state=1441, group_address_state=GroupAddress("1/1/1") ) logging_warning_mock.assert_called_once_with( "StateUpdater interval of %s to long for %s. Using maximum of %s minutes (1 day)", 1441, remote_value_long, 1440, ) remote_value_long.__del__()
def test_config_datetime(self): """Test reading DateTime objects from config file.""" xknx = XKNX(config='xknx.yaml', loop=self.loop) self.assertEqual( xknx.devices['General.Time'], DateTime(xknx, 'General.Time', group_address='2/1/1', broadcast_type=DateTimeBroadcastType.TIME, device_updated_cb=xknx.devices.device_updated)) self.assertEqual( xknx.devices['General.DateTime'], DateTime(xknx, 'General.DateTime', group_address='2/1/2', broadcast_type=DateTimeBroadcastType.DATETIME, device_updated_cb=xknx.devices.device_updated)) self.assertEqual( xknx.devices['General.Date'], DateTime(xknx, 'General.Date', group_address='2/1/3', broadcast_type=DateTimeBroadcastType.DATE, device_updated_cb=xknx.devices.device_updated))
def test_set(self): """Test setting value.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueColorRGBW(xknx, group_address=GroupAddress("1/2/3")) self.loop.run_until_complete(remote_value.set((100, 101, 102, 103))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram( GroupAddress("1/2/3"), payload=DPTArray((0x64, 0x65, 0x66, 0x67, 0x00, 0x0F)), ), ) self.loop.run_until_complete(remote_value.set((100, 101, 104, 105))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram( GroupAddress("1/2/3"), payload=DPTArray((0x64, 0x65, 0x68, 0x69, 0x00, 0x0F)), ), )
def test_state_updater_start_update_stop(self): """Test start, update_received and stop of StateUpdater.""" xknx = XKNX() remote_value_1 = RemoteValue( xknx, sync_state=True, group_address_state=GroupAddress("1/1/1") ) remote_value_2 = RemoteValue( xknx, sync_state=True, group_address_state=GroupAddress("1/1/2") ) xknx.state_updater._workers[id(remote_value_1)] = Mock() xknx.state_updater._workers[id(remote_value_2)] = Mock() assert not xknx.state_updater.started xknx.state_updater.start() assert xknx.state_updater.started # start xknx.state_updater._workers[id(remote_value_1)].start.assert_called_once_with() xknx.state_updater._workers[id(remote_value_2)].start.assert_called_once_with() # update xknx.state_updater.update_received(remote_value_2) xknx.state_updater._workers[ id(remote_value_1) ].update_received.assert_not_called() xknx.state_updater._workers[ id(remote_value_2) ].update_received.assert_called_once_with() # stop xknx.state_updater.stop() assert not xknx.state_updater.started xknx.state_updater._workers[id(remote_value_1)].stop.assert_called_once_with() xknx.state_updater._workers[id(remote_value_2)].stop.assert_called_once_with() # don't update when not started xknx.state_updater.update_received(remote_value_1) xknx.state_updater._workers[ id(remote_value_1) ].update_received.assert_not_called()
def test_from_knx(self): """Test from_knx function with normal operation.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueColorRGBW(xknx) self.assertEqual( remote_value.from_knx(DPTArray((0x64, 0x65, 0x66, 0x7F, 0x00, 0x00))), [0, 0, 0, 0], ) self.assertEqual( remote_value.from_knx(DPTArray((0x64, 0x65, 0x66, 0x7F, 0x00, 0x0F))), [100, 101, 102, 127], ) self.assertEqual( remote_value.from_knx(DPTArray((0x64, 0x65, 0x66, 0x7F, 0x00, 0x00))), [100, 101, 102, 127], ) self.assertEqual( remote_value.from_knx(DPTArray((0xFF, 0x65, 0x66, 0xFF, 0x00, 0x09))), [255, 101, 102, 255], ) self.assertEqual( remote_value.from_knx(DPTArray((0x64, 0x65, 0x66, 0x7F, 0x00, 0x01))), [255, 101, 102, 127], )
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_device_by_group_address(self): """Test get devices by group address.""" xknx = XKNX(loop=self.loop) devices = Devices() light1 = Light(xknx, 'Living-Room.Light_1', group_address_switch='1/6/7') devices.add(light1) sensor1 = BinarySensor(xknx, 'DiningRoom.Motion.Sensor', group_address='3/0/1', significant_bit=2) devices.add(sensor1) sensor2 = BinarySensor(xknx, 'DiningRoom.Motion.Sensor', group_address='3/0/1', significant_bit=3) devices.add(sensor2) light2 = Light(xknx, 'Living-Room.Light_2', group_address_switch='1/6/8') devices.add(light2) self.assertEqual( tuple(devices.devices_by_group_address(GroupAddress('1/6/7'))), (light1, )) self.assertEqual( tuple(devices.devices_by_group_address(GroupAddress('1/6/8'))), (light2, )) self.assertEqual( tuple(devices.devices_by_group_address(GroupAddress('3/0/1'))), (sensor1, sensor2))
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))
def test_set(self): """Test setting value.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueDpt2ByteUnsigned( xknx, group_address=GroupAddress("1/2/3")) self.loop.run_until_complete(remote_value.set(2571)) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x0A, 0x0B)))) self.loop.run_until_complete(remote_value.set(5500)) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram( GroupAddress("1/2/3"), payload=DPTArray(( 0x15, 0x7C, )), ), )
def test_modification_of_device(self): """Test if devices object does store references and not copies of objects.""" xknx = XKNX(loop=self.loop) devices = Devices() light1 = Light(xknx, 'Living-Room.Light_1', group_address_switch='1/6/7') devices.add(light1) for device in devices: self.loop.run_until_complete(asyncio.Task(device.set_on())) self.assertTrue(light1.state) device2 = devices["Living-Room.Light_1"] self.loop.run_until_complete(asyncio.Task(device2.set_off())) self.assertFalse(light1.state) for device in devices.devices_by_group_address(GroupAddress('1/6/7')): self.loop.run_until_complete(asyncio.Task(device.set_on())) self.assertTrue(light1.state)
def test_get_item(self): """Test get item by name or by index.""" xknx = XKNX(loop=self.loop) devices = Devices() light1 = Light(xknx, 'Living-Room.Light_1', group_address_switch='1/6/7') devices.add(light1) switch1 = Switch(xknx, "TestOutlet_1", group_address='1/2/3') devices.add(switch1) light2 = Light(xknx, 'Living-Room.Light_2', group_address_switch='1/6/8') devices.add(light2) switch2 = Switch(xknx, "TestOutlet_2", group_address='1/2/4') devices.add(switch2) self.assertEqual(devices["Living-Room.Light_1"], light1) self.assertEqual(devices["TestOutlet_1"], switch1) self.assertEqual(devices["Living-Room.Light_2"], light2) self.assertEqual(devices["TestOutlet_2"], switch2) with self.assertRaises(KeyError): # pylint: disable=pointless-statement devices["TestOutlet_X"] self.assertEqual(devices[0], light1) self.assertEqual(devices[1], switch1) self.assertEqual(devices[2], light2) self.assertEqual(devices[3], switch2) with self.assertRaises(IndexError): # pylint: disable=pointless-statement devices[4]
def test_to_knx_error(self): """Test to_knx function with wrong parametern.""" xknx = XKNX() remote_value = RemoteValueStep(xknx) with self.assertRaises(ConversionError): remote_value.to_knx(1)
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())
def test_add_wrong_type(self): """Test if exception is raised when wrong type of devices is added.""" xknx = XKNX(loop=self.loop) with self.assertRaises(TypeError): xknx.devices.add("fnord")
def test_initialization_wrong_significant_bit(self): """Test initialization with wrong significant_bit parameter.""" # pylint: disable=invalid-name xknx = XKNX(loop=self.loop) with self.assertRaises(TypeError): BinarySensor(xknx, 'TestInput', '1/2/3', significant_bit="1")
def test_from_knx(self): """Test from_knx function with normal operation.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueDpt2ByteUnsigned(xknx) self.assertEqual(remote_value.from_knx(DPTArray((0x0A, 0x0B))), 2571)
def test_from_knx(self): """Test from_knx function with normal operation.""" xknx = XKNX() remote_value = RemoteValueColorRGB(xknx) self.assertEqual(remote_value.from_knx(DPTArray((0x64, 0x65, 0x66))), (100, 101, 102))
def connect(self): self._xknx = XKNX()
def test_from_knx_controller_mode(self): """Test from_knx function with normal operation.""" xknx = XKNX() remote_value = RemoteValueControllerMode(xknx) self.assertEqual(remote_value.from_knx(DPTArray((0x02, ))), HVACControllerMode.MORNING_WARMUP)
def test_to_knx_controller_mode(self): """Test to_knx function with normal operation.""" xknx = XKNX() remote_value = RemoteValueControllerMode(xknx, ) self.assertEqual(remote_value.to_knx(HVACControllerMode.HEAT), DPTArray((0x01, )))
def test_to_knx_error(self): """Test to_knx function with wrong parametern.""" xknx = XKNX() remote_value = RemoteValueUpDown(xknx) with pytest.raises(ConversionError): remote_value.to_knx(1)
def test_search_response(self): """Test parsing and streaming SearchResponse KNX/IP packet.""" raw = ( 0x06, 0x10, 0x02, 0x02, 0x00, 0x50, 0x08, 0x01, 0xC0, 0xA8, 0x2A, 0x0A, 0x0E, 0x57, 0x36, 0x01, 0x02, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xE0, 0x00, 0x17, 0x0C, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x47, 0x69, 0x72, 0x61, 0x20, 0x4B, 0x4E, 0x58, 0x2F, 0x49, 0x50, 0x2D, 0x52, 0x6F, 0x75, 0x74, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x02, 0x01, 0x03, 0x02, 0x04, 0x01, 0x05, 0x01, 0x07, 0x01, ) xknx = XKNX() knxipframe = KNXIPFrame(xknx) self.assertEqual(knxipframe.from_knx(raw), 80) self.assertEqual(knxipframe.to_knx(), list(raw)) self.assertTrue(isinstance(knxipframe.body, SearchResponse)) self.assertEqual(knxipframe.body.control_endpoint, HPAI("192.168.42.10", 3671)) self.assertEqual(len(knxipframe.body.dibs), 2) # Specific testing of parsing and serializing of # DIBDeviceInformation and DIBSuppSVCFamilies is # done within knxip_dib_test.py self.assertTrue( isinstance(knxipframe.body.dibs[0], DIBDeviceInformation)) self.assertTrue(isinstance(knxipframe.body.dibs[1], DIBSuppSVCFamilies)) self.assertEqual(knxipframe.body.device_name, "Gira KNX/IP-Router") self.assertTrue(knxipframe.body.dibs[1].supports( DIBServiceFamily.ROUTING)) self.assertTrue(knxipframe.body.dibs[1].supports( DIBServiceFamily.TUNNELING)) self.assertFalse(knxipframe.body.dibs[1].supports( DIBServiceFamily.OBJECT_SERVER)) search_response = SearchResponse(xknx, control_endpoint=HPAI( ip_addr="192.168.42.10", port=3671)) search_response.dibs.append(knxipframe.body.dibs[0]) search_response.dibs.append(knxipframe.body.dibs[1]) knxipframe2 = KNXIPFrame.init_from_body(search_response) self.assertEqual(knxipframe2.to_knx(), list(raw))
def test_has_group_address(self): """Test has_group_address.""" xknx = XKNX(loop=self.loop) switch = Switch(xknx, 'TestOutlet', group_address='1/2/3') self.assertTrue(switch.has_group_address(GroupAddress('1/2/3'))) self.assertFalse(switch.has_group_address(GroupAddress('2/2/2')))
def test_unknown_device_name(self): """Test device_name if no DIBDeviceInformation is present.""" xknx = XKNX() search_response = SearchResponse(xknx) self.assertEqual(search_response.device_name, "UNKNOWN")
class KNXModule(object): """Representation of KNX Object.""" def __init__(self, hass, config): """Initialize of KNX module.""" self.hass = hass self.config = config self.connected = False self.initialized = True self.init_xknx() self.register_callbacks() def init_xknx(self): """Initialize of KNX object.""" from xknx import XKNX self.xknx = XKNX(config=self.config_file(), loop=self.hass.loop) @asyncio.coroutine def start(self): """Start KNX object. Connect to tunneling or Routing device.""" connection_config = self.connection_config() yield from self.xknx.start( state_updater=self.config[DOMAIN][CONF_KNX_STATE_UPDATER], connection_config=connection_config) self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.stop) self.connected = True @asyncio.coroutine def stop(self, event): """Stop KNX object. Disconnect from tunneling or Routing device.""" yield from self.xknx.stop() def config_file(self): """Resolve and return the full path of xknx.yaml if configured.""" config_file = self.config[DOMAIN].get(CONF_KNX_CONFIG) if not config_file: return None if not config_file.startswith("/"): return self.hass.config.path(config_file) return config_file def connection_config(self): """Return the connection_config.""" if CONF_KNX_TUNNELING in self.config[DOMAIN]: return self.connection_config_tunneling() elif CONF_KNX_ROUTING in self.config[DOMAIN]: return self.connection_config_routing() return self.connection_config_auto() def connection_config_routing(self): """Return the connection_config if routing is configured.""" from xknx.io import ConnectionConfig, ConnectionType local_ip = \ self.config[DOMAIN][CONF_KNX_ROUTING].get(CONF_KNX_LOCAL_IP) return ConnectionConfig( connection_type=ConnectionType.ROUTING, local_ip=local_ip) def connection_config_tunneling(self): """Return the connection_config if tunneling is configured.""" from xknx.io import ConnectionConfig, ConnectionType, \ DEFAULT_MCAST_PORT gateway_ip = \ self.config[DOMAIN][CONF_KNX_TUNNELING].get(CONF_HOST) gateway_port = \ self.config[DOMAIN][CONF_KNX_TUNNELING].get(CONF_PORT) local_ip = \ self.config[DOMAIN][CONF_KNX_TUNNELING].get(CONF_KNX_LOCAL_IP) if gateway_port is None: gateway_port = DEFAULT_MCAST_PORT return ConnectionConfig( connection_type=ConnectionType.TUNNELING, gateway_ip=gateway_ip, gateway_port=gateway_port, local_ip=local_ip) def connection_config_auto(self): """Return the connection_config if auto is configured.""" # pylint: disable=no-self-use from xknx.io import ConnectionConfig return ConnectionConfig() def register_callbacks(self): """Register callbacks within XKNX object.""" if CONF_KNX_FIRE_EVENT in self.config[DOMAIN] and \ self.config[DOMAIN][CONF_KNX_FIRE_EVENT]: from xknx.knx import AddressFilter address_filters = list(map( AddressFilter, self.config[DOMAIN][CONF_KNX_FIRE_EVENT_FILTER])) self.xknx.telegram_queue.register_telegram_received_cb( self.telegram_received_cb, address_filters) @asyncio.coroutine def telegram_received_cb(self, telegram): """Call invoked after a KNX telegram was received.""" self.hass.bus.fire('knx_event', { 'address': telegram.group_address.str(), 'data': telegram.payload.value }) # False signals XKNX to proceed with processing telegrams. return False @asyncio.coroutine def service_send_to_knx_bus(self, call): """Service for sending an arbitrary KNX message to the KNX bus.""" from xknx.knx import Telegram, Address, DPTBinary, DPTArray attr_payload = call.data.get(SERVICE_KNX_ATTR_PAYLOAD) attr_address = call.data.get(SERVICE_KNX_ATTR_ADDRESS) 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) payload = calculate_payload(attr_payload) address = Address(attr_address) telegram = Telegram() telegram.payload = payload telegram.group_address = address yield from self.xknx.telegrams.put(telegram)
def test_value_unit(self): """Test for the unit_of_measurement.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueScaling(xknx) self.assertEqual(remote_value.unit_of_measurement, "%")
def init_xknx(self): """Initialize of KNX object.""" from xknx import XKNX self.xknx = XKNX(config=self.config_file(), loop=self.hass.loop)
def test_default_value_unit(self): """Test for the default value of unit_of_measurement.""" xknx = XKNX() remote_value = RemoteValue(xknx) self.assertEqual(remote_value.unit_of_measurement, None)
def test_from_knx_unknown_operation_mode(self): """Test from_knx function with unsupported operation.""" xknx = XKNX() with self.assertRaises(ConversionError): RemoteValueBinaryHeatCool(xknx, controller_mode=None)