| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- # -*- coding: utf-8 -*-
- """
- State Conditions - Zustandsbasierte Bedingungen.
- """
- from typing import Any
- from trixy_core.scheduler.condition.base import Condition
- from trixy_core.scheduler.form_fields import FieldType, FormField, FormValidationError
- class StateCondition(Condition):
- """
- Prüft ob System/Komponente in bestimmtem Zustand ist.
- Beispiel:
- - StateCondition("satellite_kitchen", "connected")
- - StateCondition("system_mode", ["active", "standby"])
- """
- def __init__(
- self,
- state_key: str,
- expected_state: str | list[str],
- name: str | None = None,
- invert: bool = False,
- ):
- """
- Initialisiert die Condition.
- Args:
- state_key: Schlüssel für Zustand im Kontext
- expected_state: Erwarteter Zustand oder Liste
- name: Condition-Name
- invert: Ergebnis invertieren
- """
- super().__init__(name, invert)
- self._state_key = state_key
- if isinstance(expected_state, str):
- self._expected_states = [expected_state]
- else:
- self._expected_states = list(expected_state)
- @property
- def state_key(self) -> str:
- """Zustands-Schlüssel."""
- return self._state_key
- @property
- def expected_states(self) -> list[str]:
- """Erwartete Zustände."""
- return list(self._expected_states)
- def _evaluate(self, context: dict) -> bool:
- """Prüft ob Zustand erwartet ist."""
- current_state = context.get(self._state_key)
- if current_state is None:
- return False
- # String-Vergleich (case-insensitive)
- current_str = str(current_state).lower()
- return any(
- s.lower() == current_str
- for s in self._expected_states
- )
- def to_dict(self) -> dict:
- """Konvertiert zu Dictionary."""
- return {
- **self._base_dict(),
- "state_key": self._state_key,
- "expected_states": self._expected_states,
- }
- @classmethod
- def from_dict(cls, data: dict) -> "StateCondition":
- """Erstellt Condition aus Dictionary."""
- return cls(
- state_key=data["state_key"],
- expected_state=data["expected_states"],
- name=data.get("name"),
- invert=data.get("invert", False),
- )
- @classmethod
- def display_name(cls) -> str:
- """Anzeigename fuer die TUI."""
- return "Status-Pruefung"
- @classmethod
- def form_fields(cls) -> list[FormField]:
- """Gibt die Formular-Felder fuer die TUI zurueck."""
- return [
- FormField(
- name="state_key", label="Status-Schluessel", required=True,
- placeholder="satellite_kitchen",
- help_text="Schluessel fuer den Zustand im Kontext",
- ),
- FormField(
- name="expected_value", label="Erwarteter Wert",
- placeholder="connected",
- help_text="Erwarteter Zustandswert",
- ),
- FormField(
- name="comparison", label="Vergleich",
- field_type=FieldType.SELECT,
- default="eq",
- choices=[
- ("eq", "Gleich"),
- ("ne", "Ungleich"),
- ("gt", "Groesser"),
- ("lt", "Kleiner"),
- ("gte", "Groesser/Gleich"),
- ("lte", "Kleiner/Gleich"),
- ],
- ),
- FormField(
- name="invert", label="Invertieren",
- field_type=FieldType.CHECKBOX, default=False,
- ),
- ]
- @classmethod
- def validate_form(cls, data: dict) -> list[FormValidationError]:
- """Validiert Formular-Daten. Gibt Liste von Fehlern zurueck."""
- errors: list[FormValidationError] = []
- if not data.get("state_key", "").strip():
- errors.append(FormValidationError("state_key", "Status-Schluessel ist erforderlich"))
- return errors
- class BooleanCondition(Condition):
- """
- Prüft ob Boolean-Flag gesetzt ist.
- Beispiel:
- - BooleanCondition("is_home") - Nur wenn zu Hause
- - BooleanCondition("debug_mode", expected=False) - Nur wenn kein Debug
- """
- def __init__(
- self,
- flag_key: str,
- expected: bool = True,
- name: str | None = None,
- invert: bool = False,
- ):
- """
- Initialisiert die Condition.
- Args:
- flag_key: Schlüssel für Flag im Kontext
- expected: Erwarteter Wert (default: True)
- name: Condition-Name
- invert: Ergebnis invertieren
- """
- super().__init__(name, invert)
- self._flag_key = flag_key
- self._expected = expected
- @property
- def flag_key(self) -> str:
- """Flag-Schlüssel."""
- return self._flag_key
- @property
- def expected(self) -> bool:
- """Erwarteter Wert."""
- return self._expected
- def _evaluate(self, context: dict) -> bool:
- """Prüft ob Flag erwartet ist."""
- value = context.get(self._flag_key)
- # None behandeln
- if value is None:
- return not self._expected
- # Boolean konvertieren
- if isinstance(value, bool):
- return value == self._expected
- elif isinstance(value, str):
- truthy = value.lower() in ("true", "1", "yes", "on")
- return truthy == self._expected
- elif isinstance(value, (int, float)):
- return bool(value) == self._expected
- return False
- def to_dict(self) -> dict:
- """Konvertiert zu Dictionary."""
- return {
- **self._base_dict(),
- "flag_key": self._flag_key,
- "expected": self._expected,
- }
- @classmethod
- def from_dict(cls, data: dict) -> "BooleanCondition":
- """Erstellt Condition aus Dictionary."""
- return cls(
- flag_key=data["flag_key"],
- expected=data.get("expected", True),
- name=data.get("name"),
- invert=data.get("invert", False),
- )
- @classmethod
- def display_name(cls) -> str:
- """Anzeigename fuer die TUI."""
- return "Boolean-Pruefung"
- @classmethod
- def form_fields(cls) -> list[FormField]:
- """Gibt die Formular-Felder fuer die TUI zurueck."""
- return [
- FormField(
- name="state_key", label="Flag-Schluessel", required=True,
- placeholder="is_home",
- help_text="Schluessel fuer das Boolean-Flag im Kontext",
- ),
- FormField(
- name="invert", label="Invertieren",
- field_type=FieldType.CHECKBOX, default=False,
- ),
- ]
- @classmethod
- def validate_form(cls, data: dict) -> list[FormValidationError]:
- """Validiert Formular-Daten. Gibt Liste von Fehlern zurueck."""
- errors: list[FormValidationError] = []
- if not data.get("state_key", "").strip():
- errors.append(FormValidationError("state_key", "Flag-Schluessel ist erforderlich"))
- return errors
- class ExistsCondition(Condition):
- """
- Prüft ob Schlüssel im Kontext existiert.
- Beispiel:
- - ExistsCondition("user_preferences")
- - ExistsCondition("error_message", exists=False)
- """
- def __init__(
- self,
- key: str,
- exists: bool = True,
- allow_none: bool = False,
- name: str | None = None,
- invert: bool = False,
- ):
- """
- Initialisiert die Condition.
- Args:
- key: Schlüssel im Kontext
- exists: Soll existieren (True) oder nicht (False)
- allow_none: None als existent zählen
- name: Condition-Name
- invert: Ergebnis invertieren
- """
- super().__init__(name, invert)
- self._key = key
- self._exists = exists
- self._allow_none = allow_none
- @property
- def key(self) -> str:
- """Schlüssel."""
- return self._key
- def _evaluate(self, context: dict) -> bool:
- """Prüft ob Schlüssel existiert."""
- key_exists = self._key in context
- if key_exists and not self._allow_none:
- key_exists = context[self._key] is not None
- return key_exists == self._exists
- def to_dict(self) -> dict:
- """Konvertiert zu Dictionary."""
- return {
- **self._base_dict(),
- "key": self._key,
- "exists": self._exists,
- "allow_none": self._allow_none,
- }
- @classmethod
- def from_dict(cls, data: dict) -> "ExistsCondition":
- """Erstellt Condition aus Dictionary."""
- return cls(
- key=data["key"],
- exists=data.get("exists", True),
- allow_none=data.get("allow_none", False),
- name=data.get("name"),
- invert=data.get("invert", False),
- )
- @classmethod
- def display_name(cls) -> str:
- """Anzeigename fuer die TUI."""
- return "Existenz-Pruefung"
- @classmethod
- def form_fields(cls) -> list[FormField]:
- """Gibt die Formular-Felder fuer die TUI zurueck."""
- return [
- FormField(
- name="state_key", label="Schluessel", required=True,
- placeholder="user_preferences",
- help_text="Schluessel der im Kontext existieren soll",
- ),
- FormField(
- name="invert", label="Invertieren",
- field_type=FieldType.CHECKBOX, default=False,
- ),
- ]
- @classmethod
- def validate_form(cls, data: dict) -> list[FormValidationError]:
- """Validiert Formular-Daten. Gibt Liste von Fehlern zurueck."""
- errors: list[FormValidationError] = []
- if not data.get("state_key", "").strip():
- errors.append(FormValidationError("state_key", "Schluessel ist erforderlich"))
- return errors
|