Files
Smart-Dashboard/homeMesh_automations.sql
T
adminandClaude Opus 5 d3c1cb4682 Aussengelaende wird die vierte Etage AG
Was draussen haengt, stand als Pseudo-Raum "aussen" in automations.php:
"Geraete, die zu keinem Raum der Wohnung gehoeren - Carport, Garage,
Regensensor". Das waren inzwischen sechzehn Geraete in einem einzigen Topf,
der groesste ueberhaupt - in der Geraeteauswahl des Editors standen die fuenf
Carport-Wechselrichter, das Sonnensegel und das Garagentor unsortiert
nebeneinander.

Jetzt ist das Aussengelaende eine Etage wie OG, EG und UG, mit eigenen
Raeumen: Carport, Veranda, Pergola, Terrasse, Garage und Garten. Die Namen
stammen von den Geraeten selbst - die Hoymiles heissen seit jeher "Carport
1-6" und "Veranda OG2-4". Die achtzehn Geraete sind verteilt (die beiden
Ventilsteuerungen hatten noch gar keinen Raum und stehen jetzt im Garten,
zusammen mit dem Regensensor, der ihre Automatiken ausloest); "Ohne Raum"
faellt damit von acht auf sechs, und die sind es zu Recht.

AG ist eine Etage ohne Grundriss. Ihre Raeume haben kein x/y, es gibt ja
kein Bild, auf das eine Kachel zeigen koennte - ein Raum ohne Position war
schon immer vorgesehen. Das Menue und das SVG richten sich deshalb nach der
neuen floorsWithPlan(), die Zuordnung und die Automatiken nach $floors.
Bekommt der erste AG-Raum eine Kachel, erscheint die Etage von selbst auch
im Menue; es gibt keine zweite Liste, die man nachziehen muesste.

Die Etagen standen bisher an sechs Stellen noch einmal im Code. Jetzt gibt
es genau eine Liste, $floors in rooms.php, und alles andere leitet sich ab:

  * automations.php prueft gegen $floors statt gegen eine eigene Whitelist
  * ajax/AutoAction.php baut seine Etagen-Auswahl daraus
  * home.php baut Reiter und Inhaltsbereiche in einer Schleife statt
    dreimal fast dasselbe HTML
  * header.php baut die Menuepunkte daraus
  * switchTab() in homeMQTT.js liest die Reiter aus der Seite, statt OG, EG
    und UG sechsmal aufzuzaehlen
  * refreshAutomations() ebenso - eine feste Liste haette die vierte Etage
    still ausgelassen, die Tabelle waere ohne Fehlermeldung leer geblieben

Dazu die ausgeschriebenen Namen in $floorLabels: das Kuerzel steht in URLs,
SVG-Ids und im MQTT-Baum, "Aussengelaende" nur als Titel am Menuepunkt und
am Reiter.

In der Datenbank ist das Enum um 'AG' erweitert; header.php laedt rooms.php
jetzt selbst, weil es vor home.php eingebunden wird.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 17:55:11 +02:00

135 lines
9.2 KiB
SQL

-- ---------------------------------------------------------------------------
-- Automatiken (AutoActions) fuer die Datenbank `homeMesh`
-- ---------------------------------------------------------------------------
-- Bewusst hier und nicht in `solarLog`: der Editor liest Geraete, Messwerte
-- und Kommandos ohnehin aus homeMesh, und nur innerhalb einer Datenbank
-- koennen Fremdschluessel greifen. Die alten Tabellen autoActions,
-- autoactionsSensors und autoactionsActors in solarLog zeigten auf
-- solarLog.actors bzw. solarLog.sensors, waehrend das Formular homeMesh-IDs
-- lieferte - jedes INSERT lief gegen den Fremdschluessel, deshalb sind die
-- beiden Kindtabellen dort leer geblieben.
--
-- Ein Geraetebegriff: device_discovery.py legt auch reine Sensoren in
-- `actors`/`actor_states` ab (siehe insert_sensor()), die Tabellen
-- `sensors`/`sensor_states` sind unbenutzt. `actor_states` ist damit alles
-- Lesbare, `actor_commands` alles Schaltbare.
--
-- Voraussetzung: in restricted/deviceDiscovery/config.ini muss
-- clear_tables = false stehen. Discovery schreibt mit ON DUPLICATE KEY UPDATE
-- auf den URLs, das Leeren ist unnoetig - ein TRUNCATE wuerde dagegen die
-- Geraete-IDs neu vergeben und alle Regeln auf falsche Geraete zeigen lassen.
-- ---------------------------------------------------------------------------
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET NAMES utf8mb4;
-- ---------------------------------------------------------------------------
-- Die Automatik selbst: der Rahmen, in dem sie ueberhaupt greifen darf.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `automations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT 'Anzeigename, z.B. Kinder zu',
`floor` enum('UG','EG','OG','AG') NOT NULL DEFAULT 'OG' COMMENT 'Reiter in der Uebersicht. Ohne Etage waere die Automatik in keinem davon zu sehen, deshalb gibt es keinen leeren Wert. AG ist das Aussengelaende - eine Etage ohne Grundriss, siehe restricted/rooms.php',
`enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Aktiv oder pausiert',
`window_from` time NOT NULL DEFAULT '00:00:00' COMMENT 'Aktiver Zeitraum, Beginn',
`window_to` time NOT NULL DEFAULT '23:59:00' COMMENT 'Aktiver Zeitraum, Ende. Kleiner als window_from = ueber Mitternacht',
`weekdays` tinyint(3) unsigned NOT NULL DEFAULT 127 COMMENT 'Bitmaske: Bit 0 = Montag ... Bit 6 = Sonntag',
`on_vacation` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auch in den Ferien ausfuehren',
`on_holiday` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auch an Feiertagen ausfuehren',
`force_once` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Am Ende des Zeitraums auf jeden Fall ausfuehren',
`lockout_secs` int(11) NOT NULL DEFAULT 60 COMMENT 'Sperrzeit: so lange nach einer Ausloesung wird nicht erneut geschaltet. Gegen Messwerte, die um die Schwelle pendeln - jedes Ueberschreiten waere sonst eine echte Flanke. Der Editor bietet 0, 60 und 900 an',
`cond_met` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'War die Bedingung beim letzten Durchlauf erfuellt? Nur die steigende Flanke loest aus, sonst wuerde Temperatur groesser 22 im Sekundentakt feuern',
`last_run` datetime DEFAULT NULL COMMENT 'Zuletzt ausgeloest, NULL = noch nie',
`changed` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'Signal an den Runner, das Regelwerk neu zu laden',
PRIMARY KEY (`id`),
KEY `idx_automations_floor` (`floor`),
KEY `idx_automations_changed` (`changed`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
-- ---------------------------------------------------------------------------
-- Ausloeser. Eine Zeile ist ein Vergleich auf genau einen Messwert.
--
-- `state_id` zeigt direkt auf actor_states und traegt damit Geraet, Messwert,
-- Datentyp, Einheit und MQTT-Topic in einem Feld - frueher war das auf
-- sensorID, eine state-URL und ein unklares valID verteilt.
--
-- Verknuepft wird ueber `group_no`: gleiche Nummer = UND, verschiedene
-- Nummern = ODER. Ausgewertet wird also any(all(gruppe)). Im Editor ist eine
-- Gruppe ein gerahmter Block und zwischen den Bloecken steht ein ODER - die
-- Klammerung ist damit gezeichnet und nicht bloss vereinbart. Innerhalb einer
-- Gruppe wie zwischen den Gruppen ist die Reihenfolge fuer das Ergebnis egal,
-- `position` ist reine Lesehilfe.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `automation_conditions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`automation_id` int(11) NOT NULL,
`group_no` int(11) NOT NULL DEFAULT 0 COMMENT 'UND-Block. Gleiche Nummer = UND, verschiedene Nummern = ODER',
`position` int(11) NOT NULL DEFAULT 0 COMMENT 'Reihenfolge innerhalb des Blocks, nur Darstellung',
`state_id` int(11) NOT NULL COMMENT 'Referenz auf actor_states: Geraet und Messwert in einem',
`operator` enum('=','!=','>','<','>=','<=','+','-','>=+','>=-','<+','<-') NOT NULL DEFAULT '=' COMMENT 'Immer ASCII, die Beschriftung macht erst die Anzeige daraus. Bei Zeit und Datum: = um, >= ab, < vor. Beim Sonnenstand traegt der Operator zusaetzlich das Vorzeichen des Versatzes: + genau danach, >=- ab davor, <+ bis danach',
`value` varchar(255) NOT NULL DEFAULT '' COMMENT 'Schwelle als Text, der Datentyp steckt im Messwert',
PRIMARY KEY (`id`),
KEY `idx_cond_automation` (`automation_id`,`group_no`,`position`),
KEY `idx_cond_state` (`state_id`),
CONSTRAINT `fk_cond_automation` FOREIGN KEY (`automation_id`) REFERENCES `automations` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_cond_state` FOREIGN KEY (`state_id`) REFERENCES `actor_states` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
-- ---------------------------------------------------------------------------
-- Aktionen: ein Kommando eines Aktors, das bei Ausloesung geschickt wird.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `automation_actions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`automation_id` int(11) NOT NULL,
`position` int(11) NOT NULL DEFAULT 0 COMMENT 'Ausfuehrungsreihenfolge',
`command_id` int(11) NOT NULL COMMENT 'Referenz auf actor_commands: Geraet und Kommando in einem',
PRIMARY KEY (`id`),
KEY `idx_action_automation` (`automation_id`,`position`),
KEY `idx_action_command` (`command_id`),
CONSTRAINT `fk_action_automation` FOREIGN KEY (`automation_id`) REFERENCES `automations` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_action_command` FOREIGN KEY (`command_id`) REFERENCES `actor_commands` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
-- ---------------------------------------------------------------------------
-- Werte fuer die Parameter eines Kommandos. Eine Zeile je Parameter statt
-- fester Spalten Wert 1 bis Wert 4: wie viele es sind, sagt
-- command_parameters, nicht die Tabellenbreite.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `automation_action_params` (
`action_id` int(11) NOT NULL,
`parameter_id` int(11) NOT NULL COMMENT 'Referenz auf command_parameters',
`value` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`action_id`,`parameter_id`),
KEY `idx_actionparam_parameter` (`parameter_id`),
CONSTRAINT `fk_actionparam_action` FOREIGN KEY (`action_id`) REFERENCES `automation_actions` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_actionparam_parameter` FOREIGN KEY (`parameter_id`) REFERENCES `command_parameters` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
-- ---------------------------------------------------------------------------
-- Lauf-Protokoll. Speist die Spalte "zuletzt ausgeloest" in der Uebersicht
-- und macht Fehlschlaege sichtbar, die sonst still blieben. Der Runner
-- raeumt Eintraege aelter als 30 Tage selbst weg.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `automation_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`automation_id` int(11) NOT NULL,
`ts` datetime NOT NULL DEFAULT current_timestamp(),
`result` enum('fired','forced','error') NOT NULL DEFAULT 'fired',
`detail` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `idx_log_automation` (`automation_id`,`ts`),
CONSTRAINT `fk_log_automation` FOREIGN KEY (`automation_id`) REFERENCES `automations` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
-- ---------------------------------------------------------------------------
-- Ferien und Feiertage. Nur besondere Tage stehen drin, ein fehlendes Datum
-- ist ein gewoehnlicher Tag. Gefuellt von
-- SolarManager/autoActions/fetch_calendar.py, einmal jaehrlich per Cron.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `calendar_days` (
`date` date NOT NULL,
`holiday` varchar(80) DEFAULT NULL COMMENT 'Name des Feiertags, NULL = keiner',
`vacation` varchar(80) DEFAULT NULL COMMENT 'Name der Ferien, NULL = keine',
PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;