async def _send_udp_message_with_retry( message: str, transport: asyncio.DatagramTransport, response_future: asyncio.Future, ip: str, port: int, ) -> None: """Send a UDP message multiple times until we reach the maximum or a response is recieved.""" data = message.encode("utf-8") send_wait = FIRST_SEND_INTERVAL total_waited = 0.0 for send in range(MAX_SEND_DATAGRAMS): if transport.is_closing() or response_future.done(): return attempt = send + 1 _LOGGER.debug( "%s: >> %s (%s/%s) backoff=%s", ip, data, attempt, MAX_SEND_DATAGRAMS, total_waited, ) transport.sendto(data, (ip, port)) await asyncio.sleep(send_wait) total_waited += send_wait send_wait = min(send_wait * 2, MAX_BACKOFF)
def connection_made(self, transport: asyncio.DatagramTransport) -> None: """Send SSDP request when connection was made.""" # Prepare SSDP and send broadcast message for ssdp_st in SSDP_ST_LIST: request = ssdp_request(ssdp_st) transport.sendto(request, SSDP_TARGET) _LOGGER.debug("SSDP request sent %s", request)
def send_audio_ack(self, transport: asyncio.DatagramTransport): if not self.m_retrans: return sn_list = [] lost_list = [] for sn, rt in self.m_retrans.items(): if rt.packet is None: send_ack = False # Loop until the packet's next retransmission time is after now. while rt.start < self.m_now: send_ack = True if not rt.next(): # timed-out lost_list.append(rt.sn) send_ack = False break if send_ack: sn_list.append(rt.sn) for sn in lost_list: del self.m_retrans[sn] self.lost_packtes += 1 if self.m_log_enabled: self.logi('*** {}: packet {} is timed-out\n'.format( self.id, rt.sn)) if sn_list: pkt = self.get_audio_parser().build_audio_ack( self.id, sn_list, crc_off=self.m_audio_crc_off) transport.sendto(pkt, self.device_addr) self.outgoing_acks += 1 if self.m_log_enabled: self.logi('*** {}: ACK {}\n'.format(self.id, sn_list))
def __call__(self, transport: DatagramTransport, message, gateway_address): gm = GwmpMessage.from_gateway_message(message) if gm.type == GwmpMessageType.PUSH_DATA: self.logger.info("PUSH_DATA received") transport.sendto(gm.get_ack(), gateway_address) # send PUSH_ACK payload = json.loads(gm.payload) for message in payload.get("rxpk", []): lorawan_message = LorawanMessage.deserialize(message["data"]) if isinstance(lorawan_message, LorawanDataMessage): self.handle_lorawan_message(lorawan_message) else: raise TypeError("LoRaWAN message type unknown") elif gm.type == GwmpMessageType.PULL_DATA: self.logger.info("PULL_DATA received") transport.sendto(gm.get_ack(), gateway_address) # send PULL_ACK self.gateways[ gm.gateway] = gateway_address # save address for PULL_RESP elif gm.type == GwmpMessageType.TX_ACK: self.logger.info("TX_ACK received") else: raise TypeError("GWMP message type unknown")
def send_audio_ack(self, transport: asyncio.DatagramTransport): if not self.m_retrans: return sn_list = [] lost_list = [] for sn, rt in self.m_retrans.items(): if rt.packet is None: send_ack = False while rt.start < self.m_now: send_ack = True if not rt.next(): lost_list.append(rt.sn) # timed-out if self.m_log_enabled: self.logi('*** {}: packet {} is timed-out\n'.format(self.id, rt.sn)) break if send_ack: sn_list.append(rt.sn) for sn in lost_list: del self.m_retrans[sn] if sn_list: pkt = self.get_audio_parser().build_audio_ack(self.id, sn_list, crc_off=self.m_audio_crc_off) transport.sendto(pkt, self.device_addr) if self.m_log_enabled: self.logi('*** {}: ACK {}\n'.format(self.id, sn_list))
async def _async_run_scan( self, transport: asyncio.DatagramTransport, destination: tuple[str, int], timeout: int, found_all_future: "asyncio.Future[bool]", ) -> None: """Send the scans.""" _LOGGER.debug("discover: %s => %s", destination, self.DISCOVER_MESSAGE) transport.sendto(self.DISCOVER_MESSAGE, destination) quit_time = time.monotonic() + timeout remain_time = float(timeout) while True: time_out = min(remain_time, timeout / self.BROADCAST_FREQUENCY) if time_out <= 0: return try: await asyncio.wait_for(asyncio.shield(found_all_future), timeout=time_out) except asyncio.TimeoutError: if time.monotonic() >= quit_time: return # No response, send broadcast again in cast it got lost _LOGGER.debug("discover: %s => %s", destination, self.DISCOVER_MESSAGE) transport.sendto(self.DISCOVER_MESSAGE, destination) else: return # found_all remain_time = quit_time - time.monotonic()
def connection_made(self, transport: DatagramTransport) -> None: # type: ignore if self._closing: transport.close() return assert not self._transport, "Another connection made" self._transport = transport self.create_task(self._scan_loop())
def connection_made(self, transport: asyncio.DatagramTransport) -> None: sock = transport.get_extra_info('socket') sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) for adapter in self.ifaddr.get_adapters(): for ip in adapter.ips: if ip.is_IPv4: net = IPv4Network("{}/{}".format(ip.ip, ip.network_prefix), strict=False) if not net.is_link_local and not net.is_loopback: transport.sendto(b'whoareyou?', (str(net.broadcast_address), 60010))
def test_broadcast(): """test_broadcast""" mock_transport = DatagramTransport() mock_transport.sendto = MagicMock() mock_protocol = protocol.AqaraProtocol() mock_protocol.transport = mock_transport test_data = {"cmd": "read"} mock_protocol.broadcast(test_data) test_data_encoded = json.dumps(test_data).encode('utf-8') mock_transport.sendto.assert_called_with(test_data_encoded, (MCAST_ADDR, MCAST_PORT))
def test_unicast(): """test_unicast""" mock_transport = DatagramTransport() mock_transport.sendto = MagicMock() mock_protocol = protocol.AqaraProtocol() mock_protocol.transport = mock_transport test_data = {"cmd": "read"} test_addr = "10.10.10.10" mock_protocol.unicast(test_addr, test_data) test_data_encoded = json.dumps(test_data).encode('utf-8') mock_transport.sendto.assert_called_with(test_data_encoded, (test_addr, GATEWAY_PORT))
def test_multicast(self): _ttl = None mock_socket = mock.MagicMock(spec=socket.socket) def getsockopt(*_): return _ttl def setsockopt(a, b, ttl: bytes): nonlocal _ttl _ttl, = struct.unpack('b', ttl) mock_socket.getsockopt = getsockopt mock_socket.setsockopt = setsockopt protocol = MulticastProtocol('1.2.3.4', '1.2.3.4') transport = DatagramTransport() transport._extra = {'socket': mock_socket} self.assertEqual(None, protocol.set_ttl(1)) self.assertEqual(0, protocol.get_ttl()) protocol.connection_made(transport) self.assertEqual(None, protocol.set_ttl(1)) self.assertEqual(1, protocol.get_ttl())
def async_send_with_transport( log_debug: bool, transport: asyncio.DatagramTransport, packet: bytes, packet_num: int, out: DNSOutgoing, addr: Optional[str], port: int, v6_flow_scope: Union[Tuple[()], Tuple[int, int]] = (), ) -> None: s = transport.get_extra_info('socket') ipv6_socket = s.family == socket.AF_INET6 if addr is None: real_addr = _MDNS_ADDR6 if ipv6_socket else _MDNS_ADDR else: real_addr = addr if not can_send_to(ipv6_socket, real_addr): return if log_debug: log.debug( 'Sending to (%s, %d) via [socket %s (%s)] (%d bytes #%d) %r as %r...', real_addr, port or _MDNS_PORT, s.fileno(), transport.get_extra_info('sockname'), len(packet), packet_num + 1, out, packet, ) # Get flowinfo and scopeid for the IPV6 socket to create a complete IPv6 # address tuple: https://docs.python.org/3.6/library/socket.html#socket-families if ipv6_socket and not v6_flow_scope: _, _, sock_flowinfo, sock_scopeid = s.getsockname() v6_flow_scope = (sock_flowinfo, sock_scopeid) transport.sendto(packet, (real_addr, port or _MDNS_PORT, *v6_flow_scope))
def connection_made(self, transport: asyncio.DatagramTransport): local_addr, port = transport.get_extra_info('sockname') response = self.get_response(local_addr) transport.sendto(response)
async def on_connect(transport: DatagramTransport) -> None: """Handle connection made.""" packet = build_ssdp_search_packet(SSDP_TARGET, timeout, service_type) transport.sendto(packet, SSDP_TARGET)
async def on_connect(transport: DatagramTransport) -> None: """Handle connection made.""" packet = build_ssdp_search_packet(target_data, timeout, service_type) _LOGGER.debug("Sending M-SEARCH packet") _LOGGER_TRAFFIC_SSDP.debug("Sending M-SEARCH packet: %s", packet) transport.sendto(packet, target)
def connection_made( self, transport: asyncio.DatagramTransport) -> None: # type: ignore transport.sendto(self.message.raw)