Du hast vor allem mal komplettes Chaos in Deinen Rules veranstaltet.
Du musst schon verstehen, was die einzelnen Befehle bewirken, um zum Ziel zu kommen, Raten ist hier keine Option.
Die Frage wäre also zunächst: WAS willst Du eigentlich erreichen?
Zu den ganzen Fehlern in den gezeigten Rules:
Erst mal formal: Einrückungen dienen der Lesbarkeit für den Anwender, dem Parser (der die Rule liest) sind sie egal.
Der Parser nimmt die Datei und sucht zunächst Kommentarblöcke (alles zwischen den beiden Zeichenfolgen /* und */, incl.) und entfernt diese Blöcke.
Anschließend sucht er zeilenweise nach // und entfernt alles dahinter (wieder incl. //).
Damit sind nun keinerlei kommentare mehr vorhanden.
Nun werden alle Zeilenumbrüche durch ein einzelnes Leerzeichen ersetzt, ebenso werden alle Tabulatoren durch ein einzelnes Leerzeichen ersetzt, dann werden alle zusammenhängenden Leerzeichen durch ein einzelnes Leerzeichen ersetzt.
Die erste Rule
Code: Alles auswählen
rule "virtual item VerbrauchTest"
when
Item Verbrauch changed
then
VerbrauchTest.postUpdate (Verbrauch.state.toString)
end
sieht dann so aus:
Code: Alles auswählen
rule "virtual item VerbrauchTest" when Item Verbrauch changed then VerbrauchTest.postUpdate (Verbrauch.state.toString) end
Erst dann fängt der Parser an, die Rule zu analysieren.
Es geht bei den Einrückungen also ausschließlich um Lesbarkeit und Nachvollziehbarkeit, nicht um Hierarchien.
Im Umkehrschluss sollte man also Einrückungen so setzen, dass zusammenhängende Codeblöcke auf gleicher Ebene abgebildet sind. Beispiel erste Rule:
Code: Alles auswählen
rule "virtual item VerbrauchTest"
when
Item Verbrauch changed
then
VerbrauchTest.postUpdate(Verbrauch.state.toString)
end
Die Schlüsselworte rule, when, then und end sind Teil des Rahmens, der die Rule bildet. Hinter rule steht der Name der Rule, nach when folgen alle Trigger, nach then der auszuführende Code,
end kennzeichnet das Ende einer Rule.
Schlüsselworte hinter when:
Item bezeichnet ein itembasiertes Ereignis.
Text (die ausgeklammerte Rule) gibt es an dieser Stelle nicht. Bitte nicht raten oder rumprobieren, die Schlüsselworte sind bestens dokumentiert.
Time bezeichnet ein zeitliches Event. Ist hier völlig fehl am Platze, aber sei's drum...
Du definierst in der Rule "Uhrschalter setzen" einen Itemzustand, abhängig von now.getHour.
.getHour ist eine Funktion, die den Stundenanteil einer Uhrzeit (bzw. eines vollständigen Datums mit Uhrzeit) liefert. Der Stundenanteil ist das, was auf einer Digitalanzeige links vom linken Doppelpunkt steht, das heißt, um 16:00:00 Uhr liefert .getHour 16, um 16:59:59 Uhr liefert getHour ebenfalls 16, um 17:00:00 Uhr liefert .getHour dann 17.
.getHour ist per Definition eine Ganzzahl (oder auf englisch Integer), der Wertebereich ist 0 bis 23, es gibt also keine 24 Uhr, sondern nur 0 Uhr.
Entsprechend wird die Formel
auch niemals wahr, somit wird auch Uhrschalter niemals einen anderen Zustand annehmen als ON. Wenn Du den Zeitraum von 9 Uhr bis 19 Uhr haben willst, wäre der korrekte Ausdruck
, denn um 08:59:59 liefert .getHour immer noch den Wert 8, was nicht größer als 8 ist, um 19 Uhr liefert .getHour dann zum ersten Mal 19, was nicht kleiner als 19 ist.
Besser wären hier zwei Rules:
Code: Alles auswählen
rule "Uhrschalter ON"
when
Time cron "0 0 9 * * ?" // Regel um 9 Uhr ausführen
then
Uhrschalter.postUpdate(ON) // Setze Uhrschalter ON
end
rule "Uhrschalter OFF"
when
Time cron "0 0 19 * * ?" // Regel um 19 Uhr ausführen
then
Uhrschalter.postUpdate(OFF) // Setze Uhrschalter OFF
end
Durch Deine Nichteinhaltung der Einrückungen ist vollkommen unklar, was Du wo zuzuweisen versuchst. Wichtig ist aber: Du musst (!) vor dem Casten eines Wertes (das heißt, einen bestimmten Datentyp erzwingen) prüfen, ob der Wert überhaupt in dem gewünschten Format dargestellt werden kann.
Ein Number Item kann neben beliebigen Zahlenformaten immer auch die Werte NULL und UNDEF liefern. Diese beiden Werte müssen unter allen Umständen vermieden werden, deshalb muss vor dem Zugriff auf den Wert geprüft werden, ob er NULL oder UNDEF ist.
ODER man prüft, ob es sich bei einem Wert um eine Instanz des Typs Number handelt.
Bei jedem "ersten" Zugriff, das heißt, innerhalb jeder Rule müssen alle verwendeten Itemstatus geprüft werden.
newState ist ein Itemstaus, genau wie
previousState.
oldState gibt es übrigens nicht...
Dann hast Du dieses Konstrukt:
Code: Alles auswählen
var zahl = (newState as Number).floatValue
if(Math.abs((zahl as Number).floatValue - (newState as Number).floatValue) >= 20)
Mal abgesehen davon, dass zahl bereits vom Typ Number ist, das Casting hier also komplett sinnlos ist, glaubst Du tatsächlich, dass ein ein Wert, von sich selbst abgezogen eine andere Zahl als 0 ergibt?
Schließlich in der letzten Rule...
Wo kommt denn nun Extra plötzlich her? Und warum schreibst Du Code doppelt? Wenn es darum geht, entweder 0 oder Extra auszugeben, wäre der korrekte Code hierfür dieser:
Code: Alles auswählen
rule "zuschalten"
when
Item Verbrauch changed
then
val mqttActions = getActions("mqtt","mqtt:broker:neuer")
var zahl2 = 0.0
if(Schalter.state != OFF && Extra.state instanceof Number)
zahl2 = (Extra.state as Number).floatValue
mqttActions.publishMQTT("Leistung", zahl2.toString)
end
Die lokale Variable zahl2 wird mit dem Wert 0.0 vordefiniert. 0.0 bewirkt, dass hier Double als Genauigkeit verwendet wird.
Wenn der Schalter nicht OFF ist (das ist die gleiche Bedingung, wie sie für Deinen else-Teil gilt) UND Extra eine gültige Zahl liefert (wir wollen ja keine NullPointer Exception), wird der lokalen Variablen zahl2 der Wert aus Extra zugewisen, gecastet nach Number und als Float Wert interpretiert.
Anschließend wird zahl2 ausgegeben, so oder so.
Allerdings ist es mehr als fragwürdig, was das Ganze bringen soll.
Noch so eine Unsitte: Warum nutzt Du hier überhaupt die Action publishMQTT? Definiere lieber einen String Channel (oder vielleicht sogar einen Number Channel...), in dessen commandTopic Du das passende Topic einträgst (also in diesem Fall "Leistung", was allerdings auch keine sonderlich gute Idee ist. MQTT ist eigentlich hierarchisch aufgebaut, oder sollte es zumindest sein.)
Mit dem passenden Item sähe die Rule dann so aus:
Code: Alles auswählen
rule "zuschalten"
when
Item Verbrauch changed
then
var zahl2 = 0.0
if(Schalter.state != OFF && Extra.state instanceof Number)
zahl2 = (Extra.state as Number).floatValue
Leistung.sendCommand(zahl2)
end
Aber wie gesagt, das Ganze schreit danach, dass hier eine Menge Dinge gemacht werden, für nichts und wieder nichts.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet