コード例 #1
0
def test_update_properties(mocker):
    alarm = Paradox()
    alarm.panel = mocker.MagicMock()
    ps = mocker.patch("paradox.lib.ps")

    alarm._on_status_update(converted_status)

    print(ps)
コード例 #2
0
ファイル: test_status.py プロジェクト: jakezp/pai-1
async def test_update_properties(mocker):
    alarm = Paradox()
    alarm.panel = mocker.MagicMock()
    ps = mocker.patch("paradox.lib.ps")

    alarm._on_status_update(converted_status)

    await asyncio.sleep(0.01)
コード例 #3
0
ファイル: main.py プロジェクト: lsta/pai
def main(args):
    global alarm, interface_manager

    if 'config' in args and args.config is not None:
        import os
        config_file = os.path.abspath(args.config)
        cfg.load(config_file)
    else:
        cfg.load()

    config_logger(logger)

    logger.info("Starting Paradox Alarm Interface")
    logger.info("Console Log level set to {}".format(
        cfg.LOGGING_LEVEL_CONSOLE))

    interface_manager = InterfaceManager(config=cfg)
    interface_manager.start()

    time.sleep(1)

    signal.signal(signal.SIGINT, exit_handler)

    # Start interacting with the alarm
    alarm = Paradox()
    interface_manager.set_alarm(alarm)
    retry = 1
    while alarm is not None:
        logger.info("Starting...")
        retry_time_wait = 2 ^ retry
        retry_time_wait = 30 if retry_time_wait > 30 else retry_time_wait

        try:
            if alarm.connect():
                retry = 1
                alarm.loop()
            else:
                logger.error("Unable to connect to alarm")

            time.sleep(retry_time_wait)
        except ConnectionError as e:  # Connection to IP Module or MQTT lost
            logger.error("Connection to panel lost: %s. Restarting" % str(e))
            time.sleep(retry_time_wait)

        except OSError:  # Connection to IP Module or MQTT lost
            logger.exception("Restarting")
            time.sleep(retry_time_wait)

        except (KeyboardInterrupt, SystemExit):
            break  # break exits the retry loop

        except Exception:
            logger.exception("Restarting")
            time.sleep(retry_time_wait)

        retry += 1

    exit_handler()
コード例 #4
0
ファイル: test_current_state.py プロジェクト: jakezp/pai-1
def alarm(mocker):
    mocker.patch('paradox.lib.utils.main_thread_loop', asyncio.get_event_loop())
    # conn = mocker.patch("paradox.interfaces.mqtt.core.MQTTConnection")
    # conn.connected = True
    alarm = Paradox(None)
    mocker.spy(alarm.storage, 'update_container_object')
    alarm.panel = MagicMock()

    return alarm
コード例 #5
0
ファイル: main.py プロジェクト: marcantoineveilleux/pai
def main():
    logger.info("Starting Paradox Alarm Interface")
    logger.info("Console Log level set to {}".format(cfg.LOGGING_LEVEL_CONSOLE))

    interface_manager = InterfaceManager(config=cfg)
    interface_manager.start()

    time.sleep(1)

    # Load a connection to the alarm
    if cfg.CONNECTION_TYPE == "Serial":
        logger.info("Using Serial Connection")
        from paradox.connections.serial_connection import SerialCommunication

        connection = SerialCommunication(port=cfg.SERIAL_PORT)
    elif cfg.CONNECTION_TYPE == 'IP':
        logger.info("Using IP Connection")
        from paradox.connections.ip_connection import IPConnection

        connection = IPConnection(host=cfg.IP_CONNECTION_HOST, port=cfg.IP_CONNECTION_PORT, password=cfg.IP_CONNECTION_PASSWORD)
    else:
        logger.error("Invalid connection type: {}".format(cfg.CONNECTION_TYPE))
        sys.exit(-1)

    # Start interacting with the alarm
    alarm = Paradox(connection=connection, interface=interface_manager)
    retry = 1
    while True:
        logger.info("Starting...")
        retry_time_wait = 2 ^ retry
        retry_time_wait = 30 if retry_time_wait > 30 else retry_time_wait

        try:
            alarm.disconnect()
            if alarm.connect():
                retry = 1
                interface_manager.set_alarm(alarm)
                alarm.loop()
            else:
                logger.error("Unable to connect to alarm")

            time.sleep(retry_time_wait)
        except (ConnectionError, OSError):  # Connection to IP Module or MQTT lost
            logger.exception("Restarting")
            time.sleep(retry_time_wait)

        except (KeyboardInterrupt, SystemExit):
            logger.info("Exit start")
            if alarm:
                alarm.disconnect()
            break  # break exits the retry loop

        except Exception:
            logger.exception("Restarting")
            time.sleep(retry_time_wait)

        retry += 1

    interface_manager.stop()
    logger.info("Good bye!")
コード例 #6
0
ファイル: test_labels.py プロジェクト: torretahacs/pai
def test_on_labels_load():
    alarm = Paradox(None)

    alarm._on_labels_load(
        data=dict(partition={1: dict(id=1, label="Partition 1", key="Partition_1")})
    )

    assert isinstance(alarm.storage.get_container("partition"), ElementTypeContainer)

    assert alarm.storage.get_container_object("partition", "Partition_1") == dict(
        id=1, label="Partition 1", key="Partition_1"
    )
コード例 #7
0
ファイル: test_current_state.py プロジェクト: lsta/pai
def test_current_alarm(mocker):
    alarm = Paradox(None)
    mocker.spy(alarm.storage, 'update_container_object')
    alarm.panel = Panel_EVO192(alarm, 5)

    send_initial_status(alarm)

    payload = binascii.unhexlify('e2ff1cc414130b010f2c1801030000000000024f66666963652020202020202020202000d9')
    raw = LiveEvent.parse(payload)
    alarm.handle_event_message(raw)

    alarm.storage.update_container_object.assert_any_call('partition', 'Partition_1', {
        'current_state': 'triggered'
    })
コード例 #8
0
def test_partitions(mocker):
    alarm = Paradox()
    alarm.panel = mocker.MagicMock()
    alarm.panel.property_map = {
        "arm": dict(level=EventLevel.INFO,
                    message={"True": "{Type} {label} is armed",
                             "False": "{Type} {label} is disarmed"}),
    }

    event = mocker.MagicMock()
    mocker.patch("paradox.lib.ps.sendChange")
    mocker.patch("paradox.lib.ps.sendEvent")
    mocker.patch('paradox.event.ChangeEvent', return_value=event)

    ps.sendMessage("labels_loaded", data=dict(
        partition={
            1: dict(
                id=1,
                label='Partition 1',
                key='Partition_1'
            )
        }
    ))

    assert isinstance(alarm.panel, mocker.MagicMock)

    alarm.storage.update_container_object("partition", "Partition_1", dict(arm=True))

    ps.sendChange.assert_called_once_with(Change('partition', 'Partition_1', 'arm', True, initial=True))
    ps.sendChange.reset_mock()

    assert isinstance(alarm.panel, mocker.MagicMock)

    ps.sendMessage("status_update", status=dict(
        partition={
            1: dict(
                arm=False
            )
        }
    ))

    assert isinstance(alarm.panel, mocker.MagicMock)

    ps.sendChange.assert_any_call(Change('partition', 'Partition_1', 'current_state', 'disarmed', initial=True))
    ps.sendChange.assert_any_call(Change('partition', 'Partition_1', 'target_state', 'disarmed', initial=True))
    ps.sendChange.assert_any_call(Change('partition', 'Partition_1', 'arm', False, initial=False))
    assert ps.sendChange.call_count == 3

    assert ps.sendEvent.call_count == 0
コード例 #9
0
ファイル: main.py プロジェクト: jakezp/pai-1
def main(args):
    global alarm, interface_manager
    
    if 'config' in args and args.config is not None:
        import os
        config_file = os.path.abspath(args.config)
        cfg.load(config_file)
    else:
        cfg.load()

    config_logger(logger)

    logger.info(f"Starting Paradox Alarm Interface {VERSION}")
    logger.info(f"Config loaded from {cfg.CONFIG_FILE_LOCATION}")

    logger.info(f"Console Log level set to {cfg.LOGGING_LEVEL_CONSOLE}")

    signal.signal(signal.SIGINT, exit_handler)
    signal.signal(signal.SIGTERM, exit_handler)

    # Start interacting with the alarm
    alarm = Paradox()
    interface_manager = InterfaceManager(alarm, config=cfg)
    interface_manager.start()

    loop = asyncio.get_event_loop()
    loop.run_until_complete(run_loop(alarm))
    
    exit_handler()
コード例 #10
0
ファイル: pai_dump_memory.py プロジェクト: torretahacs/pai
async def dump_memory(file, memory):
    alarm = Paradox()
    if not await alarm.connect():
        logger.error("Failed to connect")

    await alarm.dump_memory(file, memory)
    await alarm.disconnect()
コード例 #11
0
def test_convert_raw_status(mocker):
    mocker.patch('paradox.lib.ps')
    p = Paradox()
    status = convert_raw_status(message_parser_output)

    assert status["zone"] == {
        1: dict(open=False, tamper=False, low_battery=False, generated_alarm=False, presently_in_alarm=False,
                activated_entry_delay=False,
                activated_intellizone_delay=False, bypassed=False, shutted_down=False,
                tx_delay=False, supervision_trouble=False),
        2: dict(open=False, tamper=False, low_battery=False, generated_alarm=False,
                presently_in_alarm=False,
                activated_entry_delay=False,
                activated_intellizone_delay=False,
                bypassed=False,
                shutted_down=False,
                tx_delay=False,
                supervision_trouble=False)
    }

    assert status["partition"] == message_parser_output["partition_status"]
    assert status["door"] == {1: dict(open=False), 2: dict(open=False)}
    assert status["bus-module"] == {1: dict(trouble=False), 2: dict(trouble=False)}
    assert status["system"]["troubles"] == message_parser_output["system"]["troubles"]

    a = json.dumps(converted_status, sort_keys=True, indent=2, default=str)
    b = json.dumps(status, sort_keys=True, indent=2, default=str)
    assert a == b
コード例 #12
0
async def test_control_doors(mocker):
    alarm = Paradox()
    alarm.panel = mocker.Mock(spec=Panel)
    alarm.panel.control_doors = CoroutineMock()

    alarm.storage.get_container("door").deep_merge(
        {1: {
            "id": 1,
            "key": "Door 1"
        }})

    assert await alarm.control_door("1", "unlock")
    alarm.panel.control_doors.assert_called_once_with([1], "unlock")
    alarm.panel.control_doors.reset_mock()

    assert await alarm.control_door("Door 1", "unlock")
    alarm.panel.control_doors.assert_called_once_with([1], "unlock")
コード例 #13
0
async def setup_panel(mocker):
    mocker.patch.multiple(
        cfg, LOGGING_LEVEL_CONSOLE=logging.DEBUG, LOGGING_DUMP_PACKETS=True
    )
    mocker.patch("paradox.lib.utils.main_thread_loop", asyncio.get_event_loop())
    # cfg.LOGGING_LEVEL_CONSOLE = logging.DEBUG
    # cfg.LOGGING_DUMP_PACKETS = True

    logger.setLevel(logging.DEBUG)
    alarm = Paradox()
    # alarm.work_loop.set_debug(True)

    alarm.run_state = RunState.RUN

    start_communication_response = Container(
        fields=Container(value=Container(product_id="MAGELLAN_MG5050"))
    )

    alarm.panel = create_panel(alarm, start_communication_response)

    await send_initial_status(alarm)
    con = MockConnection(alarm.on_connection_message)

    alarm._connection = con
    alarm._register_connection_handlers()

    return alarm, con
コード例 #14
0
def test_partitions_callable_prop(mocker):
    alarm = Paradox()
    alarm.panel = mocker.MagicMock()
    alarm.panel.property_map = {
        "arm":
        dict(level=EventLevel.INFO,
             message={
                 "True": "{Type} {label} is armed",
                 "False": "{Type} {label} is disarmed"
             }),
    }

    alarm._on_labels_load(data=dict(
        partition={1: dict(id=1, label='Partition 1', key='Partition_1')}))

    sendChange = mocker.patch("paradox.data.memory_storage.ps.sendChange")

    alarm._on_status_update(status=dict(partition={1: dict(arm=False)}))

    sendChange.assert_any_call(
        Change('partition', 'Partition_1', 'arm', False, initial=True))
    sendChange.reset_mock()

    alarm.storage.update_container_object("partition", "Partition_1",
                                          dict(arm=lambda old: not old))
    sendChange.assert_any_call(Change('partition', 'Partition_1', 'arm', True))
コード例 #15
0
ファイル: test_current_state.py プロジェクト: lsta/pai
def test_current_state_armed_away(mocker):
    alarm = Paradox(None)
    mocker.spy(alarm.storage, 'update_container_object')
    alarm.panel = MagicMock()

    send_initial_status(alarm)

    sendMessage("status_update", status=dict(
        partition={
            1: dict(
                arm=True
            )
        }
    ))
    alarm.storage.update_container_object.assert_any_call('partition', 'Partition_1', {
        'current_state': 'armed_away',
        'target_state': 'armed_away'
    })
コード例 #16
0
def test_on_labels_load():
    alarm = Paradox(None)

    alarm._on_labels_load(data=dict(
        partition={
            1: dict(
                id=1,
                label='Partition 1',
                key='Partition_1'
            )
        }
    ))

    assert isinstance(alarm.storage.get_container('partition'), ElementTypeContainer)

    assert alarm.storage.get_container_object('partition', 'Partition_1') == dict(
        id=1,
        label='Partition 1',
        key='Partition_1'
    )
コード例 #17
0
async def test_send_panic(mocker):
    alarm = Paradox()
    alarm.panel = mocker.Mock(spec=Panel)
    alarm.panel.send_panic = CoroutineMock()

    alarm.storage.get_container("partition").deep_merge(
        {1: {
            "id": 1,
            "key": "Partition 1"
        }})
    alarm.storage.get_container("user").deep_merge(
        {3: {
            "id": 3,
            "key": "User 3"
        }})

    assert await alarm.send_panic("1", "fire", "3")
    alarm.panel.send_panic.assert_called_once_with([1], "fire", 3)
    alarm.panel.send_panic.reset_mock()

    assert await alarm.send_panic("Partition 1", "fire", "User 3")
    alarm.panel.send_panic.assert_called_once_with([1], "fire", 3)
コード例 #18
0
def test_partitions_callable_prop(mocker):
    alarm = Paradox()
    alarm.panel = mocker.MagicMock()
    alarm.panel.property_map = {
        "arm": dict(level=EventLevel.INFO,
                    message={"True": "{Type} {label} is armed",
                             "False": "{Type} {label} is disarmed"}),
    }

    event = mocker.MagicMock()
    mocker.patch.object(ps, "sendChange")
    mocker.patch.object(ps, "sendEvent")
    mocker.patch('paradox.event.ChangeEvent', return_value=event)

    ps.sendMessage("labels_loaded", data=dict(
        partition={
            1: dict(
                id=1,
                label='Partition 1',
                key='Partition_1'
            )
        }
    ))

    ps.sendMessage("status_update", status=dict(
        partition={
            1: dict(
                arm=False
            )
        }
    ))

    ps.sendChange.assert_any_call(Change('partition', 'Partition_1', 'arm', False, initial=True))
    ps.sendChange.reset_mock()

    alarm.storage.update_container_object("partition", "Partition_1", dict(arm=lambda old: not old))
    ps.sendChange.assert_any_call(Change('partition', 'Partition_1', 'arm', True))

    ps.sendEvent.call_count = 0
コード例 #19
0
def test_convert_raw_status(mocker):
    mocker.patch.object(ps, 'sendChange')
    mocker.patch.object(ps, 'sendEvent')
    connection = mocker.stub(name='Connection_stub')
    p = Paradox(connection)
    status = convert_raw_status(evo_status)

    assert status["zone"] == {
        1:
        dict(open=False,
             tamper=False,
             low_battery=False,
             generated_alarm=False,
             presently_in_alarm=False,
             activated_entry_delay=False,
             activated_intellizone_delay=False,
             bypassed=False,
             shutted_down=False,
             tx_delay=False,
             supervision_trouble=False),
        2:
        dict(open=False,
             tamper=False,
             low_battery=False,
             generated_alarm=False,
             presently_in_alarm=False,
             activated_entry_delay=False,
             activated_intellizone_delay=False,
             bypassed=False,
             shutted_down=False,
             tx_delay=False,
             supervision_trouble=False)
    }
    assert status["partition"] == evo_status["partition_status"]
    assert status["door"] == {1: dict(open=False), 2: dict(open=False)}
    assert status["bus-module"] == {
        1: dict(trouble=False),
        2: dict(trouble=False)
    }
    assert status["system"]["power"] == {
        "vdc": 16.5,
        "battery": 13.4,
        "dc": 13.5
    }
    assert status["system"]["date"] == {
        'time': datetime.datetime(2019, 10, 11, 21, 12, 2),
        'weekday': 6
    }
    assert status["system"]["troubles"] == evo_status["system"]["troubles"]
コード例 #20
0
def test_partitions(mocker):
    alarm = Paradox()
    alarm.panel = mocker.MagicMock()
    alarm.panel.property_map = {
        "arm":
        dict(
            level=EventLevel.INFO,
            message={
                "True": "{Type} {label} is armed",
                "False": "{Type} {label} is disarmed",
            },
        ),
    }

    alarm._on_labels_load(data=dict(
        partition={1: dict(id=1, label="Partition 1", key="Partition_1")}))

    sendChange = mocker.patch("paradox.data.memory_storage.ps.sendChange")

    alarm.storage.update_container_object("partition", "Partition_1",
                                          dict(arm=True))

    sendChange.assert_called_once_with(
        Change("partition", "Partition_1", "arm", True, initial=True))
    sendChange.reset_mock()

    assert isinstance(alarm.panel, mocker.MagicMock)

    alarm._on_status_update(status=dict(partition={1: dict(arm=False)}))

    assert isinstance(alarm.panel, mocker.MagicMock)

    sendChange.assert_any_call(
        Change("partition",
               "Partition_1",
               "current_state",
               "disarmed",
               initial=True))
    sendChange.assert_any_call(
        Change("partition",
               "Partition_1",
               "target_state",
               "disarmed",
               initial=True))
    sendChange.assert_any_call(
        Change("partition", "Partition_1", "arm", False, initial=False))
コード例 #21
0
    def make_paradox(self) -> Paradox:
        if self._serial_port:
            config.CONNECTION_TYPE = 'Serial'
            config.SERIAL_PORT = self._serial_port
            config.SERIAL_BAUD = self._serial_baud

            self.debug('using serial connection on %s:%s', config.SERIAL_PORT,
                       config.SERIAL_BAUD)

        else:  # IP connection, e.g. 192.168.1.2:10000:paradox
            config.CONNECTION_TYPE = 'IP'
            config.IP_CONNECTION_HOST = self._ip_host
            config.IP_CONNECTION_PORT = self._ip_port
            config.IP_CONNECTION_PASSWORD = self._ip_password.encode()

            self.debug('using IP connection on %s:%s',
                       config.IP_CONNECTION_HOST, config.IP_CONNECTION_PORT)

        return Paradox()
コード例 #22
0
async def setup_panel(mocker):
    mocker.patch.object(cfg, "LOGGING_LEVEL_CONSOLE", logging.DEBUG)
    mocker.patch.object(cfg, "LOGGING_DUMP_PACKETS", True)
    mocker.patch("paradox.lib.utils.main_thread_loop", asyncio.get_event_loop())
    # cfg.LOGGING_LEVEL_CONSOLE = logging.DEBUG
    # cfg.LOGGING_DUMP_PACKETS = True
    
    logger.setLevel(logging.DEBUG)
    alarm = Paradox()
    #alarm.work_loop.set_debug(True)

    alarm.run_state = RunState.RUN
    alarm.panel = create_panel(alarm, 'MAGELLAN_MG5050')
    
    await send_initial_status(alarm)
    con = MockConnection(alarm.on_connection_message)
    
    alarm._connection = con
    alarm._register_connection_handlers()
    
    return alarm, con 
コード例 #23
0
ファイル: test_current_state.py プロジェクト: jakezp/pai-1
async def test_current_alarm(mocker):
    mocker.patch('paradox.lib.utils.main_thread_loop', asyncio.get_event_loop())
    alarm = Paradox(None)
    alarm.panel = Panel_EVO192(alarm, 5)
    mocker.spy(alarm.storage, 'update_container_object')

    send_initial_status(alarm)

    payload = binascii.unhexlify('e2ff1cc414130b010f2c1801030000000000024f66666963652020202020202020202000d9')
    raw = LiveEvent.parse(payload)
    alarm.handle_event_message(raw)

    await asyncio.sleep(0.01)

    alarm.storage.update_container_object.assert_any_call('partition', 'Partition_1', {
        'current_state': 'triggered'
    })
    alarm.panel = None
コード例 #24
0
ファイル: main.py プロジェクト: faizalps501/pai
def main(args):
    global alarm, interface_manager

    time.tzset()
    if "config" in args and args.config is not None:
        import os

        config_file = os.path.abspath(args.config)
        cfg.load(config_file)
    else:
        cfg.load()

    config_logger(logger)

    logger.info(f"Starting Paradox Alarm Interface {VERSION}")
    logger.info(f"Config loaded from {cfg.CONFIG_FILE_LOCATION}")

    logger.info(f"Console Log level set to {cfg.LOGGING_LEVEL_CONSOLE}")

    # Registering additional encodings
    register_encodings()

    # Start interacting with the alarm
    alarm = Paradox()
    loop = asyncio.get_event_loop()
    for signame in ("SIGINT", "SIGTERM"):
        sig = getattr(signal, signame)
        loop.add_signal_handler(
            sig, lambda: asyncio.ensure_future(exit_handler(signame)))

    interface_manager = InterfaceManager(alarm, config=cfg)
    interface_manager.start()

    loop = asyncio.get_event_loop()
    loop.run_until_complete(run_loop())

    sys.exit(0)
コード例 #25
0
ファイル: test_current_state.py プロジェクト: torretahacs/pai
async def test_current_alarm(mocker):
    mocker.patch("paradox.lib.utils.main_thread_loop",
                 asyncio.get_event_loop())
    alarm = Paradox(None)

    alarm.panel = create_evo192_panel(alarm)
    mocker.spy(alarm.storage, "update_container_object")

    send_initial_status(alarm)

    payload = binascii.unhexlify(
        "e2ff1cc414130b010f2c1801030000000000024f66666963652020202020202020202000d9"
    )
    raw = LiveEvent.parse(payload)
    alarm.handle_event_message(raw)

    await asyncio.sleep(0.01)

    alarm.storage.update_container_object.assert_any_call(
        "partition", "Partition_1", {"current_state": "triggered"})
    alarm.panel = None
コード例 #26
0
def main(args):
    global alarm, interface_manager

    if 'config' in args and args.config is not None:
        import os
        config_file = os.path.abspath(args.config)
        cfg.load(config_file)
    else:
        cfg.load()

    config_logger(logger)

    logger.info("Starting Paradox Alarm Interface")
    logger.info("Console Log level set to {}".format(
        cfg.LOGGING_LEVEL_CONSOLE))

    interface_manager = InterfaceManager(config=cfg)
    interface_manager.start()

    time.sleep(1)

    # Load a connection to the alarm
    if cfg.CONNECTION_TYPE == "Serial":
        logger.info("Using Serial Connection")
        from paradox.connections.serial_connection import SerialCommunication

        connection = SerialCommunication(port=cfg.SERIAL_PORT,
                                         baud=cfg.SERIAL_BAUD)
    elif cfg.CONNECTION_TYPE == 'IP':
        logger.info("Using IP Connection")
        from paradox.connections.ip_connection import IPConnection

        connection = IPConnection(host=cfg.IP_CONNECTION_HOST,
                                  port=cfg.IP_CONNECTION_PORT,
                                  password=cfg.IP_CONNECTION_PASSWORD)
    else:
        logger.error("Invalid connection type: {}".format(cfg.CONNECTION_TYPE))
        sys.exit(-1)

    signal.signal(signal.SIGINT, exit_handler)

    # Start interacting with the alarm
    alarm = Paradox(connection=connection, interface=interface_manager)
    retry = 1
    while True:
        logger.info("Starting...")
        retry_time_wait = 2 ^ retry
        retry_time_wait = 30 if retry_time_wait > 30 else retry_time_wait

        try:
            if alarm.connect():
                retry = 1
                interface_manager.set_alarm(alarm)
                alarm.loop()
            else:
                logger.error("Unable to connect to alarm")

            time.sleep(retry_time_wait)
        except ConnectionError as e:  # Connection to IP Module or MQTT lost
            logger.error("Connection to panel lost: %s. Restarting" % str(e))
            time.sleep(retry_time_wait)

        except OSError:  # Connection to IP Module or MQTT lost
            logger.exception("Restarting")
            time.sleep(retry_time_wait)

        except (KeyboardInterrupt, SystemExit):
            break  # break exits the retry loop

        except Exception:
            logger.exception("Restarting")
            time.sleep(retry_time_wait)

        retry += 1

    exit_handler()