コード例 #1
0
ファイル: test_current_state.py プロジェクト: lsta/pai
def send_initial_status(alarm):
    sendMessage("labels_loaded", data=dict(
        partition={
            1: dict(
                id=1,
                label='Partition 1',
                key='Partition_1'
            )
        }
    ))

    sendMessage("status_update", status=dict(
        partition={
            1: dict(
                arm=False,
                alarm_in_memory=False,
                audible_alarm=False,
                exit_delay=False,
                was_in_alarm=False
            )
        }
    ))

    alarm.storage.update_container_object.assert_any_call('partition', 'Partition_1', {
        'current_state': 'disarmed',
        'target_state': 'disarmed'
    })
コード例 #2
0
async def send_initial_status(alarm):
    sendMessage(
        "labels_loaded",
        data=dict(
            partition={
                1: dict(id=1, label="Partition 1", key="Partition_1"),
                2: dict(id=1, label="Partition 2", key="Partition_2"),
            }
        ),
    )

    sendMessage(
        "status_update",
        status=dict(
            partition={
                1: dict(
                    arm=False,
                    alarm_in_memory=False,
                    audible_alarm=False,
                    exit_delay=False,
                    was_in_alarm=False,
                ),
                2: dict(
                    arm=False,
                    alarm_in_memory=False,
                    audible_alarm=False,
                    exit_delay=False,
                    was_in_alarm=False,
                ),
            }
        ),
    )

    await asyncio.sleep(0.01)
コード例 #3
0
ファイル: gsm_interface.py プロジェクト: psyciknz/pai
    def handle_message(self, timestamp, source, message):
        """ Handle GSM message. It should be a command """

        self.logger.debug("Received Message {} {} {}".format(
            timestamp, source, message))

        if self.alarm is None:
            return

        if source in cfg.GSM_CONTACTS:
            ret = self.send_command(message)

            if ret:
                self.logger.info("ACCEPTED: {}".format(message))
                self.send_sms(source, "ACCEPTED: {}".format(message))
                message = "ACCEPTED: {}: {}".format(source, message)
            else:
                self.logger.warning("REJECTED: {}".format(message))
                self.send_sms(source, "REJECTED: {}".format(message))
                message = "REJECTED: {}: {}".format(source, message)
        else:
            self.logger.warning("REJECTED: {}".format(message))
            message = "REJECTED: {}: {}".format(source, message)

        ps.sendMessage("notifications",
                       message=dict(source=self.name,
                                    message=message,
                                    level=logging.INFO))
コード例 #4
0
async def test_hass():
    interface = HomeAssistantMQTTInterface()
    interface.mqtt = MagicMock()
    interface.start()

    try:
        await asyncio.sleep(0.1)  # TODO: Bad way to wait for a start

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

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

        interface.mqtt.publish.assert_called_with(
            'homeassistant/alarm_control_panel/pai/Partition_1/config',
            json.dumps(
                dict(name='Partition 1',
                     unique_id="pai_partition_Partition_1",
                     command_topic='paradox/control/partitions/Partition_1',
                     state_topic=
                     'paradox/states/partitions/Partition_1/current_state',
                     availability_topic='paradox/interface/MQTTInterface',
                     device=dict(),
                     payload_disarm="disarm",
                     payload_arm_home="arm_stay",
                     payload_arm_away="arm",
                     payload_arm_night="arm_sleep")), 0, True)
    finally:
        interface.stop()
コード例 #5
0
ファイル: gsm_interface.py プロジェクト: psyciknz/pai
    def run(self):
        self.logger.info("Starting GSM Interface")

        ps.subscribe(self.handle_panel_event, "events")
        ps.subscribe(self.handle_notify, "notifications")

        try:
            while not self.stop_running.isSet():
                time.sleep(1)

                while not self.connected() and self.stop_running.isSet():
                    logging.warning("Could not connect to modem")
                    time.sleep(10)

                try:
                    data = self.port.read(200)
                    if len(data) > 0:
                        tokens = data.decode('latin-1').strip().split('"')
                        for i in range(len(tokens)):
                            tokens[i] = tokens[i].strip()

                        if len(tokens) > 0:
                            if tokens[0] == '+CMT:':
                                source = tokens[1]
                                timestamp = datetime.datetime.strptime(
                                    tokens[5].split('+')[0],
                                    '%y/%m/%d,%H:%M:%S')
                                message = tokens[6]
                                self.handle_message(timestamp, source, message)
                            elif tokens[0].startswith('+CUSD:'):
                                ps.sendMessage("notifications",
                                               message=dict(
                                                   source=self.name,
                                                   message=tokens[1],
                                                   level=logging.INFO))
                    else:
                        self.run_loop()

                except Exception:
                    self.modem_connected = False
                    # self.logger.exception("")

        except (KeyboardInterrupt, SystemExit):
            self.logger.debug("GSM loop stopping")
            return

        except Exception:
            self.logger.exception("GSM loop")

        return True
コード例 #6
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
コード例 #7
0
    def _process_status(raw_status: Container) -> None:
        status = convert_raw_status(raw_status)

        for limit_key, limit_arr in cfg.LIMITS.items():
            if limit_key not in status:
                continue

            status[limit_key].filter(limit_arr)

        #     # TODO: throttle power update messages
        #     if time.time() - self.last_power_update >= cfg.POWER_UPDATE_INTERVAL:
        #         force = PublishPropertyChange.YES if cfg.PUSH_POWER_UPDATE_WITHOUT_CHANGE else PublishPropertyChange.NO

        if cfg.LOGGING_DUMP_STATUS:
            logger.debug("properties: %s", status)

        ps.sendMessage('status_update', status=status)
コード例 #8
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'
    })
コード例 #9
0
ファイル: test_labels.py プロジェクト: lsta/pai
def test_on_labels_load():
    alarm = Paradox(None)

    sendMessage("labels_loaded", 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'
    )
コード例 #10
0
ファイル: panel.py プロジェクト: psyciknz/pai
    async def update_labels(self):
        logger.info("Updating Labels from Panel")

        for elem_type in self.mem_map['elements']:
            elem_def = self.mem_map['elements'][elem_type]

            addresses = list(chain.from_iterable(elem_def['addresses']))
            limits = cfg.LIMITS.get(elem_type)
            if limits is not None:
                addresses = [
                    a for i, a in enumerate(addresses) if i + 1 in limits
                ]

            await self.load_labels(self.core.data[elem_type],
                                   addresses,
                                   label_offset=elem_def['label_offset'])

            logger.info("{}: {}".format(
                elem_type.title(), ', '.join(
                    [v["label"] for v in self.core.data[elem_type].values()])))

        ps.sendMessage('labels_loaded', data=self.core.data)
コード例 #11
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
コード例 #12
0
    async def full_connect(self) -> bool:
        try:
            if not await self.connect():
                return False

            logger.info("Loading data from panel memory")
            await self.panel.load_memory()

            logger.info("Running")
            self.run_state = RunState.RUN
            self.request_status_refresh()  # Trigger status update

            ps.sendMessage("connected")
            return True
        except asyncio.TimeoutError:
            logger.error(
                "Timeout while connecting to panel. Is an other connection active?"
            )
        except ConnectionError as e:
            logger.error("Failed to connect: %s" % str(e))

        self.run_state = RunState.ERROR

        return False
コード例 #13
0
    async def connect(self):
        if self._connection:
            self.disconnect()  # socket needs to be also closed
        self.panel = None

        self.run_state = RunState.INIT
        logger.info("Connecting to interface")
        if not await self.connection.connect():
            self.run_state = RunState.ERROR
            logger.error('Failed to connect to interface')
            return False

        logger.info("Connecting to panel")

        if not self.panel:
            self.panel = create_panel(self)
            self.connection.variable_message_length(self.panel.variable_message_length)

        try:
            logger.info("Initiating communication")

            initiate_reply = await self.send_wait(
                self.panel.get_message('InitiateCommunication'), None, reply_expected=0x07
            )

            if initiate_reply:
                model = initiate_reply.fields.value.label.strip(b'\0 ').decode(cfg.LABEL_ENCODING)
                firmware_version = "{}.{} build {}".format(
                    initiate_reply.fields.value.application.version,
                    initiate_reply.fields.value.application.revision,
                    initiate_reply.fields.value.application.build
                )
                serial_number = hexlify(initiate_reply.fields.value.serial_number).decode()

                logger.info("Found Panel {} version {}".format(model, firmware_version))
            else:
                raise ConnectionError("Panel did not replied to InitiateCommunication")

            logger.info("Starting communication")
            reply = await self.send_wait(self.panel.get_message('StartCommunication'),
                                         args=dict(source_id=0x02), reply_expected=0x00)

            if reply is None:
                raise ConnectionError("Panel did not replied to StartCommunication")

            if reply.fields.value.product_id is not None:
                self.panel = create_panel(self, reply.fields.value.product_id)  # Now we know what panel it is. Let's
                # recreate panel object.
                ps.sendMessage(
                    'panel_detected', panel=DetectedPanel(
                        product_id=reply.fields.value.product_id,
                        model=model,
                        firmware_version=firmware_version,
                        serial_number=serial_number
                    )
                )
            else:
                raise PanelNotDetected('Failed to detect panel')

            result = await self.panel.initialize_communication(reply, cfg.PASSWORD)
            if not result:
                raise ConnectionError("Failed to initialize communication")

            if cfg.SYNC_TIME:
                await self.sync_time()

            if cfg.DEVELOPMENT_DUMP_MEMORY:
                if hasattr(self.panel, 'dump_memory') and callable(self.panel.dump_memory):
                    logger.warning("Requested memory dump. Dumping...")

                    await self.panel.dump_memory()
                    logger.warning("Memory dump completed. Exiting pai.")
                    raise SystemExit()
                else:
                    logger.warning("Requested memory dump, but current panel type does not support it yet.")

            logger.info("Loading definitions")
            definitions = await self.panel.load_definitions()
            ps.sendMessage('definitions_loaded', data=definitions)

            logger.info("Loading labels")
            labels = await self.panel.load_labels()
            ps.sendMessage('labels_loaded', data=labels)

            logger.info("Connection OK")
            self.run_state = RunState.RUN
            self.request_status_refresh()  # Trigger status update

            ps.sendMessage('connected')
            return True
        except asyncio.TimeoutError as e:
            logger.error("Timeout while connecting to panel: %s" % str(e))
        except ConnectionError as e:
            logger.error("Failed to connect: %s" % str(e))

        self.run_state = RunState.ERROR

        return False
コード例 #14
0
 def run_state(self, value: RunState):
     self._run_state = value
     ps.sendMessage("run-state", state=value)
コード例 #15
0
ファイル: mqtt_interface.py プロジェクト: psyciknz/pai
    def handle_message(self, client, userdata, message):
        """Handle message received from the MQTT broker"""
        self.logger.info("message topic={}, payload={}".format(
            message.topic, str(message.payload.decode("utf-8"))))

        if message.retain:
            return

        if self.alarm is None:
            self.logger.warning("No alarm. Ignoring command")
            return

        topic = message.topic.split(cfg.MQTT_BASE_TOPIC)[1]

        topics = topic.split("/")

        if len(topics) < 3:
            self.logger.error("Invalid topic in mqtt message: {}".format(
                message.topic))
            return

        if topics[1] == cfg.MQTT_NOTIFICATIONS_TOPIC:
            if topics[2].upper() == "CRITICAL":
                level = logging.CRITICAL
            elif topics[2].upper() == "INFO":
                level = logging.INFO
            else:
                self.logger.error("Invalid notification level: {}".format(
                    topics[2]))
                return

            payload = message.payload.decode("latin").strip()
            ps.sendMessage("notifications",
                           message=dict(source=self.name,
                                        payload=payload,
                                        level=level))
            return

        if topics[1] != cfg.MQTT_CONTROL_TOPIC:
            self.logger.error("Invalid subtopic in mqtt message: {}".format(
                message.topic))
            return

        command = message.payload.decode("latin").strip()
        element = topics[3]

        # Process a Zone Command
        if topics[2] == cfg.MQTT_ZONE_TOPIC:
            if not self.alarm.control_zone(element, command):
                self.logger.warning("Zone command refused: {}={}".format(
                    element, command))

        # Process a Partition Command
        elif topics[2] == cfg.MQTT_PARTITION_TOPIC:

            if command in cfg.MQTT_PARTITION_HOMEBRIDGE_COMMANDS and cfg.MQTT_HOMEBRIDGE_ENABLE:
                command = cfg.MQTT_PARTITION_HOMEBRIDGE_COMMANDS[command]
            elif command in cfg.MQTT_PARTITION_HOMEASSISTANT_COMMANDS and cfg.MQTT_HOMEASSISTANT_ENABLE:
                command = cfg.MQTT_PARTITION_HOMEASSISTANT_COMMANDS[command]

            if command.startswith('code_toggle-'):
                tokens = command.split('-')
                if len(tokens) < 2:
                    return

                if tokens[1] not in cfg.MQTT_TOGGLE_CODES:
                    self.logger.warning("Invalid toggle code {}".format(
                        tokens[1]))
                    return

                if element.lower() == 'all':
                    command = 'arm'

                    for k, v in self.partitions.items():
                        # If "all" and a single partition is armed, default is
                        # to desarm
                        for k1, v1 in self.partitions[k].items():
                            if (k1 == 'arm' or k1 == 'exit_delay'
                                    or k1 == 'entry_delay') and v1:
                                command = 'disarm'
                                break

                        if command == 'disarm':
                            break

                elif element in self.partitions:
                    if ('arm' in self.partitions[element] and self.partitions[element]['arm'])\
                            or ('exit_delay' in self.partitions[element] and self.partitions[element]['exit_delay']):
                        command = 'disarm'
                    else:
                        command = 'arm'
                else:
                    self.logger.debug("Element {} not found".format(element))
                    return

                ps.sendMessage('notifications',
                               message=dict(
                                   source="mqtt",
                                   message="Command by {}: {}".format(
                                       cfg.MQTT_TOGGLE_CODES[tokens[1]],
                                       command),
                                   level=logging.INFO))

            self.logger.debug("Partition command: {} = {}".format(
                element, command))
            if not self.alarm.control_partition(element, command):
                self.logger.warning("Partition command refused: {}={}".format(
                    element, command))

        # Process an Output Command
        elif topics[2] == cfg.MQTT_OUTPUT_TOPIC:
            self.logger.debug("Output command: {} = {}".format(
                element, command))

            if not self.alarm.control_output(element, command):
                self.logger.warning("Output command refused: {}={}".format(
                    element, command))
        else:
            self.logger.error("Invalid control property {}".format(topics[2]))
コード例 #16
0
    async def connect(self) -> bool:
        if self._connection:
            await self.disconnect()  # socket needs to be also closed
        self.panel = None

        self.run_state = RunState.INIT
        logger.info("Connecting to interface")
        if not await self.connection.connect():
            self.run_state = RunState.ERROR
            logger.error("Failed to connect to interface")
            return False

        logger.info("Connecting to Panel")

        if not self.panel:
            self.panel = create_panel(self)
            self.connection.variable_message_length(
                self.panel.variable_message_length)

        try:
            initiate_reply = await self.send_wait(
                self.panel.get_message("InitiateCommunication"),
                None,
                reply_expected=0x07,
            )

            if initiate_reply:
                model = initiate_reply.fields.value.label.strip(b"\0 ").decode(
                    cfg.LABEL_ENCODING)
                firmware_version = "{}.{} build {}".format(
                    initiate_reply.fields.value.application.version,
                    initiate_reply.fields.value.application.revision,
                    initiate_reply.fields.value.application.build,
                )
                serial_number = hexlify(
                    initiate_reply.fields.value.serial_number).decode()

                logger.info("Panel Identified {} version {}".format(
                    model, firmware_version))
            else:
                raise ConnectionError(
                    "Panel did not replied to InitiateCommunication")

            logger.info("Initiating panel connection")
            reply = await self.send_wait(
                self.panel.get_message("StartCommunication"),
                args=dict(source_id=0x02),
                reply_expected=0x00,
            )

            if reply is None:
                raise ConnectionError(
                    "Panel did not replied to StartCommunication")

            if reply.fields.value.product_id is not None:
                self.panel = create_panel(
                    self, reply)  # Now we know what panel it is. Let's
                # recreate panel object.
                ps.sendMessage(
                    "panel_detected",
                    panel=DetectedPanel(
                        product_id=reply.fields.value.product_id,
                        model=model,
                        firmware_version=firmware_version,
                        serial_number=serial_number,
                    ),
                )
            else:
                raise PanelNotDetected("Failed to detect panel")

            result = await self.panel.initialize_communication(cfg.PASSWORD)
            if not result:
                raise ConnectionError("Failed to initialize communication")

            self.run_state = RunState.CONNECTED
            logger.info("Connection OK")
            return True
        except asyncio.TimeoutError:
            logger.error(
                "Timeout while connecting to panel. Is an other connection active?"
            )
        except ConnectionError as e:
            logger.error("Failed to connect: %s" % str(e))

        self.run_state = RunState.ERROR

        return False
コード例 #17
0
async def test_hass(mocker):
    mocker.patch("paradox.lib.utils.main_thread_loop",
                 asyncio.get_event_loop())
    mocker.patch.multiple(cfg, MQTT_HOMEASSISTANT_AUTODISCOVERY_ENABLE=True)
    con = mocker.patch("paradox.interfaces.mqtt.core.MQTTConnection")
    con.get_instance.return_value.availability_topic = "paradox/interface/availability"
    con.get_instance.return_value.pai_status_topic = "paradox/interface/pai_status"

    alarm = mocker.MagicMock()

    alarm.panel = create_evo192_panel(alarm)
    interface = HomeAssistantMQTTInterface(alarm)
    interface.start()
    interface.on_connect(None, None, None, None)
    assert (interface.connected_future.done()
            and interface.connected_future.result() is True)

    try:
        await asyncio.sleep(0.01)  # TODO: Bad way to wait for a start

        sendMessage(
            "panel_detected",
            panel=DetectedPanel(ProductIdEnum.parse(b"\x05"), "EVO192",
                                "6.80 build 5", "aabbccdd"),
        )

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

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

        await asyncio.sleep(0.01)

        interface.mqtt.publish.assert_any_call(
            "homeassistant/sensor/aabbccdd/pai_status/config",
            json.dumps({
                "name": "Run status",
                "unique_id": "aabbccdd_partition_pai_status",
                "state_topic": "paradox/interface/pai_status",
                "device": {
                    "manufacturer": "Paradox",
                    "model": "EVO192",
                    "identifiers": ["Paradox", "EVO192", "aabbccdd"],
                    "name": "EVO192",
                    "sw_version": "6.80 build 5",
                },
            }),
            0,
            True,
        )

        interface.mqtt.publish.assert_any_call(
            "homeassistant/alarm_control_panel/aabbccdd/Partition_1/config",
            json.dumps({
                "name": "Partition 1",
                "unique_id": "aabbccdd_partition_Partition_1",
                "command_topic": "paradox/control/partitions/Partition_1",
                "state_topic":
                "paradox/states/partitions/Partition_1/current_state",
                "availability_topic": "paradox/interface/availability",
                "device": {
                    "manufacturer": "Paradox",
                    "model": "EVO192",
                    "identifiers": ["Paradox", "EVO192", "aabbccdd"],
                    "name": "EVO192",
                    "sw_version": "6.80 build 5",
                },
                "payload_disarm": "disarm",
                "payload_arm_home": "arm_stay",
                "payload_arm_away": "arm",
                "payload_arm_night": "arm_sleep",
            }),
            0,
            True,
        )
    finally:
        interface.stop()
        interface.join()
        assert not interface.is_alive()
コード例 #18
0
ファイル: paradox.py プロジェクト: psyciknz/pai
    async def connect_async(self):
        self.disconnect()  # socket needs to be also closed

        logger.info("Connecting to interface")
        if not await self.connection.connect():
            logger.error('Failed to connect to interface')
            self.run = STATE_STOP
            return False

        self.run = STATE_STOP

        self.connection.timeout(0.5)

        logger.info("Connecting to panel")

        # Reset all states
        self.reset()

        if not self.panel:
            self.panel = create_panel(self)
            self.connection.variable_message_length(self.panel.variable_message_length)

        try:
            logger.info("Initiating communication")

            reply = await self.send_wait(self.panel.get_message('InitiateCommunication'), None, reply_expected=0x07)

            if reply:
                logger.info("Found Panel {} version {}.{} build {}".format(
                    (reply.fields.value.label.strip(b'\0 ').decode(cfg.LABEL_ENCODING)),
                    reply.fields.value.application.version,
                    reply.fields.value.application.revision,
                    reply.fields.value.application.build))
            else:
                raise ConnectionError("Panel did not replied to InitiateCommunication")


            logger.info("Starting communication")
            reply = await self.send_wait(self.panel.get_message('StartCommunication'),
                                   args=dict(source_id=0x02), reply_expected=0x00)

            if reply is None:
                raise ConnectionError("Panel did not replied to StartCommunication")

            if reply.fields.value.product_id is not None:
                self.panel = create_panel(self, reply.fields.value.product_id)  # Now we know what panel it is. Let's
                ps.sendMessage('panel_detected', product_id=reply.fields.value.product_id)
            # recreate panel object.

            result = await self.panel.initialize_communication(reply, cfg.PASSWORD)
            if not result:
                raise ConnectionError("Failed to initialize communication")

            # Now we need to start async message reading worker
            self.run = STATE_RUN

            self.receive_worker_task = self.work_loop.create_task(self.receive_worker())

            if cfg.SYNC_TIME:
                await self.sync_time()

            if cfg.DEVELOPMENT_DUMP_MEMORY:
                if hasattr(self.panel, 'dump_memory') and callable(self.panel.dump_memory):
                    logger.warn("Requested memory dump. Dumping...")

                    await self.panel.dump_memory()
                    logger.warn("Memory dump completed. Exiting pai.")
                    raise SystemExit()
                else:
                    logger.warn("Requested memory dump, but current panel type does not support it yet.")

            await self.panel.update_labels()


            logger.info("Connection OK")
            self.loop_wait = False

            ps.sendMessage('connected')
            return True
        except ConnectionError as e:
            logger.error("Failed to connect: %s" % str(e))
        except Exception:
            logger.exception("Connect error")

        self.run = STATE_STOP
        return False