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
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)
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
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
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)
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, )))
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)]
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
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
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
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
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"