Hallo zusammen,
zur Anschauung, so läuft mein Mischer aktuell seit ca. 3 Jahren. Das Skript kann gerne genutzt werden, allerdings übernehme ich keine Verantwortung für eure Anlagen! Jede Anlage hat andere Herausforderungen, die zu beachten sind.
Ich nutze die Shellys als Ergänzung zu meinem Heizungsregler (grün), der leider nicht für meine Heizungsanlage ausgelegt ist die ich wie unten Umgebaut habe. Er kann nur einen Mischer für Fußbodenheizung regeln, ich habe aber das ganze haus umgerüstet auf Fußbodenheizung.
Ich steuere also insgesamt 3x Shelly 1PM über den Eingang (SW) meines Heizungsreglers an. Da ich aber 3 Pumpengruppen habe, schalte und regle ich damit die 3 Mischer, also 3x Shelly 2PM Gen3, jeder mit Addon und eigenem Temperaturfühler! So habe ich möglichst wenig Netzwerkkommunikation. Im Fehlerfall fahren die Mischer zu. Ich finde, das bietet ausreichende Sicherheit und es kann eigentlich nichts passieren – oder wie seht ihr das?
Ich würde mich über Feedback und weitere Ideen freuen!
Eine automatisierte Mischer Steuerung für eine Heizungsanlage – vollständig realisiert mit einem Shelly 2PM Gen3 und einem alten Shelly 1PM (kein Plus-Modell).
💡 Ziel
Die Anlage sollte:
- Den Heizungsmischer abhängig von der Vorlauftemperatur automatisch steuern,
- die Pumpe per Shelly 1PM überwachen (ohne Steuerung),
- sicherheitskritische Situationen wie Überhitzung oder Pumpenausfall erkennen,
- bei Kommunikationsverlust sicher abschalten und
- nach Wiederverbindung automatisch den Betrieb wieder aufnehmen.
⚙️ Hardware
- Shelly 2PM Gen3: Steuert den Mischermotor über die integrierte Cover-Funktion (öffnen/schließen/GoToPosition).
- Shelly 1PM: Erkennt, ob die Umwälzpumpe läuft (Statusabfrage per HTTP GET auf /status).
📜 Was macht das Script?
- Der Mischer fährt beim Start der Pumpe automatisch auf eine definierte Startposition (z. B. 45 %).
- Danach regelt das Script per Impulsbetrieb den Mischer:
- Ist die Temperatur zu niedrig, wird kurz geöffnet.
- Ist sie zu hoch, wird kurz geschlossen. (alles einstellbar)
- Innerhalb der Hysterese bleibt der Mischer stehen.
- Jede Bewegung startet eine Sperrzeit (z. B. 1 Minute), um unnötige Bewegungen zu vermeiden.
- Bei Pumpenabschaltung fährt der Mischer sofort auf 0 % (geschlossen).
- Wird eine kritische Temperatur überschritten (z. B. 45 °C), erfolgt eine Sicherheits-Kaltfahrt auf eine definierte Position (z. B. 30 %).
- Das Script erkennt, wenn der Pumpen-Shelly nicht mehr erreichbar ist (z. B. WLAN-Ausfall oder Gerät defekt). Nach einem einstellbaren Timeout (z. B. 2 Minuten) wird der Mischer automatisch auf 0 % gefahren – als Sicherheitsmaßnahme.
- Wenn der Pumpen-Shelly wieder online ist, fährt der Mischer automatisch auf die Startposition zurück und nimmt den geregelten Betrieb wieder auf.
🔒 Sicherheitsfunktionen im Überblick
- Pumpenüberwachung (Zustand + Erreichbarkeit)
- Temperaturgrenze mit Kaltfahrposition
- Sperrzeit nach Bewegungen
- Rückstellung nach Pumpenausfall
- Vollständig WLAN-fähig, aber mit Fallback-Verhalten bei Verbindungsproblemen
🧠 Technik-Highlights
- Impulssteuerung mit Cover.Open, Cover.Close, Cover.Stop
- Positionierung über Cover.GoToPosition
- Temperaturabfrage direkt über Shelly.GetStatus
- Pumpenzustand per JSON-Parsing von /status des Shelly 1PM
- Script läuft vollständig lokal auf dem Shelly 2PM Gen3
🛠 Setup-Voraussetzungen
- Shelly 2PM Gen3 im „Cover-Modus“ konfiguriert
- Temperaturfühler korrekt am 2PM eingebunden (temperature:100)
- Shelly 1PM muss im selben Netz per HTTP erreichbar sein
- Script im Webinterface des Shelly 2PM einfügen
Es lässt sich flexibel anpassen, z. B. für:
- Pufferspeicher,
- Rücklaufanhebung,
- Fußbodenheizung mit Mischer,
- Sicherheitsventile in Kombination mit Heizgeräten ohne eigene Modbus/Regelung.
Skript: ->
/*
===============================================
★ Automatische Heizungsmischerregelung mit Shelly Mischerregelung V4.2 ★
===============================================
Dieses Script regelt einen Heizungsmischer (z. B. über einen Shelly 2PM Gen3)
basierend auf einer gemessenen Vorlauftemperatur und dem Zustand einer Umwälzpumpe.
Die Pumpe selbst wird durch einen separaten Shelly (z. B. Shelly 1PM) geschaltet,
dessen Status regelmäßig per HTTP abgefragt wird.
✔ Ziel: Steuern der Heizleistung durch Öffnen/Schließen des Mischers
✔ Sicherheitsmechanismen für Übertemperatur und Pumpenausfall
✔ Steuerung per Impulsbetrieb oder absolute Positionierung
✔ Ausfallsicherheit bei WLAN-/Shelly-Fehlern
=== FUNKTIONSÜBERSICHT ===
1. **Pumpe AUS**:
- Mischer fährt auf 0 % (vollständig geschlossen)
- Keine weitere Regelung, bis Pumpe wieder an
2. **Pumpe EIN**:
- Mischer fährt auf definierte Startposition (`startPosition`, z. B. 45 %)
- Danach Regelung gemäß Temperatur und Hysterese
3. **Temperaturregelung** (alle 15 Sekunden):
- Ist-Temperatur wird ausgelesen (`tempSensorKey`)
- Bei zu kalt → Mischer öffnen (Impuls)
- Bei zu warm → Mischer schließen (Impuls)
- Im Zielbereich → keine Aktion
4. **Überhitzungsschutz**:
- Wenn `safetyTemp` überschritten wird (z. B. 45 °C)
→ Mischer fährt auf `safetyPosition` (z. B. 30 %)
5. **Pumpen-Shelly nicht erreichbar (WLAN weg / Shelly defekt)**:
- Nach Ablauf von `pumpTimeoutMs` (z. B. 2 Minuten)
→ Mischer fährt **einmalig auf 0 % (Failsafe-Stellung)** → keine Heizenergie
- Wenn Shelly wieder erreichbar → Mischer fährt automatisch zurück auf `startPosition`
===============================================
*/
// === EINSTELLUNGEN ===
let pumpIP = "192.168.178.34"; // IP-Adresse des Pumpen-Shelly (1PM)
let targetTemp = 33.0; // Zieltemperatur in °C
let hysteresis = 1.5; // Toleranzbereich um die Zieltemperatur (±1,5 °C)
let impulseTimeMs = 2000; // Dauer, wie lange der Mischer pro Impuls läuft (in ms)
let lockTimeMs = 1 * 60 * 1000; // Sperrzeit nach jeder Mischerbewegung (1 Minute)
let startPosition = 45; // Öffnungsposition (in %) nach Einschalten der Pumpe
let safetyTemp = 45.0; // Sicherheitsgrenze: ab hier Mischer auf safetyPosition fahren
let safetyPosition = 30; // Öffnungsposition (in %) bei Sicherheitsabschaltung (z.B. Kaltfahren bei Überhitzung)
let pumpTimeoutMs = 1 * 60 * 1000; // Zeit (2 Minuten), nach der der Mischer bei Pumpenausfall geschlossen wird
// === VARIABLEN ===
let lastActionTime = 0; // Zeit der letzten Mischerbewegung (Timestamp)
let isMoving = false; // Flag zum Verhindern paralleler Mischerbewegungen
let lastPumpState = false; // Letzter bekannter Pumpenzustand (an/aus)
let lastPumpReachable = true; // Letzter bekannter Erreichbarkeitszustand der Pumpe
let pumpNotReachableSince = 0; // Zeitpunkt, seit wann die Pumpe nicht erreichbar ist
let tempSensorKey = "temperature:100"; // Schlüssel zum Auslesen der Temperatur im Shelly Status
// === Funktion: Pumpenzustand per HTTP GET abfragen ===
// Callback gibt zwei Werte zurück:
// - reachable: bool, ob Pumpen-Shelly erreichbar ist
// - pumpOn: bool, ob die Pumpe eingeschaltet ist (true) oder nicht (false)
function getPumpState(callback) {
Shelly.call("http.get", { url: "http://" + pumpIP + "/status" }, function(res) {
if (res && res.body) {
try {
let data = JSON.parse(res.body);
let isOn = data.relays && data.relays[0] && data.relays[0].ison === true;
callback(true, isOn);
} catch (e) {
print("Fehler beim Parsen der Pumpenantwort:", e);
callback(false, false);
}
} else {
callback(false, false);
}
});
}
// === Funktion: Mischer per Impuls bewegen (öffnen/schließen) ===
function moveMischer(direction) {
if (isMoving) return; // Wenn schon in Bewegung, nicht nochmal starten
isMoving = true;
if (direction === "open") {
Shelly.call("Cover.Open", { id: 0 });
} else if (direction === "close") {
Shelly.call("Cover.Close", { id: 0 });
}
Timer.set(impulseTimeMs, false, function () {
Shelly.call("Cover.Stop", { id: 0 });
lastActionTime = Date.now();
isMoving = false;
print("Bewegung abgeschlossen, Sperrzeit aktiv");
});
}
// === Funktion: Mischer auf eine feste Position fahren (0–100%) ===
function setMischerToPercent(percent) {
if (isMoving) return;
isMoving = true;
percent = Math.max(0, Math.min(100, percent)); // Begrenzung auf 0–100 %
print("Setze Mischer auf", percent, "%");
Shelly.call("Cover.GoToPosition", { id: 0, pos: percent });
lastActionTime = Date.now();
Timer.set(1000, false, function () {
isMoving = false;
});
}
// === Hauptzyklus: alle 15 Sekunden prüfen ===
Timer.set(15000, true, function () {
let now = Date.now();
// Pumpenzustand abfragen (Erreichbarkeit + an/aus)
getPumpState(function(reachable, pumpOn) {
if (!reachable) {
// Pumpe nicht erreichbar
if (lastPumpReachable) {
// Erster Erkennungszeitpunkt
pumpNotReachableSince = now;
print("Pumpen-Shelly nicht erreichbar, starte Timer");
}
lastPumpReachable = false;
// Prüfen, ob Pumpe länger als timeout nicht erreichbar
if (now - pumpNotReachableSince >= pumpTimeoutMs) {
// Mischer auf 0% fahren, aber nur einmalig
if (lastPumpState !== false) {
print("Pumpen-Shelly länger als Timeout nicht erreichbar → Mischer auf 0% fahren");
setMischerToPercent(0);
lastPumpState = false; // Pumpe als aus markieren
} else {
print("Pumpen-Shelly weiterhin nicht erreichbar – Mischer bleibt in Sicherheitsstellung");
}
}
return; // Keine weitere Regelung
}
// Pumpe wieder erreichbar
if (!lastPumpReachable) {
print("Pumpen-Shelly wieder erreichbar");
lastPumpReachable = true;
pumpNotReachableSince = 0;
if (pumpOn) {
// Pumpe an → Mischer auf Startposition fahren
print("Pumpe an nach Erreichbarkeit → Mischer auf Startposition", startPosition, "%");
setMischerToPercent(startPosition);
lastPumpState = true;
} else {
// Pumpe aus → Mischer auf 0%
print("Pumpe aus nach Erreichbarkeit → Mischer auf 0%");
setMischerToPercent(0);
lastPumpState = false;
}
return;
}
// Pumpe erreichbar, Zustand prüfen
if (!pumpOn) {
if (lastPumpState === true) {
// Pumpe gerade ausgeschaltet
print("Pumpe ausgeschaltet → Mischer auf 0%");
setMischerToPercent(0);
} else {
print("Pumpe weiterhin aus – keine Aktion");
}
lastPumpState = false;
return;
}
// Pumpe an
if (lastPumpState === false) {
// Pumpe gerade eingeschaltet
print("Pumpe eingeschaltet → Mischer auf Startposition", startPosition, "%");
setMischerToPercent(startPosition);
lastPumpState = true;
}
// Sperrzeit prüfen
if ((now - lastActionTime) < lockTimeMs) {
print("Sperrzeit aktiv – keine Bewegung");
return;
}
// Temperatur abfragen und regeln
Shelly.call("Shelly.GetStatus", {}, function (res) {
if (res[tempSensorKey] && res[tempSensorKey].tC !== undefined) {
let temp = res[tempSensorKey].tC;
print("Aktuelle Temperatur:", temp);
if (temp > safetyTemp) {
// Temperatur zu hoch → Mischer auf Sicherheitsposition fahren
print("Temperatur über Sicherheitsgrenze:", safetyTemp, "°C → Mischer auf", safetyPosition, "%");
setMischerToPercent(safetyPosition);
return;
}
// Hysterese-Regelung
if (temp < (targetTemp - hysteresis)) {
print("Temperatur zu niedrig → Mischer öffnen");
moveMischer("open");
} else if (temp > (targetTemp + hysteresis)) {
print("Temperatur zu hoch → Mischer schließen");
moveMischer("close");
} else {
print("Temperatur im Zielbereich → keine Aktion");
}
} else {
print("Temperatursensor nicht gefunden");
}
});
});
});
Alles anzeigen