[ControlMiniDash] Thermostat,KlimaControl,....,

Begonnen von schwatter, 25 Oktober 2025, 12:09:21

Vorheriges Thema - Nächstes Thema

schwatter

Du darfst diesen Dateianhang nicht ansehen.


Tag,

ich habe an einem kleinen Dashboard gebastelt, hauptsächlich für meine noch nicht vorhandene Klimaanlage (kommt hoffentlich nächstes Jahr).
Zum Testen habe ich mein HmIP-WTH-2 verwendet. Das ControlMiniDash besteht aus drei Teilen:


  • Dummy
  • notify
  • controlminidash.svg

Was funktioniert:


Einrichten:

1. SVG kopieren und Rechte setzen.
controlminidash.svg nach /fhem/www/images/fhemSVG. Eigentümer fhem und Gruppe dialout.

2. Dummy (Beispiel)
defmod klimaControl dummy
attr klimaControl devStateIcon {\
    use URI::Escape;;\
\
    my %values = map {\
        my $default = \
            ($_ =~ /^info\d+Val$/) ? '' :\
            ($_ =~ /btn/) ? \
                ($_ =~ /Link_s/ ? 'no link' : 'n/a') : \
            0;;\
        $_ => ReadingsVal($name, $_, $default)\
    } qw(\
        info1Val info2Val info3Val info4Val\
        btn1Cmd  btn2Cmd  btn3Cmd  btn4Cmd  btn5Cmd  btn6Cmd\
        btn1Icon btn2Icon btn3Icon btn4Icon btn5Icon btn6Icon\
        btn1Color btn2Color btn3Color btn4Color btn5Color btn6Color\
    );;\
\
    # Hole das SVG Template\
    my $svg = FW_makeImage('controlminidash');;\
\
    # Setze die Werte im SVG\
    foreach my $key (keys %values) {\
        $svg =~ s/(<tspan class="informId_ringSVG:$key">)(.*?)(<\/tspan>)/$1$values{$key}$3/;;\
    }\
\
    # --- Icon-Größe einstellen ---\
    my $icon_size = 35;;\
\
    # --- Icons definieren ---\
    my @icons = (\
        [$values{btn1Icon}, 'btn1Icon', $values{btn1Color} || '#919191', 1],\
        [$values{btn2Icon}, 'btn2Icon', $values{btn2Color} || '#919191', 1],\
        [$values{btn3Icon}, 'btn3Icon', $values{btn3Color} || '#919191', 1],\
        [$values{btn4Icon}, 'btn4Icon', $values{btn4Color} || '#919191', 1],\
        [$values{btn5Icon}, 'btn5Icon', $values{btn5Color} || '#919191', 1],\
        [$values{btn6Icon}, 'btn6Icon', $values{btn6Color} || '#919191', 1],\
    );;\
\
    # --- Icons einfügen ---\
    foreach my $entry (@icons) {\
        my ($icon_raw, $id, $fill, $should_process) = @$entry;;\
        next unless $should_process && $icon_raw;;\
        my $raw = $icon_raw;;\
        my $icon;;\
\
        if ($raw =~ s/:fhem$//) {\
            $icon = FW_makeImage("$raw\@$fill");;\
            next unless defined $icon && $icon ne '';;\
            if ($icon =~ /<svg[^>]*>/) {\
                my $svg_tag = $&;;\
                $svg_tag =~ s/\bwidth\s*=\s*"[^"]*"//i;;\
                $svg_tag =~ s/\bheight\s*=\s*"[^"]*"//i;;\
                my ($x,$y,$w,$h) = (0,0,100,100);;\
                if ($svg_tag =~ /viewBox\s*=\s*"([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)"/i) {\
                    ($x,$y,$w,$h) = ($1,$2,$3,$4);;\
                } else {\
                    $svg_tag =~ s/<svg\b/<svg viewBox="0 0 100 100"/i;;\
                }\
                my $scale = $icon_size / ($w > $h ? $w : $h);;\
                my $offset_x = ($icon_size - $w * $scale) / 2 - $x * $scale + 2;;\
                my $offset_y = ($icon_size - $h * $scale) / 2 - $y * $scale + 2;;\
                $svg_tag =~ s/<svg\b/<svg class="icon"/ unless $svg_tag =~ /class=/;;\
                $svg_tag =~ s/<svg\b/<svg width="$icon_size" height="$icon_size"/i;;\
                $icon =~ s{<svg[^>]*>}{$svg_tag};;\
                $icon =~ s{(<svg[^>]*>)}{$1<g transform="translate($offset_x,$offset_y) scale($scale)">}i;;\
                $icon =~ s{</svg>}{</g></svg>}i;;\
\
                # alle <path> einfärben\
                $icon =~ s/\bfill="[^"]*"/fill="$fill"/gi;;\
                for my $tag (qw(path rect circle ellipse polygon polyline line)) {\
                    $icon =~ s{<$tag(?![^>]*\bfill=)}{<$tag fill="$fill"}gi;;\
                }\
            }\
        } else {\
            # URL-SVG\
            $icon = uri_unescape($raw);;\
            if ($icon =~ /<svg[^>]*>/) {\
                my $svg_tag = $&;;\
                $svg_tag =~ s/\bwidth\s*=\s*"[^"]*"//i;;\
                $svg_tag =~ s/\bheight\s*=\s*"[^"]*"//i;;\
                my ($x,$y,$w,$h) = (0,0,100,100);;\
                if ($svg_tag =~ /viewBox\s*=\s*"([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)"/i) {\
                    ($x,$y,$w,$h) = ($1,$2,$3,$4);;\
                } else {\
                    $svg_tag =~ s/<svg\b/<svg viewBox="0 0 100 100"/i;;\
                }\
                my $scale = $icon_size / ($w > $h ? $w : $h);;\
                my $offset_x = ($icon_size - $w * $scale) / 2 - $x * $scale + 2;;\
                my $offset_y = ($icon_size - $h * $scale) / 2 - $y * $scale + 2;;\
                $svg_tag =~ s/<svg\b/<svg width="$icon_size" height="$icon_size"/i;;\
                $icon =~ s{<svg[^>]*>}{$svg_tag};;\
                $icon =~ s{(<svg[^>]*>)}{$1<g transform="translate($offset_x,$offset_y) scale($scale)">}i;;\
                $icon =~ s{</svg>}{</g></svg>}i;;\
            }\
\
            # einfärben\
            $icon =~ s/fill="[^"]*"/fill="$fill"/g;;\
            $icon =~ s/<path(?![^>]*fill)/<path fill="$fill"/g;;\
            next unless defined $icon;;\
        }\
\
        my $inner = "";;\
        if ($icon =~ m{<svg[^>]*>(.*)</svg>}s) {\
            $inner = $1;;\
        } else {\
            $inner = $icon;;\
        }\
\
        $svg =~ s{<g id="$id"([^>]*)>}{<g id="informId_ringSVG:$id"$1>$inner};;\
    }\
\
    # --- Werte im SVG ersetzen ---\
    foreach my $key (keys %values) {\
        $svg =~ s/(<tspan class="informId_ringSVG:$key">)(.*?)(<\/tspan>)/$1$values{$key}$3/;;\
    }\
\
    # --- Buttons einfügen ---\
    my @cmds = (\
        [ "btn1Cmd", $values{btn1Cmd}, 20, 10, "Button1" ],\
        [ "btn2Cmd", $values{btn2Cmd}, 20, 60, "Button2" ],\
        [ "btn3Cmd", $values{btn3Cmd}, 20, 110, "Button3" ],\
        [ "btn4Cmd", $values{btn4Cmd}, 295, 10, "Button4" ],\
        [ "btn5Cmd", $values{btn5Cmd}, 295, 60, "Button5" ],\
        [ "btn6Cmd", $values{btn6Cmd}, 295, 110, "Button6" ]\
    );;\
\
    foreach my $cmd (@cmds) {\
        my ($id, $command, $cx, $cy, $title) = @$cmd;;\
        next unless $command && $command ne 'n/a';;\
        my $escaped = $command;;\
        $escaped =~ s/"/&quot;;/g;;\
        $escaped =~ s/'/&#39;;/g;;\
        $escaped =~ s/\{/&#123;;/g;;\
        $escaped =~ s/\}/&#125;;/g;;\
        my $rect = qq(<rect x="$cx" y="$cy" width="40" height="40" fill="transparent" onclick="sendFHEMCmd('$escaped', this)"><title>$title</title></rect>);;\
        $svg =~ s{<g id="$id"([^>]*)>}{<g id="$id"$1>$rect};;\
    }\
\
    # --- FHEM Style ---\
    my $extras = 'viewBox="0 0 355 160" style="display:block;; margin:0;; width:355px;; height:160px;; background-color:#111111;;"';;\
    $svg =~ s/<svg([^>]*)\s*(style="[^"]*")?/<svg$1$extras/;;\
    # --- FHEM Style & Device-Name einfügen ---\
    #my $extras = qq(id="control-$name" informId="$name"\
    #    style="width:355px;;height:auto;;height:160px;;background-color:#111111;;");;\
    #$svg =~ s{<svg\b}{<svg $extras}i;;\
\
    return $svg;;\
}
attr klimaControl readingList info1Val info2Val info3Val info4Val btn1Icon btn2Icon btn3Icon btn4Icon btn5Icon btn6Icon btn1Cmd btn2Cmd btn3Cmd btn4Cmd btn5Cmd btn6Cmd btn1Color btn2Color btn3Color btn4Color btn5Color btn6Color
attr klimaControl room Test
attr klimaControl setList info1Val info2Val info3Val info4Val btn1Icon btn2Icon btn3Icon btn4Icon btn5Icon btn6Icon btn1Cmd btn2Cmd btn3Cmd btn4Cmd btn5Cmd btn6Cmd btn1Color btn2Color btn3Color btn4Color btn5Color btn6Color

setstate klimaControl tempText1 56
setstate klimaControl 2025-10-25 08:22:12 Lampe02_Arb_state on
setstate klimaControl 2025-10-25 10:00:51 btn1Cmd set HmIP_WZ_WTH cooling
setstate klimaControl 2025-10-25 11:26:20 btn1Color blue
setstate klimaControl 2025-10-24 18:21:59 btn1Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M20.79%2C13.95L18.46%2C14.57L16.46%2C13.44V10.56L18.46%2C9.43L20.79%2C10.05L21.31%2C8.12L19.54%2C7.65L20%2C5.88L18.07%2C5.36L17.45%2C7.69L15.45%2C8.82L13%2C7.38V5.12L14.71%2C3.41L13.29%2C2L12%2C3.29L10.71%2C2L9.29%2C3.41L11%2C5.12V7.38L8.5%2C8.82L6.5%2C7.69L5.92%2C5.36L4%2C5.88L4.47%2C7.65L2.7%2C8.12L3.22%2C10.05L5.55%2C9.43L7.55%2C10.56V13.45L5.55%2C14.58L3.22%2C13.96L2.7%2C15.89L4.47%2C16.36L4%2C18.12L5.93%2C18.64L6.55%2C16.31L8.55%2C15.18L11%2C16.62V18.88L9.29%2C20.59L10.71%2C22L12%2C20.71L13.29%2C22L14.7%2C20.59L13%2C18.88V16.62L15.5%2C15.17L17.5%2C16.3L18.12%2C18.63L20%2C18.12L19.53%2C16.35L21.3%2C15.88L20.79%2C13.95M9.5%2C10.56L12%2C9.11L14.5%2C10.56V13.44L12%2C14.89L9.5%2C13.44V10.56Z%22%20%2F%3E%3C%2Fsvg%3E
setstate klimaControl 2025-10-19 12:24:22 btn1Icon_i data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M8.5%204.5L5.4%209.5L8.5%2014.7L5.2%2020.5L3.4%2019.6L6.1%2014.7L3%209.5L6.7%203.6L8.5%204.5M14.7%204.4L11.6%209.5L14.7%2014.5L11.4%2020.3L9.6%2019.4L12.3%2014.5L9.2%209.5L12.9%203.5L14.7%204.4M21%204.4L17.9%209.5L21%2014.5L17.7%2020.3L15.9%2019.4L18.6%2014.5L15.5%209.5L19.2%203.5L21%204.4%22%20%2F%3E%3C%2Fsvg%3E
setstate klimaControl 2025-10-24 17:32:22 btn2Cmd { fhem("set HmIP_WZ_WTH desired-temp ".(ReadingsVal("HmIP_WZ_WTH","desired-temp",22)-0.5)) }
setstate klimaControl 2025-10-19 13:44:40 btn2Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M19%2C19V5H5V19H19M19%2C3A2%2C2%200%200%2C1%2021%2C5V19A2%2C2%200%200%2C1%2019%2C21H5A2%2C2%200%200%2C1%203%2C19V5C3%2C3.89%203.9%2C3%205%2C3H19M17%2C11V13H7V11H17Z%22%20%2F%3E%3C%2Fsvg%3E
setstate klimaControl 2025-10-24 19:34:56 btn3Cmd set HmIP_WZ_WTH boost on
setstate klimaControl 2025-10-25 09:08:35 btn3Color
setstate klimaControl 2025-10-21 00:14:30 btn3Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M13.13%2022.19L11.5%2018.36C13.07%2017.78%2014.54%2017%2015.9%2016.09L13.13%2022.19M5.64%2012.5L1.81%2010.87L7.91%208.1C7%209.46%206.22%2010.93%205.64%2012.5M19.22%204C19.5%204%2019.75%204%2019.96%204.05C20.13%205.44%2019.94%208.3%2016.66%2011.58C14.96%2013.29%2012.93%2014.6%2010.65%2015.47L8.5%2013.37C9.42%2011.06%2010.73%209.03%2012.42%207.34C15.18%204.58%2017.64%204%2019.22%204M19.22%202C17.24%202%2014.24%202.69%2011%205.93C8.81%208.12%207.5%2010.53%206.65%2012.64C6.37%2013.39%206.56%2014.21%207.11%2014.77L9.24%2016.89C9.62%2017.27%2010.13%2017.5%2010.66%2017.5C10.89%2017.5%2011.13%2017.44%2011.36%2017.35C13.5%2016.53%2015.88%2015.19%2018.07%2013C23.73%207.34%2021.61%202.39%2021.61%202.39S20.7%202%2019.22%202M14.54%209.46C13.76%208.68%2013.76%207.41%2014.54%206.63S16.59%205.85%2017.37%206.63C18.14%207.41%2018.15%208.68%2017.37%209.46C16.59%2010.24%2015.32%2010.24%2014.54%209.46M8.88%2016.53L7.47%2015.12L8.88%2016.53M6.24%2022L9.88%2018.36C9.54%2018.27%209.21%2018.12%208.91%2017.91L4.83%2022H6.24M2%2022H3.41L8.18%2017.24L6.76%2015.83L2%2020.59V22M2%2019.17L6.09%2015.09C5.88%2014.79%205.73%2014.47%205.64%2014.12L2%2017.76V19.17Z%22%20%2F%3E%3C%2Fsvg%3E
setstate klimaControl 2025-10-25 11:26:51 btn4Cmd set HmIP_WZ_WTH cooling
setstate klimaControl 2025-10-25 11:25:21 btn4CmdState off
setstate klimaControl 2025-10-25 11:26:31 btn4Color red
setstate klimaControl 2025-10-25 11:07:30 btn4Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M8.5%204.5L5.4%209.5L8.5%2014.7L5.2%2020.5L3.4%2019.6L6.1%2014.7L3%209.5L6.7%203.6L8.5%204.5M14.7%204.4L11.6%209.5L14.7%2014.5L11.4%2020.3L9.6%2019.4L12.3%2014.5L9.2%209.5L12.9%203.5L14.7%204.4M21%204.4L17.9%209.5L21%2014.5L17.7%2020.3L15.9%2019.4L18.6%2014.5L15.5%209.5L19.2%203.5L21%204.4%22%20%2F%3E%3C%2Fsvg%3E
setstate klimaControl 2025-10-25 10:06:06 btn5Cmd { fhem("set HmIP_WZ_WTH desired-temp ".(ReadingsVal("HmIP_WZ_WTH","desired-temp",22)+0.5)) }
setstate klimaControl 2025-10-19 13:44:14 btn5Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M19%2C19V5H5V19H19M19%2C3A2%2C2%200%200%2C1%2021%2C5V19A2%2C2%200%200%2C1%2019%2C21H5A2%2C2%200%200%2C1%203%2C19V5C3%2C3.89%203.9%2C3%205%2C3H19M11%2C7H13V11H17V13H13V17H11V13H7V11H11V7Z%22%20%2F%3E%3C%2Fsvg%3E
setstate klimaControl 2025-10-24 19:53:57 btn6Cmd set Lampe02_Arb on:set Lampe02_Arb off
setstate klimaControl 2025-10-25 11:29:44 btn6CmdState on
setstate klimaControl 2025-10-25 11:04:53 btn6Color
setstate klimaControl 2025-10-22 09:23:04 btn6Icon data:image/svg+xml;;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2C3A9%2C9%200%200%2C0%203%2C12A9%2C9%200%200%2C0%2012%2C21A9%2C9%200%200%2C0%2021%2C12A9%2C9%200%200%2C0%2012%2C3M12%2C19A7%2C7%200%200%2C1%205%2C12A7%2C7%200%200%2C1%2012%2C5A7%2C7%200%200%2C1%2019%2C12A7%2C7%200%200%2C1%2012%2C19M13%2C17H11V7H13V17Z%22%20%2F%3E%3C%2Fsvg%3E
setstate klimaControl 2025-10-25 12:02:51 info1Val 21.7
setstate klimaControl 2025-10-25 12:02:51 info2Val 51
setstate klimaControl 2025-10-25 12:02:51 info3Val 21.0
setstate klimaControl 2025-10-25 12:02:51 info4Val HEATING
setstate klimaControl 2025-10-25 08:27:18 unknownBtnState off

3. notif-Beispiel

defmod HmIP_WZ_WTH_notify_2 notify HmIP_WZ_WTH:(measured-temp|humidity|desired-temp|HEATING_COOLING):.*  {\
  my %map = ("measured-temp:"=>"info1Val","humidity:"=>"info2Val","desired-temp:"=>"info3Val","HEATING_COOLING:"=>"info4Val");;\
  my $target = $map{$EVTPART0};;\
  if ($target) { fhem("set klimaControl $target $EVTPART1");; Log 3, "HmIP_WZ_WTH -> $EVTPART0 $EVTPART1";; }\
}

setstate HmIP_WZ_WTH_notify_2 2025-10-25 12:02:51
setstate HmIP_WZ_WTH_notify_2 2025-10-25 11:28:02 state active
setstate HmIP_WZ_WTH_notify_2 2025-10-25 12:02:51 triggeredByDev HmIP_WZ_WTH
setstate HmIP_WZ_WTH_notify_2 2025-10-25 12:02:51 triggeredByEvent humidity: 51


Gruß schwatter

Damian

Da hast du viel Code in einen Dummy gesteckt. Kann man machen. Besser wäre, wenn man schon so viel Aufwand investiert, ein FHEM-Widget mit dieser Funktionalität zu erstellen, welches man in anderen Modulen nutzen könnte, die FHEM-Widgets unterstützen (siehe https://wiki.fhem.de/wiki/FHEMWEB/Widgets).
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

schwatter

Moin,
mh, ja – ich bin da so reingerutscht, weil ich ein echter DAU bin :)
Eigentlich ist es nur ein angepasster Powerflow/Energyflow. Ich hatte mir schon mal die DateTimePicker-Widgets in FHEM angeschaut.
Auf den ersten Blick scheint es so, als bräuchte ich einen Wrapper. Außerdem muss ich prüfen, ob alles so funktioniert, wie ich es will,
oder ob ich mit Abstrichen leben kann. Deinem Ring würden ein paar Cmd-Knöpfe übrigens auch gut stehen ;)

Gruß schwatter

Damian

Naja, wenn du es selbst programmiert hast und nicht die KI, dann kannst du kein DAU sein :)

Die Card-Funktion auf Widgets auszuweiten, habe ich auch schon überlegt. Deswegen hatte ich sie schon vorausschauend Card und nicht Plot genannt. Allerdings habe ich mich bisher nicht durchgerungen, da was zu machen. Zumal man dann intensiver in Javascript einsteigen müsste. Ein paar vernünftige FHEM-Widgets würden dem FHEM-System nicht schaden, zumal die bisherigen nur rudimentäre Funktionalität bieten und vor allem keine zeitgemäße Optik aufweisen. Klar, kann man sich schöne Dashboards mit TabletUI basteln, aber dann müsste ich zwei Oberflächen/Systeme pflegen. Dann würde ich vermutlich direkt auf Home Assistent wechseln, da hat man gleich eine schöne Oberfläche dabei.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

schwatter

#4
Mal sehen, ein Widget konnte ich laut Konsole schonmal laden. Nur noch nichts anzeigen. Ich schaue mir mal alle Widgets an, vielleicht wird es ja.

Edit:
Zum bearbeiten der SVG und Korrekturlesen nehme ich aber definitiv KI. Früher habe ich manchmal Wochenlang einen Fehler gesucht.
Da ist mir die Zeit mittlerweile zu schade. Und selbst der DEV von Valetudo outet sich da. Das Beste aus beiden Welten vereinen,
würde ich sagen.
Edit2:
Ich will einfach das Fhemweb etwas schöner wird.


Gruß schwatter