Files
adminandClaude Opus 5 79843aa2ae SolarManager unter Versionsverwaltung
Erster Stand der Hintergrundprozesse, die auf der Synology unter
/volume1/homes/wagner/SolarManager laufen: der Manager selbst, die Sammler
je Geraet, die MQTT-Bruecke, der Wecker und - neu hinzugezogen - der
AutoAction-Runner, der als Hintergrundprozess hierher gehoert und nicht ins
Web-Verzeichnis.

Zugangsdaten stehen nicht mehr im Quelltext, sondern in config.ini, die
nicht mit eingecheckt wird. Vorlage ist config.ini.example, gelesen wird sie
von konfig.py. Betroffen waren solarManager.py (Datenbank und Wattpilot),
zeit.py, gatherWaterData.py, wecker.py und skoda_testdaten.py, das sich das
Passwort bisher aus dem Quelltext eines anderen Moduls herausgesucht hat.

Die Kia-Anbindung ist mit dem Fahrzeug entfallen: kiaTest.py,
gatherCarData.py und hyundai_kia_connect_api sind nicht mehr dabei, ebenso
gatherInverterData.py, auf das nur noch eine auskommentierte Zeile zeigte.

Die mitgelieferten Bibliotheken bleiben im Repository - die NAS hat kein
pip, sie muessen neben den Skripten liegen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 20:46:59 +02:00

131 lines
3.3 KiB
Python

import contextlib
import os
import socket
import socketserver
import threading
import pytest
from tests import paho_test
class FakeBroker:
def __init__(self, transport):
if transport == "tcp":
# Bind to "localhost" for maximum performance, as described in:
# http://docs.python.org/howto/sockets.html#ipc
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("localhost", 0))
self.port = sock.getsockname()[1]
elif transport == "unix":
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind("localhost")
self.port = 1883
else:
raise ValueError(f"unsupported transport {transport}")
sock.settimeout(5)
sock.listen(1)
self._sock = sock
self._conn = None
self.transport = transport
def start(self):
if self._sock is None:
raise ValueError('Socket is not open')
(conn, address) = self._sock.accept()
conn.settimeout(5)
self._conn = conn
def finish(self):
if self._conn is not None:
self._conn.close()
self._conn = None
if self._sock is not None:
self._sock.close()
self._sock = None
if self.transport == 'unix':
try:
os.unlink('localhost')
except OSError:
pass
def receive_packet(self, num_bytes):
if self._conn is None:
raise ValueError('Connection is not open')
packet_in = self._conn.recv(num_bytes)
return packet_in
def send_packet(self, packet_out):
if self._conn is None:
raise ValueError('Connection is not open')
count = self._conn.send(packet_out)
return count
def expect_packet(self, name, packet):
if self._conn is None:
raise ValueError('Connection is not open')
paho_test.expect_packet(self._conn, name, packet)
@pytest.fixture(params=["tcp"] + (["unix"] if hasattr(socket, 'AF_UNIX') else []))
def fake_broker(request):
# print('Setup broker')
broker = FakeBroker(request.param)
yield broker
# print('Teardown broker')
broker.finish()
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class FakeWebsocketBroker(threading.Thread):
def __init__(self):
super().__init__()
self.host = "localhost"
self.port = -1 # Will be set by `serve()`
self._server = None
self._running = True
self.handler_cls = False
@contextlib.contextmanager
def serve(self, tcphandler):
self._server = ThreadedTCPServer((self.host, 0), tcphandler)
try:
self.start()
self.port = self._server.server_address[1]
if not self._running:
raise RuntimeError("Error starting server")
yield
finally:
if self._server:
self._server.shutdown()
self._server.server_close()
def run(self):
self._running = True
self._server.serve_forever()
@pytest.fixture
def fake_websocket_broker():
broker = FakeWebsocketBroker()
yield broker