Ejemplo n.º 1
0
    async def broadcast(self, profile, cluster, src_ep, dst_ep, grpid, radius,
                        sequence, data,
                        broadcast_address=BroadcastAddress.RX_ON_WHEN_IDLE):
        if not self.is_controller_running:
            raise ControllerError("ApplicationController is not running")

        aps_frame = t.EmberApsFrame()
        aps_frame.profileId = t.uint16_t(profile)
        aps_frame.clusterId = t.uint16_t(cluster)
        aps_frame.sourceEndpoint = t.uint8_t(src_ep)
        aps_frame.destinationEndpoint = t.uint8_t(dst_ep)
        aps_frame.options = t.EmberApsOption(
            t.EmberApsOption.APS_OPTION_NONE
        )
        aps_frame.groupId = t.uint16_t(grpid)
        aps_frame.sequence = t.uint8_t(sequence)

        with self._pending.new(sequence) as req:
            async with self._in_flight_msg:
                res = await self._ezsp.sendBroadcast(broadcast_address,
                                                     aps_frame, radius,
                                                     sequence, data)
                if res[0] != t.EmberStatus.SUCCESS:
                    hdr, hdr_args = self._dst_pp(broadcast_address, aps_frame)
                    msg = hdr + "Broadcast failure: %s"
                    msg_args = (hdr_args + (res[0], ))
                    raise DeliveryError(msg % msg_args)

                # Wait for messageSentHandler message
                res = await asyncio.wait_for(req.send,
                                             timeout=APS_ACK_TIMEOUT)
        return res
Ejemplo n.º 2
0
    def _handle_tx_status(self, frame_id, nwk, tries, tx_status, dsc_status):
        LOGGER.debug(
            (
                "tx_explicit to 0x%04x: %s after %i tries. Discovery Status: %s,"
                " Frame #%i"
            ),
            nwk,
            tx_status,
            tries,
            dsc_status,
            frame_id,
        )
        try:
            (fut,) = self._awaiting.pop(frame_id)
        except KeyError:
            LOGGER.debug("unexpected tx_status report received")
            return

        try:
            if tx_status in (
                t.TXStatus.BROADCAST_APS_TX_ATTEMPT,
                t.TXStatus.SELF_ADDRESSED,
                t.TXStatus.SUCCESS,
            ):
                fut.set_result(tx_status)
            else:
                fut.set_exception(DeliveryError("%s" % (tx_status,)))
        except asyncio.InvalidStateError as ex:
            LOGGER.debug("duplicate tx_status for %s nwk? State: %s", nwk, ex)
Ejemplo n.º 3
0
    async def request(self, nwk, profile, cluster, src_ep, dst_ep, sequence, data, expect_reply=True,
                      timeout=APS_REPLY_TIMEOUT):
        if not self.is_controller_running:
            raise ControllerError("ApplicationController is not running")

        aps_frame = t.EmberApsFrame()
        aps_frame.profileId = t.uint16_t(profile)
        aps_frame.clusterId = t.uint16_t(cluster)
        aps_frame.sourceEndpoint = t.uint8_t(src_ep)
        aps_frame.destinationEndpoint = t.uint8_t(dst_ep)
        aps_frame.options = t.EmberApsOption(
            t.EmberApsOption.APS_OPTION_RETRY |
            t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY
        )
        aps_frame.groupId = t.uint16_t(0)
        aps_frame.sequence = t.uint8_t(sequence)

        with self._pending.new(sequence, expect_reply) as req:
            async with self._in_flight_msg:
                res = await self._ezsp.sendUnicast(self.direct, nwk, aps_frame,
                                                   sequence, data)
                if res[0] != t.EmberStatus.SUCCESS:
                    hdr, hdr_args = self._dst_pp(nwk, aps_frame)
                    msg = hdr + "message send failure: %s"
                    msg_args = (hdr_args + (res[0], ))
                    raise DeliveryError(msg % msg_args)

                res = await asyncio.wait_for(req.send, timeout=APS_ACK_TIMEOUT)

            if expect_reply:
                res = await asyncio.wait_for(req.reply, timeout)
        return res
Ejemplo n.º 4
0
 def _handle_tx_status(self, data):
     LOGGER.debug("tx_status: %s", data)
     fut, = self._awaiting.pop(data[0])
     if data[3]:
         fut.set_exception(DeliveryError("Message send failure: delivery status = 0x%02x, retry count = %s, discovery status = 0x%02x" % (data[3], data[2], data[4])))
     else:
         fut.set_result(None)
     return
Ejemplo n.º 5
0
 def _handle_frame_failure(self, message_type, destination, aps_frame, message_tag, status, message):
     try:
         send_fut, reply_fut = self._pending.pop(message_tag)
         send_fut.set_exception(DeliveryError("Message send failure _frame_failure: %s" % (status, )))
         if reply_fut:
             reply_fut.cancel()
     except KeyError:
         LOGGER.warning("Unexpected message send failure _frame_failure")
     except asyncio.futures.InvalidStateError as exc:
         LOGGER.debug("Invalid state on future - probably duplicate response: %s", exc)
Ejemplo n.º 6
0
 def _handle_frame_failure(self, message_type, destination, aps_frame, message_tag, status, message):
     hdr, hdr_args = self._dst_pp(destination, aps_frame)
     try:
         request = self._pending[message_tag]
         msg = hdr + "message send failure: %s"
         msg_args = (hdr_args + (status, ))
         request.send.set_exception(DeliveryError(msg % msg_args))
     except KeyError:
         LOGGER.debug(hdr + "Unexpected message send failure", *hdr_args)
     except asyncio.futures.InvalidStateError as exc:
         LOGGER.debug(hdr + "Invalid state on future - probably duplicate response: %s",
                      *(hdr_args + (exc, )))
Ejemplo n.º 7
0
    async def mocksend(method, nwk, aps_frame, seq, data):
        if not ezsp_operational:
            raise EzspError
        req = app._pending[seq]
        if send_ack_received:
            if send_ack_success:
                req.send.set_result(mock.sentinel.result)
            else:
                req.send.set_exception(DeliveryError())

        if req.reply:
            if do_reply:
                req.reply.set_result(mock.sentinel.result)
        return [returnvals.pop(0)]
Ejemplo n.º 8
0
    async def request(self,
                      nwk,
                      profile,
                      cluster,
                      src_ep,
                      dst_ep,
                      sequence,
                      data,
                      expect_reply=True,
                      timeout=10):
        assert sequence not in self._pending
        send_fut = asyncio.Future()
        reply_fut = None
        if expect_reply:
            reply_fut = asyncio.Future()
        self._pending[sequence] = (send_fut, reply_fut)

        aps_frame = t.EmberApsFrame()
        aps_frame.profileId = t.uint16_t(profile)
        aps_frame.clusterId = t.uint16_t(cluster)
        aps_frame.sourceEndpoint = t.uint8_t(src_ep)
        aps_frame.destinationEndpoint = t.uint8_t(dst_ep)
        aps_frame.options = t.EmberApsOption(
            t.EmberApsOption.APS_OPTION_RETRY
            | t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY)
        aps_frame.groupId = t.uint16_t(0)
        aps_frame.sequence = t.uint8_t(sequence)

        v = await self._ezsp.sendUnicast(self.direct, nwk, aps_frame, sequence,
                                         data)
        if v[0] != t.EmberStatus.SUCCESS:
            self._pending.pop(sequence)
            send_fut.cancel()
            if expect_reply:
                reply_fut.cancel()
            raise DeliveryError("Message send failure %s" % (v[0], ))

        # Wait for messageSentHandler message
        v = await send_fut
        if expect_reply:
            # Wait for reply
            try:
                v = await asyncio.wait_for(reply_fut, timeout)
            except:  # noqa: E722
                # If we timeout (or fail for any reason), clear the future
                self._pending.pop(sequence)
                raise
        return v
Ejemplo n.º 9
0
    async def request(self,
                      nwk,
                      profile,
                      cluster,
                      src_ep,
                      dst_ep,
                      sequence,
                      data,
                      expect_reply=True,
                      timeout=10):
        assert sequence not in self._pending
        send_fut = asyncio.Future()
        reply_fut = None
        if expect_reply:
            reply_fut = asyncio.Future()
        self._pending[sequence] = (send_fut, reply_fut)

        aps_frame = t.EmberApsFrame()
        aps_frame.profileId = t.uint16_t(profile)
        aps_frame.clusterId = t.uint16_t(cluster)
        aps_frame.sourceEndpoint = t.uint8_t(src_ep)
        aps_frame.destinationEndpoint = t.uint8_t(dst_ep)
        aps_frame.options = t.EmberApsOption(
            t.EmberApsOption.APS_OPTION_RETRY
            | t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY)
        aps_frame.groupId = t.uint16_t(0)
        aps_frame.sequence = t.uint8_t(sequence)

        v = await self._ezsp.sendUnicast(self.direct, nwk, aps_frame, sequence,
                                         data)
        if v[0] != t.EmberStatus.SUCCESS:
            self._pending.pop(sequence)
            send_fut.cancel()
            if expect_reply:
                reply_fut.cancel()
            raise DeliveryError("Message send failure _send_unicast_fail %s" %
                                (v[0], ))
        try:
            v = await send_fut
        except DeliveryError as e:
            LOGGER.debug("DeliveryError: %s", e)
            raise
        except Exception as e:
            LOGGER.debug("other Exception: %s", e)
        if expect_reply:
            v = await asyncio.wait_for(reply_fut, timeout)
        return v
Ejemplo n.º 10
0
    async def request(self,
                      nwk,
                      profile,
                      cluster,
                      src_ep,
                      dst_ep,
                      sequence,
                      data,
                      expect_reply=True,
                      timeout=10):
        assert sequence not in self._pending
        send_fut = asyncio.Future()
        reply_fut = None
        if expect_reply:
            reply_fut = asyncio.Future()
        self._pending[sequence] = (send_fut, reply_fut)

        v = self._zigate.raw_aps_data_request(nwk,
                                              src_ep,
                                              dst_ep,
                                              profile,
                                              cluster,
                                              data,
                                              security=3)
        self._zigate_seq[sequence] = v.sequence

        if v.status != 0:
            self._pending.pop(sequence)
            self._zigate_seq.pop(sequence)
            if expect_reply:
                reply_fut.cancel()
            raise DeliveryError("Message send failure %s" % (v.status, ))

        if expect_reply:
            # Wait for reply
            try:
                v = await asyncio.wait_for(reply_fut, timeout)
            except:  # noqa: E722
                # If we timeout (or fail for any reason), clear the future
                self._pending.pop(sequence)
                self._zigate_seq.pop(sequence)
                raise
        return v
Ejemplo n.º 11
0
    async def request(self, nwk, profile, cluster, src_ep, dst_ep, sequence,
            data, expect_reply=True, timeout=15):
#        LOGGER.debug("pending message queue length: %s", len(self._pending))
        assert sequence not in self._pending
        send_fut = asyncio.Future()
        reply_fut = None
        if expect_reply:
            reply_fut = asyncio.Future()
        self._pending[sequence] = (send_fut, reply_fut)

        aps_frame = t.EmberApsFrame()
        aps_frame.profileId = t.uint16_t(profile)
        aps_frame.clusterId = t.uint16_t(cluster)
        aps_frame.sourceEndpoint = t.uint8_t(src_ep)
        aps_frame.destinationEndpoint = t.uint8_t(dst_ep)
        aps_frame.options = t.EmberApsOption(
            t.EmberApsOption.APS_OPTION_RETRY |
            t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY
        )
        aps_frame.groupId = t.uint16_t(0)
        aps_frame.sequence = t.uint8_t(sequence)
        LOGGER.debug(
            "sendUnicast to NWKID:0x%04x DST_EP:%s PROFIL_ID:%s CLUSTER:0x%04x TSN:%s, Timeout:%s",
            nwk, dst_ep, profile,  cluster, sequence, timeout,
            )
        try:
            v = await asyncio.wait_for(self._ezsp.sendUnicast(self.direct,
                nwk, aps_frame, sequence, data), timeout)
        except asyncio.TimeoutError:
            LOGGER.debug(
                "sendunicast uart timeout NWKID:0x%04x DST_EP:%s PROFIL_ID:%s CLUSTER:0x%04x TSN:%s, Timeout:%s",
                nwk, dst_ep, profile,  cluster, sequence, timeout,
            )
            self._pending.pop(sequence)
            send_fut.cancel()
            if expect_reply:
                reply_fut.cancel()
            raise DeliveryError("Message send failure uart timeout")
        if v[0] != t.EmberStatus.SUCCESS:
            self._pending.pop(sequence)
            send_fut.cancel()
            if expect_reply:
                reply_fut.cancel()
            LOGGER.debug("sendunicast send failure NWKID:0x%04x DST_EP:%s PROFIL_ID:%s CLUSTER:0x%04x TSN:%s, Timeout:%s",
                    nwk, dst_ep, profile,  cluster, sequence, timeout,
            )
            raise DeliveryError("Message send failure _send_unicast_fail")
        try:
            v = await asyncio.wait_for(send_fut, timeout)
        except DeliveryError as e:
            LOGGER.debug("0x%04x:%s:0x%04x sendunicast send_ACK failure - Error:%s", nwk, dst_ep, cluster, e)
            raise
        except asyncio.TimeoutError:
            LOGGER.debug("sendunicast message send_ACK timeout NWKID:0x%04x DST_EP:%s PROFIL_ID:%s CLUSTER:0x%04x TSN:%s, Timeout:%s",
                    nwk, dst_ep, profile,  cluster, sequence, timeout,
            )
            
            self._pending.pop(sequence)
            if expect_reply:
                reply_fut.cancel()
            raise DeliveryError("ACK_TIMEOUT")
 #       if v != t.EmberStatus.SUCCESS:
 #           self._pending.pop(sequence)
 #           if expect_reply:
 #               reply_fut.cancel()
 #           LOGGER.debug("sendunicast send_ACK failure 0x%04x:%s:0x%04x = %s", nwk, dst_ep, cluster, v)
 #           raise DeliveryError("sendunicast send_ACK failure")
        if expect_reply:
            try:
                v = await asyncio.wait_for(reply_fut, timeout)
            except asyncio.TimeoutError:
                LOGGER.debug(
                        "[0x%04x:%s:0x%04x] sendunicast reply(TSN:%s) timeout failure",
                        nwk, dst_ep, cluster, sequence)
                self._pending.pop(sequence)
                raise DeliveryError("sendunicast reply timeout error")
            return v
Ejemplo n.º 12
0
    async def _send_zdo_request(self, dst_addr, dst_ep, src_ep, cluster,
                                sequence, options, radius, data):
        """
        Zigpy doesn't send ZDO requests via TI's ZDO_* MT commands,
        so it will never receive a reply because ZNP intercepts ZDO replies, never
        sends a DataConfirm, and instead replies with one of its ZDO_* MT responses.

        This method translates the ZDO_* MT response into one zigpy can handle.
        """

        LOGGER.trace(
            "Intercepted a ZDO request: dst_addr=%s, dst_ep=%s, src_ep=%s, "
            "cluster=%s, sequence=%s, options=%s, radius=%s, data=%s",
            dst_addr,
            dst_ep,
            src_ep,
            cluster,
            sequence,
            options,
            radius,
            data,
        )

        assert dst_ep == ZDO_ENDPOINT

        # Deserialize the ZDO request
        zdo_hdr, data = ZDOHeader.deserialize(cluster, data)
        field_names, field_types = ZDO_CLUSTERS[cluster]
        zdo_args, _ = list_deserialize(data, field_types)
        zdo_kwargs = dict(zip(field_names, zdo_args))

        device = self.get_device(nwk=dst_addr.address)

        # Call the converter with the ZDO request's kwargs
        req_factory, rsp_factory, zdo_rsp_factory = ZDO_COMPLEX_CONVERTERS[
            cluster]
        request = req_factory(dst_addr.address, device, **zdo_kwargs)
        callback = rsp_factory(dst_addr.address)

        LOGGER.debug(
            "Intercepted AP ZDO request %s(%s) and replaced with %s",
            cluster,
            zdo_kwargs,
            request,
        )

        try:
            async with async_timeout.timeout(ZDO_REQUEST_TIMEOUT):
                response = await self._znp.request_callback_rsp(
                    request=request,
                    RspStatus=t.Status.SUCCESS,
                    callback=callback)
        except InvalidCommandResponse as e:
            raise DeliveryError(
                f"Could not send command: {e.response.Status}") from e

        zdo_rsp_cluster, zdo_response_kwargs = zdo_rsp_factory(response)

        self._receive_zdo_message(cluster=zdo_rsp_cluster,
                                  tsn=sequence,
                                  sender=device,
                                  **zdo_response_kwargs)

        return response.Status, "Request sent successfully"