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

405 lines
10 KiB
Python

"""
:mod:`websockets.exceptions` defines the following exception hierarchy:
* :exc:`WebSocketException`
* :exc:`ConnectionClosed`
* :exc:`ConnectionClosedError`
* :exc:`ConnectionClosedOK`
* :exc:`InvalidHandshake`
* :exc:`SecurityError`
* :exc:`InvalidMessage`
* :exc:`InvalidHeader`
* :exc:`InvalidHeaderFormat`
* :exc:`InvalidHeaderValue`
* :exc:`InvalidOrigin`
* :exc:`InvalidUpgrade`
* :exc:`InvalidStatus`
* :exc:`InvalidStatusCode` (legacy)
* :exc:`NegotiationError`
* :exc:`DuplicateParameter`
* :exc:`InvalidParameterName`
* :exc:`InvalidParameterValue`
* :exc:`AbortHandshake`
* :exc:`RedirectHandshake`
* :exc:`InvalidState`
* :exc:`InvalidURI`
* :exc:`PayloadTooBig`
* :exc:`ProtocolError`
"""
from __future__ import annotations
import http
from typing import Optional
from . import datastructures, frames, http11
__all__ = [
"WebSocketException",
"ConnectionClosed",
"ConnectionClosedError",
"ConnectionClosedOK",
"InvalidHandshake",
"SecurityError",
"InvalidMessage",
"InvalidHeader",
"InvalidHeaderFormat",
"InvalidHeaderValue",
"InvalidOrigin",
"InvalidUpgrade",
"InvalidStatus",
"InvalidStatusCode",
"NegotiationError",
"DuplicateParameter",
"InvalidParameterName",
"InvalidParameterValue",
"AbortHandshake",
"RedirectHandshake",
"InvalidState",
"InvalidURI",
"PayloadTooBig",
"ProtocolError",
"WebSocketProtocolError",
]
class WebSocketException(Exception):
"""
Base class for all exceptions defined by websockets.
"""
class ConnectionClosed(WebSocketException):
"""
Raised when trying to interact with a closed connection.
Attributes:
rcvd (Optional[Close]): if a close frame was received, its code and
reason are available in ``rcvd.code`` and ``rcvd.reason``.
sent (Optional[Close]): if a close frame was sent, its code and reason
are available in ``sent.code`` and ``sent.reason``.
rcvd_then_sent (Optional[bool]): if close frames were received and
sent, this attribute tells in which order this happened, from the
perspective of this side of the connection.
"""
def __init__(
self,
rcvd: Optional[frames.Close],
sent: Optional[frames.Close],
rcvd_then_sent: Optional[bool] = None,
) -> None:
self.rcvd = rcvd
self.sent = sent
self.rcvd_then_sent = rcvd_then_sent
def __str__(self) -> str:
if self.rcvd is None:
if self.sent is None:
assert self.rcvd_then_sent is None
return "no close frame received or sent"
else:
assert self.rcvd_then_sent is None
return f"sent {self.sent}; no close frame received"
else:
if self.sent is None:
assert self.rcvd_then_sent is None
return f"received {self.rcvd}; no close frame sent"
else:
assert self.rcvd_then_sent is not None
if self.rcvd_then_sent:
return f"received {self.rcvd}; then sent {self.sent}"
else:
return f"sent {self.sent}; then received {self.rcvd}"
# code and reason attributes are provided for backwards-compatibility
@property
def code(self) -> int:
if self.rcvd is None:
return frames.CloseCode.ABNORMAL_CLOSURE
return self.rcvd.code
@property
def reason(self) -> str:
if self.rcvd is None:
return ""
return self.rcvd.reason
class ConnectionClosedError(ConnectionClosed):
"""
Like :exc:`ConnectionClosed`, when the connection terminated with an error.
A close frame with a code other than 1000 (OK) or 1001 (going away) was
received or sent, or the closing handshake didn't complete properly.
"""
class ConnectionClosedOK(ConnectionClosed):
"""
Like :exc:`ConnectionClosed`, when the connection terminated properly.
A close code with code 1000 (OK) or 1001 (going away) or without a code was
received and sent.
"""
class InvalidHandshake(WebSocketException):
"""
Raised during the handshake when the WebSocket connection fails.
"""
class SecurityError(InvalidHandshake):
"""
Raised when a handshake request or response breaks a security rule.
Security limits are hard coded.
"""
class InvalidMessage(InvalidHandshake):
"""
Raised when a handshake request or response is malformed.
"""
class InvalidHeader(InvalidHandshake):
"""
Raised when an HTTP header doesn't have a valid format or value.
"""
def __init__(self, name: str, value: Optional[str] = None) -> None:
self.name = name
self.value = value
def __str__(self) -> str:
if self.value is None:
return f"missing {self.name} header"
elif self.value == "":
return f"empty {self.name} header"
else:
return f"invalid {self.name} header: {self.value}"
class InvalidHeaderFormat(InvalidHeader):
"""
Raised when an HTTP header cannot be parsed.
The format of the header doesn't match the grammar for that header.
"""
def __init__(self, name: str, error: str, header: str, pos: int) -> None:
super().__init__(name, f"{error} at {pos} in {header}")
class InvalidHeaderValue(InvalidHeader):
"""
Raised when an HTTP header has a wrong value.
The format of the header is correct but a value isn't acceptable.
"""
class InvalidOrigin(InvalidHeader):
"""
Raised when the Origin header in a request isn't allowed.
"""
def __init__(self, origin: Optional[str]) -> None:
super().__init__("Origin", origin)
class InvalidUpgrade(InvalidHeader):
"""
Raised when the Upgrade or Connection header isn't correct.
"""
class InvalidStatus(InvalidHandshake):
"""
Raised when a handshake response rejects the WebSocket upgrade.
"""
def __init__(self, response: http11.Response) -> None:
self.response = response
def __str__(self) -> str:
return (
"server rejected WebSocket connection: "
f"HTTP {self.response.status_code:d}"
)
class InvalidStatusCode(InvalidHandshake):
"""
Raised when a handshake response status code is invalid.
"""
def __init__(self, status_code: int, headers: datastructures.Headers) -> None:
self.status_code = status_code
self.headers = headers
def __str__(self) -> str:
return f"server rejected WebSocket connection: HTTP {self.status_code}"
class NegotiationError(InvalidHandshake):
"""
Raised when negotiating an extension fails.
"""
class DuplicateParameter(NegotiationError):
"""
Raised when a parameter name is repeated in an extension header.
"""
def __init__(self, name: str) -> None:
self.name = name
def __str__(self) -> str:
return f"duplicate parameter: {self.name}"
class InvalidParameterName(NegotiationError):
"""
Raised when a parameter name in an extension header is invalid.
"""
def __init__(self, name: str) -> None:
self.name = name
def __str__(self) -> str:
return f"invalid parameter name: {self.name}"
class InvalidParameterValue(NegotiationError):
"""
Raised when a parameter value in an extension header is invalid.
"""
def __init__(self, name: str, value: Optional[str]) -> None:
self.name = name
self.value = value
def __str__(self) -> str:
if self.value is None:
return f"missing value for parameter {self.name}"
elif self.value == "":
return f"empty value for parameter {self.name}"
else:
return f"invalid value for parameter {self.name}: {self.value}"
class AbortHandshake(InvalidHandshake):
"""
Raised to abort the handshake on purpose and return an HTTP response.
This exception is an implementation detail.
The public API
is :meth:`~websockets.server.WebSocketServerProtocol.process_request`.
Attributes:
status (~http.HTTPStatus): HTTP status code.
headers (Headers): HTTP response headers.
body (bytes): HTTP response body.
"""
def __init__(
self,
status: http.HTTPStatus,
headers: datastructures.HeadersLike,
body: bytes = b"",
) -> None:
# If a user passes an int instead of a HTTPStatus, fix it automatically.
self.status = http.HTTPStatus(status)
self.headers = datastructures.Headers(headers)
self.body = body
def __str__(self) -> str:
return (
f"HTTP {self.status:d}, "
f"{len(self.headers)} headers, "
f"{len(self.body)} bytes"
)
class RedirectHandshake(InvalidHandshake):
"""
Raised when a handshake gets redirected.
This exception is an implementation detail.
"""
def __init__(self, uri: str) -> None:
self.uri = uri
def __str__(self) -> str:
return f"redirect to {self.uri}"
class InvalidState(WebSocketException, AssertionError):
"""
Raised when an operation is forbidden in the current state.
This exception is an implementation detail.
It should never be raised in normal circumstances.
"""
class InvalidURI(WebSocketException):
"""
Raised when connecting to a URI that isn't a valid WebSocket URI.
"""
def __init__(self, uri: str, msg: str) -> None:
self.uri = uri
self.msg = msg
def __str__(self) -> str:
return f"{self.uri} isn't a valid URI: {self.msg}"
class PayloadTooBig(WebSocketException):
"""
Raised when receiving a frame with a payload exceeding the maximum size.
"""
class ProtocolError(WebSocketException):
"""
Raised when a frame breaks the protocol.
"""
WebSocketProtocolError = ProtocolError # for backwards compatibility