"""LRU table of ESP-NOW peer MACs seen on uplink.""" from espnow_wire import BROADCAST_MAC try: from settings import Settings except ImportError: Settings = None # ESP32 counts the broadcast peer toward the ~20 peer limit. _RESERVED_FOR_BROADCAST = 1 class PeerTable: def __init__(self, max_peers=20): limit = max(1, int(max_peers) - _RESERVED_FOR_BROADCAST) self._max = limit self._order = [] self._names = {} def _evict_lru(self, esp): if not self._order: return old = self._order.pop(0) self._names.pop(old, None) if esp is not None: try: esp.del_peer(old) except OSError: pass def touch(self, mac_bytes, name=None, esp=None): """Note a peer from uplink (LRU). Pass ``esp`` so evictions free ESP-NOW slots.""" if not mac_bytes or len(mac_bytes) != 6: return if mac_bytes == BROADCAST_MAC: return if mac_bytes in self._order: self._order.remove(mac_bytes) elif len(self._order) >= self._max: self._evict_lru(esp) self._order.append(mac_bytes) if name: self._names[mac_bytes] = str(name) if esp is not None: try: esp.add_peer(mac_bytes) except OSError: pass def ensure_peer(self, esp, mac_bytes): """Register ``mac_bytes`` on ESP-NOW, evicting LRU peers when the table is full.""" if not mac_bytes or len(mac_bytes) != 6: return False if mac_bytes == BROADCAST_MAC: try: esp.add_peer(mac_bytes) except OSError: pass return True if mac_bytes in self._order: self._order.remove(mac_bytes) self._order.append(mac_bytes) else: while len(self._order) >= self._max: self._evict_lru(esp) self._order.append(mac_bytes) # Uplink touch() only updates LRU; always add_peer before unicast send. try: esp.add_peer(mac_bytes) except OSError as err: print("add_peer failed", err) return False return True def peers(self): return list(self._order) def is_broadcast_mac(self, mac_bytes): return mac_bytes == BROADCAST_MAC def load_max_peers(): if Settings is None: return 20 try: s = Settings() return int(s.get("max_peers", 20)) except Exception: return 20