def test_len(self): """Test len() function.""" xknx = XKNX(loop=self.loop) devices = Devices() self.assertEqual(len(devices), 0) light1 = Light(xknx, 'Living-Room.Light_1', group_address_switch='1/6/7') devices.add(light1) self.assertEqual(len(devices), 1) sensor1 = BinarySensor(xknx, 'DiningRoom.Motion.Sensor', group_address_state='3/0/1', significant_bit=2) devices.add(sensor1) self.assertEqual(len(devices), 2) sensor2 = BinarySensor(xknx, 'DiningRoom.Motion.Sensor', group_address_state='3/0/1', significant_bit=3) devices.add(sensor2) self.assertEqual(len(devices), 3) light2 = Light(xknx, 'Living-Room.Light_2', group_address_switch='1/6/8') devices.add(light2) self.assertEqual(len(devices), 4)
def test_iter(self): """Test __iter__() function.""" 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_state='3/0/1', significant_bit=2) devices.add(sensor1) sensor2 = BinarySensor(xknx, 'DiningRoom.Motion.Sensor', group_address_state='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.__iter__()), (light1, sensor1, sensor2, light2))
def __init__(self, config=None, loop=None, own_address=Address(DEFAULT_ADDRESS), address_format=AddressFormat.LEVEL3, telegram_received_cb=None, device_updated_cb=None): """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams = asyncio.Queue() self.loop = loop or asyncio.get_event_loop() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = None self.knxip_interface = None self.started = False self.address_format = address_format self.own_address = own_address self.logger = logging.getLogger('xknx.log') self.knx_logger = logging.getLogger('xknx.knx') self.telegram_logger = logging.getLogger('xknx.telegram') if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb( telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb)
def __init__( self, own_address: str | IndividualAddress = DEFAULT_ADDRESS, address_format: GroupAddressType = GroupAddressType.LONG, telegram_received_cb: Callable[[Telegram], Awaitable[None]] | None = None, device_updated_cb: Callable[[Device], Awaitable[None]] | None = None, connection_state_changed_cb: Callable[[XknxConnectionState], Awaitable[None]] | None = None, rate_limit: int = DEFAULT_RATE_LIMIT, multicast_group: str = DEFAULT_MCAST_GRP, multicast_port: int = DEFAULT_MCAST_PORT, log_directory: str | None = None, state_updater: TrackerOptionType = False, daemon_mode: bool = False, connection_config: ConnectionConfig = ConnectionConfig(), ) -> None: """Initialize XKNX class.""" self.connection_manager = ConnectionManager() self.devices = Devices() self.knxip_interface = knx_interface_factory( self, connection_config=connection_config) self.management = Management(self) self.telegrams: asyncio.Queue[Telegram | None] = asyncio.Queue() self.telegram_queue = TelegramQueue(self) self.state_updater = StateUpdater(self, default_tracker_option=state_updater) self.task_registry = TaskRegistry(self) self.current_address = IndividualAddress(0) self.daemon_mode = daemon_mode self.multicast_group = multicast_group self.multicast_port = multicast_port self.own_address = IndividualAddress(own_address) self.rate_limit = rate_limit self.sigint_received = asyncio.Event() self.started = asyncio.Event() self.version = VERSION GroupAddress.address_format = address_format # for global string representation if log_directory is not None: self.setup_logging(log_directory) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb( telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb) if connection_state_changed_cb is not None: self.connection_manager.register_connection_state_changed_cb( connection_state_changed_cb)
def __init__(self, config=None, loop=None, own_address=PhysicalAddress(DEFAULT_ADDRESS), address_format=GroupAddressType.LONG, telegram_received_cb=None, device_updated_cb=None, rate_limit=DEFAULT_RATE_LIMIT): """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams = asyncio.Queue() self.loop = loop or asyncio.get_event_loop() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = None self.knxip_interface = None self.started = False self.address_format = address_format self.own_address = own_address self.rate_limit = rate_limit self.logger = logging.getLogger('xknx.log') self.knx_logger = logging.getLogger('xknx.knx') self.telegram_logger = logging.getLogger('xknx.telegram') self.connection_config = None if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb(telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb)
def test_contains(self): """Test __contains__() function.""" xknx = XKNX(loop=self.loop) devices = Devices() light1 = Light(xknx, 'Living-Room.Light_1', group_address_switch='1/6/7') devices.add(light1) light2 = Light(xknx, 'Living-Room.Light_2', group_address_switch='1/6/8') devices.add(light2) self.assertTrue('Living-Room.Light_1' in devices) self.assertTrue('Living-Room.Light_2' in devices) self.assertFalse('Living-Room.Light_3' in devices)
def __init__( self, config=None, own_address=DEFAULT_ADDRESS, address_format=GroupAddressType.LONG, telegram_received_cb=None, device_updated_cb=None, rate_limit=DEFAULT_RATE_LIMIT, multicast_group=DEFAULT_MCAST_GRP, multicast_port=DEFAULT_MCAST_PORT, log_directory=None, state_updater=False, daemon_mode=False, connection_config=ConnectionConfig(), ): """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams = asyncio.Queue() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = StateUpdater(self) self.knxip_interface = None self.started = asyncio.Event() self.address_format = address_format self.own_address = PhysicalAddress(own_address) self.rate_limit = rate_limit self.multicast_group = multicast_group self.multicast_port = multicast_port self.connection_config = connection_config self.start_state_updater = state_updater self.daemon_mode = daemon_mode self.version = VERSION if log_directory is not None: self.setup_logging(log_directory) if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb( telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb)
def __init__( self, config: Optional[str] = None, own_address: Union[str, IndividualAddress] = DEFAULT_ADDRESS, address_format: GroupAddressType = GroupAddressType.LONG, telegram_received_cb: Optional[Callable[[Telegram], Awaitable[None]]] = None, device_updated_cb: Optional[Callable[[Device], Awaitable[None]]] = None, rate_limit: int = DEFAULT_RATE_LIMIT, multicast_group: str = DEFAULT_MCAST_GRP, multicast_port: int = DEFAULT_MCAST_PORT, log_directory: Optional[str] = None, state_updater: bool = False, daemon_mode: bool = False, connection_config: ConnectionConfig = ConnectionConfig(), ) -> None: """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams: asyncio.Queue[Optional[Telegram]] = asyncio.Queue() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = StateUpdater(self) self.knxip_interface: Optional[KNXIPInterface] = None self.started = asyncio.Event() self.connected = asyncio.Event() self.address_format = address_format self.own_address = IndividualAddress(own_address) self.rate_limit = rate_limit self.multicast_group = multicast_group self.multicast_port = multicast_port self.connection_config = connection_config self.start_state_updater = state_updater self.daemon_mode = daemon_mode self.version = VERSION if log_directory is not None: self.setup_logging(log_directory) if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb(telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb)
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_iter(self): """Test __iter__() function.""" xknx = XKNX() devices = Devices() light1 = Light(xknx, "Livingroom", group_address_switch="1/6/7") sensor1 = BinarySensor(xknx, "Diningroom", group_address_state="3/0/1") sensor2 = BinarySensor(xknx, "Diningroom", group_address_state="3/0/1") light2 = Light(xknx, "Livingroom", group_address_switch="1/6/8") devices.add(light1) devices.add(sensor1) devices.add(sensor2) devices.add(light2) assert tuple(devices.__iter__()) == (light1, sensor1, sensor2, light2)
def __init__( self, config=None, loop=None, own_address=DEFAULT_ADDRESS, address_format=GroupAddressType.LONG, telegram_received_cb=None, device_updated_cb=None, rate_limit=DEFAULT_RATE_LIMIT, multicast_group=DEFAULT_MCAST_GRP, multicast_port=DEFAULT_MCAST_PORT, ): """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams = asyncio.Queue() self.loop = loop or asyncio.get_event_loop() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = StateUpdater(self) self.knxip_interface = None self.started = asyncio.Event() self.address_format = address_format self.own_address = PhysicalAddress(own_address) self.rate_limit = rate_limit self.multicast_group = multicast_group self.multicast_port = multicast_port self.logger = logging.getLogger("xknx.log") self.knx_logger = logging.getLogger("xknx.knx") self.telegram_logger = logging.getLogger("xknx.telegram") self.raw_socket_logger = logging.getLogger("xknx.raw_socket") self.connection_config = None self.version = VERSION if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb( telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb)
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_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_state='3/0/1', significant_bit=2) devices.add(sensor1) sensor2 = BinarySensor(xknx, 'DiningRoom.Motion.Sensor', group_address_state='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))
class XKNX: """Class for reading and writing KNX/IP packets.""" DEFAULT_ADDRESS = "15.15.250" DEFAULT_RATE_LIMIT = 20 def __init__( self, own_address: str | IndividualAddress = DEFAULT_ADDRESS, address_format: GroupAddressType = GroupAddressType.LONG, telegram_received_cb: Callable[[Telegram], Awaitable[None]] | None = None, device_updated_cb: Callable[[Device], Awaitable[None]] | None = None, connection_state_changed_cb: Callable[[XknxConnectionState], Awaitable[None]] | None = None, rate_limit: int = DEFAULT_RATE_LIMIT, multicast_group: str = DEFAULT_MCAST_GRP, multicast_port: int = DEFAULT_MCAST_PORT, log_directory: str | None = None, state_updater: TrackerOptionType = False, daemon_mode: bool = False, connection_config: ConnectionConfig = ConnectionConfig(), ) -> None: """Initialize XKNX class.""" self.connection_manager = ConnectionManager() self.devices = Devices() self.knxip_interface = knx_interface_factory( self, connection_config=connection_config) self.management = Management(self) self.telegrams: asyncio.Queue[Telegram | None] = asyncio.Queue() self.telegram_queue = TelegramQueue(self) self.state_updater = StateUpdater(self, default_tracker_option=state_updater) self.task_registry = TaskRegistry(self) self.current_address = IndividualAddress(0) self.daemon_mode = daemon_mode self.multicast_group = multicast_group self.multicast_port = multicast_port self.own_address = IndividualAddress(own_address) self.rate_limit = rate_limit self.sigint_received = asyncio.Event() self.started = asyncio.Event() self.version = VERSION GroupAddress.address_format = address_format # for global string representation if log_directory is not None: self.setup_logging(log_directory) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb( telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb) if connection_state_changed_cb is not None: self.connection_manager.register_connection_state_changed_cb( connection_state_changed_cb) def __del__(self) -> None: """Destructor. Cleaning up if this was not done before.""" if self.started.is_set(): try: loop = asyncio.get_event_loop() loop.run_until_complete(self.stop()) except RuntimeError as exp: logger.warning("Could not close loop, reason: %s", exp) async def __aenter__(self) -> "XKNX": """Start XKNX from context manager.""" await self.start() return self async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None, ) -> None: """Stop XKNX from context manager.""" await self.stop() async def start(self) -> None: """Start XKNX module. Connect to KNX/IP devices and start state updater.""" if self.knxip_interface.connection_config.threaded: await self.connection_manager.register_loop() self.task_registry.start() logger.info( "XKNX v%s starting %s connection to KNX bus.", VERSION, self.knxip_interface.connection_config.connection_type.name.lower( ), ) await self.knxip_interface.start() await self.telegram_queue.start() self.state_updater.start() self.started.set() if self.daemon_mode: await self.loop_until_sigint() async def join(self) -> None: """Wait until all telegrams were processed.""" await self.telegrams.join() async def stop(self) -> None: """Stop XKNX module.""" self.task_registry.stop() self.state_updater.stop() await self.join() await self.telegram_queue.stop() await self.knxip_interface.stop() self.started.clear() async def loop_until_sigint(self) -> None: """Loop until Crtl-C was pressed.""" def sigint_handler() -> None: """End loop.""" self.sigint_received.set() if platform == "win32": logger.warning("Windows does not support signals") else: loop = asyncio.get_running_loop() loop.add_signal_handler(signal.SIGINT, sigint_handler) logger.warning("Press Ctrl+C to stop") await self.sigint_received.wait() @staticmethod def setup_logging(log_directory: str) -> None: """Configure logging to file.""" if not os.path.isdir(log_directory): logger.warning("The provided log directory does not exist.") return _handler = TimedRotatingFileHandler( filename=f"{log_directory}{os.sep}xknx.log", when="midnight", backupCount=7, encoding="utf-8", ) _formatter = logging.Formatter( "%(asctime)s | %(name)s | %(levelname)s | %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) _handler.setFormatter(_formatter) _handler.setLevel(logging.DEBUG) for log_namespace in [ "xknx.log", "xknx.knx", "xknx.raw_socket", "xknx.telegram", "xknx.state_updater", ]: _logger = logging.getLogger(log_namespace) _logger.addHandler(_handler)
class XKNX: """Class for reading and writing KNX/IP packets.""" # pylint: disable=too-many-instance-attributes DEFAULT_ADDRESS = '15.15.250' DEFAULT_RATE_LIMIT = 20 def __init__(self, config=None, loop=None, own_address=PhysicalAddress(DEFAULT_ADDRESS), address_format=GroupAddressType.LONG, telegram_received_cb=None, device_updated_cb=None, rate_limit=DEFAULT_RATE_LIMIT): """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams = asyncio.Queue() self.loop = loop or asyncio.get_event_loop() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = None self.knxip_interface = None self.started = False self.address_format = address_format self.own_address = own_address self.rate_limit = rate_limit self.logger = logging.getLogger('xknx.log') self.knx_logger = logging.getLogger('xknx.knx') self.telegram_logger = logging.getLogger('xknx.telegram') self.connection_config = None if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb(telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb) def __del__(self): """Destructor. Cleaning up if this was not done before.""" if self.started: try: task = self.loop.create_task(self.stop()) self.loop.run_until_complete(task) except RuntimeError as exp: self.logger.warning("Could not close loop, reason: %s", exp) async def start(self, state_updater=False, daemon_mode=False, connection_config=None): """Start XKNX module. Connect to KNX/IP devices and start state updater.""" if connection_config is None: if self.connection_config is None: connection_config = ConnectionConfig() else: connection_config = self.connection_config self.knxip_interface = KNXIPInterface(self, connection_config=connection_config) await self.knxip_interface.start() await self.telegram_queue.start() if state_updater: from xknx.core import StateUpdater self.state_updater = StateUpdater(self) await self.state_updater.start() if daemon_mode: await self.loop_until_sigint() self.started = True async def join(self): """Wait until all telegrams were processed.""" await self.telegrams.join() async def _stop_knxip_interface_if_exists(self): """Stop KNXIPInterface if initialized.""" if self.knxip_interface is not None: await self.knxip_interface.stop() self.knxip_interface = None async def stop(self): """Stop XKNX module.""" await self.join() await self.telegram_queue.stop() await self._stop_knxip_interface_if_exists() self.started = False async def loop_until_sigint(self): """Loop until Crtl-C was pressed.""" def sigint_handler(): """End loop.""" self.sigint_received.set() if platform == "win32": self.logger.warning('Windows does not support signals') else: self.loop.add_signal_handler(signal.SIGINT, sigint_handler) self.logger.warning('Press Ctrl+C to stop') await self.sigint_received.wait()
def test_device_by_group_address(self): """Test get devices by group address.""" xknx = XKNX() 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_state="3/0/1") devices.add(sensor1) sensor2 = BinarySensor(xknx, "DiningRoom.Motion.Sensor", group_address_state="3/0/1") 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), )
class XKNX: """Class for reading and writing KNX/IP packets.""" # pylint: disable=too-many-instance-attributes DEFAULT_ADDRESS = '15.15.250' def __init__(self, config=None, loop=None, own_address=Address(DEFAULT_ADDRESS), address_format=AddressFormat.LEVEL3, telegram_received_cb=None, device_updated_cb=None): """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams = asyncio.Queue() self.loop = loop or asyncio.get_event_loop() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = None self.knxip_interface = None self.started = False self.address_format = address_format self.own_address = own_address self.logger = logging.getLogger('xknx.log') self.knx_logger = logging.getLogger('xknx.knx') self.telegram_logger = logging.getLogger('xknx.telegram') if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb( telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb) def __del__(self): """Destructor. Cleaning up if this was not done before.""" if self.started: try: task = self.loop.create_task(self.stop()) self.loop.run_until_complete(task) except RuntimeError as exp: self.logger.warning("Could not close loop, reason: %s", exp) @asyncio.coroutine def start(self, state_updater=False, daemon_mode=False, connection_config=ConnectionConfig()): """Start XKNX module. Connect to KNX/IP devices and start state updater.""" self.knxip_interface = KNXIPInterface( self, connection_config=connection_config) yield from self.knxip_interface.start() yield from self.telegram_queue.start() if state_updater: from xknx.core import StateUpdater self.state_updater = StateUpdater(self) yield from self.state_updater.start() if daemon_mode: yield from self.loop_until_sigint() self.started = True @asyncio.coroutine def join(self): """Wait until all telegrams were processed.""" yield from self.telegrams.join() def _stop_knxip_interface_if_exists(self): """Stop KNXIPInterface if initialized.""" if self.knxip_interface is not None: yield from self.knxip_interface.stop() self.knxip_interface = None @asyncio.coroutine def stop(self): """Stop XKNX module.""" yield from self.join() yield from self.telegram_queue.stop() yield from self._stop_knxip_interface_if_exists() self.started = False @asyncio.coroutine def loop_until_sigint(self): """Loop until Crtl-C was pressed.""" def sigint_handler(): """End loop.""" self.sigint_received.set() self.loop.add_signal_handler(signal.SIGINT, sigint_handler) self.logger.warning('Press Ctrl+C to stop') yield from self.sigint_received.wait()
class XKNX: """Class for reading and writing KNX/IP packets.""" # pylint: disable=too-many-instance-attributes DEFAULT_ADDRESS = '15.15.250' DEFAULT_RATE_LIMIT = 20 def __init__(self, config=None, loop=None, own_address=PhysicalAddress(DEFAULT_ADDRESS), address_format=GroupAddressType.LONG, telegram_received_cb=None, device_updated_cb=None, rate_limit=DEFAULT_RATE_LIMIT, multicast_group=DEFAULT_MCAST_GRP, multicast_port=DEFAULT_MCAST_PORT): """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams = asyncio.Queue() self.loop = loop or asyncio.get_event_loop() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = StateUpdater(self) self.knxip_interface = None self.started = asyncio.Event() self.address_format = address_format self.own_address = own_address self.rate_limit = rate_limit self.multicast_group = multicast_group self.multicast_port = multicast_port self.logger = logging.getLogger('xknx.log') self.knx_logger = logging.getLogger('xknx.knx') self.telegram_logger = logging.getLogger('xknx.telegram') self.raw_socket_logger = logging.getLogger('xknx.raw_socket') self.connection_config = None self.version = VERSION if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb(telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb) def __del__(self): """Destructor. Cleaning up if this was not done before.""" if self.started.is_set(): try: task = self.loop.create_task(self.stop()) self.loop.run_until_complete(task) except RuntimeError as exp: self.logger.warning("Could not close loop, reason: %s", exp) async def start(self, state_updater=False, daemon_mode=False, connection_config=None): """Start XKNX module. Connect to KNX/IP devices and start state updater.""" if connection_config is None: if self.connection_config is None: connection_config = ConnectionConfig() else: connection_config = self.connection_config self.knxip_interface = KNXIPInterface(self, connection_config=connection_config) self.logger.info('XKNX v%s starting %s connection to KNX bus.', VERSION, connection_config.connection_type.name.lower()) await self.knxip_interface.start() await self.telegram_queue.start() if state_updater: self.state_updater.start() self.started.set() if daemon_mode: await self.loop_until_sigint() async def join(self): """Wait until all telegrams were processed.""" await self.telegrams.join() async def _stop_knxip_interface_if_exists(self): """Stop KNXIPInterface if initialized.""" if self.knxip_interface is not None: await self.knxip_interface.stop() self.knxip_interface = None async def stop(self): """Stop XKNX module.""" self.state_updater.stop() await self.join() await self.telegram_queue.stop() await self._stop_knxip_interface_if_exists() self.started.clear() async def loop_until_sigint(self): """Loop until Crtl-C was pressed.""" def sigint_handler(): """End loop.""" self.sigint_received.set() if platform == "win32": self.logger.warning('Windows does not support signals') else: self.loop.add_signal_handler(signal.SIGINT, sigint_handler) self.logger.warning('Press Ctrl+C to stop') await self.sigint_received.wait()
def test_iter(self): """Test __iter__() function.""" xknx = XKNX() 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_state="3/0/1") devices.add(sensor1) sensor2 = BinarySensor(xknx, "DiningRoom.Motion.Sensor", group_address_state="3/0/1") devices.add(sensor2) light2 = Light(xknx, "Living-Room.Light_2", group_address_switch="1/6/8") devices.add(light2) self.assertEqual(tuple(devices.__iter__()), (light1, sensor1, sensor2, light2))
class XKNX: """Class for reading and writing KNX/IP packets.""" # pylint: disable=too-many-instance-attributes DEFAULT_ADDRESS = "15.15.250" DEFAULT_RATE_LIMIT = 20 def __init__( self, config=None, own_address=DEFAULT_ADDRESS, address_format=GroupAddressType.LONG, telegram_received_cb=None, device_updated_cb=None, rate_limit=DEFAULT_RATE_LIMIT, multicast_group=DEFAULT_MCAST_GRP, multicast_port=DEFAULT_MCAST_PORT, log_directory=None, state_updater=False, daemon_mode=False, connection_config=ConnectionConfig(), ): """Initialize XKNX class.""" # pylint: disable=too-many-arguments self.devices = Devices() self.telegrams = asyncio.Queue() self.sigint_received = asyncio.Event() self.telegram_queue = TelegramQueue(self) self.state_updater = StateUpdater(self) self.knxip_interface = None self.started = asyncio.Event() self.address_format = address_format self.own_address = PhysicalAddress(own_address) self.rate_limit = rate_limit self.multicast_group = multicast_group self.multicast_port = multicast_port self.connection_config = connection_config self.start_state_updater = state_updater self.daemon_mode = daemon_mode self.version = VERSION if log_directory is not None: self.setup_logging(log_directory) if config is not None: Config(self).read(config) if telegram_received_cb is not None: self.telegram_queue.register_telegram_received_cb( telegram_received_cb) if device_updated_cb is not None: self.devices.register_device_updated_cb(device_updated_cb) def __del__(self): """Destructor. Cleaning up if this was not done before.""" if self.started.is_set(): try: loop = asyncio.get_event_loop() loop.run_until_complete(self.stop()) except RuntimeError as exp: logger.warning("Could not close loop, reason: %s", exp) async def __aenter__(self): """Start XKNX from context manager.""" await self.start() return self async def __aexit__(self, exc_type, exc, traceback): """Stop XKNX from context manager.""" await self.stop() async def start(self): """Start XKNX module. Connect to KNX/IP devices and start state updater.""" self.knxip_interface = KNXIPInterface( self, connection_config=self.connection_config) logger.info( "XKNX v%s starting %s connection to KNX bus.", VERSION, self.connection_config.connection_type.name.lower(), ) await self.knxip_interface.start() await self.telegram_queue.start() if self.start_state_updater: self.state_updater.start() self.started.set() if self.daemon_mode: await self.loop_until_sigint() async def join(self): """Wait until all telegrams were processed.""" await self.telegrams.join() async def _stop_knxip_interface_if_exists(self): """Stop KNXIPInterface if initialized.""" if self.knxip_interface is not None: await self.knxip_interface.stop() self.knxip_interface = None async def stop(self): """Stop XKNX module.""" self.state_updater.stop() await self.join() await self.telegram_queue.stop() await self._stop_knxip_interface_if_exists() self.started.clear() async def loop_until_sigint(self): """Loop until Crtl-C was pressed.""" def sigint_handler(): """End loop.""" self.sigint_received.set() if platform == "win32": logger.warning("Windows does not support signals") else: loop = asyncio.get_running_loop() loop.add_signal_handler(signal.SIGINT, sigint_handler) logger.warning("Press Ctrl+C to stop") await self.sigint_received.wait() @staticmethod def setup_logging(log_directory: str): """Configure logging to file.""" if not os.path.isdir(log_directory): logger.warning("The provided log directory does not exist.") return _handler = TimedRotatingFileHandler( filename=f"{log_directory}{os.sep}xknx.log", when="midnight", backupCount=7, encoding="utf-8", ) _formatter = logging.Formatter( "%(asctime)s | %(name)s | %(levelname)s | %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) _handler.setFormatter(_formatter) _handler.setLevel(logging.DEBUG) for log_namespace in [ "xknx.log", "xknx.knx", "xknx.raw_socket", "xknx.telegram", "xknx.state_updater", ]: _logger = logging.getLogger(log_namespace) _logger.addHandler(_handler)
def test_device_by_group_address(self): """Test get devices by group address.""" xknx = XKNX() devices = Devices() light1 = Light(xknx, "Livingroom", group_address_switch="1/6/7") sensor1 = BinarySensor(xknx, "Diningroom", group_address_state="3/0/1") sensor2 = BinarySensor(xknx, "Diningroom", group_address_state="3/0/1") light2 = Light(xknx, "Livingroom", group_address_switch="1/6/8") devices.add(light1) devices.add(sensor1) devices.add(sensor2) devices.add(light2) assert tuple(devices.devices_by_group_address( GroupAddress("1/6/7"))) == (light1, ) assert tuple(devices.devices_by_group_address( GroupAddress("1/6/8"))) == (light2, ) assert tuple(devices.devices_by_group_address( GroupAddress("3/0/1"))) == ( sensor1, sensor2, )