Weiter zum Inhalt
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Hell
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dunkel
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Visualisierung
  4. VIS-2 Widget für Anzeige der Anrufliste

NEWS

  • Der neue Monatsrückblick für Mai und Juni 2026 ist online!
    BluefoxB
    Bluefox
    7
    1
    185

  • wichtiges UPDATE für controller 7.2.2 im stable
    HomoranH
    Homoran
    10
    1
    3.3k

  • Neues YouTube-Video: Visualisierung im Devices-Adapter
    BluefoxB
    Bluefox
    16
    1
    5.0k

VIS-2 Widget für Anzeige der Anrufliste

Geplant Angeheftet Gesperrt Verschoben Visualisierung
24 Beiträge 6 Kommentatoren 695 Aufrufe 5 Beobachtet
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • Holger EtzelH Holger Etzel

    Hallöchen,

    hat schon jemand ein Widget zur Anzeige der Anrufliste aus der Fritzbox hinbekommen?

    Gruß
    Holger

    OliverIOO Offline
    OliverIOO Offline
    OliverIO
    schrieb am zuletzt editiert von
    #2

    @Holger-Etzel

    Sowas könnte man gut mit vis-jsontemplate machen.
    Wie liegen die Daten vor?
    Wird da von einem Adapter schon ein datenpunkt bereitgestellt?
    Wenn du mir so einen datenpunktinhalt anonymisiert bereitstellst, kann ich mal schauen.

    Meine Adapter und Widgets
    TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
    Links im Profil

    Holger EtzelH 1 Antwort Letzte Antwort
    0
    • C Offline
      C Offline
      claus1993
      schrieb am zuletzt editiert von
      #3

      Klingt interessant weswegen ich mich mal mit einklinke.

      Bei mir wäre es eine Fritzbox die ein json liefert,

      {"caller":"xxxxx","callee":"yyyyyy","timestamp":"15.07.26 20:15:03","id":0,"_type":"connect","callerName":"","calleeName":"","extension":20}
      

      Wenn das funktioniert, wäre es ein Grund mal wieder die Kontaktdaten in der Fritzbox aufzuarbeiten :-)

      OliverIOO 1 Antwort Letzte Antwort
      0
      • C claus1993

        Klingt interessant weswegen ich mich mal mit einklinke.

        Bei mir wäre es eine Fritzbox die ein json liefert,

        {"caller":"xxxxx","callee":"yyyyyy","timestamp":"15.07.26 20:15:03","id":0,"_type":"connect","callerName":"","calleeName":"","extension":20}
        

        Wenn das funktioniert, wäre es ein Grund mal wieder die Kontaktdaten in der Fritzbox aufzuarbeiten :-)

        OliverIOO Offline
        OliverIOO Offline
        OliverIO
        schrieb am zuletzt editiert von
        #4

        @claus1993

        Das ist jetzt genau ein Datensatz, ist das keine Liste?

        Meine Adapter und Widgets
        TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
        Links im Profil

        1 Antwort Letzte Antwort
        0
        • OliverIOO OliverIO

          @Holger-Etzel

          Sowas könnte man gut mit vis-jsontemplate machen.
          Wie liegen die Daten vor?
          Wird da von einem Adapter schon ein datenpunkt bereitgestellt?
          Wenn du mir so einen datenpunktinhalt anonymisiert bereitstellst, kann ich mal schauen.

          Holger EtzelH Online
          Holger EtzelH Online
          Holger Etzel
          schrieb am zuletzt editiert von
          #5

          @OliverIO

          es gibt eine html:

          <div>The last call was from at from </div><div>inbound: 24<table><tr><td>29.04.25 17:24</td><td>Name</td><td>Nummer</td></tr><tr><td>29.04.25 17:26</td><td>Name</td><td>Nummer</td></tr><tr><td>20.06.25 17:32</td><td>Name</td><td>Nummer</td></tr><tr><td>21.06.25 12:06</td><td>Name</td><td>Nummer</td></tr><tr><td>25.06.25 17:41</td><td>Name</td><td>Nummer</td></tr><tr><td>08.07.25 16:34</td><td>Name</td><td>Nummer</td></tr><tr><td>13.08.25 17:40</td><td>Name</td><td>Nummer</td></tr><tr><td>21.09.25 16:03</td><td>Name</td><td>Nummer</td></tr><tr><td>27.11.25 16:22</td><td>Name</td><td>Nummer</td></tr><tr><td>16.07.26 12:30</td><td>Name</td><td>Nummer</td></tr></table></div>

          und eine json:

          [{"id":5471,"type":"1","caller":"Anrufernummer","called":"SIP: Nummer","callednumber":"Nummer","name":"xxx","numbertype":"sip","device":"unser Foni","port":"10","date":"16.07.26 12:30","duration":"0:02","count":"","path":"","sym":">","external":"Anrufernummer"},

          mit mehreren Einträgen

          OliverIOO 2 Antworten Letzte Antwort
          0
          • Holger EtzelH Holger Etzel

            @OliverIO

            es gibt eine html:

            <div>The last call was from at from </div><div>inbound: 24<table><tr><td>29.04.25 17:24</td><td>Name</td><td>Nummer</td></tr><tr><td>29.04.25 17:26</td><td>Name</td><td>Nummer</td></tr><tr><td>20.06.25 17:32</td><td>Name</td><td>Nummer</td></tr><tr><td>21.06.25 12:06</td><td>Name</td><td>Nummer</td></tr><tr><td>25.06.25 17:41</td><td>Name</td><td>Nummer</td></tr><tr><td>08.07.25 16:34</td><td>Name</td><td>Nummer</td></tr><tr><td>13.08.25 17:40</td><td>Name</td><td>Nummer</td></tr><tr><td>21.09.25 16:03</td><td>Name</td><td>Nummer</td></tr><tr><td>27.11.25 16:22</td><td>Name</td><td>Nummer</td></tr><tr><td>16.07.26 12:30</td><td>Name</td><td>Nummer</td></tr></table></div>

            und eine json:

            [{"id":5471,"type":"1","caller":"Anrufernummer","called":"SIP: Nummer","callednumber":"Nummer","name":"xxx","numbertype":"sip","device":"unser Foni","port":"10","date":"16.07.26 12:30","duration":"0:02","count":"","path":"","sym":">","external":"Anrufernummer"},

            mit mehreren Einträgen

            OliverIOO Offline
            OliverIOO Offline
            OliverIO
            schrieb am zuletzt editiert von OliverIO
            #6

            @Holger-Etzel

            Die JSON ist besser und ist eine Liste. Ich schaue nachher.

            Welche Inhalte sollen alle mit rein. Ich mach mal eine komplettierte. Rausnehmen kann man immer noch.

            Meine Adapter und Widgets
            TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
            Links im Profil

            1 Antwort Letzte Antwort
            0
            • Holger EtzelH Holger Etzel

              Hallöchen,

              hat schon jemand ein Widget zur Anzeige der Anrufliste aus der Fritzbox hinbekommen?

              Gruß
              Holger

              sigi234S Online
              sigi234S Online
              sigi234
              Forum Testing Most Active
              schrieb am zuletzt editiert von
              #7

              @Holger-Etzel

              Geht doch wunderbar mit einen Table Widget!

              Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.
              Immer Daten sichern!

              1 Antwort Letzte Antwort
              1
              • Holger EtzelH Holger Etzel

                @OliverIO

                es gibt eine html:

                <div>The last call was from at from </div><div>inbound: 24<table><tr><td>29.04.25 17:24</td><td>Name</td><td>Nummer</td></tr><tr><td>29.04.25 17:26</td><td>Name</td><td>Nummer</td></tr><tr><td>20.06.25 17:32</td><td>Name</td><td>Nummer</td></tr><tr><td>21.06.25 12:06</td><td>Name</td><td>Nummer</td></tr><tr><td>25.06.25 17:41</td><td>Name</td><td>Nummer</td></tr><tr><td>08.07.25 16:34</td><td>Name</td><td>Nummer</td></tr><tr><td>13.08.25 17:40</td><td>Name</td><td>Nummer</td></tr><tr><td>21.09.25 16:03</td><td>Name</td><td>Nummer</td></tr><tr><td>27.11.25 16:22</td><td>Name</td><td>Nummer</td></tr><tr><td>16.07.26 12:30</td><td>Name</td><td>Nummer</td></tr></table></div>

                und eine json:

                [{"id":5471,"type":"1","caller":"Anrufernummer","called":"SIP: Nummer","callednumber":"Nummer","name":"xxx","numbertype":"sip","device":"unser Foni","port":"10","date":"16.07.26 12:30","duration":"0:02","count":"","path":"","sym":">","external":"Anrufernummer"},

                mit mehreren Einträgen

                OliverIOO Offline
                OliverIOO Offline
                OliverIO
                schrieb am zuletzt editiert von OliverIO
                #8

                @Holger-Etzel sagte:

                fertig.
                ich habe mir zunächst die json daten von der ki erweitern lassen, so das mehr datensätze zur verfügung stehen.

                [
                 {
                   "id": 5471,
                   "type": "1",
                   "caller": "Anrufernummer",
                   "called": "SIP: Nummer",
                   "callednumber": "Nummer",
                   "name": "xxx",
                   "numbertype": "sip",
                   "device": "unser Foni",
                   "port": "10",
                   "date": "16.07.26 12:30",
                   "duration": "0:02",
                   "count": "",
                   "path": "",
                   "sym": ">",
                   "external": "Anrufernummer"
                 },
                 {
                   "id": 5472,
                   "type": "1",
                   "caller": "01711234567",
                   "called": "SIP: 1002",
                   "callednumber": "1002",
                   "name": "Max Mustermann",
                   "numbertype": "sip",
                   "device": "Telefon Büro",
                   "port": "11",
                   "date": "16.07.26 12:45",
                   "duration": "1:34",
                   "count": "",
                   "path": "",
                   "sym": ">",
                   "external": "01711234567"
                 },
                 {
                   "id": 5473,
                   "type": "2",
                   "caller": "1003",
                   "called": "SIP: 1001",
                   "callednumber": "1001",
                   "name": "Empfang",
                   "numbertype": "sip",
                   "device": "Telefon Empfang",
                   "port": "12",
                   "date": "16.07.26 13:02",
                   "duration": "0:47",
                   "count": "",
                   "path": "",
                   "sym": "<",
                   "external": "1003"
                 },
                 {
                   "id": 5474,
                   "type": "1",
                   "caller": "015901234567",
                   "called": "SIP: 1005",
                   "callednumber": "1005",
                   "name": "Support",
                   "numbertype": "sip",
                   "device": "Softphone",
                   "port": "13",
                   "date": "16.07.26 13:18",
                   "duration": "3:12",
                   "count": "",
                   "path": "",
                   "sym": ">",
                   "external": "015901234567"
                 },
                 {
                   "id": 5475,
                   "type": "2",
                   "caller": "1001",
                   "called": "SIP: 1004",
                   "callednumber": "1004",
                   "name": "Vertrieb",
                   "numbertype": "sip",
                   "device": "IP-Telefon",
                   "port": "14",
                   "date": "16.07.26 14:05",
                   "duration": "5:28",
                   "count": "",
                   "path": "",
                   "sym": "<",
                   "external": "1001"
                 }
                ]
                

                dann habe ich ein template/vorlage für das widget vis-jsontemplate aus dem adapter erstellen lassen. die grundfunktionalität ist relativ einfach, da ich es schön gestalten wollte ist viel css dazugekommen

                einrichten
                wenn nicht vorhanden den adapter vis-jsontemplate installieren
                dann das widget json template in einer view hinzufügen
                unter datapoint dann den entsprechenden datenpunkt auswählen
                und in das feld template das folgende einfügen

                <%
                <%
                /*
                * ioBroker.vis-jsontemplate
                * Responsive Anrufliste
                *
                * ============================================================
                * EINFACHE DARSTELLUNGS- UND FARBKONFIGURATION
                * ============================================================
                */
                
                /* Darstellung auswählen: "dark" oder "light" */
                var themeMode = "light";
                
                /*
                * Hier können alle verwendeten Farben angepasst werden.
                * Gültig sind z. B. #ffffff, rgb(...), rgba(...), transparent.
                */
                var themes = {
                   dark: {
                       background: "rgba(27, 31, 39, 0.96)",
                       cardBackground: "rgba(255, 255, 255, 0.055)",
                       cardHoverBackground: "rgba(255, 255, 255, 0.09)",
                       border: "rgba(255, 255, 255, 0.09)",
                       hoverBorder: "rgba(255, 255, 255, 0.15)",
                       text: "#f4f6f8",
                       mutedText: "#9ba6b2",
                       incoming: "#43c982",
                       outgoing: "#5fa8ff",
                       unknown: "#a9b0b8",
                       iconText: "#ffffff",
                       headerIconBackground: "rgba(95, 168, 255, 0.14)",
                       countBackground: "rgba(255, 255, 255, 0.06)",
                       avatarBackground: "rgba(255, 255, 255, 0.07)",
                       durationBackground: "rgba(255, 255, 255, 0.055)",
                       scrollbarThumb: "rgba(255, 255, 255, 0.22)",
                       scrollbarTrack: "transparent",
                       shadow: "rgba(0, 0, 0, 0.24)"
                   },
                
                   light: {
                       background: "rgba(248, 250, 252, 0.98)",
                       cardBackground: "#ffffff",
                       cardHoverBackground: "#f1f5f9",
                       border: "rgba(15, 23, 42, 0.12)",
                       hoverBorder: "rgba(15, 23, 42, 0.22)",
                       text: "#172033",
                       mutedText: "#667085",
                       incoming: "#168653",
                       outgoing: "#2563c9",
                       unknown: "#6b7280",
                       iconText: "#ffffff",
                       headerIconBackground: "rgba(37, 99, 201, 0.12)",
                       countBackground: "rgba(15, 23, 42, 0.055)",
                       avatarBackground: "rgba(15, 23, 42, 0.055)",
                       durationBackground: "rgba(15, 23, 42, 0.055)",
                       scrollbarThumb: "rgba(15, 23, 42, 0.25)",
                       scrollbarTrack: "transparent",
                       shadow: "rgba(15, 23, 42, 0.16)"
                   }
                };
                
                /* Weitere leicht auffindbare Darstellungsoptionen */
                var widgetRadius = "16px";
                var cardRadius = "13px";
                var shadowDefinition = "0 12px 35px";
                
                /* Ab hier sind normalerweise keine Anpassungen erforderlich. */
                var theme = themes[themeMode] || themes.dark;
                
                var resolvedWidgetId =
                   typeof widgetID !== "undefined"
                       ? widgetID
                       : (typeof widgetid !== "undefined" ? widgetid : "");
                
                var calls = [];
                var errorMessage = "";
                
                try {
                   if (typeof data === "string") {
                       calls = JSON.parse(data);
                   } else {
                       calls = data;
                   }
                
                   /*
                    * Manche Datenpunkte enthalten das Array nochmals
                    * in einer Eigenschaft wie "data", "items" oder "result".
                    */
                   if (!Array.isArray(calls) && calls) {
                       if (Array.isArray(calls.data)) {
                           calls = calls.data;
                       } else if (Array.isArray(calls.items)) {
                           calls = calls.items;
                       } else if (Array.isArray(calls.result)) {
                           calls = calls.result;
                       }
                   }
                
                   if (!Array.isArray(calls)) {
                       calls = [];
                       errorMessage = "Der Datenpunkt enthält kein JSON-Array.";
                   }
                } catch (error) {
                   calls = [];
                   errorMessage = "Die JSON-Daten konnten nicht verarbeitet werden.";
                }
                
                function safe(value, fallback) {
                   if (value === undefined || value === null || value === "") {
                       return fallback || "";
                   }
                
                   return String(value);
                }
                
                function getInitials(name) {
                   var cleanName = safe(name, "?").trim();
                
                   if (!cleanName) {
                       return "?";
                   }
                
                   var parts = cleanName.split(/\s+/);
                
                   if (parts.length === 1) {
                       return parts[0].substring(0, 2).toUpperCase();
                   }
                
                   return (
                       parts[0].substring(0, 1) +
                       parts[parts.length - 1].substring(0, 1)
                   ).toUpperCase();
                }
                
                function getDirection(call) {
                   /*
                    * Zuordnung anhand des Feldes "sym".
                    * Bei abweichender Bedeutung einfach hier tauschen.
                    */
                   if (call.sym === ">") {
                       return {
                           css: "outgoing",
                           icon: "↗",
                           label: "Ausgehend"
                       };
                   }
                
                   if (call.sym === "<") {
                       return {
                           css: "incoming",
                           icon: "↙",
                           label: "Eingehend"
                       };
                   }
                
                   return {
                       css: "unknown",
                       icon: "↔",
                       label: "Unbekannt"
                   };
                }
                
                function getDateParts(dateValue) {
                   var value = safe(dateValue, "–");
                   var parts = value.split(" ");
                
                   return {
                       date: parts[0] || "–",
                       time: parts.slice(1).join(" ") || ""
                   };
                }
                %>
                
                <style>
                   #<%= resolvedWidgetId %> {
                       width: 100%;
                       height: 100%;
                       box-sizing: border-box;
                       overflow: hidden;
                
                       --call-bg: <%= theme.background %>;
                       --call-card-bg: <%= theme.cardBackground %>;
                       --call-card-hover: <%= theme.cardHoverBackground %>;
                       --call-border: <%= theme.border %>;
                       --call-hover-border: <%= theme.hoverBorder %>;
                       --call-text: <%= theme.text %>;
                       --call-muted: <%= theme.mutedText %>;
                       --call-incoming: <%= theme.incoming %>;
                       --call-outgoing: <%= theme.outgoing %>;
                       --call-unknown: <%= theme.unknown %>;
                       --call-icon-text: <%= theme.iconText %>;
                       --call-header-icon-bg: <%= theme.headerIconBackground %>;
                       --call-count-bg: <%= theme.countBackground %>;
                       --call-avatar-bg: <%= theme.avatarBackground %>;
                       --call-duration-bg: <%= theme.durationBackground %>;
                       --call-scrollbar-thumb: <%= theme.scrollbarThumb %>;
                       --call-scrollbar-track: <%= theme.scrollbarTrack %>;
                       --call-shadow: <%= theme.shadow %>;
                       --call-radius: <%= widgetRadius %>;
                       --call-card-radius: <%= cardRadius %>;
                
                       font-family:
                           Inter,
                           -apple-system,
                           BlinkMacSystemFont,
                           "Segoe UI",
                           Roboto,
                           Arial,
                           sans-serif;
                       color: var(--call-text);
                   }
                
                   #<%= resolvedWidgetId %> * {
                       box-sizing: border-box;
                   }
                
                   #<%= resolvedWidgetId %> .call-widget {
                       width: 100%;
                       height: 100%;
                       display: flex;
                       flex-direction: column;
                       overflow: hidden;
                       background: var(--call-bg);
                       border: 1px solid var(--call-border);
                       border-radius: var(--call-radius);
                       box-shadow: <%= shadowDefinition %> var(--call-shadow);
                   }
                
                   #<%= resolvedWidgetId %> .call-header {
                       flex: 0 0 auto;
                       display: flex;
                       align-items: center;
                       justify-content: space-between;
                       gap: 16px;
                       padding: 18px 20px 14px;
                       border-bottom: 1px solid var(--call-border);
                   }
                
                   #<%= resolvedWidgetId %> .call-header-title {
                       display: flex;
                       align-items: center;
                       gap: 11px;
                       min-width: 0;
                   }
                
                   #<%= resolvedWidgetId %> .call-header-icon {
                       width: 38px;
                       height: 38px;
                       flex: 0 0 38px;
                       display: grid;
                       place-items: center;
                       border-radius: 11px;
                       background: var(--call-header-icon-bg);
                       font-size: 20px;
                   }
                
                   #<%= resolvedWidgetId %> .call-title {
                       margin: 0;
                       font-size: 18px;
                       line-height: 1.2;
                       font-weight: 700;
                       letter-spacing: 0.01em;
                   }
                
                   #<%= resolvedWidgetId %> .call-subtitle {
                       margin-top: 3px;
                       color: var(--call-muted);
                       font-size: 12px;
                   }
                
                   #<%= resolvedWidgetId %> .call-count {
                       flex: 0 0 auto;
                       min-width: 34px;
                       padding: 6px 10px;
                       border: 1px solid var(--call-border);
                       border-radius: 999px;
                       background: var(--call-count-bg);
                       text-align: center;
                       font-size: 12px;
                       font-weight: 700;
                   }
                
                   #<%= resolvedWidgetId %> .call-list {
                       flex: 1 1 auto;
                       min-height: 0;
                       overflow-x: hidden;
                       overflow-y: auto;
                       padding: 10px;
                       scrollbar-width: thin;
                       scrollbar-color: var(--call-scrollbar-thumb) var(--call-scrollbar-track);
                   }
                
                   #<%= resolvedWidgetId %> .call-list::-webkit-scrollbar {
                       width: 7px;
                   }
                
                   #<%= resolvedWidgetId %> .call-list::-webkit-scrollbar-track {
                       background: var(--call-scrollbar-track);
                   }
                
                   #<%= resolvedWidgetId %> .call-list::-webkit-scrollbar-thumb {
                       background: var(--call-scrollbar-thumb);
                       border-radius: 10px;
                   }
                
                   #<%= resolvedWidgetId %> .call-card {
                       position: relative;
                       display: grid;
                       grid-template-columns: 48px minmax(130px, 1.4fr) minmax(125px, 1fr) auto;
                       align-items: center;
                       gap: 13px;
                       margin-bottom: 8px;
                       padding: 13px 14px;
                       overflow: hidden;
                       background: var(--call-card-bg);
                       border: 1px solid var(--call-border);
                       border-radius: var(--call-card-radius);
                       transition:
                           background 150ms ease,
                           transform 150ms ease,
                           border-color 150ms ease;
                   }
                
                   #<%= resolvedWidgetId %> .call-card:last-child {
                       margin-bottom: 0;
                   }
                
                   #<%= resolvedWidgetId %> .call-card:hover {
                       background: var(--call-card-hover);
                       border-color: var(--call-hover-border);
                       transform: translateY(-1px);
                   }
                
                   #<%= resolvedWidgetId %> .call-card::before {
                       position: absolute;
                       top: 9px;
                       bottom: 9px;
                       left: 0;
                       width: 3px;
                       border-radius: 0 4px 4px 0;
                       background: var(--call-unknown);
                       content: "";
                   }
                
                   #<%= resolvedWidgetId %> .call-card.incoming::before {
                       background: var(--call-incoming);
                   }
                
                   #<%= resolvedWidgetId %> .call-card.outgoing::before {
                       background: var(--call-outgoing);
                   }
                
                   #<%= resolvedWidgetId %> .call-avatar {
                       position: relative;
                       width: 44px;
                       height: 44px;
                       display: grid;
                       place-items: center;
                       border: 1px solid var(--call-border);
                       border-radius: 50%;
                       background: var(--call-avatar-bg);
                       font-size: 13px;
                       font-weight: 750;
                       letter-spacing: 0.03em;
                   }
                
                   #<%= resolvedWidgetId %> .call-direction-icon {
                       position: absolute;
                       right: -3px;
                       bottom: -3px;
                       width: 20px;
                       height: 20px;
                       display: grid;
                       place-items: center;
                       border: 2px solid var(--call-bg);
                       border-radius: 50%;
                       background: var(--call-unknown);
                       color: var(--call-icon-text);
                       font-size: 12px;
                       font-weight: 800;
                   }
                
                   #<%= resolvedWidgetId %> .incoming .call-direction-icon {
                       background: var(--call-incoming);
                   }
                
                   #<%= resolvedWidgetId %> .outgoing .call-direction-icon {
                       background: var(--call-outgoing);
                   }
                
                   #<%= resolvedWidgetId %> .call-person,
                   #<%= resolvedWidgetId %> .call-target {
                       min-width: 0;
                   }
                
                   #<%= resolvedWidgetId %> .call-name {
                       overflow: hidden;
                       margin-bottom: 4px;
                       color: var(--call-text);
                       font-size: 14px;
                       line-height: 1.25;
                       font-weight: 700;
                       text-overflow: ellipsis;
                       white-space: nowrap;
                   }
                
                   #<%= resolvedWidgetId %> .call-number,
                   #<%= resolvedWidgetId %> .call-target-value,
                   #<%= resolvedWidgetId %> .call-device {
                       overflow: hidden;
                       color: var(--call-muted);
                       font-size: 12px;
                       line-height: 1.35;
                       text-overflow: ellipsis;
                       white-space: nowrap;
                   }
                
                   #<%= resolvedWidgetId %> .call-target-label {
                       margin-bottom: 3px;
                       color: var(--call-muted);
                       font-size: 10px;
                       font-weight: 700;
                       letter-spacing: 0.08em;
                       text-transform: uppercase;
                   }
                
                   #<%= resolvedWidgetId %> .call-meta {
                       min-width: 92px;
                       text-align: right;
                   }
                
                   #<%= resolvedWidgetId %> .call-time {
                       margin-bottom: 4px;
                       font-size: 13px;
                       font-weight: 700;
                   }
                
                   #<%= resolvedWidgetId %> .call-date {
                       color: var(--call-muted);
                       font-size: 11px;
                   }
                
                   #<%= resolvedWidgetId %> .call-duration {
                       display: inline-flex;
                       align-items: center;
                       gap: 5px;
                       margin-top: 7px;
                       padding: 4px 7px;
                       border-radius: 7px;
                       background: var(--call-duration-bg);
                       color: var(--call-muted);
                       font-size: 11px;
                   }
                
                   #<%= resolvedWidgetId %> .call-duration::before {
                       content: "◷";
                       font-size: 12px;
                   }
                
                   #<%= resolvedWidgetId %> .call-status {
                       display: inline-block;
                       margin-top: 6px;
                       font-size: 10px;
                       font-weight: 700;
                   }
                
                   #<%= resolvedWidgetId %> .incoming .call-status {
                       color: var(--call-incoming);
                   }
                
                   #<%= resolvedWidgetId %> .outgoing .call-status {
                       color: var(--call-outgoing);
                   }
                
                   #<%= resolvedWidgetId %> .unknown .call-status {
                       color: var(--call-unknown);
                   }
                
                   #<%= resolvedWidgetId %> .call-empty {
                       min-height: 180px;
                       height: 100%;
                       display: grid;
                       place-items: center;
                       padding: 30px;
                       text-align: center;
                   }
                
                   #<%= resolvedWidgetId %> .call-empty-icon {
                       margin-bottom: 12px;
                       font-size: 36px;
                       opacity: 0.7;
                   }
                
                   #<%= resolvedWidgetId %> .call-empty-title {
                       margin-bottom: 6px;
                       font-size: 15px;
                       font-weight: 700;
                   }
                
                   #<%= resolvedWidgetId %> .call-empty-text {
                       max-width: 320px;
                       color: var(--call-muted);
                       font-size: 12px;
                       line-height: 1.5;
                   }
                
                   @media (max-width: 650px) {
                       #<%= resolvedWidgetId %> .call-header {
                           padding: 14px 15px 12px;
                       }
                
                       #<%= resolvedWidgetId %> .call-list {
                           padding: 8px;
                       }
                
                       #<%= resolvedWidgetId %> .call-card {
                           grid-template-columns: 45px minmax(0, 1fr) auto;
                           gap: 11px;
                           padding: 12px;
                       }
                
                       #<%= resolvedWidgetId %> .call-target {
                           display: none;
                       }
                
                       #<%= resolvedWidgetId %> .call-meta {
                           min-width: 70px;
                       }
                
                       #<%= resolvedWidgetId %> .call-status {
                           display: none;
                       }
                   }
                
                   @media (max-width: 420px) {
                       #<%= resolvedWidgetId %> .call-subtitle {
                           display: none;
                       }
                
                       #<%= resolvedWidgetId %> .call-card {
                           grid-template-columns: 42px minmax(0, 1fr);
                       }
                
                       #<%= resolvedWidgetId %> .call-meta {
                           grid-column: 2;
                           display: flex;
                           align-items: center;
                           gap: 8px;
                           margin-top: -2px;
                           text-align: left;
                       }
                
                       #<%= resolvedWidgetId %> .call-time,
                       #<%= resolvedWidgetId %> .call-date,
                       #<%= resolvedWidgetId %> .call-duration {
                           margin: 0;
                       }
                
                       #<%= resolvedWidgetId %> .call-date {
                           display: none;
                       }
                   }
                </style>
                
                <div class="call-widget">
                   <div class="call-header">
                       <div class="call-header-title">
                           <div class="call-header-icon">☎</div>
                
                           <div>
                               <h2 class="call-title">Anrufliste</h2>
                               <div class="call-subtitle">Letzte Verbindungen</div>
                           </div>
                       </div>
                
                       <div class="call-count">
                           <%= calls.length %>
                       </div>
                   </div>
                
                   <div class="call-list">
                       <% if (calls.length === 0) { %>
                           <div class="call-empty">
                               <div>
                                   <div class="call-empty-icon">
                                       <%= errorMessage ? "⚠" : "☎" %>
                                   </div>
                
                                   <div class="call-empty-title">
                                       <%= errorMessage ? "Datenfehler" : "Keine Anrufe" %>
                                   </div>
                
                                   <div class="call-empty-text">
                                       <%= errorMessage || "Aktuell sind keine Einträge in der Anrufliste vorhanden." %>
                                   </div>
                               </div>
                           </div>
                       <% } else { %>
                           <% calls.forEach(function(call) { %>
                               <%
                                   var direction = getDirection(call);
                                   var dateParts = getDateParts(call.date);
                                   var displayName = safe(call.name, "Unbekannter Teilnehmer");
                                   var displayNumber = safe(
                                       call.external || call.caller,
                                       "Keine Rufnummer"
                                   );
                                   var target = safe(
                                       call.callednumber || call.called,
                                       "–"
                                   );
                               %>
                
                               <div class="call-card <%= direction.css %>">
                                   <div class="call-avatar">
                                       <span><%= getInitials(displayName) %></span>
                
                                       <span
                                           class="call-direction-icon"
                                           title="<%= direction.label %>"
                                       >
                                           <%= direction.icon %>
                                       </span>
                                   </div>
                
                                   <div class="call-person">
                                       <div
                                           class="call-name"
                                           title="<%= displayName %>"
                                       >
                                           <%= displayName %>
                                       </div>
                
                                       <div
                                           class="call-number"
                                           title="<%= displayNumber %>"
                                       >
                                           <%= displayNumber %>
                                       </div>
                
                                       <% if (call.device) { %>
                                           <div
                                               class="call-device"
                                               title="<%= safe(call.device) %>"
                                           >
                                               <%= safe(call.device) %>
                                               <% if (call.port) { %>
                                                   · Port <%= safe(call.port) %>
                                               <% } %>
                                           </div>
                                       <% } %>
                                   </div>
                
                                   <div class="call-target">
                                       <div class="call-target-label">Ziel</div>
                
                                       <div
                                           class="call-target-value"
                                           title="<%= target %>"
                                       >
                                           <%= target %>
                                       </div>
                
                                       <div class="call-status">
                                           <%= direction.label %>
                                       </div>
                                   </div>
                
                                   <div class="call-meta">
                                       <div class="call-time">
                                           <%= dateParts.time || "–" %>
                                       </div>
                
                                       <div class="call-date">
                                           <%= dateParts.date %>
                                       </div>
                
                                       <div class="call-duration">
                                           <%= safe(call.duration, "0:00") %>
                                       </div>
                                   </div>
                               </div>
                           <% }); %>
                       <% } %>
                   </div>
                </div>
                

                367a9fc0-5c6e-408e-837b-31beda30c36b-image.jpeg

                je nach vorlieben und länge des wiedgets noch unter css common bei overflow-y auto einstellen. das sorgt dann dafür, das der inhalt auf die größe des widgets begrenzt wird und scrollbalken an der seite erscheinen, falls der inhalt über die größe hinaus geht

                1b557ce1-0024-408e-980d-8b63dcec2433-image.jpeg

                so sieht dann das ergebnis aus.
                dark/dunkel darstellung
                b8e68ef9-2c55-4483-8e3f-481bd92a08dc-image.jpeg

                light/hell darstellung
                c0356eca-28b9-4c50-9c9f-bb1c22d8c74a-image.jpeg
                Habe gerade festgestellt, das es unter vis1 noch ein problem gibt. muss ich noch nachschauen
                -> verdammt. eine inkonsistenz zwischen vis1/2 im eigenen adapter. habe das template oben mit einem workaround korrigiert

                das beispiel wurde als usecase in der adapterdokumentation von vis-jsontemplate hinterlegt
                https://github.com/oweitman/ioBroker.vis-jsontemplate/blob/main/documentation/usecase-fritzbox-call-list.md

                als anmerkung: das template hat mir die ki zu 99% (bis auf den workaround) als oneshot bereitgestellt. werde auch mal schauen ob man das noch auf helle darstellung erweitern kann.

                Meine Adapter und Widgets
                TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                Links im Profil

                OliverIOO 1 Antwort Letzte Antwort
                0
                • OliverIOO OliverIO

                  @Holger-Etzel sagte:

                  fertig.
                  ich habe mir zunächst die json daten von der ki erweitern lassen, so das mehr datensätze zur verfügung stehen.

                  [
                   {
                     "id": 5471,
                     "type": "1",
                     "caller": "Anrufernummer",
                     "called": "SIP: Nummer",
                     "callednumber": "Nummer",
                     "name": "xxx",
                     "numbertype": "sip",
                     "device": "unser Foni",
                     "port": "10",
                     "date": "16.07.26 12:30",
                     "duration": "0:02",
                     "count": "",
                     "path": "",
                     "sym": ">",
                     "external": "Anrufernummer"
                   },
                   {
                     "id": 5472,
                     "type": "1",
                     "caller": "01711234567",
                     "called": "SIP: 1002",
                     "callednumber": "1002",
                     "name": "Max Mustermann",
                     "numbertype": "sip",
                     "device": "Telefon Büro",
                     "port": "11",
                     "date": "16.07.26 12:45",
                     "duration": "1:34",
                     "count": "",
                     "path": "",
                     "sym": ">",
                     "external": "01711234567"
                   },
                   {
                     "id": 5473,
                     "type": "2",
                     "caller": "1003",
                     "called": "SIP: 1001",
                     "callednumber": "1001",
                     "name": "Empfang",
                     "numbertype": "sip",
                     "device": "Telefon Empfang",
                     "port": "12",
                     "date": "16.07.26 13:02",
                     "duration": "0:47",
                     "count": "",
                     "path": "",
                     "sym": "<",
                     "external": "1003"
                   },
                   {
                     "id": 5474,
                     "type": "1",
                     "caller": "015901234567",
                     "called": "SIP: 1005",
                     "callednumber": "1005",
                     "name": "Support",
                     "numbertype": "sip",
                     "device": "Softphone",
                     "port": "13",
                     "date": "16.07.26 13:18",
                     "duration": "3:12",
                     "count": "",
                     "path": "",
                     "sym": ">",
                     "external": "015901234567"
                   },
                   {
                     "id": 5475,
                     "type": "2",
                     "caller": "1001",
                     "called": "SIP: 1004",
                     "callednumber": "1004",
                     "name": "Vertrieb",
                     "numbertype": "sip",
                     "device": "IP-Telefon",
                     "port": "14",
                     "date": "16.07.26 14:05",
                     "duration": "5:28",
                     "count": "",
                     "path": "",
                     "sym": "<",
                     "external": "1001"
                   }
                  ]
                  

                  dann habe ich ein template/vorlage für das widget vis-jsontemplate aus dem adapter erstellen lassen. die grundfunktionalität ist relativ einfach, da ich es schön gestalten wollte ist viel css dazugekommen

                  einrichten
                  wenn nicht vorhanden den adapter vis-jsontemplate installieren
                  dann das widget json template in einer view hinzufügen
                  unter datapoint dann den entsprechenden datenpunkt auswählen
                  und in das feld template das folgende einfügen

                  <%
                  <%
                  /*
                  * ioBroker.vis-jsontemplate
                  * Responsive Anrufliste
                  *
                  * ============================================================
                  * EINFACHE DARSTELLUNGS- UND FARBKONFIGURATION
                  * ============================================================
                  */
                  
                  /* Darstellung auswählen: "dark" oder "light" */
                  var themeMode = "light";
                  
                  /*
                  * Hier können alle verwendeten Farben angepasst werden.
                  * Gültig sind z. B. #ffffff, rgb(...), rgba(...), transparent.
                  */
                  var themes = {
                     dark: {
                         background: "rgba(27, 31, 39, 0.96)",
                         cardBackground: "rgba(255, 255, 255, 0.055)",
                         cardHoverBackground: "rgba(255, 255, 255, 0.09)",
                         border: "rgba(255, 255, 255, 0.09)",
                         hoverBorder: "rgba(255, 255, 255, 0.15)",
                         text: "#f4f6f8",
                         mutedText: "#9ba6b2",
                         incoming: "#43c982",
                         outgoing: "#5fa8ff",
                         unknown: "#a9b0b8",
                         iconText: "#ffffff",
                         headerIconBackground: "rgba(95, 168, 255, 0.14)",
                         countBackground: "rgba(255, 255, 255, 0.06)",
                         avatarBackground: "rgba(255, 255, 255, 0.07)",
                         durationBackground: "rgba(255, 255, 255, 0.055)",
                         scrollbarThumb: "rgba(255, 255, 255, 0.22)",
                         scrollbarTrack: "transparent",
                         shadow: "rgba(0, 0, 0, 0.24)"
                     },
                  
                     light: {
                         background: "rgba(248, 250, 252, 0.98)",
                         cardBackground: "#ffffff",
                         cardHoverBackground: "#f1f5f9",
                         border: "rgba(15, 23, 42, 0.12)",
                         hoverBorder: "rgba(15, 23, 42, 0.22)",
                         text: "#172033",
                         mutedText: "#667085",
                         incoming: "#168653",
                         outgoing: "#2563c9",
                         unknown: "#6b7280",
                         iconText: "#ffffff",
                         headerIconBackground: "rgba(37, 99, 201, 0.12)",
                         countBackground: "rgba(15, 23, 42, 0.055)",
                         avatarBackground: "rgba(15, 23, 42, 0.055)",
                         durationBackground: "rgba(15, 23, 42, 0.055)",
                         scrollbarThumb: "rgba(15, 23, 42, 0.25)",
                         scrollbarTrack: "transparent",
                         shadow: "rgba(15, 23, 42, 0.16)"
                     }
                  };
                  
                  /* Weitere leicht auffindbare Darstellungsoptionen */
                  var widgetRadius = "16px";
                  var cardRadius = "13px";
                  var shadowDefinition = "0 12px 35px";
                  
                  /* Ab hier sind normalerweise keine Anpassungen erforderlich. */
                  var theme = themes[themeMode] || themes.dark;
                  
                  var resolvedWidgetId =
                     typeof widgetID !== "undefined"
                         ? widgetID
                         : (typeof widgetid !== "undefined" ? widgetid : "");
                  
                  var calls = [];
                  var errorMessage = "";
                  
                  try {
                     if (typeof data === "string") {
                         calls = JSON.parse(data);
                     } else {
                         calls = data;
                     }
                  
                     /*
                      * Manche Datenpunkte enthalten das Array nochmals
                      * in einer Eigenschaft wie "data", "items" oder "result".
                      */
                     if (!Array.isArray(calls) && calls) {
                         if (Array.isArray(calls.data)) {
                             calls = calls.data;
                         } else if (Array.isArray(calls.items)) {
                             calls = calls.items;
                         } else if (Array.isArray(calls.result)) {
                             calls = calls.result;
                         }
                     }
                  
                     if (!Array.isArray(calls)) {
                         calls = [];
                         errorMessage = "Der Datenpunkt enthält kein JSON-Array.";
                     }
                  } catch (error) {
                     calls = [];
                     errorMessage = "Die JSON-Daten konnten nicht verarbeitet werden.";
                  }
                  
                  function safe(value, fallback) {
                     if (value === undefined || value === null || value === "") {
                         return fallback || "";
                     }
                  
                     return String(value);
                  }
                  
                  function getInitials(name) {
                     var cleanName = safe(name, "?").trim();
                  
                     if (!cleanName) {
                         return "?";
                     }
                  
                     var parts = cleanName.split(/\s+/);
                  
                     if (parts.length === 1) {
                         return parts[0].substring(0, 2).toUpperCase();
                     }
                  
                     return (
                         parts[0].substring(0, 1) +
                         parts[parts.length - 1].substring(0, 1)
                     ).toUpperCase();
                  }
                  
                  function getDirection(call) {
                     /*
                      * Zuordnung anhand des Feldes "sym".
                      * Bei abweichender Bedeutung einfach hier tauschen.
                      */
                     if (call.sym === ">") {
                         return {
                             css: "outgoing",
                             icon: "↗",
                             label: "Ausgehend"
                         };
                     }
                  
                     if (call.sym === "<") {
                         return {
                             css: "incoming",
                             icon: "↙",
                             label: "Eingehend"
                         };
                     }
                  
                     return {
                         css: "unknown",
                         icon: "↔",
                         label: "Unbekannt"
                     };
                  }
                  
                  function getDateParts(dateValue) {
                     var value = safe(dateValue, "–");
                     var parts = value.split(" ");
                  
                     return {
                         date: parts[0] || "–",
                         time: parts.slice(1).join(" ") || ""
                     };
                  }
                  %>
                  
                  <style>
                     #<%= resolvedWidgetId %> {
                         width: 100%;
                         height: 100%;
                         box-sizing: border-box;
                         overflow: hidden;
                  
                         --call-bg: <%= theme.background %>;
                         --call-card-bg: <%= theme.cardBackground %>;
                         --call-card-hover: <%= theme.cardHoverBackground %>;
                         --call-border: <%= theme.border %>;
                         --call-hover-border: <%= theme.hoverBorder %>;
                         --call-text: <%= theme.text %>;
                         --call-muted: <%= theme.mutedText %>;
                         --call-incoming: <%= theme.incoming %>;
                         --call-outgoing: <%= theme.outgoing %>;
                         --call-unknown: <%= theme.unknown %>;
                         --call-icon-text: <%= theme.iconText %>;
                         --call-header-icon-bg: <%= theme.headerIconBackground %>;
                         --call-count-bg: <%= theme.countBackground %>;
                         --call-avatar-bg: <%= theme.avatarBackground %>;
                         --call-duration-bg: <%= theme.durationBackground %>;
                         --call-scrollbar-thumb: <%= theme.scrollbarThumb %>;
                         --call-scrollbar-track: <%= theme.scrollbarTrack %>;
                         --call-shadow: <%= theme.shadow %>;
                         --call-radius: <%= widgetRadius %>;
                         --call-card-radius: <%= cardRadius %>;
                  
                         font-family:
                             Inter,
                             -apple-system,
                             BlinkMacSystemFont,
                             "Segoe UI",
                             Roboto,
                             Arial,
                             sans-serif;
                         color: var(--call-text);
                     }
                  
                     #<%= resolvedWidgetId %> * {
                         box-sizing: border-box;
                     }
                  
                     #<%= resolvedWidgetId %> .call-widget {
                         width: 100%;
                         height: 100%;
                         display: flex;
                         flex-direction: column;
                         overflow: hidden;
                         background: var(--call-bg);
                         border: 1px solid var(--call-border);
                         border-radius: var(--call-radius);
                         box-shadow: <%= shadowDefinition %> var(--call-shadow);
                     }
                  
                     #<%= resolvedWidgetId %> .call-header {
                         flex: 0 0 auto;
                         display: flex;
                         align-items: center;
                         justify-content: space-between;
                         gap: 16px;
                         padding: 18px 20px 14px;
                         border-bottom: 1px solid var(--call-border);
                     }
                  
                     #<%= resolvedWidgetId %> .call-header-title {
                         display: flex;
                         align-items: center;
                         gap: 11px;
                         min-width: 0;
                     }
                  
                     #<%= resolvedWidgetId %> .call-header-icon {
                         width: 38px;
                         height: 38px;
                         flex: 0 0 38px;
                         display: grid;
                         place-items: center;
                         border-radius: 11px;
                         background: var(--call-header-icon-bg);
                         font-size: 20px;
                     }
                  
                     #<%= resolvedWidgetId %> .call-title {
                         margin: 0;
                         font-size: 18px;
                         line-height: 1.2;
                         font-weight: 700;
                         letter-spacing: 0.01em;
                     }
                  
                     #<%= resolvedWidgetId %> .call-subtitle {
                         margin-top: 3px;
                         color: var(--call-muted);
                         font-size: 12px;
                     }
                  
                     #<%= resolvedWidgetId %> .call-count {
                         flex: 0 0 auto;
                         min-width: 34px;
                         padding: 6px 10px;
                         border: 1px solid var(--call-border);
                         border-radius: 999px;
                         background: var(--call-count-bg);
                         text-align: center;
                         font-size: 12px;
                         font-weight: 700;
                     }
                  
                     #<%= resolvedWidgetId %> .call-list {
                         flex: 1 1 auto;
                         min-height: 0;
                         overflow-x: hidden;
                         overflow-y: auto;
                         padding: 10px;
                         scrollbar-width: thin;
                         scrollbar-color: var(--call-scrollbar-thumb) var(--call-scrollbar-track);
                     }
                  
                     #<%= resolvedWidgetId %> .call-list::-webkit-scrollbar {
                         width: 7px;
                     }
                  
                     #<%= resolvedWidgetId %> .call-list::-webkit-scrollbar-track {
                         background: var(--call-scrollbar-track);
                     }
                  
                     #<%= resolvedWidgetId %> .call-list::-webkit-scrollbar-thumb {
                         background: var(--call-scrollbar-thumb);
                         border-radius: 10px;
                     }
                  
                     #<%= resolvedWidgetId %> .call-card {
                         position: relative;
                         display: grid;
                         grid-template-columns: 48px minmax(130px, 1.4fr) minmax(125px, 1fr) auto;
                         align-items: center;
                         gap: 13px;
                         margin-bottom: 8px;
                         padding: 13px 14px;
                         overflow: hidden;
                         background: var(--call-card-bg);
                         border: 1px solid var(--call-border);
                         border-radius: var(--call-card-radius);
                         transition:
                             background 150ms ease,
                             transform 150ms ease,
                             border-color 150ms ease;
                     }
                  
                     #<%= resolvedWidgetId %> .call-card:last-child {
                         margin-bottom: 0;
                     }
                  
                     #<%= resolvedWidgetId %> .call-card:hover {
                         background: var(--call-card-hover);
                         border-color: var(--call-hover-border);
                         transform: translateY(-1px);
                     }
                  
                     #<%= resolvedWidgetId %> .call-card::before {
                         position: absolute;
                         top: 9px;
                         bottom: 9px;
                         left: 0;
                         width: 3px;
                         border-radius: 0 4px 4px 0;
                         background: var(--call-unknown);
                         content: "";
                     }
                  
                     #<%= resolvedWidgetId %> .call-card.incoming::before {
                         background: var(--call-incoming);
                     }
                  
                     #<%= resolvedWidgetId %> .call-card.outgoing::before {
                         background: var(--call-outgoing);
                     }
                  
                     #<%= resolvedWidgetId %> .call-avatar {
                         position: relative;
                         width: 44px;
                         height: 44px;
                         display: grid;
                         place-items: center;
                         border: 1px solid var(--call-border);
                         border-radius: 50%;
                         background: var(--call-avatar-bg);
                         font-size: 13px;
                         font-weight: 750;
                         letter-spacing: 0.03em;
                     }
                  
                     #<%= resolvedWidgetId %> .call-direction-icon {
                         position: absolute;
                         right: -3px;
                         bottom: -3px;
                         width: 20px;
                         height: 20px;
                         display: grid;
                         place-items: center;
                         border: 2px solid var(--call-bg);
                         border-radius: 50%;
                         background: var(--call-unknown);
                         color: var(--call-icon-text);
                         font-size: 12px;
                         font-weight: 800;
                     }
                  
                     #<%= resolvedWidgetId %> .incoming .call-direction-icon {
                         background: var(--call-incoming);
                     }
                  
                     #<%= resolvedWidgetId %> .outgoing .call-direction-icon {
                         background: var(--call-outgoing);
                     }
                  
                     #<%= resolvedWidgetId %> .call-person,
                     #<%= resolvedWidgetId %> .call-target {
                         min-width: 0;
                     }
                  
                     #<%= resolvedWidgetId %> .call-name {
                         overflow: hidden;
                         margin-bottom: 4px;
                         color: var(--call-text);
                         font-size: 14px;
                         line-height: 1.25;
                         font-weight: 700;
                         text-overflow: ellipsis;
                         white-space: nowrap;
                     }
                  
                     #<%= resolvedWidgetId %> .call-number,
                     #<%= resolvedWidgetId %> .call-target-value,
                     #<%= resolvedWidgetId %> .call-device {
                         overflow: hidden;
                         color: var(--call-muted);
                         font-size: 12px;
                         line-height: 1.35;
                         text-overflow: ellipsis;
                         white-space: nowrap;
                     }
                  
                     #<%= resolvedWidgetId %> .call-target-label {
                         margin-bottom: 3px;
                         color: var(--call-muted);
                         font-size: 10px;
                         font-weight: 700;
                         letter-spacing: 0.08em;
                         text-transform: uppercase;
                     }
                  
                     #<%= resolvedWidgetId %> .call-meta {
                         min-width: 92px;
                         text-align: right;
                     }
                  
                     #<%= resolvedWidgetId %> .call-time {
                         margin-bottom: 4px;
                         font-size: 13px;
                         font-weight: 700;
                     }
                  
                     #<%= resolvedWidgetId %> .call-date {
                         color: var(--call-muted);
                         font-size: 11px;
                     }
                  
                     #<%= resolvedWidgetId %> .call-duration {
                         display: inline-flex;
                         align-items: center;
                         gap: 5px;
                         margin-top: 7px;
                         padding: 4px 7px;
                         border-radius: 7px;
                         background: var(--call-duration-bg);
                         color: var(--call-muted);
                         font-size: 11px;
                     }
                  
                     #<%= resolvedWidgetId %> .call-duration::before {
                         content: "◷";
                         font-size: 12px;
                     }
                  
                     #<%= resolvedWidgetId %> .call-status {
                         display: inline-block;
                         margin-top: 6px;
                         font-size: 10px;
                         font-weight: 700;
                     }
                  
                     #<%= resolvedWidgetId %> .incoming .call-status {
                         color: var(--call-incoming);
                     }
                  
                     #<%= resolvedWidgetId %> .outgoing .call-status {
                         color: var(--call-outgoing);
                     }
                  
                     #<%= resolvedWidgetId %> .unknown .call-status {
                         color: var(--call-unknown);
                     }
                  
                     #<%= resolvedWidgetId %> .call-empty {
                         min-height: 180px;
                         height: 100%;
                         display: grid;
                         place-items: center;
                         padding: 30px;
                         text-align: center;
                     }
                  
                     #<%= resolvedWidgetId %> .call-empty-icon {
                         margin-bottom: 12px;
                         font-size: 36px;
                         opacity: 0.7;
                     }
                  
                     #<%= resolvedWidgetId %> .call-empty-title {
                         margin-bottom: 6px;
                         font-size: 15px;
                         font-weight: 700;
                     }
                  
                     #<%= resolvedWidgetId %> .call-empty-text {
                         max-width: 320px;
                         color: var(--call-muted);
                         font-size: 12px;
                         line-height: 1.5;
                     }
                  
                     @media (max-width: 650px) {
                         #<%= resolvedWidgetId %> .call-header {
                             padding: 14px 15px 12px;
                         }
                  
                         #<%= resolvedWidgetId %> .call-list {
                             padding: 8px;
                         }
                  
                         #<%= resolvedWidgetId %> .call-card {
                             grid-template-columns: 45px minmax(0, 1fr) auto;
                             gap: 11px;
                             padding: 12px;
                         }
                  
                         #<%= resolvedWidgetId %> .call-target {
                             display: none;
                         }
                  
                         #<%= resolvedWidgetId %> .call-meta {
                             min-width: 70px;
                         }
                  
                         #<%= resolvedWidgetId %> .call-status {
                             display: none;
                         }
                     }
                  
                     @media (max-width: 420px) {
                         #<%= resolvedWidgetId %> .call-subtitle {
                             display: none;
                         }
                  
                         #<%= resolvedWidgetId %> .call-card {
                             grid-template-columns: 42px minmax(0, 1fr);
                         }
                  
                         #<%= resolvedWidgetId %> .call-meta {
                             grid-column: 2;
                             display: flex;
                             align-items: center;
                             gap: 8px;
                             margin-top: -2px;
                             text-align: left;
                         }
                  
                         #<%= resolvedWidgetId %> .call-time,
                         #<%= resolvedWidgetId %> .call-date,
                         #<%= resolvedWidgetId %> .call-duration {
                             margin: 0;
                         }
                  
                         #<%= resolvedWidgetId %> .call-date {
                             display: none;
                         }
                     }
                  </style>
                  
                  <div class="call-widget">
                     <div class="call-header">
                         <div class="call-header-title">
                             <div class="call-header-icon">☎</div>
                  
                             <div>
                                 <h2 class="call-title">Anrufliste</h2>
                                 <div class="call-subtitle">Letzte Verbindungen</div>
                             </div>
                         </div>
                  
                         <div class="call-count">
                             <%= calls.length %>
                         </div>
                     </div>
                  
                     <div class="call-list">
                         <% if (calls.length === 0) { %>
                             <div class="call-empty">
                                 <div>
                                     <div class="call-empty-icon">
                                         <%= errorMessage ? "⚠" : "☎" %>
                                     </div>
                  
                                     <div class="call-empty-title">
                                         <%= errorMessage ? "Datenfehler" : "Keine Anrufe" %>
                                     </div>
                  
                                     <div class="call-empty-text">
                                         <%= errorMessage || "Aktuell sind keine Einträge in der Anrufliste vorhanden." %>
                                     </div>
                                 </div>
                             </div>
                         <% } else { %>
                             <% calls.forEach(function(call) { %>
                                 <%
                                     var direction = getDirection(call);
                                     var dateParts = getDateParts(call.date);
                                     var displayName = safe(call.name, "Unbekannter Teilnehmer");
                                     var displayNumber = safe(
                                         call.external || call.caller,
                                         "Keine Rufnummer"
                                     );
                                     var target = safe(
                                         call.callednumber || call.called,
                                         "–"
                                     );
                                 %>
                  
                                 <div class="call-card <%= direction.css %>">
                                     <div class="call-avatar">
                                         <span><%= getInitials(displayName) %></span>
                  
                                         <span
                                             class="call-direction-icon"
                                             title="<%= direction.label %>"
                                         >
                                             <%= direction.icon %>
                                         </span>
                                     </div>
                  
                                     <div class="call-person">
                                         <div
                                             class="call-name"
                                             title="<%= displayName %>"
                                         >
                                             <%= displayName %>
                                         </div>
                  
                                         <div
                                             class="call-number"
                                             title="<%= displayNumber %>"
                                         >
                                             <%= displayNumber %>
                                         </div>
                  
                                         <% if (call.device) { %>
                                             <div
                                                 class="call-device"
                                                 title="<%= safe(call.device) %>"
                                             >
                                                 <%= safe(call.device) %>
                                                 <% if (call.port) { %>
                                                     · Port <%= safe(call.port) %>
                                                 <% } %>
                                             </div>
                                         <% } %>
                                     </div>
                  
                                     <div class="call-target">
                                         <div class="call-target-label">Ziel</div>
                  
                                         <div
                                             class="call-target-value"
                                             title="<%= target %>"
                                         >
                                             <%= target %>
                                         </div>
                  
                                         <div class="call-status">
                                             <%= direction.label %>
                                         </div>
                                     </div>
                  
                                     <div class="call-meta">
                                         <div class="call-time">
                                             <%= dateParts.time || "–" %>
                                         </div>
                  
                                         <div class="call-date">
                                             <%= dateParts.date %>
                                         </div>
                  
                                         <div class="call-duration">
                                             <%= safe(call.duration, "0:00") %>
                                         </div>
                                     </div>
                                 </div>
                             <% }); %>
                         <% } %>
                     </div>
                  </div>
                  

                  367a9fc0-5c6e-408e-837b-31beda30c36b-image.jpeg

                  je nach vorlieben und länge des wiedgets noch unter css common bei overflow-y auto einstellen. das sorgt dann dafür, das der inhalt auf die größe des widgets begrenzt wird und scrollbalken an der seite erscheinen, falls der inhalt über die größe hinaus geht

                  1b557ce1-0024-408e-980d-8b63dcec2433-image.jpeg

                  so sieht dann das ergebnis aus.
                  dark/dunkel darstellung
                  b8e68ef9-2c55-4483-8e3f-481bd92a08dc-image.jpeg

                  light/hell darstellung
                  c0356eca-28b9-4c50-9c9f-bb1c22d8c74a-image.jpeg
                  Habe gerade festgestellt, das es unter vis1 noch ein problem gibt. muss ich noch nachschauen
                  -> verdammt. eine inkonsistenz zwischen vis1/2 im eigenen adapter. habe das template oben mit einem workaround korrigiert

                  das beispiel wurde als usecase in der adapterdokumentation von vis-jsontemplate hinterlegt
                  https://github.com/oweitman/ioBroker.vis-jsontemplate/blob/main/documentation/usecase-fritzbox-call-list.md

                  als anmerkung: das template hat mir die ki zu 99% (bis auf den workaround) als oneshot bereitgestellt. werde auch mal schauen ob man das noch auf helle darstellung erweitern kann.

                  OliverIOO Offline
                  OliverIOO Offline
                  OliverIO
                  schrieb am zuletzt editiert von OliverIO
                  #9

                  im obigen template wurde jetzt noch die hell/dunkel light/dark einstellung ergänzt. ausserdem können nun alle farben individuell konfiguriert werden

                  /* Darstellung auswählen: "dark" oder "light" */
                  var themeMode = "light";
                  
                  /*
                   * Hier können alle verwendeten Farben angepasst werden.
                   * Gültig sind z. B. #ffffff, rgb(...), rgba(...), transparent.
                   */
                  var themes = {
                      dark: {
                          background: "rgba(27, 31, 39, 0.96)",
                          cardBackground: "rgba(255, 255, 255, 0.055)",
                          cardHoverBackground: "rgba(255, 255, 255, 0.09)",
                          border: "rgba(255, 255, 255, 0.09)",
                          hoverBorder: "rgba(255, 255, 255, 0.15)",
                          text: "#f4f6f8",
                          mutedText: "#9ba6b2",
                          incoming: "#43c982",
                          outgoing: "#5fa8ff",
                          unknown: "#a9b0b8",
                          iconText: "#ffffff",
                          headerIconBackground: "rgba(95, 168, 255, 0.14)",
                          countBackground: "rgba(255, 255, 255, 0.06)",
                          avatarBackground: "rgba(255, 255, 255, 0.07)",
                          durationBackground: "rgba(255, 255, 255, 0.055)",
                          scrollbarThumb: "rgba(255, 255, 255, 0.22)",
                          scrollbarTrack: "transparent",
                          shadow: "rgba(0, 0, 0, 0.24)"
                      },
                  
                      light: {
                          background: "rgba(248, 250, 252, 0.98)",
                          cardBackground: "#ffffff",
                          cardHoverBackground: "#f1f5f9",
                          border: "rgba(15, 23, 42, 0.12)",
                          hoverBorder: "rgba(15, 23, 42, 0.22)",
                          text: "#172033",
                          mutedText: "#667085",
                          incoming: "#168653",
                          outgoing: "#2563c9",
                          unknown: "#6b7280",
                          iconText: "#ffffff",
                          headerIconBackground: "rgba(37, 99, 201, 0.12)",
                          countBackground: "rgba(15, 23, 42, 0.055)",
                          avatarBackground: "rgba(15, 23, 42, 0.055)",
                          durationBackground: "rgba(15, 23, 42, 0.055)",
                          scrollbarThumb: "rgba(15, 23, 42, 0.25)",
                          scrollbarTrack: "transparent",
                          shadow: "rgba(15, 23, 42, 0.16)"
                      }
                  };
                  

                  Meine Adapter und Widgets
                  TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                  Links im Profil

                  1 Antwort Letzte Antwort
                  0
                  • skvarelS Online
                    skvarelS Online
                    skvarel
                    Developer
                    schrieb am zuletzt editiert von skvarel
                    #10

                    inventwo json Widget, direkt mit der dem tr-064.0.calllists.missed.json Datenpunk

                    (Nummern nur für den Screenhot abgedeckt)
                    c7017f67-d1b2-4f98-9c5b-501b821b571a-image.jpeg

                    (Nummern nur für den Screenhot abgedeckt)
                    931f4219-3df9-4eaf-a2d0-b8fc8b302384-image.jpeg

                    #TeamInventwo
                    Unsere Adapter:
                    Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                    Wer uns mit einem Kaffee unterstützen möchte: PayPal

                    Holger EtzelH 1 Antwort Letzte Antwort
                    0
                    • Holger EtzelH Online
                      Holger EtzelH Online
                      Holger Etzel
                      schrieb am zuletzt editiert von
                      #11

                      @skvarel

                      wo finde ich das Widget in der VIS-2

                      skvarelS 1 Antwort Letzte Antwort
                      0
                      • Holger EtzelH Holger Etzel

                        @skvarel

                        wo finde ich das Widget in der VIS-2

                        skvarelS Online
                        skvarelS Online
                        skvarel
                        Developer
                        schrieb am zuletzt editiert von
                        #12

                        @Holger-Etzel

                        4d1afc8e-077c-4a47-bd49-001d1c21619c-image.jpeg

                        #TeamInventwo
                        Unsere Adapter:
                        Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                        Wer uns mit einem Kaffee unterstützen möchte: PayPal

                        1 Antwort Letzte Antwort
                        0
                        • Holger EtzelH Online
                          Holger EtzelH Online
                          Holger Etzel
                          schrieb am zuletzt editiert von
                          #13

                          @skvarel

                          und wo finde ich das Widget auf deiner Startseite?

                          1 Antwort Letzte Antwort
                          0
                          • Ro75R Online
                            Ro75R Online
                            Ro75
                            schrieb am zuletzt editiert von
                            #14

                            @skvarel @oliverio bekommt ihr das mit euren Widgets auch mit Bildern hin?
                            Unbenannt_anonymized.png

                            Ro75.

                            SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

                            skvarelS Holger EtzelH OliverIOO 3 Antworten Letzte Antwort
                            0
                            • Ro75R Ro75

                              @skvarel @oliverio bekommt ihr das mit euren Widgets auch mit Bildern hin?
                              Unbenannt_anonymized.png

                              Ro75.

                              skvarelS Online
                              skvarelS Online
                              skvarel
                              Developer
                              schrieb am zuletzt editiert von
                              #15

                              @Ro75 .. ja, wenn die Bildurl im json steht

                              74693ea3-3d0f-44c9-bdb8-a41d449b51c1-image.jpeg

                              #TeamInventwo
                              Unsere Adapter:
                              Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                              Wer uns mit einem Kaffee unterstützen möchte: PayPal

                              1 Antwort Letzte Antwort
                              1
                              • Ro75R Ro75

                                @skvarel @oliverio bekommt ihr das mit euren Widgets auch mit Bildern hin?
                                Unbenannt_anonymized.png

                                Ro75.

                                Holger EtzelH Online
                                Holger EtzelH Online
                                Holger Etzel
                                schrieb am zuletzt editiert von
                                #16

                                @Ro75

                                genauso suche ich das, kannst du das bitte veröffentlichen?

                                Ro75R 1 Antwort Letzte Antwort
                                0
                                • Ro75R Ro75

                                  @skvarel @oliverio bekommt ihr das mit euren Widgets auch mit Bildern hin?
                                  Unbenannt_anonymized.png

                                  Ro75.

                                  OliverIOO Offline
                                  OliverIOO Offline
                                  OliverIO
                                  schrieb am zuletzt editiert von
                                  #17

                                  @Ro75 sagte:

                                  bekommt ihr das mit euren Widgets auch mit Bildern hin?

                                  wenn die bildchen hinterlegt sind, kann man das auf die nummern mappen

                                  Meine Adapter und Widgets
                                  TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                                  Links im Profil

                                  1 Antwort Letzte Antwort
                                  1
                                  • Holger EtzelH Holger Etzel

                                    @Ro75

                                    genauso suche ich das, kannst du das bitte veröffentlichen?

                                    Ro75R Online
                                    Ro75R Online
                                    Ro75
                                    schrieb am zuletzt editiert von
                                    #18

                                    @Holger-Etzel Das Problem an der Stelle ist, ich mache das ohne den Adapter. Ich habe mein eigenes tr-064 Skript und keine Adapter mehr, weil der Adapter mir zu wenig Infos gab.

                                    Im Grunde kann ich auch alles, aber eben mehr. Alle Anruflisten, alle Telefonbücher, alle Fritzbox-Infos.

                                    Deswegen wird dir das so nichts nützen - ich muss mal schauen ob ich das Projekt mal veröffentliche.
                                    Unbenannt_anonymized.png

                                    Unbenannt2_anonymized.png

                                    Ro75.

                                    SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

                                    1 Antwort Letzte Antwort
                                    0
                                    • skvarelS skvarel

                                      inventwo json Widget, direkt mit der dem tr-064.0.calllists.missed.json Datenpunk

                                      (Nummern nur für den Screenhot abgedeckt)
                                      c7017f67-d1b2-4f98-9c5b-501b821b571a-image.jpeg

                                      (Nummern nur für den Screenhot abgedeckt)
                                      931f4219-3df9-4eaf-a2d0-b8fc8b302384-image.jpeg

                                      Holger EtzelH Online
                                      Holger EtzelH Online
                                      Holger Etzel
                                      schrieb am zuletzt editiert von
                                      #19

                                      @skvarel
                                      wie hast du eigentlich dein Energiefluss-Widget gemacht, sieht gut aus.

                                      skvarelS 1 Antwort Letzte Antwort
                                      0
                                      • Ro75R Online
                                        Ro75R Online
                                        Ro75
                                        schrieb am zuletzt editiert von
                                        #20

                                        Das Script holt die Daten aus der Fritzbox, bereitet das JSON noch etwas auf und Grafana stellt das Diagramm mit dem Infinity-Plugin dar. Bei mir wird in Grafana nur noch darüber gearbeitet.

                                        Ro75.

                                        SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

                                        1 Antwort Letzte Antwort
                                        0
                                        • Holger EtzelH Holger Etzel

                                          @skvarel
                                          wie hast du eigentlich dein Energiefluss-Widget gemacht, sieht gut aus.

                                          skvarelS Online
                                          skvarelS Online
                                          skvarel
                                          Developer
                                          schrieb am zuletzt editiert von
                                          #21

                                          @Holger-Etzel .. das kommt von dem 'ernergiefluss-erweitert' Adapter

                                          Auf meiner Startseite als Vorschau und per Popup dann nochmal in groß und ausführlicher.

                                          b4247c0f-3f80-4daa-b824-b4131c83a5ec-image.jpeg

                                          #TeamInventwo
                                          Unsere Adapter:
                                          Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                                          Wer uns mit einem Kaffee unterstützen möchte: PayPal

                                          Holger EtzelH 1 Antwort Letzte Antwort
                                          1

                                          Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.

                                          Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.

                                          Mit deinem Input könnte dieser Beitrag noch besser werden 💗

                                          Registrieren Anmelden
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          197

                                          Online

                                          33.0k

                                          Benutzer

                                          83.4k

                                          Themen

                                          1.3m

                                          Beiträge
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2026
                                          logo
                                          • Anmelden

                                          • Du hast noch kein Konto? Registrieren

                                          • Anmelden oder registrieren, um zu suchen
                                          • Erster Beitrag
                                            Letzter Beitrag
                                          0
                                          • Home
                                          • Aktuell
                                          • Tags
                                          • Ungelesen 0
                                          • Kategorien
                                          • Unreplied
                                          • Beliebt
                                          • GitHub
                                          • Docu
                                          • Hilfe