Die steigende Flanke allein schuetzt nicht gegen einen Messwert, der um die Schwelle pendelt: "Temperatur > 22" bei 22,1 / 21,9 / 22,1 Grad ist jedes Mal eine echte Flanke, und ueber MQTT koennen die Werte im Sekundentakt hereinkommen. Gemessen: fuenf Kommandos in einer halben Minute. automations.lockout_secs sagt jetzt, wie lange nach einer Ausloesung nicht wieder geschaltet wird. Der Editor bietet drei Stufen an - ohne, eine Minute, eine Viertelstunde -, weil die passende Wahl am Geraet haengt und nicht an einer Zahl: ein Rollladen soll nicht alle zwanzig Sekunden losfahren, eine Lichtfarbe darf das. Gespeichert werden Sekunden, damit eine vierte Stufe eine Zeile in lockoutChoices() ist und keine Wanderung durch die Datenbank. Vorbelegt ist eine Minute. Eine Flanke in der Sperrzeit wird verworfen, nicht aufgehoben. Ein Rollladen, der eine Viertelstunde spaeter doch noch losfaehrt, weil vor langer Zeit einmal eine Schwelle gestreift wurde, waere unangenehmer als einer, der gar nicht faehrt - und der naechste echte Anlass nach Ablauf der Sperre kommt ohnehin durch. Verworfene Flanken stehen auf DEBUG und nicht in automation_log, sonst waere die Tabelle bei einem zappelnden Sensor voll davon. force_once bleibt unberuehrt: es greift nur, wenn im Fenster gar nichts gelaufen ist - dann ist auch keine Sperre aktiv. Nachgemessen am pendelnden Sensor: mit 60 s Sperre ein Kommando, ohne Sperre fuenf. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
135 lines
9.0 KiB
SQL
135 lines
9.0 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') NOT NULL DEFAULT '' COMMENT 'Reiter in der Uebersicht',
|
|
`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
|
|
-- restricted/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;
|