Regenbasierte automatische Gartenbewässerung + homeMesh-Anbindung für AutoAction-Regeleditor
- Neues stündliches Python-Script (restricted/gartenbewaesserung/auto_watering.py): holt Niederschlags- und Sonnenuntergangsdaten von Open-Meteo, wertet je Zone (Hochbeete/Tröge/Garten vorn) individuelle Regen-Schwellenwerte und Mindest-Bewässerungsdauern aus und startet bei Bedarf per MQTT den passenden Automatik-Modus der Ventilsteuerung im Zeitraum vor Sonnenuntergang; veröffentlicht den Regen-Status zusätzlich stündlich als retained MQTT-Nachricht je Zone. - Web-UI: neue Bewässerungs-Steuerung/-Anzeige (ajax/watering.php, js/solar/solarMQTT.js, assets/img/realtime.svg) inkl. Live-Status-Icons (aus/geplant/pausiert/aktiv per Farbe und Symbol). - AutoAction-Regeleditor (ajax/actorDetails.php, sensorDetails.php, fillActorDD.php, fillSensorDD.php, tahoma.php, AutoAction.php) liest Actor-/Sensor-Parameter jetzt live aus der homeMesh-Datenbank statt aus stark vereinfachten Altfeldern, inkl. zugehöriger Anpassungen am Device-Discovery-Tooling (restricted/deviceDiscovery/*, neues logic_module.py). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ from modules.tahoma_module import TahomaModule
|
||||
from modules.wled_module import WLEDModule
|
||||
from modules.mqtt_module import MQTTModule
|
||||
from modules.shelly_module import ShellyModule
|
||||
from modules.logic_module import LogicModule #nicht Vergessen neue Module auch in Zeile 546 einzufügen!!
|
||||
|
||||
# Logging
|
||||
logging.basicConfig(
|
||||
@@ -142,6 +143,14 @@ class Config:
|
||||
return 2
|
||||
return self._get_int('wled', 'timeout', 2)
|
||||
|
||||
# LOGIC
|
||||
@property
|
||||
def logic_enable(self) -> bool:
|
||||
if not self.config.has_section('logic'):
|
||||
return False
|
||||
return self._get_bool('logic', 'enable', False)
|
||||
|
||||
|
||||
# MQTT
|
||||
@property
|
||||
def mqtt_enable(self) -> bool:
|
||||
@@ -236,6 +245,33 @@ class Config:
|
||||
class DatabaseManager:
|
||||
"""Zentrale Datenbank-Verwaltung - ALLE DB-Operationen hier"""
|
||||
|
||||
PARAMETER_TYPES = {
|
||||
0: "bool",
|
||||
1: "integer", # Integer value
|
||||
2: "float", # Floating point number
|
||||
3: "string", # Text string
|
||||
4: "undefined",
|
||||
5: "time",
|
||||
6: "array",
|
||||
7: "deltatime",
|
||||
8: "date",
|
||||
9: "datetime",
|
||||
}
|
||||
|
||||
PARAMETER_TYPES_MAPPING = {
|
||||
"bool": 0,
|
||||
"boolean" : 0,
|
||||
"integer" : 1, # Integer value
|
||||
"float" : 2, # Floating point number
|
||||
"number" : 2, # Floating point number
|
||||
"string" : 3, # Text string
|
||||
"time" : 5,
|
||||
"array": 6,
|
||||
"deltatime" : 7,
|
||||
"date" : 8,
|
||||
"datetime" : 9,
|
||||
}
|
||||
|
||||
def __init__(self, host: str, database: str, user: str, password: str, port: int = 3306):
|
||||
self.host = host
|
||||
self.database = database
|
||||
@@ -272,13 +308,13 @@ class DatabaseManager:
|
||||
try:
|
||||
cursor = self.connection.cursor()
|
||||
cursor.execute("SET FOREIGN_KEY_CHECKS=0")
|
||||
|
||||
cursor.execute("DELETE FROM command_parameters")
|
||||
cursor.execute("DELETE FROM actor_commands")
|
||||
cursor.execute("DELETE FROM actor_states")
|
||||
cursor.execute("DELETE FROM actors")
|
||||
cursor.execute("DELETE FROM sensor_states")
|
||||
cursor.execute("DELETE FROM sensors")
|
||||
cursor.execute("TRUNCATE state_types")
|
||||
cursor.execute("TRUNCATE command_parameters")
|
||||
cursor.execute("TRUNCATE actor_commands")
|
||||
cursor.execute("TRUNCATE actor_states")
|
||||
cursor.execute("TRUNCATE actors")
|
||||
cursor.execute("TRUNCATE sensor_states")
|
||||
cursor.execute("TRUNCATE sensors")
|
||||
|
||||
cursor.execute("SET FOREIGN_KEY_CHECKS=1")
|
||||
|
||||
@@ -289,6 +325,8 @@ class DatabaseManager:
|
||||
logger.error(f"✗ Fehler beim Leeren der Tabellen: {e}")
|
||||
self.connection.rollback()
|
||||
|
||||
|
||||
|
||||
def insert_actor(self, device_type: str, name: str, url: str,
|
||||
commands: list, states: list) -> bool:
|
||||
"""
|
||||
@@ -306,22 +344,35 @@ class DatabaseManager:
|
||||
|
||||
# Actor einfügen
|
||||
query = """
|
||||
INSERT INTO actors (type, name, parameters, url)
|
||||
VALUES (%s, %s, NULL, %s)
|
||||
INSERT INTO actors (type, name, url)
|
||||
VALUES (%s, %s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
type = VALUES(type),
|
||||
name = VALUES(name)
|
||||
"""
|
||||
cursor.execute(query, (device_type, name, url))
|
||||
actor_id = cursor.lastrowid
|
||||
query = """
|
||||
SELECT ID FROM actors where url = %s
|
||||
"""
|
||||
cursor.execute(query, (url))
|
||||
actor_id = cursor.fetchone()[0]
|
||||
|
||||
# Commands einfügen
|
||||
for cmd in commands:
|
||||
command_name = cmd.get('command', '')
|
||||
|
||||
command_url = cmd.get('url', '')
|
||||
cmd_query = """
|
||||
INSERT INTO actor_commands (actor_id, command_name)
|
||||
VALUES (%s, %s)
|
||||
INSERT INTO actor_commands (actor_id, command_name, command_url)
|
||||
VALUES (%s, %s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
command_name = VALUES(command_name)
|
||||
"""
|
||||
cursor.execute(cmd_query, (actor_id, command_name))
|
||||
command_id = cursor.lastrowid
|
||||
cursor.execute(cmd_query, (actor_id, command_name, command_url))
|
||||
query = """
|
||||
SELECT ID FROM actor_commands where actor_id = %s and command_url = %s
|
||||
"""
|
||||
cursor.execute(query, (actor_id, command_url))
|
||||
command_id = cursor.fetchone()[0]
|
||||
|
||||
# Parameter einfügen
|
||||
for param in cmd.get('parameters', []):
|
||||
@@ -329,12 +380,18 @@ class DatabaseManager:
|
||||
INSERT INTO command_parameters
|
||||
(command_id, parameter_name, parameter_type, min_value, max_value, possible_values, url)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
parameter_name = VALUES(parameter_name),
|
||||
parameter_type = VALUES(parameter_type),
|
||||
min_value = VALUES(min_value),
|
||||
max_value = VALUES(max_value),
|
||||
possible_values = VALUES(possible_values)
|
||||
"""
|
||||
param_name = param.get('name', '')
|
||||
param_type = param.get('type', '')
|
||||
min_val = param.get('min')
|
||||
max_val = param.get('max')
|
||||
possible_vals = json.dumps(param.get('values')) if 'values' in param else None
|
||||
possible_vals = json.dumps(param.get('values')) if 'values' in param else ""
|
||||
param_url = param.get('url')
|
||||
|
||||
cursor.execute(param_query,
|
||||
@@ -344,16 +401,22 @@ class DatabaseManager:
|
||||
for state in states:
|
||||
state_query = """
|
||||
INSERT INTO actor_states
|
||||
(actor_id, state_name, state_type, current_value, unit, url)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
(actor_id, state_name, state_type, current_value, unit, url, possible_values)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
state_name = VALUES(state_name),
|
||||
state_type = VALUES(state_type),
|
||||
current_value = VALUES(current_value),
|
||||
possible_values = VALUES(possible_values),
|
||||
unit = VALUES(unit)
|
||||
"""
|
||||
state_name = state.get('name', '')
|
||||
state_type = state.get('type', 0)
|
||||
state_type = self.PARAMETER_TYPES_MAPPING.get(state.get('type', 0), 4)
|
||||
current_value = str(state.get('current_value', '')) if 'current_value' in state else None
|
||||
unit = state.get('unit')
|
||||
state_url = state.get('url')
|
||||
|
||||
cursor.execute(state_query, (actor_id, state_name, state_type, current_value, unit, state_url))
|
||||
possible_vals = json.dumps(state.get('values')) if 'values' in state else ""
|
||||
cursor.execute(state_query, (actor_id, state_name, state_type, current_value, unit, state_url, possible_vals))
|
||||
|
||||
self.connection.commit()
|
||||
cursor.close()
|
||||
@@ -363,6 +426,17 @@ class DatabaseManager:
|
||||
logger.error(f"✗ Fehler beim Einfügen des Aktors {name}: {e}")
|
||||
self.connection.rollback()
|
||||
return False
|
||||
|
||||
def fillVariableTypes(self):
|
||||
cursor = self.connection.cursor()
|
||||
query = """
|
||||
INSERT INTO state_types (id, type)
|
||||
VALUES (%s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
type = VALUES(type)
|
||||
"""
|
||||
for id, parameter_type in self.PARAMETER_TYPES.items():
|
||||
cursor.execute(query, (id, parameter_type))
|
||||
|
||||
def insert_sensor(self, device_type: str, name: str, url: str, states: list) -> bool:
|
||||
"""
|
||||
@@ -379,21 +453,34 @@ class DatabaseManager:
|
||||
|
||||
# Sensor einfügen
|
||||
query = """
|
||||
INSERT INTO sensors (type, name, parameters, url)
|
||||
VALUES (%s, %s, NULL, %s)
|
||||
INSERT INTO actors (type, name, url)
|
||||
VALUES (%s, %s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
type = VALUES(type),
|
||||
name = VALUES(name)
|
||||
"""
|
||||
cursor.execute(query, (device_type, name, url))
|
||||
sensor_id = cursor.lastrowid
|
||||
query = """
|
||||
SELECT ID FROM actors where url = %s
|
||||
"""
|
||||
cursor.execute(query, (url))
|
||||
sensor_id = cursor.fetchone()[0]
|
||||
|
||||
# States einfügen
|
||||
for state in states:
|
||||
state_query = """
|
||||
INSERT INTO sensor_states
|
||||
(sensor_id, state_name, state_type, current_value, unit, url)
|
||||
INSERT INTO actor_states
|
||||
(actor_id, state_name, state_type, current_value, unit, url)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
state_name = VALUES(state_name),
|
||||
state_type = VALUES(state_type),
|
||||
current_value = VALUES(current_value),
|
||||
unit = VALUES(unit)
|
||||
"""
|
||||
state_name = state.get('name', '')
|
||||
state_type = state.get('type', 0)
|
||||
state_type = self.PARAMETER_TYPES_MAPPING.get(state.get('type', 0), 4)
|
||||
current_value = str(state.get('current_value', '')) if 'current_value' in state else None
|
||||
unit = state.get('unit')
|
||||
state_url = state.get('url')
|
||||
@@ -460,13 +547,15 @@ def main():
|
||||
|
||||
total_actors = 0
|
||||
total_sensors = 0
|
||||
|
||||
|
||||
db.fillVariableTypes()
|
||||
# Module initialisieren
|
||||
modules = [
|
||||
TahomaModule(config),
|
||||
WLEDModule(config),
|
||||
MQTTModule(config),
|
||||
ShellyModule(config)
|
||||
ShellyModule(config),
|
||||
LogicModule(config)
|
||||
]
|
||||
|
||||
# Jedes Modul durchlaufen
|
||||
|
||||
Reference in New Issue
Block a user