▁ ▂ ▃ ▄ ▅ ▆ ▇ █ Einfacher RSSI Bargraph im Status

Begonnen von Torxgewinde, 16 März 2025, 18:21:58

Vorheriges Thema - Nächstes Thema

Torxgewinde

Hallo,
Ich wollte für einen Tasmota-Shelly einen einfachen RSSI-Indicator im Status haben, der sieht dann so ungefähr aus:

  •  ▂ ▃ ▄ für RSSI Werte von -100 bis -90
  • ▁ ▂ ▃ ▄ für RSSI Werte von -89 bis -70
  • ▁ ▂ ▃ ▄ für RSSI Werte von -69 bis -50
  • ▁ ▂ ▃ ▄ für RSSI Werte von -49 bis 0
  • Andere Werte erzeugen ? ? ? als Wert

    Hier der Snippet für ein Stateformat:
    attr Terrassenlicht stateFormat { \
      return 'offline' if (ReadingsVal($name, "availability", "") eq "offline");;\
      \
      my %map;;\
      $map{$_} = '<span style="color:red;;">▁</span><span style="color:#A8A8A8;;">▂&ThinSpace;;▃&ThinSpace;;▄</span>' for -100..-90;;\
      $map{$_} = '<span style="color:orange;;">▁&ThinSpace;;▂</span><span style="color:#A8A8A8;;">&ThinSpace;;▃&ThinSpace;;▄</span>' for -89..-70;;\
      $map{$_} = '<span style="color:yellow;;">▁&ThinSpace;;▂&ThinSpace;;▃</span><span style="color:#A8A8A8;;">&ThinSpace;;▄</span>' for -69..-50;;\
      $map{$_} = '<span style="color:green;;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄</span>' for -49..0;;\
      \
      return '<html>'.\
        ReadingsVal($name, "state", "???").\
        ' &nbsp;;'.\
        ($map{ReadingsNum($name, "Wifi_Signal", 0)} // '???').\
        '</html>';;\
    }

    Sie sieht es dann aus:
    Du darfst diesen Dateianhang nicht ansehen.

Wernieman

Würde es nicht Sinn machen, es in eine eigene Prozedure auszulagern?

Es ist so direkt aus der Config kopiert?
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

JoWiemann

Hallo,

ich würde:
ReadingsVal($name, "availability", "");
durch:
ReadingsVal($name, "availability", "offline");

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

Torxgewinde

#3
Hallo @Wernieman: Sofern man das bei mehreren Devices wiederverwendet kann/sollte man über Auslagern nachdenken. Ich favorisiere die Dinge, die das Device betreffen, beim Device zu lassen. Dann ist alles schön auf einer Seite. Gehen tut beides, da gibt FHEM einem keine starren Regeln vor. Ja, das ist so aus der Config kopiert, aber eben nur der Snippet für attr stateFormat.

Ich arbeite sogar lieber mit userReadings um mir irgendwelche trivialen Notifies oder DOIFs zu sparen (weniger Devices). Hier ein Beispiel einer Tasters, der direkt beim Taster-Device eingetragen ist und den Aktor so ansteuert. Nebenbei mappt der Ausdruck von '0','1' auf 'off','on' und setzt den State basierend auf dem Reading switchState:
attr TasterTerrassenbeleuchtung userReadings state:switchState:.* {
    my $ss = ReadingsVal($name, "switchState", "???");
    my $val = ({ 0 => 'off', 1 => 'on' }->{$ss} // $ss);
    fhem("set Terrassenlicht $val");
    return $val;
}


Hallo @JoWiemann: Danke, guter Hinweis.
attr Terrassenlicht stateFormat { \
  return 'offline' if (ReadingsVal($name, 'availability', 'offline') eq 'offline');;\
  \
  my %map;;\
  $map{$_} = '<span style="color:red;;">▁</span><span style="color:#A8A8A8;;">▂&ThinSpace;;▃&ThinSpace;;▄</span>' for -100..-90;;\
  $map{$_} = '<span style="color:orange;;">▁&ThinSpace;;▂</span><span style="color:#A8A8A8;;">&ThinSpace;;▃&ThinSpace;;▄</span>' for -89..-70;;\
  $map{$_} = '<span style="color:yellow;;">▁&ThinSpace;;▂&ThinSpace;;▃</span><span style="color:#A8A8A8;;">&ThinSpace;;▄</span>' for -69..-50;;\
  $map{$_} = '<span style="color:green;;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄</span>' for -49..0;;\
  \
  return '<html>'.\
    ReadingsVal($name, "state", "???").\
    ' &nbsp;;'.\
    ($map{ReadingsNum($name, "Wifi_Signal", 0)} // '???').\
    '</html>';;\
}


Torxgewinde

#4
Btw: Falls jemand mehr Zwischenstufen möchte: hier wäre ein Balken von ganz klein bis ganz groß:

▁ ▂ ▃ ▄ ▅ ▆ ▇ █

Dies hier wäre eine Variante mit feinerer Abstufung dazu:

attr Terrassenlicht stateFormat { \
  return 'offline' if (ReadingsVal($name, "availability", "offline") eq "offline");;\
  \
  my %map;;\
  $map{$_} = '<span style="color:red;;">▁</span>' for -100..-90;;\
  $map{$_} = '<span style="color:darkorange;;">▁&ThinSpace;;▂</span>' for -89..-80;;\
  $map{$_} = '<span style="color:orange;;">▁&ThinSpace;;▂&ThinSpace;;▃</span>' for -79..-70;;\
  $map{$_} = '<span style="color:yellow;;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄</span>' for -69..-60;;\
  $map{$_} = '<span style="color:lightgreen;;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅</span>' for -59..-50;;\
  $map{$_} = '<span style="color:green;;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅&ThinSpace;;▆</span>' for -49..-40;;\
  $map{$_} = '<span style="color:darkgreen;;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅&ThinSpace;;▆&ThinSpace;;▇</span>' for -39..-30;;\
  $map{$_} = '<span style="color:limegreen;;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅&ThinSpace;;▆&ThinSpace;;▇&ThinSpace;;█</span>' for -29..0;;\
\
  return '<html>'.\
    ReadingsVal($name, "state", "???").\
    ' <span style="position: relative;; display: inline-block;; transform: scaleX(0.4);;">'.\
    '   <span style="color: lightgray;; font-size: 1em;; font-weight: bold;; white-space: nowrap;;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅&ThinSpace;;▆&ThinSpace;;▇&ThinSpace;;█</span>'.\
    '   <span style="position: absolute;; top: 0;; left: 0;; font-size: 1em;; font-weight: bold;; white-space: nowrap;;">'.\
    ($map{ReadingsNum($name, "Wifi_Signal", 0)} // '???').\
    '   </span>'.\
    '</span></html>';;\
}

So sieht es dann zB. auf dem Handy aus:
Du darfst diesen Dateianhang nicht ansehen.

Beta-User

Zitat von: Torxgewinde am 17 März 2025, 06:30:31aber eben nur der Snippet für attr stateFormat.
Wenn du eine grafische Anzeige generieren willst, ist stateFormat/STATE imo nicht die optimale Stelle => devStateIcon... (stateFormat wird immer evaluiert, wenn sich irgendein Reading ändert, devStateIcon wird nur ausgeführt, wenn man eine Anzeige braucht.)

Davon abgesehen würde ich solche "wiederverwertbaren" snipplets (die man ggf. ja auch mit anderem Zeug dann kombiniert) immer in myUtils-code auszulagern, statt ständig alles (im Ergebnis) durch Text-eval zu hauen.

Dass es keine verbindlichen Vorgaben für beides gibt, ist eine Sache, aber effizient macht es das noch lange nicht, wenn man die hier vorgeschlagene Variante wählt..

Zitat von: Torxgewinde am 17 März 2025, 06:30:31arbeite sogar lieber mit userReadings um mir irgendwelche trivialen Notifies oder DOIFs zu sparen (weniger Devices)
Irgendwie habe ich kein besonders gutes Gefühl dabei, innerhalb der Event-Verarbeitung plötzlich ganz andere Devices zu adressieren, und "weniger Devices" sehe ich auch nicht unbedingt als Wert an sich an.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

Von der Effizienz her ist es etwas suboptimal, denn bei jedem Event, wird ein Hash mit 100 Elementen aufgebaut, obwohl man immer nur eins benötigt.
 
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Torxgewinde

Auch gute Anregungen, ändert jetzt nichts großartig am Output, ist aber im Detail ein wenig effizienter:

attr Terrassenlicht stateFormat { \
  return 'offline' if (ReadingsVal($name, "availability", "offline") eq "offline");;\
  \
  no warnings 'redefine';;\
  ######\
  #  returns html for a simple bargraph\
  ## parameter $1 is the signal strength RSSI (-100 to 0)\
  ## parameter $2 is an optional color for the bar\
  sub SignalToBar($;;$) {\
    my ($v, $c) = @_;;\
    \
    return '<span style="color:'.($c // 'red')       .';;">▁</span>' if $v >= -100 && $v <= -90;;\
    return '<span style="color:'.($c // 'darkorange').';;">▁&ThinSpace;;▂</span>' if $v >= -89 && $v <= -80;;\
    return '<span style="color:'.($c // 'orange')    .';;">▁&ThinSpace;;▂&ThinSpace;;▃</span>' if $v >= -79 && $v <= -70;;\
    return '<span style="color:'.($c // 'yellow')    .';;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄</span>' if $v >= -69 && $v <= -60;;\
    return '<span style="color:'.($c // 'lightgreen').';;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅</span>' if $v >= -59 && $v <= -50;;\
    return '<span style="color:'.($c // 'green')     .';;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅&ThinSpace;;▆</span>' if $v >= -49 && $v <= -40;;\
    return '<span style="color:'.($c // 'darkgreen') .';;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅&ThinSpace;;▆&ThinSpace;;▇</span>' if $v >= -39 && $v <= -30;;\
    return '<span style="color:'.($c // 'limegreen') .';;">▁&ThinSpace;;▂&ThinSpace;;▃&ThinSpace;;▄&ThinSpace;;▅&ThinSpace;;▆&ThinSpace;;▇&ThinSpace;;█</span>' if $v >= -29 && $v <= 0;;\
    \
    return '???';;\
  }\
  use warnings 'redefine';;\
  \
  my $signal = ReadingsNum($name, "Wifi_Signal", 0);;\
  my $sigBar = SignalToBar($signal);;\
  my $backGnd = SignalToBar(0, 'lightgray');; #returns a full gray bargraph\
  \
  return '<html>'.\
    ReadingsVal($name, "state", "???").\
    ' <span style="position: relative;; display: inline-block;; transform: scaleX(0.4);; font-size: 1em;; white-space: nowrap;;">'.\
    '   <span style="color: lightgray;;">'.\
    $backGnd.\
    '   </span>'.\
    '   <span style="position: absolute;; top: 0;; left: 0;;">'.\
    $sigBar.\
    '   </span>'.\
    '</span></html>';;\
}

Torxgewinde

...ich guck' mir mal an wie FHEM das mit SVGs umgesetzt, so richtig Browserunabhängig ist die Lösung mit den UTF8 Zeichen nicht und ich will nicht die Schriftarten vorgeben.

Damian

Zitat von: Torxgewinde am 20 März 2025, 07:17:25...ich guck' mir mal an wie FHEM das mit SVGs umgesetzt, so richtig Browserunabhängig ist die Lösung mit den UTF8 Zeichen nicht und ich will nicht die Schriftarten vorgeben.

Im DOIF-Modul habe ich einige SVG-Grafiken als Perlfunktion gebaut, die man durch Aufruf zur Darstellung nutzen kann. Die kannst du dir mal anschauen. Da wird ganz primitiv HTML-Code generiert.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Torxgewinde

Danke Damian, deine DOIF uiTable Komponenten finde ich super und die nutze auch bereits sehr gerne.

In diesem Fall wollte ich es minimal halten. Ich habe hier mal eine Idee umgesetzt, die nur mit einem SVG mit ein wenig cleverem CSS-rules auskommt.

Man braucht dafür ein SVG "bargraph.svg", welches mit dem Parameter --rssi gesteuert werden kann. Das SVG kann man in /opt/fhem/www/images/fhemSVG/bargraph.svg kopieren und FHEM dann einmal neustarten:
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" style="--rssi:100">
  <style>
    #bar00 {
      height: 1%;
      y: 99%;
      fill: #ff0000; /* Red for 0% */
    }

    /* For bars from 10% to 100% */
    #bar10 {
      --cmp: max(0, calc(var(--rssi) - 10));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 10%);
      y: calc(100% - var(--cmp2) * 10%);
      fill: #ff0000; /* Red for 10% */
    }

    #bar20 {
      --cmp: max(0, calc(var(--rssi) - 20));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 20%);
      y: calc(100% - var(--cmp2) * 20%);
      fill: #ff5500; /* Orange for 20% */
    }

    #bar30 {
      --cmp: max(0, calc(var(--rssi) - 30));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 30%);
      y: calc(100% - var(--cmp2) * 30%);
      fill: #ff9900; /* Yellow-Orange for 30% */
    }

    #bar40 {
      --cmp: max(0, calc(var(--rssi) - 40));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 40%);
      y: calc(100% - var(--cmp2) * 40%);
      fill: #ffcc00; /* Yellow for 40% */
    }

    #bar50 {
      --cmp: max(0, calc(var(--rssi) - 50));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 50%);
      y: calc(100% - var(--cmp2) * 50%);
      fill: #ffff00; /* Yellow for 50% */
    }

    #bar60 {
      --cmp: max(0, calc(var(--rssi) - 60));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 60%);
      y: calc(100% - var(--cmp2) * 60%);
      fill: #99ff00; /* Light Green for 60% */
    }

    #bar70 {
      --cmp: max(0, calc(var(--rssi) - 70));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 70%);
      y: calc(100% - var(--cmp2) * 70%);
      fill: #66cc00; /* Green for 70% */
    }

    #bar80 {
      --cmp: max(0, calc(var(--rssi) - 80));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 80%);
      y: calc(100% - var(--cmp2) * 80%);
      fill: #339900; /* Darker Green for 80% */
    }

    #bar90 {
      --cmp: max(0, calc(var(--rssi) - 90));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 90%);
      y: calc(100% - var(--cmp2) * 90%);
      fill: #006600; /* Dark Green for 90% */
    }

    #bar100 {
      --cmp: max(0, calc(var(--rssi) - 99));
      --cmp2: min(1, calc(var(--cmp)));
      height: calc(var(--cmp2) * 100%);
      y: calc(100% - var(--cmp2) * 100%);
      fill: #004d00; /* Dark Green for 100% */
    }

  </style>

  <rect id="bar00" x="0" width="7%" />
  <rect id="bar10" x="9%" width="7%" />
  <rect id="bar20" x="18%" width="7%" />
  <rect id="bar30" x="27%" width="7%" />
  <rect id="bar40" x="36%" width="7%" />
  <rect id="bar50" x="45%" width="7%" />
  <rect id="bar60" x="54%" width="7%" />
  <rect id="bar70" x="63%" width="7%" />
  <rect id="bar80" x="72%" width="7%" />
  <rect id="bar90" x="81%" width="7%" />
  <rect id="bar100" x="90%" width="7%" />
</svg>

Hier ein simples Testdevice um es zu demonstrieren:
defmod bargraphSVG dummy
attr bargraphSVG devStateIcon {\
    my $val = 100+ReadingsVal($name, 'wert', '???');;\
    my $bargraph = FW_makeImage('bargraph');;\
    $bargraph =~ s/(<svg[^>]+style="[^"]*)--rssi:\d{1,3}/$1--rssi:$val/;;\
    \
    return $bargraph;;\
}
attr bargraphSVG readingList wert
attr bargraphSVG setList wert
attr bargraphSVG webCmd wert -100:wert -82:wert -55:wert -40:wert -10:wert -3

So sieht es dann aus:
Du darfst diesen Dateianhang nicht ansehen.

Die Funktion FW_makeImage() erlaubt es fill, stroke, data-txt und CSS-Klassen zu setzen. Leider eignet sich davon nichts für CSS-Variablen, deswegen habe ich das mit dem Regex in das SVG geschrieben.

Das relativ ungewühnliche ist, dass man mit den CSS-styles und den Min(),Max() und Calc() Funktionen Elemente ein aus ausblenden kann. Das habe ich bisher noch nicht so gesehen, vielleicht findet der ein oder andere das ja auch auch inspirierend.

Torxgewinde

@Beta-User:
Als DevStateIcon wäre das dann mit dem SVG so ganz OK, danke für den Hinweis:

attr Terrassenlicht devStateIcon {\
  ##on:on off:off offline:light_question\
  return FW_makeImage('light_question') if (ReadingsVal($name, "availability", "offline") eq "offline");;\
  \
  my $val = 100+ReadingsNum($name, 'Wifi_Signal', 0);;\
  my $bargraph = FW_makeImage('progressbar');;\
  $bargraph =~ s/(<svg[^>]+style="[^"]*)--rssi:\d{1,3}/$1--rssi:$val;; border: solid 1px #CCCCCC/;;\
  \
  return '<span>'.ReadingsVal($name, "state", "???")."</span>&nbsp;;".$bargraph;;\
}

So sieht es dann aus, hier habe ich noch einen grauen Rahmen um das SVG ergänzt:
Du darfst diesen Dateianhang nicht ansehen.

Alternativ, wenn man lieber Icons für den Status mag:
attr Terrassenlicht devStateIcon {\
  ##on:on off:off offline:light_question\
  return FW_makeImage('light_question') if (ReadingsVal($name, "availability", "offline") eq "offline");;\
  \
  my $val = 100+ReadingsNum($name, 'Wifi_Signal', 0);;\
  my $bargraph = FW_makeImage('progressbar');;\
  $bargraph =~ s/(<svg[^>]+style="[^"]*)--rssi:\d{1,3}/$1--rssi:$val;; border: solid 1px #CCCCCC/;;\
  \
  my $stateIco = FW_makeImage(ReadingsVal($name, "state", "???"));;\
  \
  return $stateIco."&nbsp;;".$bargraph;;\
}

Sieht dann so aus:
Du darfst diesen Dateianhang nicht ansehen.

Beta-User

Zitat von: Torxgewinde am 20 März 2025, 22:16:24Als DevStateIcon wäre das dann mit dem SVG so ganz OK, danke für den Hinweis:
Immer wieder gerne :) .

Meine persönliches styling wäre farblos, ohne Rand und ohne webCmd (dafür das toggeln über devStateIcon).

Das mit dem SVG finde ich eine coole Lösung, mache ich auch immer mal wieder, irgendwo müßte ich z.B. auch Code für die Visualisierung der Temperaturen im Pufferspeicher gepostet haben.

PS: Falls jemand eine Idee hat, wie man per SVG einen Pfeil bastelt, der sich (entsprechend der Windrichtung) um den Mittelpunkt des SVG dreht (und das ganze über der "normalen" Windrose veranstaltet) - her damit :) . (Der Pfeil aus dem Barometer-SVG ist eigentlich ganz cool, aber bisher ist es mir nicht gelungen, den so zu drehen, dass es vernünftig aussieht...)
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Torxgewinde

@Beta-User: Der Pfeil den du gerne da rausholen würdest scheint der Pfad M1865 zu sein, der hat einen komisches Zentrum um ihn zu rotieren, aber durch rumprobieren könnte man das barometer.svg so interaktiv machen:
<svg class="icon weather_barometric_pressure" data-txt="weather_barometric_pressure" version="1.0" xmlns="http://www.w3.org/2000/svg" width="468pt" height="470pt" viewBox="0 0 468 470" preserveAspectRatio="xMidYMid meet" style="--myRot: 0deg;">
  <metadata> Created by potrace 1.8, written by Peter Selinger 2001-2007 </metadata>
  <g transform="translate(0,470) scale(0.158108,-0.158108)" stroke="none">
    <path d="M702 2958 c-6 -9 -6 -71 2 -188 11 -173 11 -175 35 -178 l24 -4 -8 184 c-7 171 -12 197 -36 198 -4 0 -11 -6 -17 -12z"></path>
    <path d="M887 2963 c-3 -4 -6 -62 -8 -128 -4 -117 -3 -120 18 -123 20 -3 22 2 28 70 10 110 4 182 -17 186 -9 2 -18 -1 -21 -5z"></path>
    <path d="M1057 2906 c-17 -118 -19 -203 -7 -211 21 -13 33 12 46 102 20 133 19 153 -10 153 -20 0 -24 -6 -29 -44z"></path>
    <path d="M1230 2863 c-7 -32 -18 -86 -25 -120 -13 -66 -12 -73 16 -73 13 0 21 19 37 88 38 164 38 162 9 162 -23 0 -27 -6 -37 -57z"></path>
    <path d="M1366 2710 c-46 -161 -51 -207 -24 -198 15 5 121 336 114 355 -3 7 -14 13 -24 13 -15 0 -26 -29 -66 -170z"></path>
    <path d="M1575 2808 c-2 -7 -20 -55 -40 -106 -19 -52 -35 -100 -35 -108 0 -17 36 -19 43 -1 59 141 86 211 81 218 -8 13 -44 11 -49 -3z"></path>
    <path d="M1689 2643 c-48 -103 -54 -133 -27 -133 16 0 118 189 118 219 0 15 -6 21 -20 21 -17 0 -31 -22 -71 -107z"></path>
    <path d="M1837 2571 c-60 -103 -68 -131 -39 -131 25 0 145 199 130 217 -20 25 -34 12 -91 -86z"></path>
    <path d="M1942 2434 c-56 -80 -102 -152 -102 -160 0 -8 8 -14 18 -14 14 1 179 212 215 276 8 14 -7 44 -22 44 -4 0 -53 -66 -109 -146z"></path>
    <path d="M2103 2380 c-40 -48 -73 -96 -73 -105 0 -35 39 -8 114 78 62 72 75 92 65 103 -6 8 -17 14 -23 13 -6 0 -44 -40 -83 -89z"></path>
    <path d="M2226 2271 c-62 -62 -87 -94 -83 -105 4 -9 11 -16 16 -16 15 0 181 160 181 175 0 7 -6 17 -13 23 -10 9 -34 -9 -101 -77z"></path>
    <path d="M2333 2150 c-61 -54 -90 -86 -86 -95 9 -25 26 -17 108 51 113 93 111 90 95 108 -7 9 -15 16 -19 16 -4 0 -48 -36 -98 -80z"></path>
    <path d="M2495 2068 c-228 -168 -254 -190 -249 -203 4 -8 11 -15 16 -15 6 0 75 45 155 101 100 70 143 106 141 117 -5 25 -29 24 -63 0z"></path>
    <path d="M1865 1944 c-161 -147 -345 -232 -409 -190 -31 20 -60 21 -76 2 -19 -24 -7 -59 30 -84 31 -21 43 -23 105 -19 39 3 81 8 95 12 16 5 -84 -102 -264 -283 -273 -274 -291 -290 -315 -281 -37 14 -64 4 -101 -37 -28 -33 -31 -42 -26 -78 l6 -41 -288 -288 -289 -289 -41 21 c-89 45 -189 36 -258 -23 -31 -27 -36 -36 -30 -59 10 -40 46 -45 93 -13 45 31 110 35 153 10 66 -40 84 -134 37 -201 -31 -45 -27 -80 11 -90 35 -9 53 5 85 68 24 45 28 63 25 114 -2 36 -12 77 -25 101 l-21 41 291 290 292 290 42 -4 c39 -5 46 -2 79 31 34 34 36 40 31 79 l-6 43 289 289 c286 286 289 289 274 240 -28 -90 -7 -184 48 -219 13 -8 22 -6 37 8 25 23 26 42 5 85 -13 27 -14 40 -5 78 25 97 120 250 210 338 42 40 53 79 29 103 -23 23 -53 11 -113 -44z" style="transform-origin: 1000px 1000px; transform: rotate(var(--myRot));"></path>
    <path d="M2590 1928 c-155 -98 -169 -108 -164 -123 4 -8 11 -15 17 -15 13 0 205 109 212 120 8 12 -6 40 -19 40 -6 0 -27 -10 -46 -22z"></path>
    <path d="M2605 1745 c-93 -50 -120 -75 -96 -89 5 -3 59 17 121 46 93 43 110 55 108 72 -2 11 -10 22 -18 23 -8 0 -60 -23 -115 -52z"></path>
    <path d="M2668 1593 c-60 -25 -108 -48 -108 -52 0 -3 4 -12 9 -20 7 -11 30 -6 125 29 102 38 117 46 114 64 -2 11 -10 22 -18 23 -8 1 -63 -19 -122 -44z"></path>
    <path d="M2666 1420 c-157 -51 -178 -62 -158 -82 9 -9 52 0 184 39 146 42 173 53 173 69 0 31 -39 26 -199 -26z"></path>
    <path d="M2763 1276 c-99 -23 -117 -33 -103 -56 7 -12 22 -11 107 4 125 24 143 31 143 56 0 26 -24 25 -147 -4z"></path>
    <path d="M2795 1111 c-89 -13 -110 -19 -113 -34 -5 -27 17 -29 139 -15 111 13 114 14 114 38 0 31 -5 31 -140 11z"></path>
    <path d="M2813 943 c-100 -4 -135 -16 -118 -43 10 -15 252 -13 258 3 6 17 -11 47 -26 45 -7 -1 -58 -4 -114 -5z"></path>
    <path d="M2574 759 c-3 -6 -2 -15 4 -21 11 -11 184 -26 305 -27 64 -1 67 0 67 23 0 22 -4 23 -82 29 -131 11 -286 8 -294 -4z"></path>
    <path d="M1680 621 l-35 -6 -3 -302 -2 -303 60 0 60 0 0 159 c0 173 -1 171 59 171 53 0 60 -21 63 -180 l3 -145 60 0 60 0 0 155 c0 174 -9 203 -76 248 -29 20 -44 23 -102 20 l-67 -3 0 98 c0 108 5 103 -80 88z"></path>
    <path d="M2143 563 l-23 -4 0 -275 0 -274 65 0 65 0 0 95 0 95 58 0 c109 0 185 37 221 107 14 28 14 128 0 156 -23 44 -64 78 -113 92 -47 14 -217 19 -273 8z m243 -118 c19 -12 24 -24 24 -60 0 -55 -21 -71 -103 -81 l-57 -7 0 80 c0 44 3 83 8 87 11 12 102 -1 128 -19z"></path>
    <path d="M2650 431 c-22 -6 -25 -12 -21 -47 1 -21 5 -42 7 -45 2 -3 37 -4 77 -1 66 4 77 2 96 -17 39 -39 28 -51 -47 -51 -125 0 -195 -61 -178 -154 19 -99 120 -135 293 -105 l63 11 0 155 c0 183 -8 211 -74 243 -44 21 -160 27 -216 11z m180 -286 l0 -45 -53 0 c-55 0 -77 12 -77 41 0 32 29 49 81 49 l49 0 0 -45z"></path>
  </g>
</svg>

Hier wäre eine HTML Seite zum rumprobieren mit dem barometer.svg, dass dann aufgepeppt ist mit dem Parameter --myRot zum Rotieren:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Bla</title>
</head>
<body>

  <button class="rotate-btn" onclick="rotateSVG(15)">Rotate SVG +</button>
  <button class="rotate-btn" onclick="rotateSVG(-15)">Rotate SVG -</button>
 
  <div id="svg-container">
    <svg class="icon weather_barometric_pressure" data-txt="weather_barometric_pressure" version="1.0" xmlns="http://www.w3.org/2000/svg" width="468pt" height="470pt" viewBox="0 0 468 470" preserveAspectRatio="xMidYMid meet" style="--myRot: 0deg;">
  <metadata> Created by potrace 1.8, written by Peter Selinger 2001-2007 </metadata>
  <g transform="translate(0,470) scale(0.158108,-0.158108)" stroke="none">
    <path d="M702 2958 c-6 -9 -6 -71 2 -188 11 -173 11 -175 35 -178 l24 -4 -8 184 c-7 171 -12 197 -36 198 -4 0 -11 -6 -17 -12z"></path>
    <path d="M887 2963 c-3 -4 -6 -62 -8 -128 -4 -117 -3 -120 18 -123 20 -3 22 2 28 70 10 110 4 182 -17 186 -9 2 -18 -1 -21 -5z"></path>
    <path d="M1057 2906 c-17 -118 -19 -203 -7 -211 21 -13 33 12 46 102 20 133 19 153 -10 153 -20 0 -24 -6 -29 -44z"></path>
    <path d="M1230 2863 c-7 -32 -18 -86 -25 -120 -13 -66 -12 -73 16 -73 13 0 21 19 37 88 38 164 38 162 9 162 -23 0 -27 -6 -37 -57z"></path>
    <path d="M1366 2710 c-46 -161 -51 -207 -24 -198 15 5 121 336 114 355 -3 7 -14 13 -24 13 -15 0 -26 -29 -66 -170z"></path>
    <path d="M1575 2808 c-2 -7 -20 -55 -40 -106 -19 -52 -35 -100 -35 -108 0 -17 36 -19 43 -1 59 141 86 211 81 218 -8 13 -44 11 -49 -3z"></path>
    <path d="M1689 2643 c-48 -103 -54 -133 -27 -133 16 0 118 189 118 219 0 15 -6 21 -20 21 -17 0 -31 -22 -71 -107z"></path>
    <path d="M1837 2571 c-60 -103 -68 -131 -39 -131 25 0 145 199 130 217 -20 25 -34 12 -91 -86z"></path>
    <path d="M1942 2434 c-56 -80 -102 -152 -102 -160 0 -8 8 -14 18 -14 14 1 179 212 215 276 8 14 -7 44 -22 44 -4 0 -53 -66 -109 -146z"></path>
    <path d="M2103 2380 c-40 -48 -73 -96 -73 -105 0 -35 39 -8 114 78 62 72 75 92 65 103 -6 8 -17 14 -23 13 -6 0 -44 -40 -83 -89z"></path>
    <path d="M2226 2271 c-62 -62 -87 -94 -83 -105 4 -9 11 -16 16 -16 15 0 181 160 181 175 0 7 -6 17 -13 23 -10 9 -34 -9 -101 -77z"></path>
    <path d="M2333 2150 c-61 -54 -90 -86 -86 -95 9 -25 26 -17 108 51 113 93 111 90 95 108 -7 9 -15 16 -19 16 -4 0 -48 -36 -98 -80z"></path>
    <path d="M2495 2068 c-228 -168 -254 -190 -249 -203 4 -8 11 -15 16 -15 6 0 75 45 155 101 100 70 143 106 141 117 -5 25 -29 24 -63 0z"></path>
    <path d="M1865 1944 c-161 -147 -345 -232 -409 -190 -31 20 -60 21 -76 2 -19 -24 -7 -59 30 -84 31 -21 43 -23 105 -19 39 3 81 8 95 12 16 5 -84 -102 -264 -283 -273 -274 -291 -290 -315 -281 -37 14 -64 4 -101 -37 -28 -33 -31 -42 -26 -78 l6 -41 -288 -288 -289 -289 -41 21 c-89 45 -189 36 -258 -23 -31 -27 -36 -36 -30 -59 10 -40 46 -45 93 -13 45 31 110 35 153 10 66 -40 84 -134 37 -201 -31 -45 -27 -80 11 -90 35 -9 53 5 85 68 24 45 28 63 25 114 -2 36 -12 77 -25 101 l-21 41 291 290 292 290 42 -4 c39 -5 46 -2 79 31 34 34 36 40 31 79 l-6 43 289 289 c286 286 289 289 274 240 -28 -90 -7 -184 48 -219 13 -8 22 -6 37 8 25 23 26 42 5 85 -13 27 -14 40 -5 78 25 97 120 250 210 338 42 40 53 79 29 103 -23 23 -53 11 -113 -44z" style="transform-origin: 1000px 1000px; transform: rotate(var(--myRot)); transition: transform 1s ease-in-out;"></path>
    <path d="M2590 1928 c-155 -98 -169 -108 -164 -123 4 -8 11 -15 17 -15 13 0 205 109 212 120 8 12 -6 40 -19 40 -6 0 -27 -10 -46 -22z"></path>
    <path d="M2605 1745 c-93 -50 -120 -75 -96 -89 5 -3 59 17 121 46 93 43 110 55 108 72 -2 11 -10 22 -18 23 -8 0 -60 -23 -115 -52z"></path>
    <path d="M2668 1593 c-60 -25 -108 -48 -108 -52 0 -3 4 -12 9 -20 7 -11 30 -6 125 29 102 38 117 46 114 64 -2 11 -10 22 -18 23 -8 1 -63 -19 -122 -44z"></path>
    <path d="M2666 1420 c-157 -51 -178 -62 -158 -82 9 -9 52 0 184 39 146 42 173 53 173 69 0 31 -39 26 -199 -26z"></path>
    <path d="M2763 1276 c-99 -23 -117 -33 -103 -56 7 -12 22 -11 107 4 125 24 143 31 143 56 0 26 -24 25 -147 -4z"></path>
    <path d="M2795 1111 c-89 -13 -110 -19 -113 -34 -5 -27 17 -29 139 -15 111 13 114 14 114 38 0 31 -5 31 -140 11z"></path>
    <path d="M2813 943 c-100 -4 -135 -16 -118 -43 10 -15 252 -13 258 3 6 17 -11 47 -26 45 -7 -1 -58 -4 -114 -5z"></path>
    <path d="M2574 759 c-3 -6 -2 -15 4 -21 11 -11 184 -26 305 -27 64 -1 67 0 67 23 0 22 -4 23 -82 29 -131 11 -286 8 -294 -4z"></path>
    <path d="M1680 621 l-35 -6 -3 -302 -2 -303 60 0 60 0 0 159 c0 173 -1 171 59 171 53 0 60 -21 63 -180 l3 -145 60 0 60 0 0 155 c0 174 -9 203 -76 248 -29 20 -44 23 -102 20 l-67 -3 0 98 c0 108 5 103 -80 88z"></path>
    <path d="M2143 563 l-23 -4 0 -275 0 -274 65 0 65 0 0 95 0 95 58 0 c109 0 185 37 221 107 14 28 14 128 0 156 -23 44 -64 78 -113 92 -47 14 -217 19 -273 8z m243 -118 c19 -12 24 -24 24 -60 0 -55 -21 -71 -103 -81 l-57 -7 0 80 c0 44 3 83 8 87 11 12 102 -1 128 -19z"></path>
    <path d="M2650 431 c-22 -6 -25 -12 -21 -47 1 -21 5 -42 7 -45 2 -3 37 -4 77 -1 66 4 77 2 96 -17 39 -39 28 -51 -47 -51 -125 0 -195 -61 -178 -154 19 -99 120 -135 293 -105 l63 11 0 155 c0 183 -8 211 -74 243 -44 21 -160 27 -216 11z m180 -286 l0 -45 -53 0 c-55 0 -77 12 -77 41 0 32 29 49 81 49 l49 0 0 -45z"></path>
  </g>
</svg>
  </div>

  <script>
    function rotateSVG(bla) {
      const svgElement = document.querySelector('.icon');
      let currentRotation = parseInt(getComputedStyle(svgElement).getPropertyValue('--myRot') || 0);
      const newRotation = currentRotation + bla;
      svgElement.style.setProperty('--myRot', `${newRotation}deg`);
    }
  </script>

</body>
</html>

Für mehr habe ich gerade keine Zeit...

Habe das mal über die Windrose gelegt... Ich denke es ist das, was dir vorschwebte.
Du darfst diesen Dateianhang nicht ansehen.
Du darfst diesen Dateianhang nicht ansehen.

Damian

Ich benutze das Icon wind.svg und drehe es über transform-rotate:

my $svg_icon=::FW_makeImage($ic);
    if(!($svg_icon =~ s/\sheight="[^"]*"/ height="18"/)) {
        $svg_icon =~ s/svg/svg height="18"/ }
    if(!($svg_icon =~ s/\swidth="[^"]*"/ width="18"/)) {
        $svg_icon =~ s/svg/svg width="18"/ }
    $out.='<g transform="translate('.$ix.', '.$iy.') translate(9, 9) scale('.$iscale.') translate(-9, -9) rotate('.$rotate.',9,9) ">';
    $out.= $svg_icon;
    $out.='</g>';
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF