from typing import Optional from zope import interface from zope.interface import Attribute class IEndpoint(interface.Interface): queue = Attribute("""The queue to send the message""") state = Attribute("""The connection status""") def isConnected(self) -> bool: """ Return True if the endpoint is connected. This function and [connect] are supposed to work together : the main loop application will call reconnect after a while when isConnected return False. """ def connect(self): """ Connect or reconnect to the endpoint """ def fetch(self): """ Check the elements from the connection """ def send(self, data: list[dict[str, str]]): """ Send element to the connection """ class IConnection(interface.Interface): """ Define a connected element (serial, network…) """ def connect(self) -> None: """ Connect """ def read(self) -> str: """ Read from the connection and return the bytes. Return None if there is nothing to read (non-blocking) Raise an exception if disconnected """ def write(self, content:str) -> None: """ Write into the connection. Raise an exception if disconnected """ from interfaces import desktopEvent from zope import component import json from interfaces.message import IMessage, Debug @component.adapter(IConnection) @interface.implementer(IEndpoint) class EndPoint(object): STATE_DISCONNECTED = 0 STATE_CONNECTING = 1 STATE_CONNECTED = 2 def __init__(self, connection): self.connection = connection self.queue = None self.state = self.STATE_DISCONNECTED def isConnected(self) -> bool: return self.state != self.STATE_DISCONNECTED def connect(self): try: self.connection.connect() self.state = self.STATE_CONNECTED component.handle(Debug("Connected")) except Exception as e: print(e) self.state = self.STATE_DISCONNECTED def fetch(self): """ Read from the peripherical and put them in the queue if any data are available. """ if self.state != self.STATE_CONNECTED: return try: received = self.connection.read() except Exception as e: print("fetch error", e) self.state = self.STATE_DISCONNECTED return else: if received is None or received == b'': # If we do not have any entry from the macropad, just return return print("recv", received) layout = str(received, "utf-8").strip() desktop = component.queryUtility(desktopEvent.IDesktop) if desktop is not None: title = desktop.getForegroundWindowTitle() else: title = None self.queue.put((layout, title)) def send(self, data: list[dict[str, str]]): """ Send the data to the macropad. The data must be the representation of a json element. """ if self.state != self.STATE_CONNECTED: return try: j = json.JSONEncoder().encode( data ) + "\n" self.connection.write(str.encode(j)) except Exception as e: print("send error", e) self.state = self.STATE_DISCONNECTED