<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://tippvomtibb.de/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Chris+T.+Ludwig</id>
	<title>TippvomTibb - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://tippvomtibb.de/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Chris+T.+Ludwig"/>
	<link rel="alternate" type="text/html" href="https://tippvomtibb.de/mediawiki/Spezial:Beitr%C3%A4ge/Chris_T._Ludwig"/>
	<updated>2026-05-02T08:18:04Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.44.0</generator>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4891</id>
		<title>(FHEM) FTUI 3 Components</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4891"/>
		<updated>2026-05-01T07:20:43Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* element.components.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=grid.components.js=&lt;br /&gt;
&lt;br /&gt;
Irgendwie wird die Flaeche durch eine falsche Grid-Anzahl nicht voll ausgenutzt.&lt;br /&gt;
Zum Testen habe ich in configureGrid() mal folgende Zeilen eingefuegt.&lt;br /&gt;
Durch (col-1) wurde der Fehler behoben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    cols = (this.cols &amp;gt; 0) ? this.cols : highestCol;&lt;br /&gt;
    rows = (this.rows &amp;gt; 0) ? this.rows : highestRow;&lt;br /&gt;
    console.log(&amp;quot;Cols: &amp;quot;+cols+&amp;quot; highest: &amp;quot;+highestCol);&lt;br /&gt;
    console.log(&amp;quot;Rows: &amp;quot;+rows+&amp;quot; highest: &amp;quot;+highestRow);&lt;br /&gt;
    baseWidth = (this.baseWidth &amp;gt; 0) ? this.baseWidth : (this.clientWidth - this.margin) / (cols-1);&lt;br /&gt;
    baseHeight = (this.baseHeight &amp;gt; 0) ? this.baseHeight : (this.clientHeight - this.margin) / rows;&lt;br /&gt;
    console.log(&amp;quot;baseWidth: &amp;quot;+baseWidth+&amp;quot; calc: &amp;quot;+this.baseWidth+&amp;quot;/&amp;quot;+this.clientWidth+&amp;quot;/&amp;quot;+this.margin);&lt;br /&gt;
    console.log(&amp;quot;baseHeight: &amp;quot;+baseHeight+&amp;quot; calc: &amp;quot;+this.baseHeight+&amp;quot;/&amp;quot;+this.clientHeight+&amp;quot;/&amp;quot;+this.margin);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=element.components.js=&lt;br /&gt;
&lt;br /&gt;
Deklaration der &#039;&#039;&#039;Basisklasse&#039;&#039;&#039; für alle FTUI-v3-Webcomponents. Jede Komponente wie `ftui-button`, `ftui-label`, `ftui-icon` usw. erbt vermutlich von `FtuiElement`.&lt;br /&gt;
&lt;br /&gt;
Sie kuemmert sich um:&lt;br /&gt;
&lt;br /&gt;
* automatische IDs&lt;br /&gt;
* Standardattribute wie `hidden`, `disabled`, `readonly`, `margin`, `padding`&lt;br /&gt;
* Verbindung zwischen Attributen und JavaScript-Properties&lt;br /&gt;
* Shadow DOM&lt;br /&gt;
* Change-Events für FTUI-Bindings&lt;br /&gt;
* Hooks für abgeleitete Komponenten&lt;br /&gt;
&lt;br /&gt;
==Kurzueberblick==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class FtuiElement extends HTMLElement&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`FtuiElement` erweitert den nativen Browser-Typ `HTMLElement`. Dadurch wird daraus die gemeinsame Basis für eigene HTML-Tags wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Import==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden drei Hilfsfunktionen verwendet:&lt;br /&gt;
* isNumeric()   prüft, ob ein Wert numerisch ist&lt;br /&gt;
* toKebabCase() wandelt z.B. &amp;quot;baseWidth&amp;quot; in &amp;quot;base-width&amp;quot;&lt;br /&gt;
* log()         schreibt Debug-Ausgaben&lt;br /&gt;
&lt;br /&gt;
==UID-Zähler==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const uids = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bekommt jedes FTUI-Element automatisch eine ID, falls keine gesetzt wurde.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird intern etwa zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label id=&amp;quot;ftui_label_1&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Constructor==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
constructor(properties) {&lt;br /&gt;
  super();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`super()` ruft den Konstruktor von `HTMLElement` auf. Das ist bei Webcomponents Pflicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (!this.id) {&lt;br /&gt;
  ...&lt;br /&gt;
  this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls das Element keine ID hat, wird automatisch eine erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden die Standard-Properties der Basisklasse mit den Properties der konkreten Komponente gemischt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FtuiElement.properties = {&lt;br /&gt;
  hidden: false,&lt;br /&gt;
  disabled: false,&lt;br /&gt;
  readonly: false,&lt;br /&gt;
  margin: &#039;&#039;,&lt;br /&gt;
  padding: &#039;&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein `ftui-button` könnte zusätzlich `value`, `color`, `fill` usw. definieren.&lt;br /&gt;
&lt;br /&gt;
Achtung: `Object.assign(FtuiElement.properties, properties)` verändert das Objekt `FtuiElement.properties`. Sauberer wäre oft:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.assign({}, FtuiElement.properties, properties)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonst können Properties versehentlich global vermischt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.initProperties(this.properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugt für jede Property Getter/Setter und initialisiert die passenden HTML-Attribute.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
  this.createShadowRoot(this.template());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die konkrete Komponente eine Methode `template()` besitzt, wird daraus ein Shadow DOM erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.isActiveChange = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Merker für aktive Änderungen, wichtig für Two-Way-Binding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (window.ftuiApp) {&lt;br /&gt;
  ftuiApp.attachBinding(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die FTUI-App bereits existiert, wird das Element mit dem FTUI-Binding-System verbunden.&lt;br /&gt;
&lt;br /&gt;
==Shadow DOM==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
createShadowRoot(content) {&lt;br /&gt;
  const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
  elemTemplate.innerHTML = content;&lt;br /&gt;
  this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
  this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode baut das interne DOM der Komponente.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template() {&lt;br /&gt;
  return `&amp;lt;button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/button&amp;gt;`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu einem Shadow DOM innerhalb des Elements.&lt;br /&gt;
&lt;br /&gt;
`mode: &#039;open&#039;` bedeutet: Man kann von außen per JavaScript darauf zugreifen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.shadowRoot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Standard-Properties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get properties() {&lt;br /&gt;
  return {&lt;br /&gt;
    hidden: false,&lt;br /&gt;
    disabled: false,&lt;br /&gt;
    readonly: false,&lt;br /&gt;
    margin: &#039;&#039;,&lt;br /&gt;
    padding: &#039;&#039;,&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes FTUI-Element bekommt diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button hidden&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button readonly&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button margin=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button padding=&amp;quot;0.5&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==observedAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get observedAttributes() {&lt;br /&gt;
  return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Browser ruft `attributeChangedCallback()` nur für Attribute auf, die hier stehen.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Hier werden nur die Basiseigenschaften beobachtet. Abgeleitete Komponenten müssen wahrscheinlich selbst `observedAttributes` erweitern, sonst werden ihre eigenen Attribute nicht automatisch beobachtet.&lt;br /&gt;
&lt;br /&gt;
==convertToAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static convertToAttributes(properties) {&lt;br /&gt;
  return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfsfunktion, um Property-Namen in HTML-Attributnamen umzuwandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
baseWidth&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
base-width&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==connectedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
connectedCallback() {&lt;br /&gt;
  this.updateProperties();&lt;br /&gt;
  if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
    this.onConnected();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode wird automatisch vom Browser aufgerufen, wenn das Element in die Seite eingefügt wird.&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
&lt;br /&gt;
1. Standardwerte werden verarbeitet.&lt;br /&gt;
&lt;br /&gt;
2. Falls die konkrete Komponente `onConnected()` definiert, wird diese Hook-Funktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel in einer Kindklasse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onConnected() {&lt;br /&gt;
  console.log(&#039;Button ist im DOM&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==attributeChangedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird aufgerufen, wenn ein beobachtetes Attribut geändert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(3, `${this.id} -  attributeChangedCallback ...`)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Debug-Ausgabe.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
  this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hook für Kindklassen.&lt;br /&gt;
&lt;br /&gt;
Danach behandelt die Basisklasse ihre Standardattribute:&lt;br /&gt;
&lt;br /&gt;
#==hidden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;hidden&#039;:&lt;br /&gt;
  this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn `hidden` gesetzt ist, wird das Element ausgeblendet.&lt;br /&gt;
&lt;br /&gt;
#==disabled&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;disabled&#039;:&lt;br /&gt;
  this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optisch ausgegraut und nicht anklickbar.&lt;br /&gt;
&lt;br /&gt;
#==readonly&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;readonly&#039;:&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht bedienbar, aber ohne optischen Filter.&lt;br /&gt;
&lt;br /&gt;
#==margin&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;margin&#039;:&lt;br /&gt;
  if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
    this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 1em;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn nicht numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;10px&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bleibt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 10px;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `FTUI-GRID` wird `margin` hier nicht gesetzt, vermutlich weil `ftui-grid` sein eigenes Margin-Verhalten hat.&lt;br /&gt;
&lt;br /&gt;
#==padding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;padding&#039;:&lt;br /&gt;
  this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu `margin`.&lt;br /&gt;
&lt;br /&gt;
==submitChange==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
submitChange(property, value) {&lt;br /&gt;
  this.isActiveChange[property] = true;&lt;br /&gt;
  this[property] = value;&lt;br /&gt;
  this.emitChangeEvent(property, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist wichtig für FTUI-Bindings.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Komponente intern einen Wert ändert, etwa ein Button oder Slider, ruft sie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.submitChange(&#039;value&#039;, newValue);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann passiert:&lt;br /&gt;
&lt;br /&gt;
1. Änderung wird als aktiv markiert.&lt;br /&gt;
2. Property wird gesetzt.&lt;br /&gt;
3. Ein Event `valueChange` wird ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Dieses Event kann FTUI dann nutzen, um per `(value)` oder `[(value)]` etwas an FHEM zu senden.&lt;br /&gt;
&lt;br /&gt;
==emitChangeEvent und emitEvent==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitChangeEvent(attribute, value) {&lt;br /&gt;
  this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus `value` wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
valueChange&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitEvent(name, value) {&lt;br /&gt;
  const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
  this.dispatchEvent(event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird ein normales DOM-Event ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.emitChangeEvent(&#039;value&#039;, &#039;on&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
new CustomEvent(&#039;valueChange&#039;, { detail: &#039;on&#039; })&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initProperties(properties) {&lt;br /&gt;
  Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für jede Property wird automatisch ein passender Getter/Setter erzeugt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden: false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und HTML-Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#==Boolean&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
  this.defineBooleanProperty(name, attr);&lt;br /&gt;
  this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute funktionieren nach HTML-Logik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bedeutet `true`.&lt;br /&gt;
&lt;br /&gt;
#==Number&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
  this.defineNumberProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird beim Lesen in `Number(...)` umgewandelt.&lt;br /&gt;
&lt;br /&gt;
#==String&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else {&lt;br /&gt;
  this.defineStringProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale String-Attribute.&lt;br /&gt;
&lt;br /&gt;
==initAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr)) {&lt;br /&gt;
    this.setAttribute(attr, value);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Attribut noch nicht im HTML steht, wird der Default gesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding: &#039;&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
führt zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initBooleanAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initBooleanAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
    this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute werden nur gesetzt, wenn der Default `true` ist.&lt;br /&gt;
&lt;br /&gt;
Da `hidden`, `disabled`, `readonly` alle `false` sind, werden sie standardmäßig nicht gesetzt.&lt;br /&gt;
&lt;br /&gt;
==defineBooleanProperty==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.defineProperty(this, name, {&lt;br /&gt;
  get() {&lt;br /&gt;
    return this.hasAttribute(attr)&lt;br /&gt;
      &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
  },&lt;br /&gt;
  set(value) {&lt;br /&gt;
    if (value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    } else {&lt;br /&gt;
      this.removeAttribute(attr);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und bekommt im HTML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = false;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird das Attribut entfernt.&lt;br /&gt;
&lt;br /&gt;
Interessant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disabled=&amp;quot;false&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird als `false` interpretiert. Das ist etwas komfortabler als natives HTML, wo ein vorhandenes Boolean-Attribut normalerweise immer wahr ist.&lt;br /&gt;
&lt;br /&gt;
==defineNumberProperty==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return Number(this.getAttribute(attr)); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.row&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert Zahl `2`.&lt;br /&gt;
&lt;br /&gt;
==defineStringProperty==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return this.getAttribute(attr); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale Abbildung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.text = &#039;Hallo&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
text=&amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==updateProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updateProperties() {&lt;br /&gt;
  Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
    const attr = toKebabCase(name);&lt;br /&gt;
    if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
      this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
    }&lt;br /&gt;
  })&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einfügen ins DOM werden Standardattribute nochmal verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Warum?&lt;br /&gt;
Wenn ein Attribut schon beim Erzeugen gesetzt wurde, kann es sein, dass die Style-Logik noch nicht gelaufen ist. `updateProperties()` erzwingt deshalb die Behandlung der Defaultwerte.&lt;br /&gt;
&lt;br /&gt;
==Kommentierte Version==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&lt;br /&gt;
// Zähler für automatisch erzeugte IDs pro Elementtyp&lt;br /&gt;
const uids = {};&lt;br /&gt;
&lt;br /&gt;
// Basisklasse aller FTUI-Webcomponents&lt;br /&gt;
export class FtuiElement extends HTMLElement {&lt;br /&gt;
&lt;br /&gt;
  constructor(properties) {&lt;br /&gt;
    super();&lt;br /&gt;
&lt;br /&gt;
    // Falls keine ID vorhanden ist, automatisch eine eindeutige ID erzeugen&lt;br /&gt;
    if (!this.id) {&lt;br /&gt;
      if (!uids[this.localName]) {&lt;br /&gt;
        uids[this.localName] = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Standardproperties der Basisklasse mit Komponentenproperties kombinieren&lt;br /&gt;
    this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&lt;br /&gt;
    // Für alle Properties Getter/Setter und Default-Attribute anlegen&lt;br /&gt;
    this.initProperties(this.properties);&lt;br /&gt;
&lt;br /&gt;
    // Wenn die Komponente ein Template besitzt, Shadow DOM erzeugen&lt;br /&gt;
    if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
      this.createShadowRoot(this.template());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Merker für aktive Änderungen, wichtig für Output-/Two-Way-Bindings&lt;br /&gt;
    this.isActiveChange = {};&lt;br /&gt;
&lt;br /&gt;
    // Element beim globalen FTUI-Binding-System anmelden&lt;br /&gt;
    if (window.ftuiApp) {&lt;br /&gt;
      ftuiApp.attachBinding(this);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt Shadow DOM aus einem HTML-Template-String&lt;br /&gt;
  createShadowRoot(content) {&lt;br /&gt;
    const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
    elemTemplate.innerHTML = content;&lt;br /&gt;
    this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
    this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Standardattribute, die jedes FTUI-Element besitzt&lt;br /&gt;
  static get properties() {&lt;br /&gt;
    return {&lt;br /&gt;
      hidden: false,&lt;br /&gt;
      disabled: false,&lt;br /&gt;
      readonly: false,&lt;br /&gt;
      margin: &#039;&#039;,&lt;br /&gt;
      padding: &#039;&#039;,&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Attribute, bei deren Änderung attributeChangedCallback ausgelöst wird&lt;br /&gt;
  static get observedAttributes() {&lt;br /&gt;
    return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wandelt Property-Namen in HTML-Attributnamen um&lt;br /&gt;
  static convertToAttributes(properties) {&lt;br /&gt;
    return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird vom Browser aufgerufen, wenn das Element in den DOM eingefügt wurde&lt;br /&gt;
  connectedCallback() {&lt;br /&gt;
    this.updateProperties();&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
      this.onConnected();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird aufgerufen, wenn sich ein beobachtetes Attribut ändert&lt;br /&gt;
  attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
    log(3, `${this.id} -  attributeChangedCallback name=${name}, oldValue=${oldValue}, newValue=${newValue}`);&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
      this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const hasValue = newValue !== null &amp;amp;&amp;amp; newValue !== false;&lt;br /&gt;
&lt;br /&gt;
    // Standardattribute behandeln&lt;br /&gt;
    switch (name) {&lt;br /&gt;
      case &#039;hidden&#039;:&lt;br /&gt;
        this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;disabled&#039;:&lt;br /&gt;
        this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;readonly&#039;:&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;margin&#039;: {&lt;br /&gt;
        // ftui-grid behandelt margin selbst&lt;br /&gt;
        if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
          this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      case &#039;padding&#039;: {&lt;br /&gt;
        this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird von Komponenten genutzt, wenn ein Benutzerwert geändert wurde&lt;br /&gt;
  submitChange(property, value) {&lt;br /&gt;
    this.isActiveChange[property] = true;&lt;br /&gt;
    this[property] = value;&lt;br /&gt;
    this.emitChangeEvent(property, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt z.B. valueChange aus value&lt;br /&gt;
  emitChangeEvent(attribute, value) {&lt;br /&gt;
    this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Löst ein DOM CustomEvent aus&lt;br /&gt;
  emitEvent(name, value) {&lt;br /&gt;
    const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
    this.dispatchEvent(event);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisiert Properties abhängig vom Typ des Defaultwerts&lt;br /&gt;
  initProperties(properties) {&lt;br /&gt;
    Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
        this.defineBooleanProperty(name, attr);&lt;br /&gt;
        this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
        this.defineNumberProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else {&lt;br /&gt;
        this.defineStringProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Default-Attribut, wenn es noch nicht existiert&lt;br /&gt;
  initAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr)) {&lt;br /&gt;
      this.setAttribute(attr, value);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Boolean-Attribut nur, wenn Default true ist&lt;br /&gt;
  initBooleanAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Boolean-Property mit HTML-Attribut-Synchronisierung&lt;br /&gt;
  defineBooleanProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.hasAttribute(attr)&lt;br /&gt;
          &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        if (value) {&lt;br /&gt;
          this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
        } else {&lt;br /&gt;
          this.removeAttribute(attr);&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Number-Property&lt;br /&gt;
  defineNumberProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return Number(this.getAttribute(attr));&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert String-Property&lt;br /&gt;
  defineStringProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.getAttribute(attr);&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Behandelt Defaultwerte beim Einfügen des Elements in den DOM&lt;br /&gt;
  updateProperties() {&lt;br /&gt;
    Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
        this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mögliche Schwachstellen / Besonderheiten==&lt;br /&gt;
&lt;br /&gt;
1. **`Object.assign(FtuiElement.properties, properties)` mutiert die statischen Basiseigenschaften.**&lt;br /&gt;
   Sicherer wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign({}, FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. **`observedAttributes` enthält nur Basiseigenschaften.**&lt;br /&gt;
   Kindklassen müssen eigene Attribute selbst hinzufügen.&lt;br /&gt;
&lt;br /&gt;
3. **Boolean-Erkennung `newValue !== false` ist etwas ungenau.**&lt;br /&gt;
   Attribute liefern normalerweise Strings oder `null`, nicht echtes `false`. Bei `disabled=&amp;quot;false&amp;quot;` funktioniert der Getter zwar korrekt, aber `attributeChangedCallback()` behandelt `&amp;quot;false&amp;quot;` trotzdem als vorhanden.&lt;br /&gt;
&lt;br /&gt;
4. **`initAttribute()` setzt auch leere Default-Strings als Attribute.**&lt;br /&gt;
   Dadurch entstehen Attribute wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;&amp;quot;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist nicht falsch, aber kann unnötige `attributeChangedCallback()`-Aufrufe erzeugen.&lt;br /&gt;
&lt;br /&gt;
5. **`createShadowRoot()` nutzt `innerHTML`.**&lt;br /&gt;
   Das ist normal für Templates, aber Template-Inhalte sollten kontrolliert sein.&lt;br /&gt;
&lt;br /&gt;
==Merksatz==&lt;br /&gt;
&lt;br /&gt;
Dieses Programm macht aus einer normalen Webcomponent eine **FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events**.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4890</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4890"/>
		<updated>2026-05-01T07:16:25Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Formatierung/Layout */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── element.components.js       Macht aus einer normalen Webcomponent eine FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events.&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── (insgesamt 37 siehe unten)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
Es gibt insgesamt 37 Komponenten:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
badge&lt;br /&gt;
button&lt;br /&gt;
cell&lt;br /&gt;
chart&lt;br /&gt;
checkbox&lt;br /&gt;
circlemenu&lt;br /&gt;
clock&lt;br /&gt;
colorpicker&lt;br /&gt;
column&lt;br /&gt;
content&lt;br /&gt;
departure&lt;br /&gt;
dropdown&lt;br /&gt;
grid&lt;br /&gt;
icon&lt;br /&gt;
image&lt;br /&gt;
input&lt;br /&gt;
knob&lt;br /&gt;
label&lt;br /&gt;
main&lt;br /&gt;
map&lt;br /&gt;
medialist&lt;br /&gt;
menu&lt;br /&gt;
meter&lt;br /&gt;
nav&lt;br /&gt;
popup&lt;br /&gt;
rotor&lt;br /&gt;
row&lt;br /&gt;
segment&lt;br /&gt;
slider&lt;br /&gt;
solar&lt;br /&gt;
speak&lt;br /&gt;
swiper&lt;br /&gt;
switch&lt;br /&gt;
tab&lt;br /&gt;
timeset&lt;br /&gt;
view&lt;br /&gt;
weather&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;View&#039;&#039;&#039; ist die Basis fuer:&lt;br /&gt;
* Toolbar&lt;br /&gt;
* Stage&lt;br /&gt;
* Sheet&lt;br /&gt;
* Section&lt;br /&gt;
* Item&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
 Die Breite eines ftui-grid-tile wird im ftui-grid (Parent) berechnet&lt;br /&gt;
 → über base-width, width, margin&lt;br /&gt;
 → und dann als CSS (grid/flex/inline-style) umgesetzt&lt;br /&gt;
&lt;br /&gt;
Die Breite der Grid-Tiles wird irgendwie falsch berechnet. &lt;br /&gt;
Dadurch entsteht faelschlicherweise eine leere Flaeche an der rechten Seite.&lt;br /&gt;
Siehe [[(FHEM) FTUI 3 Components]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4889</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4889"/>
		<updated>2026-05-01T05:39:28Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Formatierung/Layout */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── element.components.js       Macht aus einer normalen Webcomponent eine FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events.&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── (insgesamt 37 siehe unten)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
Es gibt insgesamt 37 Komponenten:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
badge&lt;br /&gt;
button&lt;br /&gt;
cell&lt;br /&gt;
chart&lt;br /&gt;
checkbox&lt;br /&gt;
circlemenu&lt;br /&gt;
clock&lt;br /&gt;
colorpicker&lt;br /&gt;
column&lt;br /&gt;
content&lt;br /&gt;
departure&lt;br /&gt;
dropdown&lt;br /&gt;
grid&lt;br /&gt;
icon&lt;br /&gt;
image&lt;br /&gt;
input&lt;br /&gt;
knob&lt;br /&gt;
label&lt;br /&gt;
main&lt;br /&gt;
map&lt;br /&gt;
medialist&lt;br /&gt;
menu&lt;br /&gt;
meter&lt;br /&gt;
nav&lt;br /&gt;
popup&lt;br /&gt;
rotor&lt;br /&gt;
row&lt;br /&gt;
segment&lt;br /&gt;
slider&lt;br /&gt;
solar&lt;br /&gt;
speak&lt;br /&gt;
swiper&lt;br /&gt;
switch&lt;br /&gt;
tab&lt;br /&gt;
timeset&lt;br /&gt;
view&lt;br /&gt;
weather&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;View&#039;&#039;&#039; ist die Basis fuer:&lt;br /&gt;
* Toolbar&lt;br /&gt;
* Stage&lt;br /&gt;
* Sheet&lt;br /&gt;
* Section&lt;br /&gt;
* Item&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
 Die Breite eines ftui-grid-tile wird im ftui-grid (Parent) berechnet&lt;br /&gt;
 → über base-width, width, margin&lt;br /&gt;
 → und dann als CSS (grid/flex/inline-style) umgesetzt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4888</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4888"/>
		<updated>2026-04-27T16:47:21Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Komponentenprogramme */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── element.components.js       Macht aus einer normalen Webcomponent eine FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events.&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── (insgesamt 37 siehe unten)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
Es gibt insgesamt 37 Komponenten:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
badge&lt;br /&gt;
button&lt;br /&gt;
cell&lt;br /&gt;
chart&lt;br /&gt;
checkbox&lt;br /&gt;
circlemenu&lt;br /&gt;
clock&lt;br /&gt;
colorpicker&lt;br /&gt;
column&lt;br /&gt;
content&lt;br /&gt;
departure&lt;br /&gt;
dropdown&lt;br /&gt;
grid&lt;br /&gt;
icon&lt;br /&gt;
image&lt;br /&gt;
input&lt;br /&gt;
knob&lt;br /&gt;
label&lt;br /&gt;
main&lt;br /&gt;
map&lt;br /&gt;
medialist&lt;br /&gt;
menu&lt;br /&gt;
meter&lt;br /&gt;
nav&lt;br /&gt;
popup&lt;br /&gt;
rotor&lt;br /&gt;
row&lt;br /&gt;
segment&lt;br /&gt;
slider&lt;br /&gt;
solar&lt;br /&gt;
speak&lt;br /&gt;
swiper&lt;br /&gt;
switch&lt;br /&gt;
tab&lt;br /&gt;
timeset&lt;br /&gt;
view&lt;br /&gt;
weather&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;View&#039;&#039;&#039; ist die Basis fuer:&lt;br /&gt;
* Toolbar&lt;br /&gt;
* Stage&lt;br /&gt;
* Sheet&lt;br /&gt;
* Section&lt;br /&gt;
* Item&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4887</id>
		<title>(FHEM) FTUI 3 Components</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4887"/>
		<updated>2026-04-26T15:44:03Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* initBooleanAttribute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=element.components.js=&lt;br /&gt;
&lt;br /&gt;
Deklaration der &#039;&#039;&#039;Basisklasse&#039;&#039;&#039; für alle FTUI-v3-Webcomponents. Jede Komponente wie `ftui-button`, `ftui-label`, `ftui-icon` usw. erbt vermutlich von `FtuiElement`.&lt;br /&gt;
&lt;br /&gt;
Sie kuemmert sich um:&lt;br /&gt;
&lt;br /&gt;
* automatische IDs&lt;br /&gt;
* Standardattribute wie `hidden`, `disabled`, `readonly`, `margin`, `padding`&lt;br /&gt;
* Verbindung zwischen Attributen und JavaScript-Properties&lt;br /&gt;
* Shadow DOM&lt;br /&gt;
* Change-Events für FTUI-Bindings&lt;br /&gt;
* Hooks für abgeleitete Komponenten&lt;br /&gt;
&lt;br /&gt;
==Kurzueberblick==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class FtuiElement extends HTMLElement&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`FtuiElement` erweitert den nativen Browser-Typ `HTMLElement`. Dadurch wird daraus die gemeinsame Basis für eigene HTML-Tags wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Import==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden drei Hilfsfunktionen verwendet:&lt;br /&gt;
* isNumeric()   prüft, ob ein Wert numerisch ist&lt;br /&gt;
* toKebabCase() wandelt z.B. &amp;quot;baseWidth&amp;quot; in &amp;quot;base-width&amp;quot;&lt;br /&gt;
* log()         schreibt Debug-Ausgaben&lt;br /&gt;
&lt;br /&gt;
==UID-Zähler==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const uids = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bekommt jedes FTUI-Element automatisch eine ID, falls keine gesetzt wurde.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird intern etwa zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label id=&amp;quot;ftui_label_1&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Constructor==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
constructor(properties) {&lt;br /&gt;
  super();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`super()` ruft den Konstruktor von `HTMLElement` auf. Das ist bei Webcomponents Pflicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (!this.id) {&lt;br /&gt;
  ...&lt;br /&gt;
  this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls das Element keine ID hat, wird automatisch eine erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden die Standard-Properties der Basisklasse mit den Properties der konkreten Komponente gemischt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FtuiElement.properties = {&lt;br /&gt;
  hidden: false,&lt;br /&gt;
  disabled: false,&lt;br /&gt;
  readonly: false,&lt;br /&gt;
  margin: &#039;&#039;,&lt;br /&gt;
  padding: &#039;&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein `ftui-button` könnte zusätzlich `value`, `color`, `fill` usw. definieren.&lt;br /&gt;
&lt;br /&gt;
Achtung: `Object.assign(FtuiElement.properties, properties)` verändert das Objekt `FtuiElement.properties`. Sauberer wäre oft:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.assign({}, FtuiElement.properties, properties)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonst können Properties versehentlich global vermischt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.initProperties(this.properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugt für jede Property Getter/Setter und initialisiert die passenden HTML-Attribute.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
  this.createShadowRoot(this.template());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die konkrete Komponente eine Methode `template()` besitzt, wird daraus ein Shadow DOM erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.isActiveChange = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Merker für aktive Änderungen, wichtig für Two-Way-Binding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (window.ftuiApp) {&lt;br /&gt;
  ftuiApp.attachBinding(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die FTUI-App bereits existiert, wird das Element mit dem FTUI-Binding-System verbunden.&lt;br /&gt;
&lt;br /&gt;
==Shadow DOM==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
createShadowRoot(content) {&lt;br /&gt;
  const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
  elemTemplate.innerHTML = content;&lt;br /&gt;
  this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
  this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode baut das interne DOM der Komponente.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template() {&lt;br /&gt;
  return `&amp;lt;button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/button&amp;gt;`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu einem Shadow DOM innerhalb des Elements.&lt;br /&gt;
&lt;br /&gt;
`mode: &#039;open&#039;` bedeutet: Man kann von außen per JavaScript darauf zugreifen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.shadowRoot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Standard-Properties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get properties() {&lt;br /&gt;
  return {&lt;br /&gt;
    hidden: false,&lt;br /&gt;
    disabled: false,&lt;br /&gt;
    readonly: false,&lt;br /&gt;
    margin: &#039;&#039;,&lt;br /&gt;
    padding: &#039;&#039;,&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes FTUI-Element bekommt diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button hidden&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button readonly&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button margin=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button padding=&amp;quot;0.5&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==observedAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get observedAttributes() {&lt;br /&gt;
  return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Browser ruft `attributeChangedCallback()` nur für Attribute auf, die hier stehen.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Hier werden nur die Basiseigenschaften beobachtet. Abgeleitete Komponenten müssen wahrscheinlich selbst `observedAttributes` erweitern, sonst werden ihre eigenen Attribute nicht automatisch beobachtet.&lt;br /&gt;
&lt;br /&gt;
==convertToAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static convertToAttributes(properties) {&lt;br /&gt;
  return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfsfunktion, um Property-Namen in HTML-Attributnamen umzuwandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
baseWidth&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
base-width&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==connectedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
connectedCallback() {&lt;br /&gt;
  this.updateProperties();&lt;br /&gt;
  if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
    this.onConnected();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode wird automatisch vom Browser aufgerufen, wenn das Element in die Seite eingefügt wird.&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
&lt;br /&gt;
1. Standardwerte werden verarbeitet.&lt;br /&gt;
&lt;br /&gt;
2. Falls die konkrete Komponente `onConnected()` definiert, wird diese Hook-Funktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel in einer Kindklasse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onConnected() {&lt;br /&gt;
  console.log(&#039;Button ist im DOM&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==attributeChangedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird aufgerufen, wenn ein beobachtetes Attribut geändert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(3, `${this.id} -  attributeChangedCallback ...`)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Debug-Ausgabe.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
  this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hook für Kindklassen.&lt;br /&gt;
&lt;br /&gt;
Danach behandelt die Basisklasse ihre Standardattribute:&lt;br /&gt;
&lt;br /&gt;
#==hidden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;hidden&#039;:&lt;br /&gt;
  this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn `hidden` gesetzt ist, wird das Element ausgeblendet.&lt;br /&gt;
&lt;br /&gt;
#==disabled&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;disabled&#039;:&lt;br /&gt;
  this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optisch ausgegraut und nicht anklickbar.&lt;br /&gt;
&lt;br /&gt;
#==readonly&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;readonly&#039;:&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht bedienbar, aber ohne optischen Filter.&lt;br /&gt;
&lt;br /&gt;
#==margin&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;margin&#039;:&lt;br /&gt;
  if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
    this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 1em;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn nicht numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;10px&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bleibt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 10px;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `FTUI-GRID` wird `margin` hier nicht gesetzt, vermutlich weil `ftui-grid` sein eigenes Margin-Verhalten hat.&lt;br /&gt;
&lt;br /&gt;
#==padding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;padding&#039;:&lt;br /&gt;
  this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu `margin`.&lt;br /&gt;
&lt;br /&gt;
==submitChange==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
submitChange(property, value) {&lt;br /&gt;
  this.isActiveChange[property] = true;&lt;br /&gt;
  this[property] = value;&lt;br /&gt;
  this.emitChangeEvent(property, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist wichtig für FTUI-Bindings.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Komponente intern einen Wert ändert, etwa ein Button oder Slider, ruft sie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.submitChange(&#039;value&#039;, newValue);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann passiert:&lt;br /&gt;
&lt;br /&gt;
1. Änderung wird als aktiv markiert.&lt;br /&gt;
2. Property wird gesetzt.&lt;br /&gt;
3. Ein Event `valueChange` wird ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Dieses Event kann FTUI dann nutzen, um per `(value)` oder `[(value)]` etwas an FHEM zu senden.&lt;br /&gt;
&lt;br /&gt;
==emitChangeEvent und emitEvent==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitChangeEvent(attribute, value) {&lt;br /&gt;
  this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus `value` wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
valueChange&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitEvent(name, value) {&lt;br /&gt;
  const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
  this.dispatchEvent(event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird ein normales DOM-Event ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.emitChangeEvent(&#039;value&#039;, &#039;on&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
new CustomEvent(&#039;valueChange&#039;, { detail: &#039;on&#039; })&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initProperties(properties) {&lt;br /&gt;
  Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für jede Property wird automatisch ein passender Getter/Setter erzeugt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden: false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und HTML-Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#==Boolean&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
  this.defineBooleanProperty(name, attr);&lt;br /&gt;
  this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute funktionieren nach HTML-Logik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bedeutet `true`.&lt;br /&gt;
&lt;br /&gt;
#==Number&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
  this.defineNumberProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird beim Lesen in `Number(...)` umgewandelt.&lt;br /&gt;
&lt;br /&gt;
#==String&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else {&lt;br /&gt;
  this.defineStringProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale String-Attribute.&lt;br /&gt;
&lt;br /&gt;
==initAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr)) {&lt;br /&gt;
    this.setAttribute(attr, value);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Attribut noch nicht im HTML steht, wird der Default gesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding: &#039;&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
führt zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initBooleanAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initBooleanAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
    this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute werden nur gesetzt, wenn der Default `true` ist.&lt;br /&gt;
&lt;br /&gt;
Da `hidden`, `disabled`, `readonly` alle `false` sind, werden sie standardmäßig nicht gesetzt.&lt;br /&gt;
&lt;br /&gt;
==defineBooleanProperty==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.defineProperty(this, name, {&lt;br /&gt;
  get() {&lt;br /&gt;
    return this.hasAttribute(attr)&lt;br /&gt;
      &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
  },&lt;br /&gt;
  set(value) {&lt;br /&gt;
    if (value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    } else {&lt;br /&gt;
      this.removeAttribute(attr);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und bekommt im HTML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = false;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird das Attribut entfernt.&lt;br /&gt;
&lt;br /&gt;
Interessant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disabled=&amp;quot;false&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird als `false` interpretiert. Das ist etwas komfortabler als natives HTML, wo ein vorhandenes Boolean-Attribut normalerweise immer wahr ist.&lt;br /&gt;
&lt;br /&gt;
==defineNumberProperty==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return Number(this.getAttribute(attr)); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.row&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert Zahl `2`.&lt;br /&gt;
&lt;br /&gt;
==defineStringProperty==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return this.getAttribute(attr); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale Abbildung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.text = &#039;Hallo&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
text=&amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==updateProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updateProperties() {&lt;br /&gt;
  Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
    const attr = toKebabCase(name);&lt;br /&gt;
    if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
      this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
    }&lt;br /&gt;
  })&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einfügen ins DOM werden Standardattribute nochmal verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Warum?&lt;br /&gt;
Wenn ein Attribut schon beim Erzeugen gesetzt wurde, kann es sein, dass die Style-Logik noch nicht gelaufen ist. `updateProperties()` erzwingt deshalb die Behandlung der Defaultwerte.&lt;br /&gt;
&lt;br /&gt;
==Kommentierte Version==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&lt;br /&gt;
// Zähler für automatisch erzeugte IDs pro Elementtyp&lt;br /&gt;
const uids = {};&lt;br /&gt;
&lt;br /&gt;
// Basisklasse aller FTUI-Webcomponents&lt;br /&gt;
export class FtuiElement extends HTMLElement {&lt;br /&gt;
&lt;br /&gt;
  constructor(properties) {&lt;br /&gt;
    super();&lt;br /&gt;
&lt;br /&gt;
    // Falls keine ID vorhanden ist, automatisch eine eindeutige ID erzeugen&lt;br /&gt;
    if (!this.id) {&lt;br /&gt;
      if (!uids[this.localName]) {&lt;br /&gt;
        uids[this.localName] = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Standardproperties der Basisklasse mit Komponentenproperties kombinieren&lt;br /&gt;
    this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&lt;br /&gt;
    // Für alle Properties Getter/Setter und Default-Attribute anlegen&lt;br /&gt;
    this.initProperties(this.properties);&lt;br /&gt;
&lt;br /&gt;
    // Wenn die Komponente ein Template besitzt, Shadow DOM erzeugen&lt;br /&gt;
    if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
      this.createShadowRoot(this.template());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Merker für aktive Änderungen, wichtig für Output-/Two-Way-Bindings&lt;br /&gt;
    this.isActiveChange = {};&lt;br /&gt;
&lt;br /&gt;
    // Element beim globalen FTUI-Binding-System anmelden&lt;br /&gt;
    if (window.ftuiApp) {&lt;br /&gt;
      ftuiApp.attachBinding(this);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt Shadow DOM aus einem HTML-Template-String&lt;br /&gt;
  createShadowRoot(content) {&lt;br /&gt;
    const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
    elemTemplate.innerHTML = content;&lt;br /&gt;
    this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
    this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Standardattribute, die jedes FTUI-Element besitzt&lt;br /&gt;
  static get properties() {&lt;br /&gt;
    return {&lt;br /&gt;
      hidden: false,&lt;br /&gt;
      disabled: false,&lt;br /&gt;
      readonly: false,&lt;br /&gt;
      margin: &#039;&#039;,&lt;br /&gt;
      padding: &#039;&#039;,&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Attribute, bei deren Änderung attributeChangedCallback ausgelöst wird&lt;br /&gt;
  static get observedAttributes() {&lt;br /&gt;
    return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wandelt Property-Namen in HTML-Attributnamen um&lt;br /&gt;
  static convertToAttributes(properties) {&lt;br /&gt;
    return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird vom Browser aufgerufen, wenn das Element in den DOM eingefügt wurde&lt;br /&gt;
  connectedCallback() {&lt;br /&gt;
    this.updateProperties();&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
      this.onConnected();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird aufgerufen, wenn sich ein beobachtetes Attribut ändert&lt;br /&gt;
  attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
    log(3, `${this.id} -  attributeChangedCallback name=${name}, oldValue=${oldValue}, newValue=${newValue}`);&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
      this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const hasValue = newValue !== null &amp;amp;&amp;amp; newValue !== false;&lt;br /&gt;
&lt;br /&gt;
    // Standardattribute behandeln&lt;br /&gt;
    switch (name) {&lt;br /&gt;
      case &#039;hidden&#039;:&lt;br /&gt;
        this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;disabled&#039;:&lt;br /&gt;
        this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;readonly&#039;:&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;margin&#039;: {&lt;br /&gt;
        // ftui-grid behandelt margin selbst&lt;br /&gt;
        if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
          this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      case &#039;padding&#039;: {&lt;br /&gt;
        this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird von Komponenten genutzt, wenn ein Benutzerwert geändert wurde&lt;br /&gt;
  submitChange(property, value) {&lt;br /&gt;
    this.isActiveChange[property] = true;&lt;br /&gt;
    this[property] = value;&lt;br /&gt;
    this.emitChangeEvent(property, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt z.B. valueChange aus value&lt;br /&gt;
  emitChangeEvent(attribute, value) {&lt;br /&gt;
    this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Löst ein DOM CustomEvent aus&lt;br /&gt;
  emitEvent(name, value) {&lt;br /&gt;
    const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
    this.dispatchEvent(event);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisiert Properties abhängig vom Typ des Defaultwerts&lt;br /&gt;
  initProperties(properties) {&lt;br /&gt;
    Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
        this.defineBooleanProperty(name, attr);&lt;br /&gt;
        this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
        this.defineNumberProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else {&lt;br /&gt;
        this.defineStringProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Default-Attribut, wenn es noch nicht existiert&lt;br /&gt;
  initAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr)) {&lt;br /&gt;
      this.setAttribute(attr, value);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Boolean-Attribut nur, wenn Default true ist&lt;br /&gt;
  initBooleanAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Boolean-Property mit HTML-Attribut-Synchronisierung&lt;br /&gt;
  defineBooleanProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.hasAttribute(attr)&lt;br /&gt;
          &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        if (value) {&lt;br /&gt;
          this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
        } else {&lt;br /&gt;
          this.removeAttribute(attr);&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Number-Property&lt;br /&gt;
  defineNumberProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return Number(this.getAttribute(attr));&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert String-Property&lt;br /&gt;
  defineStringProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.getAttribute(attr);&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Behandelt Defaultwerte beim Einfügen des Elements in den DOM&lt;br /&gt;
  updateProperties() {&lt;br /&gt;
    Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
        this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mögliche Schwachstellen / Besonderheiten==&lt;br /&gt;
&lt;br /&gt;
1. **`Object.assign(FtuiElement.properties, properties)` mutiert die statischen Basiseigenschaften.**&lt;br /&gt;
   Sicherer wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign({}, FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. **`observedAttributes` enthält nur Basiseigenschaften.**&lt;br /&gt;
   Kindklassen müssen eigene Attribute selbst hinzufügen.&lt;br /&gt;
&lt;br /&gt;
3. **Boolean-Erkennung `newValue !== false` ist etwas ungenau.**&lt;br /&gt;
   Attribute liefern normalerweise Strings oder `null`, nicht echtes `false`. Bei `disabled=&amp;quot;false&amp;quot;` funktioniert der Getter zwar korrekt, aber `attributeChangedCallback()` behandelt `&amp;quot;false&amp;quot;` trotzdem als vorhanden.&lt;br /&gt;
&lt;br /&gt;
4. **`initAttribute()` setzt auch leere Default-Strings als Attribute.**&lt;br /&gt;
   Dadurch entstehen Attribute wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;&amp;quot;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist nicht falsch, aber kann unnötige `attributeChangedCallback()`-Aufrufe erzeugen.&lt;br /&gt;
&lt;br /&gt;
5. **`createShadowRoot()` nutzt `innerHTML`.**&lt;br /&gt;
   Das ist normal für Templates, aber Template-Inhalte sollten kontrolliert sein.&lt;br /&gt;
&lt;br /&gt;
==Merksatz==&lt;br /&gt;
&lt;br /&gt;
Dieses Programm macht aus einer normalen Webcomponent eine **FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events**.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4886</id>
		<title>(FHEM) FTUI 3 Components</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4886"/>
		<updated>2026-04-26T15:42:37Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* connectedCallback */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=element.components.js=&lt;br /&gt;
&lt;br /&gt;
Deklaration der &#039;&#039;&#039;Basisklasse&#039;&#039;&#039; für alle FTUI-v3-Webcomponents. Jede Komponente wie `ftui-button`, `ftui-label`, `ftui-icon` usw. erbt vermutlich von `FtuiElement`.&lt;br /&gt;
&lt;br /&gt;
Sie kuemmert sich um:&lt;br /&gt;
&lt;br /&gt;
* automatische IDs&lt;br /&gt;
* Standardattribute wie `hidden`, `disabled`, `readonly`, `margin`, `padding`&lt;br /&gt;
* Verbindung zwischen Attributen und JavaScript-Properties&lt;br /&gt;
* Shadow DOM&lt;br /&gt;
* Change-Events für FTUI-Bindings&lt;br /&gt;
* Hooks für abgeleitete Komponenten&lt;br /&gt;
&lt;br /&gt;
==Kurzueberblick==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class FtuiElement extends HTMLElement&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`FtuiElement` erweitert den nativen Browser-Typ `HTMLElement`. Dadurch wird daraus die gemeinsame Basis für eigene HTML-Tags wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Import==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden drei Hilfsfunktionen verwendet:&lt;br /&gt;
* isNumeric()   prüft, ob ein Wert numerisch ist&lt;br /&gt;
* toKebabCase() wandelt z.B. &amp;quot;baseWidth&amp;quot; in &amp;quot;base-width&amp;quot;&lt;br /&gt;
* log()         schreibt Debug-Ausgaben&lt;br /&gt;
&lt;br /&gt;
==UID-Zähler==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const uids = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bekommt jedes FTUI-Element automatisch eine ID, falls keine gesetzt wurde.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird intern etwa zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label id=&amp;quot;ftui_label_1&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Constructor==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
constructor(properties) {&lt;br /&gt;
  super();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`super()` ruft den Konstruktor von `HTMLElement` auf. Das ist bei Webcomponents Pflicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (!this.id) {&lt;br /&gt;
  ...&lt;br /&gt;
  this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls das Element keine ID hat, wird automatisch eine erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden die Standard-Properties der Basisklasse mit den Properties der konkreten Komponente gemischt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FtuiElement.properties = {&lt;br /&gt;
  hidden: false,&lt;br /&gt;
  disabled: false,&lt;br /&gt;
  readonly: false,&lt;br /&gt;
  margin: &#039;&#039;,&lt;br /&gt;
  padding: &#039;&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein `ftui-button` könnte zusätzlich `value`, `color`, `fill` usw. definieren.&lt;br /&gt;
&lt;br /&gt;
Achtung: `Object.assign(FtuiElement.properties, properties)` verändert das Objekt `FtuiElement.properties`. Sauberer wäre oft:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.assign({}, FtuiElement.properties, properties)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonst können Properties versehentlich global vermischt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.initProperties(this.properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugt für jede Property Getter/Setter und initialisiert die passenden HTML-Attribute.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
  this.createShadowRoot(this.template());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die konkrete Komponente eine Methode `template()` besitzt, wird daraus ein Shadow DOM erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.isActiveChange = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Merker für aktive Änderungen, wichtig für Two-Way-Binding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (window.ftuiApp) {&lt;br /&gt;
  ftuiApp.attachBinding(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die FTUI-App bereits existiert, wird das Element mit dem FTUI-Binding-System verbunden.&lt;br /&gt;
&lt;br /&gt;
==Shadow DOM==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
createShadowRoot(content) {&lt;br /&gt;
  const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
  elemTemplate.innerHTML = content;&lt;br /&gt;
  this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
  this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode baut das interne DOM der Komponente.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template() {&lt;br /&gt;
  return `&amp;lt;button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/button&amp;gt;`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu einem Shadow DOM innerhalb des Elements.&lt;br /&gt;
&lt;br /&gt;
`mode: &#039;open&#039;` bedeutet: Man kann von außen per JavaScript darauf zugreifen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.shadowRoot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Standard-Properties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get properties() {&lt;br /&gt;
  return {&lt;br /&gt;
    hidden: false,&lt;br /&gt;
    disabled: false,&lt;br /&gt;
    readonly: false,&lt;br /&gt;
    margin: &#039;&#039;,&lt;br /&gt;
    padding: &#039;&#039;,&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes FTUI-Element bekommt diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button hidden&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button readonly&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button margin=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button padding=&amp;quot;0.5&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==observedAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get observedAttributes() {&lt;br /&gt;
  return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Browser ruft `attributeChangedCallback()` nur für Attribute auf, die hier stehen.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Hier werden nur die Basiseigenschaften beobachtet. Abgeleitete Komponenten müssen wahrscheinlich selbst `observedAttributes` erweitern, sonst werden ihre eigenen Attribute nicht automatisch beobachtet.&lt;br /&gt;
&lt;br /&gt;
==convertToAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static convertToAttributes(properties) {&lt;br /&gt;
  return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfsfunktion, um Property-Namen in HTML-Attributnamen umzuwandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
baseWidth&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
base-width&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==connectedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
connectedCallback() {&lt;br /&gt;
  this.updateProperties();&lt;br /&gt;
  if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
    this.onConnected();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode wird automatisch vom Browser aufgerufen, wenn das Element in die Seite eingefügt wird.&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
&lt;br /&gt;
1. Standardwerte werden verarbeitet.&lt;br /&gt;
&lt;br /&gt;
2. Falls die konkrete Komponente `onConnected()` definiert, wird diese Hook-Funktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel in einer Kindklasse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onConnected() {&lt;br /&gt;
  console.log(&#039;Button ist im DOM&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==attributeChangedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird aufgerufen, wenn ein beobachtetes Attribut geändert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(3, `${this.id} -  attributeChangedCallback ...`)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Debug-Ausgabe.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
  this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hook für Kindklassen.&lt;br /&gt;
&lt;br /&gt;
Danach behandelt die Basisklasse ihre Standardattribute:&lt;br /&gt;
&lt;br /&gt;
#==hidden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;hidden&#039;:&lt;br /&gt;
  this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn `hidden` gesetzt ist, wird das Element ausgeblendet.&lt;br /&gt;
&lt;br /&gt;
#==disabled&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;disabled&#039;:&lt;br /&gt;
  this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optisch ausgegraut und nicht anklickbar.&lt;br /&gt;
&lt;br /&gt;
#==readonly&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;readonly&#039;:&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht bedienbar, aber ohne optischen Filter.&lt;br /&gt;
&lt;br /&gt;
#==margin&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;margin&#039;:&lt;br /&gt;
  if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
    this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 1em;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn nicht numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;10px&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bleibt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 10px;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `FTUI-GRID` wird `margin` hier nicht gesetzt, vermutlich weil `ftui-grid` sein eigenes Margin-Verhalten hat.&lt;br /&gt;
&lt;br /&gt;
#==padding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;padding&#039;:&lt;br /&gt;
  this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu `margin`.&lt;br /&gt;
&lt;br /&gt;
==submitChange==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
submitChange(property, value) {&lt;br /&gt;
  this.isActiveChange[property] = true;&lt;br /&gt;
  this[property] = value;&lt;br /&gt;
  this.emitChangeEvent(property, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist wichtig für FTUI-Bindings.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Komponente intern einen Wert ändert, etwa ein Button oder Slider, ruft sie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.submitChange(&#039;value&#039;, newValue);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann passiert:&lt;br /&gt;
&lt;br /&gt;
1. Änderung wird als aktiv markiert.&lt;br /&gt;
2. Property wird gesetzt.&lt;br /&gt;
3. Ein Event `valueChange` wird ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Dieses Event kann FTUI dann nutzen, um per `(value)` oder `[(value)]` etwas an FHEM zu senden.&lt;br /&gt;
&lt;br /&gt;
==emitChangeEvent und emitEvent==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitChangeEvent(attribute, value) {&lt;br /&gt;
  this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus `value` wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
valueChange&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitEvent(name, value) {&lt;br /&gt;
  const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
  this.dispatchEvent(event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird ein normales DOM-Event ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.emitChangeEvent(&#039;value&#039;, &#039;on&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
new CustomEvent(&#039;valueChange&#039;, { detail: &#039;on&#039; })&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initProperties(properties) {&lt;br /&gt;
  Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für jede Property wird automatisch ein passender Getter/Setter erzeugt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden: false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und HTML-Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#==Boolean&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
  this.defineBooleanProperty(name, attr);&lt;br /&gt;
  this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute funktionieren nach HTML-Logik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bedeutet `true`.&lt;br /&gt;
&lt;br /&gt;
#==Number&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
  this.defineNumberProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird beim Lesen in `Number(...)` umgewandelt.&lt;br /&gt;
&lt;br /&gt;
#==String&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else {&lt;br /&gt;
  this.defineStringProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale String-Attribute.&lt;br /&gt;
&lt;br /&gt;
==initAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr)) {&lt;br /&gt;
    this.setAttribute(attr, value);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Attribut noch nicht im HTML steht, wird der Default gesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding: &#039;&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
führt zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initBooleanAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initBooleanAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
    this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute werden nur gesetzt, wenn der Default `true` ist.&lt;br /&gt;
&lt;br /&gt;
Da `hidden`, `disabled`, `readonly` alle `false` sind, werden sie standardmäßig nicht gesetzt.&lt;br /&gt;
&lt;br /&gt;
==defineBooleanProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.defineProperty(this, name, {&lt;br /&gt;
  get() {&lt;br /&gt;
    return this.hasAttribute(attr)&lt;br /&gt;
      &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
  },&lt;br /&gt;
  set(value) {&lt;br /&gt;
    if (value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    } else {&lt;br /&gt;
      this.removeAttribute(attr);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und bekommt im HTML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = false;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird das Attribut entfernt.&lt;br /&gt;
&lt;br /&gt;
Interessant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disabled=&amp;quot;false&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird als `false` interpretiert. Das ist etwas komfortabler als natives HTML, wo ein vorhandenes Boolean-Attribut normalerweise immer wahr ist.&lt;br /&gt;
&lt;br /&gt;
==defineNumberProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return Number(this.getAttribute(attr)); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.row&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert Zahl `2`.&lt;br /&gt;
&lt;br /&gt;
==defineStringProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return this.getAttribute(attr); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale Abbildung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.text = &#039;Hallo&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
text=&amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==updateProperties&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updateProperties() {&lt;br /&gt;
  Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
    const attr = toKebabCase(name);&lt;br /&gt;
    if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
      this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
    }&lt;br /&gt;
  })&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einfügen ins DOM werden Standardattribute nochmal verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Warum?&lt;br /&gt;
Wenn ein Attribut schon beim Erzeugen gesetzt wurde, kann es sein, dass die Style-Logik noch nicht gelaufen ist. `updateProperties()` erzwingt deshalb die Behandlung der Defaultwerte.&lt;br /&gt;
&lt;br /&gt;
==Kommentierte Version==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&lt;br /&gt;
// Zähler für automatisch erzeugte IDs pro Elementtyp&lt;br /&gt;
const uids = {};&lt;br /&gt;
&lt;br /&gt;
// Basisklasse aller FTUI-Webcomponents&lt;br /&gt;
export class FtuiElement extends HTMLElement {&lt;br /&gt;
&lt;br /&gt;
  constructor(properties) {&lt;br /&gt;
    super();&lt;br /&gt;
&lt;br /&gt;
    // Falls keine ID vorhanden ist, automatisch eine eindeutige ID erzeugen&lt;br /&gt;
    if (!this.id) {&lt;br /&gt;
      if (!uids[this.localName]) {&lt;br /&gt;
        uids[this.localName] = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Standardproperties der Basisklasse mit Komponentenproperties kombinieren&lt;br /&gt;
    this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&lt;br /&gt;
    // Für alle Properties Getter/Setter und Default-Attribute anlegen&lt;br /&gt;
    this.initProperties(this.properties);&lt;br /&gt;
&lt;br /&gt;
    // Wenn die Komponente ein Template besitzt, Shadow DOM erzeugen&lt;br /&gt;
    if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
      this.createShadowRoot(this.template());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Merker für aktive Änderungen, wichtig für Output-/Two-Way-Bindings&lt;br /&gt;
    this.isActiveChange = {};&lt;br /&gt;
&lt;br /&gt;
    // Element beim globalen FTUI-Binding-System anmelden&lt;br /&gt;
    if (window.ftuiApp) {&lt;br /&gt;
      ftuiApp.attachBinding(this);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt Shadow DOM aus einem HTML-Template-String&lt;br /&gt;
  createShadowRoot(content) {&lt;br /&gt;
    const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
    elemTemplate.innerHTML = content;&lt;br /&gt;
    this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
    this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Standardattribute, die jedes FTUI-Element besitzt&lt;br /&gt;
  static get properties() {&lt;br /&gt;
    return {&lt;br /&gt;
      hidden: false,&lt;br /&gt;
      disabled: false,&lt;br /&gt;
      readonly: false,&lt;br /&gt;
      margin: &#039;&#039;,&lt;br /&gt;
      padding: &#039;&#039;,&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Attribute, bei deren Änderung attributeChangedCallback ausgelöst wird&lt;br /&gt;
  static get observedAttributes() {&lt;br /&gt;
    return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wandelt Property-Namen in HTML-Attributnamen um&lt;br /&gt;
  static convertToAttributes(properties) {&lt;br /&gt;
    return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird vom Browser aufgerufen, wenn das Element in den DOM eingefügt wurde&lt;br /&gt;
  connectedCallback() {&lt;br /&gt;
    this.updateProperties();&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
      this.onConnected();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird aufgerufen, wenn sich ein beobachtetes Attribut ändert&lt;br /&gt;
  attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
    log(3, `${this.id} -  attributeChangedCallback name=${name}, oldValue=${oldValue}, newValue=${newValue}`);&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
      this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const hasValue = newValue !== null &amp;amp;&amp;amp; newValue !== false;&lt;br /&gt;
&lt;br /&gt;
    // Standardattribute behandeln&lt;br /&gt;
    switch (name) {&lt;br /&gt;
      case &#039;hidden&#039;:&lt;br /&gt;
        this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;disabled&#039;:&lt;br /&gt;
        this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;readonly&#039;:&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;margin&#039;: {&lt;br /&gt;
        // ftui-grid behandelt margin selbst&lt;br /&gt;
        if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
          this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      case &#039;padding&#039;: {&lt;br /&gt;
        this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird von Komponenten genutzt, wenn ein Benutzerwert geändert wurde&lt;br /&gt;
  submitChange(property, value) {&lt;br /&gt;
    this.isActiveChange[property] = true;&lt;br /&gt;
    this[property] = value;&lt;br /&gt;
    this.emitChangeEvent(property, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt z.B. valueChange aus value&lt;br /&gt;
  emitChangeEvent(attribute, value) {&lt;br /&gt;
    this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Löst ein DOM CustomEvent aus&lt;br /&gt;
  emitEvent(name, value) {&lt;br /&gt;
    const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
    this.dispatchEvent(event);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisiert Properties abhängig vom Typ des Defaultwerts&lt;br /&gt;
  initProperties(properties) {&lt;br /&gt;
    Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
        this.defineBooleanProperty(name, attr);&lt;br /&gt;
        this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
        this.defineNumberProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else {&lt;br /&gt;
        this.defineStringProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Default-Attribut, wenn es noch nicht existiert&lt;br /&gt;
  initAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr)) {&lt;br /&gt;
      this.setAttribute(attr, value);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Boolean-Attribut nur, wenn Default true ist&lt;br /&gt;
  initBooleanAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Boolean-Property mit HTML-Attribut-Synchronisierung&lt;br /&gt;
  defineBooleanProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.hasAttribute(attr)&lt;br /&gt;
          &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        if (value) {&lt;br /&gt;
          this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
        } else {&lt;br /&gt;
          this.removeAttribute(attr);&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Number-Property&lt;br /&gt;
  defineNumberProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return Number(this.getAttribute(attr));&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert String-Property&lt;br /&gt;
  defineStringProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.getAttribute(attr);&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Behandelt Defaultwerte beim Einfügen des Elements in den DOM&lt;br /&gt;
  updateProperties() {&lt;br /&gt;
    Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
        this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mögliche Schwachstellen / Besonderheiten==&lt;br /&gt;
&lt;br /&gt;
1. **`Object.assign(FtuiElement.properties, properties)` mutiert die statischen Basiseigenschaften.**&lt;br /&gt;
   Sicherer wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign({}, FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. **`observedAttributes` enthält nur Basiseigenschaften.**&lt;br /&gt;
   Kindklassen müssen eigene Attribute selbst hinzufügen.&lt;br /&gt;
&lt;br /&gt;
3. **Boolean-Erkennung `newValue !== false` ist etwas ungenau.**&lt;br /&gt;
   Attribute liefern normalerweise Strings oder `null`, nicht echtes `false`. Bei `disabled=&amp;quot;false&amp;quot;` funktioniert der Getter zwar korrekt, aber `attributeChangedCallback()` behandelt `&amp;quot;false&amp;quot;` trotzdem als vorhanden.&lt;br /&gt;
&lt;br /&gt;
4. **`initAttribute()` setzt auch leere Default-Strings als Attribute.**&lt;br /&gt;
   Dadurch entstehen Attribute wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;&amp;quot;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist nicht falsch, aber kann unnötige `attributeChangedCallback()`-Aufrufe erzeugen.&lt;br /&gt;
&lt;br /&gt;
5. **`createShadowRoot()` nutzt `innerHTML`.**&lt;br /&gt;
   Das ist normal für Templates, aber Template-Inhalte sollten kontrolliert sein.&lt;br /&gt;
&lt;br /&gt;
==Merksatz==&lt;br /&gt;
&lt;br /&gt;
Dieses Programm macht aus einer normalen Webcomponent eine **FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events**.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4885</id>
		<title>(FHEM) FTUI 3 Components</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4885"/>
		<updated>2026-04-26T15:39:41Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* observedAttributes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=element.components.js=&lt;br /&gt;
&lt;br /&gt;
Deklaration der &#039;&#039;&#039;Basisklasse&#039;&#039;&#039; für alle FTUI-v3-Webcomponents. Jede Komponente wie `ftui-button`, `ftui-label`, `ftui-icon` usw. erbt vermutlich von `FtuiElement`.&lt;br /&gt;
&lt;br /&gt;
Sie kuemmert sich um:&lt;br /&gt;
&lt;br /&gt;
* automatische IDs&lt;br /&gt;
* Standardattribute wie `hidden`, `disabled`, `readonly`, `margin`, `padding`&lt;br /&gt;
* Verbindung zwischen Attributen und JavaScript-Properties&lt;br /&gt;
* Shadow DOM&lt;br /&gt;
* Change-Events für FTUI-Bindings&lt;br /&gt;
* Hooks für abgeleitete Komponenten&lt;br /&gt;
&lt;br /&gt;
==Kurzueberblick==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class FtuiElement extends HTMLElement&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`FtuiElement` erweitert den nativen Browser-Typ `HTMLElement`. Dadurch wird daraus die gemeinsame Basis für eigene HTML-Tags wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Import==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden drei Hilfsfunktionen verwendet:&lt;br /&gt;
* isNumeric()   prüft, ob ein Wert numerisch ist&lt;br /&gt;
* toKebabCase() wandelt z.B. &amp;quot;baseWidth&amp;quot; in &amp;quot;base-width&amp;quot;&lt;br /&gt;
* log()         schreibt Debug-Ausgaben&lt;br /&gt;
&lt;br /&gt;
==UID-Zähler==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const uids = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bekommt jedes FTUI-Element automatisch eine ID, falls keine gesetzt wurde.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird intern etwa zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label id=&amp;quot;ftui_label_1&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Constructor==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
constructor(properties) {&lt;br /&gt;
  super();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`super()` ruft den Konstruktor von `HTMLElement` auf. Das ist bei Webcomponents Pflicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (!this.id) {&lt;br /&gt;
  ...&lt;br /&gt;
  this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls das Element keine ID hat, wird automatisch eine erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden die Standard-Properties der Basisklasse mit den Properties der konkreten Komponente gemischt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FtuiElement.properties = {&lt;br /&gt;
  hidden: false,&lt;br /&gt;
  disabled: false,&lt;br /&gt;
  readonly: false,&lt;br /&gt;
  margin: &#039;&#039;,&lt;br /&gt;
  padding: &#039;&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein `ftui-button` könnte zusätzlich `value`, `color`, `fill` usw. definieren.&lt;br /&gt;
&lt;br /&gt;
Achtung: `Object.assign(FtuiElement.properties, properties)` verändert das Objekt `FtuiElement.properties`. Sauberer wäre oft:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.assign({}, FtuiElement.properties, properties)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonst können Properties versehentlich global vermischt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.initProperties(this.properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugt für jede Property Getter/Setter und initialisiert die passenden HTML-Attribute.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
  this.createShadowRoot(this.template());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die konkrete Komponente eine Methode `template()` besitzt, wird daraus ein Shadow DOM erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.isActiveChange = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Merker für aktive Änderungen, wichtig für Two-Way-Binding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (window.ftuiApp) {&lt;br /&gt;
  ftuiApp.attachBinding(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die FTUI-App bereits existiert, wird das Element mit dem FTUI-Binding-System verbunden.&lt;br /&gt;
&lt;br /&gt;
==Shadow DOM==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
createShadowRoot(content) {&lt;br /&gt;
  const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
  elemTemplate.innerHTML = content;&lt;br /&gt;
  this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
  this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode baut das interne DOM der Komponente.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template() {&lt;br /&gt;
  return `&amp;lt;button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/button&amp;gt;`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu einem Shadow DOM innerhalb des Elements.&lt;br /&gt;
&lt;br /&gt;
`mode: &#039;open&#039;` bedeutet: Man kann von außen per JavaScript darauf zugreifen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.shadowRoot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Standard-Properties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get properties() {&lt;br /&gt;
  return {&lt;br /&gt;
    hidden: false,&lt;br /&gt;
    disabled: false,&lt;br /&gt;
    readonly: false,&lt;br /&gt;
    margin: &#039;&#039;,&lt;br /&gt;
    padding: &#039;&#039;,&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes FTUI-Element bekommt diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button hidden&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button readonly&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button margin=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button padding=&amp;quot;0.5&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==observedAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get observedAttributes() {&lt;br /&gt;
  return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Browser ruft `attributeChangedCallback()` nur für Attribute auf, die hier stehen.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Hier werden nur die Basiseigenschaften beobachtet. Abgeleitete Komponenten müssen wahrscheinlich selbst `observedAttributes` erweitern, sonst werden ihre eigenen Attribute nicht automatisch beobachtet.&lt;br /&gt;
&lt;br /&gt;
==convertToAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static convertToAttributes(properties) {&lt;br /&gt;
  return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfsfunktion, um Property-Namen in HTML-Attributnamen umzuwandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
baseWidth&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
base-width&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==connectedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
connectedCallback() {&lt;br /&gt;
  this.updateProperties();&lt;br /&gt;
  if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
    this.onConnected();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode wird automatisch vom Browser aufgerufen, wenn das Element in die Seite eingefügt wird.&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
&lt;br /&gt;
1. Standardwerte werden verarbeitet.&lt;br /&gt;
2. Falls die konkrete Komponente `onConnected()` definiert, wird diese Hook-Funktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel in einer Kindklasse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onConnected() {&lt;br /&gt;
  console.log(&#039;Button ist im DOM&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==attributeChangedCallback&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird aufgerufen, wenn ein beobachtetes Attribut geändert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(3, `${this.id} -  attributeChangedCallback ...`)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Debug-Ausgabe.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
  this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hook für Kindklassen.&lt;br /&gt;
&lt;br /&gt;
Danach behandelt die Basisklasse ihre Standardattribute:&lt;br /&gt;
&lt;br /&gt;
#==hidden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;hidden&#039;:&lt;br /&gt;
  this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn `hidden` gesetzt ist, wird das Element ausgeblendet.&lt;br /&gt;
&lt;br /&gt;
#==disabled&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;disabled&#039;:&lt;br /&gt;
  this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optisch ausgegraut und nicht anklickbar.&lt;br /&gt;
&lt;br /&gt;
#==readonly&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;readonly&#039;:&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht bedienbar, aber ohne optischen Filter.&lt;br /&gt;
&lt;br /&gt;
#==margin&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;margin&#039;:&lt;br /&gt;
  if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
    this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 1em;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn nicht numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;10px&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bleibt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 10px;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `FTUI-GRID` wird `margin` hier nicht gesetzt, vermutlich weil `ftui-grid` sein eigenes Margin-Verhalten hat.&lt;br /&gt;
&lt;br /&gt;
#==padding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;padding&#039;:&lt;br /&gt;
  this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu `margin`.&lt;br /&gt;
&lt;br /&gt;
==submitChange==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
submitChange(property, value) {&lt;br /&gt;
  this.isActiveChange[property] = true;&lt;br /&gt;
  this[property] = value;&lt;br /&gt;
  this.emitChangeEvent(property, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist wichtig für FTUI-Bindings.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Komponente intern einen Wert ändert, etwa ein Button oder Slider, ruft sie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.submitChange(&#039;value&#039;, newValue);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann passiert:&lt;br /&gt;
&lt;br /&gt;
1. Änderung wird als aktiv markiert.&lt;br /&gt;
2. Property wird gesetzt.&lt;br /&gt;
3. Ein Event `valueChange` wird ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Dieses Event kann FTUI dann nutzen, um per `(value)` oder `[(value)]` etwas an FHEM zu senden.&lt;br /&gt;
&lt;br /&gt;
==emitChangeEvent und emitEvent==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitChangeEvent(attribute, value) {&lt;br /&gt;
  this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus `value` wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
valueChange&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitEvent(name, value) {&lt;br /&gt;
  const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
  this.dispatchEvent(event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird ein normales DOM-Event ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.emitChangeEvent(&#039;value&#039;, &#039;on&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
new CustomEvent(&#039;valueChange&#039;, { detail: &#039;on&#039; })&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initProperties(properties) {&lt;br /&gt;
  Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für jede Property wird automatisch ein passender Getter/Setter erzeugt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden: false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und HTML-Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#==Boolean&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
  this.defineBooleanProperty(name, attr);&lt;br /&gt;
  this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute funktionieren nach HTML-Logik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bedeutet `true`.&lt;br /&gt;
&lt;br /&gt;
#==Number&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
  this.defineNumberProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird beim Lesen in `Number(...)` umgewandelt.&lt;br /&gt;
&lt;br /&gt;
#==String&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else {&lt;br /&gt;
  this.defineStringProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale String-Attribute.&lt;br /&gt;
&lt;br /&gt;
==initAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr)) {&lt;br /&gt;
    this.setAttribute(attr, value);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Attribut noch nicht im HTML steht, wird der Default gesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding: &#039;&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
führt zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initBooleanAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initBooleanAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
    this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute werden nur gesetzt, wenn der Default `true` ist.&lt;br /&gt;
&lt;br /&gt;
Da `hidden`, `disabled`, `readonly` alle `false` sind, werden sie standardmäßig nicht gesetzt.&lt;br /&gt;
&lt;br /&gt;
==defineBooleanProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.defineProperty(this, name, {&lt;br /&gt;
  get() {&lt;br /&gt;
    return this.hasAttribute(attr)&lt;br /&gt;
      &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
  },&lt;br /&gt;
  set(value) {&lt;br /&gt;
    if (value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    } else {&lt;br /&gt;
      this.removeAttribute(attr);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und bekommt im HTML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = false;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird das Attribut entfernt.&lt;br /&gt;
&lt;br /&gt;
Interessant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disabled=&amp;quot;false&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird als `false` interpretiert. Das ist etwas komfortabler als natives HTML, wo ein vorhandenes Boolean-Attribut normalerweise immer wahr ist.&lt;br /&gt;
&lt;br /&gt;
==defineNumberProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return Number(this.getAttribute(attr)); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.row&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert Zahl `2`.&lt;br /&gt;
&lt;br /&gt;
==defineStringProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return this.getAttribute(attr); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale Abbildung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.text = &#039;Hallo&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
text=&amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==updateProperties&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updateProperties() {&lt;br /&gt;
  Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
    const attr = toKebabCase(name);&lt;br /&gt;
    if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
      this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
    }&lt;br /&gt;
  })&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einfügen ins DOM werden Standardattribute nochmal verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Warum?&lt;br /&gt;
Wenn ein Attribut schon beim Erzeugen gesetzt wurde, kann es sein, dass die Style-Logik noch nicht gelaufen ist. `updateProperties()` erzwingt deshalb die Behandlung der Defaultwerte.&lt;br /&gt;
&lt;br /&gt;
==Kommentierte Version==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&lt;br /&gt;
// Zähler für automatisch erzeugte IDs pro Elementtyp&lt;br /&gt;
const uids = {};&lt;br /&gt;
&lt;br /&gt;
// Basisklasse aller FTUI-Webcomponents&lt;br /&gt;
export class FtuiElement extends HTMLElement {&lt;br /&gt;
&lt;br /&gt;
  constructor(properties) {&lt;br /&gt;
    super();&lt;br /&gt;
&lt;br /&gt;
    // Falls keine ID vorhanden ist, automatisch eine eindeutige ID erzeugen&lt;br /&gt;
    if (!this.id) {&lt;br /&gt;
      if (!uids[this.localName]) {&lt;br /&gt;
        uids[this.localName] = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Standardproperties der Basisklasse mit Komponentenproperties kombinieren&lt;br /&gt;
    this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&lt;br /&gt;
    // Für alle Properties Getter/Setter und Default-Attribute anlegen&lt;br /&gt;
    this.initProperties(this.properties);&lt;br /&gt;
&lt;br /&gt;
    // Wenn die Komponente ein Template besitzt, Shadow DOM erzeugen&lt;br /&gt;
    if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
      this.createShadowRoot(this.template());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Merker für aktive Änderungen, wichtig für Output-/Two-Way-Bindings&lt;br /&gt;
    this.isActiveChange = {};&lt;br /&gt;
&lt;br /&gt;
    // Element beim globalen FTUI-Binding-System anmelden&lt;br /&gt;
    if (window.ftuiApp) {&lt;br /&gt;
      ftuiApp.attachBinding(this);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt Shadow DOM aus einem HTML-Template-String&lt;br /&gt;
  createShadowRoot(content) {&lt;br /&gt;
    const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
    elemTemplate.innerHTML = content;&lt;br /&gt;
    this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
    this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Standardattribute, die jedes FTUI-Element besitzt&lt;br /&gt;
  static get properties() {&lt;br /&gt;
    return {&lt;br /&gt;
      hidden: false,&lt;br /&gt;
      disabled: false,&lt;br /&gt;
      readonly: false,&lt;br /&gt;
      margin: &#039;&#039;,&lt;br /&gt;
      padding: &#039;&#039;,&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Attribute, bei deren Änderung attributeChangedCallback ausgelöst wird&lt;br /&gt;
  static get observedAttributes() {&lt;br /&gt;
    return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wandelt Property-Namen in HTML-Attributnamen um&lt;br /&gt;
  static convertToAttributes(properties) {&lt;br /&gt;
    return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird vom Browser aufgerufen, wenn das Element in den DOM eingefügt wurde&lt;br /&gt;
  connectedCallback() {&lt;br /&gt;
    this.updateProperties();&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
      this.onConnected();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird aufgerufen, wenn sich ein beobachtetes Attribut ändert&lt;br /&gt;
  attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
    log(3, `${this.id} -  attributeChangedCallback name=${name}, oldValue=${oldValue}, newValue=${newValue}`);&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
      this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const hasValue = newValue !== null &amp;amp;&amp;amp; newValue !== false;&lt;br /&gt;
&lt;br /&gt;
    // Standardattribute behandeln&lt;br /&gt;
    switch (name) {&lt;br /&gt;
      case &#039;hidden&#039;:&lt;br /&gt;
        this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;disabled&#039;:&lt;br /&gt;
        this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;readonly&#039;:&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;margin&#039;: {&lt;br /&gt;
        // ftui-grid behandelt margin selbst&lt;br /&gt;
        if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
          this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      case &#039;padding&#039;: {&lt;br /&gt;
        this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird von Komponenten genutzt, wenn ein Benutzerwert geändert wurde&lt;br /&gt;
  submitChange(property, value) {&lt;br /&gt;
    this.isActiveChange[property] = true;&lt;br /&gt;
    this[property] = value;&lt;br /&gt;
    this.emitChangeEvent(property, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt z.B. valueChange aus value&lt;br /&gt;
  emitChangeEvent(attribute, value) {&lt;br /&gt;
    this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Löst ein DOM CustomEvent aus&lt;br /&gt;
  emitEvent(name, value) {&lt;br /&gt;
    const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
    this.dispatchEvent(event);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisiert Properties abhängig vom Typ des Defaultwerts&lt;br /&gt;
  initProperties(properties) {&lt;br /&gt;
    Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
        this.defineBooleanProperty(name, attr);&lt;br /&gt;
        this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
        this.defineNumberProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else {&lt;br /&gt;
        this.defineStringProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Default-Attribut, wenn es noch nicht existiert&lt;br /&gt;
  initAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr)) {&lt;br /&gt;
      this.setAttribute(attr, value);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Boolean-Attribut nur, wenn Default true ist&lt;br /&gt;
  initBooleanAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Boolean-Property mit HTML-Attribut-Synchronisierung&lt;br /&gt;
  defineBooleanProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.hasAttribute(attr)&lt;br /&gt;
          &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        if (value) {&lt;br /&gt;
          this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
        } else {&lt;br /&gt;
          this.removeAttribute(attr);&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Number-Property&lt;br /&gt;
  defineNumberProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return Number(this.getAttribute(attr));&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert String-Property&lt;br /&gt;
  defineStringProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.getAttribute(attr);&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Behandelt Defaultwerte beim Einfügen des Elements in den DOM&lt;br /&gt;
  updateProperties() {&lt;br /&gt;
    Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
        this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mögliche Schwachstellen / Besonderheiten==&lt;br /&gt;
&lt;br /&gt;
1. **`Object.assign(FtuiElement.properties, properties)` mutiert die statischen Basiseigenschaften.**&lt;br /&gt;
   Sicherer wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign({}, FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. **`observedAttributes` enthält nur Basiseigenschaften.**&lt;br /&gt;
   Kindklassen müssen eigene Attribute selbst hinzufügen.&lt;br /&gt;
&lt;br /&gt;
3. **Boolean-Erkennung `newValue !== false` ist etwas ungenau.**&lt;br /&gt;
   Attribute liefern normalerweise Strings oder `null`, nicht echtes `false`. Bei `disabled=&amp;quot;false&amp;quot;` funktioniert der Getter zwar korrekt, aber `attributeChangedCallback()` behandelt `&amp;quot;false&amp;quot;` trotzdem als vorhanden.&lt;br /&gt;
&lt;br /&gt;
4. **`initAttribute()` setzt auch leere Default-Strings als Attribute.**&lt;br /&gt;
   Dadurch entstehen Attribute wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;&amp;quot;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist nicht falsch, aber kann unnötige `attributeChangedCallback()`-Aufrufe erzeugen.&lt;br /&gt;
&lt;br /&gt;
5. **`createShadowRoot()` nutzt `innerHTML`.**&lt;br /&gt;
   Das ist normal für Templates, aber Template-Inhalte sollten kontrolliert sein.&lt;br /&gt;
&lt;br /&gt;
==Merksatz==&lt;br /&gt;
&lt;br /&gt;
Dieses Programm macht aus einer normalen Webcomponent eine **FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events**.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4884</id>
		<title>(FHEM) FTUI 3 Components</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4884"/>
		<updated>2026-04-26T15:34:35Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Import */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=element.components.js=&lt;br /&gt;
&lt;br /&gt;
Deklaration der &#039;&#039;&#039;Basisklasse&#039;&#039;&#039; für alle FTUI-v3-Webcomponents. Jede Komponente wie `ftui-button`, `ftui-label`, `ftui-icon` usw. erbt vermutlich von `FtuiElement`.&lt;br /&gt;
&lt;br /&gt;
Sie kuemmert sich um:&lt;br /&gt;
&lt;br /&gt;
* automatische IDs&lt;br /&gt;
* Standardattribute wie `hidden`, `disabled`, `readonly`, `margin`, `padding`&lt;br /&gt;
* Verbindung zwischen Attributen und JavaScript-Properties&lt;br /&gt;
* Shadow DOM&lt;br /&gt;
* Change-Events für FTUI-Bindings&lt;br /&gt;
* Hooks für abgeleitete Komponenten&lt;br /&gt;
&lt;br /&gt;
==Kurzueberblick==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class FtuiElement extends HTMLElement&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`FtuiElement` erweitert den nativen Browser-Typ `HTMLElement`. Dadurch wird daraus die gemeinsame Basis für eigene HTML-Tags wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Import==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden drei Hilfsfunktionen verwendet:&lt;br /&gt;
* isNumeric()   prüft, ob ein Wert numerisch ist&lt;br /&gt;
* toKebabCase() wandelt z.B. &amp;quot;baseWidth&amp;quot; in &amp;quot;base-width&amp;quot;&lt;br /&gt;
* log()         schreibt Debug-Ausgaben&lt;br /&gt;
&lt;br /&gt;
==UID-Zähler==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const uids = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bekommt jedes FTUI-Element automatisch eine ID, falls keine gesetzt wurde.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird intern etwa zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label id=&amp;quot;ftui_label_1&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Constructor==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
constructor(properties) {&lt;br /&gt;
  super();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`super()` ruft den Konstruktor von `HTMLElement` auf. Das ist bei Webcomponents Pflicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (!this.id) {&lt;br /&gt;
  ...&lt;br /&gt;
  this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls das Element keine ID hat, wird automatisch eine erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden die Standard-Properties der Basisklasse mit den Properties der konkreten Komponente gemischt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FtuiElement.properties = {&lt;br /&gt;
  hidden: false,&lt;br /&gt;
  disabled: false,&lt;br /&gt;
  readonly: false,&lt;br /&gt;
  margin: &#039;&#039;,&lt;br /&gt;
  padding: &#039;&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein `ftui-button` könnte zusätzlich `value`, `color`, `fill` usw. definieren.&lt;br /&gt;
&lt;br /&gt;
Achtung: `Object.assign(FtuiElement.properties, properties)` verändert das Objekt `FtuiElement.properties`. Sauberer wäre oft:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.assign({}, FtuiElement.properties, properties)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonst können Properties versehentlich global vermischt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.initProperties(this.properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugt für jede Property Getter/Setter und initialisiert die passenden HTML-Attribute.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
  this.createShadowRoot(this.template());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die konkrete Komponente eine Methode `template()` besitzt, wird daraus ein Shadow DOM erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.isActiveChange = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Merker für aktive Änderungen, wichtig für Two-Way-Binding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (window.ftuiApp) {&lt;br /&gt;
  ftuiApp.attachBinding(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die FTUI-App bereits existiert, wird das Element mit dem FTUI-Binding-System verbunden.&lt;br /&gt;
&lt;br /&gt;
==Shadow DOM==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
createShadowRoot(content) {&lt;br /&gt;
  const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
  elemTemplate.innerHTML = content;&lt;br /&gt;
  this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
  this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode baut das interne DOM der Komponente.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template() {&lt;br /&gt;
  return `&amp;lt;button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/button&amp;gt;`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu einem Shadow DOM innerhalb des Elements.&lt;br /&gt;
&lt;br /&gt;
`mode: &#039;open&#039;` bedeutet: Man kann von außen per JavaScript darauf zugreifen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.shadowRoot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Standard-Properties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get properties() {&lt;br /&gt;
  return {&lt;br /&gt;
    hidden: false,&lt;br /&gt;
    disabled: false,&lt;br /&gt;
    readonly: false,&lt;br /&gt;
    margin: &#039;&#039;,&lt;br /&gt;
    padding: &#039;&#039;,&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes FTUI-Element bekommt diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button hidden&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button readonly&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button margin=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button padding=&amp;quot;0.5&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==observedAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get observedAttributes() {&lt;br /&gt;
  return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Browser ruft `attributeChangedCallback()` nur für Attribute auf, die hier stehen.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Hier werden nur die Basiseigenschaften beobachtet. Abgeleitete Komponenten müssen wahrscheinlich selbst `observedAttributes` erweitern, sonst werden ihre eigenen Attribute nicht automatisch beobachtet.&lt;br /&gt;
&lt;br /&gt;
==convertToAttributes&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static convertToAttributes(properties) {&lt;br /&gt;
  return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfsfunktion, um Property-Namen in HTML-Attributnamen umzuwandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
baseWidth&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
base-width&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==connectedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
connectedCallback() {&lt;br /&gt;
  this.updateProperties();&lt;br /&gt;
  if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
    this.onConnected();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode wird automatisch vom Browser aufgerufen, wenn das Element in die Seite eingefügt wird.&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
&lt;br /&gt;
1. Standardwerte werden verarbeitet.&lt;br /&gt;
2. Falls die konkrete Komponente `onConnected()` definiert, wird diese Hook-Funktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel in einer Kindklasse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onConnected() {&lt;br /&gt;
  console.log(&#039;Button ist im DOM&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==attributeChangedCallback&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird aufgerufen, wenn ein beobachtetes Attribut geändert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(3, `${this.id} -  attributeChangedCallback ...`)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Debug-Ausgabe.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
  this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hook für Kindklassen.&lt;br /&gt;
&lt;br /&gt;
Danach behandelt die Basisklasse ihre Standardattribute:&lt;br /&gt;
&lt;br /&gt;
#==hidden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;hidden&#039;:&lt;br /&gt;
  this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn `hidden` gesetzt ist, wird das Element ausgeblendet.&lt;br /&gt;
&lt;br /&gt;
#==disabled&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;disabled&#039;:&lt;br /&gt;
  this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optisch ausgegraut und nicht anklickbar.&lt;br /&gt;
&lt;br /&gt;
#==readonly&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;readonly&#039;:&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht bedienbar, aber ohne optischen Filter.&lt;br /&gt;
&lt;br /&gt;
#==margin&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;margin&#039;:&lt;br /&gt;
  if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
    this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 1em;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn nicht numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;10px&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bleibt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 10px;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `FTUI-GRID` wird `margin` hier nicht gesetzt, vermutlich weil `ftui-grid` sein eigenes Margin-Verhalten hat.&lt;br /&gt;
&lt;br /&gt;
#==padding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;padding&#039;:&lt;br /&gt;
  this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu `margin`.&lt;br /&gt;
&lt;br /&gt;
==submitChange==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
submitChange(property, value) {&lt;br /&gt;
  this.isActiveChange[property] = true;&lt;br /&gt;
  this[property] = value;&lt;br /&gt;
  this.emitChangeEvent(property, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist wichtig für FTUI-Bindings.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Komponente intern einen Wert ändert, etwa ein Button oder Slider, ruft sie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.submitChange(&#039;value&#039;, newValue);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann passiert:&lt;br /&gt;
&lt;br /&gt;
1. Änderung wird als aktiv markiert.&lt;br /&gt;
2. Property wird gesetzt.&lt;br /&gt;
3. Ein Event `valueChange` wird ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Dieses Event kann FTUI dann nutzen, um per `(value)` oder `[(value)]` etwas an FHEM zu senden.&lt;br /&gt;
&lt;br /&gt;
==emitChangeEvent und emitEvent==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitChangeEvent(attribute, value) {&lt;br /&gt;
  this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus `value` wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
valueChange&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitEvent(name, value) {&lt;br /&gt;
  const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
  this.dispatchEvent(event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird ein normales DOM-Event ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.emitChangeEvent(&#039;value&#039;, &#039;on&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
new CustomEvent(&#039;valueChange&#039;, { detail: &#039;on&#039; })&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initProperties(properties) {&lt;br /&gt;
  Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für jede Property wird automatisch ein passender Getter/Setter erzeugt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden: false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und HTML-Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#==Boolean&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
  this.defineBooleanProperty(name, attr);&lt;br /&gt;
  this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute funktionieren nach HTML-Logik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bedeutet `true`.&lt;br /&gt;
&lt;br /&gt;
#==Number&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
  this.defineNumberProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird beim Lesen in `Number(...)` umgewandelt.&lt;br /&gt;
&lt;br /&gt;
#==String&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else {&lt;br /&gt;
  this.defineStringProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale String-Attribute.&lt;br /&gt;
&lt;br /&gt;
==initAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr)) {&lt;br /&gt;
    this.setAttribute(attr, value);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Attribut noch nicht im HTML steht, wird der Default gesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding: &#039;&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
führt zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initBooleanAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initBooleanAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
    this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute werden nur gesetzt, wenn der Default `true` ist.&lt;br /&gt;
&lt;br /&gt;
Da `hidden`, `disabled`, `readonly` alle `false` sind, werden sie standardmäßig nicht gesetzt.&lt;br /&gt;
&lt;br /&gt;
==defineBooleanProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.defineProperty(this, name, {&lt;br /&gt;
  get() {&lt;br /&gt;
    return this.hasAttribute(attr)&lt;br /&gt;
      &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
  },&lt;br /&gt;
  set(value) {&lt;br /&gt;
    if (value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    } else {&lt;br /&gt;
      this.removeAttribute(attr);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und bekommt im HTML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = false;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird das Attribut entfernt.&lt;br /&gt;
&lt;br /&gt;
Interessant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disabled=&amp;quot;false&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird als `false` interpretiert. Das ist etwas komfortabler als natives HTML, wo ein vorhandenes Boolean-Attribut normalerweise immer wahr ist.&lt;br /&gt;
&lt;br /&gt;
==defineNumberProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return Number(this.getAttribute(attr)); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.row&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert Zahl `2`.&lt;br /&gt;
&lt;br /&gt;
==defineStringProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return this.getAttribute(attr); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale Abbildung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.text = &#039;Hallo&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
text=&amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==updateProperties&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updateProperties() {&lt;br /&gt;
  Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
    const attr = toKebabCase(name);&lt;br /&gt;
    if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
      this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
    }&lt;br /&gt;
  })&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einfügen ins DOM werden Standardattribute nochmal verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Warum?&lt;br /&gt;
Wenn ein Attribut schon beim Erzeugen gesetzt wurde, kann es sein, dass die Style-Logik noch nicht gelaufen ist. `updateProperties()` erzwingt deshalb die Behandlung der Defaultwerte.&lt;br /&gt;
&lt;br /&gt;
==Kommentierte Version==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&lt;br /&gt;
// Zähler für automatisch erzeugte IDs pro Elementtyp&lt;br /&gt;
const uids = {};&lt;br /&gt;
&lt;br /&gt;
// Basisklasse aller FTUI-Webcomponents&lt;br /&gt;
export class FtuiElement extends HTMLElement {&lt;br /&gt;
&lt;br /&gt;
  constructor(properties) {&lt;br /&gt;
    super();&lt;br /&gt;
&lt;br /&gt;
    // Falls keine ID vorhanden ist, automatisch eine eindeutige ID erzeugen&lt;br /&gt;
    if (!this.id) {&lt;br /&gt;
      if (!uids[this.localName]) {&lt;br /&gt;
        uids[this.localName] = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Standardproperties der Basisklasse mit Komponentenproperties kombinieren&lt;br /&gt;
    this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&lt;br /&gt;
    // Für alle Properties Getter/Setter und Default-Attribute anlegen&lt;br /&gt;
    this.initProperties(this.properties);&lt;br /&gt;
&lt;br /&gt;
    // Wenn die Komponente ein Template besitzt, Shadow DOM erzeugen&lt;br /&gt;
    if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
      this.createShadowRoot(this.template());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Merker für aktive Änderungen, wichtig für Output-/Two-Way-Bindings&lt;br /&gt;
    this.isActiveChange = {};&lt;br /&gt;
&lt;br /&gt;
    // Element beim globalen FTUI-Binding-System anmelden&lt;br /&gt;
    if (window.ftuiApp) {&lt;br /&gt;
      ftuiApp.attachBinding(this);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt Shadow DOM aus einem HTML-Template-String&lt;br /&gt;
  createShadowRoot(content) {&lt;br /&gt;
    const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
    elemTemplate.innerHTML = content;&lt;br /&gt;
    this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
    this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Standardattribute, die jedes FTUI-Element besitzt&lt;br /&gt;
  static get properties() {&lt;br /&gt;
    return {&lt;br /&gt;
      hidden: false,&lt;br /&gt;
      disabled: false,&lt;br /&gt;
      readonly: false,&lt;br /&gt;
      margin: &#039;&#039;,&lt;br /&gt;
      padding: &#039;&#039;,&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Attribute, bei deren Änderung attributeChangedCallback ausgelöst wird&lt;br /&gt;
  static get observedAttributes() {&lt;br /&gt;
    return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wandelt Property-Namen in HTML-Attributnamen um&lt;br /&gt;
  static convertToAttributes(properties) {&lt;br /&gt;
    return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird vom Browser aufgerufen, wenn das Element in den DOM eingefügt wurde&lt;br /&gt;
  connectedCallback() {&lt;br /&gt;
    this.updateProperties();&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
      this.onConnected();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird aufgerufen, wenn sich ein beobachtetes Attribut ändert&lt;br /&gt;
  attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
    log(3, `${this.id} -  attributeChangedCallback name=${name}, oldValue=${oldValue}, newValue=${newValue}`);&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
      this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const hasValue = newValue !== null &amp;amp;&amp;amp; newValue !== false;&lt;br /&gt;
&lt;br /&gt;
    // Standardattribute behandeln&lt;br /&gt;
    switch (name) {&lt;br /&gt;
      case &#039;hidden&#039;:&lt;br /&gt;
        this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;disabled&#039;:&lt;br /&gt;
        this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;readonly&#039;:&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;margin&#039;: {&lt;br /&gt;
        // ftui-grid behandelt margin selbst&lt;br /&gt;
        if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
          this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      case &#039;padding&#039;: {&lt;br /&gt;
        this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird von Komponenten genutzt, wenn ein Benutzerwert geändert wurde&lt;br /&gt;
  submitChange(property, value) {&lt;br /&gt;
    this.isActiveChange[property] = true;&lt;br /&gt;
    this[property] = value;&lt;br /&gt;
    this.emitChangeEvent(property, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt z.B. valueChange aus value&lt;br /&gt;
  emitChangeEvent(attribute, value) {&lt;br /&gt;
    this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Löst ein DOM CustomEvent aus&lt;br /&gt;
  emitEvent(name, value) {&lt;br /&gt;
    const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
    this.dispatchEvent(event);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisiert Properties abhängig vom Typ des Defaultwerts&lt;br /&gt;
  initProperties(properties) {&lt;br /&gt;
    Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
        this.defineBooleanProperty(name, attr);&lt;br /&gt;
        this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
        this.defineNumberProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else {&lt;br /&gt;
        this.defineStringProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Default-Attribut, wenn es noch nicht existiert&lt;br /&gt;
  initAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr)) {&lt;br /&gt;
      this.setAttribute(attr, value);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Boolean-Attribut nur, wenn Default true ist&lt;br /&gt;
  initBooleanAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Boolean-Property mit HTML-Attribut-Synchronisierung&lt;br /&gt;
  defineBooleanProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.hasAttribute(attr)&lt;br /&gt;
          &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        if (value) {&lt;br /&gt;
          this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
        } else {&lt;br /&gt;
          this.removeAttribute(attr);&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Number-Property&lt;br /&gt;
  defineNumberProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return Number(this.getAttribute(attr));&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert String-Property&lt;br /&gt;
  defineStringProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.getAttribute(attr);&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Behandelt Defaultwerte beim Einfügen des Elements in den DOM&lt;br /&gt;
  updateProperties() {&lt;br /&gt;
    Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
        this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mögliche Schwachstellen / Besonderheiten==&lt;br /&gt;
&lt;br /&gt;
1. **`Object.assign(FtuiElement.properties, properties)` mutiert die statischen Basiseigenschaften.**&lt;br /&gt;
   Sicherer wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign({}, FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. **`observedAttributes` enthält nur Basiseigenschaften.**&lt;br /&gt;
   Kindklassen müssen eigene Attribute selbst hinzufügen.&lt;br /&gt;
&lt;br /&gt;
3. **Boolean-Erkennung `newValue !== false` ist etwas ungenau.**&lt;br /&gt;
   Attribute liefern normalerweise Strings oder `null`, nicht echtes `false`. Bei `disabled=&amp;quot;false&amp;quot;` funktioniert der Getter zwar korrekt, aber `attributeChangedCallback()` behandelt `&amp;quot;false&amp;quot;` trotzdem als vorhanden.&lt;br /&gt;
&lt;br /&gt;
4. **`initAttribute()` setzt auch leere Default-Strings als Attribute.**&lt;br /&gt;
   Dadurch entstehen Attribute wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;&amp;quot;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist nicht falsch, aber kann unnötige `attributeChangedCallback()`-Aufrufe erzeugen.&lt;br /&gt;
&lt;br /&gt;
5. **`createShadowRoot()` nutzt `innerHTML`.**&lt;br /&gt;
   Das ist normal für Templates, aber Template-Inhalte sollten kontrolliert sein.&lt;br /&gt;
&lt;br /&gt;
==Merksatz==&lt;br /&gt;
&lt;br /&gt;
Dieses Programm macht aus einer normalen Webcomponent eine **FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events**.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4883</id>
		<title>(FHEM) FTUI 3 Components</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4883"/>
		<updated>2026-04-26T15:33:56Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* element.components.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=element.components.js=&lt;br /&gt;
&lt;br /&gt;
Deklaration der &#039;&#039;&#039;Basisklasse&#039;&#039;&#039; für alle FTUI-v3-Webcomponents. Jede Komponente wie `ftui-button`, `ftui-label`, `ftui-icon` usw. erbt vermutlich von `FtuiElement`.&lt;br /&gt;
&lt;br /&gt;
Sie kuemmert sich um:&lt;br /&gt;
&lt;br /&gt;
* automatische IDs&lt;br /&gt;
* Standardattribute wie `hidden`, `disabled`, `readonly`, `margin`, `padding`&lt;br /&gt;
* Verbindung zwischen Attributen und JavaScript-Properties&lt;br /&gt;
* Shadow DOM&lt;br /&gt;
* Change-Events für FTUI-Bindings&lt;br /&gt;
* Hooks für abgeleitete Komponenten&lt;br /&gt;
&lt;br /&gt;
==Kurzueberblick==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class FtuiElement extends HTMLElement&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`FtuiElement` erweitert den nativen Browser-Typ `HTMLElement`. Dadurch wird daraus die gemeinsame Basis für eigene HTML-Tags wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Import==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden drei Hilfsfunktionen verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
isNumeric()   prüft, ob ein Wert numerisch ist&lt;br /&gt;
toKebabCase() wandelt z.B. &amp;quot;baseWidth&amp;quot; in &amp;quot;base-width&amp;quot;&lt;br /&gt;
log()         schreibt Debug-Ausgaben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==UID-Zähler==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const uids = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bekommt jedes FTUI-Element automatisch eine ID, falls keine gesetzt wurde.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird intern etwa zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label id=&amp;quot;ftui_label_1&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Constructor==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
constructor(properties) {&lt;br /&gt;
  super();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`super()` ruft den Konstruktor von `HTMLElement` auf. Das ist bei Webcomponents Pflicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (!this.id) {&lt;br /&gt;
  ...&lt;br /&gt;
  this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls das Element keine ID hat, wird automatisch eine erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden die Standard-Properties der Basisklasse mit den Properties der konkreten Komponente gemischt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FtuiElement.properties = {&lt;br /&gt;
  hidden: false,&lt;br /&gt;
  disabled: false,&lt;br /&gt;
  readonly: false,&lt;br /&gt;
  margin: &#039;&#039;,&lt;br /&gt;
  padding: &#039;&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein `ftui-button` könnte zusätzlich `value`, `color`, `fill` usw. definieren.&lt;br /&gt;
&lt;br /&gt;
Achtung: `Object.assign(FtuiElement.properties, properties)` verändert das Objekt `FtuiElement.properties`. Sauberer wäre oft:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.assign({}, FtuiElement.properties, properties)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonst können Properties versehentlich global vermischt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.initProperties(this.properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugt für jede Property Getter/Setter und initialisiert die passenden HTML-Attribute.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
  this.createShadowRoot(this.template());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die konkrete Komponente eine Methode `template()` besitzt, wird daraus ein Shadow DOM erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.isActiveChange = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Merker für aktive Änderungen, wichtig für Two-Way-Binding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (window.ftuiApp) {&lt;br /&gt;
  ftuiApp.attachBinding(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die FTUI-App bereits existiert, wird das Element mit dem FTUI-Binding-System verbunden.&lt;br /&gt;
&lt;br /&gt;
==Shadow DOM==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
createShadowRoot(content) {&lt;br /&gt;
  const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
  elemTemplate.innerHTML = content;&lt;br /&gt;
  this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
  this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode baut das interne DOM der Komponente.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template() {&lt;br /&gt;
  return `&amp;lt;button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/button&amp;gt;`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu einem Shadow DOM innerhalb des Elements.&lt;br /&gt;
&lt;br /&gt;
`mode: &#039;open&#039;` bedeutet: Man kann von außen per JavaScript darauf zugreifen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.shadowRoot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Standard-Properties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get properties() {&lt;br /&gt;
  return {&lt;br /&gt;
    hidden: false,&lt;br /&gt;
    disabled: false,&lt;br /&gt;
    readonly: false,&lt;br /&gt;
    margin: &#039;&#039;,&lt;br /&gt;
    padding: &#039;&#039;,&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes FTUI-Element bekommt diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button hidden&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button readonly&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button margin=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button padding=&amp;quot;0.5&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==observedAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get observedAttributes() {&lt;br /&gt;
  return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Browser ruft `attributeChangedCallback()` nur für Attribute auf, die hier stehen.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Hier werden nur die Basiseigenschaften beobachtet. Abgeleitete Komponenten müssen wahrscheinlich selbst `observedAttributes` erweitern, sonst werden ihre eigenen Attribute nicht automatisch beobachtet.&lt;br /&gt;
&lt;br /&gt;
==convertToAttributes&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static convertToAttributes(properties) {&lt;br /&gt;
  return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfsfunktion, um Property-Namen in HTML-Attributnamen umzuwandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
baseWidth&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
base-width&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==connectedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
connectedCallback() {&lt;br /&gt;
  this.updateProperties();&lt;br /&gt;
  if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
    this.onConnected();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode wird automatisch vom Browser aufgerufen, wenn das Element in die Seite eingefügt wird.&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
&lt;br /&gt;
1. Standardwerte werden verarbeitet.&lt;br /&gt;
2. Falls die konkrete Komponente `onConnected()` definiert, wird diese Hook-Funktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel in einer Kindklasse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onConnected() {&lt;br /&gt;
  console.log(&#039;Button ist im DOM&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==attributeChangedCallback&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird aufgerufen, wenn ein beobachtetes Attribut geändert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(3, `${this.id} -  attributeChangedCallback ...`)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Debug-Ausgabe.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
  this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hook für Kindklassen.&lt;br /&gt;
&lt;br /&gt;
Danach behandelt die Basisklasse ihre Standardattribute:&lt;br /&gt;
&lt;br /&gt;
#==hidden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;hidden&#039;:&lt;br /&gt;
  this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn `hidden` gesetzt ist, wird das Element ausgeblendet.&lt;br /&gt;
&lt;br /&gt;
#==disabled&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;disabled&#039;:&lt;br /&gt;
  this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optisch ausgegraut und nicht anklickbar.&lt;br /&gt;
&lt;br /&gt;
#==readonly&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;readonly&#039;:&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht bedienbar, aber ohne optischen Filter.&lt;br /&gt;
&lt;br /&gt;
#==margin&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;margin&#039;:&lt;br /&gt;
  if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
    this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 1em;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn nicht numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;10px&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bleibt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 10px;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `FTUI-GRID` wird `margin` hier nicht gesetzt, vermutlich weil `ftui-grid` sein eigenes Margin-Verhalten hat.&lt;br /&gt;
&lt;br /&gt;
#==padding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;padding&#039;:&lt;br /&gt;
  this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu `margin`.&lt;br /&gt;
&lt;br /&gt;
==submitChange==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
submitChange(property, value) {&lt;br /&gt;
  this.isActiveChange[property] = true;&lt;br /&gt;
  this[property] = value;&lt;br /&gt;
  this.emitChangeEvent(property, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist wichtig für FTUI-Bindings.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Komponente intern einen Wert ändert, etwa ein Button oder Slider, ruft sie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.submitChange(&#039;value&#039;, newValue);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann passiert:&lt;br /&gt;
&lt;br /&gt;
1. Änderung wird als aktiv markiert.&lt;br /&gt;
2. Property wird gesetzt.&lt;br /&gt;
3. Ein Event `valueChange` wird ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Dieses Event kann FTUI dann nutzen, um per `(value)` oder `[(value)]` etwas an FHEM zu senden.&lt;br /&gt;
&lt;br /&gt;
==emitChangeEvent und emitEvent==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitChangeEvent(attribute, value) {&lt;br /&gt;
  this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus `value` wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
valueChange&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitEvent(name, value) {&lt;br /&gt;
  const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
  this.dispatchEvent(event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird ein normales DOM-Event ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.emitChangeEvent(&#039;value&#039;, &#039;on&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
new CustomEvent(&#039;valueChange&#039;, { detail: &#039;on&#039; })&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initProperties(properties) {&lt;br /&gt;
  Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für jede Property wird automatisch ein passender Getter/Setter erzeugt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden: false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und HTML-Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#==Boolean&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
  this.defineBooleanProperty(name, attr);&lt;br /&gt;
  this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute funktionieren nach HTML-Logik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bedeutet `true`.&lt;br /&gt;
&lt;br /&gt;
#==Number&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
  this.defineNumberProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird beim Lesen in `Number(...)` umgewandelt.&lt;br /&gt;
&lt;br /&gt;
#==String&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else {&lt;br /&gt;
  this.defineStringProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale String-Attribute.&lt;br /&gt;
&lt;br /&gt;
==initAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr)) {&lt;br /&gt;
    this.setAttribute(attr, value);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Attribut noch nicht im HTML steht, wird der Default gesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding: &#039;&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
führt zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initBooleanAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initBooleanAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
    this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute werden nur gesetzt, wenn der Default `true` ist.&lt;br /&gt;
&lt;br /&gt;
Da `hidden`, `disabled`, `readonly` alle `false` sind, werden sie standardmäßig nicht gesetzt.&lt;br /&gt;
&lt;br /&gt;
==defineBooleanProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.defineProperty(this, name, {&lt;br /&gt;
  get() {&lt;br /&gt;
    return this.hasAttribute(attr)&lt;br /&gt;
      &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
  },&lt;br /&gt;
  set(value) {&lt;br /&gt;
    if (value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    } else {&lt;br /&gt;
      this.removeAttribute(attr);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und bekommt im HTML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = false;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird das Attribut entfernt.&lt;br /&gt;
&lt;br /&gt;
Interessant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disabled=&amp;quot;false&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird als `false` interpretiert. Das ist etwas komfortabler als natives HTML, wo ein vorhandenes Boolean-Attribut normalerweise immer wahr ist.&lt;br /&gt;
&lt;br /&gt;
==defineNumberProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return Number(this.getAttribute(attr)); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.row&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert Zahl `2`.&lt;br /&gt;
&lt;br /&gt;
==defineStringProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return this.getAttribute(attr); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale Abbildung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.text = &#039;Hallo&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
text=&amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==updateProperties&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updateProperties() {&lt;br /&gt;
  Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
    const attr = toKebabCase(name);&lt;br /&gt;
    if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
      this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
    }&lt;br /&gt;
  })&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einfügen ins DOM werden Standardattribute nochmal verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Warum?&lt;br /&gt;
Wenn ein Attribut schon beim Erzeugen gesetzt wurde, kann es sein, dass die Style-Logik noch nicht gelaufen ist. `updateProperties()` erzwingt deshalb die Behandlung der Defaultwerte.&lt;br /&gt;
&lt;br /&gt;
==Kommentierte Version==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&lt;br /&gt;
// Zähler für automatisch erzeugte IDs pro Elementtyp&lt;br /&gt;
const uids = {};&lt;br /&gt;
&lt;br /&gt;
// Basisklasse aller FTUI-Webcomponents&lt;br /&gt;
export class FtuiElement extends HTMLElement {&lt;br /&gt;
&lt;br /&gt;
  constructor(properties) {&lt;br /&gt;
    super();&lt;br /&gt;
&lt;br /&gt;
    // Falls keine ID vorhanden ist, automatisch eine eindeutige ID erzeugen&lt;br /&gt;
    if (!this.id) {&lt;br /&gt;
      if (!uids[this.localName]) {&lt;br /&gt;
        uids[this.localName] = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Standardproperties der Basisklasse mit Komponentenproperties kombinieren&lt;br /&gt;
    this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&lt;br /&gt;
    // Für alle Properties Getter/Setter und Default-Attribute anlegen&lt;br /&gt;
    this.initProperties(this.properties);&lt;br /&gt;
&lt;br /&gt;
    // Wenn die Komponente ein Template besitzt, Shadow DOM erzeugen&lt;br /&gt;
    if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
      this.createShadowRoot(this.template());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Merker für aktive Änderungen, wichtig für Output-/Two-Way-Bindings&lt;br /&gt;
    this.isActiveChange = {};&lt;br /&gt;
&lt;br /&gt;
    // Element beim globalen FTUI-Binding-System anmelden&lt;br /&gt;
    if (window.ftuiApp) {&lt;br /&gt;
      ftuiApp.attachBinding(this);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt Shadow DOM aus einem HTML-Template-String&lt;br /&gt;
  createShadowRoot(content) {&lt;br /&gt;
    const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
    elemTemplate.innerHTML = content;&lt;br /&gt;
    this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
    this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Standardattribute, die jedes FTUI-Element besitzt&lt;br /&gt;
  static get properties() {&lt;br /&gt;
    return {&lt;br /&gt;
      hidden: false,&lt;br /&gt;
      disabled: false,&lt;br /&gt;
      readonly: false,&lt;br /&gt;
      margin: &#039;&#039;,&lt;br /&gt;
      padding: &#039;&#039;,&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Attribute, bei deren Änderung attributeChangedCallback ausgelöst wird&lt;br /&gt;
  static get observedAttributes() {&lt;br /&gt;
    return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wandelt Property-Namen in HTML-Attributnamen um&lt;br /&gt;
  static convertToAttributes(properties) {&lt;br /&gt;
    return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird vom Browser aufgerufen, wenn das Element in den DOM eingefügt wurde&lt;br /&gt;
  connectedCallback() {&lt;br /&gt;
    this.updateProperties();&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
      this.onConnected();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird aufgerufen, wenn sich ein beobachtetes Attribut ändert&lt;br /&gt;
  attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
    log(3, `${this.id} -  attributeChangedCallback name=${name}, oldValue=${oldValue}, newValue=${newValue}`);&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
      this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const hasValue = newValue !== null &amp;amp;&amp;amp; newValue !== false;&lt;br /&gt;
&lt;br /&gt;
    // Standardattribute behandeln&lt;br /&gt;
    switch (name) {&lt;br /&gt;
      case &#039;hidden&#039;:&lt;br /&gt;
        this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;disabled&#039;:&lt;br /&gt;
        this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;readonly&#039;:&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;margin&#039;: {&lt;br /&gt;
        // ftui-grid behandelt margin selbst&lt;br /&gt;
        if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
          this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      case &#039;padding&#039;: {&lt;br /&gt;
        this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird von Komponenten genutzt, wenn ein Benutzerwert geändert wurde&lt;br /&gt;
  submitChange(property, value) {&lt;br /&gt;
    this.isActiveChange[property] = true;&lt;br /&gt;
    this[property] = value;&lt;br /&gt;
    this.emitChangeEvent(property, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt z.B. valueChange aus value&lt;br /&gt;
  emitChangeEvent(attribute, value) {&lt;br /&gt;
    this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Löst ein DOM CustomEvent aus&lt;br /&gt;
  emitEvent(name, value) {&lt;br /&gt;
    const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
    this.dispatchEvent(event);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisiert Properties abhängig vom Typ des Defaultwerts&lt;br /&gt;
  initProperties(properties) {&lt;br /&gt;
    Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
        this.defineBooleanProperty(name, attr);&lt;br /&gt;
        this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
        this.defineNumberProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else {&lt;br /&gt;
        this.defineStringProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Default-Attribut, wenn es noch nicht existiert&lt;br /&gt;
  initAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr)) {&lt;br /&gt;
      this.setAttribute(attr, value);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Boolean-Attribut nur, wenn Default true ist&lt;br /&gt;
  initBooleanAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Boolean-Property mit HTML-Attribut-Synchronisierung&lt;br /&gt;
  defineBooleanProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.hasAttribute(attr)&lt;br /&gt;
          &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        if (value) {&lt;br /&gt;
          this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
        } else {&lt;br /&gt;
          this.removeAttribute(attr);&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Number-Property&lt;br /&gt;
  defineNumberProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return Number(this.getAttribute(attr));&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert String-Property&lt;br /&gt;
  defineStringProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.getAttribute(attr);&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Behandelt Defaultwerte beim Einfügen des Elements in den DOM&lt;br /&gt;
  updateProperties() {&lt;br /&gt;
    Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
        this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mögliche Schwachstellen / Besonderheiten==&lt;br /&gt;
&lt;br /&gt;
1. **`Object.assign(FtuiElement.properties, properties)` mutiert die statischen Basiseigenschaften.**&lt;br /&gt;
   Sicherer wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign({}, FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. **`observedAttributes` enthält nur Basiseigenschaften.**&lt;br /&gt;
   Kindklassen müssen eigene Attribute selbst hinzufügen.&lt;br /&gt;
&lt;br /&gt;
3. **Boolean-Erkennung `newValue !== false` ist etwas ungenau.**&lt;br /&gt;
   Attribute liefern normalerweise Strings oder `null`, nicht echtes `false`. Bei `disabled=&amp;quot;false&amp;quot;` funktioniert der Getter zwar korrekt, aber `attributeChangedCallback()` behandelt `&amp;quot;false&amp;quot;` trotzdem als vorhanden.&lt;br /&gt;
&lt;br /&gt;
4. **`initAttribute()` setzt auch leere Default-Strings als Attribute.**&lt;br /&gt;
   Dadurch entstehen Attribute wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;&amp;quot;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist nicht falsch, aber kann unnötige `attributeChangedCallback()`-Aufrufe erzeugen.&lt;br /&gt;
&lt;br /&gt;
5. **`createShadowRoot()` nutzt `innerHTML`.**&lt;br /&gt;
   Das ist normal für Templates, aber Template-Inhalte sollten kontrolliert sein.&lt;br /&gt;
&lt;br /&gt;
==Merksatz==&lt;br /&gt;
&lt;br /&gt;
Dieses Programm macht aus einer normalen Webcomponent eine **FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events**.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4882</id>
		<title>(FHEM) FTUI 3 Components</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3_Components&amp;diff=4882"/>
		<updated>2026-04-26T15:20:13Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Die Seite wurde neu angelegt: „=element.components.js=  Definition der &amp;#039;&amp;#039;&amp;#039;Basisklasse&amp;#039;&amp;#039;&amp;#039; für alle FTUI-v3-Webcomponents. Jede Komponente wie `ftui-button`, `ftui-label`, `ftui-icon` usw. erbt vermutlich von `FtuiElement`.  Sie kuemmert sich um:  * automatische IDs * Standardattribute wie `hidden`, `disabled`, `readonly`, `margin`, `padding` * Verbindung zwischen Attributen und JavaScript-Properties * Shadow DOM * Change-Events für FTUI-Bindings * Hooks für abgeleitete Komponenten  =…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=element.components.js=&lt;br /&gt;
&lt;br /&gt;
Definition der &#039;&#039;&#039;Basisklasse&#039;&#039;&#039; für alle FTUI-v3-Webcomponents. Jede Komponente wie `ftui-button`, `ftui-label`, `ftui-icon` usw. erbt vermutlich von `FtuiElement`.&lt;br /&gt;
&lt;br /&gt;
Sie kuemmert sich um:&lt;br /&gt;
&lt;br /&gt;
* automatische IDs&lt;br /&gt;
* Standardattribute wie `hidden`, `disabled`, `readonly`, `margin`, `padding`&lt;br /&gt;
* Verbindung zwischen Attributen und JavaScript-Properties&lt;br /&gt;
* Shadow DOM&lt;br /&gt;
* Change-Events für FTUI-Bindings&lt;br /&gt;
* Hooks für abgeleitete Komponenten&lt;br /&gt;
&lt;br /&gt;
==Kurzueberblick==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class FtuiElement extends HTMLElement&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`FtuiElement` erweitert den nativen Browser-Typ `HTMLElement`. Dadurch wird daraus die gemeinsame Basis für eigene HTML-Tags wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Import==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden drei Hilfsfunktionen verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
isNumeric()   prüft, ob ein Wert numerisch ist&lt;br /&gt;
toKebabCase() wandelt z.B. &amp;quot;baseWidth&amp;quot; in &amp;quot;base-width&amp;quot;&lt;br /&gt;
log()         schreibt Debug-Ausgaben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==UID-Zähler==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const uids = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bekommt jedes FTUI-Element automatisch eine ID, falls keine gesetzt wurde.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird intern etwa zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-label id=&amp;quot;ftui_label_1&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Constructor==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
constructor(properties) {&lt;br /&gt;
  super();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
`super()` ruft den Konstruktor von `HTMLElement` auf. Das ist bei Webcomponents Pflicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (!this.id) {&lt;br /&gt;
  ...&lt;br /&gt;
  this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls das Element keine ID hat, wird automatisch eine erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden die Standard-Properties der Basisklasse mit den Properties der konkreten Komponente gemischt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FtuiElement.properties = {&lt;br /&gt;
  hidden: false,&lt;br /&gt;
  disabled: false,&lt;br /&gt;
  readonly: false,&lt;br /&gt;
  margin: &#039;&#039;,&lt;br /&gt;
  padding: &#039;&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein `ftui-button` könnte zusätzlich `value`, `color`, `fill` usw. definieren.&lt;br /&gt;
&lt;br /&gt;
Achtung: `Object.assign(FtuiElement.properties, properties)` verändert das Objekt `FtuiElement.properties`. Sauberer wäre oft:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.assign({}, FtuiElement.properties, properties)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonst können Properties versehentlich global vermischt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.initProperties(this.properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugt für jede Property Getter/Setter und initialisiert die passenden HTML-Attribute.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
  this.createShadowRoot(this.template());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die konkrete Komponente eine Methode `template()` besitzt, wird daraus ein Shadow DOM erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.isActiveChange = {};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Merker für aktive Änderungen, wichtig für Two-Way-Binding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (window.ftuiApp) {&lt;br /&gt;
  ftuiApp.attachBinding(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn die FTUI-App bereits existiert, wird das Element mit dem FTUI-Binding-System verbunden.&lt;br /&gt;
&lt;br /&gt;
==Shadow DOM==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
createShadowRoot(content) {&lt;br /&gt;
  const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
  elemTemplate.innerHTML = content;&lt;br /&gt;
  this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
  this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode baut das interne DOM der Komponente.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template() {&lt;br /&gt;
  return `&amp;lt;button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/button&amp;gt;`;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu einem Shadow DOM innerhalb des Elements.&lt;br /&gt;
&lt;br /&gt;
`mode: &#039;open&#039;` bedeutet: Man kann von außen per JavaScript darauf zugreifen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.shadowRoot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Standard-Properties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get properties() {&lt;br /&gt;
  return {&lt;br /&gt;
    hidden: false,&lt;br /&gt;
    disabled: false,&lt;br /&gt;
    readonly: false,&lt;br /&gt;
    margin: &#039;&#039;,&lt;br /&gt;
    padding: &#039;&#039;,&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes FTUI-Element bekommt diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button hidden&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button readonly&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button margin=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button padding=&amp;quot;0.5&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==observedAttributes==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static get observedAttributes() {&lt;br /&gt;
  return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Browser ruft `attributeChangedCallback()` nur für Attribute auf, die hier stehen.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Hier werden nur die Basiseigenschaften beobachtet. Abgeleitete Komponenten müssen wahrscheinlich selbst `observedAttributes` erweitern, sonst werden ihre eigenen Attribute nicht automatisch beobachtet.&lt;br /&gt;
&lt;br /&gt;
==convertToAttributes&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static convertToAttributes(properties) {&lt;br /&gt;
  return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfsfunktion, um Property-Namen in HTML-Attributnamen umzuwandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
baseWidth&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
base-width&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==connectedCallback==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
connectedCallback() {&lt;br /&gt;
  this.updateProperties();&lt;br /&gt;
  if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
    this.onConnected();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode wird automatisch vom Browser aufgerufen, wenn das Element in die Seite eingefügt wird.&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
&lt;br /&gt;
1. Standardwerte werden verarbeitet.&lt;br /&gt;
2. Falls die konkrete Komponente `onConnected()` definiert, wird diese Hook-Funktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel in einer Kindklasse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onConnected() {&lt;br /&gt;
  console.log(&#039;Button ist im DOM&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==attributeChangedCallback&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird aufgerufen, wenn ein beobachtetes Attribut geändert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(3, `${this.id} -  attributeChangedCallback ...`)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Debug-Ausgabe.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
  this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hook für Kindklassen.&lt;br /&gt;
&lt;br /&gt;
Danach behandelt die Basisklasse ihre Standardattribute:&lt;br /&gt;
&lt;br /&gt;
#==hidden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;hidden&#039;:&lt;br /&gt;
  this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn `hidden` gesetzt ist, wird das Element ausgeblendet.&lt;br /&gt;
&lt;br /&gt;
#==disabled&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;disabled&#039;:&lt;br /&gt;
  this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optisch ausgegraut und nicht anklickbar.&lt;br /&gt;
&lt;br /&gt;
#==readonly&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;readonly&#039;:&lt;br /&gt;
  this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht bedienbar, aber ohne optischen Filter.&lt;br /&gt;
&lt;br /&gt;
#==margin&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;margin&#039;:&lt;br /&gt;
  if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
    this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 1em;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn nicht numerisch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;10px&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bleibt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin: 10px;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `FTUI-GRID` wird `margin` hier nicht gesetzt, vermutlich weil `ftui-grid` sein eigenes Margin-Verhalten hat.&lt;br /&gt;
&lt;br /&gt;
#==padding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
case &#039;padding&#039;:&lt;br /&gt;
  this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu `margin`.&lt;br /&gt;
&lt;br /&gt;
==submitChange==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
submitChange(property, value) {&lt;br /&gt;
  this.isActiveChange[property] = true;&lt;br /&gt;
  this[property] = value;&lt;br /&gt;
  this.emitChangeEvent(property, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist wichtig für FTUI-Bindings.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Komponente intern einen Wert ändert, etwa ein Button oder Slider, ruft sie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.submitChange(&#039;value&#039;, newValue);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann passiert:&lt;br /&gt;
&lt;br /&gt;
1. Änderung wird als aktiv markiert.&lt;br /&gt;
2. Property wird gesetzt.&lt;br /&gt;
3. Ein Event `valueChange` wird ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Dieses Event kann FTUI dann nutzen, um per `(value)` oder `[(value)]` etwas an FHEM zu senden.&lt;br /&gt;
&lt;br /&gt;
==emitChangeEvent und emitEvent==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitChangeEvent(attribute, value) {&lt;br /&gt;
  this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus `value` wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;text&lt;br /&gt;
valueChange&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emitEvent(name, value) {&lt;br /&gt;
  const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
  this.dispatchEvent(event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird ein normales DOM-Event ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.emitChangeEvent(&#039;value&#039;, &#039;on&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
new CustomEvent(&#039;valueChange&#039;, { detail: &#039;on&#039; })&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initProperties==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initProperties(properties) {&lt;br /&gt;
  Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für jede Property wird automatisch ein passender Getter/Setter erzeugt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden: false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und HTML-Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hidden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#==Boolean&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
  this.defineBooleanProperty(name, attr);&lt;br /&gt;
  this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute funktionieren nach HTML-Logik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bedeutet `true`.&lt;br /&gt;
&lt;br /&gt;
#==Number&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
  this.defineNumberProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird beim Lesen in `Number(...)` umgewandelt.&lt;br /&gt;
&lt;br /&gt;
#==String&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
else {&lt;br /&gt;
  this.defineStringProperty(name, attr);&lt;br /&gt;
  this.initAttribute(attr, defaultValue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale String-Attribute.&lt;br /&gt;
&lt;br /&gt;
==initAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr)) {&lt;br /&gt;
    this.setAttribute(attr, value);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Attribut noch nicht im HTML steht, wird der Default gesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding: &#039;&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
führt zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==initBooleanAttribute==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
initBooleanAttribute(attr, value) {&lt;br /&gt;
  if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
    this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Boolean-Attribute werden nur gesetzt, wenn der Default `true` ist.&lt;br /&gt;
&lt;br /&gt;
Da `hidden`, `disabled`, `readonly` alle `false` sind, werden sie standardmäßig nicht gesetzt.&lt;br /&gt;
&lt;br /&gt;
==defineBooleanProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object.defineProperty(this, name, {&lt;br /&gt;
  get() {&lt;br /&gt;
    return this.hasAttribute(attr)&lt;br /&gt;
      &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
  },&lt;br /&gt;
  set(value) {&lt;br /&gt;
    if (value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    } else {&lt;br /&gt;
      this.removeAttribute(attr);&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und bekommt im HTML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button disabled&amp;gt;&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.disabled = false;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird das Attribut entfernt.&lt;br /&gt;
&lt;br /&gt;
Interessant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disabled=&amp;quot;false&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird als `false` interpretiert. Das ist etwas komfortabler als natives HTML, wo ein vorhandenes Boolean-Attribut normalerweise immer wahr ist.&lt;br /&gt;
&lt;br /&gt;
==defineNumberProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return Number(this.getAttribute(attr)); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.row&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert Zahl `2`.&lt;br /&gt;
&lt;br /&gt;
==defineStringProperty&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get() { return this.getAttribute(attr); },&lt;br /&gt;
set(value) { this.setAttribute(attr, value); },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normale Abbildung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
element.text = &#039;Hallo&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
text=&amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==updateProperties&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updateProperties() {&lt;br /&gt;
  Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
    const attr = toKebabCase(name);&lt;br /&gt;
    if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
      this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
    }&lt;br /&gt;
  })&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einfügen ins DOM werden Standardattribute nochmal verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Warum?&lt;br /&gt;
Wenn ein Attribut schon beim Erzeugen gesetzt wurde, kann es sein, dass die Style-Logik noch nicht gelaufen ist. `updateProperties()` erzwingt deshalb die Behandlung der Defaultwerte.&lt;br /&gt;
&lt;br /&gt;
==Kommentierte Version==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { isNumeric, toKebabCase, log } from &#039;../modules/ftui/ftui.helper.js&#039;;&lt;br /&gt;
&lt;br /&gt;
// Zähler für automatisch erzeugte IDs pro Elementtyp&lt;br /&gt;
const uids = {};&lt;br /&gt;
&lt;br /&gt;
// Basisklasse aller FTUI-Webcomponents&lt;br /&gt;
export class FtuiElement extends HTMLElement {&lt;br /&gt;
&lt;br /&gt;
  constructor(properties) {&lt;br /&gt;
    super();&lt;br /&gt;
&lt;br /&gt;
    // Falls keine ID vorhanden ist, automatisch eine eindeutige ID erzeugen&lt;br /&gt;
    if (!this.id) {&lt;br /&gt;
      if (!uids[this.localName]) {&lt;br /&gt;
        uids[this.localName] = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.id = `${this.localName.replace(/-/g, &#039;_&#039;)}_${uids[this.localName]++}`;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Standardproperties der Basisklasse mit Komponentenproperties kombinieren&lt;br /&gt;
    this.properties = Object.assign(FtuiElement.properties, properties);&lt;br /&gt;
&lt;br /&gt;
    // Für alle Properties Getter/Setter und Default-Attribute anlegen&lt;br /&gt;
    this.initProperties(this.properties);&lt;br /&gt;
&lt;br /&gt;
    // Wenn die Komponente ein Template besitzt, Shadow DOM erzeugen&lt;br /&gt;
    if (typeof this.template === &#039;function&#039;) {&lt;br /&gt;
      this.createShadowRoot(this.template());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Merker für aktive Änderungen, wichtig für Output-/Two-Way-Bindings&lt;br /&gt;
    this.isActiveChange = {};&lt;br /&gt;
&lt;br /&gt;
    // Element beim globalen FTUI-Binding-System anmelden&lt;br /&gt;
    if (window.ftuiApp) {&lt;br /&gt;
      ftuiApp.attachBinding(this);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt Shadow DOM aus einem HTML-Template-String&lt;br /&gt;
  createShadowRoot(content) {&lt;br /&gt;
    const elemTemplate = document.createElement(&#039;template&#039;);&lt;br /&gt;
    elemTemplate.innerHTML = content;&lt;br /&gt;
    this.attachShadow({ mode: &#039;open&#039; });&lt;br /&gt;
    this.shadowRoot.appendChild(elemTemplate.content.cloneNode(true));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Standardattribute, die jedes FTUI-Element besitzt&lt;br /&gt;
  static get properties() {&lt;br /&gt;
    return {&lt;br /&gt;
      hidden: false,&lt;br /&gt;
      disabled: false,&lt;br /&gt;
      readonly: false,&lt;br /&gt;
      margin: &#039;&#039;,&lt;br /&gt;
      padding: &#039;&#039;,&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Attribute, bei deren Änderung attributeChangedCallback ausgelöst wird&lt;br /&gt;
  static get observedAttributes() {&lt;br /&gt;
    return [...Object.keys(FtuiElement.properties)];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wandelt Property-Namen in HTML-Attributnamen um&lt;br /&gt;
  static convertToAttributes(properties) {&lt;br /&gt;
    return Object.keys(properties).map(property =&amp;gt; toKebabCase(property));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird vom Browser aufgerufen, wenn das Element in den DOM eingefügt wurde&lt;br /&gt;
  connectedCallback() {&lt;br /&gt;
    this.updateProperties();&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onConnected === &#039;function&#039;) {&lt;br /&gt;
      this.onConnected();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird aufgerufen, wenn sich ein beobachtetes Attribut ändert&lt;br /&gt;
  attributeChangedCallback(name, oldValue, newValue) {&lt;br /&gt;
    log(3, `${this.id} -  attributeChangedCallback name=${name}, oldValue=${oldValue}, newValue=${newValue}`);&lt;br /&gt;
&lt;br /&gt;
    // Hook für abgeleitete Komponenten&lt;br /&gt;
    if (typeof this.onAttributeChanged === &#039;function&#039;) {&lt;br /&gt;
      this.onAttributeChanged(name, newValue, oldValue);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const hasValue = newValue !== null &amp;amp;&amp;amp; newValue !== false;&lt;br /&gt;
&lt;br /&gt;
    // Standardattribute behandeln&lt;br /&gt;
    switch (name) {&lt;br /&gt;
      case &#039;hidden&#039;:&lt;br /&gt;
        this.style.display = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;disabled&#039;:&lt;br /&gt;
        this.style.filter = hasValue ? &#039;sepia(1) saturate(0) blur(1px)&#039; : &#039;&#039;;&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;readonly&#039;:&lt;br /&gt;
        this.style.pointerEvents = hasValue ? &#039;none&#039; : &#039;&#039;;&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
      case &#039;margin&#039;: {&lt;br /&gt;
        // ftui-grid behandelt margin selbst&lt;br /&gt;
        if (this.tagName !== &#039;FTUI-GRID&#039;) {&lt;br /&gt;
          this.style.margin = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      case &#039;padding&#039;: {&lt;br /&gt;
        this.style.padding = isNumeric(newValue) ? newValue + &#039;em&#039; : newValue;&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Wird von Komponenten genutzt, wenn ein Benutzerwert geändert wurde&lt;br /&gt;
  submitChange(property, value) {&lt;br /&gt;
    this.isActiveChange[property] = true;&lt;br /&gt;
    this[property] = value;&lt;br /&gt;
    this.emitChangeEvent(property, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Erzeugt z.B. valueChange aus value&lt;br /&gt;
  emitChangeEvent(attribute, value) {&lt;br /&gt;
    this.emitEvent(attribute + &#039;Change&#039;, value);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Löst ein DOM CustomEvent aus&lt;br /&gt;
  emitEvent(name, value) {&lt;br /&gt;
    const event = new CustomEvent(name, { detail: value });&lt;br /&gt;
    this.dispatchEvent(event);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Initialisiert Properties abhängig vom Typ des Defaultwerts&lt;br /&gt;
  initProperties(properties) {&lt;br /&gt;
    Object.entries(properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (typeof properties[name] === &#039;boolean&#039;) {&lt;br /&gt;
        this.defineBooleanProperty(name, attr);&lt;br /&gt;
        this.initBooleanAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else if (typeof properties[name] === &#039;number&#039;) {&lt;br /&gt;
        this.defineNumberProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
&lt;br /&gt;
      } else {&lt;br /&gt;
        this.defineStringProperty(name, attr);&lt;br /&gt;
        this.initAttribute(attr, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Default-Attribut, wenn es noch nicht existiert&lt;br /&gt;
  initAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr)) {&lt;br /&gt;
      this.setAttribute(attr, value);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Setzt Boolean-Attribut nur, wenn Default true ist&lt;br /&gt;
  initBooleanAttribute(attr, value) {&lt;br /&gt;
    if (!this.hasAttribute(attr) &amp;amp;&amp;amp; value) {&lt;br /&gt;
      this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Boolean-Property mit HTML-Attribut-Synchronisierung&lt;br /&gt;
  defineBooleanProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.hasAttribute(attr)&lt;br /&gt;
          &amp;amp;&amp;amp; this.getAttribute(attr) !== &#039;false&#039;;&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        if (value) {&lt;br /&gt;
          this.setAttribute(attr, &#039;&#039;);&lt;br /&gt;
        } else {&lt;br /&gt;
          this.removeAttribute(attr);&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert Number-Property&lt;br /&gt;
  defineNumberProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return Number(this.getAttribute(attr));&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Definiert String-Property&lt;br /&gt;
  defineStringProperty(name, attr) {&lt;br /&gt;
    Object.defineProperty(this, name, {&lt;br /&gt;
      get() {&lt;br /&gt;
        return this.getAttribute(attr);&lt;br /&gt;
      },&lt;br /&gt;
      set(value) {&lt;br /&gt;
        this.setAttribute(attr, value);&lt;br /&gt;
      },&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Behandelt Defaultwerte beim Einfügen des Elements in den DOM&lt;br /&gt;
  updateProperties() {&lt;br /&gt;
    Object.entries(this.properties).forEach(([name, defaultValue]) =&amp;gt; {&lt;br /&gt;
      const attr = toKebabCase(name);&lt;br /&gt;
&lt;br /&gt;
      if (this.getAttribute(attr) === String(defaultValue)) {&lt;br /&gt;
        this.attributeChangedCallback(attr, null, defaultValue);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mögliche Schwachstellen / Besonderheiten==&lt;br /&gt;
&lt;br /&gt;
1. **`Object.assign(FtuiElement.properties, properties)` mutiert die statischen Basiseigenschaften.**&lt;br /&gt;
   Sicherer wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.properties = Object.assign({}, FtuiElement.properties, properties);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. **`observedAttributes` enthält nur Basiseigenschaften.**&lt;br /&gt;
   Kindklassen müssen eigene Attribute selbst hinzufügen.&lt;br /&gt;
&lt;br /&gt;
3. **Boolean-Erkennung `newValue !== false` ist etwas ungenau.**&lt;br /&gt;
   Attribute liefern normalerweise Strings oder `null`, nicht echtes `false`. Bei `disabled=&amp;quot;false&amp;quot;` funktioniert der Getter zwar korrekt, aber `attributeChangedCallback()` behandelt `&amp;quot;false&amp;quot;` trotzdem als vorhanden.&lt;br /&gt;
&lt;br /&gt;
4. **`initAttribute()` setzt auch leere Default-Strings als Attribute.**&lt;br /&gt;
   Dadurch entstehen Attribute wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
margin=&amp;quot;&amp;quot;&lt;br /&gt;
padding=&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist nicht falsch, aber kann unnötige `attributeChangedCallback()`-Aufrufe erzeugen.&lt;br /&gt;
&lt;br /&gt;
5. **`createShadowRoot()` nutzt `innerHTML`.**&lt;br /&gt;
   Das ist normal für Templates, aber Template-Inhalte sollten kontrolliert sein.&lt;br /&gt;
&lt;br /&gt;
==Merksatz==&lt;br /&gt;
&lt;br /&gt;
Dieses Programm macht aus einer normalen Webcomponent eine **FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events**.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4881</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4881"/>
		<updated>2026-04-26T15:08:08Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Hauptstruktur */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── element.components.js       Macht aus einer normalen Webcomponent eine FTUI-kompatible Komponente mit Attributbindung, Defaultwerten, Shadow DOM und Change-Events.&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── (insgesamt 37 siehe unten)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
Es gibt insgesamt 37 Komponenten:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
badge&lt;br /&gt;
button&lt;br /&gt;
cell&lt;br /&gt;
chart&lt;br /&gt;
checkbox&lt;br /&gt;
circlemenu&lt;br /&gt;
clock&lt;br /&gt;
colorpicker&lt;br /&gt;
column&lt;br /&gt;
content&lt;br /&gt;
departure&lt;br /&gt;
dropdown&lt;br /&gt;
grid&lt;br /&gt;
icon&lt;br /&gt;
image&lt;br /&gt;
input&lt;br /&gt;
knob&lt;br /&gt;
label&lt;br /&gt;
main&lt;br /&gt;
map&lt;br /&gt;
medialist&lt;br /&gt;
menu&lt;br /&gt;
meter&lt;br /&gt;
nav&lt;br /&gt;
popup&lt;br /&gt;
rotor&lt;br /&gt;
row&lt;br /&gt;
segment&lt;br /&gt;
slider&lt;br /&gt;
solar&lt;br /&gt;
speak&lt;br /&gt;
swiper&lt;br /&gt;
switch&lt;br /&gt;
tab&lt;br /&gt;
timeset&lt;br /&gt;
view&lt;br /&gt;
weather&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4880</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4880"/>
		<updated>2026-04-26T15:06:36Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Komponentenprogramme */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── element.components.js&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── (insgesamt 37 siehe unten)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
Es gibt insgesamt 37 Komponenten:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
badge&lt;br /&gt;
button&lt;br /&gt;
cell&lt;br /&gt;
chart&lt;br /&gt;
checkbox&lt;br /&gt;
circlemenu&lt;br /&gt;
clock&lt;br /&gt;
colorpicker&lt;br /&gt;
column&lt;br /&gt;
content&lt;br /&gt;
departure&lt;br /&gt;
dropdown&lt;br /&gt;
grid&lt;br /&gt;
icon&lt;br /&gt;
image&lt;br /&gt;
input&lt;br /&gt;
knob&lt;br /&gt;
label&lt;br /&gt;
main&lt;br /&gt;
map&lt;br /&gt;
medialist&lt;br /&gt;
menu&lt;br /&gt;
meter&lt;br /&gt;
nav&lt;br /&gt;
popup&lt;br /&gt;
rotor&lt;br /&gt;
row&lt;br /&gt;
segment&lt;br /&gt;
slider&lt;br /&gt;
solar&lt;br /&gt;
speak&lt;br /&gt;
swiper&lt;br /&gt;
switch&lt;br /&gt;
tab&lt;br /&gt;
timeset&lt;br /&gt;
view&lt;br /&gt;
weather&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4879</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4879"/>
		<updated>2026-04-26T15:02:23Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Hauptstruktur */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── element.components.js&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── (insgesamt 37 siehe unten)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
components/row/row.component.js&lt;br /&gt;
components/column/column.component.js&lt;br /&gt;
components/tab/tab.component.js&lt;br /&gt;
components/view/view.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4878</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4878"/>
		<updated>2026-04-26T14:50:57Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Hauptstruktur */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── element.components.js&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
components/row/row.component.js&lt;br /&gt;
components/column/column.component.js&lt;br /&gt;
components/tab/tab.component.js&lt;br /&gt;
components/view/view.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4877</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4877"/>
		<updated>2026-04-26T14:47:46Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Custom HTML Tags */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
components/row/row.component.js&lt;br /&gt;
components/column/column.component.js&lt;br /&gt;
components/tab/tab.component.js&lt;br /&gt;
components/view/view.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4876</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4876"/>
		<updated>2026-04-26T11:02:52Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Kleiner Demo-View mit FHEM-Kommunikation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
components/row/row.component.js&lt;br /&gt;
components/column/column.component.js&lt;br /&gt;
components/tab/tab.component.js&lt;br /&gt;
components/view/view.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4875</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4875"/>
		<updated>2026-04-26T11:01:58Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Ueberblick */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
==Kernprogramme==&lt;br /&gt;
&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         &lt;br /&gt;
&lt;br /&gt;
| `ftui.helper.js`                                                            |&lt;br /&gt;
&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  &lt;br /&gt;
&lt;br /&gt;
| Chart-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        &lt;br /&gt;
&lt;br /&gt;
| Color-Komponenten                                                           |&lt;br /&gt;
&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            &lt;br /&gt;
&lt;br /&gt;
| Slider/Range-Komponenten                                                    |&lt;br /&gt;
&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      &lt;br /&gt;
&lt;br /&gt;
| `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
==Datenfluss==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
==Komponentenprogramme==&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
components/row/row.component.js&lt;br /&gt;
components/column/column.component.js&lt;br /&gt;
components/tab/tab.component.js&lt;br /&gt;
components/view/view.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
==Kleiner Demo-View mit FHEM-Kommunikation==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
 &lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wichtigster Zusammenhang==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4874</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4874"/>
		<updated>2026-04-26T07:37:20Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Ueberblick=&lt;br /&gt;
Das Projekt ist ein &#039;&#039;&#039;ES2020-Web-Components-Framework&#039;&#039;&#039; für FHEM/Home Assistant; FTUI v3 ist nicht kompatibel mit FTUI v1/v2 und unterstützt mehrere Backends. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
==1. Hauptstruktur==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
www/ftui/&lt;br /&gt;
├── ftui.js                         Startdatei&lt;br /&gt;
├── modules/&lt;br /&gt;
│   ├── ftui/&lt;br /&gt;
│   │   ├── ftui.app.js             App-Initialisierung&lt;br /&gt;
│   │   ├── backend.service.js      Backend-Router FHEM/HA&lt;br /&gt;
│   │   ├── fhem.service.js         Kommunikation mit FHEM&lt;br /&gt;
│   │   ├── ha.service.js           Home-Assistant-Backend&lt;br /&gt;
│   │   ├── ftui.binding.js         Binding-System [ ], ( ), [( )]&lt;br /&gt;
│   │   └── ftui.helper.js          Hilfsfunktionen, Pipes, Observer&lt;br /&gt;
│   ├── chart.js                    Chart-Bibliothek&lt;br /&gt;
│   ├── iro.js                      Farbpicker-Bibliothek&lt;br /&gt;
│   ├── hocon/                      Parser für map/step-ähnliche Syntax&lt;br /&gt;
│   ├── rangeable/                  Slider-Hilfsbibliothek&lt;br /&gt;
│   └── vanilla-notify/             Toast/Notify&lt;br /&gt;
└── components/&lt;br /&gt;
    ├── button/&lt;br /&gt;
    ├── label/&lt;br /&gt;
    ├── icon/&lt;br /&gt;
    ├── grid/&lt;br /&gt;
    ├── row/&lt;br /&gt;
    ├── column/&lt;br /&gt;
    ├── tab/&lt;br /&gt;
    ├── view/&lt;br /&gt;
    └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zentrale Komponenten-Ladung passiert dynamisch: `ftui.app.js` sucht unbekannte `ftui-*` Elemente und lädt daraus automatisch Dateien nach dem Muster `components/&amp;lt;gruppe&amp;gt;/&amp;lt;name&amp;gt;.component.js`. ([GitHub][2])&lt;br /&gt;
&lt;br /&gt;
## 2. Kernprogramme&lt;br /&gt;
&lt;br /&gt;
| Datei                | Aufgabe                                                                                                                               | Abhängigkeiten                                                              |&lt;br /&gt;
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |&lt;br /&gt;
| `ftui.js`            | Einstiegspunkt. Lädt `ftui.app.js`, setzt `window.ftuiApp`, startet `init()` und registriert Online/Offline/Visibility/Error-Handler. | `ftui.app.js`                                                               |&lt;br /&gt;
| `ftui.app.js`        | Liest Meta-Tags, bestimmt `fhemDir`, lädt Komponenten, startet Binding und Backend.                                                   | `backend.service.js`, `ftui.binding.js`, `ftui.helper.js`, `vanilla-notify` |&lt;br /&gt;
| `backend.service.js` | Vermittler zwischen UI und Backend. Entscheidet: FHEM oder Home Assistant. Leitet Reads/Writes weiter.                                | `fhem.service.js`, `ha.service.js`, `ftui.helper.js`                        |&lt;br /&gt;
| `fhem.service.js`    | FHEM-Kommunikation: `jsonlist2` Refresh, WebSocket/`inform`, `sendCommand`, CSRF-Handling, Reading-Cache.                             | `backend.service.js`, `ftui.helper.js`                                      |&lt;br /&gt;
| `ftui.binding.js`    | Parst `[attr]`, `(attr)`, `[(attr)]`, `@click`, Pipes wie `map`, `step`, `append`. Verknüpft Readings mit DOM-Properties.             | `backend.service.js`, `ftui.helper.js`, `hocon`                             |&lt;br /&gt;
| `ftui.helper.js`     | Utility-Funktionen: Regex-Matching, `map`, `step`, Datum, Zahlen, DOM-Helfer, `Subject` Observer.                                     | keine zentrale Abhängigkeit                                                 |&lt;br /&gt;
| `ha.service.js`      | Backend analog zu `fhem.service.js`, aber für Home Assistant.                                                                         | `ftui.helper.js`                                                            |&lt;br /&gt;
| `chart.js`           | externe Chart-Bibliothek für FTUI-Chart-Komponenten.                                                                                  | Chart-Komponenten                                                           |&lt;br /&gt;
| `iro.js`             | externe Farbpicker-Bibliothek.                                                                                                        | Color-Komponenten                                                           |&lt;br /&gt;
| `rangeable/*`        | externe Slider-Bibliothek.                                                                                                            | Slider/Range-Komponenten                                                    |&lt;br /&gt;
| `vanilla-notify/*`   | Toast-Meldungen für Debug/Error.                                                                                                      | `ftui.app.js`                                                               |&lt;br /&gt;
&lt;br /&gt;
## 3. Datenfluss&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTML:&lt;br /&gt;
&amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
→ ftui.app.js erkennt ftui-label&lt;br /&gt;
→ lädt components/label/label.component.js&lt;br /&gt;
→ ftui.binding.js liest [text]&lt;br /&gt;
→ backend.service.js registriert Reading&lt;br /&gt;
→ fhem.service.js erzeugt Filter&lt;br /&gt;
→ fhem.service.js holt initial per jsonlist2&lt;br /&gt;
→ fhem.service.js öffnet WebSocket mit inform&lt;br /&gt;
→ Änderung in FHEM&lt;br /&gt;
→ WebSocket Event&lt;br /&gt;
→ Reading-Cache wird aktualisiert&lt;br /&gt;
→ Binding setzt label.text&lt;br /&gt;
→ DOM aktualisiert sich&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FTUI verwendet für Eingaben/Ausgaben diese Richtungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[attr]     Backend → Widget&lt;br /&gt;
(attr)     Widget  → Backend&lt;br /&gt;
[(attr)]   Backend ↔ Widget&lt;br /&gt;
@click     JavaScript/Event → z.B. sendFhem(...)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
## 4. FHEM-Kommunikation&lt;br /&gt;
&lt;br /&gt;
`fhem.service.js` macht drei Dinge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Initialwerte:&lt;br /&gt;
   sendCommand(&amp;quot;jsonlist2 &amp;lt;filter&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
2. Live-Updates:&lt;br /&gt;
   WebSocket auf:&lt;br /&gt;
   /fhem?XHR=1&amp;amp;inform=type=status;filter=...;fmt=JSON&lt;br /&gt;
&lt;br /&gt;
3. Schreibbefehle:&lt;br /&gt;
   /fhem?cmd=&amp;lt;befehl&amp;gt;&amp;amp;fwcsrf=&amp;lt;token&amp;gt;&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Code enthält `ensureCSrf()` und `fetchCSrf()`: Vor `sendCommand()` wird ein CSRF-Token geholt, falls noch keines gesetzt ist. ([GitHub][3])&lt;br /&gt;
&lt;br /&gt;
## 5. Komponentenprogramme&lt;br /&gt;
&lt;br /&gt;
Die Komponenten liegen jeweils als Web Component in `components/&amp;lt;name&amp;gt;/...component.js`.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
components/button/button.component.js&lt;br /&gt;
components/button/button-nice.component.js&lt;br /&gt;
components/label/label.component.js&lt;br /&gt;
components/icon/icon.component.js&lt;br /&gt;
components/grid/grid.component.js&lt;br /&gt;
components/row/row.component.js&lt;br /&gt;
components/column/column.component.js&lt;br /&gt;
components/tab/tab.component.js&lt;br /&gt;
components/view/view.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für `button` zeigt das Repository z.B. `button.component.js`, `button-nice.component.js` und CSS. ([GitHub][4])&lt;br /&gt;
Für `label` gibt es `label.component.js`. ([GitHub][5])&lt;br /&gt;
&lt;br /&gt;
## 6. Kleiner Demo-View mit FHEM-Kommunikation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;html&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label text=&amp;quot;Rollladen AZC&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;ftui-icon&lt;br /&gt;
        [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:user_icons\/fts_shutter_0,1:user_icons\/fts_shutter_updown,2:user_icons\/fts_shutter_40,3:user_icons\/fts_shutter_100&#039;)&amp;quot;&lt;br /&gt;
        size=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;ftui-label [text]=&amp;quot;RL_AZC:status2bit&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was dabei passiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[text]=&amp;quot;RL_AZC:status2bit&amp;quot;&lt;br /&gt;
→ registriert Reading RL_AZC-status2bit&lt;br /&gt;
&lt;br /&gt;
[name]=&amp;quot;RL_AZC:status2bit | map(...)&amp;quot;&lt;br /&gt;
→ liest dasselbe Reading&lt;br /&gt;
→ setzt abhängig vom Wert das Icon&lt;br /&gt;
&lt;br /&gt;
@click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&lt;br /&gt;
→ ruft backendService.sendUpdate()&lt;br /&gt;
→ fhem.service.js sendCommand()&lt;br /&gt;
→ FHEM bekommt set RL_AZC up&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
## 7. Wichtigster Zusammenhang&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui.js&lt;br /&gt;
└── ftui.app.js&lt;br /&gt;
    ├── lädt Komponenten dynamisch&lt;br /&gt;
    ├── startet FtuiBinding&lt;br /&gt;
    └── backend.service.js&lt;br /&gt;
        ├── fhem.service.js&lt;br /&gt;
        │   ├── jsonlist2&lt;br /&gt;
        │   ├── WebSocket inform&lt;br /&gt;
        │   ├── CSRF&lt;br /&gt;
        │   └── sendCommand()&lt;br /&gt;
        └── ha.service.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für eine echte vollständige Datei-für-Datei-Kommentierung aller Komponenten wäre ein lokaler Checkout oder ZIP des Repositories sinnvoll, weil GitHub die komplette Komponentenliste im Web-Tree nicht vollständig zuverlässig ausliefert.&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui &amp;quot;GitHub - knowthelist/ftui: FTUI version 3 · GitHub&amp;quot;&lt;br /&gt;
[2]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/ftui.app.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
[3]: https://raw.githubusercontent.com/knowthelist/ftui/master/www/ftui/modules/ftui/fhem.service.js &amp;quot;raw.githubusercontent.com&amp;quot;&lt;br /&gt;
[4]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/button &amp;quot;ftui/www/ftui/components/button at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
[5]: https://github.com/knowthelist/ftui/tree/master/www/ftui/components/label &amp;quot;ftui/www/ftui/components/label at master · knowthelist/ftui · GitHub&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4873</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4873"/>
		<updated>2026-04-26T06:26:11Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Tag-List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Local Bindung und Events=== &lt;br /&gt;
&lt;br /&gt;
Die schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
===Pipes===&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
===Formatierung/Layout===&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4872</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4872"/>
		<updated>2026-04-26T06:24:04Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Tag-List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es funktioniert auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4871</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4871"/>
		<updated>2026-04-26T06:23:22Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Tag-List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von get-value und set-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4870</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4870"/>
		<updated>2026-04-25T13:12:45Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Fehler nach Update */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HTTP/1.1 400 Bad Request&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
Cache-Control: no-cache, no-store, must-revalidate&lt;br /&gt;
X-FHEM-csrfToken: csrf_19345435345346&lt;br /&gt;
Content-Type: text/html; charset=UTF-8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4869</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4869"/>
		<updated>2026-04-25T09:42:21Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Beispiele */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Objekthierarchie=&lt;br /&gt;
&lt;br /&gt;
Hier ist ein praktischer **Objektbaum für FTUI v3** mit den typischen erlaubten Eltern-Kind-Beziehungen.&lt;br /&gt;
&lt;br /&gt;
FTUI stellt Layout-Komponenten wie **Grid**, **Row**, **Column** und **Tab/View-Strukturen** bereit; die offiziellen Beispiele zeigen `ftui-grid` mit `ftui-grid-tile`, darin `ftui-grid-header`, `ftui-row` und `ftui-column`. ([GitHub][1])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
└── ftui-tab-view              optional: eine Seite / ein Tab-Inhalt&lt;br /&gt;
    └── ftui-grid              Hauptlayout einer Seite&lt;br /&gt;
        ├── ftui-grid-tile     Kachel im Grid&lt;br /&gt;
        │   ├── ftui-grid-header   Kopfzeile/Titel der Kachel&lt;br /&gt;
        │   ├── ftui-row           horizontale Anordnung&lt;br /&gt;
        │   │   ├── ftui-column    vertikale Untergruppe&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   ├── ftui-icon&lt;br /&gt;
        │   │   │   └── ftui-button&lt;br /&gt;
        │   │   ├── ftui-icon&lt;br /&gt;
        │   │   └── ftui-label&lt;br /&gt;
        │   ├── ftui-column        vertikale Anordnung&lt;br /&gt;
        │   │   ├── ftui-row&lt;br /&gt;
        │   │   │   ├── ftui-label&lt;br /&gt;
        │   │   │   └── ftui-icon&lt;br /&gt;
        │   │   ├── ftui-label&lt;br /&gt;
        │   │   └── ftui-button&lt;br /&gt;
        │   ├── ftui-label&lt;br /&gt;
        │   ├── ftui-icon&lt;br /&gt;
        │   └── ftui-button&lt;br /&gt;
        └── ftui-grid-tile&lt;br /&gt;
            └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Regeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-tab-view&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    └── normale Widgets / HTML&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid&lt;br /&gt;
└── sollte hauptsächlich enthalten:&lt;br /&gt;
    └── ftui-grid-tile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-tile&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-grid-header&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-grid-header&lt;br /&gt;
└── sollte innerhalb von ftui-grid-tile stehen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-row&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-column&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ftui-column&lt;br /&gt;
└── darf enthalten:&lt;br /&gt;
    ├── ftui-row&lt;br /&gt;
    ├── ftui-label&lt;br /&gt;
    ├── ftui-icon&lt;br /&gt;
    ├── ftui-button&lt;br /&gt;
    └── andere Widgets&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein sauberes Beispiel (html):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;rollladen&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;10&amp;quot;&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; height=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-header&amp;gt;Arbeitszimmer&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;ftui-column&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label text=&amp;quot;Rollladen&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;user_icons/fts_shutter_100&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
        &amp;lt;ftui-row&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC up&#039;)&amp;quot;&amp;gt;Auf&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC stop&#039;)&amp;quot;&amp;gt;Stop&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;set RL_AZC down&#039;)&amp;quot;&amp;gt;Ab&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
      &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt: **`ftui-grid` ordnet Kacheln an, `ftui-grid-tile` ist die Kachel, `ftui-row` und `ftui-column` strukturieren den Inhalt der Kachel.**&lt;br /&gt;
&lt;br /&gt;
[1]: https://github.com/knowthelist/ftui?utm_source=chatgpt.com &amp;quot;knowthelist/ftui: FTUI version 3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4868</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4868"/>
		<updated>2026-04-15T16:38:28Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Formatierung/Layout */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
   Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4867</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4867"/>
		<updated>2026-04-15T16:37:24Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Formatierung/Layout */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
 ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
    Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4866</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4866"/>
		<updated>2026-04-15T15:47:04Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Beispiele */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
    Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer dass &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur meinen Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte mal selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstieg reicht mir das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
Das kommt mir trotzdem komisch vor. Ich werde weiter testen.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/icons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Die Icons in den Unterordnern eine eine map einzubinden hat mich Nerven gekostet.&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;ftui_iconpath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
oder &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;iconPath&amp;quot; content=&amp;quot;path/to/your/icons/&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
scheint es in FTUI nicht (mehr) zu geben und die Pfadangabe in der map-Umgebung wird nicht (richtig) ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Das Escapen den Slashes hat nur bedingt funktioniert. Erst das Quoten mit z. B. ` hat dann geklappt.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-icon [name]=&amp;quot;RL_AZC:status2bit | map(&#039;0:`user_icons/fts_shutter_0`,1:`user_icons/fts_shutter_updown`,2:`user_icons/fts_shutter_40`,3:`user_icons/fts_shutter_100`&#039;)&amp;quot; size=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Kleiner Ausflug in mein KNX=&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4865</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4865"/>
		<updated>2026-04-15T09:29:31Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Beispiele */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
    Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer das &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur mein Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte man selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstirg reicht mir das das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/incons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Kleiner Ausflug in mein KNX.&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Fehler nach Update=&lt;br /&gt;
&lt;br /&gt;
Im &amp;quot;sendcommand&amp;quot; ist auf einmal der wfcsrf Parameter leer.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldung&lt;br /&gt;
&lt;br /&gt;
 fhem command failed syntaxerror: json.parse: unexpected end of data at line 1 column 1 of the json data&lt;br /&gt;
&lt;br /&gt;
Spurensuche:&lt;br /&gt;
&lt;br /&gt;
 curl -i &amp;quot;http://127.0.0.1:8083/fhem?cmd=jsonlist2&amp;amp;XHR=1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://fhem.clx.local:8083/fhem&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fhem.service.js wurde durch das Versionupdate die Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // init FhemService&lt;br /&gt;
    fhemService.setConfig(this.config);&lt;br /&gt;
    fhemService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
    fhemService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
    this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
    this.initPage();&lt;br /&gt;
&lt;br /&gt;
    // call health check periodically&lt;br /&gt;
    setInterval(() =&amp;gt; {&lt;br /&gt;
      this.checkConnection();&lt;br /&gt;
    }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ersetzt durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // initialize backend service&lt;br /&gt;
    await this.initBackends();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In initBackends steht folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der BackendService wurde erzeugt um auch die Anbindung an HA in einem Aufwasch zu realisieren.&lt;br /&gt;
Es haette aber gereicht fhemService.setConfig(this.config); zu ersetzen. Stattdessen wurde&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fhemService = fhemService;&lt;br /&gt;
&lt;br /&gt;
    // init Page after CSFS Token has been retrieved&lt;br /&gt;
    await fhemService.fetchCSrf()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
gleich mit geloescht aber nicht ersetzt.&lt;br /&gt;
&lt;br /&gt;
Nach der Korrektur von initBackends() in der ftui.app.js hatten meine Seiten wieder Zugriff auf FHEM.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // initialize backend services&lt;br /&gt;
  async initBackends() {&lt;br /&gt;
    try {&lt;br /&gt;
      // Initialize backend service&lt;br /&gt;
      backendService.setConfig(this.config);&lt;br /&gt;
      backendService.debugEvents.subscribe(text =&amp;gt; this.toast(text));&lt;br /&gt;
      backendService.errorEvents.subscribe(text =&amp;gt; this.toast(text, &#039;error&#039;));&lt;br /&gt;
&lt;br /&gt;
      this.fhemService = fhemService;&lt;br /&gt;
      await fhemService.fetchCSrf()&lt;br /&gt;
&lt;br /&gt;
      await this.initPage();&lt;br /&gt;
&lt;br /&gt;
      // call health check periodically&lt;br /&gt;
      setInterval(() =&amp;gt; {&lt;br /&gt;
        this.checkConnection();&lt;br /&gt;
      }, this.config.updateCheckInterval * 60 * 1000);&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
      ftui.error(&#039;[ftuiApp] initBackends error - &#039; + err);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_00_MQTT2_CLIENT.pm&amp;diff=4864</id>
		<title>(FHEM) 00 MQTT2 CLIENT.pm</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_00_MQTT2_CLIENT.pm&amp;diff=4864"/>
		<updated>2026-04-14T16:59:11Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Fehler 2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[(FHEM) MQTT#Module im Detail| [Zurueck Uebersicht]]] &lt;br /&gt;
&lt;br /&gt;
Uebersetzung aus dem FHEM WIKI&lt;br /&gt;
&lt;br /&gt;
  MQTT2_CLIENT ist eine Reinraumimplementierung eines MQTT-Clients (der eine Verbindung zu einem externen Server wie mosquitto herstellt) ohne Perl-Bibliotheken. Es dient als IODev für MQTT2_DEVICES.&lt;br /&gt;
&lt;br /&gt;
Was auch immer eine Reinraumimplementierung sein soll. Ich vermute es ist damit gemeint, dass die Programmierung &amp;quot;auszerhalb&amp;quot; des FHEM-Frameworks arbeitet.   &lt;br /&gt;
&lt;br /&gt;
=Steckbrief=&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Define&#039;&#039;&#039;||define &amp;lt;name&amp;gt; MQTT2_CLIENT &amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;4&amp;quot;|&#039;&#039;&#039;Set&#039;&#039;&#039;||publish [-r] topic value||  Verschickt eine Nachricht an den Topic; -r setzt das retain Flag&lt;br /&gt;
|-&lt;br /&gt;
|password &amp;lt;password&amp;gt; value|| Setzt das Passwort, welches in FHEM/FhemUtils/uniqueID gespeicher ist. Wenn kein &amp;lt;passwort&amp;gt; uebergeben wird, wird das Passwort geloescht.&lt;br /&gt;
|-&lt;br /&gt;
|connect||Dient zum manuellen Verbinden an den Broker.&lt;br /&gt;
|-&lt;br /&gt;
|disconnect||Dient zum manuellen Unterbrechen der Verbindung zum Broker.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Get&#039;&#039;&#039;||&amp;amp;nbsp;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Readings&#039;&#039;&#039;||&amp;amp;nbsp;&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;21&amp;quot;|&#039;&#039;&#039;Attributes&#039;&#039;&#039;||&amp;quot;autocreate [no|simple|complex]&amp;quot;||siehe Erlaeuterung&lt;br /&gt;
|-&lt;br /&gt;
|binaryTopicRegexp &amp;lt;regular-expression&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|ignoreRegexp||&lt;br /&gt;
|-&lt;br /&gt;
|clientId &amp;lt;name&amp;gt;||setzt die ClientId&lt;br /&gt;
|-&lt;br /&gt;
|clientOrder [MQTT2_DEVICE] [MQTT_GENERIC_BRIDGE]||&lt;br /&gt;
|-&lt;br /&gt;
|connectTimeout &amp;lt;seconds&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|disable||&lt;br /&gt;
|-&lt;br /&gt;
|disabledForIntervals||&lt;br /&gt;
|-&lt;br /&gt;
|disconnectAfter &amp;lt;seconds&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|keepaliveTimeout &amp;lt;seconds;&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|lwt||&lt;br /&gt;
|-&lt;br /&gt;
|lwtRetain||&lt;br /&gt;
|-&lt;br /&gt;
|mqttVersion 3.1,3.1.1||&lt;br /&gt;
|-&lt;br /&gt;
|msgAfterConnect [-r] topic message||&lt;br /&gt;
|-&lt;br /&gt;
|msgBeforeDisconnect [-r] topic message||&lt;br /&gt;
|-&lt;br /&gt;
|qosMaxQueueLength &amp;lt;number&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|rawEvents &amp;lt;topic-regexp&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|subscriptions &amp;lt;subscriptions&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|SSL||&lt;br /&gt;
|-&lt;br /&gt;
|sslargs||&lt;br /&gt;
|-&lt;br /&gt;
|username &amp;lt;username&amp;gt;||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Erlaeuterungen=&lt;br /&gt;
 Es wird nur QoS 0 und 1 unterstuetzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Saetze evtl. zum Einbauen.&lt;br /&gt;
 Ich nutze ebenfalls MQTT_GENERIC_BRIDGE zum &amp;quot;Weiterbefördern&amp;quot; relevanter Informationen von den Nebensystemen zum Hauptsystem.&lt;br /&gt;
&lt;br /&gt;
 Das Trennen der vom Nebensystem angelieferten Informationen übernimmt dabei das Attribut bridgeRegexp eines allgemeinen MQTT2_DEVICEs auf dem Hauptsystem.&lt;br /&gt;
&lt;br /&gt;
=Usererror 1=&lt;br /&gt;
Was bei mir gefehlt hat war die &amp;quot;base&amp;quot; Definition sowie eine Bridge mit dem entsprechenden Filter&lt;br /&gt;
&lt;br /&gt;
bridgeRegexp \&lt;br /&gt;
SmartHome/MqttGenericBridge2/([A-Za-z0-9]*)/.*:.* &amp;quot;mgb2_$1&amp;quot;\&lt;br /&gt;
&lt;br /&gt;
Das Autocreate hat nach den Anpassungen dann auch funktioniert. Die letzte Herausforderung ist nun, dass ich auf dem Slavesystem Namen mit einem Underscore habe und diese abgeschnitten werden. Experimente mit der RegEx Definition hatten bisher noch keinen Erfolg. &lt;br /&gt;
&lt;br /&gt;
Da dieser Matchingparameter [([A-Za-z0-9]*)] bei den Underscores aufhört bei der Deviceerstellung, habe ich eine Wildcard [.*] genutz... ist aber nicht effektiv, da alle Radings in einem Device landen &lt;br /&gt;
&lt;br /&gt;
([A-Za-z0-9]...*) [https://forum.fhem.de/index.php?topic=107145.0 weitere Varianten]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
hat das Problem mit den _ in den Devicenamen gelöst.&lt;br /&gt;
&lt;br /&gt;
([^/]+)&lt;br /&gt;
&lt;br /&gt;
https://forum.fhem.de/index.php?topic=98206.0&lt;br /&gt;
&lt;br /&gt;
=Usererror 2=&lt;br /&gt;
Ich falsch davon ausgegangen, dass die Port-Angabe entfallen kann wenn sie Standard (1883) ist.&lt;br /&gt;
&lt;br /&gt;
 defmod MQTT_CLX_FHEMClient MQTT2_CLIENT 192.168.178.10:1883&lt;br /&gt;
&lt;br /&gt;
Defmod mit Port-Angabe hat dann auch direkt ein Publish zugelassen.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_00_MQTT2_CLIENT.pm&amp;diff=4863</id>
		<title>(FHEM) 00 MQTT2 CLIENT.pm</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_00_MQTT2_CLIENT.pm&amp;diff=4863"/>
		<updated>2026-04-14T16:58:59Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Fehler 1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[(FHEM) MQTT#Module im Detail| [Zurueck Uebersicht]]] &lt;br /&gt;
&lt;br /&gt;
Uebersetzung aus dem FHEM WIKI&lt;br /&gt;
&lt;br /&gt;
  MQTT2_CLIENT ist eine Reinraumimplementierung eines MQTT-Clients (der eine Verbindung zu einem externen Server wie mosquitto herstellt) ohne Perl-Bibliotheken. Es dient als IODev für MQTT2_DEVICES.&lt;br /&gt;
&lt;br /&gt;
Was auch immer eine Reinraumimplementierung sein soll. Ich vermute es ist damit gemeint, dass die Programmierung &amp;quot;auszerhalb&amp;quot; des FHEM-Frameworks arbeitet.   &lt;br /&gt;
&lt;br /&gt;
=Steckbrief=&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Define&#039;&#039;&#039;||define &amp;lt;name&amp;gt; MQTT2_CLIENT &amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;4&amp;quot;|&#039;&#039;&#039;Set&#039;&#039;&#039;||publish [-r] topic value||  Verschickt eine Nachricht an den Topic; -r setzt das retain Flag&lt;br /&gt;
|-&lt;br /&gt;
|password &amp;lt;password&amp;gt; value|| Setzt das Passwort, welches in FHEM/FhemUtils/uniqueID gespeicher ist. Wenn kein &amp;lt;passwort&amp;gt; uebergeben wird, wird das Passwort geloescht.&lt;br /&gt;
|-&lt;br /&gt;
|connect||Dient zum manuellen Verbinden an den Broker.&lt;br /&gt;
|-&lt;br /&gt;
|disconnect||Dient zum manuellen Unterbrechen der Verbindung zum Broker.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Get&#039;&#039;&#039;||&amp;amp;nbsp;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Readings&#039;&#039;&#039;||&amp;amp;nbsp;&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;21&amp;quot;|&#039;&#039;&#039;Attributes&#039;&#039;&#039;||&amp;quot;autocreate [no|simple|complex]&amp;quot;||siehe Erlaeuterung&lt;br /&gt;
|-&lt;br /&gt;
|binaryTopicRegexp &amp;lt;regular-expression&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|ignoreRegexp||&lt;br /&gt;
|-&lt;br /&gt;
|clientId &amp;lt;name&amp;gt;||setzt die ClientId&lt;br /&gt;
|-&lt;br /&gt;
|clientOrder [MQTT2_DEVICE] [MQTT_GENERIC_BRIDGE]||&lt;br /&gt;
|-&lt;br /&gt;
|connectTimeout &amp;lt;seconds&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|disable||&lt;br /&gt;
|-&lt;br /&gt;
|disabledForIntervals||&lt;br /&gt;
|-&lt;br /&gt;
|disconnectAfter &amp;lt;seconds&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|keepaliveTimeout &amp;lt;seconds;&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|lwt||&lt;br /&gt;
|-&lt;br /&gt;
|lwtRetain||&lt;br /&gt;
|-&lt;br /&gt;
|mqttVersion 3.1,3.1.1||&lt;br /&gt;
|-&lt;br /&gt;
|msgAfterConnect [-r] topic message||&lt;br /&gt;
|-&lt;br /&gt;
|msgBeforeDisconnect [-r] topic message||&lt;br /&gt;
|-&lt;br /&gt;
|qosMaxQueueLength &amp;lt;number&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|rawEvents &amp;lt;topic-regexp&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|subscriptions &amp;lt;subscriptions&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|SSL||&lt;br /&gt;
|-&lt;br /&gt;
|sslargs||&lt;br /&gt;
|-&lt;br /&gt;
|username &amp;lt;username&amp;gt;||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Erlaeuterungen=&lt;br /&gt;
 Es wird nur QoS 0 und 1 unterstuetzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Saetze evtl. zum Einbauen.&lt;br /&gt;
 Ich nutze ebenfalls MQTT_GENERIC_BRIDGE zum &amp;quot;Weiterbefördern&amp;quot; relevanter Informationen von den Nebensystemen zum Hauptsystem.&lt;br /&gt;
&lt;br /&gt;
 Das Trennen der vom Nebensystem angelieferten Informationen übernimmt dabei das Attribut bridgeRegexp eines allgemeinen MQTT2_DEVICEs auf dem Hauptsystem.&lt;br /&gt;
&lt;br /&gt;
=Usererror 1=&lt;br /&gt;
Was bei mir gefehlt hat war die &amp;quot;base&amp;quot; Definition sowie eine Bridge mit dem entsprechenden Filter&lt;br /&gt;
&lt;br /&gt;
bridgeRegexp \&lt;br /&gt;
SmartHome/MqttGenericBridge2/([A-Za-z0-9]*)/.*:.* &amp;quot;mgb2_$1&amp;quot;\&lt;br /&gt;
&lt;br /&gt;
Das Autocreate hat nach den Anpassungen dann auch funktioniert. Die letzte Herausforderung ist nun, dass ich auf dem Slavesystem Namen mit einem Underscore habe und diese abgeschnitten werden. Experimente mit der RegEx Definition hatten bisher noch keinen Erfolg. &lt;br /&gt;
&lt;br /&gt;
Da dieser Matchingparameter [([A-Za-z0-9]*)] bei den Underscores aufhört bei der Deviceerstellung, habe ich eine Wildcard [.*] genutz... ist aber nicht effektiv, da alle Radings in einem Device landen &lt;br /&gt;
&lt;br /&gt;
([A-Za-z0-9]...*) [https://forum.fhem.de/index.php?topic=107145.0 weitere Varianten]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
hat das Problem mit den _ in den Devicenamen gelöst.&lt;br /&gt;
&lt;br /&gt;
([^/]+)&lt;br /&gt;
&lt;br /&gt;
https://forum.fhem.de/index.php?topic=98206.0&lt;br /&gt;
&lt;br /&gt;
=Fehler 2=&lt;br /&gt;
Ich falsch davon ausgegangen, dass die Port-Angabe entfallen kann wenn sie Standard (1883) ist.&lt;br /&gt;
&lt;br /&gt;
 defmod MQTT_CLX_FHEMClient MQTT2_CLIENT 192.168.178.10:1883&lt;br /&gt;
&lt;br /&gt;
Defmod mit Port-Angabe hat dann auch direkt ein Publish zugelassen.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_00_MQTT2_CLIENT.pm&amp;diff=4862</id>
		<title>(FHEM) 00 MQTT2 CLIENT.pm</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_00_MQTT2_CLIENT.pm&amp;diff=4862"/>
		<updated>2026-04-14T16:58:19Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[(FHEM) MQTT#Module im Detail| [Zurueck Uebersicht]]] &lt;br /&gt;
&lt;br /&gt;
Uebersetzung aus dem FHEM WIKI&lt;br /&gt;
&lt;br /&gt;
  MQTT2_CLIENT ist eine Reinraumimplementierung eines MQTT-Clients (der eine Verbindung zu einem externen Server wie mosquitto herstellt) ohne Perl-Bibliotheken. Es dient als IODev für MQTT2_DEVICES.&lt;br /&gt;
&lt;br /&gt;
Was auch immer eine Reinraumimplementierung sein soll. Ich vermute es ist damit gemeint, dass die Programmierung &amp;quot;auszerhalb&amp;quot; des FHEM-Frameworks arbeitet.   &lt;br /&gt;
&lt;br /&gt;
=Steckbrief=&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Define&#039;&#039;&#039;||define &amp;lt;name&amp;gt; MQTT2_CLIENT &amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;4&amp;quot;|&#039;&#039;&#039;Set&#039;&#039;&#039;||publish [-r] topic value||  Verschickt eine Nachricht an den Topic; -r setzt das retain Flag&lt;br /&gt;
|-&lt;br /&gt;
|password &amp;lt;password&amp;gt; value|| Setzt das Passwort, welches in FHEM/FhemUtils/uniqueID gespeicher ist. Wenn kein &amp;lt;passwort&amp;gt; uebergeben wird, wird das Passwort geloescht.&lt;br /&gt;
|-&lt;br /&gt;
|connect||Dient zum manuellen Verbinden an den Broker.&lt;br /&gt;
|-&lt;br /&gt;
|disconnect||Dient zum manuellen Unterbrechen der Verbindung zum Broker.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Get&#039;&#039;&#039;||&amp;amp;nbsp;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Readings&#039;&#039;&#039;||&amp;amp;nbsp;&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;21&amp;quot;|&#039;&#039;&#039;Attributes&#039;&#039;&#039;||&amp;quot;autocreate [no|simple|complex]&amp;quot;||siehe Erlaeuterung&lt;br /&gt;
|-&lt;br /&gt;
|binaryTopicRegexp &amp;lt;regular-expression&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|ignoreRegexp||&lt;br /&gt;
|-&lt;br /&gt;
|clientId &amp;lt;name&amp;gt;||setzt die ClientId&lt;br /&gt;
|-&lt;br /&gt;
|clientOrder [MQTT2_DEVICE] [MQTT_GENERIC_BRIDGE]||&lt;br /&gt;
|-&lt;br /&gt;
|connectTimeout &amp;lt;seconds&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|disable||&lt;br /&gt;
|-&lt;br /&gt;
|disabledForIntervals||&lt;br /&gt;
|-&lt;br /&gt;
|disconnectAfter &amp;lt;seconds&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|keepaliveTimeout &amp;lt;seconds;&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|lwt||&lt;br /&gt;
|-&lt;br /&gt;
|lwtRetain||&lt;br /&gt;
|-&lt;br /&gt;
|mqttVersion 3.1,3.1.1||&lt;br /&gt;
|-&lt;br /&gt;
|msgAfterConnect [-r] topic message||&lt;br /&gt;
|-&lt;br /&gt;
|msgBeforeDisconnect [-r] topic message||&lt;br /&gt;
|-&lt;br /&gt;
|qosMaxQueueLength &amp;lt;number&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|rawEvents &amp;lt;topic-regexp&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|subscriptions &amp;lt;subscriptions&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|SSL||&lt;br /&gt;
|-&lt;br /&gt;
|sslargs||&lt;br /&gt;
|-&lt;br /&gt;
|username &amp;lt;username&amp;gt;||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Erlaeuterungen=&lt;br /&gt;
 Es wird nur QoS 0 und 1 unterstuetzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Saetze evtl. zum Einbauen.&lt;br /&gt;
 Ich nutze ebenfalls MQTT_GENERIC_BRIDGE zum &amp;quot;Weiterbefördern&amp;quot; relevanter Informationen von den Nebensystemen zum Hauptsystem.&lt;br /&gt;
&lt;br /&gt;
 Das Trennen der vom Nebensystem angelieferten Informationen übernimmt dabei das Attribut bridgeRegexp eines allgemeinen MQTT2_DEVICEs auf dem Hauptsystem.&lt;br /&gt;
&lt;br /&gt;
=Fehler 1=&lt;br /&gt;
Was bei mir gefehlt hat war die &amp;quot;base&amp;quot; Definition sowie eine Bridge mit dem entsprechenden Filter&lt;br /&gt;
&lt;br /&gt;
bridgeRegexp \&lt;br /&gt;
SmartHome/MqttGenericBridge2/([A-Za-z0-9]*)/.*:.* &amp;quot;mgb2_$1&amp;quot;\&lt;br /&gt;
&lt;br /&gt;
Das Autocreate hat nach den Anpassungen dann auch funktioniert. Die letzte Herausforderung ist nun, dass ich auf dem Slavesystem Namen mit einem Underscore habe und diese abgeschnitten werden. Experimente mit der RegEx Definition hatten bisher noch keinen Erfolg. &lt;br /&gt;
&lt;br /&gt;
Da dieser Matchingparameter [([A-Za-z0-9]*)] bei den Underscores aufhört bei der Deviceerstellung, habe ich eine Wildcard [.*] genutz... ist aber nicht effektiv, da alle Radings in einem Device landen &lt;br /&gt;
&lt;br /&gt;
([A-Za-z0-9]...*) [https://forum.fhem.de/index.php?topic=107145.0 weitere Varianten]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
hat das Problem mit den _ in den Devicenamen gelöst.&lt;br /&gt;
&lt;br /&gt;
([^/]+)&lt;br /&gt;
&lt;br /&gt;
https://forum.fhem.de/index.php?topic=98206.0&lt;br /&gt;
&lt;br /&gt;
=Fehler 2=&lt;br /&gt;
Ich falsch davon ausgegangen, dass die Port-Angabe entfallen kann wenn sie Standard (1883) ist.&lt;br /&gt;
&lt;br /&gt;
 defmod MQTT_CLX_FHEMClient MQTT2_CLIENT 192.168.178.10:1883&lt;br /&gt;
&lt;br /&gt;
Defmod mit Port-Angabe hat dann auch direkt ein Publish zugelassen.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4861</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4861"/>
		<updated>2026-04-14T13:32:51Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
 FTUI V2 hat bei mir zwar funktioniert, war aber einfach auf einem Tablet zu langsam. Bei einer Seite mit mehr als 20 Widgets konnte man dem mühsamen Aufbau zuschauen. An einen schnellen Seitenwechsel ist nicht zu denken. Da war mein ASCII-Tablet (MT701) aus den 2000er deutlich schneller.  &lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
    Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer das &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur mein Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte man selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstirg reicht mir das das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/incons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Kleiner Ausflug in mein KNX.&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Windows-Surface_als_Smarthome-Display&amp;diff=4860</id>
		<title>Windows-Surface als Smarthome-Display</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Windows-Surface_als_Smarthome-Display&amp;diff=4860"/>
		<updated>2026-04-14T08:11:44Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Die beiden [https://en.wikipedia.org/wiki/Surface_(2012_tablet) WindowsRT Surface Displays] waren einfach zu schade, um Sie nur rumliegen zu haben. &lt;br /&gt;
&lt;br /&gt;
Die Hauptseite ist [https://tippvomtibb.de/wiki/index.php?title=Windows_Surface_RT hier]&lt;br /&gt;
&lt;br /&gt;
Ziele die ich verfolge.&lt;br /&gt;
&lt;br /&gt;
* Autostart Browser Fullscreen auf FHEM Startseite&lt;br /&gt;
* periodisches An-/und Abschalten der Spannungsversorgung (Akku zwischen 60-80% halten)&lt;br /&gt;
* Virtuelle Tastatur bei Windows-Button Betaetigung&lt;br /&gt;
* Watchdog/Keepalive WLAN&lt;br /&gt;
* Timeout: automatische Rueckkehr zum Startbildschirm&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
=WLAN Problem=&lt;br /&gt;
 [    9.704201] mwifiex_sdio mmc2:0001:1: info: FW download over, size 533976 bytes&lt;br /&gt;
 [    9.945765] mwifiex_sdio mmc2:0001:1: WLAN FW is active&lt;br /&gt;
 [   10.210971] mwifiex_sdio mmc2:0001:1: CMD_RESP: cmd 0x242 error, result=0x2&lt;br /&gt;
 [   10.211047] mwifiex_sdio mmc2:0001:1: mwifiex_process_cmdresp: cmd 0x242 failed during       initialization&lt;br /&gt;
 [   10.535490] mwifiex_sdio mmc2:0001:1: info: MWIFIEX VERSION: mwifiex 1.0 (14.68.29.p59) &lt;br /&gt;
 [   10.535524] mwifiex_sdio mmc2:0001:1: driver_version = mwifiex 1.0 (14.68.29.p59) &lt;br /&gt;
 [   17.295906] mwifiex_sdio mmc2:0001:1: info: trying to associate to bssid 24:5a:4c:22:8a:8f&lt;br /&gt;
 [   17.324014] mwifiex_sdio mmc2:0001:1: info: associated to bssid 24:5a:4c:22:8a:8f successfully&lt;br /&gt;
 [   17.354089] mwifiex_sdio mmc2:0001:1: CMD_RESP: cmd 0x23f error, result=0x2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Problem ist [https://github.com/jakeday/linux-surface/issues/420 hier] und [https://wiki.postmarketos.org/wiki/Microsoft_Surface_RT_(microsoft-surface-rt) hier]geloest.&lt;br /&gt;
&lt;br /&gt;
Das Modul mwifiex_sdio muss noch mit modprobe geladen werden.&lt;br /&gt;
&lt;br /&gt;
 sudo modprobe mwifiex_sdio&lt;br /&gt;
&lt;br /&gt;
Nicht wundern, es ist scheinbar standardmaeszig kein Modul geladen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Module                  Size  Used by&lt;br /&gt;
mwifiex_sdio           28672  0&lt;br /&gt;
mwifiex               237568  1 mwifiex_sdio&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Sobald das Modul geladen ist kann man das WLAN benutzen.&lt;br /&gt;
&lt;br /&gt;
=Feste IP=&lt;br /&gt;
Ab Bookworm wird nicht mehr etc/dhcp.conf zu hinterlegen genommen, sondern jetzt dient dazu der NetworkManager fuer die Aufgaben rund ums Netzwerk.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl restart NetworkManager &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=WLAN/LAN Management=&lt;br /&gt;
&lt;br /&gt;
 nmcli device wifi list&lt;br /&gt;
&lt;br /&gt;
 nmcli device show&lt;br /&gt;
&lt;br /&gt;
 nmcli connection show&lt;br /&gt;
&lt;br /&gt;
=Surfstick=&lt;br /&gt;
Ich benutzederzeit keinen Surfstick in den Displays.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl disable --now ModemManager&lt;br /&gt;
&lt;br /&gt;
=Reboot=&lt;br /&gt;
Ein Reboot funktioniert nicht wirklich, da kein ACPI, kein BIOS sondern ueber die Firmware des SoC Tegra3.&lt;br /&gt;
 &lt;br /&gt;
Das Surface faehrt zwar runter bleibt aber beim Poweroff haengen.&lt;br /&gt;
&lt;br /&gt;
Wenn keine Bildschirmanzeige da ist, die auf das Warten auf den Powerknopf hinweist, dann gibt das Beruehren des Windowsknopfes ein Vibrationssignal von sich. &lt;br /&gt;
&lt;br /&gt;
Derzeit kenne ich nur die Loesung 5 Sekunden Powerknopf festhalten und wieder kurz druecken.&lt;br /&gt;
&lt;br /&gt;
=Autostart=&lt;br /&gt;
Mist. Nach dem Wechsel auf Labwc (Wayland) geht mein Autostart nicht mehr.&lt;br /&gt;
&lt;br /&gt;
 echo $XDG_SESSION_TYPE&lt;br /&gt;
&lt;br /&gt;
Mit&lt;br /&gt;
&lt;br /&gt;
 sudo raspi-config&lt;br /&gt;
&lt;br /&gt;
erhaelt man unter Punkt 6 eine Umschaltmoeglichkeit. &lt;br /&gt;
&lt;br /&gt;
X11     Openbox window manager with X11 backend&lt;br /&gt;
Wayfire Wayfire window manager with Wayland backend&lt;br /&gt;
Labwc   Labwc window manager with Wayland backend&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir -p ~/.config/labwc&lt;br /&gt;
nano ~/.config/labwc/autostart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Windows-Taste=&lt;br /&gt;
Um die Windowstaste (Super_L) anzupassen, aendert man einfach nur den Eintrag &amp;lt;command&amp;gt; in /etc/xdg/openbox/lxde-pi-rc.xml.&lt;br /&gt;
[https://forums.raspberrypi.com/viewtopic.php?t=209261]&lt;br /&gt;
&lt;br /&gt;
Standrardmaeszig steht dort &#039;lxpanelctl menu&#039;, was aber nur das Startmenu oeffnet. Ich moechte aber, dass bei jeder Button-Aktivitaet das Panel sichtbar und wieder unsichtbar wird.&lt;br /&gt;
&lt;br /&gt;
Das Startmenu bekomme ich dann ja durch Klicken auf die Himbeere.&lt;br /&gt;
&lt;br /&gt;
In Raspbian 12 Bookworm laeuft das lxpanel mit dem Profil LXDE-pi.&lt;br /&gt;
&lt;br /&gt;
Die passende panel-Config-Datei befindet sich in .config/lxpanel/LXDE-pi/panels/.&lt;br /&gt;
&lt;br /&gt;
In der Panel-Datei muss man also den Eintrag autohide zwischen 0 und 1 wechseln und lxpanelctl restart ausfuehren.&lt;br /&gt;
&lt;br /&gt;
Also die Idee ist wie folgt.&lt;br /&gt;
&lt;br /&gt;
An den Super_L wird ein Bash-Script gebunden, welches den autohide Eintrag toggelt und das Panel neu startet.&lt;br /&gt;
&lt;br /&gt;
Das Toggeln uebernimmt sed.&lt;br /&gt;
 sed -i &#039;s/^autohide=1$/autohide=0/&#039; .config/lxpanel/LXDE-pi/panels/panel&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ich habe mal meine Testzeilen dringelassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
configfile=&amp;quot;/home/chris/.config/lxpanel/LXDE-pi/panels/panel&amp;quot;&lt;br /&gt;
sed=&amp;quot;/usr/bin/sed&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$sed -i &#039;s/^autohide=1$/autohide=0/;ta;q1;:a&#039; $configfile&lt;br /&gt;
retval=&amp;quot;$?&amp;quot;&lt;br /&gt;
#echo &amp;quot;1-&amp;gt;0 $retval&amp;quot;&lt;br /&gt;
#if [ $retval -ne 1 ]; then&lt;br /&gt;
#    echo &amp;quot;exit&amp;quot;&lt;br /&gt;
#    exit 0&lt;br /&gt;
#fi&lt;br /&gt;
[ $retval -ne 1 ] &amp;amp;&amp;amp; exit&lt;br /&gt;
$sed -i &#039;s/^autohide=0$/autohide=1/;ta;q2;:a&#039; $configfile&lt;br /&gt;
#retval=&amp;quot;$?&amp;quot;&lt;br /&gt;
#echo &amp;quot;0-&amp;gt;1 $retval&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==DTB Datei==&lt;br /&gt;
&lt;br /&gt;
 DTB = Device Tree Binary&lt;br /&gt;
&lt;br /&gt;
In dieser Datei ist die Hardwarestruktur des PI o.ä. codiert. Diese Datei ist binaer und somit nicht einfach lesbar. Hierzu schreibt Jeff Geerling:&lt;br /&gt;
&lt;br /&gt;
How to customize the dtb (device tree binary) on the Raspberry Pi&lt;br /&gt;
November 17, 2023&lt;br /&gt;
&lt;br /&gt;
Every so often, when you&#039;re debugging weird hardware issues on SBCs like the Raspberry Pi, it&#039;s useful to get way down into the guts of how the Pi represents its hardware to Linux.&lt;br /&gt;
&lt;br /&gt;
And the Linux kernel uses a method called Device Tree overlays to do it. On the Pi 5 (and other Pis), these overlays are stored as .dtb files inside the /boot/firmware directory, and there&#039;s an overlay for every major Raspberry Pi hardware model.&lt;br /&gt;
&lt;br /&gt;
I&#039;ve had to modify the dtb files in the past to increase the PCIe BAR space for early GPU testing on the Compute Module 4. And recently I&#039;ve had to mess with how the PCIe address space is set up for testing certain devices on the Raspberry Pi 5.&lt;br /&gt;
&lt;br /&gt;
The problem is, you can&#039;t just hand-edit a .dtb file—they&#039;re in a format readable only by the Linux kernel. You have to decompile the .dtb file to a .dts (source) file, edit it, then recompile it to a .dtb.&lt;br /&gt;
&lt;br /&gt;
As an example, I needed to change the msi-parent for the Pi 5&#039;s external PCIe connector to allow for full MSI-X support for a Google Coral TPU (work on getting it to work is ongoing):&lt;br /&gt;
&lt;br /&gt;
# Back up the current dtb&lt;br /&gt;
 sudo cp /boot/firmware/bcm2712-rpi-5-b.dtb /boot/firmware/bcm2712-rpi-5-b.dtb.bak&lt;br /&gt;
&lt;br /&gt;
# Decompile the current dtb (ignore warnings)&lt;br /&gt;
 dtc -I dtb -O dts /boot/firmware/bcm2712-rpi-5-b.dtb -o ~/test.dts&lt;br /&gt;
&lt;br /&gt;
# Edit the file&lt;br /&gt;
 nano ~/test.dts&lt;br /&gt;
&lt;br /&gt;
# Change the line: msi-parent = &amp;lt;0x2f&amp;gt;; (under `pcie@110000`)&lt;br /&gt;
# To: msi-parent = &amp;lt;0x66&amp;gt;;&lt;br /&gt;
# Then save the file.&lt;br /&gt;
&lt;br /&gt;
# Recompile the dtb and move it back to the firmware directory&lt;br /&gt;
 dtc -I dts -O dtb ~/test.dts -o ~/test.dtb&lt;br /&gt;
 sudo mv ~/test.dtb /boot/firmware/bcm2712-rpi-5-b.dtb&lt;br /&gt;
&lt;br /&gt;
Check out elinux&#039;s Device Tree Reference for more useful background info.&lt;br /&gt;
&lt;br /&gt;
I consider myself an absolute noob at Device Trees in Linux... but I&#039;ve now done this enough times that I&#039;d like a simple reference and most of the ones out there assume you are an expert-level wizard in all things Linux/hardware!&lt;br /&gt;
&lt;br /&gt;
=Virtuelle Tastatur=&lt;br /&gt;
Manchmal muss man halt doch was am Display eingeben. Das &#039;matchbox-keyboard&#039; hat mir nicht gefallen.&lt;br /&gt;
&lt;br /&gt;
Das sqeekboard kommt meinen Vorstellungen schon recht nahe.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldungen beim Start aus der Konsole:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Info: Loaded layout Path: &amp;quot;/usr/share/misc/squeekboard/keyboards/de_wide.yaml&amp;quot;&lt;br /&gt;
Info: Loaded layout Path: &amp;quot;/usr/share/misc/squeekboard/keyboards/de_wide.yaml&amp;quot;&lt;br /&gt;
Debug: Tried file &amp;quot;/usr/share/misc/squeekboard/keyboards/terminal/de_wide.yaml&amp;quot;, but it&#039;s missing: Datei oder Verzeichnis nicht gefunden (os error 2)&lt;br /&gt;
Warning: Failed to load layout from Resource: terminal/de_wide: Missing resource, skipping&lt;br /&gt;
Debug: Tried file &amp;quot;/usr/share/misc/squeekboard/keyboards/terminal/de.yaml&amp;quot;, but it&#039;s missing: Datei oder Verzeichnis nicht gefunden (os error 2)&lt;br /&gt;
Warning: Failed to load layout from Resource: terminal/de: Missing resource, skipping&lt;br /&gt;
Info: Loaded layout Path: &amp;quot;/usr/share/misc/squeekboard/keyboards/terminal/us_wide.yaml&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Windows-Surface_als_Smarthome-Display&amp;diff=4859</id>
		<title>Windows-Surface als Smarthome-Display</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Windows-Surface_als_Smarthome-Display&amp;diff=4859"/>
		<updated>2026-04-14T08:08:15Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* DTB Datei */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Die beiden [https://en.wikipedia.org/wiki/Surface_(2012_tablet) WindowsRT Surface Displays] waren einfach zu schade, um Sie nur rumliegen zu haben. &lt;br /&gt;
&lt;br /&gt;
Die Hauptseite ist [https://tippvomtibb.de/wiki/index.php?title=Windows_Surface_RT hier]&lt;br /&gt;
&lt;br /&gt;
Ziele die ich verfolge.&lt;br /&gt;
&lt;br /&gt;
- Autostart Browser Fullscreen auf FHEM Startseite&lt;br /&gt;
- periodisches An-/und Abschalten der Spannungsversorgung (Akku zwischen 60-80% halten)&lt;br /&gt;
- Virtuelle Tastatur bei Windows-Button Betaetigung&lt;br /&gt;
- Watchdog/Keepalive WLAN&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
=WLAN Problem=&lt;br /&gt;
 [    9.704201] mwifiex_sdio mmc2:0001:1: info: FW download over, size 533976 bytes&lt;br /&gt;
 [    9.945765] mwifiex_sdio mmc2:0001:1: WLAN FW is active&lt;br /&gt;
 [   10.210971] mwifiex_sdio mmc2:0001:1: CMD_RESP: cmd 0x242 error, result=0x2&lt;br /&gt;
 [   10.211047] mwifiex_sdio mmc2:0001:1: mwifiex_process_cmdresp: cmd 0x242 failed during       initialization&lt;br /&gt;
 [   10.535490] mwifiex_sdio mmc2:0001:1: info: MWIFIEX VERSION: mwifiex 1.0 (14.68.29.p59) &lt;br /&gt;
 [   10.535524] mwifiex_sdio mmc2:0001:1: driver_version = mwifiex 1.0 (14.68.29.p59) &lt;br /&gt;
 [   17.295906] mwifiex_sdio mmc2:0001:1: info: trying to associate to bssid 24:5a:4c:22:8a:8f&lt;br /&gt;
 [   17.324014] mwifiex_sdio mmc2:0001:1: info: associated to bssid 24:5a:4c:22:8a:8f successfully&lt;br /&gt;
 [   17.354089] mwifiex_sdio mmc2:0001:1: CMD_RESP: cmd 0x23f error, result=0x2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Problem ist [https://github.com/jakeday/linux-surface/issues/420 hier] und [https://wiki.postmarketos.org/wiki/Microsoft_Surface_RT_(microsoft-surface-rt) hier]geloest.&lt;br /&gt;
&lt;br /&gt;
Das Modul mwifiex_sdio muss noch mit modprobe geladen werden.&lt;br /&gt;
&lt;br /&gt;
 sudo modprobe mwifiex_sdio&lt;br /&gt;
&lt;br /&gt;
Nicht wundern, es ist scheinbar standardmaeszig kein Modul geladen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Module                  Size  Used by&lt;br /&gt;
mwifiex_sdio           28672  0&lt;br /&gt;
mwifiex               237568  1 mwifiex_sdio&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Sobald das Modul geladen ist kann man das WLAN benutzen.&lt;br /&gt;
&lt;br /&gt;
=Feste IP=&lt;br /&gt;
Ab Bookworm wird nicht mehr etc/dhcp.conf zu hinterlegen genommen, sondern jetzt dient dazu der NetworkManager fuer die Aufgaben rund ums Netzwerk.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl restart NetworkManager &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=WLAN/LAN Management=&lt;br /&gt;
&lt;br /&gt;
 nmcli device wifi list&lt;br /&gt;
&lt;br /&gt;
 nmcli device show&lt;br /&gt;
&lt;br /&gt;
 nmcli connection show&lt;br /&gt;
&lt;br /&gt;
=Surfstick=&lt;br /&gt;
Ich benutzederzeit keinen Surfstick in den Displays.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl disable --now ModemManager&lt;br /&gt;
&lt;br /&gt;
=Reboot=&lt;br /&gt;
Ein Reboot funktioniert nicht wirklich, da kein ACPI, kein BIOS sondern ueber die Firmware des SoC Tegra3.&lt;br /&gt;
 &lt;br /&gt;
Das Surface faehrt zwar runter bleibt aber beim Poweroff haengen.&lt;br /&gt;
&lt;br /&gt;
Wenn keine Bildschirmanzeige da ist, die auf das Warten auf den Powerknopf hinweist, dann gibt das Beruehren des Windowsknopfes ein Vibrationssignal von sich. &lt;br /&gt;
&lt;br /&gt;
Derzeit kenne ich nur die Loesung 5 Sekunden Powerknopf festhalten und wieder kurz druecken.&lt;br /&gt;
&lt;br /&gt;
=Autostart=&lt;br /&gt;
Mist. Nach dem Wechsel auf Labwc (Wayland) geht mein Autostart nicht mehr.&lt;br /&gt;
&lt;br /&gt;
 echo $XDG_SESSION_TYPE&lt;br /&gt;
&lt;br /&gt;
Mit&lt;br /&gt;
&lt;br /&gt;
 sudo raspi-config&lt;br /&gt;
&lt;br /&gt;
erhaelt man unter Punkt 6 eine Umschaltmoeglichkeit. &lt;br /&gt;
&lt;br /&gt;
X11     Openbox window manager with X11 backend&lt;br /&gt;
Wayfire Wayfire window manager with Wayland backend&lt;br /&gt;
Labwc   Labwc window manager with Wayland backend&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir -p ~/.config/labwc&lt;br /&gt;
nano ~/.config/labwc/autostart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Windows-Taste=&lt;br /&gt;
Um die Windowstaste (Super_L) anzupassen, aendert man einfach nur den Eintrag &amp;lt;command&amp;gt; in /etc/xdg/openbox/lxde-pi-rc.xml.&lt;br /&gt;
[https://forums.raspberrypi.com/viewtopic.php?t=209261]&lt;br /&gt;
&lt;br /&gt;
Standrardmaeszig steht dort &#039;lxpanelctl menu&#039;, was aber nur das Startmenu oeffnet. Ich moechte aber, dass bei jeder Button-Aktivitaet das Panel sichtbar und wieder unsichtbar wird.&lt;br /&gt;
&lt;br /&gt;
Das Startmenu bekomme ich dann ja durch Klicken auf die Himbeere.&lt;br /&gt;
&lt;br /&gt;
In Raspbian 12 Bookworm laeuft das lxpanel mit dem Profil LXDE-pi.&lt;br /&gt;
&lt;br /&gt;
Die passende panel-Config-Datei befindet sich in .config/lxpanel/LXDE-pi/panels/.&lt;br /&gt;
&lt;br /&gt;
In der Panel-Datei muss man also den Eintrag autohide zwischen 0 und 1 wechseln und lxpanelctl restart ausfuehren.&lt;br /&gt;
&lt;br /&gt;
Also die Idee ist wie folgt.&lt;br /&gt;
&lt;br /&gt;
An den Super_L wird ein Bash-Script gebunden, welches den autohide Eintrag toggelt und das Panel neu startet.&lt;br /&gt;
&lt;br /&gt;
Das Toggeln uebernimmt sed.&lt;br /&gt;
 sed -i &#039;s/^autohide=1$/autohide=0/&#039; .config/lxpanel/LXDE-pi/panels/panel&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ich habe mal meine Testzeilen dringelassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
configfile=&amp;quot;/home/chris/.config/lxpanel/LXDE-pi/panels/panel&amp;quot;&lt;br /&gt;
sed=&amp;quot;/usr/bin/sed&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$sed -i &#039;s/^autohide=1$/autohide=0/;ta;q1;:a&#039; $configfile&lt;br /&gt;
retval=&amp;quot;$?&amp;quot;&lt;br /&gt;
#echo &amp;quot;1-&amp;gt;0 $retval&amp;quot;&lt;br /&gt;
#if [ $retval -ne 1 ]; then&lt;br /&gt;
#    echo &amp;quot;exit&amp;quot;&lt;br /&gt;
#    exit 0&lt;br /&gt;
#fi&lt;br /&gt;
[ $retval -ne 1 ] &amp;amp;&amp;amp; exit&lt;br /&gt;
$sed -i &#039;s/^autohide=0$/autohide=1/;ta;q2;:a&#039; $configfile&lt;br /&gt;
#retval=&amp;quot;$?&amp;quot;&lt;br /&gt;
#echo &amp;quot;0-&amp;gt;1 $retval&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==DTB Datei==&lt;br /&gt;
&lt;br /&gt;
 DTB = Device Tree Binary&lt;br /&gt;
&lt;br /&gt;
In dieser Datei ist die Hardwarestruktur des PI o.ä. codiert. Diese Datei ist binaer und somit nicht einfach lesbar. Hierzu schreibt Jeff Geerling:&lt;br /&gt;
&lt;br /&gt;
How to customize the dtb (device tree binary) on the Raspberry Pi&lt;br /&gt;
November 17, 2023&lt;br /&gt;
&lt;br /&gt;
Every so often, when you&#039;re debugging weird hardware issues on SBCs like the Raspberry Pi, it&#039;s useful to get way down into the guts of how the Pi represents its hardware to Linux.&lt;br /&gt;
&lt;br /&gt;
And the Linux kernel uses a method called Device Tree overlays to do it. On the Pi 5 (and other Pis), these overlays are stored as .dtb files inside the /boot/firmware directory, and there&#039;s an overlay for every major Raspberry Pi hardware model.&lt;br /&gt;
&lt;br /&gt;
I&#039;ve had to modify the dtb files in the past to increase the PCIe BAR space for early GPU testing on the Compute Module 4. And recently I&#039;ve had to mess with how the PCIe address space is set up for testing certain devices on the Raspberry Pi 5.&lt;br /&gt;
&lt;br /&gt;
The problem is, you can&#039;t just hand-edit a .dtb file—they&#039;re in a format readable only by the Linux kernel. You have to decompile the .dtb file to a .dts (source) file, edit it, then recompile it to a .dtb.&lt;br /&gt;
&lt;br /&gt;
As an example, I needed to change the msi-parent for the Pi 5&#039;s external PCIe connector to allow for full MSI-X support for a Google Coral TPU (work on getting it to work is ongoing):&lt;br /&gt;
&lt;br /&gt;
# Back up the current dtb&lt;br /&gt;
 sudo cp /boot/firmware/bcm2712-rpi-5-b.dtb /boot/firmware/bcm2712-rpi-5-b.dtb.bak&lt;br /&gt;
&lt;br /&gt;
# Decompile the current dtb (ignore warnings)&lt;br /&gt;
 dtc -I dtb -O dts /boot/firmware/bcm2712-rpi-5-b.dtb -o ~/test.dts&lt;br /&gt;
&lt;br /&gt;
# Edit the file&lt;br /&gt;
 nano ~/test.dts&lt;br /&gt;
&lt;br /&gt;
# Change the line: msi-parent = &amp;lt;0x2f&amp;gt;; (under `pcie@110000`)&lt;br /&gt;
# To: msi-parent = &amp;lt;0x66&amp;gt;;&lt;br /&gt;
# Then save the file.&lt;br /&gt;
&lt;br /&gt;
# Recompile the dtb and move it back to the firmware directory&lt;br /&gt;
 dtc -I dts -O dtb ~/test.dts -o ~/test.dtb&lt;br /&gt;
 sudo mv ~/test.dtb /boot/firmware/bcm2712-rpi-5-b.dtb&lt;br /&gt;
&lt;br /&gt;
Check out elinux&#039;s Device Tree Reference for more useful background info.&lt;br /&gt;
&lt;br /&gt;
I consider myself an absolute noob at Device Trees in Linux... but I&#039;ve now done this enough times that I&#039;d like a simple reference and most of the ones out there assume you are an expert-level wizard in all things Linux/hardware!&lt;br /&gt;
&lt;br /&gt;
=Virtuelle Tastatur=&lt;br /&gt;
Manchmal muss man halt doch was am Display eingeben. Das &#039;matchbox-keyboard&#039; hat mir nicht gefallen.&lt;br /&gt;
&lt;br /&gt;
Das sqeekboard kommt meinen Vorstellungen schon recht nahe.&lt;br /&gt;
&lt;br /&gt;
Fehlermeldungen beim Start aus der Konsole:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Info: Loaded layout Path: &amp;quot;/usr/share/misc/squeekboard/keyboards/de_wide.yaml&amp;quot;&lt;br /&gt;
Info: Loaded layout Path: &amp;quot;/usr/share/misc/squeekboard/keyboards/de_wide.yaml&amp;quot;&lt;br /&gt;
Debug: Tried file &amp;quot;/usr/share/misc/squeekboard/keyboards/terminal/de_wide.yaml&amp;quot;, but it&#039;s missing: Datei oder Verzeichnis nicht gefunden (os error 2)&lt;br /&gt;
Warning: Failed to load layout from Resource: terminal/de_wide: Missing resource, skipping&lt;br /&gt;
Debug: Tried file &amp;quot;/usr/share/misc/squeekboard/keyboards/terminal/de.yaml&amp;quot;, but it&#039;s missing: Datei oder Verzeichnis nicht gefunden (os error 2)&lt;br /&gt;
Warning: Failed to load layout from Resource: terminal/de: Missing resource, skipping&lt;br /&gt;
Info: Loaded layout Path: &amp;quot;/usr/share/misc/squeekboard/keyboards/terminal/us_wide.yaml&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Windows-Surface_als_Smarthome-Display&amp;diff=4858</id>
		<title>Windows-Surface als Smarthome-Display</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Windows-Surface_als_Smarthome-Display&amp;diff=4858"/>
		<updated>2026-04-14T07:55:29Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Die beiden [https://en.wikipedia.org/wiki/Surface_(2012_tablet) WindowsRT Surface Displays] waren einfach zu schade, um Sie nur rumliegen zu haben. &lt;br /&gt;
&lt;br /&gt;
Die Hauptseite ist [https://tippvomtibb.de/wiki/index.php?title=Windows_Surface_RT hier]&lt;br /&gt;
&lt;br /&gt;
Ziele die ich verfolge.&lt;br /&gt;
&lt;br /&gt;
- Autostart Browser Fullscreen auf FHEM Startseite&lt;br /&gt;
- periodisches An-/und Abschalten der Spannungsversorgung (Akku zwischen 60-80% halten)&lt;br /&gt;
- Virtuelle Tastatur bei Windows-Button Betaetigung&lt;br /&gt;
- Watchdog/Keepalive WLAN&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
=WLAN Problem=&lt;br /&gt;
 [    9.704201] mwifiex_sdio mmc2:0001:1: info: FW download over, size 533976 bytes&lt;br /&gt;
 [    9.945765] mwifiex_sdio mmc2:0001:1: WLAN FW is active&lt;br /&gt;
 [   10.210971] mwifiex_sdio mmc2:0001:1: CMD_RESP: cmd 0x242 error, result=0x2&lt;br /&gt;
 [   10.211047] mwifiex_sdio mmc2:0001:1: mwifiex_process_cmdresp: cmd 0x242 failed during       initialization&lt;br /&gt;
 [   10.535490] mwifiex_sdio mmc2:0001:1: info: MWIFIEX VERSION: mwifiex 1.0 (14.68.29.p59) &lt;br /&gt;
 [   10.535524] mwifiex_sdio mmc2:0001:1: driver_version = mwifiex 1.0 (14.68.29.p59) &lt;br /&gt;
 [   17.295906] mwifiex_sdio mmc2:0001:1: info: trying to associate to bssid 24:5a:4c:22:8a:8f&lt;br /&gt;
 [   17.324014] mwifiex_sdio mmc2:0001:1: info: associated to bssid 24:5a:4c:22:8a:8f successfully&lt;br /&gt;
 [   17.354089] mwifiex_sdio mmc2:0001:1: CMD_RESP: cmd 0x23f error, result=0x2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Problem ist [https://github.com/jakeday/linux-surface/issues/420 hier] und [https://wiki.postmarketos.org/wiki/Microsoft_Surface_RT_(microsoft-surface-rt) hier]geloest.&lt;br /&gt;
&lt;br /&gt;
Das Modul mwifiex_sdio muss noch mit modprobe geladen werden.&lt;br /&gt;
&lt;br /&gt;
 sudo modprobe mwifiex_sdio&lt;br /&gt;
&lt;br /&gt;
Nicht wundern, es ist scheinbar standardmaeszig kein Modul geladen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Module                  Size  Used by&lt;br /&gt;
mwifiex_sdio           28672  0&lt;br /&gt;
mwifiex               237568  1 mwifiex_sdio&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Sobald das Modul geladen ist kann man das WLAN benutzen.&lt;br /&gt;
&lt;br /&gt;
=Feste IP=&lt;br /&gt;
Ab Bookworm wird nicht mehr etc/dhcp.conf zu hinterlegen genommen, sondern jetzt dient dazu der NetworkManager fuer die Aufgaben rund ums Netzwerk.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl restart NetworkManager &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=WLAN/LAN Management=&lt;br /&gt;
&lt;br /&gt;
 nmcli device wifi list&lt;br /&gt;
&lt;br /&gt;
 nmcli device show&lt;br /&gt;
&lt;br /&gt;
 nmcli connection show&lt;br /&gt;
&lt;br /&gt;
=Surfstick=&lt;br /&gt;
Ich benutzederzeit keinen Surfstick in den Displays.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl disable --now ModemManager&lt;br /&gt;
&lt;br /&gt;
=Reboot=&lt;br /&gt;
Ein Reboot funktioniert nicht wirklich, da kein ACPI, kein BIOS sondern ueber die Firmware des SoC Tegra3.&lt;br /&gt;
 &lt;br /&gt;
Das Surface faehrt zwar runter bleibt aber beim Poweroff haengen.&lt;br /&gt;
&lt;br /&gt;
Wenn keine Bildschirmanzeige da ist, die auf das Warten auf den Powerknopf hinweist, dann gibt das Beruehren des Windowsknopfes ein Vibrationssignal von sich. &lt;br /&gt;
&lt;br /&gt;
Derzeit kenne ich nur die Loesung 5 Sekunden Powerknopf festhalten und wieder kurz druecken.&lt;br /&gt;
&lt;br /&gt;
=Autostart=&lt;br /&gt;
Mist. Nach dem Wechsel auf Labwc (Wayland) geht mein Autostart nicht mehr.&lt;br /&gt;
&lt;br /&gt;
 echo $XDG_SESSION_TYPE&lt;br /&gt;
&lt;br /&gt;
Mit&lt;br /&gt;
&lt;br /&gt;
 sudo raspi-config&lt;br /&gt;
&lt;br /&gt;
erhaelt man unter Punkt 6 eine Umschaltmoeglichkeit. &lt;br /&gt;
&lt;br /&gt;
X11     Openbox window manager with X11 backend&lt;br /&gt;
Wayfire Wayfire window manager with Wayland backend&lt;br /&gt;
Labwc   Labwc window manager with Wayland backend&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir -p ~/.config/labwc&lt;br /&gt;
nano ~/.config/labwc/autostart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Windows-Taste=&lt;br /&gt;
Um die Windowstaste (Super_L) anzupassen, aendert man einfach nur den Eintrag &amp;lt;command&amp;gt; in /etc/xdg/openbox/lxde-pi-rc.xml.&lt;br /&gt;
[https://forums.raspberrypi.com/viewtopic.php?t=209261]&lt;br /&gt;
&lt;br /&gt;
Standrardmaeszig steht dort &#039;lxpanelctl menu&#039;, was aber nur das Startmenu oeffnet. Ich moechte aber, dass bei jeder Button-Aktivitaet das Panel sichtbar und wieder unsichtbar wird.&lt;br /&gt;
&lt;br /&gt;
Das Startmenu bekomme ich dann ja durch Klicken auf die Himbeere.&lt;br /&gt;
&lt;br /&gt;
In Raspbian 12 Bookworm laeuft das lxpanel mit dem Profil LXDE-pi.&lt;br /&gt;
&lt;br /&gt;
Die passende panel-Config-Datei befindet sich in .config/lxpanel/LXDE-pi/panels/.&lt;br /&gt;
&lt;br /&gt;
In der Panel-Datei muss man also den Eintrag autohide zwischen 0 und 1 wechseln und lxpanelctl restart ausfuehren.&lt;br /&gt;
&lt;br /&gt;
Also die Idee ist wie folgt.&lt;br /&gt;
&lt;br /&gt;
An den Super_L wird ein Bash-Script gebunden, welches den autohide Eintrag toggelt und das Panel neu startet.&lt;br /&gt;
&lt;br /&gt;
Das Toggeln uebernimmt sed.&lt;br /&gt;
 sed -i &#039;s/^autohide=1$/autohide=0/&#039; .config/lxpanel/LXDE-pi/panels/panel&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ich habe mal meine Testzeilen dringelassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
configfile=&amp;quot;/home/chris/.config/lxpanel/LXDE-pi/panels/panel&amp;quot;&lt;br /&gt;
sed=&amp;quot;/usr/bin/sed&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$sed -i &#039;s/^autohide=1$/autohide=0/;ta;q1;:a&#039; $configfile&lt;br /&gt;
retval=&amp;quot;$?&amp;quot;&lt;br /&gt;
#echo &amp;quot;1-&amp;gt;0 $retval&amp;quot;&lt;br /&gt;
#if [ $retval -ne 1 ]; then&lt;br /&gt;
#    echo &amp;quot;exit&amp;quot;&lt;br /&gt;
#    exit 0&lt;br /&gt;
#fi&lt;br /&gt;
[ $retval -ne 1 ] &amp;amp;&amp;amp; exit&lt;br /&gt;
$sed -i &#039;s/^autohide=0$/autohide=1/;ta;q2;:a&#039; $configfile&lt;br /&gt;
#retval=&amp;quot;$?&amp;quot;&lt;br /&gt;
#echo &amp;quot;0-&amp;gt;1 $retval&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==DTB Datei==&lt;br /&gt;
&lt;br /&gt;
 DTB = Device Tree Binary&lt;br /&gt;
&lt;br /&gt;
In dieser Datei ist die Hardwarestruktur des PI o.ä. codiert. Diese Datei ist binaer und somit nicht einfach lesbar. Hierzu schreibt Jeff Geerling:&lt;br /&gt;
&lt;br /&gt;
How to customize the dtb (device tree binary) on the Raspberry Pi&lt;br /&gt;
November 17, 2023&lt;br /&gt;
&lt;br /&gt;
Every so often, when you&#039;re debugging weird hardware issues on SBCs like the Raspberry Pi, it&#039;s useful to get way down into the guts of how the Pi represents its hardware to Linux.&lt;br /&gt;
&lt;br /&gt;
And the Linux kernel uses a method called Device Tree overlays to do it. On the Pi 5 (and other Pis), these overlays are stored as .dtb files inside the /boot/firmware directory, and there&#039;s an overlay for every major Raspberry Pi hardware model.&lt;br /&gt;
&lt;br /&gt;
I&#039;ve had to modify the dtb files in the past to increase the PCIe BAR space for early GPU testing on the Compute Module 4. And recently I&#039;ve had to mess with how the PCIe address space is set up for testing certain devices on the Raspberry Pi 5.&lt;br /&gt;
&lt;br /&gt;
The problem is, you can&#039;t just hand-edit a .dtb file—they&#039;re in a format readable only by the Linux kernel. You have to decompile the .dtb file to a .dts (source) file, edit it, then recompile it to a .dtb.&lt;br /&gt;
&lt;br /&gt;
As an example, I needed to change the msi-parent for the Pi 5&#039;s external PCIe connector to allow for full MSI-X support for a Google Coral TPU (work on getting it to work is ongoing):&lt;br /&gt;
&lt;br /&gt;
# Back up the current dtb&lt;br /&gt;
sudo cp /boot/firmware/bcm2712-rpi-5-b.dtb /boot/firmware/bcm2712-rpi-5-b.dtb.bak&lt;br /&gt;
&lt;br /&gt;
# Decompile the current dtb (ignore warnings)&lt;br /&gt;
dtc -I dtb -O dts /boot/firmware/bcm2712-rpi-5-b.dtb -o ~/test.dts&lt;br /&gt;
&lt;br /&gt;
# Edit the file&lt;br /&gt;
nano ~/test.dts&lt;br /&gt;
&lt;br /&gt;
# Change the line: msi-parent = &amp;lt;0x2f&amp;gt;; (under `pcie@110000`)&lt;br /&gt;
# To: msi-parent = &amp;lt;0x66&amp;gt;;&lt;br /&gt;
# Then save the file.&lt;br /&gt;
&lt;br /&gt;
# Recompile the dtb and move it back to the firmware directory&lt;br /&gt;
dtc -I dts -O dtb ~/test.dts -o ~/test.dtb&lt;br /&gt;
sudo mv ~/test.dtb /boot/firmware/bcm2712-rpi-5-b.dtb&lt;br /&gt;
&lt;br /&gt;
Check out elinux&#039;s Device Tree Reference for more useful background info.&lt;br /&gt;
&lt;br /&gt;
I consider myself an absolute noob at Device Trees in Linux... but I&#039;ve now done this enough times that I&#039;d like a simple reference and most of the ones out there assume you are an expert-level wizard in all things Linux/hardware!&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Windows-Surface_als_Smarthome-Display&amp;diff=4857</id>
		<title>Windows-Surface als Smarthome-Display</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Windows-Surface_als_Smarthome-Display&amp;diff=4857"/>
		<updated>2026-04-14T07:49:24Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Surfstick */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Die beiden [https://en.wikipedia.org/wiki/Surface_(2012_tablet) WindowsRT Surface Displays] waren einfach zu schade, um Sie nur rumliegen zu haben. &lt;br /&gt;
&lt;br /&gt;
Ich bin nicht mehr sicher was ich auf dieser Seite ergaenzen wollte. Die Hauptseite ist [https://tippvomtibb.de/wiki/index.php?title=Windows_Surface_RT hier]&lt;br /&gt;
&lt;br /&gt;
=WLAN Problem=&lt;br /&gt;
 [    9.704201] mwifiex_sdio mmc2:0001:1: info: FW download over, size 533976 bytes&lt;br /&gt;
 [    9.945765] mwifiex_sdio mmc2:0001:1: WLAN FW is active&lt;br /&gt;
 [   10.210971] mwifiex_sdio mmc2:0001:1: CMD_RESP: cmd 0x242 error, result=0x2&lt;br /&gt;
 [   10.211047] mwifiex_sdio mmc2:0001:1: mwifiex_process_cmdresp: cmd 0x242 failed during       initialization&lt;br /&gt;
 [   10.535490] mwifiex_sdio mmc2:0001:1: info: MWIFIEX VERSION: mwifiex 1.0 (14.68.29.p59) &lt;br /&gt;
 [   10.535524] mwifiex_sdio mmc2:0001:1: driver_version = mwifiex 1.0 (14.68.29.p59) &lt;br /&gt;
 [   17.295906] mwifiex_sdio mmc2:0001:1: info: trying to associate to bssid 24:5a:4c:22:8a:8f&lt;br /&gt;
 [   17.324014] mwifiex_sdio mmc2:0001:1: info: associated to bssid 24:5a:4c:22:8a:8f successfully&lt;br /&gt;
 [   17.354089] mwifiex_sdio mmc2:0001:1: CMD_RESP: cmd 0x23f error, result=0x2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Problem ist [https://github.com/jakeday/linux-surface/issues/420 hier] und [https://wiki.postmarketos.org/wiki/Microsoft_Surface_RT_(microsoft-surface-rt) hier]geloest.&lt;br /&gt;
&lt;br /&gt;
Das Modul mwifiex_sdio muss noch mit modprobe geladen werden.&lt;br /&gt;
&lt;br /&gt;
 sudo modprobe mwifiex_sdio&lt;br /&gt;
&lt;br /&gt;
Nicht wundern, es ist scheinbar standardmaeszig kein Modul geladen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Module                  Size  Used by&lt;br /&gt;
mwifiex_sdio           28672  0&lt;br /&gt;
mwifiex               237568  1 mwifiex_sdio&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Sobald das Modul geladen ist kann man das WLAN benutzen.&lt;br /&gt;
&lt;br /&gt;
=Feste IP=&lt;br /&gt;
Ab Bookworm wird nicht mehr etc/dhcp.conf zu hinterlegen genommen, sondern jetzt dient dazu der NetworkManager fuer die Aufgaben rund ums Netzwerk.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl restart NetworkManager &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=WLAN/LAN Management=&lt;br /&gt;
&lt;br /&gt;
 nmcli device wifi list&lt;br /&gt;
&lt;br /&gt;
 nmcli device show&lt;br /&gt;
&lt;br /&gt;
 nmcli connection show&lt;br /&gt;
&lt;br /&gt;
=Surfstick=&lt;br /&gt;
Ich benutzederzeit keinen Surfstick in den Displays.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl disable --now ModemManager&lt;br /&gt;
&lt;br /&gt;
=Reboot=&lt;br /&gt;
Ein Reboot funktioniert nicht wirklich, da kein ACPI, kein BIOS sondern ueber die Firmware des SoC Tegra3.&lt;br /&gt;
 &lt;br /&gt;
Das Surface faehrt zwar runter bleibt aber beim Poweroff haengen.&lt;br /&gt;
&lt;br /&gt;
Wenn keine Bildschirmanzeige da ist, die auf das Warten auf den Powerknopf hinweist, dann gibt das Beruehren des Windowsknopfes ein Vibrationssignal von sich. &lt;br /&gt;
&lt;br /&gt;
Derzeit kenne ich nur die Loesung 5 Sekunden Powerknopf festhalten und wieder kurz druecken.&lt;br /&gt;
&lt;br /&gt;
=Autostart=&lt;br /&gt;
Mist. Nach dem Wechsel auf Labwc (Wayland) geht mein Autostart nicht mehr.&lt;br /&gt;
&lt;br /&gt;
 echo $XDG_SESSION_TYPE&lt;br /&gt;
&lt;br /&gt;
Mit&lt;br /&gt;
&lt;br /&gt;
 sudo raspi-config&lt;br /&gt;
&lt;br /&gt;
erhaelt man unter Punkt 6 eine Umschaltmoeglichkeit. &lt;br /&gt;
&lt;br /&gt;
X11     Openbox window manager with X11 backend&lt;br /&gt;
Wayfire Wayfire window manager with Wayland backend&lt;br /&gt;
Labwc   Labwc window manager with Wayland backend&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir -p ~/.config/labwc&lt;br /&gt;
nano ~/.config/labwc/autostart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Windows-Taste=&lt;br /&gt;
Um die Windowstaste (Super_L) anzupassen, aendert man einfach nur den Eintrag &amp;lt;command&amp;gt; in /etc/xdg/openbox/lxde-pi-rc.xml.&lt;br /&gt;
[https://forums.raspberrypi.com/viewtopic.php?t=209261]&lt;br /&gt;
&lt;br /&gt;
Standrardmaeszig steht dort &#039;lxpanelctl menu&#039;, was aber nur das Startmenu oeffnet. Ich moechte aber, dass bei jeder Button-Aktivitaet das Panel sichtbar und wieder unsichtbar wird.&lt;br /&gt;
&lt;br /&gt;
Das Startmenu bekomme ich dann ja durch Klicken auf die Himbeere.&lt;br /&gt;
&lt;br /&gt;
In Raspbian 12 Bookworm laeuft das lxpanel mit dem Profil LXDE-pi.&lt;br /&gt;
&lt;br /&gt;
Die passende panel-Config-Datei befindet sich in .config/lxpanel/LXDE-pi/panels/.&lt;br /&gt;
&lt;br /&gt;
In der Panel-Datei muss man also den Eintrag autohide zwischen 0 und 1 wechseln und lxpanelctl restart ausfuehren.&lt;br /&gt;
&lt;br /&gt;
Also die Idee ist wie folgt.&lt;br /&gt;
&lt;br /&gt;
An den Super_L wird ein Bash-Script gebunden, welches den autohide Eintrag toggelt und das Panel neu startet.&lt;br /&gt;
&lt;br /&gt;
Das Toggeln uebernimmt sed.&lt;br /&gt;
 sed -i &#039;s/^autohide=1$/autohide=0/&#039; .config/lxpanel/LXDE-pi/panels/panel&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ich habe mal meine Testzeilen dringelassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
configfile=&amp;quot;/home/chris/.config/lxpanel/LXDE-pi/panels/panel&amp;quot;&lt;br /&gt;
sed=&amp;quot;/usr/bin/sed&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$sed -i &#039;s/^autohide=1$/autohide=0/;ta;q1;:a&#039; $configfile&lt;br /&gt;
retval=&amp;quot;$?&amp;quot;&lt;br /&gt;
#echo &amp;quot;1-&amp;gt;0 $retval&amp;quot;&lt;br /&gt;
#if [ $retval -ne 1 ]; then&lt;br /&gt;
#    echo &amp;quot;exit&amp;quot;&lt;br /&gt;
#    exit 0&lt;br /&gt;
#fi&lt;br /&gt;
[ $retval -ne 1 ] &amp;amp;&amp;amp; exit&lt;br /&gt;
$sed -i &#039;s/^autohide=0$/autohide=1/;ta;q2;:a&#039; $configfile&lt;br /&gt;
#retval=&amp;quot;$?&amp;quot;&lt;br /&gt;
#echo &amp;quot;0-&amp;gt;1 $retval&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==DTB Datei==&lt;br /&gt;
&lt;br /&gt;
 DTB = Device Tree Binary&lt;br /&gt;
&lt;br /&gt;
In dieser Datei ist die Hardwarestruktur des PI o.ä. codiert. Diese Datei ist binaer und somit nicht einfach lesbar. Hierzu schreibt Jeff Geerling:&lt;br /&gt;
&lt;br /&gt;
How to customize the dtb (device tree binary) on the Raspberry Pi&lt;br /&gt;
November 17, 2023&lt;br /&gt;
&lt;br /&gt;
Every so often, when you&#039;re debugging weird hardware issues on SBCs like the Raspberry Pi, it&#039;s useful to get way down into the guts of how the Pi represents its hardware to Linux.&lt;br /&gt;
&lt;br /&gt;
And the Linux kernel uses a method called Device Tree overlays to do it. On the Pi 5 (and other Pis), these overlays are stored as .dtb files inside the /boot/firmware directory, and there&#039;s an overlay for every major Raspberry Pi hardware model.&lt;br /&gt;
&lt;br /&gt;
I&#039;ve had to modify the dtb files in the past to increase the PCIe BAR space for early GPU testing on the Compute Module 4. And recently I&#039;ve had to mess with how the PCIe address space is set up for testing certain devices on the Raspberry Pi 5.&lt;br /&gt;
&lt;br /&gt;
The problem is, you can&#039;t just hand-edit a .dtb file—they&#039;re in a format readable only by the Linux kernel. You have to decompile the .dtb file to a .dts (source) file, edit it, then recompile it to a .dtb.&lt;br /&gt;
&lt;br /&gt;
As an example, I needed to change the msi-parent for the Pi 5&#039;s external PCIe connector to allow for full MSI-X support for a Google Coral TPU (work on getting it to work is ongoing):&lt;br /&gt;
&lt;br /&gt;
# Back up the current dtb&lt;br /&gt;
sudo cp /boot/firmware/bcm2712-rpi-5-b.dtb /boot/firmware/bcm2712-rpi-5-b.dtb.bak&lt;br /&gt;
&lt;br /&gt;
# Decompile the current dtb (ignore warnings)&lt;br /&gt;
dtc -I dtb -O dts /boot/firmware/bcm2712-rpi-5-b.dtb -o ~/test.dts&lt;br /&gt;
&lt;br /&gt;
# Edit the file&lt;br /&gt;
nano ~/test.dts&lt;br /&gt;
&lt;br /&gt;
# Change the line: msi-parent = &amp;lt;0x2f&amp;gt;; (under `pcie@110000`)&lt;br /&gt;
# To: msi-parent = &amp;lt;0x66&amp;gt;;&lt;br /&gt;
# Then save the file.&lt;br /&gt;
&lt;br /&gt;
# Recompile the dtb and move it back to the firmware directory&lt;br /&gt;
dtc -I dts -O dtb ~/test.dts -o ~/test.dtb&lt;br /&gt;
sudo mv ~/test.dtb /boot/firmware/bcm2712-rpi-5-b.dtb&lt;br /&gt;
&lt;br /&gt;
Check out elinux&#039;s Device Tree Reference for more useful background info.&lt;br /&gt;
&lt;br /&gt;
I consider myself an absolute noob at Device Trees in Linux... but I&#039;ve now done this enough times that I&#039;d like a simple reference and most of the ones out there assume you are an expert-level wizard in all things Linux/hardware!&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Smarthome_Mobil_Notify_Services&amp;diff=4856</id>
		<title>Smarthome Mobil Notify Services</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Smarthome_Mobil_Notify_Services&amp;diff=4856"/>
		<updated>2026-04-14T07:47:36Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Hier sammele ich das fuer mich notwendige Wissen, um mit meiner Gebaeudeautomation (FHEM, iobroker, EDOMI, ...) Nachrichten an mein Mobiles zu senden. In einer ersten Recherche sind mir ein paar wenige Dienste begegnet, die ich hier aufliste und deren Verwendung ich kurz beschreibe.&lt;br /&gt;
&lt;br /&gt;
=Notify-Apps=&lt;br /&gt;
==Signal==&lt;br /&gt;
Zum Ausprobieren habe ich erst mal die Desktopversion auf meinem Arbeitsrechner (OpenSuSE LEAP 15.3) installiert.&lt;br /&gt;
&lt;br /&gt;
Linux (Debian-based) Install Instructions&lt;br /&gt;
&lt;br /&gt;
# NOTE: These instructions only work for 64 bit Debian-based&lt;br /&gt;
# Linux distributions such as Ubuntu, Mint etc.&lt;br /&gt;
&lt;br /&gt;
# 1. Install our official public software signing key&lt;br /&gt;
wget -O- https://updates.signal.org/desktop/apt/keys.asc | gpg --dearmor &amp;gt; signal-desktop-keyring.gpg&lt;br /&gt;
cat signal-desktop-keyring.gpg | sudo tee -a /usr/share/keyrings/signal-desktop-keyring.gpg &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# 2. Add our repository to your list of repositories&lt;br /&gt;
echo &#039;deb [arch=amd64 signed-by=/usr/share/keyrings/signal-desktop-keyring.gpg] https://updates.signal.org/desktop/apt xenial main&#039; |\&lt;br /&gt;
  sudo tee -a /etc/apt/sources.list.d/signal-xenial.list&lt;br /&gt;
&lt;br /&gt;
# 3. Update your package database and install signal&lt;br /&gt;
sudo apt update &amp;amp;&amp;amp; sudo apt install signal-desktop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://snapcraft.io/install/signal-desktop/opensuse&lt;br /&gt;
&lt;br /&gt;
==WhatsApp==&lt;br /&gt;
Mit Whatsapp scheint es ueber diesen [https://www.twilio.com/try-twilio  Dienst] zu gehen.&lt;br /&gt;
&lt;br /&gt;
https://dev.to/jajoosam/build-a-whatsapp-bot-fast--2hdc&lt;br /&gt;
&lt;br /&gt;
==Pushover==&lt;br /&gt;
TODO&lt;br /&gt;
==Telegram==&lt;br /&gt;
Ueber die Telegram Bot API.&lt;br /&gt;
[https://webdesign.weisshart.de/php2mobile.php Link]&lt;br /&gt;
&lt;br /&gt;
Zum Probieren habe ich mal die Telegram-App auf meinem Android-Smartphone installiert.&lt;br /&gt;
&lt;br /&gt;
Die Anfragen bzgl. Zugriff auf Anrufe, Kontakte, ... kann man auch getrost ablehnen. &lt;br /&gt;
Die ANmeldung uber die Telefonnummer und Authentifizierung ueber SMS und/oder eMail ging problemlos. &lt;br /&gt;
Die Suche nach dem Nutzer Botfather war erfogreich.&lt;br /&gt;
Nach dem Starten gab es gleich mal eine Kommandouebersicht, die man mit der Nachricht &amp;quot;/start&amp;quot; jederzeit neu anfordern kann.&lt;br /&gt;
&lt;br /&gt;
Der Aufruf von https://t.me/Accipiter_bot zeigte zwar meinen Bot, aber das Senden einer Nachricht scheiterte aber an dem fehlenden Plugin im Browser, um mit dem Protokoll tg:// umgehen zu koennen. -&amp;gt; TODO&lt;br /&gt;
&lt;br /&gt;
Um aber eine Cat-Nachricht ueber php bzw. curl absetzen zu koennen benoetigt man, neben dem API-Token, eine Chat-ID, die man ueber den Umweg durch den Aufruf https://api.telegram.org/bot&amp;lt;token&amp;gt;/getUpdates herausbekommt. Die JSON-Nachricht beinhaltet einen Eintrag id:&amp;lt;chat-id&amp;gt;. Die kopiert man sich heraus und ergaenzt sie in der &amp;quot;SendeNachricht-URL&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
 https://api.telegram.org/bot&amp;lt;token&amp;gt;/sendMessage?chat_id=chat-id&amp;amp;text=Here%20is%20your%20Smarthome&lt;br /&gt;
&lt;br /&gt;
Erfolg:-)&lt;br /&gt;
&lt;br /&gt;
Der Telegram-Client fuer Linux funktioniert auch auf Anhieb:-))&lt;br /&gt;
&lt;br /&gt;
==Jabber==&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Signal4==&lt;br /&gt;
[https://www.signl4.com/]&lt;br /&gt;
Das Starter-Package ist kostenlos:-)&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(Zigbee2MQTT)_Stolperfallen&amp;diff=4855</id>
		<title>(Zigbee2MQTT) Stolperfallen</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(Zigbee2MQTT)_Stolperfallen&amp;diff=4855"/>
		<updated>2026-04-12T08:33:25Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Die Seite wurde neu angelegt: „=Fehlermeldung=   Beim Deaktivieren der legacy_action_sensor option kommt folgende fehlermeldung frontend:api:bridge: Sending {&amp;quot;topic&amp;quot;:&amp;quot;bridge/request/options&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;options&amp;quot;:{&amp;quot;homeassistant&amp;quot;:{&amp;quot;legacy_action_sensor&amp;quot;:false}},&amp;quot;transaction&amp;quot;:&amp;quot;yaom6-1&amp;quot;}} [4/12/2026, 7:57:04 AM] z2m: Request &amp;#039;zigbee2mqtt/bridge/request/options&amp;#039; failed with error: &amp;#039;Extension with name HomeAssistant already present&amp;#039;  Das ist sehr wahrscheinlich &amp;#039;&amp;#039;&amp;#039;ein Bug beim Umschalten de…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Fehlermeldung=&lt;br /&gt;
&lt;br /&gt;
 Beim Deaktivieren der legacy_action_sensor option kommt folgende fehlermeldung frontend:api:bridge: Sending {&amp;quot;topic&amp;quot;:&amp;quot;bridge/request/options&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;options&amp;quot;:{&amp;quot;homeassistant&amp;quot;:{&amp;quot;legacy_action_sensor&amp;quot;:false}},&amp;quot;transaction&amp;quot;:&amp;quot;yaom6-1&amp;quot;}} [4/12/2026, 7:57:04 AM] z2m: Request &#039;zigbee2mqtt/bridge/request/options&#039; failed with error: &#039;Extension with name HomeAssistant already present&#039;&lt;br /&gt;
&lt;br /&gt;
Das ist sehr wahrscheinlich &#039;&#039;&#039;ein Bug beim Umschalten der Home-Assistant-Optionen zur Laufzeit im Zigbee2MQTT-Frontend&#039;&#039;&#039;, nicht ein Fehler in deiner Konfiguration. Ein aktueller Issue zeigt denselben Fehler beim Ein-/Ausschalten von HA-Optionen per &amp;lt;code&amp;gt;bridge/request/options&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;Extension with name HomeAssistant already present&amp;lt;/code&amp;gt; ([https://github.com/Koenkk/zigbee2mqtt/issues/31089?utm_source=chatgpt.com GitHub])&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Außerdem ist &amp;lt;code&amp;gt;legacy_action_sensor&amp;lt;/code&amp;gt; in der Zigbee2MQTT-Doku als Home-Assistant-Option beschrieben; wenn aktiviert, sendet Zigbee2MQTT &#039;&#039;&#039;nach jeder Action absichtlich ein leeres &amp;lt;code&amp;gt;action&amp;lt;/code&amp;gt;&#039;&#039;&#039;. ([https://www.zigbee2mqtt.io/guide/configuration/homeassistant.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;was-das-für-dich-bedeutet&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Was das für dich bedeutet ==&lt;br /&gt;
&lt;br /&gt;
Dein eigentliches Problem bleibt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.../action&amp;lt;/code&amp;gt; Topic ist korrekt&lt;br /&gt;
* &amp;lt;code&amp;gt;payload.action&amp;lt;/code&amp;gt; wird danach leer&lt;br /&gt;
* das passt exakt zu &amp;lt;code&amp;gt;legacy_action_sensor: true&amp;lt;/code&amp;gt; ([https://www.zigbee2mqtt.io/guide/configuration/homeassistant.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;br /&gt;
&lt;br /&gt;
Der neue Fehler bedeutet nur:&lt;br /&gt;
&lt;br /&gt;
* das &#039;&#039;&#039;Frontend kann die HomeAssistant-Extension gerade nicht sauber live neu aufbauen&#039;&#039;&#039;&lt;br /&gt;
* daher musst du die Option &#039;&#039;&#039;in der Datei ändern und Zigbee2MQTT neu starten&#039;&#039;&#039;, statt sie im Frontend umzuschalten. ([https://github.com/Koenkk/zigbee2mqtt/issues/31089?utm_source=chatgpt.com GitHub])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;so-gehst-du-sauber-vor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== So gehst du sauber vor ==&lt;br /&gt;
&lt;br /&gt;
Öffne deine Zigbee2MQTT-&amp;lt;code&amp;gt;configuration.yaml&amp;lt;/code&amp;gt; und setze:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;homeassistant:&lt;br /&gt;
  enabled: true&lt;br /&gt;
  legacy_action_sensor: false&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Optional, wenn du die neuere Richtung nutzen willst:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;homeassistant:&lt;br /&gt;
  enabled: true&lt;br /&gt;
  legacy_action_sensor: false&lt;br /&gt;
  experimental_event_entities: true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Danach &#039;&#039;&#039;Zigbee2MQTT komplett neu starten&#039;&#039;&#039;. Die Doku führt beide Optionen im &amp;lt;code&amp;gt;homeassistant&amp;lt;/code&amp;gt;-Block. ([https://www.zigbee2mqtt.io/guide/configuration/homeassistant.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;wichtig-nicht-im-frontend-umschalten&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Wichtig: nicht im Frontend umschalten ==&lt;br /&gt;
&lt;br /&gt;
Der Fehler kommt genau von diesem Weg:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;zigbee2mqtt/bridge/request/options&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
also vom dynamischen Options-Update aus dem Frontend. Dafür gibt es aktuell einen bekannten Fehler bei HA-bezogenen Optionen. ([https://github.com/Koenkk/zigbee2mqtt/issues/31089?utm_source=chatgpt.com GitHub])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;wenn-du-den-code-trotzdem-patchen-willst&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Wenn du den Code trotzdem patchen willst ==&lt;br /&gt;
&lt;br /&gt;
Die richtige Stelle ist &#039;&#039;&#039;nicht&#039;&#039;&#039; der TS0044-Converter, sondern die HA-Extension von Zigbee2MQTT:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;lib/extension/homeassistant.ts&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dort sitzt die Logik, die bei aktivem &amp;lt;code&amp;gt;legacy_action_sensor&amp;lt;/code&amp;gt; direkt nach einer Action noch einmal &amp;lt;code&amp;gt;{action: &amp;amp;quot;&amp;amp;quot;}&amp;lt;/code&amp;gt; publiziert. Das ist im aktuellen Quellcode so zu sehen. ([https://github.com/Koenkk/zigbee2mqtt/blob/master/lib/extension/homeassistant.ts?utm_source=chatgpt.com GitHub])&lt;br /&gt;
&lt;br /&gt;
Wenn du lokal patchen willst, ist der Minimal-Patch, diese Zeile zu deaktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ts&amp;quot;&amp;gt;await this.publishEntityState(data.entity, {action: &amp;quot;&amp;quot;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Aber der bessere Weg ist wirklich, &amp;lt;code&amp;gt;legacy_action_sensor: false&amp;lt;/code&amp;gt; in der Konfig zu setzen und Zigbee2MQTT neu zu starten. ([https://www.zigbee2mqtt.io/guide/configuration/homeassistant.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;erwartetes-verhalten-danach&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Erwartetes Verhalten danach ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Neustart solltest du typischerweise sehen:&lt;br /&gt;
&lt;br /&gt;
* kein absichtliches Leeren von &amp;lt;code&amp;gt;payload.action&amp;lt;/code&amp;gt;&lt;br /&gt;
* Actions weiter korrekt auf &amp;lt;code&amp;gt;zigbee2mqtt/DEIN_GERAET/action&amp;lt;/code&amp;gt;&lt;br /&gt;
* in Home Assistant besser mit MQTT Device Triggern oder Event-Entities arbeiten statt mit dem alten Action-Sensor. Das wird in den Zigbee2MQTT-Hinweisen ebenfalls so empfohlen. ([https://github.com/Koenkk/zigbee2mqtt/issues/25461?utm_source=chatgpt.com GitHub])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;wenn-du-den-patch-direkt-suchst&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Wenn du den Patch direkt suchst ==&lt;br /&gt;
&lt;br /&gt;
Je nach Installation findest du die laufende JS-Datei oft eher hier als die TS-Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;/app/lib/extension/homeassistant.js&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Quellreferenz bleibt aber:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;lib/extension/homeassistant.ts&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
([https://github.com/Koenkk/zigbee2mqtt/blob/master/lib/extension/homeassistant.ts?utm_source=chatgpt.com GitHub])&lt;br /&gt;
&lt;br /&gt;
=TS0044 action &amp;lt;nowiki&amp;gt;=&amp;lt;/nowiki&amp;gt; Leerstring im payload=&lt;br /&gt;
&lt;br /&gt;
Das liegt sehr wahrscheinlich &#039;&#039;&#039;nicht am TS0044_1-Converter&#039;&#039;&#039;, sondern an der &#039;&#039;&#039;Home-Assistant-Integration von Zigbee2MQTT&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der entscheidende Hinweis ist dein Symptom:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;.../action&amp;lt;/code&amp;gt; Topic&#039;&#039;&#039; enthält den richtigen Tastendruck&lt;br /&gt;
* &#039;&#039;&#039;Payload-Feld &amp;lt;code&amp;gt;action&amp;lt;/code&amp;gt;&#039;&#039;&#039; im normalen Geräte-Topic ist danach leer&lt;br /&gt;
* im Frontend ist &amp;lt;code&amp;gt;action&amp;lt;/code&amp;gt; ebenfalls leer&lt;br /&gt;
&lt;br /&gt;
Genau dieses Verhalten ist dokumentiert, wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;homeassistant.legacy_action_sensor&amp;lt;/code&amp;gt;&#039;&#039;&#039; aktiviert ist: Dann wird &#039;&#039;&#039;nach jeder Action absichtlich ein leeres &amp;lt;code&amp;gt;action&amp;lt;/code&amp;gt;&#039;&#039;&#039; gesendet. ([https://www.zigbee2mqtt.io/guide/configuration/all-settings.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;br /&gt;
&lt;br /&gt;
Außerdem zeigt der Zigbee2MQTT-Quellcode genau diese Logik in der Home-Assistant-Extension: Wenn &amp;lt;code&amp;gt;legacyActionSensor&amp;lt;/code&amp;gt; aktiv ist und &amp;lt;code&amp;gt;data.message.action&amp;lt;/code&amp;gt; existiert, wird direkt danach &amp;lt;code&amp;gt;publishEntityState(..., {action: &amp;amp;quot;&amp;amp;quot;})&amp;lt;/code&amp;gt; aufgerufen; erst darunter wird der korrekte Wert zusätzlich auf &amp;lt;code&amp;gt;.../action&amp;lt;/code&amp;gt; publiziert. ([https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/master/lib/extension/homeassistant.ts GitHub])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;was-bei-dir-also-wahrscheinlich-aktiv-ist&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Was bei dir also wahrscheinlich aktiv ist ==&lt;br /&gt;
&lt;br /&gt;
In deiner &amp;lt;code&amp;gt;configuration.yaml&amp;lt;/code&amp;gt; von Zigbee2MQTT vermutlich so etwas:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;homeassistant:&lt;br /&gt;
  enabled: true&lt;br /&gt;
  legacy_action_sensor: true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Doku beschreibt genau dazu:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;legacy_action_sensor&amp;lt;/code&amp;gt;: aktiviert alte Action-Sensoren&lt;br /&gt;
* dabei wird &#039;&#039;&#039;nach jeder Action ein leerer &amp;lt;code&amp;gt;action&amp;lt;/code&amp;gt;-Wert gesendet&#039;&#039;&#039; ([https://www.zigbee2mqtt.io/guide/configuration/all-settings.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;warum-recent-activity-trotzdem-richtig-ist&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Warum „Recent activity“ trotzdem richtig ist ==&lt;br /&gt;
&lt;br /&gt;
Weil Zigbee2MQTT für Actions zusätzlich das separate MQTT-Topic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;zigbee2mqtt/DEIN_GERAET/action&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
publiziert. Das ist genau der Pfad, den Zigbee2MQTT für MQTT Device Triggers verwendet. ([https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/master/lib/extension/homeassistant.ts GitHub])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;was-du-tun-solltest&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Was du tun solltest ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;sauberste-lösung&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Sauberste Lösung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;legacy_action_sensor&amp;lt;/code&amp;gt; deaktivieren oder entfernen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;homeassistant:&lt;br /&gt;
  enabled: true&lt;br /&gt;
  legacy_action_sensor: false&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Danach Zigbee2MQTT neu starten.&lt;br /&gt;
&lt;br /&gt;
Dann solltest du für Automationen das &#039;&#039;&#039;separate &amp;lt;code&amp;gt;/action&amp;lt;/code&amp;gt; Topic&#039;&#039;&#039; oder die von Zigbee2MQTT empfohlenen &#039;&#039;&#039;MQTT device triggers&#039;&#039;&#039; verwenden. Zigbee2MQTT empfiehlt für Button-Actions in Home Assistant genau diesen Weg. ([https://www.zigbee2mqtt.io/guide/usage/integrations/home_assistant.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;falls-du-in-home-assistant-event-entities-willst&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Falls du in Home Assistant Event-Entities willst ===&lt;br /&gt;
&lt;br /&gt;
Optional:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;homeassistant:&lt;br /&gt;
  enabled: true&lt;br /&gt;
  experimental_event_entities: true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das ist die neuere Richtung für Action-/Event-Geräte. ([https://www.zigbee2mqtt.io/guide/configuration/all-settings.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;wo-du-patchen-müsstest&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Wo du patchen müsstest ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nicht&#039;&#039;&#039; im TS0044_1-Geräte-Converter, denn der liefert die Action offenbar korrekt — sonst wäre das Topic &amp;lt;code&amp;gt;.../action&amp;lt;/code&amp;gt; nicht richtig.&lt;br /&gt;
&lt;br /&gt;
Die richtige Stelle ist in Zigbee2MQTT selbst:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;lib/extension/homeassistant.ts&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dort in der Methode &amp;lt;code&amp;gt;onPublishEntityState(...)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die relevante Stelle ist sinngemäß:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ts&amp;quot;&amp;gt;if (this.legacyActionSensor &amp;amp;&amp;amp; data.message.action) {&lt;br /&gt;
    await this.publishEntityState(data.entity, {action: &amp;quot;&amp;quot;});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if (settings.get().advanced.output === &amp;quot;json&amp;quot; &amp;amp;&amp;amp; entity.isDevice() &amp;amp;&amp;amp; entity.definition &amp;amp;&amp;amp; data.message.action) {&lt;br /&gt;
    const value = data.message.action.toString();&lt;br /&gt;
    await this.publishDeviceTriggerDiscover(entity, &amp;quot;action&amp;quot;, value);&lt;br /&gt;
    await this.mqtt.publish(`${data.entity.name}/action`, value, {});&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Genau dieser Code ist im Repository sichtbar. ([https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/master/lib/extension/homeassistant.ts GitHub])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;wenn-du-wirklich-patchen-willst&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Wenn du wirklich patchen willst ==&lt;br /&gt;
&lt;br /&gt;
Dann ist der Minimal-Patch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ts&amp;quot;&amp;gt;if (this.legacyActionSensor &amp;amp;&amp;amp; data.message.action) {&lt;br /&gt;
    // await this.publishEntityState(data.entity, {action: &amp;quot;&amp;quot;});&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
oder härter:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ts&amp;quot;&amp;gt;if (false &amp;amp;&amp;amp; this.legacyActionSensor &amp;amp;&amp;amp; data.message.action) {&lt;br /&gt;
    await this.publishEntityState(data.entity, {action: &amp;quot;&amp;quot;});&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;wichtig-dabei&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Wichtig dabei ==&lt;br /&gt;
&lt;br /&gt;
Bei einer laufenden Installation patchst du meistens &#039;&#039;&#039;nicht&#039;&#039;&#039; die TypeScript-Quelldatei direkt, sondern die ausgelieferte JS-Datei im Container/Add-on. Je nach Installation liegt sie oft unter etwas wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;/app/lib/extension/homeassistant.js&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die saubere Lösung ist aber trotzdem: &#039;&#039;&#039;&amp;lt;code&amp;gt;legacy_action_sensor&amp;lt;/code&amp;gt; ausschalten&#039;&#039;&#039;, statt den Code zu patchen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;kurzfazit&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Kurzfazit ==&lt;br /&gt;
&lt;br /&gt;
Dein Problem ist sehr wahrscheinlich:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;kein TS0044_1-Bug&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;kein Converter-Bug&#039;&#039;&#039;&lt;br /&gt;
* sondern das absichtliche Verhalten von &#039;&#039;&#039;&amp;lt;code&amp;gt;legacy_action_sensor: true&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die beste Abhilfe ist daher, diese Option auszuschalten und mit &amp;lt;code&amp;gt;.../action&amp;lt;/code&amp;gt; bzw. MQTT Device Triggers weiterzuarbeiten. ([https://www.zigbee2mqtt.io/guide/configuration/all-settings.html?utm_source=chatgpt.com Zigbee2MQTT])&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4854</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4854"/>
		<updated>2026-04-11T06:51:00Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Beispiele */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
    Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da passiert nix, auszer das &#039;DummyFTUI&#039; auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur mein Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte man selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstirg reicht mir das das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/incons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Kleiner Ausflug in mein KNX.&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_Stolperfallen&amp;diff=4853</id>
		<title>(FHEM) Stolperfallen</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_Stolperfallen&amp;diff=4853"/>
		<updated>2026-04-11T06:48:23Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* KNX Toggle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=(PGM2) RoomIcons bei Untermenues=&lt;br /&gt;
Hab mal wieder einen Volltreffer gelandet. Weil die Raeume im FHEMWEB zu viele wurden, aber auch nicht mit &amp;quot;hiddenroom&amp;quot; unterdrueckt werden sollten. Wollte ich mal &#039;Room in Room&#039; aussprobieren.&lt;br /&gt;
Leider scheint das nicht zu funktionieren (Stand fhem 6.0). Aus der Commandref geht eine Alternative hervor. Und zwar, wenn man im Raumnamen ein &#039;-&amp;gt;&#039; verwendet, fuehrt das dazu, dass im FHEM-Roommenue&lt;br /&gt;
aufklappbare Unterraeume (die nach dem -&amp;gt;) angezeigt werden. Man kann also keinen kompletten bestehenden Raum einem anderen Raum zuordnen, sondern man ist gezwungen alle Devices den neuen Raumnamen zuzuordnen.&lt;br /&gt;
Ein unschoener Effekt ist zudem, dass der uebergeordnete Raumname kein Icon mehr hat.&lt;br /&gt;
&lt;br /&gt;
Dies bestatigt diese [https://forum.fhem.de/index.php?topic=91800.0 Forums-Nachfrage].&lt;br /&gt;
&lt;br /&gt;
Ein Blick in den Quellcode von 01_FHEMWEB.pm zeigt:&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;01-FHEMWEB.pm (Auszug)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
 ##########################&lt;br /&gt;
  # Rooms and other links&lt;br /&gt;
  foreach my $r (@FW_roomsArr) {                                     # Das Array mit den Raumnamen wird nacheinander durchlaufen; In der Variablen r steht der aktuell zu pruefende Raumname&lt;br /&gt;
    next if($r eq &amp;quot;hidden&amp;quot; || $FW_hiddenroom{$r});                   # Ist der Raum hidden geh zu naechsten = tue nichts mit diesem Raum&lt;br /&gt;
    $FW_room = AttrVal($FW_wname, &amp;quot;defaultRoom&amp;quot;, $r)&lt;br /&gt;
        if(!$FW_room &amp;amp;&amp;amp; $FW_ss);&lt;br /&gt;
    if(my $devspec = $FW_extraRooms{$r}) {&lt;br /&gt;
      my $r = $r;&lt;br /&gt;
      $r =~ s/&amp;amp;nbsp;/ /g;                                            # Ersetze alle NoBreakSpaces durch Leerzeichen&lt;br /&gt;
      push @list1, FW_htmlEscape($r);&lt;br /&gt;
      push @list2, &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($devspec);               # Da habe ich noch keine Idee zu &lt;br /&gt;
    } else {&lt;br /&gt;
      push @list1, FW_htmlEscape($r);&lt;br /&gt;
      push @list2, &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($r);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  my $sfx = AttrVal(&amp;quot;global&amp;quot;, &amp;quot;language&amp;quot;, &amp;quot;EN&amp;quot;);&lt;br /&gt;
  $sfx = ($sfx eq &amp;quot;EN&amp;quot; ? &amp;quot;&amp;quot; : &amp;quot;_$sfx&amp;quot;);                              # wenn die eingestellte Sprache EN ist , dann ist sfx leer; es wird also z.B: an den Dateinamen commandref nichts angehaengt (sfx steht fuer suffix)&lt;br /&gt;
  my @list = (                                                       # das ist wohl die Standard Ausgangsliste; interassant: Everything gehoert zum Raummenue und der Rest zum (Name?)-Menue darunter&lt;br /&gt;
     &amp;quot;Everything&amp;quot;,    &amp;quot;$FW_ME?room=all&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,              &amp;quot;&amp;quot;,&lt;br /&gt;
     &amp;quot;Commandref&amp;quot;,    &amp;quot;$FW_ME/docs/commandref${sfx}.html&amp;quot;,&lt;br /&gt;
     &amp;quot;Remote doc&amp;quot;,    &amp;quot;http://fhem.de/fhem.html#Documentation&amp;quot;,&lt;br /&gt;
     &amp;quot;Edit files&amp;quot;,    &amp;quot;$FW_ME?cmd=style%20list&amp;quot;,&lt;br /&gt;
     &amp;quot;Select style&amp;quot;,  &amp;quot;$FW_ME?cmd=style%20select&amp;quot;,&lt;br /&gt;
     &amp;quot;Event monitor&amp;quot;, &amp;quot;$FW_ME?cmd=style%20eventMonitor&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,           &amp;quot;&amp;quot;);&lt;br /&gt;
  my $lastname = &amp;quot;,&amp;quot;; # Avoid double &amp;quot;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
  my $lfn = &amp;quot;Logfile&amp;quot;;&lt;br /&gt;
  if($defs{$lfn}) { # Add the current Logfile to the list if defined&lt;br /&gt;
    my @l = FW_fileList($defs{$lfn}{logfile},1);&lt;br /&gt;
    my $fn = pop @l;&lt;br /&gt;
    splice @list, 4,0, (&amp;quot;Logfile&amp;quot;,&lt;br /&gt;
                      &amp;quot;$FW_ME/FileLog_logWrapper?dev=$lfn&amp;amp;type=text&amp;amp;file=$fn&amp;quot;);    # fuege den Logfile-Eintrag vor dem Eintrag Commandref ein (wenn definiert)&lt;br /&gt;
  }&lt;br /&gt;
# die menuEntries sind der dritte Block (Rooms, (Name?)-Menue, menuEntries)&lt;br /&gt;
  my @me = split(&amp;quot;,&amp;quot;, AttrVal($FW_wname, &amp;quot;menuEntries&amp;quot;, &amp;quot;&amp;quot;));       # die menuEntries sind eine komma-separierte Liste von Eintraegen der Art Menuname,Kommando     &lt;br /&gt;
  push @list, @me, &amp;quot;&amp;quot;, &amp;quot;&amp;quot; if(@me);                                  # Die Eintragungen werden angehaengt &amp;quot;&amp;quot;,&amp;quot;&amp;quot; ist jeweils der Block-Abschluss, bzw. Separator&lt;br /&gt;
&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx+= 2) {                        # haenge die Kommandoliste an die Raumliste an&lt;br /&gt;
    next if($FW_hiddenroom{$list[$idx]} || $list[$idx] eq $lastname);&lt;br /&gt;
    push @list1, $list[$idx];                                       # in list1 stehen die Menunamen und in list2 die Aktionen&lt;br /&gt;
    push @list2, $list[$idx+1];&lt;br /&gt;
    $lastname = $list[$idx];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;div id=\&amp;quot;menu\&amp;quot;&amp;gt;&amp;quot;;                                        # hier geht&#039;s dann los mit dem Zusammenbau der HTML-Seite (siehe unten)&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;table&amp;gt;&amp;quot;;&lt;br /&gt;
  if($FW_ss) {  # Make a selection sensitive dropdown list          # $FW_ss also &#039;&#039;&#039;F&#039;&#039;&#039;HEM&#039;&#039;&#039;W&#039;&#039;&#039;EB &#039;&#039;&#039;s&#039;&#039;&#039;ensitive &#039;&#039;&#039;s&#039;&#039;&#039;election auf einem &#039;&#039;&#039;S&#039;&#039;&#039;mall &#039;&#039;&#039;S&#039;&#039;&#039;creen &lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;select OnChange=\&amp;quot;location.href=&amp;quot; .&lt;br /&gt;
                              &amp;quot;this.options[this.selectedIndex].value\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
    foreach(my $idx = 0; $idx &amp;lt; @list1; $idx++) {&lt;br /&gt;
      next if(!$list1[$idx]);&lt;br /&gt;
      my $sel = ($list1[$idx] eq $FW_room ? &amp;quot; selected=\&amp;quot;selected\&amp;quot;&amp;quot;  : &amp;quot;&amp;quot;);&lt;br /&gt;
      FW_pO &amp;quot;&amp;lt;option value=&#039;$list2[$idx]&#039;$sel&amp;gt;$list1[$idx]&amp;lt;/option&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;/select&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
&lt;br /&gt;
    my $tblnr = 1;&lt;br /&gt;
    my $roomEscaped = FW_htmlEscape($FW_room);                   # ersetzte alle (&amp;amp; &amp;lt; &amp;gt; &#039; (\n)) aus dem aktuellen (?) Raumnamen, wird allerdings nie mehr verwendet&lt;br /&gt;
    my $current;&lt;br /&gt;
    $current = &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($FW_room) if($FW_room);&lt;br /&gt;
    $current = &amp;quot;$FW_ME?cmd=&amp;quot;.urlEncode($cmd) if($cmd);&lt;br /&gt;
    foreach(my $idx = 0; $idx &amp;lt; @list1; $idx++) {                # jetzt durchlaufe die gesamte Liste mit allen Menueintraegen und baue die HTML-Seite (roomBlock1-3)&lt;br /&gt;
      my ($l1, $l2) = ($list1[$idx], $list2[$idx]);&lt;br /&gt;
      if(!$l1) {&lt;br /&gt;
        FW_pO &amp;quot;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; if($idx);                     # die Raumbloecke sind eine Tabelle in der Tabelle menu, diese Table-Ende wird nach jedem Block (Eintrag &amp;quot;&amp;quot;,&amp;quot;&amp;quot;) ausgegeben. Am Anfang ist $idx noch 0, dann also nicht.&lt;br /&gt;
        if($idx&amp;lt;int(@list1)-1) {&lt;br /&gt;
          FW_pO &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=\&amp;quot;room roomBlock$tblnr\&amp;quot;&amp;gt;&amp;quot;;   # hier waere es auch moeglich durch einfuegen eines leeren/speziellen Eintrags einen neunen Block zu generieren&lt;br /&gt;
          $tblnr++;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
      } else {                                                    # hier kommt jetzt der aktuell interessante Teil&lt;br /&gt;
        FW_pF &amp;quot;&amp;lt;tr%s&amp;gt;&amp;quot;, ($current &amp;amp;&amp;amp; $current eq $l2) ? &amp;quot; class=\&amp;quot;sel\&amp;quot;&amp;quot; : &amp;quot;&amp;quot;;   # FW_pF ist eine formatierte Ausgabe&lt;br /&gt;
&lt;br /&gt;
        my $class = &amp;quot;menu_$l1&amp;quot;;&lt;br /&gt;
        $class =~ s/[^A-Z0-9]/_/gi;&lt;br /&gt;
&lt;br /&gt;
        # image tag if we have an icon, else empty&lt;br /&gt;
        my $icoName = &amp;quot;ico$l1&amp;quot;;&lt;br /&gt;
        map { my ($n,$v) = split(&amp;quot;:&amp;quot;,$_); $icoName=$v if($l1 =~ m/^$n$/); }    # die roomOcons sind eine Leerzeichen-getrennte Liste von Raumname:IconName $n:$v&lt;br /&gt;
                        split(&amp;quot; &amp;quot;, AttrVal($FW_wname, &amp;quot;roomIcons&amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
        my $icon = FW_iconName($icoName) ?&lt;br /&gt;
                        FW_makeImage($icoName,$icoName,&amp;quot;icon&amp;quot;).&amp;quot;&amp;amp;nbsp;&amp;quot; : &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        if($l1 eq &amp;quot;Save config&amp;quot;) {                                            # Spezialbehandlung des Menueintrags &lt;br /&gt;
          $l1 .= &#039;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt; &#039;.&lt;br /&gt;
                  &#039;&amp;lt;a id=&amp;quot;saveCheck&amp;quot; class=&amp;quot;changed&amp;quot; style=&amp;quot;visibility:&#039;.&lt;br /&gt;
                  (int(@structChangeHist) ? &#039;visible&#039; : &#039;hidden&#039;).&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;?&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        # Force external browser if FHEMWEB is installed as an offline app.&lt;br /&gt;
        my $target = &#039;&#039;;        # Forum 33066, 39854&lt;br /&gt;
        $target = &#039;target=&amp;quot;_blank&amp;quot;&#039; if($l2 =~ s/^$FW_ME\/\+/$FW_ME\//);&lt;br /&gt;
        $target = &#039;target=&amp;quot;_blank&amp;quot;&#039; if($l2 =~ m/commandref|fhem.de.fhem.html/);&lt;br /&gt;
        if($l2 =~ m/.html$/ || $l2 =~ m/^(http|javascript)/ || length($target)){    # wenn das Kommando auf .html endet oder mit http oder javascript beginnt target definiert (siehe die beiden Zeilen vorher) ist, dann mach aus dem Komando einen Link&lt;br /&gt;
           FW_pO &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;$l2&#039; $target&amp;gt;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;quot;.&lt;br /&gt;
                 &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
        } else {&lt;br /&gt;
          FW_pH $l2, &amp;quot;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;quot;, 1, $class;     # ein bisschen merkwuerdig, aber ab im &amp;quot;normalfall&amp;quot; wird alles ab &amp;lt;td&amp;gt; zwischen &amp;lt;/td&amp;gt; durch FW_pH ausgegeben, siehe unten Erlaeuterung&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        FW_pO &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/div&amp;gt;&amp;quot; if($hasMenuScroll);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei mir sieht das Ergebnis dann so aus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;Quellcode fhem.html (Auszug roomBlock1-3)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;menuScrollArea&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?&#039;&amp;quot;&amp;gt;&amp;lt;div id=&amp;quot;logo&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;menu&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_LEUCHTEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=LEUCHTEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoBELEUCHTUNG&#039; src=&amp;quot;/fhem/images/default/icoBELEUCHTUNG.png&amp;quot; alt=&amp;quot;icoBELEUCHTUNG&amp;quot; title=&amp;quot;icoBELEUCHTUNG&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;LEUCHTEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_ROLLLADEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=ROLLLADEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoROLLLADEN&#039; src=&amp;quot;/fhem/images/user_icons/icoROLLLADEN.png&amp;quot; alt=&amp;quot;icoROLLLADEN&amp;quot; title=&amp;quot;icoROLLLADEN&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;ROLLLADEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_AUSSEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=AUSSEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoAUSSEN&#039; src=&amp;quot;/fhem/images/default/icoAUSSEN.png&amp;quot; alt=&amp;quot;icoAUSSEN&amp;quot; title=&amp;quot;icoAUSSEN&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;AUSSEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_KLIMA&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=KLIMA&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoTemp&#039; src=&amp;quot;/fhem/images/default/icoTemp.png&amp;quot; alt=&amp;quot;icoTemp&amp;quot; title=&amp;quot;icoTemp&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;KLIMA&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_HAUST__R&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=HAUST%c3%9cR&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoHouse&#039; src=&amp;quot;/fhem/images/default/icoHouse.png&amp;quot; alt=&amp;quot;icoHouse&amp;quot; title=&amp;quot;icoHouse&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;HAUSTÜR&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_HEIZUNG&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=HEIZUNG&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoHEIZUNG&#039; src=&amp;quot;/fhem/images/default/icoHEIZUNG.png&amp;quot; alt=&amp;quot;icoHEIZUNG&amp;quot; title=&amp;quot;icoHEIZUNG&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;HEIZUNG&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_MULTIMEDIA&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=MULTIMEDIA&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoAUDIOVIDEO&#039; src=&amp;quot;/fhem/images/user_icons/icoAUDIOVIDEO.png&amp;quot; alt=&amp;quot;icoAUDIOVIDEO&amp;quot; title=&amp;quot;icoAUDIOVIDEO&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;MULTIMEDIA&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;sel&amp;quot;&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_Interfaces&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eInterfaces&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;Interfaces&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_LOGO_&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eLOGO%21&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;LOGO!&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Schaltuhren&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=Schaltuhren&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoUhr&#039; src=&amp;quot;/fhem/images/default/icoUhr.png&amp;quot; alt=&amp;quot;icoUhr&amp;quot; title=&amp;quot;icoUhr&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Schaltuhren&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Z__hler&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=Z%c3%a4hler&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoZAEHLER&#039; src=&amp;quot;/fhem/images/user_icons/icoZAEHLER.png&amp;quot; alt=&amp;quot;icoZAEHLER&amp;quot; title=&amp;quot;icoZAEHLER&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Zähler&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Everything&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=all&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoEverything&#039; src=&amp;quot;/fhem/images/default/icoEverything.png&amp;quot; alt=&amp;quot;icoEverything&amp;quot; title=&amp;quot;icoEverything&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Everything&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock2&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Logfile&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem/FileLog_logWrapper?dev=Logfile&amp;amp;type=text&amp;amp;file=fhem-2022-09.log&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Logfile&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;/fhem/docs/commandref.html&#039; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Commandref&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.de/fhem.html#Documentation&#039; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Remote doc&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Edit_files&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20list&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Edit files&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Select_style&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20select&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Select style&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Event_monitor&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20eventMonitor&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Event monitor&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock3&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.de&#039; &amp;gt;&amp;lt;span&amp;gt;fhem.de&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://culfw.de&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        culfw.de&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.clx.local:8083/fhem?cmd=style%20iconFor&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        Icons&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________restart&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=shutdown+restart&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        restart&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________rereadcfg&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=rereadcfg&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        rereadcfg&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________rereadicons&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=set WEB rereadicons&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        rereadicons&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.clx.local:8083/fhem/ftui/index.html&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
			FTUI&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 my $class = &amp;quot;menu_$l1&amp;quot;;&lt;br /&gt;
 $class =~ s/[^A-Z0-9]/_/gi;                   # alle nicht Buchstaben und Ziffern werden durch Unterstrich ersetzt, also auch Umlaute&lt;br /&gt;
 my $icon = FW_iconName($icoName) ? FW_makeImage($icoName,$icoName,&amp;quot;icon&amp;quot;).&amp;quot;&amp;amp;nbsp;&amp;quot; : &amp;quot;&amp;quot;;&lt;br /&gt;
 FW_pH $l2, &amp;quot;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;quot;, 1, $class;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Everything&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=all&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoEverything&#039; src=&amp;quot;/fhem/images/default/icoEverything.png&amp;quot; alt=&amp;quot;icoEverything&amp;quot; title=&amp;quot;icoEverything&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Everything&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;sel&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_Interfaces&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eInterfaces&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;Interfaces&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_LOGO_&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eLOGO%21&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;LOGO!&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FHEM ROOMinROOMIcon.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Man hat also 2 Baustellen. Moechte man ein Icon am uebergoeordneten Menu, muesste man es in die Klasse packen. Bei den Untermenues muss man die Funktion FW_iconName in 01_FHEMWEB.pm patchen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
# check if the icon exists, and if yes, returns its &amp;quot;logical&amp;quot; name;&lt;br /&gt;
sub&lt;br /&gt;
FW_iconName($)                        # der Parameter ist falls $l1 nicht irgendwo vorher im Code manipuliert wurde in meinem Beispiel icoSystem-&amp;gt;Interfaces. Ein Icon mit diesem Namen gibt es nicht!&lt;br /&gt;
{&lt;br /&gt;
  my ($oname)= @_;&lt;br /&gt;
  return undef if(!defined($oname));&lt;br /&gt;
  my $name = $oname;&lt;br /&gt;
  $name =~ s/@.*//;&lt;br /&gt;
  foreach my $pe (@FW_iconDirs) {&lt;br /&gt;
    return $oname if($pe &amp;amp;&amp;amp; $FW_icons{$pe} &amp;amp;&amp;amp; $FW_icons{$pe}{$name});&lt;br /&gt;
  }&lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die Variable &lt;br /&gt;
 $l1 SYSTEM-&amp;amp;gt;Interfaces, bzw. SYSTEM__gt_Interfaces&lt;br /&gt;
wird per Regex getestet.&lt;br /&gt;
&lt;br /&gt;
Wenn man also das Attribut so anlegt, klappt es: &lt;br /&gt;
&lt;br /&gt;
 attr WEB roomIcons ... SYSTEM-.*Interfaces:icoSYSTEM ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FHEM ROOMinROOMIcon 2.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Das Icon vor den &amp;quot;Stammraum&amp;quot; zu platzieren gestaltet sich etwas schwieriger.&lt;br /&gt;
&lt;br /&gt;
Die passende Stelle in fhemweb.js ist zwar schnell ausgemacht, aber einfach ein Image nach dem &amp;lt;td&amp;gt; einfuegen bringt nicht das genwueschte Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;fhemweb.js (Auszug)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line&amp;gt;&lt;br /&gt;
function&lt;br /&gt;
FW_treeMenu()&lt;br /&gt;
{&lt;br /&gt;
  var a = $(&amp;quot;a&amp;quot;).get(0);&lt;br /&gt;
  var col = &#039;rgb(39, 135, 38)&#039;;&lt;br /&gt;
  if(window.getComputedStyle &amp;amp;&amp;amp; a)&lt;br /&gt;
    col = getComputedStyle(a,null).getPropertyValue(&#039;color&#039;); &lt;br /&gt;
  FW_arrowRight = &#039;data:image/svg+xml;utf8,&amp;lt;svg viewBox=&amp;quot;0 0 1792 1792&amp;quot; xmlns=&amp;quot;http://www.w3.org/2000/svg&amp;quot;&amp;gt;&amp;lt;path fill=&amp;quot;gray&amp;quot; d=&amp;quot;M1171 960q0 13-10 23l-466 466q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l393-393-393-393q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l466 466q10 10 10 23z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;&lt;br /&gt;
      .replace(&#039;gray&#039;, col);&lt;br /&gt;
  FW_arrowDown =FW_arrowRight.replace(&#039;/&amp;gt;&#039;,&#039; transform=&amp;quot;rotate(90,896,896)&amp;quot;/&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
  var fnd;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;div#menu table.room&amp;quot;).each(function(){     // one loop per Block&lt;br /&gt;
    var t = this, ma = {};&lt;br /&gt;
    $(t).find(&amp;quot;td &amp;gt; div &amp;gt; a &amp;gt; span&amp;quot;).each(function(e){&lt;br /&gt;
      var span = this, spanTxt = $(span).text().replace(/,/g,&#039;&#039;);&lt;br /&gt;
      var ta = spanTxt.split(&amp;quot;-&amp;gt;&amp;quot;);&lt;br /&gt;
      if(ta.length &amp;lt;= 1)&lt;br /&gt;
        return;&lt;br /&gt;
      fnd = true;&lt;br /&gt;
      var nxt=&amp;quot;&amp;quot;, lst=&amp;quot;&amp;quot;, tr=$(span).closest(&amp;quot;tr&amp;quot;);&lt;br /&gt;
      for(var i1=0; i1&amp;lt;ta.length-1; i1++) {&lt;br /&gt;
        nxt += &amp;quot;-&amp;gt;&amp;quot;+ta[i1];&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;#####HIER PLATZ FUER ICON####&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        ma[nxt] = true;&lt;br /&gt;
        lst = nxt;&lt;br /&gt;
      }&lt;br /&gt;
      $(span).html(ta[ta.length-1]);&lt;br /&gt;
      $(tr).attr(&amp;quot;data-mTree&amp;quot;, nxt)&lt;br /&gt;
           .addClass(&amp;quot;menuTree level&amp;quot;+(ta.length-1));&lt;br /&gt;
    });&lt;br /&gt;
  });&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Patch==&lt;br /&gt;
fhemweb.js (FHEM 6.2 ) Zeile 1157&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);     &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; zwischen &amp;lt;td&amp;gt;&amp;lt;div&amp;gt; und &amp;lt;a href...&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;img src=&#039;/fhem/images/default/ico&amp;quot;+ta[i1]+&amp;quot;.png&#039;&amp;gt;&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht elegant, aber funktioniert solange ein Icon mit dem gleichen Namen wie der uebergeordnete Raum existiert. Hier SYSTEM also icoSYSTEM.png&lt;br /&gt;
&lt;br /&gt;
=(PGM2) MenuBloecke erweitern=&lt;br /&gt;
&lt;br /&gt;
Ich will meine FTUI-Seiten in PGM2 anbinden und nicht nur den Link einfach unten anhaengen (siehe Screenshot).&lt;br /&gt;
FTUI soll in einem eigenen Block stehen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FTUI Link.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Um einen neuen Block zu beginnen ist in der Menuliste ein leerer Eintrag notwendig. Siehe Quellcode in FHEMWEB.pm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
  my @list = (&lt;br /&gt;
     &amp;quot;Everything&amp;quot;,    &amp;quot;$FW_ME?room=all&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,              &amp;quot;&amp;quot;,                                    &amp;lt;--------------------neuer Block&lt;br /&gt;
     &amp;quot;Commandref&amp;quot;,    &amp;quot;$FW_ME/docs/commandref${sfx}.html&amp;quot;,&lt;br /&gt;
     &amp;quot;Remote doc&amp;quot;,    &amp;quot;http://fhem.de/fhem.html#Documentation&amp;quot;,&lt;br /&gt;
     &amp;quot;Edit files&amp;quot;,    &amp;quot;$FW_ME?cmd=style%20list&amp;quot;,&lt;br /&gt;
     &amp;quot;Select style&amp;quot;,  &amp;quot;$FW_ME?cmd=style%20select&amp;quot;,&lt;br /&gt;
     &amp;quot;Event monitor&amp;quot;, &amp;quot;$FW_ME?cmd=style%20eventMonitor&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,           &amp;quot;&amp;quot;);                                      &amp;lt;--------------------neuer Block&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leider ist es mir nicht gelungen in der fhem.conf direkt einen leeren Eintrag zu erzwingen, daher mein Umweg ueber einen Platzhalter (NEWBLOCK)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEB menuEntries fhem.de,http://fhem.de,\&lt;br /&gt;
                        culfw.de,http://culfw.de,\&lt;br /&gt;
                        Icons,http://fhem.clx.local:8083/fhem?cmd=style%20iconFor,\&lt;br /&gt;
                        restart,cmd=shutdown+restart,\&lt;br /&gt;
                        rereadcfg,cmd=rereadcfg,\&lt;br /&gt;
                        rereadicons,cmd=set WEB rereadicons,\&lt;br /&gt;
                        NEWBLOCK,NEWBLOCK,\&lt;br /&gt;
                        FTUI,http://fhem.clx.local:8083/fhem/ftui/index.html&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Patch==&lt;br /&gt;
FHEM/01_FHEMWEB.pm Version 6.2 Zeile 1803 (Nachtrag: In Version 6.3 ist es Zeile 1815)&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot Blockbuilding.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
  my @me = split(&amp;quot;,&amp;quot;, AttrVal($FW_wname, &amp;quot;menuEntries&amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
  push @list, @me, &amp;quot;&amp;quot;, &amp;quot;&amp;quot; if(@me);&lt;br /&gt;
&lt;br /&gt;
===============================snip==================================================&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx++) {&lt;br /&gt;
    if ( $list[$idx] =~ m/NEWBLOCK/ ) {&lt;br /&gt;
      splice @list, $idx, 1,(&amp;quot;&amp;quot;);  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  splice @list, 6, 0, (&amp;quot;&amp;quot;,&amp;quot;&amp;quot;); #  fuegt ein Blockende nach Logfile ein&lt;br /&gt;
===============================snip==================================================&lt;br /&gt;
   &lt;br /&gt;
  my $lastname = &amp;quot;,&amp;quot;; &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; neu in Version 6.2&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx+= 2) {&lt;br /&gt;
    next if($FW_hiddenroom{$list[$idx]} || $list[$idx] eq $lastname);&lt;br /&gt;
    push @list1, $list[$idx];&lt;br /&gt;
    push @list2, $list[$idx+1];&lt;br /&gt;
    $lastname = $list[$idx];&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem Patch einen Restart mit&lt;br /&gt;
 systemctl restart fhem&lt;br /&gt;
durchfuehren.&lt;br /&gt;
&lt;br /&gt;
=Eventlog=&lt;br /&gt;
&lt;br /&gt;
Ich habe das Problem, dass das KM271 Logomatic 2107 Modul einfach zu  geschschwaetzig ist und zudem auch noch ERR-Eintraege produziert, die ich nicht mitloggen moechte.&lt;br /&gt;
&lt;br /&gt;
 2023-12-24 09:24:40 KM271 KM271 ERR_Fehlerspeicher1: Code 01 (+): 09:04Uhr vor 68 Tagen | (-): 195:124Uhr vor 0 Tagen&lt;br /&gt;
&lt;br /&gt;
Derzeit nutze ich das Filelog.&lt;br /&gt;
&lt;br /&gt;
 define Logfile FileLog /var/log/fhem/fhem-%Y-%m.log fakelog&lt;br /&gt;
&lt;br /&gt;
Dabei bin ueber den Eintrag fakelog gestolpert, mit dem ich irgendwie nix mehr anfangen konnte. Habe ich vermutlich mit CopyPaste aus einer Beispiel-Conf uebernommen.&lt;br /&gt;
&lt;br /&gt;
Folgende [https://forum.fhem.de/index.php?topic=95689.0 Seite] brachte Licht in&#039;s Dunkel.&lt;br /&gt;
&lt;br /&gt;
Das ist die Syntax des Logfile Devices.&lt;br /&gt;
&lt;br /&gt;
 define &amp;lt;name&amp;gt; FileLog &amp;lt;filename&amp;gt; &amp;lt;regexp&amp;gt; [readonly] &lt;br /&gt;
&lt;br /&gt;
Daraus kann man ableiten das der Begriff fakelog einen regulaeren Ausdruck darstellt. Irgendjemand hat sich den Begriff mal ausgedacht, um den notwendigen Parameter mit einem Suchfilter zu belegen, der niemals eintritt.&lt;br /&gt;
&lt;br /&gt;
Ein anderer Zeitgenosse hat jetzt genau diesen Begriff als Suchfilter in seiner Wirkung begrenzt.[https://forum.fhem.de/index.php/topic,95351]&lt;br /&gt;
Dies wurde mit dem Update vom 05. Januar 2019 wirksam (Rudi hat im Januar update das controlset disabled; Boeser Rudi)&lt;br /&gt;
&lt;br /&gt;
 Mit dem folgenden kleinen patch werden set, Regexp parts und Create SVG plot für fakelog nicht mehr angezeigt. meiner meinung nach ist nichts davon sinnvoll und verhindert so fehlbedienung. Ich würde gerne ein solches fakelog device dann auch für die anzeige der alexa-fhem logs nutzen statt den anzeige teil doppelt zu implementieren. aber mit den vielen knöpfen ist das eine mögliche fehlerquelle :)&lt;br /&gt;
&lt;br /&gt;
Will man wieder den alten Zustand herstellen, muss man sich nur einen neuen Suchfilter, der nie zutrifft, ausdenken.&lt;br /&gt;
&lt;br /&gt;
Aber nun zum eigentlichen Problem.&lt;br /&gt;
Ein guter Hinweis ist [https://forum.fhem.de/index.php?topic=51611.0 hier] &lt;br /&gt;
&lt;br /&gt;
 Wenn bestimmte Zeilen nicht in die Logdatei geschrieben werden sollen, ist das Attribut ignoreRegexp hilfreich. Wenn beispielsweise alle Zeilen, die die Zeichenfolge &amp;quot;AbCd&amp;quot; oder &amp;quot;CdEf&amp;quot; enthalten nicht geloggt werden sollen, dann wäre&lt;br /&gt;
 &lt;br /&gt;
 Code Auswählen&lt;br /&gt;
 attr &amp;lt;log-name&amp;gt; ignoreRegexp .*AbCd.*|.*CdEf.*&lt;br /&gt;
 eine Attributdefinition, die das ermöglicht. &lt;br /&gt;
 &lt;br /&gt;
 Dies bezieht sich aber nur auf normale FileLog-Instanzen. Falls Events aus dem globalen FHEM-Logfile ausgeschlossen werden sollen, muss man das Attribut in global angeben. (Zusammenhang siehe #Globale Logdatei) &lt;br /&gt;
  &lt;br /&gt;
 Code Auswählen&lt;br /&gt;
 attr global ignoreRegexp .*AbCd.*|.*CdEf.*&lt;br /&gt;
&lt;br /&gt;
=KNX Toggle=&lt;br /&gt;
Um einen toogle z.B. auf einem Licht hinzubekommen. &lt;br /&gt;
&lt;br /&gt;
 set L_Bar_Auszen g1 toggle&lt;br /&gt;
&lt;br /&gt;
ist es notwendig das Attribut KNX_TOGGLE zu setzen.&lt;br /&gt;
&lt;br /&gt;
 attr KNX_TOGGLE L_Bar_Auszen:state&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_Stolperfallen&amp;diff=4852</id>
		<title>(FHEM) Stolperfallen</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_Stolperfallen&amp;diff=4852"/>
		<updated>2026-04-11T06:47:50Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Eventlog */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=(PGM2) RoomIcons bei Untermenues=&lt;br /&gt;
Hab mal wieder einen Volltreffer gelandet. Weil die Raeume im FHEMWEB zu viele wurden, aber auch nicht mit &amp;quot;hiddenroom&amp;quot; unterdrueckt werden sollten. Wollte ich mal &#039;Room in Room&#039; aussprobieren.&lt;br /&gt;
Leider scheint das nicht zu funktionieren (Stand fhem 6.0). Aus der Commandref geht eine Alternative hervor. Und zwar, wenn man im Raumnamen ein &#039;-&amp;gt;&#039; verwendet, fuehrt das dazu, dass im FHEM-Roommenue&lt;br /&gt;
aufklappbare Unterraeume (die nach dem -&amp;gt;) angezeigt werden. Man kann also keinen kompletten bestehenden Raum einem anderen Raum zuordnen, sondern man ist gezwungen alle Devices den neuen Raumnamen zuzuordnen.&lt;br /&gt;
Ein unschoener Effekt ist zudem, dass der uebergeordnete Raumname kein Icon mehr hat.&lt;br /&gt;
&lt;br /&gt;
Dies bestatigt diese [https://forum.fhem.de/index.php?topic=91800.0 Forums-Nachfrage].&lt;br /&gt;
&lt;br /&gt;
Ein Blick in den Quellcode von 01_FHEMWEB.pm zeigt:&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;01-FHEMWEB.pm (Auszug)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
 ##########################&lt;br /&gt;
  # Rooms and other links&lt;br /&gt;
  foreach my $r (@FW_roomsArr) {                                     # Das Array mit den Raumnamen wird nacheinander durchlaufen; In der Variablen r steht der aktuell zu pruefende Raumname&lt;br /&gt;
    next if($r eq &amp;quot;hidden&amp;quot; || $FW_hiddenroom{$r});                   # Ist der Raum hidden geh zu naechsten = tue nichts mit diesem Raum&lt;br /&gt;
    $FW_room = AttrVal($FW_wname, &amp;quot;defaultRoom&amp;quot;, $r)&lt;br /&gt;
        if(!$FW_room &amp;amp;&amp;amp; $FW_ss);&lt;br /&gt;
    if(my $devspec = $FW_extraRooms{$r}) {&lt;br /&gt;
      my $r = $r;&lt;br /&gt;
      $r =~ s/&amp;amp;nbsp;/ /g;                                            # Ersetze alle NoBreakSpaces durch Leerzeichen&lt;br /&gt;
      push @list1, FW_htmlEscape($r);&lt;br /&gt;
      push @list2, &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($devspec);               # Da habe ich noch keine Idee zu &lt;br /&gt;
    } else {&lt;br /&gt;
      push @list1, FW_htmlEscape($r);&lt;br /&gt;
      push @list2, &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($r);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  my $sfx = AttrVal(&amp;quot;global&amp;quot;, &amp;quot;language&amp;quot;, &amp;quot;EN&amp;quot;);&lt;br /&gt;
  $sfx = ($sfx eq &amp;quot;EN&amp;quot; ? &amp;quot;&amp;quot; : &amp;quot;_$sfx&amp;quot;);                              # wenn die eingestellte Sprache EN ist , dann ist sfx leer; es wird also z.B: an den Dateinamen commandref nichts angehaengt (sfx steht fuer suffix)&lt;br /&gt;
  my @list = (                                                       # das ist wohl die Standard Ausgangsliste; interassant: Everything gehoert zum Raummenue und der Rest zum (Name?)-Menue darunter&lt;br /&gt;
     &amp;quot;Everything&amp;quot;,    &amp;quot;$FW_ME?room=all&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,              &amp;quot;&amp;quot;,&lt;br /&gt;
     &amp;quot;Commandref&amp;quot;,    &amp;quot;$FW_ME/docs/commandref${sfx}.html&amp;quot;,&lt;br /&gt;
     &amp;quot;Remote doc&amp;quot;,    &amp;quot;http://fhem.de/fhem.html#Documentation&amp;quot;,&lt;br /&gt;
     &amp;quot;Edit files&amp;quot;,    &amp;quot;$FW_ME?cmd=style%20list&amp;quot;,&lt;br /&gt;
     &amp;quot;Select style&amp;quot;,  &amp;quot;$FW_ME?cmd=style%20select&amp;quot;,&lt;br /&gt;
     &amp;quot;Event monitor&amp;quot;, &amp;quot;$FW_ME?cmd=style%20eventMonitor&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,           &amp;quot;&amp;quot;);&lt;br /&gt;
  my $lastname = &amp;quot;,&amp;quot;; # Avoid double &amp;quot;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
  my $lfn = &amp;quot;Logfile&amp;quot;;&lt;br /&gt;
  if($defs{$lfn}) { # Add the current Logfile to the list if defined&lt;br /&gt;
    my @l = FW_fileList($defs{$lfn}{logfile},1);&lt;br /&gt;
    my $fn = pop @l;&lt;br /&gt;
    splice @list, 4,0, (&amp;quot;Logfile&amp;quot;,&lt;br /&gt;
                      &amp;quot;$FW_ME/FileLog_logWrapper?dev=$lfn&amp;amp;type=text&amp;amp;file=$fn&amp;quot;);    # fuege den Logfile-Eintrag vor dem Eintrag Commandref ein (wenn definiert)&lt;br /&gt;
  }&lt;br /&gt;
# die menuEntries sind der dritte Block (Rooms, (Name?)-Menue, menuEntries)&lt;br /&gt;
  my @me = split(&amp;quot;,&amp;quot;, AttrVal($FW_wname, &amp;quot;menuEntries&amp;quot;, &amp;quot;&amp;quot;));       # die menuEntries sind eine komma-separierte Liste von Eintraegen der Art Menuname,Kommando     &lt;br /&gt;
  push @list, @me, &amp;quot;&amp;quot;, &amp;quot;&amp;quot; if(@me);                                  # Die Eintragungen werden angehaengt &amp;quot;&amp;quot;,&amp;quot;&amp;quot; ist jeweils der Block-Abschluss, bzw. Separator&lt;br /&gt;
&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx+= 2) {                        # haenge die Kommandoliste an die Raumliste an&lt;br /&gt;
    next if($FW_hiddenroom{$list[$idx]} || $list[$idx] eq $lastname);&lt;br /&gt;
    push @list1, $list[$idx];                                       # in list1 stehen die Menunamen und in list2 die Aktionen&lt;br /&gt;
    push @list2, $list[$idx+1];&lt;br /&gt;
    $lastname = $list[$idx];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;div id=\&amp;quot;menu\&amp;quot;&amp;gt;&amp;quot;;                                        # hier geht&#039;s dann los mit dem Zusammenbau der HTML-Seite (siehe unten)&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;table&amp;gt;&amp;quot;;&lt;br /&gt;
  if($FW_ss) {  # Make a selection sensitive dropdown list          # $FW_ss also &#039;&#039;&#039;F&#039;&#039;&#039;HEM&#039;&#039;&#039;W&#039;&#039;&#039;EB &#039;&#039;&#039;s&#039;&#039;&#039;ensitive &#039;&#039;&#039;s&#039;&#039;&#039;election auf einem &#039;&#039;&#039;S&#039;&#039;&#039;mall &#039;&#039;&#039;S&#039;&#039;&#039;creen &lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;select OnChange=\&amp;quot;location.href=&amp;quot; .&lt;br /&gt;
                              &amp;quot;this.options[this.selectedIndex].value\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
    foreach(my $idx = 0; $idx &amp;lt; @list1; $idx++) {&lt;br /&gt;
      next if(!$list1[$idx]);&lt;br /&gt;
      my $sel = ($list1[$idx] eq $FW_room ? &amp;quot; selected=\&amp;quot;selected\&amp;quot;&amp;quot;  : &amp;quot;&amp;quot;);&lt;br /&gt;
      FW_pO &amp;quot;&amp;lt;option value=&#039;$list2[$idx]&#039;$sel&amp;gt;$list1[$idx]&amp;lt;/option&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;/select&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
&lt;br /&gt;
    my $tblnr = 1;&lt;br /&gt;
    my $roomEscaped = FW_htmlEscape($FW_room);                   # ersetzte alle (&amp;amp; &amp;lt; &amp;gt; &#039; (\n)) aus dem aktuellen (?) Raumnamen, wird allerdings nie mehr verwendet&lt;br /&gt;
    my $current;&lt;br /&gt;
    $current = &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($FW_room) if($FW_room);&lt;br /&gt;
    $current = &amp;quot;$FW_ME?cmd=&amp;quot;.urlEncode($cmd) if($cmd);&lt;br /&gt;
    foreach(my $idx = 0; $idx &amp;lt; @list1; $idx++) {                # jetzt durchlaufe die gesamte Liste mit allen Menueintraegen und baue die HTML-Seite (roomBlock1-3)&lt;br /&gt;
      my ($l1, $l2) = ($list1[$idx], $list2[$idx]);&lt;br /&gt;
      if(!$l1) {&lt;br /&gt;
        FW_pO &amp;quot;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; if($idx);                     # die Raumbloecke sind eine Tabelle in der Tabelle menu, diese Table-Ende wird nach jedem Block (Eintrag &amp;quot;&amp;quot;,&amp;quot;&amp;quot;) ausgegeben. Am Anfang ist $idx noch 0, dann also nicht.&lt;br /&gt;
        if($idx&amp;lt;int(@list1)-1) {&lt;br /&gt;
          FW_pO &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=\&amp;quot;room roomBlock$tblnr\&amp;quot;&amp;gt;&amp;quot;;   # hier waere es auch moeglich durch einfuegen eines leeren/speziellen Eintrags einen neunen Block zu generieren&lt;br /&gt;
          $tblnr++;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
      } else {                                                    # hier kommt jetzt der aktuell interessante Teil&lt;br /&gt;
        FW_pF &amp;quot;&amp;lt;tr%s&amp;gt;&amp;quot;, ($current &amp;amp;&amp;amp; $current eq $l2) ? &amp;quot; class=\&amp;quot;sel\&amp;quot;&amp;quot; : &amp;quot;&amp;quot;;   # FW_pF ist eine formatierte Ausgabe&lt;br /&gt;
&lt;br /&gt;
        my $class = &amp;quot;menu_$l1&amp;quot;;&lt;br /&gt;
        $class =~ s/[^A-Z0-9]/_/gi;&lt;br /&gt;
&lt;br /&gt;
        # image tag if we have an icon, else empty&lt;br /&gt;
        my $icoName = &amp;quot;ico$l1&amp;quot;;&lt;br /&gt;
        map { my ($n,$v) = split(&amp;quot;:&amp;quot;,$_); $icoName=$v if($l1 =~ m/^$n$/); }    # die roomOcons sind eine Leerzeichen-getrennte Liste von Raumname:IconName $n:$v&lt;br /&gt;
                        split(&amp;quot; &amp;quot;, AttrVal($FW_wname, &amp;quot;roomIcons&amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
        my $icon = FW_iconName($icoName) ?&lt;br /&gt;
                        FW_makeImage($icoName,$icoName,&amp;quot;icon&amp;quot;).&amp;quot;&amp;amp;nbsp;&amp;quot; : &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        if($l1 eq &amp;quot;Save config&amp;quot;) {                                            # Spezialbehandlung des Menueintrags &lt;br /&gt;
          $l1 .= &#039;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt; &#039;.&lt;br /&gt;
                  &#039;&amp;lt;a id=&amp;quot;saveCheck&amp;quot; class=&amp;quot;changed&amp;quot; style=&amp;quot;visibility:&#039;.&lt;br /&gt;
                  (int(@structChangeHist) ? &#039;visible&#039; : &#039;hidden&#039;).&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;?&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        # Force external browser if FHEMWEB is installed as an offline app.&lt;br /&gt;
        my $target = &#039;&#039;;        # Forum 33066, 39854&lt;br /&gt;
        $target = &#039;target=&amp;quot;_blank&amp;quot;&#039; if($l2 =~ s/^$FW_ME\/\+/$FW_ME\//);&lt;br /&gt;
        $target = &#039;target=&amp;quot;_blank&amp;quot;&#039; if($l2 =~ m/commandref|fhem.de.fhem.html/);&lt;br /&gt;
        if($l2 =~ m/.html$/ || $l2 =~ m/^(http|javascript)/ || length($target)){    # wenn das Kommando auf .html endet oder mit http oder javascript beginnt target definiert (siehe die beiden Zeilen vorher) ist, dann mach aus dem Komando einen Link&lt;br /&gt;
           FW_pO &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;$l2&#039; $target&amp;gt;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;quot;.&lt;br /&gt;
                 &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
        } else {&lt;br /&gt;
          FW_pH $l2, &amp;quot;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;quot;, 1, $class;     # ein bisschen merkwuerdig, aber ab im &amp;quot;normalfall&amp;quot; wird alles ab &amp;lt;td&amp;gt; zwischen &amp;lt;/td&amp;gt; durch FW_pH ausgegeben, siehe unten Erlaeuterung&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        FW_pO &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/div&amp;gt;&amp;quot; if($hasMenuScroll);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei mir sieht das Ergebnis dann so aus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;Quellcode fhem.html (Auszug roomBlock1-3)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;menuScrollArea&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?&#039;&amp;quot;&amp;gt;&amp;lt;div id=&amp;quot;logo&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;menu&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_LEUCHTEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=LEUCHTEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoBELEUCHTUNG&#039; src=&amp;quot;/fhem/images/default/icoBELEUCHTUNG.png&amp;quot; alt=&amp;quot;icoBELEUCHTUNG&amp;quot; title=&amp;quot;icoBELEUCHTUNG&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;LEUCHTEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_ROLLLADEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=ROLLLADEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoROLLLADEN&#039; src=&amp;quot;/fhem/images/user_icons/icoROLLLADEN.png&amp;quot; alt=&amp;quot;icoROLLLADEN&amp;quot; title=&amp;quot;icoROLLLADEN&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;ROLLLADEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_AUSSEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=AUSSEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoAUSSEN&#039; src=&amp;quot;/fhem/images/default/icoAUSSEN.png&amp;quot; alt=&amp;quot;icoAUSSEN&amp;quot; title=&amp;quot;icoAUSSEN&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;AUSSEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_KLIMA&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=KLIMA&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoTemp&#039; src=&amp;quot;/fhem/images/default/icoTemp.png&amp;quot; alt=&amp;quot;icoTemp&amp;quot; title=&amp;quot;icoTemp&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;KLIMA&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_HAUST__R&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=HAUST%c3%9cR&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoHouse&#039; src=&amp;quot;/fhem/images/default/icoHouse.png&amp;quot; alt=&amp;quot;icoHouse&amp;quot; title=&amp;quot;icoHouse&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;HAUSTÜR&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_HEIZUNG&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=HEIZUNG&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoHEIZUNG&#039; src=&amp;quot;/fhem/images/default/icoHEIZUNG.png&amp;quot; alt=&amp;quot;icoHEIZUNG&amp;quot; title=&amp;quot;icoHEIZUNG&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;HEIZUNG&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_MULTIMEDIA&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=MULTIMEDIA&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoAUDIOVIDEO&#039; src=&amp;quot;/fhem/images/user_icons/icoAUDIOVIDEO.png&amp;quot; alt=&amp;quot;icoAUDIOVIDEO&amp;quot; title=&amp;quot;icoAUDIOVIDEO&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;MULTIMEDIA&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;sel&amp;quot;&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_Interfaces&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eInterfaces&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;Interfaces&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_LOGO_&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eLOGO%21&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;LOGO!&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Schaltuhren&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=Schaltuhren&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoUhr&#039; src=&amp;quot;/fhem/images/default/icoUhr.png&amp;quot; alt=&amp;quot;icoUhr&amp;quot; title=&amp;quot;icoUhr&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Schaltuhren&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Z__hler&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=Z%c3%a4hler&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoZAEHLER&#039; src=&amp;quot;/fhem/images/user_icons/icoZAEHLER.png&amp;quot; alt=&amp;quot;icoZAEHLER&amp;quot; title=&amp;quot;icoZAEHLER&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Zähler&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Everything&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=all&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoEverything&#039; src=&amp;quot;/fhem/images/default/icoEverything.png&amp;quot; alt=&amp;quot;icoEverything&amp;quot; title=&amp;quot;icoEverything&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Everything&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock2&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Logfile&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem/FileLog_logWrapper?dev=Logfile&amp;amp;type=text&amp;amp;file=fhem-2022-09.log&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Logfile&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;/fhem/docs/commandref.html&#039; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Commandref&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.de/fhem.html#Documentation&#039; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Remote doc&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Edit_files&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20list&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Edit files&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Select_style&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20select&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Select style&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Event_monitor&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20eventMonitor&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Event monitor&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock3&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.de&#039; &amp;gt;&amp;lt;span&amp;gt;fhem.de&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://culfw.de&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        culfw.de&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.clx.local:8083/fhem?cmd=style%20iconFor&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        Icons&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________restart&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=shutdown+restart&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        restart&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________rereadcfg&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=rereadcfg&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        rereadcfg&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________rereadicons&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=set WEB rereadicons&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        rereadicons&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.clx.local:8083/fhem/ftui/index.html&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
			FTUI&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 my $class = &amp;quot;menu_$l1&amp;quot;;&lt;br /&gt;
 $class =~ s/[^A-Z0-9]/_/gi;                   # alle nicht Buchstaben und Ziffern werden durch Unterstrich ersetzt, also auch Umlaute&lt;br /&gt;
 my $icon = FW_iconName($icoName) ? FW_makeImage($icoName,$icoName,&amp;quot;icon&amp;quot;).&amp;quot;&amp;amp;nbsp;&amp;quot; : &amp;quot;&amp;quot;;&lt;br /&gt;
 FW_pH $l2, &amp;quot;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;quot;, 1, $class;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Everything&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=all&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoEverything&#039; src=&amp;quot;/fhem/images/default/icoEverything.png&amp;quot; alt=&amp;quot;icoEverything&amp;quot; title=&amp;quot;icoEverything&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Everything&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;sel&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_Interfaces&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eInterfaces&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;Interfaces&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_LOGO_&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eLOGO%21&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;LOGO!&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FHEM ROOMinROOMIcon.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Man hat also 2 Baustellen. Moechte man ein Icon am uebergoeordneten Menu, muesste man es in die Klasse packen. Bei den Untermenues muss man die Funktion FW_iconName in 01_FHEMWEB.pm patchen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
# check if the icon exists, and if yes, returns its &amp;quot;logical&amp;quot; name;&lt;br /&gt;
sub&lt;br /&gt;
FW_iconName($)                        # der Parameter ist falls $l1 nicht irgendwo vorher im Code manipuliert wurde in meinem Beispiel icoSystem-&amp;gt;Interfaces. Ein Icon mit diesem Namen gibt es nicht!&lt;br /&gt;
{&lt;br /&gt;
  my ($oname)= @_;&lt;br /&gt;
  return undef if(!defined($oname));&lt;br /&gt;
  my $name = $oname;&lt;br /&gt;
  $name =~ s/@.*//;&lt;br /&gt;
  foreach my $pe (@FW_iconDirs) {&lt;br /&gt;
    return $oname if($pe &amp;amp;&amp;amp; $FW_icons{$pe} &amp;amp;&amp;amp; $FW_icons{$pe}{$name});&lt;br /&gt;
  }&lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die Variable &lt;br /&gt;
 $l1 SYSTEM-&amp;amp;gt;Interfaces, bzw. SYSTEM__gt_Interfaces&lt;br /&gt;
wird per Regex getestet.&lt;br /&gt;
&lt;br /&gt;
Wenn man also das Attribut so anlegt, klappt es: &lt;br /&gt;
&lt;br /&gt;
 attr WEB roomIcons ... SYSTEM-.*Interfaces:icoSYSTEM ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FHEM ROOMinROOMIcon 2.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Das Icon vor den &amp;quot;Stammraum&amp;quot; zu platzieren gestaltet sich etwas schwieriger.&lt;br /&gt;
&lt;br /&gt;
Die passende Stelle in fhemweb.js ist zwar schnell ausgemacht, aber einfach ein Image nach dem &amp;lt;td&amp;gt; einfuegen bringt nicht das genwueschte Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;fhemweb.js (Auszug)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line&amp;gt;&lt;br /&gt;
function&lt;br /&gt;
FW_treeMenu()&lt;br /&gt;
{&lt;br /&gt;
  var a = $(&amp;quot;a&amp;quot;).get(0);&lt;br /&gt;
  var col = &#039;rgb(39, 135, 38)&#039;;&lt;br /&gt;
  if(window.getComputedStyle &amp;amp;&amp;amp; a)&lt;br /&gt;
    col = getComputedStyle(a,null).getPropertyValue(&#039;color&#039;); &lt;br /&gt;
  FW_arrowRight = &#039;data:image/svg+xml;utf8,&amp;lt;svg viewBox=&amp;quot;0 0 1792 1792&amp;quot; xmlns=&amp;quot;http://www.w3.org/2000/svg&amp;quot;&amp;gt;&amp;lt;path fill=&amp;quot;gray&amp;quot; d=&amp;quot;M1171 960q0 13-10 23l-466 466q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l393-393-393-393q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l466 466q10 10 10 23z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;&lt;br /&gt;
      .replace(&#039;gray&#039;, col);&lt;br /&gt;
  FW_arrowDown =FW_arrowRight.replace(&#039;/&amp;gt;&#039;,&#039; transform=&amp;quot;rotate(90,896,896)&amp;quot;/&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
  var fnd;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;div#menu table.room&amp;quot;).each(function(){     // one loop per Block&lt;br /&gt;
    var t = this, ma = {};&lt;br /&gt;
    $(t).find(&amp;quot;td &amp;gt; div &amp;gt; a &amp;gt; span&amp;quot;).each(function(e){&lt;br /&gt;
      var span = this, spanTxt = $(span).text().replace(/,/g,&#039;&#039;);&lt;br /&gt;
      var ta = spanTxt.split(&amp;quot;-&amp;gt;&amp;quot;);&lt;br /&gt;
      if(ta.length &amp;lt;= 1)&lt;br /&gt;
        return;&lt;br /&gt;
      fnd = true;&lt;br /&gt;
      var nxt=&amp;quot;&amp;quot;, lst=&amp;quot;&amp;quot;, tr=$(span).closest(&amp;quot;tr&amp;quot;);&lt;br /&gt;
      for(var i1=0; i1&amp;lt;ta.length-1; i1++) {&lt;br /&gt;
        nxt += &amp;quot;-&amp;gt;&amp;quot;+ta[i1];&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;#####HIER PLATZ FUER ICON####&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        ma[nxt] = true;&lt;br /&gt;
        lst = nxt;&lt;br /&gt;
      }&lt;br /&gt;
      $(span).html(ta[ta.length-1]);&lt;br /&gt;
      $(tr).attr(&amp;quot;data-mTree&amp;quot;, nxt)&lt;br /&gt;
           .addClass(&amp;quot;menuTree level&amp;quot;+(ta.length-1));&lt;br /&gt;
    });&lt;br /&gt;
  });&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Patch==&lt;br /&gt;
fhemweb.js (FHEM 6.2 ) Zeile 1157&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);     &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; zwischen &amp;lt;td&amp;gt;&amp;lt;div&amp;gt; und &amp;lt;a href...&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;img src=&#039;/fhem/images/default/ico&amp;quot;+ta[i1]+&amp;quot;.png&#039;&amp;gt;&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht elegant, aber funktioniert solange ein Icon mit dem gleichen Namen wie der uebergeordnete Raum existiert. Hier SYSTEM also icoSYSTEM.png&lt;br /&gt;
&lt;br /&gt;
=(PGM2) MenuBloecke erweitern=&lt;br /&gt;
&lt;br /&gt;
Ich will meine FTUI-Seiten in PGM2 anbinden und nicht nur den Link einfach unten anhaengen (siehe Screenshot).&lt;br /&gt;
FTUI soll in einem eigenen Block stehen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FTUI Link.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Um einen neuen Block zu beginnen ist in der Menuliste ein leerer Eintrag notwendig. Siehe Quellcode in FHEMWEB.pm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
  my @list = (&lt;br /&gt;
     &amp;quot;Everything&amp;quot;,    &amp;quot;$FW_ME?room=all&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,              &amp;quot;&amp;quot;,                                    &amp;lt;--------------------neuer Block&lt;br /&gt;
     &amp;quot;Commandref&amp;quot;,    &amp;quot;$FW_ME/docs/commandref${sfx}.html&amp;quot;,&lt;br /&gt;
     &amp;quot;Remote doc&amp;quot;,    &amp;quot;http://fhem.de/fhem.html#Documentation&amp;quot;,&lt;br /&gt;
     &amp;quot;Edit files&amp;quot;,    &amp;quot;$FW_ME?cmd=style%20list&amp;quot;,&lt;br /&gt;
     &amp;quot;Select style&amp;quot;,  &amp;quot;$FW_ME?cmd=style%20select&amp;quot;,&lt;br /&gt;
     &amp;quot;Event monitor&amp;quot;, &amp;quot;$FW_ME?cmd=style%20eventMonitor&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,           &amp;quot;&amp;quot;);                                      &amp;lt;--------------------neuer Block&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leider ist es mir nicht gelungen in der fhem.conf direkt einen leeren Eintrag zu erzwingen, daher mein Umweg ueber einen Platzhalter (NEWBLOCK)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEB menuEntries fhem.de,http://fhem.de,\&lt;br /&gt;
                        culfw.de,http://culfw.de,\&lt;br /&gt;
                        Icons,http://fhem.clx.local:8083/fhem?cmd=style%20iconFor,\&lt;br /&gt;
                        restart,cmd=shutdown+restart,\&lt;br /&gt;
                        rereadcfg,cmd=rereadcfg,\&lt;br /&gt;
                        rereadicons,cmd=set WEB rereadicons,\&lt;br /&gt;
                        NEWBLOCK,NEWBLOCK,\&lt;br /&gt;
                        FTUI,http://fhem.clx.local:8083/fhem/ftui/index.html&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Patch==&lt;br /&gt;
FHEM/01_FHEMWEB.pm Version 6.2 Zeile 1803 (Nachtrag: In Version 6.3 ist es Zeile 1815)&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot Blockbuilding.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
  my @me = split(&amp;quot;,&amp;quot;, AttrVal($FW_wname, &amp;quot;menuEntries&amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
  push @list, @me, &amp;quot;&amp;quot;, &amp;quot;&amp;quot; if(@me);&lt;br /&gt;
&lt;br /&gt;
===============================snip==================================================&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx++) {&lt;br /&gt;
    if ( $list[$idx] =~ m/NEWBLOCK/ ) {&lt;br /&gt;
      splice @list, $idx, 1,(&amp;quot;&amp;quot;);  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  splice @list, 6, 0, (&amp;quot;&amp;quot;,&amp;quot;&amp;quot;); #  fuegt ein Blockende nach Logfile ein&lt;br /&gt;
===============================snip==================================================&lt;br /&gt;
   &lt;br /&gt;
  my $lastname = &amp;quot;,&amp;quot;; &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; neu in Version 6.2&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx+= 2) {&lt;br /&gt;
    next if($FW_hiddenroom{$list[$idx]} || $list[$idx] eq $lastname);&lt;br /&gt;
    push @list1, $list[$idx];&lt;br /&gt;
    push @list2, $list[$idx+1];&lt;br /&gt;
    $lastname = $list[$idx];&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem Patch einen Restart mit&lt;br /&gt;
 systemctl restart fhem&lt;br /&gt;
durchfuehren.&lt;br /&gt;
&lt;br /&gt;
=Eventlog=&lt;br /&gt;
&lt;br /&gt;
Ich habe das Problem, dass das KM271 Logomatic 2107 Modul einfach zu  geschschwaetzig ist und zudem auch noch ERR-Eintraege produziert, die ich nicht mitloggen moechte.&lt;br /&gt;
&lt;br /&gt;
 2023-12-24 09:24:40 KM271 KM271 ERR_Fehlerspeicher1: Code 01 (+): 09:04Uhr vor 68 Tagen | (-): 195:124Uhr vor 0 Tagen&lt;br /&gt;
&lt;br /&gt;
Derzeit nutze ich das Filelog.&lt;br /&gt;
&lt;br /&gt;
 define Logfile FileLog /var/log/fhem/fhem-%Y-%m.log fakelog&lt;br /&gt;
&lt;br /&gt;
Dabei bin ueber den Eintrag fakelog gestolpert, mit dem ich irgendwie nix mehr anfangen konnte. Habe ich vermutlich mit CopyPaste aus einer Beispiel-Conf uebernommen.&lt;br /&gt;
&lt;br /&gt;
Folgende [https://forum.fhem.de/index.php?topic=95689.0 Seite] brachte Licht in&#039;s Dunkel.&lt;br /&gt;
&lt;br /&gt;
Das ist die Syntax des Logfile Devices.&lt;br /&gt;
&lt;br /&gt;
 define &amp;lt;name&amp;gt; FileLog &amp;lt;filename&amp;gt; &amp;lt;regexp&amp;gt; [readonly] &lt;br /&gt;
&lt;br /&gt;
Daraus kann man ableiten das der Begriff fakelog einen regulaeren Ausdruck darstellt. Irgendjemand hat sich den Begriff mal ausgedacht, um den notwendigen Parameter mit einem Suchfilter zu belegen, der niemals eintritt.&lt;br /&gt;
&lt;br /&gt;
Ein anderer Zeitgenosse hat jetzt genau diesen Begriff als Suchfilter in seiner Wirkung begrenzt.[https://forum.fhem.de/index.php/topic,95351]&lt;br /&gt;
Dies wurde mit dem Update vom 05. Januar 2019 wirksam (Rudi hat im Januar update das controlset disabled; Boeser Rudi)&lt;br /&gt;
&lt;br /&gt;
 Mit dem folgenden kleinen patch werden set, Regexp parts und Create SVG plot für fakelog nicht mehr angezeigt. meiner meinung nach ist nichts davon sinnvoll und verhindert so fehlbedienung. Ich würde gerne ein solches fakelog device dann auch für die anzeige der alexa-fhem logs nutzen statt den anzeige teil doppelt zu implementieren. aber mit den vielen knöpfen ist das eine mögliche fehlerquelle :)&lt;br /&gt;
&lt;br /&gt;
Will man wieder den alten Zustand herstellen, muss man sich nur einen neuen Suchfilter, der nie zutrifft, ausdenken.&lt;br /&gt;
&lt;br /&gt;
Aber nun zum eigentlichen Problem.&lt;br /&gt;
Ein guter Hinweis ist [https://forum.fhem.de/index.php?topic=51611.0 hier] &lt;br /&gt;
&lt;br /&gt;
 Wenn bestimmte Zeilen nicht in die Logdatei geschrieben werden sollen, ist das Attribut ignoreRegexp hilfreich. Wenn beispielsweise alle Zeilen, die die Zeichenfolge &amp;quot;AbCd&amp;quot; oder &amp;quot;CdEf&amp;quot; enthalten nicht geloggt werden sollen, dann wäre&lt;br /&gt;
 &lt;br /&gt;
 Code Auswählen&lt;br /&gt;
 attr &amp;lt;log-name&amp;gt; ignoreRegexp .*AbCd.*|.*CdEf.*&lt;br /&gt;
 eine Attributdefinition, die das ermöglicht. &lt;br /&gt;
 &lt;br /&gt;
 Dies bezieht sich aber nur auf normale FileLog-Instanzen. Falls Events aus dem globalen FHEM-Logfile ausgeschlossen werden sollen, muss man das Attribut in global angeben. (Zusammenhang siehe #Globale Logdatei) &lt;br /&gt;
  &lt;br /&gt;
 Code Auswählen&lt;br /&gt;
 attr global ignoreRegexp .*AbCd.*|.*CdEf.*&lt;br /&gt;
&lt;br /&gt;
=KNX Toggle=&lt;br /&gt;
Um einen toogle z.B. auf einem Licht hinzubekommen. &lt;br /&gt;
&lt;br /&gt;
 set L_Bar_Auszen g1 toggle&lt;br /&gt;
&lt;br /&gt;
ist es notzwendig das Attribut KNX_TOGGLE zu setzen.&lt;br /&gt;
&lt;br /&gt;
 attr KNX_TOGGLE L_Bar_Auszen:state&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_Stolperfallen&amp;diff=4851</id>
		<title>(FHEM) Stolperfallen</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_Stolperfallen&amp;diff=4851"/>
		<updated>2026-04-11T06:46:12Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Eventlog */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=(PGM2) RoomIcons bei Untermenues=&lt;br /&gt;
Hab mal wieder einen Volltreffer gelandet. Weil die Raeume im FHEMWEB zu viele wurden, aber auch nicht mit &amp;quot;hiddenroom&amp;quot; unterdrueckt werden sollten. Wollte ich mal &#039;Room in Room&#039; aussprobieren.&lt;br /&gt;
Leider scheint das nicht zu funktionieren (Stand fhem 6.0). Aus der Commandref geht eine Alternative hervor. Und zwar, wenn man im Raumnamen ein &#039;-&amp;gt;&#039; verwendet, fuehrt das dazu, dass im FHEM-Roommenue&lt;br /&gt;
aufklappbare Unterraeume (die nach dem -&amp;gt;) angezeigt werden. Man kann also keinen kompletten bestehenden Raum einem anderen Raum zuordnen, sondern man ist gezwungen alle Devices den neuen Raumnamen zuzuordnen.&lt;br /&gt;
Ein unschoener Effekt ist zudem, dass der uebergeordnete Raumname kein Icon mehr hat.&lt;br /&gt;
&lt;br /&gt;
Dies bestatigt diese [https://forum.fhem.de/index.php?topic=91800.0 Forums-Nachfrage].&lt;br /&gt;
&lt;br /&gt;
Ein Blick in den Quellcode von 01_FHEMWEB.pm zeigt:&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;01-FHEMWEB.pm (Auszug)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
 ##########################&lt;br /&gt;
  # Rooms and other links&lt;br /&gt;
  foreach my $r (@FW_roomsArr) {                                     # Das Array mit den Raumnamen wird nacheinander durchlaufen; In der Variablen r steht der aktuell zu pruefende Raumname&lt;br /&gt;
    next if($r eq &amp;quot;hidden&amp;quot; || $FW_hiddenroom{$r});                   # Ist der Raum hidden geh zu naechsten = tue nichts mit diesem Raum&lt;br /&gt;
    $FW_room = AttrVal($FW_wname, &amp;quot;defaultRoom&amp;quot;, $r)&lt;br /&gt;
        if(!$FW_room &amp;amp;&amp;amp; $FW_ss);&lt;br /&gt;
    if(my $devspec = $FW_extraRooms{$r}) {&lt;br /&gt;
      my $r = $r;&lt;br /&gt;
      $r =~ s/&amp;amp;nbsp;/ /g;                                            # Ersetze alle NoBreakSpaces durch Leerzeichen&lt;br /&gt;
      push @list1, FW_htmlEscape($r);&lt;br /&gt;
      push @list2, &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($devspec);               # Da habe ich noch keine Idee zu &lt;br /&gt;
    } else {&lt;br /&gt;
      push @list1, FW_htmlEscape($r);&lt;br /&gt;
      push @list2, &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($r);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  my $sfx = AttrVal(&amp;quot;global&amp;quot;, &amp;quot;language&amp;quot;, &amp;quot;EN&amp;quot;);&lt;br /&gt;
  $sfx = ($sfx eq &amp;quot;EN&amp;quot; ? &amp;quot;&amp;quot; : &amp;quot;_$sfx&amp;quot;);                              # wenn die eingestellte Sprache EN ist , dann ist sfx leer; es wird also z.B: an den Dateinamen commandref nichts angehaengt (sfx steht fuer suffix)&lt;br /&gt;
  my @list = (                                                       # das ist wohl die Standard Ausgangsliste; interassant: Everything gehoert zum Raummenue und der Rest zum (Name?)-Menue darunter&lt;br /&gt;
     &amp;quot;Everything&amp;quot;,    &amp;quot;$FW_ME?room=all&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,              &amp;quot;&amp;quot;,&lt;br /&gt;
     &amp;quot;Commandref&amp;quot;,    &amp;quot;$FW_ME/docs/commandref${sfx}.html&amp;quot;,&lt;br /&gt;
     &amp;quot;Remote doc&amp;quot;,    &amp;quot;http://fhem.de/fhem.html#Documentation&amp;quot;,&lt;br /&gt;
     &amp;quot;Edit files&amp;quot;,    &amp;quot;$FW_ME?cmd=style%20list&amp;quot;,&lt;br /&gt;
     &amp;quot;Select style&amp;quot;,  &amp;quot;$FW_ME?cmd=style%20select&amp;quot;,&lt;br /&gt;
     &amp;quot;Event monitor&amp;quot;, &amp;quot;$FW_ME?cmd=style%20eventMonitor&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,           &amp;quot;&amp;quot;);&lt;br /&gt;
  my $lastname = &amp;quot;,&amp;quot;; # Avoid double &amp;quot;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
  my $lfn = &amp;quot;Logfile&amp;quot;;&lt;br /&gt;
  if($defs{$lfn}) { # Add the current Logfile to the list if defined&lt;br /&gt;
    my @l = FW_fileList($defs{$lfn}{logfile},1);&lt;br /&gt;
    my $fn = pop @l;&lt;br /&gt;
    splice @list, 4,0, (&amp;quot;Logfile&amp;quot;,&lt;br /&gt;
                      &amp;quot;$FW_ME/FileLog_logWrapper?dev=$lfn&amp;amp;type=text&amp;amp;file=$fn&amp;quot;);    # fuege den Logfile-Eintrag vor dem Eintrag Commandref ein (wenn definiert)&lt;br /&gt;
  }&lt;br /&gt;
# die menuEntries sind der dritte Block (Rooms, (Name?)-Menue, menuEntries)&lt;br /&gt;
  my @me = split(&amp;quot;,&amp;quot;, AttrVal($FW_wname, &amp;quot;menuEntries&amp;quot;, &amp;quot;&amp;quot;));       # die menuEntries sind eine komma-separierte Liste von Eintraegen der Art Menuname,Kommando     &lt;br /&gt;
  push @list, @me, &amp;quot;&amp;quot;, &amp;quot;&amp;quot; if(@me);                                  # Die Eintragungen werden angehaengt &amp;quot;&amp;quot;,&amp;quot;&amp;quot; ist jeweils der Block-Abschluss, bzw. Separator&lt;br /&gt;
&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx+= 2) {                        # haenge die Kommandoliste an die Raumliste an&lt;br /&gt;
    next if($FW_hiddenroom{$list[$idx]} || $list[$idx] eq $lastname);&lt;br /&gt;
    push @list1, $list[$idx];                                       # in list1 stehen die Menunamen und in list2 die Aktionen&lt;br /&gt;
    push @list2, $list[$idx+1];&lt;br /&gt;
    $lastname = $list[$idx];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;div id=\&amp;quot;menu\&amp;quot;&amp;gt;&amp;quot;;                                        # hier geht&#039;s dann los mit dem Zusammenbau der HTML-Seite (siehe unten)&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;table&amp;gt;&amp;quot;;&lt;br /&gt;
  if($FW_ss) {  # Make a selection sensitive dropdown list          # $FW_ss also &#039;&#039;&#039;F&#039;&#039;&#039;HEM&#039;&#039;&#039;W&#039;&#039;&#039;EB &#039;&#039;&#039;s&#039;&#039;&#039;ensitive &#039;&#039;&#039;s&#039;&#039;&#039;election auf einem &#039;&#039;&#039;S&#039;&#039;&#039;mall &#039;&#039;&#039;S&#039;&#039;&#039;creen &lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;select OnChange=\&amp;quot;location.href=&amp;quot; .&lt;br /&gt;
                              &amp;quot;this.options[this.selectedIndex].value\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
    foreach(my $idx = 0; $idx &amp;lt; @list1; $idx++) {&lt;br /&gt;
      next if(!$list1[$idx]);&lt;br /&gt;
      my $sel = ($list1[$idx] eq $FW_room ? &amp;quot; selected=\&amp;quot;selected\&amp;quot;&amp;quot;  : &amp;quot;&amp;quot;);&lt;br /&gt;
      FW_pO &amp;quot;&amp;lt;option value=&#039;$list2[$idx]&#039;$sel&amp;gt;$list1[$idx]&amp;lt;/option&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;/select&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
&lt;br /&gt;
    my $tblnr = 1;&lt;br /&gt;
    my $roomEscaped = FW_htmlEscape($FW_room);                   # ersetzte alle (&amp;amp; &amp;lt; &amp;gt; &#039; (\n)) aus dem aktuellen (?) Raumnamen, wird allerdings nie mehr verwendet&lt;br /&gt;
    my $current;&lt;br /&gt;
    $current = &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($FW_room) if($FW_room);&lt;br /&gt;
    $current = &amp;quot;$FW_ME?cmd=&amp;quot;.urlEncode($cmd) if($cmd);&lt;br /&gt;
    foreach(my $idx = 0; $idx &amp;lt; @list1; $idx++) {                # jetzt durchlaufe die gesamte Liste mit allen Menueintraegen und baue die HTML-Seite (roomBlock1-3)&lt;br /&gt;
      my ($l1, $l2) = ($list1[$idx], $list2[$idx]);&lt;br /&gt;
      if(!$l1) {&lt;br /&gt;
        FW_pO &amp;quot;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; if($idx);                     # die Raumbloecke sind eine Tabelle in der Tabelle menu, diese Table-Ende wird nach jedem Block (Eintrag &amp;quot;&amp;quot;,&amp;quot;&amp;quot;) ausgegeben. Am Anfang ist $idx noch 0, dann also nicht.&lt;br /&gt;
        if($idx&amp;lt;int(@list1)-1) {&lt;br /&gt;
          FW_pO &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=\&amp;quot;room roomBlock$tblnr\&amp;quot;&amp;gt;&amp;quot;;   # hier waere es auch moeglich durch einfuegen eines leeren/speziellen Eintrags einen neunen Block zu generieren&lt;br /&gt;
          $tblnr++;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
      } else {                                                    # hier kommt jetzt der aktuell interessante Teil&lt;br /&gt;
        FW_pF &amp;quot;&amp;lt;tr%s&amp;gt;&amp;quot;, ($current &amp;amp;&amp;amp; $current eq $l2) ? &amp;quot; class=\&amp;quot;sel\&amp;quot;&amp;quot; : &amp;quot;&amp;quot;;   # FW_pF ist eine formatierte Ausgabe&lt;br /&gt;
&lt;br /&gt;
        my $class = &amp;quot;menu_$l1&amp;quot;;&lt;br /&gt;
        $class =~ s/[^A-Z0-9]/_/gi;&lt;br /&gt;
&lt;br /&gt;
        # image tag if we have an icon, else empty&lt;br /&gt;
        my $icoName = &amp;quot;ico$l1&amp;quot;;&lt;br /&gt;
        map { my ($n,$v) = split(&amp;quot;:&amp;quot;,$_); $icoName=$v if($l1 =~ m/^$n$/); }    # die roomOcons sind eine Leerzeichen-getrennte Liste von Raumname:IconName $n:$v&lt;br /&gt;
                        split(&amp;quot; &amp;quot;, AttrVal($FW_wname, &amp;quot;roomIcons&amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
        my $icon = FW_iconName($icoName) ?&lt;br /&gt;
                        FW_makeImage($icoName,$icoName,&amp;quot;icon&amp;quot;).&amp;quot;&amp;amp;nbsp;&amp;quot; : &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        if($l1 eq &amp;quot;Save config&amp;quot;) {                                            # Spezialbehandlung des Menueintrags &lt;br /&gt;
          $l1 .= &#039;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt; &#039;.&lt;br /&gt;
                  &#039;&amp;lt;a id=&amp;quot;saveCheck&amp;quot; class=&amp;quot;changed&amp;quot; style=&amp;quot;visibility:&#039;.&lt;br /&gt;
                  (int(@structChangeHist) ? &#039;visible&#039; : &#039;hidden&#039;).&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;?&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        # Force external browser if FHEMWEB is installed as an offline app.&lt;br /&gt;
        my $target = &#039;&#039;;        # Forum 33066, 39854&lt;br /&gt;
        $target = &#039;target=&amp;quot;_blank&amp;quot;&#039; if($l2 =~ s/^$FW_ME\/\+/$FW_ME\//);&lt;br /&gt;
        $target = &#039;target=&amp;quot;_blank&amp;quot;&#039; if($l2 =~ m/commandref|fhem.de.fhem.html/);&lt;br /&gt;
        if($l2 =~ m/.html$/ || $l2 =~ m/^(http|javascript)/ || length($target)){    # wenn das Kommando auf .html endet oder mit http oder javascript beginnt target definiert (siehe die beiden Zeilen vorher) ist, dann mach aus dem Komando einen Link&lt;br /&gt;
           FW_pO &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;$l2&#039; $target&amp;gt;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;quot;.&lt;br /&gt;
                 &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
        } else {&lt;br /&gt;
          FW_pH $l2, &amp;quot;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;quot;, 1, $class;     # ein bisschen merkwuerdig, aber ab im &amp;quot;normalfall&amp;quot; wird alles ab &amp;lt;td&amp;gt; zwischen &amp;lt;/td&amp;gt; durch FW_pH ausgegeben, siehe unten Erlaeuterung&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        FW_pO &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/div&amp;gt;&amp;quot; if($hasMenuScroll);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei mir sieht das Ergebnis dann so aus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;Quellcode fhem.html (Auszug roomBlock1-3)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;menuScrollArea&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?&#039;&amp;quot;&amp;gt;&amp;lt;div id=&amp;quot;logo&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;menu&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_LEUCHTEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=LEUCHTEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoBELEUCHTUNG&#039; src=&amp;quot;/fhem/images/default/icoBELEUCHTUNG.png&amp;quot; alt=&amp;quot;icoBELEUCHTUNG&amp;quot; title=&amp;quot;icoBELEUCHTUNG&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;LEUCHTEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_ROLLLADEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=ROLLLADEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoROLLLADEN&#039; src=&amp;quot;/fhem/images/user_icons/icoROLLLADEN.png&amp;quot; alt=&amp;quot;icoROLLLADEN&amp;quot; title=&amp;quot;icoROLLLADEN&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;ROLLLADEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_AUSSEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=AUSSEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoAUSSEN&#039; src=&amp;quot;/fhem/images/default/icoAUSSEN.png&amp;quot; alt=&amp;quot;icoAUSSEN&amp;quot; title=&amp;quot;icoAUSSEN&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;AUSSEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_KLIMA&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=KLIMA&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoTemp&#039; src=&amp;quot;/fhem/images/default/icoTemp.png&amp;quot; alt=&amp;quot;icoTemp&amp;quot; title=&amp;quot;icoTemp&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;KLIMA&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_HAUST__R&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=HAUST%c3%9cR&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoHouse&#039; src=&amp;quot;/fhem/images/default/icoHouse.png&amp;quot; alt=&amp;quot;icoHouse&amp;quot; title=&amp;quot;icoHouse&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;HAUSTÜR&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_HEIZUNG&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=HEIZUNG&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoHEIZUNG&#039; src=&amp;quot;/fhem/images/default/icoHEIZUNG.png&amp;quot; alt=&amp;quot;icoHEIZUNG&amp;quot; title=&amp;quot;icoHEIZUNG&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;HEIZUNG&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_MULTIMEDIA&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=MULTIMEDIA&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoAUDIOVIDEO&#039; src=&amp;quot;/fhem/images/user_icons/icoAUDIOVIDEO.png&amp;quot; alt=&amp;quot;icoAUDIOVIDEO&amp;quot; title=&amp;quot;icoAUDIOVIDEO&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;MULTIMEDIA&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;sel&amp;quot;&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_Interfaces&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eInterfaces&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;Interfaces&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_LOGO_&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eLOGO%21&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;LOGO!&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Schaltuhren&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=Schaltuhren&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoUhr&#039; src=&amp;quot;/fhem/images/default/icoUhr.png&amp;quot; alt=&amp;quot;icoUhr&amp;quot; title=&amp;quot;icoUhr&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Schaltuhren&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Z__hler&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=Z%c3%a4hler&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoZAEHLER&#039; src=&amp;quot;/fhem/images/user_icons/icoZAEHLER.png&amp;quot; alt=&amp;quot;icoZAEHLER&amp;quot; title=&amp;quot;icoZAEHLER&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Zähler&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Everything&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=all&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoEverything&#039; src=&amp;quot;/fhem/images/default/icoEverything.png&amp;quot; alt=&amp;quot;icoEverything&amp;quot; title=&amp;quot;icoEverything&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Everything&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock2&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Logfile&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem/FileLog_logWrapper?dev=Logfile&amp;amp;type=text&amp;amp;file=fhem-2022-09.log&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Logfile&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;/fhem/docs/commandref.html&#039; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Commandref&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.de/fhem.html#Documentation&#039; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Remote doc&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Edit_files&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20list&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Edit files&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Select_style&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20select&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Select style&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Event_monitor&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20eventMonitor&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Event monitor&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock3&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.de&#039; &amp;gt;&amp;lt;span&amp;gt;fhem.de&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://culfw.de&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        culfw.de&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.clx.local:8083/fhem?cmd=style%20iconFor&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        Icons&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________restart&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=shutdown+restart&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        restart&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________rereadcfg&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=rereadcfg&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        rereadcfg&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________rereadicons&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=set WEB rereadicons&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        rereadicons&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.clx.local:8083/fhem/ftui/index.html&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
			FTUI&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 my $class = &amp;quot;menu_$l1&amp;quot;;&lt;br /&gt;
 $class =~ s/[^A-Z0-9]/_/gi;                   # alle nicht Buchstaben und Ziffern werden durch Unterstrich ersetzt, also auch Umlaute&lt;br /&gt;
 my $icon = FW_iconName($icoName) ? FW_makeImage($icoName,$icoName,&amp;quot;icon&amp;quot;).&amp;quot;&amp;amp;nbsp;&amp;quot; : &amp;quot;&amp;quot;;&lt;br /&gt;
 FW_pH $l2, &amp;quot;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;quot;, 1, $class;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Everything&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=all&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoEverything&#039; src=&amp;quot;/fhem/images/default/icoEverything.png&amp;quot; alt=&amp;quot;icoEverything&amp;quot; title=&amp;quot;icoEverything&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Everything&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;sel&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_Interfaces&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eInterfaces&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;Interfaces&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_LOGO_&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eLOGO%21&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;LOGO!&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FHEM ROOMinROOMIcon.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Man hat also 2 Baustellen. Moechte man ein Icon am uebergoeordneten Menu, muesste man es in die Klasse packen. Bei den Untermenues muss man die Funktion FW_iconName in 01_FHEMWEB.pm patchen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
# check if the icon exists, and if yes, returns its &amp;quot;logical&amp;quot; name;&lt;br /&gt;
sub&lt;br /&gt;
FW_iconName($)                        # der Parameter ist falls $l1 nicht irgendwo vorher im Code manipuliert wurde in meinem Beispiel icoSystem-&amp;gt;Interfaces. Ein Icon mit diesem Namen gibt es nicht!&lt;br /&gt;
{&lt;br /&gt;
  my ($oname)= @_;&lt;br /&gt;
  return undef if(!defined($oname));&lt;br /&gt;
  my $name = $oname;&lt;br /&gt;
  $name =~ s/@.*//;&lt;br /&gt;
  foreach my $pe (@FW_iconDirs) {&lt;br /&gt;
    return $oname if($pe &amp;amp;&amp;amp; $FW_icons{$pe} &amp;amp;&amp;amp; $FW_icons{$pe}{$name});&lt;br /&gt;
  }&lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die Variable &lt;br /&gt;
 $l1 SYSTEM-&amp;amp;gt;Interfaces, bzw. SYSTEM__gt_Interfaces&lt;br /&gt;
wird per Regex getestet.&lt;br /&gt;
&lt;br /&gt;
Wenn man also das Attribut so anlegt, klappt es: &lt;br /&gt;
&lt;br /&gt;
 attr WEB roomIcons ... SYSTEM-.*Interfaces:icoSYSTEM ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FHEM ROOMinROOMIcon 2.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Das Icon vor den &amp;quot;Stammraum&amp;quot; zu platzieren gestaltet sich etwas schwieriger.&lt;br /&gt;
&lt;br /&gt;
Die passende Stelle in fhemweb.js ist zwar schnell ausgemacht, aber einfach ein Image nach dem &amp;lt;td&amp;gt; einfuegen bringt nicht das genwueschte Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;fhemweb.js (Auszug)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line&amp;gt;&lt;br /&gt;
function&lt;br /&gt;
FW_treeMenu()&lt;br /&gt;
{&lt;br /&gt;
  var a = $(&amp;quot;a&amp;quot;).get(0);&lt;br /&gt;
  var col = &#039;rgb(39, 135, 38)&#039;;&lt;br /&gt;
  if(window.getComputedStyle &amp;amp;&amp;amp; a)&lt;br /&gt;
    col = getComputedStyle(a,null).getPropertyValue(&#039;color&#039;); &lt;br /&gt;
  FW_arrowRight = &#039;data:image/svg+xml;utf8,&amp;lt;svg viewBox=&amp;quot;0 0 1792 1792&amp;quot; xmlns=&amp;quot;http://www.w3.org/2000/svg&amp;quot;&amp;gt;&amp;lt;path fill=&amp;quot;gray&amp;quot; d=&amp;quot;M1171 960q0 13-10 23l-466 466q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l393-393-393-393q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l466 466q10 10 10 23z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;&lt;br /&gt;
      .replace(&#039;gray&#039;, col);&lt;br /&gt;
  FW_arrowDown =FW_arrowRight.replace(&#039;/&amp;gt;&#039;,&#039; transform=&amp;quot;rotate(90,896,896)&amp;quot;/&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
  var fnd;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;div#menu table.room&amp;quot;).each(function(){     // one loop per Block&lt;br /&gt;
    var t = this, ma = {};&lt;br /&gt;
    $(t).find(&amp;quot;td &amp;gt; div &amp;gt; a &amp;gt; span&amp;quot;).each(function(e){&lt;br /&gt;
      var span = this, spanTxt = $(span).text().replace(/,/g,&#039;&#039;);&lt;br /&gt;
      var ta = spanTxt.split(&amp;quot;-&amp;gt;&amp;quot;);&lt;br /&gt;
      if(ta.length &amp;lt;= 1)&lt;br /&gt;
        return;&lt;br /&gt;
      fnd = true;&lt;br /&gt;
      var nxt=&amp;quot;&amp;quot;, lst=&amp;quot;&amp;quot;, tr=$(span).closest(&amp;quot;tr&amp;quot;);&lt;br /&gt;
      for(var i1=0; i1&amp;lt;ta.length-1; i1++) {&lt;br /&gt;
        nxt += &amp;quot;-&amp;gt;&amp;quot;+ta[i1];&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;#####HIER PLATZ FUER ICON####&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        ma[nxt] = true;&lt;br /&gt;
        lst = nxt;&lt;br /&gt;
      }&lt;br /&gt;
      $(span).html(ta[ta.length-1]);&lt;br /&gt;
      $(tr).attr(&amp;quot;data-mTree&amp;quot;, nxt)&lt;br /&gt;
           .addClass(&amp;quot;menuTree level&amp;quot;+(ta.length-1));&lt;br /&gt;
    });&lt;br /&gt;
  });&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Patch==&lt;br /&gt;
fhemweb.js (FHEM 6.2 ) Zeile 1157&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);     &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; zwischen &amp;lt;td&amp;gt;&amp;lt;div&amp;gt; und &amp;lt;a href...&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;img src=&#039;/fhem/images/default/ico&amp;quot;+ta[i1]+&amp;quot;.png&#039;&amp;gt;&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht elegant, aber funktioniert solange ein Icon mit dem gleichen Namen wie der uebergeordnete Raum existiert. Hier SYSTEM also icoSYSTEM.png&lt;br /&gt;
&lt;br /&gt;
=(PGM2) MenuBloecke erweitern=&lt;br /&gt;
&lt;br /&gt;
Ich will meine FTUI-Seiten in PGM2 anbinden und nicht nur den Link einfach unten anhaengen (siehe Screenshot).&lt;br /&gt;
FTUI soll in einem eigenen Block stehen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FTUI Link.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Um einen neuen Block zu beginnen ist in der Menuliste ein leerer Eintrag notwendig. Siehe Quellcode in FHEMWEB.pm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
  my @list = (&lt;br /&gt;
     &amp;quot;Everything&amp;quot;,    &amp;quot;$FW_ME?room=all&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,              &amp;quot;&amp;quot;,                                    &amp;lt;--------------------neuer Block&lt;br /&gt;
     &amp;quot;Commandref&amp;quot;,    &amp;quot;$FW_ME/docs/commandref${sfx}.html&amp;quot;,&lt;br /&gt;
     &amp;quot;Remote doc&amp;quot;,    &amp;quot;http://fhem.de/fhem.html#Documentation&amp;quot;,&lt;br /&gt;
     &amp;quot;Edit files&amp;quot;,    &amp;quot;$FW_ME?cmd=style%20list&amp;quot;,&lt;br /&gt;
     &amp;quot;Select style&amp;quot;,  &amp;quot;$FW_ME?cmd=style%20select&amp;quot;,&lt;br /&gt;
     &amp;quot;Event monitor&amp;quot;, &amp;quot;$FW_ME?cmd=style%20eventMonitor&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,           &amp;quot;&amp;quot;);                                      &amp;lt;--------------------neuer Block&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leider ist es mir nicht gelungen in der fhem.conf direkt einen leeren Eintrag zu erzwingen, daher mein Umweg ueber einen Platzhalter (NEWBLOCK)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEB menuEntries fhem.de,http://fhem.de,\&lt;br /&gt;
                        culfw.de,http://culfw.de,\&lt;br /&gt;
                        Icons,http://fhem.clx.local:8083/fhem?cmd=style%20iconFor,\&lt;br /&gt;
                        restart,cmd=shutdown+restart,\&lt;br /&gt;
                        rereadcfg,cmd=rereadcfg,\&lt;br /&gt;
                        rereadicons,cmd=set WEB rereadicons,\&lt;br /&gt;
                        NEWBLOCK,NEWBLOCK,\&lt;br /&gt;
                        FTUI,http://fhem.clx.local:8083/fhem/ftui/index.html&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Patch==&lt;br /&gt;
FHEM/01_FHEMWEB.pm Version 6.2 Zeile 1803 (Nachtrag: In Version 6.3 ist es Zeile 1815)&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot Blockbuilding.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
  my @me = split(&amp;quot;,&amp;quot;, AttrVal($FW_wname, &amp;quot;menuEntries&amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
  push @list, @me, &amp;quot;&amp;quot;, &amp;quot;&amp;quot; if(@me);&lt;br /&gt;
&lt;br /&gt;
===============================snip==================================================&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx++) {&lt;br /&gt;
    if ( $list[$idx] =~ m/NEWBLOCK/ ) {&lt;br /&gt;
      splice @list, $idx, 1,(&amp;quot;&amp;quot;);  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  splice @list, 6, 0, (&amp;quot;&amp;quot;,&amp;quot;&amp;quot;); #  fuegt ein Blockende nach Logfile ein&lt;br /&gt;
===============================snip==================================================&lt;br /&gt;
   &lt;br /&gt;
  my $lastname = &amp;quot;,&amp;quot;; &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; neu in Version 6.2&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx+= 2) {&lt;br /&gt;
    next if($FW_hiddenroom{$list[$idx]} || $list[$idx] eq $lastname);&lt;br /&gt;
    push @list1, $list[$idx];&lt;br /&gt;
    push @list2, $list[$idx+1];&lt;br /&gt;
    $lastname = $list[$idx];&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem Patch einen Restart mit&lt;br /&gt;
 systemctl restart fhem&lt;br /&gt;
durchfuehren.&lt;br /&gt;
&lt;br /&gt;
=Eventlog=&lt;br /&gt;
&lt;br /&gt;
Ich habe das Problem, dass das KM271 Logomatic 2107 Modul einfach zu  geschschwaetzig ist und zudem auch noch ERR-Eintraege produziert, die ich nicht mitloggen moechte.&lt;br /&gt;
&lt;br /&gt;
 2023-12-24 09:24:40 KM271 KM271 ERR_Fehlerspeicher1: Code 01 (+): 09:04Uhr vor 68 Tagen | (-): 195:124Uhr vor 0 Tagen&lt;br /&gt;
&lt;br /&gt;
Derzeit nutze ich das Filelog.&lt;br /&gt;
&lt;br /&gt;
 define Logfile FileLog /var/log/fhem/fhem-%Y-%m.log fakelog&lt;br /&gt;
&lt;br /&gt;
Dabei bin ueber den Eintrag fakelog gestolpert, mit dem ich irgendwie nix mehr anfangen konnte. Habe ich vermutlich mit CopyPaste aus einer Beispiel-Conf uebernommen.&lt;br /&gt;
&lt;br /&gt;
Folgende [https://forum.fhem.de/index.php?topic=95689.0 Seite] brachte Licht in&#039;s Dunkel.&lt;br /&gt;
&lt;br /&gt;
Das ist die Syntax des Logfile Devices.&lt;br /&gt;
&lt;br /&gt;
 define &amp;lt;name&amp;gt; FileLog &amp;lt;filename&amp;gt; &amp;lt;regexp&amp;gt; [readonly] &lt;br /&gt;
&lt;br /&gt;
Daraus kann man ableiten das der Begriff fakelog einen regulaeren Ausdruck darstellt. Irgendjemand hat sich den Begriff mal ausgedacht, um den notwendigen Parameter mit einem Suchfilter zu belegen, der niemals eintritt.&lt;br /&gt;
&lt;br /&gt;
Ein anderer Zeitgenosse hat jetzt genau diesen Begriff als Suchfilter in seiner Wirkung begrenzt.[https://forum.fhem.de/index.php/topic,95351]&lt;br /&gt;
Dies wurde mit dem Update vom 05. Januar 2019 wirksam (Rudi hat im Januar update das controlset disabled; Boeser Rudi)&lt;br /&gt;
&lt;br /&gt;
 Mit dem folgenden kleinen patch werden set, Regexp parts und Create SVG plot für fakelog nicht mehr angezeigt. meiner meinung nach ist nichts davon sinnvoll und verhindert so fehlbedienung. Ich würde gerne ein solches fakelog device dann auch für die anzeige der alexa-fhem logs nutzen statt den anzeige teil doppelt zu implementieren. aber mit den vielen knöpfen ist das eine mögliche fehlerquelle :)&lt;br /&gt;
&lt;br /&gt;
Will man wieder den alten Zustand herstellen, muss man sich nur einen neuen Suchfilter der nie zutrifft ausdenken.&lt;br /&gt;
&lt;br /&gt;
Aber nun zum eigentlichen Problem.&lt;br /&gt;
Ein guter Hinweis ist [https://forum.fhem.de/index.php?topic=51611.0 hier] &lt;br /&gt;
&lt;br /&gt;
 Wenn bestimmte Zeilen nicht in die Logdatei geschrieben werden sollen, ist das Attribut ignoreRegexp hilfreich. Wenn beispielsweise alle Zeilen, die die Zeichenfolge &amp;quot;AbCd&amp;quot; oder &amp;quot;CdEf&amp;quot; enthalten nicht geloggt werden sollen, dann wäre&lt;br /&gt;
 &lt;br /&gt;
 Code Auswählen&lt;br /&gt;
 attr &amp;lt;log-name&amp;gt; ignoreRegexp .*AbCd.*|.*CdEf.*&lt;br /&gt;
 eine Attributdefinition, die das ermöglicht. &lt;br /&gt;
 &lt;br /&gt;
 Dies bezieht sich aber nur auf normale FileLog-Instanzen. Falls Events aus dem globalen FHEM-Logfile ausgeschlossen werden sollen, muss man das Attribut in global angeben. (Zusammenhang siehe #Globale Logdatei) &lt;br /&gt;
  &lt;br /&gt;
 Code Auswählen&lt;br /&gt;
 attr global ignoreRegexp .*AbCd.*|.*CdEf.*&lt;br /&gt;
&lt;br /&gt;
=KNX Toggle=&lt;br /&gt;
Um einen toogle z.B. auf einem Licht hinzubekommen. &lt;br /&gt;
&lt;br /&gt;
 set L_Bar_Auszen g1 toggle&lt;br /&gt;
&lt;br /&gt;
ist es notzwendig das Attribut KNX_TOGGLE zu setzen.&lt;br /&gt;
&lt;br /&gt;
 attr KNX_TOGGLE L_Bar_Auszen:state&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_Stolperfallen&amp;diff=4850</id>
		<title>(FHEM) Stolperfallen</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_Stolperfallen&amp;diff=4850"/>
		<updated>2026-04-11T06:40:07Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Patch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=(PGM2) RoomIcons bei Untermenues=&lt;br /&gt;
Hab mal wieder einen Volltreffer gelandet. Weil die Raeume im FHEMWEB zu viele wurden, aber auch nicht mit &amp;quot;hiddenroom&amp;quot; unterdrueckt werden sollten. Wollte ich mal &#039;Room in Room&#039; aussprobieren.&lt;br /&gt;
Leider scheint das nicht zu funktionieren (Stand fhem 6.0). Aus der Commandref geht eine Alternative hervor. Und zwar, wenn man im Raumnamen ein &#039;-&amp;gt;&#039; verwendet, fuehrt das dazu, dass im FHEM-Roommenue&lt;br /&gt;
aufklappbare Unterraeume (die nach dem -&amp;gt;) angezeigt werden. Man kann also keinen kompletten bestehenden Raum einem anderen Raum zuordnen, sondern man ist gezwungen alle Devices den neuen Raumnamen zuzuordnen.&lt;br /&gt;
Ein unschoener Effekt ist zudem, dass der uebergeordnete Raumname kein Icon mehr hat.&lt;br /&gt;
&lt;br /&gt;
Dies bestatigt diese [https://forum.fhem.de/index.php?topic=91800.0 Forums-Nachfrage].&lt;br /&gt;
&lt;br /&gt;
Ein Blick in den Quellcode von 01_FHEMWEB.pm zeigt:&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;01-FHEMWEB.pm (Auszug)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
 ##########################&lt;br /&gt;
  # Rooms and other links&lt;br /&gt;
  foreach my $r (@FW_roomsArr) {                                     # Das Array mit den Raumnamen wird nacheinander durchlaufen; In der Variablen r steht der aktuell zu pruefende Raumname&lt;br /&gt;
    next if($r eq &amp;quot;hidden&amp;quot; || $FW_hiddenroom{$r});                   # Ist der Raum hidden geh zu naechsten = tue nichts mit diesem Raum&lt;br /&gt;
    $FW_room = AttrVal($FW_wname, &amp;quot;defaultRoom&amp;quot;, $r)&lt;br /&gt;
        if(!$FW_room &amp;amp;&amp;amp; $FW_ss);&lt;br /&gt;
    if(my $devspec = $FW_extraRooms{$r}) {&lt;br /&gt;
      my $r = $r;&lt;br /&gt;
      $r =~ s/&amp;amp;nbsp;/ /g;                                            # Ersetze alle NoBreakSpaces durch Leerzeichen&lt;br /&gt;
      push @list1, FW_htmlEscape($r);&lt;br /&gt;
      push @list2, &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($devspec);               # Da habe ich noch keine Idee zu &lt;br /&gt;
    } else {&lt;br /&gt;
      push @list1, FW_htmlEscape($r);&lt;br /&gt;
      push @list2, &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($r);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  my $sfx = AttrVal(&amp;quot;global&amp;quot;, &amp;quot;language&amp;quot;, &amp;quot;EN&amp;quot;);&lt;br /&gt;
  $sfx = ($sfx eq &amp;quot;EN&amp;quot; ? &amp;quot;&amp;quot; : &amp;quot;_$sfx&amp;quot;);                              # wenn die eingestellte Sprache EN ist , dann ist sfx leer; es wird also z.B: an den Dateinamen commandref nichts angehaengt (sfx steht fuer suffix)&lt;br /&gt;
  my @list = (                                                       # das ist wohl die Standard Ausgangsliste; interassant: Everything gehoert zum Raummenue und der Rest zum (Name?)-Menue darunter&lt;br /&gt;
     &amp;quot;Everything&amp;quot;,    &amp;quot;$FW_ME?room=all&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,              &amp;quot;&amp;quot;,&lt;br /&gt;
     &amp;quot;Commandref&amp;quot;,    &amp;quot;$FW_ME/docs/commandref${sfx}.html&amp;quot;,&lt;br /&gt;
     &amp;quot;Remote doc&amp;quot;,    &amp;quot;http://fhem.de/fhem.html#Documentation&amp;quot;,&lt;br /&gt;
     &amp;quot;Edit files&amp;quot;,    &amp;quot;$FW_ME?cmd=style%20list&amp;quot;,&lt;br /&gt;
     &amp;quot;Select style&amp;quot;,  &amp;quot;$FW_ME?cmd=style%20select&amp;quot;,&lt;br /&gt;
     &amp;quot;Event monitor&amp;quot;, &amp;quot;$FW_ME?cmd=style%20eventMonitor&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,           &amp;quot;&amp;quot;);&lt;br /&gt;
  my $lastname = &amp;quot;,&amp;quot;; # Avoid double &amp;quot;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
  my $lfn = &amp;quot;Logfile&amp;quot;;&lt;br /&gt;
  if($defs{$lfn}) { # Add the current Logfile to the list if defined&lt;br /&gt;
    my @l = FW_fileList($defs{$lfn}{logfile},1);&lt;br /&gt;
    my $fn = pop @l;&lt;br /&gt;
    splice @list, 4,0, (&amp;quot;Logfile&amp;quot;,&lt;br /&gt;
                      &amp;quot;$FW_ME/FileLog_logWrapper?dev=$lfn&amp;amp;type=text&amp;amp;file=$fn&amp;quot;);    # fuege den Logfile-Eintrag vor dem Eintrag Commandref ein (wenn definiert)&lt;br /&gt;
  }&lt;br /&gt;
# die menuEntries sind der dritte Block (Rooms, (Name?)-Menue, menuEntries)&lt;br /&gt;
  my @me = split(&amp;quot;,&amp;quot;, AttrVal($FW_wname, &amp;quot;menuEntries&amp;quot;, &amp;quot;&amp;quot;));       # die menuEntries sind eine komma-separierte Liste von Eintraegen der Art Menuname,Kommando     &lt;br /&gt;
  push @list, @me, &amp;quot;&amp;quot;, &amp;quot;&amp;quot; if(@me);                                  # Die Eintragungen werden angehaengt &amp;quot;&amp;quot;,&amp;quot;&amp;quot; ist jeweils der Block-Abschluss, bzw. Separator&lt;br /&gt;
&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx+= 2) {                        # haenge die Kommandoliste an die Raumliste an&lt;br /&gt;
    next if($FW_hiddenroom{$list[$idx]} || $list[$idx] eq $lastname);&lt;br /&gt;
    push @list1, $list[$idx];                                       # in list1 stehen die Menunamen und in list2 die Aktionen&lt;br /&gt;
    push @list2, $list[$idx+1];&lt;br /&gt;
    $lastname = $list[$idx];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;div id=\&amp;quot;menu\&amp;quot;&amp;gt;&amp;quot;;                                        # hier geht&#039;s dann los mit dem Zusammenbau der HTML-Seite (siehe unten)&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;table&amp;gt;&amp;quot;;&lt;br /&gt;
  if($FW_ss) {  # Make a selection sensitive dropdown list          # $FW_ss also &#039;&#039;&#039;F&#039;&#039;&#039;HEM&#039;&#039;&#039;W&#039;&#039;&#039;EB &#039;&#039;&#039;s&#039;&#039;&#039;ensitive &#039;&#039;&#039;s&#039;&#039;&#039;election auf einem &#039;&#039;&#039;S&#039;&#039;&#039;mall &#039;&#039;&#039;S&#039;&#039;&#039;creen &lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;select OnChange=\&amp;quot;location.href=&amp;quot; .&lt;br /&gt;
                              &amp;quot;this.options[this.selectedIndex].value\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
    foreach(my $idx = 0; $idx &amp;lt; @list1; $idx++) {&lt;br /&gt;
      next if(!$list1[$idx]);&lt;br /&gt;
      my $sel = ($list1[$idx] eq $FW_room ? &amp;quot; selected=\&amp;quot;selected\&amp;quot;&amp;quot;  : &amp;quot;&amp;quot;);&lt;br /&gt;
      FW_pO &amp;quot;&amp;lt;option value=&#039;$list2[$idx]&#039;$sel&amp;gt;$list1[$idx]&amp;lt;/option&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;/select&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
    FW_pO &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
&lt;br /&gt;
    my $tblnr = 1;&lt;br /&gt;
    my $roomEscaped = FW_htmlEscape($FW_room);                   # ersetzte alle (&amp;amp; &amp;lt; &amp;gt; &#039; (\n)) aus dem aktuellen (?) Raumnamen, wird allerdings nie mehr verwendet&lt;br /&gt;
    my $current;&lt;br /&gt;
    $current = &amp;quot;$FW_ME?room=&amp;quot;.urlEncode($FW_room) if($FW_room);&lt;br /&gt;
    $current = &amp;quot;$FW_ME?cmd=&amp;quot;.urlEncode($cmd) if($cmd);&lt;br /&gt;
    foreach(my $idx = 0; $idx &amp;lt; @list1; $idx++) {                # jetzt durchlaufe die gesamte Liste mit allen Menueintraegen und baue die HTML-Seite (roomBlock1-3)&lt;br /&gt;
      my ($l1, $l2) = ($list1[$idx], $list2[$idx]);&lt;br /&gt;
      if(!$l1) {&lt;br /&gt;
        FW_pO &amp;quot;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; if($idx);                     # die Raumbloecke sind eine Tabelle in der Tabelle menu, diese Table-Ende wird nach jedem Block (Eintrag &amp;quot;&amp;quot;,&amp;quot;&amp;quot;) ausgegeben. Am Anfang ist $idx noch 0, dann also nicht.&lt;br /&gt;
        if($idx&amp;lt;int(@list1)-1) {&lt;br /&gt;
          FW_pO &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=\&amp;quot;room roomBlock$tblnr\&amp;quot;&amp;gt;&amp;quot;;   # hier waere es auch moeglich durch einfuegen eines leeren/speziellen Eintrags einen neunen Block zu generieren&lt;br /&gt;
          $tblnr++;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
      } else {                                                    # hier kommt jetzt der aktuell interessante Teil&lt;br /&gt;
        FW_pF &amp;quot;&amp;lt;tr%s&amp;gt;&amp;quot;, ($current &amp;amp;&amp;amp; $current eq $l2) ? &amp;quot; class=\&amp;quot;sel\&amp;quot;&amp;quot; : &amp;quot;&amp;quot;;   # FW_pF ist eine formatierte Ausgabe&lt;br /&gt;
&lt;br /&gt;
        my $class = &amp;quot;menu_$l1&amp;quot;;&lt;br /&gt;
        $class =~ s/[^A-Z0-9]/_/gi;&lt;br /&gt;
&lt;br /&gt;
        # image tag if we have an icon, else empty&lt;br /&gt;
        my $icoName = &amp;quot;ico$l1&amp;quot;;&lt;br /&gt;
        map { my ($n,$v) = split(&amp;quot;:&amp;quot;,$_); $icoName=$v if($l1 =~ m/^$n$/); }    # die roomOcons sind eine Leerzeichen-getrennte Liste von Raumname:IconName $n:$v&lt;br /&gt;
                        split(&amp;quot; &amp;quot;, AttrVal($FW_wname, &amp;quot;roomIcons&amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
        my $icon = FW_iconName($icoName) ?&lt;br /&gt;
                        FW_makeImage($icoName,$icoName,&amp;quot;icon&amp;quot;).&amp;quot;&amp;amp;nbsp;&amp;quot; : &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        if($l1 eq &amp;quot;Save config&amp;quot;) {                                            # Spezialbehandlung des Menueintrags &lt;br /&gt;
          $l1 .= &#039;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt; &#039;.&lt;br /&gt;
                  &#039;&amp;lt;a id=&amp;quot;saveCheck&amp;quot; class=&amp;quot;changed&amp;quot; style=&amp;quot;visibility:&#039;.&lt;br /&gt;
                  (int(@structChangeHist) ? &#039;visible&#039; : &#039;hidden&#039;).&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;?&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        # Force external browser if FHEMWEB is installed as an offline app.&lt;br /&gt;
        my $target = &#039;&#039;;        # Forum 33066, 39854&lt;br /&gt;
        $target = &#039;target=&amp;quot;_blank&amp;quot;&#039; if($l2 =~ s/^$FW_ME\/\+/$FW_ME\//);&lt;br /&gt;
        $target = &#039;target=&amp;quot;_blank&amp;quot;&#039; if($l2 =~ m/commandref|fhem.de.fhem.html/);&lt;br /&gt;
        if($l2 =~ m/.html$/ || $l2 =~ m/^(http|javascript)/ || length($target)){    # wenn das Kommando auf .html endet oder mit http oder javascript beginnt target definiert (siehe die beiden Zeilen vorher) ist, dann mach aus dem Komando einen Link&lt;br /&gt;
           FW_pO &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;$l2&#039; $target&amp;gt;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;quot;.&lt;br /&gt;
                 &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
        } else {&lt;br /&gt;
          FW_pH $l2, &amp;quot;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;quot;, 1, $class;     # ein bisschen merkwuerdig, aber ab im &amp;quot;normalfall&amp;quot; wird alles ab &amp;lt;td&amp;gt; zwischen &amp;lt;/td&amp;gt; durch FW_pH ausgegeben, siehe unten Erlaeuterung&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        FW_pO &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
  FW_pO &amp;quot;&amp;lt;/div&amp;gt;&amp;quot; if($hasMenuScroll);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei mir sieht das Ergebnis dann so aus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;Quellcode fhem.html (Auszug roomBlock1-3)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;menuScrollArea&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?&#039;&amp;quot;&amp;gt;&amp;lt;div id=&amp;quot;logo&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;menu&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_LEUCHTEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=LEUCHTEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoBELEUCHTUNG&#039; src=&amp;quot;/fhem/images/default/icoBELEUCHTUNG.png&amp;quot; alt=&amp;quot;icoBELEUCHTUNG&amp;quot; title=&amp;quot;icoBELEUCHTUNG&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;LEUCHTEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_ROLLLADEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=ROLLLADEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoROLLLADEN&#039; src=&amp;quot;/fhem/images/user_icons/icoROLLLADEN.png&amp;quot; alt=&amp;quot;icoROLLLADEN&amp;quot; title=&amp;quot;icoROLLLADEN&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;ROLLLADEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_AUSSEN&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=AUSSEN&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoAUSSEN&#039; src=&amp;quot;/fhem/images/default/icoAUSSEN.png&amp;quot; alt=&amp;quot;icoAUSSEN&amp;quot; title=&amp;quot;icoAUSSEN&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;AUSSEN&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_KLIMA&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=KLIMA&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoTemp&#039; src=&amp;quot;/fhem/images/default/icoTemp.png&amp;quot; alt=&amp;quot;icoTemp&amp;quot; title=&amp;quot;icoTemp&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;KLIMA&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_HAUST__R&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=HAUST%c3%9cR&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoHouse&#039; src=&amp;quot;/fhem/images/default/icoHouse.png&amp;quot; alt=&amp;quot;icoHouse&amp;quot; title=&amp;quot;icoHouse&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;HAUSTÜR&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_HEIZUNG&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=HEIZUNG&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoHEIZUNG&#039; src=&amp;quot;/fhem/images/default/icoHEIZUNG.png&amp;quot; alt=&amp;quot;icoHEIZUNG&amp;quot; title=&amp;quot;icoHEIZUNG&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;HEIZUNG&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_MULTIMEDIA&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=MULTIMEDIA&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoAUDIOVIDEO&#039; src=&amp;quot;/fhem/images/user_icons/icoAUDIOVIDEO.png&amp;quot; alt=&amp;quot;icoAUDIOVIDEO&amp;quot; title=&amp;quot;icoAUDIOVIDEO&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;MULTIMEDIA&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;sel&amp;quot;&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_Interfaces&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eInterfaces&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;Interfaces&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_LOGO_&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eLOGO%21&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;LOGO!&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Schaltuhren&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=Schaltuhren&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoUhr&#039; src=&amp;quot;/fhem/images/default/icoUhr.png&amp;quot; alt=&amp;quot;icoUhr&amp;quot; title=&amp;quot;icoUhr&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Schaltuhren&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Z__hler&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=Z%c3%a4hler&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoZAEHLER&#039; src=&amp;quot;/fhem/images/user_icons/icoZAEHLER.png&amp;quot; alt=&amp;quot;icoZAEHLER&amp;quot; title=&amp;quot;icoZAEHLER&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Zähler&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Everything&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=all&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoEverything&#039; src=&amp;quot;/fhem/images/default/icoEverything.png&amp;quot; alt=&amp;quot;icoEverything&amp;quot; title=&amp;quot;icoEverything&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Everything&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock2&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Logfile&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem/FileLog_logWrapper?dev=Logfile&amp;amp;type=text&amp;amp;file=fhem-2022-09.log&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Logfile&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;/fhem/docs/commandref.html&#039; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Commandref&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.de/fhem.html#Documentation&#039; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Remote doc&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Edit_files&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20list&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Edit files&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Select_style&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20select&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Select style&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Event_monitor&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=style%20eventMonitor&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;Event monitor&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;table class=&amp;quot;room roomBlock3&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.de&#039; &amp;gt;&amp;lt;span&amp;gt;fhem.de&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://culfw.de&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        culfw.de&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.clx.local:8083/fhem?cmd=style%20iconFor&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        Icons&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________restart&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=shutdown+restart&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        restart&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________rereadcfg&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=rereadcfg&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        rereadcfg&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu__________________________rereadicons&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?cmd=set WEB rereadicons&amp;amp;fwcsrf=csrf_932432427076413&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
                        rereadicons&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;http://fhem.clx.local:8083/fhem/ftui/index.html&#039; &amp;gt;&amp;lt;span&amp;gt;&lt;br /&gt;
			FTUI&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 my $class = &amp;quot;menu_$l1&amp;quot;;&lt;br /&gt;
 $class =~ s/[^A-Z0-9]/_/gi;                   # alle nicht Buchstaben und Ziffern werden durch Unterstrich ersetzt, also auch Umlaute&lt;br /&gt;
 my $icon = FW_iconName($icoName) ? FW_makeImage($icoName,$icoName,&amp;quot;icon&amp;quot;).&amp;quot;&amp;amp;nbsp;&amp;quot; : &amp;quot;&amp;quot;;&lt;br /&gt;
 FW_pH $l2, &amp;quot;$icon&amp;lt;span&amp;gt;$l1&amp;lt;/span&amp;gt;&amp;quot;, 1, $class;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_Everything&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=all&#039;&amp;quot;&amp;gt;&amp;lt;img class=&#039;icon icoEverything&#039; src=&amp;quot;/fhem/images/default/icoEverything.png&amp;quot; alt=&amp;quot;icoEverything&amp;quot; title=&amp;quot;icoEverything&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;span&amp;gt;Everything&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;tr class=&amp;quot;sel&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_Interfaces&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eInterfaces&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;Interfaces&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;menu_SYSTEM__gt_LOGO_&amp;quot;&amp;gt;&amp;lt;a onClick=&amp;quot;location.href=&#039;/fhem?room=SYSTEM%2d%3eLOGO%21&#039;&amp;quot;&amp;gt;&amp;lt;span&amp;gt;SYSTEM-&amp;amp;gt;LOGO!&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FHEM ROOMinROOMIcon.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Man hat also 2 Baustellen. Moechte man ein Icon am uebergoeordneten Menu, muesste man es in die Klasse packen. Bei den Untermenues muss man die Funktion FW_iconName in 01_FHEMWEB.pm patchen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
# check if the icon exists, and if yes, returns its &amp;quot;logical&amp;quot; name;&lt;br /&gt;
sub&lt;br /&gt;
FW_iconName($)                        # der Parameter ist falls $l1 nicht irgendwo vorher im Code manipuliert wurde in meinem Beispiel icoSystem-&amp;gt;Interfaces. Ein Icon mit diesem Namen gibt es nicht!&lt;br /&gt;
{&lt;br /&gt;
  my ($oname)= @_;&lt;br /&gt;
  return undef if(!defined($oname));&lt;br /&gt;
  my $name = $oname;&lt;br /&gt;
  $name =~ s/@.*//;&lt;br /&gt;
  foreach my $pe (@FW_iconDirs) {&lt;br /&gt;
    return $oname if($pe &amp;amp;&amp;amp; $FW_icons{$pe} &amp;amp;&amp;amp; $FW_icons{$pe}{$name});&lt;br /&gt;
  }&lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die Variable &lt;br /&gt;
 $l1 SYSTEM-&amp;amp;gt;Interfaces, bzw. SYSTEM__gt_Interfaces&lt;br /&gt;
wird per Regex getestet.&lt;br /&gt;
&lt;br /&gt;
Wenn man also das Attribut so anlegt, klappt es: &lt;br /&gt;
&lt;br /&gt;
 attr WEB roomIcons ... SYSTEM-.*Interfaces:icoSYSTEM ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FHEM ROOMinROOMIcon 2.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Das Icon vor den &amp;quot;Stammraum&amp;quot; zu platzieren gestaltet sich etwas schwieriger.&lt;br /&gt;
&lt;br /&gt;
Die passende Stelle in fhemweb.js ist zwar schnell ausgemacht, aber einfach ein Image nach dem &amp;lt;td&amp;gt; einfuegen bringt nicht das genwueschte Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:1200px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;fhemweb.js (Auszug)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line&amp;gt;&lt;br /&gt;
function&lt;br /&gt;
FW_treeMenu()&lt;br /&gt;
{&lt;br /&gt;
  var a = $(&amp;quot;a&amp;quot;).get(0);&lt;br /&gt;
  var col = &#039;rgb(39, 135, 38)&#039;;&lt;br /&gt;
  if(window.getComputedStyle &amp;amp;&amp;amp; a)&lt;br /&gt;
    col = getComputedStyle(a,null).getPropertyValue(&#039;color&#039;); &lt;br /&gt;
  FW_arrowRight = &#039;data:image/svg+xml;utf8,&amp;lt;svg viewBox=&amp;quot;0 0 1792 1792&amp;quot; xmlns=&amp;quot;http://www.w3.org/2000/svg&amp;quot;&amp;gt;&amp;lt;path fill=&amp;quot;gray&amp;quot; d=&amp;quot;M1171 960q0 13-10 23l-466 466q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l393-393-393-393q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l466 466q10 10 10 23z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;&lt;br /&gt;
      .replace(&#039;gray&#039;, col);&lt;br /&gt;
  FW_arrowDown =FW_arrowRight.replace(&#039;/&amp;gt;&#039;,&#039; transform=&amp;quot;rotate(90,896,896)&amp;quot;/&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
  var fnd;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;div#menu table.room&amp;quot;).each(function(){     // one loop per Block&lt;br /&gt;
    var t = this, ma = {};&lt;br /&gt;
    $(t).find(&amp;quot;td &amp;gt; div &amp;gt; a &amp;gt; span&amp;quot;).each(function(e){&lt;br /&gt;
      var span = this, spanTxt = $(span).text().replace(/,/g,&#039;&#039;);&lt;br /&gt;
      var ta = spanTxt.split(&amp;quot;-&amp;gt;&amp;quot;);&lt;br /&gt;
      if(ta.length &amp;lt;= 1)&lt;br /&gt;
        return;&lt;br /&gt;
      fnd = true;&lt;br /&gt;
      var nxt=&amp;quot;&amp;quot;, lst=&amp;quot;&amp;quot;, tr=$(span).closest(&amp;quot;tr&amp;quot;);&lt;br /&gt;
      for(var i1=0; i1&amp;lt;ta.length-1; i1++) {&lt;br /&gt;
        nxt += &amp;quot;-&amp;gt;&amp;quot;+ta[i1];&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;#####HIER PLATZ FUER ICON####&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        ma[nxt] = true;&lt;br /&gt;
        lst = nxt;&lt;br /&gt;
      }&lt;br /&gt;
      $(span).html(ta[ta.length-1]);&lt;br /&gt;
      $(tr).attr(&amp;quot;data-mTree&amp;quot;, nxt)&lt;br /&gt;
           .addClass(&amp;quot;menuTree level&amp;quot;+(ta.length-1));&lt;br /&gt;
    });&lt;br /&gt;
  });&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Patch==&lt;br /&gt;
fhemweb.js (FHEM 6.2 ) Zeile 1157&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);     &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; zwischen &amp;lt;td&amp;gt;&amp;lt;div&amp;gt; und &amp;lt;a href...&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
        if(!ma[nxt]) {&lt;br /&gt;
          $(tr).before(&amp;quot;&amp;lt;tr class=&#039;menuTree closed level&amp;quot;+i1+&amp;quot;&#039; &amp;quot;+&lt;br /&gt;
              &amp;quot;data-mTree=&#039;&amp;quot;+lst+&amp;quot;&#039; data-nxt=&#039;&amp;quot;+nxt+&amp;quot;&#039;&amp;gt;&amp;quot;+&lt;br /&gt;
              &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div&amp;gt;&amp;lt;img src=&#039;/fhem/images/default/ico&amp;quot;+ta[i1]+&amp;quot;.png&#039;&amp;gt;&amp;lt;a href=&#039;#&#039;&amp;gt;&amp;quot;+ta[i1]+&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht elegant, aber funktioniert solange ein Icon mit dem gleichen Namen wie der uebergeordnete Raum existiert. Hier SYSTEM also icoSYSTEM.png&lt;br /&gt;
&lt;br /&gt;
=(PGM2) MenuBloecke erweitern=&lt;br /&gt;
&lt;br /&gt;
Ich will meine FTUI-Seiten in PGM2 anbinden und nicht nur den Link einfach unten anhaengen (siehe Screenshot).&lt;br /&gt;
FTUI soll in einem eigenen Block stehen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot FTUI Link.png|mini]]&lt;br /&gt;
&lt;br /&gt;
Um einen neuen Block zu beginnen ist in der Menuliste ein leerer Eintrag notwendig. Siehe Quellcode in FHEMWEB.pm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
  my @list = (&lt;br /&gt;
     &amp;quot;Everything&amp;quot;,    &amp;quot;$FW_ME?room=all&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,              &amp;quot;&amp;quot;,                                    &amp;lt;--------------------neuer Block&lt;br /&gt;
     &amp;quot;Commandref&amp;quot;,    &amp;quot;$FW_ME/docs/commandref${sfx}.html&amp;quot;,&lt;br /&gt;
     &amp;quot;Remote doc&amp;quot;,    &amp;quot;http://fhem.de/fhem.html#Documentation&amp;quot;,&lt;br /&gt;
     &amp;quot;Edit files&amp;quot;,    &amp;quot;$FW_ME?cmd=style%20list&amp;quot;,&lt;br /&gt;
     &amp;quot;Select style&amp;quot;,  &amp;quot;$FW_ME?cmd=style%20select&amp;quot;,&lt;br /&gt;
     &amp;quot;Event monitor&amp;quot;, &amp;quot;$FW_ME?cmd=style%20eventMonitor&amp;quot;,&lt;br /&gt;
     &amp;quot;&amp;quot;,           &amp;quot;&amp;quot;);                                      &amp;lt;--------------------neuer Block&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leider ist es mir nicht gelungen in der fhem.conf direkt einen leeren Eintrag zu erzwingen, daher mein Umweg ueber einen Platzhalter (NEWBLOCK)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr WEB menuEntries fhem.de,http://fhem.de,\&lt;br /&gt;
                        culfw.de,http://culfw.de,\&lt;br /&gt;
                        Icons,http://fhem.clx.local:8083/fhem?cmd=style%20iconFor,\&lt;br /&gt;
                        restart,cmd=shutdown+restart,\&lt;br /&gt;
                        rereadcfg,cmd=rereadcfg,\&lt;br /&gt;
                        rereadicons,cmd=set WEB rereadicons,\&lt;br /&gt;
                        NEWBLOCK,NEWBLOCK,\&lt;br /&gt;
                        FTUI,http://fhem.clx.local:8083/fhem/ftui/index.html&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Patch==&lt;br /&gt;
FHEM/01_FHEMWEB.pm Version 6.2 Zeile 1803 (Nachtrag: In Version 6.3 ist es Zeile 1815)&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot Blockbuilding.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
  my @me = split(&amp;quot;,&amp;quot;, AttrVal($FW_wname, &amp;quot;menuEntries&amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
  push @list, @me, &amp;quot;&amp;quot;, &amp;quot;&amp;quot; if(@me);&lt;br /&gt;
&lt;br /&gt;
===============================snip==================================================&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx++) {&lt;br /&gt;
    if ( $list[$idx] =~ m/NEWBLOCK/ ) {&lt;br /&gt;
      splice @list, $idx, 1,(&amp;quot;&amp;quot;);  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  splice @list, 6, 0, (&amp;quot;&amp;quot;,&amp;quot;&amp;quot;); #  fuegt ein Blockende nach Logfile ein&lt;br /&gt;
===============================snip==================================================&lt;br /&gt;
   &lt;br /&gt;
  my $lastname = &amp;quot;,&amp;quot;; &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; neu in Version 6.2&lt;br /&gt;
  for(my $idx = 0; $idx &amp;lt; @list; $idx+= 2) {&lt;br /&gt;
    next if($FW_hiddenroom{$list[$idx]} || $list[$idx] eq $lastname);&lt;br /&gt;
    push @list1, $list[$idx];&lt;br /&gt;
    push @list2, $list[$idx+1];&lt;br /&gt;
    $lastname = $list[$idx];&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem Patch einen Restart mit&lt;br /&gt;
 systemctl restart fhem&lt;br /&gt;
durchfuehren.&lt;br /&gt;
&lt;br /&gt;
=Eventlog=&lt;br /&gt;
&lt;br /&gt;
Ich habe das Problem, dass das KM271 Logomatic 2107 Modul einfach zu  geschschwaetzig ist und zudem auch noch ERR-Eintraege produziert, die ich nicht mitloggen moechte.&lt;br /&gt;
&lt;br /&gt;
 2023-12-24 09:24:40 KM271 KM271 ERR_Fehlerspeicher1: Code 01 (+): 09:04Uhr vor 68 Tagen | (-): 195:124Uhr vor 0 Tagen&lt;br /&gt;
&lt;br /&gt;
Derzeit nutze ich das Filelog.&lt;br /&gt;
&lt;br /&gt;
 define Logfile FileLog /var/log/fhem/fhem-%Y-%m.log fakelog&lt;br /&gt;
&lt;br /&gt;
Dabei bin ueber den Eintrag fakelog gestolpert, mit dem ich irgendwie nix mehr anfangen konnte. Habe ich vermutlich mit CopyPaste aus einer Beispiel-Conf uebernommen.&lt;br /&gt;
&lt;br /&gt;
Folgende [https://forum.fhem.de/index.php?topic=95689.0 Seite] brachte Licht in&#039;s Dunkel.&lt;br /&gt;
&lt;br /&gt;
Das ist die Syntax des Logfile Devices.&lt;br /&gt;
&lt;br /&gt;
 define &amp;lt;name&amp;gt; FileLog &amp;lt;filename&amp;gt; &amp;lt;regexp&amp;gt; [readonly] &lt;br /&gt;
&lt;br /&gt;
Daraus kann man ableiten das der Begriff fakelog einen regulaeren Ausdruck darstellt. Irgendjemand hat sich den Begriff mal ausgedacht, um den notwendigen Parameter mit einem Suchfilter zu belegen, der niemals eintritt.&lt;br /&gt;
&lt;br /&gt;
An anderer Zeitgenosse hat jetzt genau diesen Begriff als Suchfilter in seiner Wirkung begrenzt.[https://forum.fhem.de/index.php/topic,95351]&lt;br /&gt;
Dies wurde mit dem Update vom 05. Januar 2019 wirksam (Rudi hat im Januar update das controlset disabled; Boeser Rudi)&lt;br /&gt;
&lt;br /&gt;
 Mit dem folgenden kleinen patch werden set, Regexp parts und Create SVG plot für fakelog nicht mehr angezeigt. meiner meinung nach ist nichts davon sinnvoll und verhindert so fehlbedienung. Ich würde gerne ein solches fakelog device dann auch für die anzeige der alexa-fhem logs nutzen statt den anzeige teil doppelt zu implementieren. aber mit den vielen knöpfen ist das eine mögliche fehlerquelle :)&lt;br /&gt;
&lt;br /&gt;
Will man wieder den alten Zustand herstellen, muss man sich nur einen neuen Suchfilter der nie zutrifft ausdenken.&lt;br /&gt;
&lt;br /&gt;
Aber nun zum eigentlichen Problem.&lt;br /&gt;
Ein guter Hinweis ist [https://forum.fhem.de/index.php?topic=51611.0 hier] &lt;br /&gt;
&lt;br /&gt;
 Wenn bestimmte Zeilen nicht in die Logdatei geschrieben werden sollen, ist das Attribut ignoreRegexp hilfreich. Wenn beispielsweise alle Zeilen, die die Zeichenfolge &amp;quot;AbCd&amp;quot; oder &amp;quot;CdEf&amp;quot; enthalten nicht geloggt werden sollen, dann wäre&lt;br /&gt;
 &lt;br /&gt;
 Code Auswählen&lt;br /&gt;
 attr &amp;lt;log-name&amp;gt; ignoreRegexp .*AbCd.*|.*CdEf.*&lt;br /&gt;
 eine Attributdefinition, die das ermöglicht. &lt;br /&gt;
 &lt;br /&gt;
 Dies bezieht sich aber nur auf normale FileLog-Instanzen. Falls Events aus dem globalen FHEM-Logfile ausgeschlossen werden sollen, muss man das Attribut in global angeben. (Zusammenhang siehe #Globale Logdatei) &lt;br /&gt;
  &lt;br /&gt;
 Code Auswählen&lt;br /&gt;
 attr global ignoreRegexp .*AbCd.*|.*CdEf.*&lt;br /&gt;
&lt;br /&gt;
=KNX Toggle=&lt;br /&gt;
Um einen toogle z.B. auf einem Licht hinzubekommen. &lt;br /&gt;
&lt;br /&gt;
 set L_Bar_Auszen g1 toggle&lt;br /&gt;
&lt;br /&gt;
ist es notzwendig das Attribut KNX_TOGGLE zu setzen.&lt;br /&gt;
&lt;br /&gt;
 attr KNX_TOGGLE L_Bar_Auszen:state&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4849</id>
		<title>(FHEM) FTUI 3</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(FHEM)_FTUI_3&amp;diff=4849"/>
		<updated>2026-04-11T06:14:34Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Seit 2000 (Beginn noch unbestaetigt) gibt es eine neue Variante &#039;FHEM Tablet User Interface Version 3&#039;, oder kurz FTUI3 genannt.&lt;br /&gt;
&lt;br /&gt;
Der Entwickler hat seine bisherige Arbeit bei [https://github.com/knowthelist/ftui Github] eingestellt. Es gibt keine offizelle Release, also alles noch &#039;Under Construction&#039;&lt;br /&gt;
&lt;br /&gt;
 UI builder framework for FHEM — http://fhem.de/fhem.html with a clear intention: Keep it short and simple!&lt;br /&gt;
&lt;br /&gt;
 FTUI 3 uses Web Components technologies in pure ES2020 javascript.&lt;br /&gt;
&lt;br /&gt;
Diese zwei Aussagen und mein Eindruck, dass die Version 2 doch recht sperrig ist, haben mich dazu bewogen es mit FTIU3 mal zu probieren.&lt;br /&gt;
&lt;br /&gt;
Im [https://forum.fhem.de/index.php?action=search2 FHEM-Forum] gibt es 118 Beitraege (Stand082022) zum Thema FTUI3&lt;br /&gt;
&lt;br /&gt;
=Custom HTML Tags=&lt;br /&gt;
&lt;br /&gt;
[https://matthewjamestaylor.com/custom-tags Einstieg]&lt;br /&gt;
&lt;br /&gt;
Das ganze Framework arbeitet mit eigenen HTML-Tags die alle mit &amp;lt;ftui-...&amp;gt; eingeleitet werden.&lt;br /&gt;
&lt;br /&gt;
Mit meinem fing-Befehl&lt;br /&gt;
&lt;br /&gt;
 alias fing=&#039;function _fing(){ find &amp;quot;$1&amp;quot; -type f -exec grep -l &amp;quot;$2&amp;quot; {} \; ; }; _fing&#039;&lt;br /&gt;
&lt;br /&gt;
kann ich relativ schnell herausfinden wo das custom-tag ueberall drinsteckt. z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fhem:/opt/fhem/www/ftui # fing /opt/fhem/www/ftui &amp;quot;&amp;lt;ftui-button&amp;quot;&lt;br /&gt;
/opt/fhem/www/ftui/index.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/meter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/colors.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/circlemenu.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/badge.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/swiper.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/binding.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/button-nice.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/tab.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/speak.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/icon.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/popup.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/themes.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/grid.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/content-view2.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-shutter.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/contents/mobile-view-sonos.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/mobile_full.html&lt;br /&gt;
/opt/fhem/www/ftui/examples/label.html&lt;br /&gt;
/opt/fhem/www/ftui/components/button/button-nice.component.js&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neben den 3 Dateien&lt;br /&gt;
 ftui.js&lt;br /&gt;
 ftui.css&lt;br /&gt;
 &lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
gibt es noch 5 Verzeichnisse&lt;br /&gt;
 components&lt;br /&gt;
 modules&lt;br /&gt;
&lt;br /&gt;
 themes&lt;br /&gt;
 icons&lt;br /&gt;
&lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
Das Verzeihnis components enthaelt die Definitionen der Custom-Tags.&lt;br /&gt;
Modules weisz ich noch nicht.&lt;br /&gt;
&lt;br /&gt;
Themes und icons enthalten das was man erwartet, bis auf die Datei demo.html. Mit deren Hilfe werden alle Icons angezeigt.&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
=Inhalt=&lt;br /&gt;
Neben den 3 Dateien &lt;br /&gt;
 ftui.css&lt;br /&gt;
 ftui.js&lt;br /&gt;
 index.html&lt;br /&gt;
&lt;br /&gt;
verteilt sich der Rest auf 5 Verzeichnisse&lt;br /&gt;
 compnents&lt;br /&gt;
 modules&lt;br /&gt;
 &lt;br /&gt;
 icons&lt;br /&gt;
 themes&lt;br /&gt;
 &lt;br /&gt;
 examples&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In components befinden sich alle Kompnenten die per Tag benutzt werden koennen.&lt;br /&gt;
Die Bedeutung von modules ist mir noch unklar.&lt;br /&gt;
&lt;br /&gt;
In themes und icons steckt genau das drin was man vermutet, auszer in icons gibt es noch eine demo.html&lt;br /&gt;
&lt;br /&gt;
 http://fhem.clx.local:8083/fhem/ftui/icons/demo.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui.js==&lt;br /&gt;
Mit 1326 Bytes ja richtig bescheiden.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Tag-List===&lt;br /&gt;
&lt;br /&gt;
Die eckigen [] und runden () Klammern um die Attribute sind die Kurzformen von set-value und get-value.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-label get-color=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt;   wird dann &amp;lt;ftui-label [color]=&amp;quot;dummy1:color&amp;quot;&amp;gt;demo&amp;lt;/ftui-label&amp;gt; wenn die Farbe aus dem FHEM-Device dummy1 das Reading color uebernommen werden soll.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button set-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button (value)=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wenn der (?) Wert des FHEM-Devices dummy1 gesetzt werden soll.&lt;br /&gt;
&lt;br /&gt;
Es gehen auch Hin- und Rueckweg kombiniert.&lt;br /&gt;
&lt;br /&gt;
 Aus &amp;lt;ftui-button getset-value=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; wird dann &amp;lt;ftui-button [(value)]=&amp;quot;dummy1&amp;quot;&amp;gt;&amp;lt;/ftui-button&amp;gt; In dem Beispiel aus der Doku kann ich aber nicht mehr die Funktion erkennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Local Bindung und Events schiebe ich mal nach hinten.&lt;br /&gt;
&lt;br /&gt;
Pipes sind recht hilfreich wenn der Wert aus einem FHEM-Device-Reading und der Wert fuer das FTUI-Attribut nicht kompatibel sind. Dann kann man durch die Pipes eine Anpassung realisieren.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    part(number)&lt;br /&gt;
    toDate(string)&lt;br /&gt;
    toBool(string|number)&lt;br /&gt;
    toInt(number)&lt;br /&gt;
    format(string)&lt;br /&gt;
    round(number)&lt;br /&gt;
    add(number)&lt;br /&gt;
    multiply(number)&lt;br /&gt;
    divide(number)&lt;br /&gt;
    replace(find, replace)&lt;br /&gt;
    map(&#039;in1:out1,in2:out2,...&#039;)&lt;br /&gt;
    step(&#039;1:ok,6:warning,10:alert&#039;)&lt;br /&gt;
    scale(minIn, maxIn, minOut, maxOut)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Formatierung/Layout====&lt;br /&gt;
&lt;br /&gt;
Alignment and margins can be changed by the attributes&lt;br /&gt;
&lt;br /&gt;
    align-item (top, bottom, left, right, center, around, stretch)&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Tab      &amp;lt;ftui-tab view=&amp;quot;View1&amp;quot; title=&amp;quot;Home&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab fill=&amp;quot;clear&amp;quot; direction=&amp;quot;vertical&amp;quot; view=&amp;quot;View1&amp;quot; title=&amp;quot;Tab1&amp;quot; active&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-tab-view id=&amp;quot;View1&amp;quot; [hidden]=&amp;quot;ftuitest | isNot(&#039;1&#039;)&amp;quot;&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-tab-title text-align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/ftui-tab-title&amp;gt; &lt;br /&gt;
             &amp;lt;ftui-segment [(value)]=&amp;quot;ftuitest&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Grid     &amp;lt;ftui-grid base-width=&amp;quot;150&amp;quot; base-height=&amp;quot;120&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid base-width=&amp;quot;75&amp;quot; base-height=&amp;quot;75&amp;quot; shape=&amp;quot;round&amp;quot; margin=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;4&amp;quot; color=&amp;quot;translucent&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-tile row=&amp;quot;3&amp;quot; col=&amp;quot;3&amp;quot; height=&amp;quot;1000&amp;quot; width=&amp;quot;1&amp;quot; color=&amp;quot;red&amp;quot;&amp;gt;&lt;br /&gt;
             &amp;lt;ftui-grid-header&amp;gt;ROUND&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Circlemenu &amp;lt;ftui-circlemenu circle-radius=&amp;quot;4&amp;quot; keep-open&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-circlemenu keep-open direction=&amp;quot;horizontal-right&amp;quot; opacity=&amp;quot;0.2&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Row &amp;lt;ftui-row align-items=&amp;quot;center&amp;quot; class=&amp;quot;size-3&amp;quot;&amp;gt;&amp;lt;ftui-row align-items=&amp;quot;top&amp;quot; margin=&amp;quot;1&amp;quot; width=&amp;quot;80%&amp;quot; height=&amp;quot;33%&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row color=&amp;quot;ok-warning-alert&amp;quot;&amp;gt;ok-warning-alert&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row slot=&amp;quot;end&amp;quot; align-items=&amp;quot;stretch&amp;quot;&amp;gt;&amp;lt;ftui-row color=&amp;quot;red&amp;quot;&amp;gt;red&amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Column  &amp;lt;ftui-column width=&amp;quot;10%&amp;quot; height=&amp;quot;70%&amp;quot;&amp;gt;&amp;lt;span&amp;gt;R&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;G&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;B&amp;lt;/span&amp;gt;&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-column shape=&amp;quot;round&amp;quot; color=&amp;quot;light&amp;quot;&amp;gt;light&amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
            class=&amp;quot;big&amp;quot;  align-items=&amp;quot;top|left|right|stretch|around|dark&amp;quot;  class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   Cell  &amp;lt;ftui-cell class=&amp;quot;size-7&amp;quot; style=&amp;quot;height: 1250px&amp;quot;&amp;gt;Tab2&amp;lt;/ftui-cell&amp;gt; &amp;lt;ftui-cell slot=&amp;quot;end&amp;quot; width=&amp;quot;70px&amp;quot;&amp;gt;&amp;lt;ftui-cell align-items=&amp;quot;left&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    View&amp;lt;ftui-view-toolbar slot=&amp;quot;header&amp;quot;&amp;gt;    &amp;lt;ftui-view id=&amp;quot;view-1&amp;quot;&amp;gt; &amp;lt;ftui-view-sheet shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     ViewStage &amp;lt;ftui-view-stage&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
ViewSection &amp;lt;ftui-view-section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ViewItem  &amp;lt;ftui-view-item target=&amp;quot;details-1&amp;quot;&amp;gt;Item 2-1&amp;lt;/ftui-view-item&amp;gt;   wird haeufig mit FHEM-Devices kombiniert: Decke, Wand, Desktop, Fenster, ...&lt;br /&gt;
&lt;br /&gt;
    Swiper &amp;lt;ftui-swiper scrollbar auto-play interval=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper3&amp;quot; dots auto-play interval=&amp;quot;3&amp;quot;&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper2&amp;quot; [(value)]=local:swiper2 dots&amp;gt; &amp;lt;ftui-swiper id=&amp;quot;swiper1&amp;quot; scrollbar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Design====&lt;br /&gt;
Die sogenannten &#039;fixed theme colors&#039; die zur Verfuegung stehen, koennen [https://knowthelist.github.io/ftui/www/ftui/examples/colors.html hier] betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
====Komponenten/Elemente====&lt;br /&gt;
&lt;br /&gt;
    [https://github.com/knowthelist/ftui#label Label]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Icon Icon](x)&lt;br /&gt;
    [https://github.com/knowthelist/ftui#button Button]x &lt;br /&gt;
    [https://github.com/knowthelist/ftui#SegmentedButton SegmentedButton]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Knob Knob]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Slider Slider]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Checkbox Checkbox]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Weather Weather]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Dropdown Dropdown]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Colorpicker Colorpicker]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Image Image]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Badge Badge]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Clock Clock]&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Chart Chart]x&lt;br /&gt;
    [https://github.com/knowthelist/ftui#Medialist Medialist]&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-slider margin=&amp;quot;-34px 0 0 0&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-button (value)=&amp;quot;ftuitest&amp;quot; states=&amp;quot;0,4,10,45,75,90,100&amp;quot;&amp;gt;Try&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-icon margin=&amp;quot;-10px 0 0 0&amp;quot; name=&amp;quot;circle&amp;quot; class=&amp;quot;size-0&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-dropdown nochevron class=&amp;quot;size-3&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-meter value=&amp;quot;50&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-input [(value)]=&amp;quot;ftuitest&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot; type=&amp;quot;number&amp;quot; size=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/ftui-input&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-departure [list]=&amp;quot;depDummy:HBF&amp;quot; icon=&amp;quot;bus&amp;quot;  getinterval=&amp;quot;120&amp;quot; station=&amp;quot;Default&amp;quot; alternate&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-segment-button value=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart title=&amp;quot;demo-chart&amp;quot; y-label=&amp;quot;Heizung&amp;quot; y1-label=&amp;quot;Außen&amp;quot; y-unit=&amp;quot;°C&amp;quot; y1-unit=&amp;quot;°C&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-chart-data label=&amp;quot;Außen&amp;quot; color=&amp;quot;warning&amp;quot; log=&amp;quot;FileLog_AgroWeather&amp;quot; spec=&amp;quot;4:AgroWeather.temperature&amp;quot;&lt;br /&gt;
 &amp;lt;ftui-chart-controls units=&amp;quot;day, week&amp;quot;&amp;gt;&amp;lt;/ftui-chart-controls&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Miscellaneous====&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knowthelist/ftui#speak Speak]x&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&lt;br /&gt;
Alle Elemente verfuegen ueber diese Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    hidden&lt;br /&gt;
    disabled&lt;br /&gt;
    readonly&lt;br /&gt;
    margin&lt;br /&gt;
    padding&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
Fangen wir mal klein an. Zu Beginn erst einmal das Grundgeruest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:600px; overflow:auto;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;FTUI Grundgeruest&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;!--&lt;br /&gt;
    /* FHEM tablet ui - FTUI */&lt;br /&gt;
    /**&lt;br /&gt;
    * UI builder framework for FHEM&lt;br /&gt;
    *&lt;br /&gt;
    * Version: 3.0.0&lt;br /&gt;
    *&lt;br /&gt;
    * Copyright (c) 2015-2021 Mario Stephan &amp;lt;mstephan@shared-files.de&amp;gt;&lt;br /&gt;
    * Under MIT License (http://www.opensource.org/licenses/mit-license.php)&lt;br /&gt;
    * https://github.com/knowthelist/ftui&lt;br /&gt;
    */&lt;br /&gt;
    --&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;ftui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link href=&amp;quot;ftui.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;themes/ftui-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;favicon.ico&amp;quot; rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- avoid 300ms delay on click--&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast_position&amp;quot; content=&amp;quot;topLeft&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- verbose level 0-4 --&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;debug&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;title&amp;gt;FTUI Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Testen habe ich jetzt umeinander die Code-Bloecke im &amp;lt;body&amp;gt; einkopiert.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Um die Toast-Meldungen ab-/anzuschalten folgendes einfuegen&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das passiert nix, auszer dass DummyFTUI auf dem Bildschirm steht, aber kein Button nix.&lt;br /&gt;
Mal einen View drumbauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;ftui-view id=&amp;quot;View1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-button (value)=&amp;quot;set dummyFTUI off&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;angle-up&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;DummyFTUI&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-view&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen war oft auszenherum eine Grid.&lt;br /&gt;
&lt;br /&gt;
Immer noch nix.&lt;br /&gt;
&lt;br /&gt;
Ganz anderer Fehler!&lt;br /&gt;
&lt;br /&gt;
Ich habe mein Beispiel in Verzeichnis examples abgelegt und folgenden Eintrag vergessen.&lt;br /&gt;
   &amp;lt;base href=&amp;quot;../&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. erst ma muss man den Pfad zum FTUI-root Verzeichnis richtig einstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;window-shutter&amp;quot; class=&amp;quot;size-4&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button @click=&amp;quot;sendFhem(&#039;{setJalousieNight()}&#039;)&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot;&lt;br /&gt;
                       margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;angle-down&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-label&amp;gt;Nacht&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sieht man einen Button ueber die gesamte Bildschirmbreite wobei man den Pfeil nach unten und das Label Nacht bedienen kann und auch eine Aktion ausloest. Das Rollladen-Ikon ist nicht zum clicken.&lt;br /&gt;
Es liegt auch auszerhalb des Button-Tags.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Bei der Vergabe von eigenen ID-Namen immer alles in/nur Kleinbuchstaben und maximal einen Unterstrich.&lt;br /&gt;
&lt;br /&gt;
So jetzt geht&#039;s an Grid, um Einfluss auf Position und Groesze der Komponenten nehmen zu koennen.&lt;br /&gt;
&lt;br /&gt;
https://www.ionos.de/digitalguide/websites/webseiten-erstellen/css-grid/ &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; cols und rows gibt es auch nicht mehr beim Grid, nur noch base-width und base-height - mit jeweils 140 als default. « Letzte Änderung: 07 Februar 2022, 19:47:25 von [https://forum.fhem.de/index.php?topic=115259.2130 setstate] »&lt;br /&gt;
&lt;br /&gt;
Das konnte ich aber so nicht direkt finden. Im Code (grid.component.js) steht im Konstruktor:&lt;br /&gt;
&lt;br /&gt;
  constructor() {&lt;br /&gt;
    const properties = {&lt;br /&gt;
      minX: 0,&lt;br /&gt;
      minY: 0,&lt;br /&gt;
      baseWidth: 0,&lt;br /&gt;
      baseHeight: 0,&lt;br /&gt;
      margin: 8,&lt;br /&gt;
      resize: false,&lt;br /&gt;
      responsive: false,&lt;br /&gt;
    }; &lt;br /&gt;
&lt;br /&gt;
Weiter unten folgt dann in Kombination mit dem Attribut &#039;responsive&#039;:&lt;br /&gt;
     :host([responsive]) {&lt;br /&gt;
        display: grid;&lt;br /&gt;
        grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-template-rows: repeat(auto-fill, minmax(140px, 1fr));&lt;br /&gt;
        grid-auto-flow: dense;&lt;br /&gt;
        grid-auto-columns: 25%;&lt;br /&gt;
        grid-auto-rows: 25%;&lt;br /&gt;
        gap: ${this.margin}px;&lt;br /&gt;
        margin: ${this.margin}px;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Die Attributnamen werden im Code von der &#039;Bindestrichschreibweise&#039; in die &#039;CamelCase-Schreibweise&#039; gewandelt. Bsp (s.o.): base-width -&amp;gt; baseWidth&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle gebe ich mal nur mein Testcode an. Alle Kombinationen der Einstellungen von base-height, base-width, margin, row, col, height und width anzugeben ist natuerlich nicht moeglich. Jeder sollte man selbst rumspielen.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;2&amp;quot; col=&amp;quot;2&amp;quot; height=&amp;quot;2&amp;quot; width=&amp;quot;2&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-3&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;10&amp;quot; base-height=&amp;quot;10&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;31&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;10&amp;quot; width=&amp;quot;10&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-grid-tile row=&amp;quot;41&amp;quot; col=&amp;quot;5&amp;quot; height=&amp;quot;20&amp;quot; width=&amp;quot;20&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;dummy.state | map(&#039;on:warning, off:ok&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set dummy&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon name=&amp;quot;lightbulb-o&amp;quot; class=&amp;quot;size-5&amp;quot; height=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy2&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ftui-grid&amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Fuer row und col sind sogar 0 und negative Werte moeglich. Mit z.B. margin=0 rutscht der Button aus dem Fenster. Die kleinste sinnvolle Groesze ist 1.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Mit base-height und base-width kann man gemeinsam mit height und width die Groesze des Button und damit das Verhaeltnis zum Icon /Label beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Mal zum Rechnen von row und col wird 1 abgezogen und dann mit base multipliziert. height und width werden direkt mit der base multipliziert und ergeben die Groesze des Buttons. Ein Button 100:1 hat die gleiche Groesze wie ein 10:10.&lt;br /&gt;
Zwei Grids auf einer Seite sind keine gute Idee, dann muss man noch mehr rechnen (s.o.).&lt;br /&gt;
Fuer den Einstirg reicht mir das das beim Layout. Jetzt machen wir mal noch Funktion dazu.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;ftui-grid base-width=&amp;quot;100&amp;quot; base-height=&amp;quot;100&amp;quot; margin=&amp;quot;0&amp;quot; shape=&amp;quot;round&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; height=&amp;quot;1&amp;quot; width=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot;&lt;br /&gt;
                      [color]=&amp;quot;test | map(&#039;on:red, off:orange&#039;)&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-button (value)=&amp;quot;set test&amp;quot; direction=&amp;quot;vertical&amp;quot; color=&amp;quot;current&amp;quot; fill=&amp;quot;none&amp;quot; margin=&amp;quot;-1em&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-icon [name]=&amp;quot;test | map(&#039;on:lightbulb-on, off:lightbulb&#039;)&amp;quot; class=&amp;quot;size-1&amp;quot; height=&amp;quot;100%&amp;quot; [color]=&amp;quot;test | map(&#039;on:yellow, off:primary&#039;)&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-label&amp;gt;Dummy1&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
     &amp;lt;ftui-content [content]=&amp;quot;test:state&amp;quot;&amp;gt;&amp;lt;/ftui-content&amp;gt;&lt;br /&gt;
         &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;span style=&amp;quot;color:#FFFF00;background:green&amp;quot;&amp;gt;HINWEIS:&amp;lt;/span&amp;gt; Das state-Reading darf nicht angegeben werden. Siehe Beispiel oben. Weder beim set () noch beim get [] ist ein test.state angegeben.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel waechst weiter und die Anpassung meiner FHEM-Instanz an meine Tablets funktioniert praechtig.&lt;br /&gt;
Jetzt besteht der Wunsch nach passenden Icons. Mit&lt;br /&gt;
&lt;br /&gt;
 fing /opt/fhem/www/ftui &amp;quot;icons/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
dass in den Beipielen Repositpries von &#039;bas&#039; und &#039;kleinklima&#039; genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Bas ist [https://basmilius.github.io/weather-icons/ unter] zu finden und mit&lt;br /&gt;
&lt;br /&gt;
 wget -r -A svg -nd  -nH -l 1 https://github.com/basmilius/weather-icons/tree/dev/production/fill/svg&lt;br /&gt;
&lt;br /&gt;
habe ich mir mal die svg in das Verzeichnis ftui/icons/wether/basmilius geholt. Wer will kann noch einen Softlink im Verzeichnis ftui/incons/weather mit dem Namen bas darauf legen.&lt;br /&gt;
&lt;br /&gt;
 ln -s basmilius bas&lt;br /&gt;
&lt;br /&gt;
Es ist nicht schwer einzelne passende Icons zu finden, aber ein Paket herunterzuladen spart halt doch Zeit. Fundstellen bitte gerne in die Kommentare.&lt;br /&gt;
&lt;br /&gt;
Kleiner Ausflug in mein KNX.&lt;br /&gt;
&lt;br /&gt;
FHEM unterstuetzt nur fogende Datentypen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dpt1&lt;br /&gt;
dpt1.000&lt;br /&gt;
dpt1.001&lt;br /&gt;
dpt1.002&lt;br /&gt;
dpt1.003&lt;br /&gt;
dpt1.004&lt;br /&gt;
dpt1.005&lt;br /&gt;
dpt1.006&lt;br /&gt;
dpt1.007&lt;br /&gt;
dpt1.008&lt;br /&gt;
dpt1.009&lt;br /&gt;
dpt1.010&lt;br /&gt;
dpt1.011&lt;br /&gt;
dpt1.012&lt;br /&gt;
dpt1.013&lt;br /&gt;
dpt1.014&lt;br /&gt;
dpt1.015&lt;br /&gt;
dpt1.016&lt;br /&gt;
dpt1.017&lt;br /&gt;
dpt1.018&lt;br /&gt;
dpt1.019&lt;br /&gt;
dpt1.021&lt;br /&gt;
dpt1.022&lt;br /&gt;
dpt1.023&lt;br /&gt;
dpt2&lt;br /&gt;
dpt232     ???&lt;br /&gt;
dpt3&lt;br /&gt;
dpt3.007&lt;br /&gt;
dpt5&lt;br /&gt;
dpt5.001&lt;br /&gt;
dpt5.003&lt;br /&gt;
dpt5.004&lt;br /&gt;
dpt6&lt;br /&gt;
dpt6.001&lt;br /&gt;
dpt7&lt;br /&gt;
dpt7.001&lt;br /&gt;
dpt7.005&lt;br /&gt;
dpt7.006&lt;br /&gt;
dpt7.007&lt;br /&gt;
dpt7.012&lt;br /&gt;
dpt7.013&lt;br /&gt;
dpt8&lt;br /&gt;
dpt8.005&lt;br /&gt;
dpt8.010&lt;br /&gt;
dpt8.011&lt;br /&gt;
dpt9&lt;br /&gt;
dpt9.001&lt;br /&gt;
dpt9.004&lt;br /&gt;
dpt9.005&lt;br /&gt;
dpt9.006&lt;br /&gt;
dpt9.007&lt;br /&gt;
dpt9.008&lt;br /&gt;
dpt9.009&lt;br /&gt;
dpt9.010&lt;br /&gt;
dpt9.020&lt;br /&gt;
dpt9.021&lt;br /&gt;
dpt9.024&lt;br /&gt;
dpt9.025&lt;br /&gt;
dpt9.026&lt;br /&gt;
dpt9.028&lt;br /&gt;
dpt10&lt;br /&gt;
dpt11&lt;br /&gt;
dpt12&lt;br /&gt;
dpt13&lt;br /&gt;
dpt13.010&lt;br /&gt;
dpt13.013&lt;br /&gt;
dpt14&lt;br /&gt;
dpt14.019&lt;br /&gt;
dpt14.027&lt;br /&gt;
dpt14.033&lt;br /&gt;
dpt14.056&lt;br /&gt;
dpt14.057&lt;br /&gt;
dpt14.068&lt;br /&gt;
dpt14.076&lt;br /&gt;
dpt16&lt;br /&gt;
dpt16.000&lt;br /&gt;
dpt16.001&lt;br /&gt;
dpt17.001&lt;br /&gt;
dpt18.001&lt;br /&gt;
dpt19&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;lt;span id=&amp;quot;RFC&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;Request for Comments=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;comments /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4848</id>
		<title>Tibber Pulse (API)</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4848"/>
		<updated>2026-04-10T06:36:03Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* JavaScript */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Da ich aktuell vom Energieversorger einen zweiten Hausanschluss (40kW :-() gelegt bekommen habe und ich für die Wallboxen zur Foerderung einen 100% Oekostrom haben musste, habe ich kurzerhand einen Vertrag mit Tibber (mal zum Testen) abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
=Installation=&lt;br /&gt;
Pulse IR und Bridge&lt;br /&gt;
[[Datei:Tibber Pulse IR und Bridge.png|mini|Tibber Pulse IR und Bridge]]&lt;br /&gt;
Eigentlich verlief die Installation problemlos. Fuer mich war allerdings der Assistent in der Tibber-App zu kindisch. Das ist eher was für Apple-User. Ich haette mir an der ein oder anderen Stelle mehr Hardcore-Infos gewuenscht. Da der mehrstufige Prozess (WLAN-Bridge-Pulse-Zaehler) doch einige Solperfallen enthaelt, ist es fuer mich eher frustrierend jedesmal bei einem Fehler zurueck auf Los geschickt zu werden. Das geht definitiv besser. Ist aber wieder ein Beispiel, dass die Informatiker-Jobs heute nur noch durch 5-jaehrige Schimpansen (ungelernte ;-)) besetzt werden.&lt;br /&gt;
&lt;br /&gt;
Bei mir gab es letztendlich nur einen gravierenden Fehler. Von den mitgelieferten Akkus des Pulse war einer defekt. Bei mir sind das zwei AA-Zellen auf LiBasis! Ein Nachladen mit einem Lithium-1.5V-Lader hat keinen Erfolg gebracht. Erst der Austausch gegen 2 neue Zellen war erfolgreich.&lt;br /&gt;
&lt;br /&gt;
=App=&lt;br /&gt;
[[Datei:TibberAppIcon.png|mini|TibberAppIcon]]&lt;br /&gt;
Die App find ich ziemlich verquer. Sobald Rechnungsanschrift und Installationsorte unterschiedlich sind, geht das Chaos los. Auch der Support tut sich dann schwer. Ich traue mich gar nicht so richtig weitere Vertraege abzuschlieszen.&lt;br /&gt;
Alles ueber die App machen zu wollen/muessen ist ... Das gehoert fuer mich ins WebUserPortal.&lt;br /&gt;
&lt;br /&gt;
Was echt nervt ist, dass sich gefühlt jeden Monat das LookandFeel aendert. Schon die dritte oder vierte Designaenderung des Homescreens (Zuhause).&lt;br /&gt;
&lt;br /&gt;
=API=&lt;br /&gt;
Da die App eher suboptimal ist, habe ich recht zuegig damit angefangen mir die notwendigen Infos ueber die API auf eine Infoseite im Intranet zu holen. Ein Integration in FHEM und/oder HA steht noch aus.&lt;br /&gt;
&lt;br /&gt;
=Erste Schritte=&lt;br /&gt;
&lt;br /&gt;
Uebersicht [https://developer.tibber.com/docs/overview]&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung: Die Plattform wird über GraphQL verfügbar gemacht, eine Technologie, die von Facebook konzipiert und entwickelt wurde.&lt;br /&gt;
&lt;br /&gt;
==Was ist GraphQL?==&lt;br /&gt;
&lt;br /&gt;
[https://de.wikipedia.org/wiki/GraphQL Wikipedia]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/graphql GraphQL]&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zu GraphQL===&lt;br /&gt;
&lt;br /&gt;
GraphQL ist eine von Facebook entwickelte Abfragesprache und Laufzeitumgebung für APIs (Application Programming Interfaces). Im Gegensatz zu traditionellen RESTful APIs, bei denen die Client-Anwendung verschiedene Endpunkte aufruft, um Daten abzurufen oder zu manipulieren, ermöglicht GraphQL dem Client, genau die Daten abzurufen, die er benötigt, indem er eine einzige Abfrage an den Server sendet.&lt;br /&gt;
&lt;br /&gt;
Hier sind einige Hauptmerkmale von GraphQL:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;Declarative Datenabfrage&#039;&#039;&#039;: Der Client spezifiziert genau, welche Daten er vom Server erhalten möchte, indem er eine GraphQL-Abfrage definiert, die die benötigten Felder und deren Struktur beschreibt. Dadurch kann der Client genau die Daten erhalten, die er benötigt, ohne Overfetching oder Underfetching.&lt;br /&gt;
    &#039;&#039;&#039;Typsystem&#039;&#039;&#039;: GraphQL definiert ein Typsystem, das die Struktur und den Typ der Daten beschreibt, die von der API zurückgegeben werden können. Dies ermöglicht eine strikte Typisierung und verbesserte Dokumentation.&lt;br /&gt;
    &#039;&#039;&#039;Single-Endpoint-Architektur&#039;&#039;&#039;: Im Gegensatz zu RESTful APIs, die oft mehrere Endpunkte für verschiedene Ressourcentypen haben, hat eine GraphQL-API normalerweise nur einen Endpunkt, über den alle Abfragen und Mutationen gesendet werden.&lt;br /&gt;
    &#039;&#039;&#039;Echtzeitfähigkeit&#039;&#039;&#039;: GraphQL unterstützt auch Abonnements, die es ermöglichen, Daten in Echtzeit zu empfangen, wenn sich etwas ändert. Dies ist besonders nützlich für Anwendungsfälle wie Live-Chats, Benachrichtigungen und Echtzeit-Dashboard-Aktualisierungen.&lt;br /&gt;
    &#039;&#039;&#039;Entwicklungserfahrung&#039;&#039;&#039;: GraphQL bietet eine verbesserte Entwicklererfahrung, da Clients genau die Daten erhalten können, die sie benötigen, und Entwickler APIs schneller entwickeln können, ohne sich um Versionierung oder unerwartete Seiteneffekte durch Änderungen an der Backend-API kümmern zu müssen.&lt;br /&gt;
&lt;br /&gt;
Insgesamt bietet GraphQL eine flexible, effiziente und moderne Möglichkeit, APIs zu entwerfen und zu verwenden, die den Anforderungen moderner Anwendungen besser gerecht wird als traditionelle RESTful APIs.&lt;br /&gt;
&lt;br /&gt;
Auf der Tibber-Seite heiszt es:&lt;br /&gt;
&lt;br /&gt;
 The GraphQL query language is basically about selecting fields on objects.&lt;br /&gt;
&lt;br /&gt;
Was dies bedeutet kommt weiter unten. Weiter heiszt es auf der Tibber Seite.&lt;br /&gt;
&lt;br /&gt;
    GraphQL ist eine Abfragesprache für Ihre(? unsere) API und eine serverseitige Laufzeitumgebung zum Ausführen von Abfragen mithilfe eines Typsystems, das Sie für Ihre Daten definieren. GraphQL ist nicht an eine bestimmte Datenbank oder Speicher-Engine gebunden und wird stattdessen durch Ihren vorhandenen Code und Ihre Daten unterstützt.&lt;br /&gt;
&lt;br /&gt;
Mit der Uebersetzung von Google hadere ich noch. Der Satz kommt vermutlich ursrpruenglich von Facebook und wurde von Tibber 1:1 uebernommen.&lt;br /&gt;
&lt;br /&gt;
Der naechste Satz gibt schon mehr Aufschluss:&lt;br /&gt;
 Tibber hat sich für GraphQL als API entschieden, weil es unseren Integratoren und uns selbst Flexibilitaet bietet. Die Moeglichkeit, die gewuenschten Daten genau zu definieren, macht die Nutzung wesentlich einfacher als herkoemmliche [https://www.ibm.com/de-de/topics/rest-apis REST-basierte APIs]. Es ist auch sehr schoen, dass die serverseitige Implementierung von GraphQL sehr gut mit unserer Microservice-Architektur zusammenspielt :-)&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg nutze ich den [https://developer.tibber.com/explorer Api Explorer im TibberDev]. Nachdem man sich dort einen Personal Token angelegt hat, kann&#039;s direkt losgehen. Der [https://developer.tibber.com/explorer GraphiQL] hat auch eine Autovervollstaendigung. Sehr praktisch.&lt;br /&gt;
[[Datei:TibberDevAccount.png|mini|TibberDevAccount]]&lt;br /&gt;
Die Root-Types sind &lt;br /&gt;
*query: Query&lt;br /&gt;
*mutation: RootMutation&lt;br /&gt;
*subscription: RootSubscription&lt;br /&gt;
*fragment feht irgendwie in der Doc &lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist recht simpel und das Doc in knapp einer Stunde durchprobiert. So dass ich dann schnell an dem Punkt angelangt bin, dass ich ein Programm (PHP, Python oder JavaScript) brauche welches die fuer mich interessanten Daten in meine MySQL-DB uebertraegt. Ob ich zuerst anzeige und dann speichere, also sozusagen ueber die Datenbank puffere, oder umgekehrt muss ich noch festlegen.&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zum Subprotokoll===&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; ist ein WebSocket-Subprotokoll, das speziell für die Kommunikation zwischen Client und Server in GraphQL-Anwendungen entwickelt wurde. Es basiert auf dem WebSocket-Protokoll, das eine bidirektionale, persistente Verbindung zwischen einem Client und einem Server ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Im Kontext von GraphQL wird &amp;quot;graphql-transport-ws&amp;quot; verwendet, um GraphQL-Abfragen, Mutationen und Abonnements über WebSocket-Verbindungen zu übertragen. Im Gegensatz zum traditionellen HTTP-Protokoll, das eine separate Anfrage-Antwort-Struktur hat, ermöglicht WebSocket eine kontinuierliche Kommunikation zwischen Client und Server, was besonders nützlich ist, wenn Echtzeit-Aktualisierungen benötigt werden, wie bei Abonnements in GraphQL.&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; definiert spezifische Nachrichtenformate und Handshake-Verfahren, die für die GraphQL-Kommunikation über WebSockets erforderlich sind. Durch die Verwendung dieses Subprotokolls können Entwickler GraphQL-Anwendungen erstellen, die von den Echtzeitfähigkeiten von WebSocket profitieren, ohne auf die Vorteile und Flexibilität von GraphQL verzichten zu müssen.&lt;br /&gt;
&lt;br /&gt;
==Mehrfacher Hinweis Subprotokolwechsel Dezember 2022==&lt;br /&gt;
&lt;br /&gt;
 Die TibberAPI unterstuetzte zunaechst GraphQL-Websocket-Subscriptions mithilfe des Unterprotokolls graphql-ws. Diese Bibliothek wird/wurde jedoch archiviert und nicht mehr gepflegt. Daher wurde am 31. Maerz 2022 die Unterstützung für das Unterprotokoll graphql-transport-ws hinzugefuegt.&lt;br /&gt;
&lt;br /&gt;
 Die Unterstuetzung für das alte Protokoll wird im Dezember 2022 entfernt. Gleichzeitig aendert sich auch die URL für die Verbindung zum Websocket-Server. Zuvor konnte die Verbindung unter &#039;&#039;&#039;wss://api.tibber.com/v1-beta/gql/subscriptions&#039;&#039;&#039; hergestellt werden. Nach der Aenderung muss die URL ueber GraphQL abgefragt werden (siehe Beispiel unten) und der Client muss das Unterprotokoll graphql-transport-ws unterstuetzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight code=xml&amp;gt;&lt;br /&gt;
Frage:&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;websocketSubscriptionUrl&amp;quot;: &amp;quot;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Anfragenbegrenzung==&lt;br /&gt;
 Zum Schutz der API gilt eine Ratenbegrenzung von 100 Anfragen in 5 Minuten pro IP-Adresse. Beachten Sie, dass die Preise einmal täglich am Nachmittag berechnet werden (für Norwegen und Schweden sind sie zunächst vorläufig und werden später möglicherweise mit geringfügigen Änderungen nach Bestätigung der Wechselkurse endgültig festgelegt). Sie können „priceInfo.today“ und „priceInfo.tomorrow“ verwenden, um sie im Voraus abzurufen, anstatt nur „priceInfo.current“ für die aktuelle Stunde zu verwenden.&lt;br /&gt;
&lt;br /&gt;
=In medias res=&lt;br /&gt;
Zum Starten empfiehlt die Tibber-API-Doc, die fundamentalen Konzepte von GraphQL, die Kommunikation mit der API und den Explorer.&lt;br /&gt;
&lt;br /&gt;
Um die nachfolgenden Beispiele schnell zu verstehen, hier der Link zur [https://developer.tibber.com/docs/reference API-Referenz]&lt;br /&gt;
&lt;br /&gt;
 Bei der GraphQL-Abfragesprache geht es im Wesentlichen um die Auswahl von Feldern in Objekten.&lt;br /&gt;
&lt;br /&gt;
Auf der Docs-Reference-Seite gibt es einen interessanten Hinweis:&lt;br /&gt;
  This reference is auto-generated from https://api.tibber.com&lt;br /&gt;
&lt;br /&gt;
Es koennte als eine URL geben, die einem die aktuelle &#039;&#039;&#039;Tibber GraphQL Schema Reference&#039;&#039;&#039; direkt zur Auswertung ausgibt. Der Umweg ueber das Parsen der HTML-Ausgabe ginge zur Not natuerlich auch.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objekt Typen==&lt;br /&gt;
&lt;br /&gt;
Die Begriffsdifferenzierung Objekt und Object Types ist mir derzeit noch nicht klar. Ich werde wohl um die allgemeine [https://github.com/graphql/graphql-spec Spec von GraphQL] nicht herumkommen:-(&lt;br /&gt;
&lt;br /&gt;
Bei &#039;Home&#039; wird von einem Objekttyp gesprochen. Klar ist, man hat Objekte, die mit dem Schluesselwort type eingeleitet und dann beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 Einige Felder sind Skalare (Boolesche Werte, Zeichenfolgen, Ganzzahlen, Gleitkommazahlen), waehrend andere Objekttypen sind (Adresse, LegalEntity). Eine GraphQL-Abfrage muss vollstaendig auf Skalarebene sein. D.h. in einer Abfrage duerfen die abgefragten Felder keine Objekte (keine weiteren Objekttypen) sein, sondern hierarchisch heruntergebrochen auf weitere Inhalte bis es Skalare sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
This query is valid:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
This is not:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner   &amp;lt;- hier ist der Fehler. Dies ist kein Skalar!&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
The owner field is an object type and you would need to specify which scalar fields you would like returned:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner{&lt;br /&gt;
            name&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==query Datenabfrage (read)==&lt;br /&gt;
&lt;br /&gt;
query-&amp;gt;viewer&lt;br /&gt;
&lt;br /&gt;
Zuerst mit homes die Zaehlerplatz-iD holen und dan mit home (id: &amp;quot;&amp;lt;ID&amp;gt;&amp;quot;) nur die data dazu holen.&lt;br /&gt;
&lt;br /&gt;
Hello World;-)&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    login&lt;br /&gt;
    name&lt;br /&gt;
    userId&lt;br /&gt;
    accountType&lt;br /&gt;
    home (id: &amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;) {&lt;br /&gt;
      id&lt;br /&gt;
      features {&lt;br /&gt;
        realTimeConsumptionEnabled&lt;br /&gt;
      }&lt;br /&gt;
      subscriptions {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      currentSubscription {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      meteringPointData {&lt;br /&gt;
        consumptionEan&lt;br /&gt;
        gridCompany&lt;br /&gt;
        gridAreaCode&lt;br /&gt;
        priceAreaCode&lt;br /&gt;
        productionEan&lt;br /&gt;
        energyTaxType&lt;br /&gt;
        vatType&lt;br /&gt;
        estimatedAnnualConsumption&lt;br /&gt;
      }&lt;br /&gt;
      owner {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      mainFuseSize&lt;br /&gt;
    }&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant fand ich z.B.&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;priceAreaCode&amp;quot;: &amp;quot;Amprion&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;estimatedAnnualConsumption&amp;quot;: 500&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;mainFuseSize&amp;quot;: null&lt;br /&gt;
&lt;br /&gt;
und was da noch alles an Daten einzutragen ist.&lt;br /&gt;
 size: Int&lt;br /&gt;
 The size of the home in square meters&lt;br /&gt;
&lt;br /&gt;
==mutation Datenveraenderung (write)==&lt;br /&gt;
&lt;br /&gt;
Mutationen gibt es nur 3, wobei die erste bei mir entfaellt.&lt;br /&gt;
&lt;br /&gt;
 sendMeterReading(input: MeterReadingInput!): MeterReadingResponse!&lt;br /&gt;
 Send meter reading for home (only available for Norwegian users) &lt;br /&gt;
 &lt;br /&gt;
 updateHome(input: UpdateHomeInput!): Home!&lt;br /&gt;
 Update home information&lt;br /&gt;
 &lt;br /&gt;
 sendPushNotification(input: PushNotificationInput!): PushNotificationResponse!&lt;br /&gt;
 Send notification to Tibber app on registered devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==subscription==&lt;br /&gt;
&lt;br /&gt;
Subscription hat nur 2 Felder, wobei dauerhaft nur das erste interessant ist.&lt;br /&gt;
&lt;br /&gt;
 liveMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to real-time measurement stream from Pulse or Watty device&lt;br /&gt;
&lt;br /&gt;
 testMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to test stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
subscription{&lt;br /&gt;
  liveMeasurement(homeId: &amp;quot;&amp;lt;homeID&amp;gt;&amp;quot;){&lt;br /&gt;
    timestamp&lt;br /&gt;
    power&lt;br /&gt;
    lastMeterConsumption&lt;br /&gt;
    accumulatedConsumption&lt;br /&gt;
    accumulatedProduction&lt;br /&gt;
    accumulatedConsumptionLastHour&lt;br /&gt;
    accumulatedProductionLastHour&lt;br /&gt;
    accumulatedCost&lt;br /&gt;
    accumulatedReward&lt;br /&gt;
    currency&lt;br /&gt;
    minPower&lt;br /&gt;
    averagePower&lt;br /&gt;
    maxPower&lt;br /&gt;
    powerProduction&lt;br /&gt;
    powerReactive&lt;br /&gt;
    powerProductionReactive&lt;br /&gt;
    minPowerProduction&lt;br /&gt;
    maxPowerProduction&lt;br /&gt;
    lastMeterProduction&lt;br /&gt;
    powerFactor&lt;br /&gt;
    voltagePhase1&lt;br /&gt;
    voltagePhase2&lt;br /&gt;
    voltagePhase3&lt;br /&gt;
    currentL1&lt;br /&gt;
    currentL2&lt;br /&gt;
    currentL3&lt;br /&gt;
    signalStrength&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==fragment==&lt;br /&gt;
&lt;br /&gt;
?&lt;br /&gt;
&lt;br /&gt;
Dazu habe ich bisher keine weiteren Infos gefunden.&lt;br /&gt;
&lt;br /&gt;
=Programmierung=&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung steht bei mir hier die Client-Programmierung im Vordergrund.&lt;br /&gt;
&lt;br /&gt;
==Query/Mutation==&lt;br /&gt;
&lt;br /&gt;
Die Abfragen und Aenderungen zu programmieren sind schnell erledigt.&lt;br /&gt;
&lt;br /&gt;
Der Einstieg auf der Konsole gelingt mit curl recht gut. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
 curl \&lt;br /&gt;
-H &amp;quot;Authorization: Bearer xxxxx-yyyyyy_dhfakjebnrqauvhdjhfaklhdf \&lt;br /&gt;
-H &amp;quot;Content-Type: application/json&amp;quot; \&lt;br /&gt;
-X POST \&lt;br /&gt;
-d  &#039;mutation{ sendMeterReading(input:{homeId:&amp;quot;some home id&amp;quot;, reading: 123}){time reading  }}&#039; https://api.tibber.com/v1-beta/gql&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Subscription==&lt;br /&gt;
&lt;br /&gt;
Die Subscriptions sind schon ein wenig anspruchsvoller zu programmieren als Query und Mutation (QuM).&lt;br /&gt;
Ist QuM eine simple http-Anfrage mit einer Antwort im JSON-Format, ist es bei den Subscriptions ein Websocket, der einmal geoeffnet in unregelmaeszigen Abstaenden JSON-Pakete liefert.&lt;br /&gt;
&lt;br /&gt;
===Anforderungen für Websocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
* Clients muessen den User-Agent-Header festlegen, wenn sie die GraphQL-API aufrufen und eine Websocket-Verbindung oeffnen. Sowohl die Plattform als auch die Treiberversion muessen angegeben werden. Z.B. Homey/10.0.0 com.tibber/1.8.3&lt;br /&gt;
* Clients müssen das Unterprotokoll graphql-transport-ws implementieren&lt;br /&gt;
* Clients müssen Jitter und exponentielles Backoff implementieren, wenn sie Anfragen an die API wiederholen&lt;br /&gt;
* Clients müssen die WebSocket-Server-URL dynamisch abfragen&lt;br /&gt;
&lt;br /&gt;
===Best Practices für die Implementierung von WebSocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
Bei der Implementierung eines Clients zum Abonnieren von Live-Daten sind einige Szenarien zu beruecksichtigen:&lt;br /&gt;
* Keine Daten aufgrund von Netzwerkproblemen: Es kann sinnvoll sein, einen Wiederverbindungsmechanismus zu implementieren, falls mehrere Minuten lang keine Daten empfangen wurden. Bei einem erneuten Versuch muss zunaechst die alte Websocket-Verbindung zerstoert und ordnungsgemaeszer Jitter und exponentielle Verzoegerung implementiert werden.&lt;br /&gt;
* Ein Echtzeitgeraet wird moeglicherweise entfernt. Es wird empfohlen, vor dem erneuten Verbinden immer zu ueberpruefen, ob home.features.realTimeConsumptionEnabled einen echten Wert hat.&lt;br /&gt;
* Eine WebSocket-Verbindung kann aufgrund eines ungueltigen Authentifizierungstokens verboten sein (z. B. weil ein Benutzer möglicherweise sein Tibber-Konto entfernt hat). In diesem Fall darf der Client es nicht mit demselben Token erneut versuchen. Stattdessen muss der Autorisierungsprozess erneut ausgeführt werden, wobei der Benutzer neue Anmeldeinformationen eingibt.&lt;br /&gt;
* Es kann zu einem Neustart der Tibber-Websocket-API kommen, was bedeutet, dass die Verbindung mit dem &amp;quot;Code 1001&amp;quot; und dem Grund &amp;quot;Going away&amp;quot; geschlossen wird. In diesem Fall kann der Client nach einer zufälligen Verzoegerung von 1 bis 60 Sekunden (Jitter angewendet) die Verbindung wiederherstellen, um zu vermeiden, dass alle Clients gleichzeitig die Verbindung wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
In PHP habe ich wenig bis gar nichts gefunden. Ich wollte mir auch einfach die Nutzung einer GraphQL-Library ersparen. Python habe ich erst einmal an den Schluss verschoben und einem Integration in FHEM den Vorzug gegeben.&lt;br /&gt;
&lt;br /&gt;
===Perl (FHEM)===&lt;br /&gt;
Hier der Code der tatsaechlich funktioniert, obwohl er an ein paar Stellen nicht so vorgeht wie ich aus den Tibber-Docs erwartet hatte.&lt;br /&gt;
&lt;br /&gt;
HINWEIS: Dies ist der &amp;quot;nackte&amp;quot; Perl-Code. In der &amp;quot;Originaldatei&amp;quot; sind noch die &amp;quot;FHEM-Escapes&amp;quot; doppelter Strichpunkt und Zeilenfolgezeichen \ enthalten. Eine Eingabe ueber die FHEM-Kommandozeile hat bei mir nicht funktioniert. Erst das direkte Einkopieren in die fhem.conf zeigte das nachfolgende, funktionierende Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
connect:cmd:.connect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	# establish connection to websocket&lt;br /&gt;
	# format must also include portnumber if a path is to be specified&lt;br /&gt;
	$hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
	&lt;br /&gt;
	# callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
	# websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
	&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onDisconnect {&lt;br /&gt;
	my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect&lt;br /&gt;
	my $timerFunction = sub() {&lt;br /&gt;
		my ($hash) = @_;&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
		&lt;br /&gt;
		# only re-connect if device is not connected&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));&lt;br /&gt;
	};&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash, $timerFunction);&lt;br /&gt;
	&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onConnectionAck:websocketData:.*connection_ack.* {&lt;br /&gt;
	#websocketData contains the string &amp;quot;connection_ack&amp;quot;&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# do not proceed if connection is lost&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device not open&amp;quot; if (!defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	my $homeId = AttrVal($name, &amp;quot;homeId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myId = AttrVal($name, &amp;quot;myId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# build the query, do it in pieces, the comma at the end caused perl errors&lt;br /&gt;
	# so we put it together in this not very elegant way&lt;br /&gt;
	my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;&lt;br /&gt;
	$json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;&lt;br /&gt;
	$json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;&lt;br /&gt;
	$json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;&lt;br /&gt;
	$json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;&lt;br /&gt;
	$json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;&lt;br /&gt;
	$json .= &#039;}}&#039;;&lt;br /&gt;
	&lt;br /&gt;
	#send the string via websocket as ASCII&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
	DevIo_SimpleWrite($hash, $json, 2);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onNextLiveMeasurement:websocketData:.*next.*payload.*data.*liveMeasurement.* {&lt;br /&gt;
	#websocketData contains next-live-measurement-data&lt;br /&gt;
	my $val = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;{}&amp;quot;);&lt;br /&gt;
	my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};&lt;br /&gt;
	&lt;br /&gt;
	my $ret = &amp;quot;got values for:\n&amp;quot;;&lt;br /&gt;
	foreach my $k (sort keys %res) {&lt;br /&gt;
		$ret .= &amp;quot;$k\n&amp;quot;;&lt;br /&gt;
		readingsBulkUpdate($hash, makeReadingName($k), $res{$k});&lt;br /&gt;
	}&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Code Analyse====&lt;br /&gt;
&lt;br /&gt;
Es gibt 5 Eintrittspunkte:&lt;br /&gt;
* connect&lt;br /&gt;
* disconnect&lt;br /&gt;
* onDisconnect&lt;br /&gt;
* onConnectionAck&lt;br /&gt;
* onNextLiveMeasurement&lt;br /&gt;
&lt;br /&gt;
Die ersten beiden legen die Befehlsfolgen für ein Start-Kommando (set cmd connect, bzw. set cmd disconnect) oder Stop-Kommando fest.&lt;br /&gt;
Die letzten 3 reagieren auf die entsprechenden Events.&lt;br /&gt;
=====connect=====&lt;br /&gt;
&lt;br /&gt;
Lexikalische Variablen mittels my, bzw. my()&lt;br /&gt;
&lt;br /&gt;
 Jede Variable, die mit our deklariert oder auch &amp;quot;einfach so&amp;quot; ohne eine Deklaration verwendet wird, wird in die Symboltabelle des jeweils aktuellen Packages aufgenommen.&lt;br /&gt;
 Deklariert man dagegen eine Variable mit dem Operator my, so wird die entsprechende Variable in einer anderen Tabelle abgelegt, auf die kein expliziter Zugriff möglich ist. &lt;br /&gt;
 Neben der Tatsache, daß my-Variablen in einer eigenen Tabelle verwaltet werden, ist von besonderer Bedeutung, daß sie nur einen recht beschränkten Gültigkeitsbereich besitzen. Eine durch my erzeugte Variable steht nur in dem aktuellen Block (definiert durch geschweifte Klammern &amp;quot;{...}&amp;quot;), der aktuellen Datei oder innerhalb eines Arguments von eval() zur Verfügung. Außerhalb davon existieren diese Variablen nicht, es ist also (im Gegensatz zu our-Variablen) auch mit Hilfe des Package-Namens dort kein Zugriff möglich&lt;br /&gt;
 Es kann in einem Package durchaus zwei Variablen gleichen Namens geben: eine in der Symboltabelle und eine, die durch einen Aufruf von my entstanden ist. In einem solchen Falle wird bei einfacher Verwendung des Bezeichners auf die my-Variable zugegriffen. &lt;br /&gt;
&lt;br /&gt;
In $defs stehen alle Defines drin. In $hash wird also der Name des des aufrufenden Devices lokal hinterlegt und danach mit DevIO_IsOpen() der Status abgefragt. &lt;br /&gt;
$defs ist ein Hash. Ein Hash ist ein anderer Namen fuer ein assoziatives Array. Also eine ungeordnete Liste von Skalaren (Zahl oder String) die ueber einen Stringwert angesprochen (Lookup) werden koennen.  &lt;br /&gt;
&lt;br /&gt;
FHEM-Spezial:&lt;br /&gt;
 In $defs findet man alle Devices, z.b. $defs{&amp;quot;Rolladen_Tuere&amp;quot;} beinhaltet das Device.&lt;br /&gt;
 my @alle = keys %defs;&lt;br /&gt;
 my @teilmenge = defInfo(&#039;TYPE=notify&#039;,&#039;NAME&#039;); liefert ein array mit allen notify devices.&lt;br /&gt;
 z.B. my @teilmenge = defInfo(&#039;NAME=Rollladen.*&#039;,&#039;NAME&#039;);&lt;br /&gt;
&lt;br /&gt;
 return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
Sollte also das Device bereits im State &amp;quot;offen&amp;quot; sein, wird die connect-Routine abgebrochen. Dann erscheint in den Readings nicht Datum/Uhrzeit des Connects sondern die Meldung &amp;quot;Device already open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
 # establish connection to websocket&lt;br /&gt;
 # format must also include portnumber if a path is to be specified&lt;br /&gt;
 $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 HASH-Operationen (Beispiel):&lt;br /&gt;
 $href ={APR =&amp;gt; 4,AUG =&amp;gt; 8}; #anonymous hash&lt;br /&gt;
 $el = $href-&amp;gt;{APR}; $el = %{$href}{APR}; #access element of hash&lt;br /&gt;
 $href2 = {%{$href1}}; #copy hash&lt;br /&gt;
 if (ref($r) eq &amp;quot;HASH&amp;quot;) {} #checks if $r points to hash&lt;br /&gt;
&lt;br /&gt;
Dem Internal &amp;quot;DeviceName&amp;quot; wird das Ergebnis von AttrVal() zugewiesen. Bei mir ergab das:&lt;br /&gt;
 DeviceName wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&lt;br /&gt;
 FHEM-Referenz:&lt;br /&gt;
 AttrVal(&amp;lt;devicename&amp;gt;,&amp;lt;attribute&amp;gt;,&amp;lt;defaultvalue&amp;gt;)&lt;br /&gt;
 Gibt das entsprechende Attribut des Gerätes zurück&lt;br /&gt;
 { Value(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { OldValue(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { time_str2num(OldTimestamp(&amp;quot;wz&amp;quot;)) }&lt;br /&gt;
 { ReadingsVal(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, &amp;quot;20&amp;quot;)+0 }&lt;br /&gt;
 { ReadingsTimestamp(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, 0)}&lt;br /&gt;
 { AttrVal(&amp;quot;wz&amp;quot;, &amp;quot;room&amp;quot;, &amp;quot;none&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Das Attribut websocketURL ist bei mir wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions. Somit erklaert sich auch der Eintrag bei DeviceName. &amp;quot;wss:echo.websocket.org:443&amp;quot; ist nur der Defaultwert.&lt;br /&gt;
&lt;br /&gt;
Man haette zum Testen auch die folgenden beiden verwenden/eintragen koennen.&lt;br /&gt;
 There are free test servers that we can use to experiment with a WebSocket client, such as:&lt;br /&gt;
&lt;br /&gt;
    Hoppscotch – wss://echo-websocket.hoppscotch.io (GUI)&lt;br /&gt;
    λ if else – wss://ws.ifelse.io (GUI)&lt;br /&gt;
&lt;br /&gt;
 Both provide a GUI for testing via a browser. The former sends a timestamp to the client every second but doesn’t respond to client messages. The latter is a standard echo service that sends a copy of each received message back to the client. Both make sense in testing.&lt;br /&gt;
&lt;br /&gt;
Da curl nicht nativ websocket-Kommunikation unterstuetzt kann man auf folgende Tool ausweichen. Auch chrome bietet ein Websocket-Client-Plugin.&lt;br /&gt;
*wssh3&lt;br /&gt;
*websocat&lt;br /&gt;
*wscat&lt;br /&gt;
&lt;br /&gt;
 # special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
&lt;br /&gt;
 # callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
 # websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion wird direkt, ohne Namen, in das/den (?) Hash unter directReadFn abgelegt. &lt;br /&gt;
Die Callback-Funktion:&lt;br /&gt;
Hier wird mit Hilfe der Funktion DevIo_SimpleRead() die Rueckmeldung des Server eingelesen. Siehe [https://wiki.fhem.de/wiki/DevIo]&lt;br /&gt;
Hinweis zum Logging in FHEM: [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#Logging]&lt;br /&gt;
Die eigentliche Aktivitaet geschieht in den sieben Zeilen am Ende der Callback-Fkt. &lt;br /&gt;
 ReadingsAge(&amp;lt;devicename&amp;gt;,&amp;lt;reading&amp;gt;,&amp;lt;defaultvalue&amp;gt;) #gibt das Alter des Readings in Sekunden zurück. &lt;br /&gt;
 AttrVal hatten wir schon.&lt;br /&gt;
 isNext bekommt seinen Wert aus dem durch einen REGEX geliefert Wert des Buffers. Beispiel ($match) = ($dirty =~ /^(.*)$/s); &lt;br /&gt;
&lt;br /&gt;
 Die Funktion readingsBeginUpdate() bereitet die Definition mit dem Hash $hash auf ein Update von Readings vor. Dies betrifft insbesondere das Setzen von Umgebungsvariablen sowie dem aktuellen Zeitstempel als Änderungszeitpunkt. Der Aufruf dieser Funktion ist notwendig um eigentliche Updates mit der Funktion readingsBulkUpdate() auf der gewünschten Definition durchführen zu können. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#readingsBeginUpdate]&lt;br /&gt;
&lt;br /&gt;
Die Funktion readingsEndUpdate() beendet den Bulk-Update Prozess durch die Funktionen readingsBeginUpdate() &amp;amp; readingsBulkUpdate() und triggert optional die entsprechenden Events sämtlicher erzeugter Readings für die Definition $hash. Desweiteren werden nachgelagerte Tasks wie bspw. die Erzeugung von User-Readings (Attribut: userReadings), sowie die Erzeugung des STATE aufgrund des Attributs stateFormat durchgeführt. Sofern $do_trigger gesetzt ist, werden alle anstehenden Events nach Abschluss getriggert.&lt;br /&gt;
&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
Das ist die eigentliche Kontaktaufnahme. &lt;br /&gt;
&lt;br /&gt;
Dies dient vermutlich dazu, bis zum ersten Empfang von Daten, das Reading websocketDate zu leeren.&lt;br /&gt;
&lt;br /&gt;
 return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss ercheint dann im Reading connect die aktuelle Zeit.&lt;br /&gt;
&lt;br /&gt;
readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=====disconnect=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
 disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erlaeuterung:&lt;br /&gt;
&lt;br /&gt;
*Die Funktion RemoveInternalTimer löscht möglicherweise noch anstehende Timer welche mit dem Übergabeparameter $arg gescheduled sind. Optional kann man zusätzlich die Suche auf eine bestimmte Funktion $functionName weiter einschränken. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#RemoveInternalTimer]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von $hash ein und gibt diese zurück. [https://wiki.fhem.de/wiki/DevIo#DevIo_SimpleRead()]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition $hash. [https://wiki.fhem.de/wiki/DevIo#DevIo_CloseDev()]&lt;br /&gt;
&lt;br /&gt;
*POSIX:: [https://metacpan.org/pod/POSIX#strftime]&lt;br /&gt;
&lt;br /&gt;
=====onDisconnect=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=====onConnectionAck=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====onNextLiveMeasurement=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===JavaScript===&lt;br /&gt;
Zum Eingewoehnen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;ws://echo.websocket.org&#039;);&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung hergestellt.&#039;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
    socket.send(&#039;Hallo Server!&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&#039;Nachricht vom Server erhalten:&#039;, event.data);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und jetzt das Gerippe fuer Tibber mit Haut versehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
const WebSocket = require(&#039;ws&#039;);&lt;br /&gt;
&lt;br /&gt;
const query = `subscription {&lt;br /&gt;
    liveMeasurement(homeId: &amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;) {&lt;br /&gt;
      timestamp&lt;br /&gt;
      power&lt;br /&gt;
      accumulatedConsumption&lt;br /&gt;
      minPower&lt;br /&gt;
      maxPower&lt;br /&gt;
      averagePower&lt;br /&gt;
      voltagePhase1&lt;br /&gt;
      currentL1&lt;br /&gt;
      accumulatedCost&lt;br /&gt;
      currency&lt;br /&gt;
   } &lt;br /&gt;
}`;&lt;br /&gt;
&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&#039;,[&#039;graphql-transport-ws&#039;], { &lt;br /&gt;
        headers: {&lt;br /&gt;
                &#039;Content-Type&#039;: &#039;application/json&#039;,&lt;br /&gt;
                &#039;User-Agent&#039;: &#039;NodeJS tibberview&#039;,&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&amp;quot;WebSocket-Verbindung hergestellt.&amp;quot;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
   const connectionQuery = {&lt;br /&gt;
      type: &amp;quot;connection_init&amp;quot;,&lt;br /&gt;
      payload: {&lt;br /&gt;
          token: &amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    socket.send(JSON.stringify(connectionQuery));&lt;br /&gt;
    console.log(&amp;quot;Verbindung zum &#039;Tibber feed&#039; hergestellt.&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&amp;quot;Nachricht vom Server erhalten:&amp;quot;, event.data);&lt;br /&gt;
    const msg = JSON.parse(event.data);&lt;br /&gt;
&lt;br /&gt;
    if (msg.type === &amp;quot;connection_ack&amp;quot;) {&lt;br /&gt;
      console.log(&amp;quot;Tibber ACK empfangen: &amp;quot;, msg);&lt;br /&gt;
&lt;br /&gt;
      const realtimeDataQuery = {&lt;br /&gt;
        id: &amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&lt;br /&gt;
        type: &amp;quot;subscribe&amp;quot;,&lt;br /&gt;
        payload: { query }&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
      socket.send(JSON.stringify(realtimeDataQuery));&lt;br /&gt;
    }&lt;br /&gt;
    else if (msg.type === &amp;quot;next&amp;quot;) {&lt;br /&gt;
      if (!msg.payload.data) {&lt;br /&gt;
        console.log(&amp;quot;⚠  Nachricht hat keinen Inhalt.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      const data = msg.payload.data.liveMeasurement;&lt;br /&gt;
      socket.emit(&amp;quot;data&amp;quot;, data);&lt;br /&gt;
    } else {&lt;br /&gt;
      console.log(&amp;quot;Nachrichteninhalt: &amp;quot;, msg)&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm erzeugte folgende Ausgabe:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
worker:/mnt/1.5T_RAID/Programme/JavaScript/WebSocket # node tibberclient_demo.js &lt;br /&gt;
WebSocket-Verbindung hergestellt.&lt;br /&gt;
Verbindung zum &#039;Tibber feed&#039; hergestellt.&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;type&amp;quot;:&amp;quot;connection_ack&amp;quot;}&lt;br /&gt;
Tibber ACK empfangen:  { type: &#039;connection_ack&#039; }&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:48.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.6,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:51.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:52.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:230,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:53.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.2,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:54.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.1,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:55.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:230.2,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:56.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:57.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.9,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:58.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:59.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.4,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:00.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.7,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:01.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.6,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
^C&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An der Stelle mache ich jetzt einen Ausflug in PHP.&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
Nachdem der Websocket-Kontakt mit JavaScript funktioniert hat, kommt jetzt ein Serverprogramm was folgendes tun soll.&lt;br /&gt;
&lt;br /&gt;
*Abfrage der Basisdaten&lt;br /&gt;
*Objekt Types Builder&lt;br /&gt;
*Mutation&lt;br /&gt;
*Websocket Subsrciption&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===Python===&lt;br /&gt;
&lt;br /&gt;
Quelle: https://github.com/Danielhiversen/pyTibber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Client-Beispiel mit eingesetzem DEMO_TOKEN und user_agent&lt;br /&gt;
Obwohl DEMO_TOKEN in tibber.py definiert ist, wird es nicht uebernommen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import tibber&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def start():&lt;br /&gt;
  tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
  await tibber_connection.update_info()&lt;br /&gt;
  print(tibber_connection.name)&lt;br /&gt;
&lt;br /&gt;
  home = tibber_connection.get_homes()[0]&lt;br /&gt;
  await home.fetch_consumption_data()&lt;br /&gt;
  await home.update_info()&lt;br /&gt;
  print(home.address1)&lt;br /&gt;
&lt;br /&gt;
  await home.update_price_info()&lt;br /&gt;
  print(home.current_price_info)&lt;br /&gt;
  &lt;br /&gt;
  # await tibber_connection.close_connection()&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(start())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
import aiohttp&lt;br /&gt;
import tibber&lt;br /&gt;
&lt;br /&gt;
def _callback(pkg):&lt;br /&gt;
    print(pkg)&lt;br /&gt;
    data = pkg.get(&amp;quot;data&amp;quot;)&lt;br /&gt;
    if data is None:&lt;br /&gt;
        return&lt;br /&gt;
    print(data.get(&amp;quot;liveMeasurement&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def run():&lt;br /&gt;
    async with aiohttp.ClientSession() as session:&lt;br /&gt;
        tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, websession=session, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
        await tibber_connection.update_info()&lt;br /&gt;
    home = tibber_connection.get_homes()[0]&lt;br /&gt;
    await home.rt_subscribe(_callback)    &lt;br /&gt;
&lt;br /&gt;
    while True:&lt;br /&gt;
      await asyncio.sleep(10)&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(run())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4847</id>
		<title>Tibber Pulse (API)</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4847"/>
		<updated>2026-04-10T06:34:21Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* JavaScript */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Da ich aktuell vom Energieversorger einen zweiten Hausanschluss (40kW :-() gelegt bekommen habe und ich für die Wallboxen zur Foerderung einen 100% Oekostrom haben musste, habe ich kurzerhand einen Vertrag mit Tibber (mal zum Testen) abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
=Installation=&lt;br /&gt;
Pulse IR und Bridge&lt;br /&gt;
[[Datei:Tibber Pulse IR und Bridge.png|mini|Tibber Pulse IR und Bridge]]&lt;br /&gt;
Eigentlich verlief die Installation problemlos. Fuer mich war allerdings der Assistent in der Tibber-App zu kindisch. Das ist eher was für Apple-User. Ich haette mir an der ein oder anderen Stelle mehr Hardcore-Infos gewuenscht. Da der mehrstufige Prozess (WLAN-Bridge-Pulse-Zaehler) doch einige Solperfallen enthaelt, ist es fuer mich eher frustrierend jedesmal bei einem Fehler zurueck auf Los geschickt zu werden. Das geht definitiv besser. Ist aber wieder ein Beispiel, dass die Informatiker-Jobs heute nur noch durch 5-jaehrige Schimpansen (ungelernte ;-)) besetzt werden.&lt;br /&gt;
&lt;br /&gt;
Bei mir gab es letztendlich nur einen gravierenden Fehler. Von den mitgelieferten Akkus des Pulse war einer defekt. Bei mir sind das zwei AA-Zellen auf LiBasis! Ein Nachladen mit einem Lithium-1.5V-Lader hat keinen Erfolg gebracht. Erst der Austausch gegen 2 neue Zellen war erfolgreich.&lt;br /&gt;
&lt;br /&gt;
=App=&lt;br /&gt;
[[Datei:TibberAppIcon.png|mini|TibberAppIcon]]&lt;br /&gt;
Die App find ich ziemlich verquer. Sobald Rechnungsanschrift und Installationsorte unterschiedlich sind, geht das Chaos los. Auch der Support tut sich dann schwer. Ich traue mich gar nicht so richtig weitere Vertraege abzuschlieszen.&lt;br /&gt;
Alles ueber die App machen zu wollen/muessen ist ... Das gehoert fuer mich ins WebUserPortal.&lt;br /&gt;
&lt;br /&gt;
Was echt nervt ist, dass sich gefühlt jeden Monat das LookandFeel aendert. Schon die dritte oder vierte Designaenderung des Homescreens (Zuhause).&lt;br /&gt;
&lt;br /&gt;
=API=&lt;br /&gt;
Da die App eher suboptimal ist, habe ich recht zuegig damit angefangen mir die notwendigen Infos ueber die API auf eine Infoseite im Intranet zu holen. Ein Integration in FHEM und/oder HA steht noch aus.&lt;br /&gt;
&lt;br /&gt;
=Erste Schritte=&lt;br /&gt;
&lt;br /&gt;
Uebersicht [https://developer.tibber.com/docs/overview]&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung: Die Plattform wird über GraphQL verfügbar gemacht, eine Technologie, die von Facebook konzipiert und entwickelt wurde.&lt;br /&gt;
&lt;br /&gt;
==Was ist GraphQL?==&lt;br /&gt;
&lt;br /&gt;
[https://de.wikipedia.org/wiki/GraphQL Wikipedia]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/graphql GraphQL]&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zu GraphQL===&lt;br /&gt;
&lt;br /&gt;
GraphQL ist eine von Facebook entwickelte Abfragesprache und Laufzeitumgebung für APIs (Application Programming Interfaces). Im Gegensatz zu traditionellen RESTful APIs, bei denen die Client-Anwendung verschiedene Endpunkte aufruft, um Daten abzurufen oder zu manipulieren, ermöglicht GraphQL dem Client, genau die Daten abzurufen, die er benötigt, indem er eine einzige Abfrage an den Server sendet.&lt;br /&gt;
&lt;br /&gt;
Hier sind einige Hauptmerkmale von GraphQL:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;Declarative Datenabfrage&#039;&#039;&#039;: Der Client spezifiziert genau, welche Daten er vom Server erhalten möchte, indem er eine GraphQL-Abfrage definiert, die die benötigten Felder und deren Struktur beschreibt. Dadurch kann der Client genau die Daten erhalten, die er benötigt, ohne Overfetching oder Underfetching.&lt;br /&gt;
    &#039;&#039;&#039;Typsystem&#039;&#039;&#039;: GraphQL definiert ein Typsystem, das die Struktur und den Typ der Daten beschreibt, die von der API zurückgegeben werden können. Dies ermöglicht eine strikte Typisierung und verbesserte Dokumentation.&lt;br /&gt;
    &#039;&#039;&#039;Single-Endpoint-Architektur&#039;&#039;&#039;: Im Gegensatz zu RESTful APIs, die oft mehrere Endpunkte für verschiedene Ressourcentypen haben, hat eine GraphQL-API normalerweise nur einen Endpunkt, über den alle Abfragen und Mutationen gesendet werden.&lt;br /&gt;
    &#039;&#039;&#039;Echtzeitfähigkeit&#039;&#039;&#039;: GraphQL unterstützt auch Abonnements, die es ermöglichen, Daten in Echtzeit zu empfangen, wenn sich etwas ändert. Dies ist besonders nützlich für Anwendungsfälle wie Live-Chats, Benachrichtigungen und Echtzeit-Dashboard-Aktualisierungen.&lt;br /&gt;
    &#039;&#039;&#039;Entwicklungserfahrung&#039;&#039;&#039;: GraphQL bietet eine verbesserte Entwicklererfahrung, da Clients genau die Daten erhalten können, die sie benötigen, und Entwickler APIs schneller entwickeln können, ohne sich um Versionierung oder unerwartete Seiteneffekte durch Änderungen an der Backend-API kümmern zu müssen.&lt;br /&gt;
&lt;br /&gt;
Insgesamt bietet GraphQL eine flexible, effiziente und moderne Möglichkeit, APIs zu entwerfen und zu verwenden, die den Anforderungen moderner Anwendungen besser gerecht wird als traditionelle RESTful APIs.&lt;br /&gt;
&lt;br /&gt;
Auf der Tibber-Seite heiszt es:&lt;br /&gt;
&lt;br /&gt;
 The GraphQL query language is basically about selecting fields on objects.&lt;br /&gt;
&lt;br /&gt;
Was dies bedeutet kommt weiter unten. Weiter heiszt es auf der Tibber Seite.&lt;br /&gt;
&lt;br /&gt;
    GraphQL ist eine Abfragesprache für Ihre(? unsere) API und eine serverseitige Laufzeitumgebung zum Ausführen von Abfragen mithilfe eines Typsystems, das Sie für Ihre Daten definieren. GraphQL ist nicht an eine bestimmte Datenbank oder Speicher-Engine gebunden und wird stattdessen durch Ihren vorhandenen Code und Ihre Daten unterstützt.&lt;br /&gt;
&lt;br /&gt;
Mit der Uebersetzung von Google hadere ich noch. Der Satz kommt vermutlich ursrpruenglich von Facebook und wurde von Tibber 1:1 uebernommen.&lt;br /&gt;
&lt;br /&gt;
Der naechste Satz gibt schon mehr Aufschluss:&lt;br /&gt;
 Tibber hat sich für GraphQL als API entschieden, weil es unseren Integratoren und uns selbst Flexibilitaet bietet. Die Moeglichkeit, die gewuenschten Daten genau zu definieren, macht die Nutzung wesentlich einfacher als herkoemmliche [https://www.ibm.com/de-de/topics/rest-apis REST-basierte APIs]. Es ist auch sehr schoen, dass die serverseitige Implementierung von GraphQL sehr gut mit unserer Microservice-Architektur zusammenspielt :-)&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg nutze ich den [https://developer.tibber.com/explorer Api Explorer im TibberDev]. Nachdem man sich dort einen Personal Token angelegt hat, kann&#039;s direkt losgehen. Der [https://developer.tibber.com/explorer GraphiQL] hat auch eine Autovervollstaendigung. Sehr praktisch.&lt;br /&gt;
[[Datei:TibberDevAccount.png|mini|TibberDevAccount]]&lt;br /&gt;
Die Root-Types sind &lt;br /&gt;
*query: Query&lt;br /&gt;
*mutation: RootMutation&lt;br /&gt;
*subscription: RootSubscription&lt;br /&gt;
*fragment feht irgendwie in der Doc &lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist recht simpel und das Doc in knapp einer Stunde durchprobiert. So dass ich dann schnell an dem Punkt angelangt bin, dass ich ein Programm (PHP, Python oder JavaScript) brauche welches die fuer mich interessanten Daten in meine MySQL-DB uebertraegt. Ob ich zuerst anzeige und dann speichere, also sozusagen ueber die Datenbank puffere, oder umgekehrt muss ich noch festlegen.&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zum Subprotokoll===&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; ist ein WebSocket-Subprotokoll, das speziell für die Kommunikation zwischen Client und Server in GraphQL-Anwendungen entwickelt wurde. Es basiert auf dem WebSocket-Protokoll, das eine bidirektionale, persistente Verbindung zwischen einem Client und einem Server ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Im Kontext von GraphQL wird &amp;quot;graphql-transport-ws&amp;quot; verwendet, um GraphQL-Abfragen, Mutationen und Abonnements über WebSocket-Verbindungen zu übertragen. Im Gegensatz zum traditionellen HTTP-Protokoll, das eine separate Anfrage-Antwort-Struktur hat, ermöglicht WebSocket eine kontinuierliche Kommunikation zwischen Client und Server, was besonders nützlich ist, wenn Echtzeit-Aktualisierungen benötigt werden, wie bei Abonnements in GraphQL.&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; definiert spezifische Nachrichtenformate und Handshake-Verfahren, die für die GraphQL-Kommunikation über WebSockets erforderlich sind. Durch die Verwendung dieses Subprotokolls können Entwickler GraphQL-Anwendungen erstellen, die von den Echtzeitfähigkeiten von WebSocket profitieren, ohne auf die Vorteile und Flexibilität von GraphQL verzichten zu müssen.&lt;br /&gt;
&lt;br /&gt;
==Mehrfacher Hinweis Subprotokolwechsel Dezember 2022==&lt;br /&gt;
&lt;br /&gt;
 Die TibberAPI unterstuetzte zunaechst GraphQL-Websocket-Subscriptions mithilfe des Unterprotokolls graphql-ws. Diese Bibliothek wird/wurde jedoch archiviert und nicht mehr gepflegt. Daher wurde am 31. Maerz 2022 die Unterstützung für das Unterprotokoll graphql-transport-ws hinzugefuegt.&lt;br /&gt;
&lt;br /&gt;
 Die Unterstuetzung für das alte Protokoll wird im Dezember 2022 entfernt. Gleichzeitig aendert sich auch die URL für die Verbindung zum Websocket-Server. Zuvor konnte die Verbindung unter &#039;&#039;&#039;wss://api.tibber.com/v1-beta/gql/subscriptions&#039;&#039;&#039; hergestellt werden. Nach der Aenderung muss die URL ueber GraphQL abgefragt werden (siehe Beispiel unten) und der Client muss das Unterprotokoll graphql-transport-ws unterstuetzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight code=xml&amp;gt;&lt;br /&gt;
Frage:&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;websocketSubscriptionUrl&amp;quot;: &amp;quot;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Anfragenbegrenzung==&lt;br /&gt;
 Zum Schutz der API gilt eine Ratenbegrenzung von 100 Anfragen in 5 Minuten pro IP-Adresse. Beachten Sie, dass die Preise einmal täglich am Nachmittag berechnet werden (für Norwegen und Schweden sind sie zunächst vorläufig und werden später möglicherweise mit geringfügigen Änderungen nach Bestätigung der Wechselkurse endgültig festgelegt). Sie können „priceInfo.today“ und „priceInfo.tomorrow“ verwenden, um sie im Voraus abzurufen, anstatt nur „priceInfo.current“ für die aktuelle Stunde zu verwenden.&lt;br /&gt;
&lt;br /&gt;
=In medias res=&lt;br /&gt;
Zum Starten empfiehlt die Tibber-API-Doc, die fundamentalen Konzepte von GraphQL, die Kommunikation mit der API und den Explorer.&lt;br /&gt;
&lt;br /&gt;
Um die nachfolgenden Beispiele schnell zu verstehen, hier der Link zur [https://developer.tibber.com/docs/reference API-Referenz]&lt;br /&gt;
&lt;br /&gt;
 Bei der GraphQL-Abfragesprache geht es im Wesentlichen um die Auswahl von Feldern in Objekten.&lt;br /&gt;
&lt;br /&gt;
Auf der Docs-Reference-Seite gibt es einen interessanten Hinweis:&lt;br /&gt;
  This reference is auto-generated from https://api.tibber.com&lt;br /&gt;
&lt;br /&gt;
Es koennte als eine URL geben, die einem die aktuelle &#039;&#039;&#039;Tibber GraphQL Schema Reference&#039;&#039;&#039; direkt zur Auswertung ausgibt. Der Umweg ueber das Parsen der HTML-Ausgabe ginge zur Not natuerlich auch.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objekt Typen==&lt;br /&gt;
&lt;br /&gt;
Die Begriffsdifferenzierung Objekt und Object Types ist mir derzeit noch nicht klar. Ich werde wohl um die allgemeine [https://github.com/graphql/graphql-spec Spec von GraphQL] nicht herumkommen:-(&lt;br /&gt;
&lt;br /&gt;
Bei &#039;Home&#039; wird von einem Objekttyp gesprochen. Klar ist, man hat Objekte, die mit dem Schluesselwort type eingeleitet und dann beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 Einige Felder sind Skalare (Boolesche Werte, Zeichenfolgen, Ganzzahlen, Gleitkommazahlen), waehrend andere Objekttypen sind (Adresse, LegalEntity). Eine GraphQL-Abfrage muss vollstaendig auf Skalarebene sein. D.h. in einer Abfrage duerfen die abgefragten Felder keine Objekte (keine weiteren Objekttypen) sein, sondern hierarchisch heruntergebrochen auf weitere Inhalte bis es Skalare sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
This query is valid:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
This is not:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner   &amp;lt;- hier ist der Fehler. Dies ist kein Skalar!&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
The owner field is an object type and you would need to specify which scalar fields you would like returned:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner{&lt;br /&gt;
            name&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==query Datenabfrage (read)==&lt;br /&gt;
&lt;br /&gt;
query-&amp;gt;viewer&lt;br /&gt;
&lt;br /&gt;
Zuerst mit homes die Zaehlerplatz-iD holen und dan mit home (id: &amp;quot;&amp;lt;ID&amp;gt;&amp;quot;) nur die data dazu holen.&lt;br /&gt;
&lt;br /&gt;
Hello World;-)&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    login&lt;br /&gt;
    name&lt;br /&gt;
    userId&lt;br /&gt;
    accountType&lt;br /&gt;
    home (id: &amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;) {&lt;br /&gt;
      id&lt;br /&gt;
      features {&lt;br /&gt;
        realTimeConsumptionEnabled&lt;br /&gt;
      }&lt;br /&gt;
      subscriptions {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      currentSubscription {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      meteringPointData {&lt;br /&gt;
        consumptionEan&lt;br /&gt;
        gridCompany&lt;br /&gt;
        gridAreaCode&lt;br /&gt;
        priceAreaCode&lt;br /&gt;
        productionEan&lt;br /&gt;
        energyTaxType&lt;br /&gt;
        vatType&lt;br /&gt;
        estimatedAnnualConsumption&lt;br /&gt;
      }&lt;br /&gt;
      owner {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      mainFuseSize&lt;br /&gt;
    }&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant fand ich z.B.&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;priceAreaCode&amp;quot;: &amp;quot;Amprion&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;estimatedAnnualConsumption&amp;quot;: 500&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;mainFuseSize&amp;quot;: null&lt;br /&gt;
&lt;br /&gt;
und was da noch alles an Daten einzutragen ist.&lt;br /&gt;
 size: Int&lt;br /&gt;
 The size of the home in square meters&lt;br /&gt;
&lt;br /&gt;
==mutation Datenveraenderung (write)==&lt;br /&gt;
&lt;br /&gt;
Mutationen gibt es nur 3, wobei die erste bei mir entfaellt.&lt;br /&gt;
&lt;br /&gt;
 sendMeterReading(input: MeterReadingInput!): MeterReadingResponse!&lt;br /&gt;
 Send meter reading for home (only available for Norwegian users) &lt;br /&gt;
 &lt;br /&gt;
 updateHome(input: UpdateHomeInput!): Home!&lt;br /&gt;
 Update home information&lt;br /&gt;
 &lt;br /&gt;
 sendPushNotification(input: PushNotificationInput!): PushNotificationResponse!&lt;br /&gt;
 Send notification to Tibber app on registered devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==subscription==&lt;br /&gt;
&lt;br /&gt;
Subscription hat nur 2 Felder, wobei dauerhaft nur das erste interessant ist.&lt;br /&gt;
&lt;br /&gt;
 liveMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to real-time measurement stream from Pulse or Watty device&lt;br /&gt;
&lt;br /&gt;
 testMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to test stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
subscription{&lt;br /&gt;
  liveMeasurement(homeId: &amp;quot;&amp;lt;homeID&amp;gt;&amp;quot;){&lt;br /&gt;
    timestamp&lt;br /&gt;
    power&lt;br /&gt;
    lastMeterConsumption&lt;br /&gt;
    accumulatedConsumption&lt;br /&gt;
    accumulatedProduction&lt;br /&gt;
    accumulatedConsumptionLastHour&lt;br /&gt;
    accumulatedProductionLastHour&lt;br /&gt;
    accumulatedCost&lt;br /&gt;
    accumulatedReward&lt;br /&gt;
    currency&lt;br /&gt;
    minPower&lt;br /&gt;
    averagePower&lt;br /&gt;
    maxPower&lt;br /&gt;
    powerProduction&lt;br /&gt;
    powerReactive&lt;br /&gt;
    powerProductionReactive&lt;br /&gt;
    minPowerProduction&lt;br /&gt;
    maxPowerProduction&lt;br /&gt;
    lastMeterProduction&lt;br /&gt;
    powerFactor&lt;br /&gt;
    voltagePhase1&lt;br /&gt;
    voltagePhase2&lt;br /&gt;
    voltagePhase3&lt;br /&gt;
    currentL1&lt;br /&gt;
    currentL2&lt;br /&gt;
    currentL3&lt;br /&gt;
    signalStrength&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==fragment==&lt;br /&gt;
&lt;br /&gt;
?&lt;br /&gt;
&lt;br /&gt;
Dazu habe ich bisher keine weiteren Infos gefunden.&lt;br /&gt;
&lt;br /&gt;
=Programmierung=&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung steht bei mir hier die Client-Programmierung im Vordergrund.&lt;br /&gt;
&lt;br /&gt;
==Query/Mutation==&lt;br /&gt;
&lt;br /&gt;
Die Abfragen und Aenderungen zu programmieren sind schnell erledigt.&lt;br /&gt;
&lt;br /&gt;
Der Einstieg auf der Konsole gelingt mit curl recht gut. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
 curl \&lt;br /&gt;
-H &amp;quot;Authorization: Bearer xxxxx-yyyyyy_dhfakjebnrqauvhdjhfaklhdf \&lt;br /&gt;
-H &amp;quot;Content-Type: application/json&amp;quot; \&lt;br /&gt;
-X POST \&lt;br /&gt;
-d  &#039;mutation{ sendMeterReading(input:{homeId:&amp;quot;some home id&amp;quot;, reading: 123}){time reading  }}&#039; https://api.tibber.com/v1-beta/gql&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Subscription==&lt;br /&gt;
&lt;br /&gt;
Die Subscriptions sind schon ein wenig anspruchsvoller zu programmieren als Query und Mutation (QuM).&lt;br /&gt;
Ist QuM eine simple http-Anfrage mit einer Antwort im JSON-Format, ist es bei den Subscriptions ein Websocket, der einmal geoeffnet in unregelmaeszigen Abstaenden JSON-Pakete liefert.&lt;br /&gt;
&lt;br /&gt;
===Anforderungen für Websocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
* Clients muessen den User-Agent-Header festlegen, wenn sie die GraphQL-API aufrufen und eine Websocket-Verbindung oeffnen. Sowohl die Plattform als auch die Treiberversion muessen angegeben werden. Z.B. Homey/10.0.0 com.tibber/1.8.3&lt;br /&gt;
* Clients müssen das Unterprotokoll graphql-transport-ws implementieren&lt;br /&gt;
* Clients müssen Jitter und exponentielles Backoff implementieren, wenn sie Anfragen an die API wiederholen&lt;br /&gt;
* Clients müssen die WebSocket-Server-URL dynamisch abfragen&lt;br /&gt;
&lt;br /&gt;
===Best Practices für die Implementierung von WebSocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
Bei der Implementierung eines Clients zum Abonnieren von Live-Daten sind einige Szenarien zu beruecksichtigen:&lt;br /&gt;
* Keine Daten aufgrund von Netzwerkproblemen: Es kann sinnvoll sein, einen Wiederverbindungsmechanismus zu implementieren, falls mehrere Minuten lang keine Daten empfangen wurden. Bei einem erneuten Versuch muss zunaechst die alte Websocket-Verbindung zerstoert und ordnungsgemaeszer Jitter und exponentielle Verzoegerung implementiert werden.&lt;br /&gt;
* Ein Echtzeitgeraet wird moeglicherweise entfernt. Es wird empfohlen, vor dem erneuten Verbinden immer zu ueberpruefen, ob home.features.realTimeConsumptionEnabled einen echten Wert hat.&lt;br /&gt;
* Eine WebSocket-Verbindung kann aufgrund eines ungueltigen Authentifizierungstokens verboten sein (z. B. weil ein Benutzer möglicherweise sein Tibber-Konto entfernt hat). In diesem Fall darf der Client es nicht mit demselben Token erneut versuchen. Stattdessen muss der Autorisierungsprozess erneut ausgeführt werden, wobei der Benutzer neue Anmeldeinformationen eingibt.&lt;br /&gt;
* Es kann zu einem Neustart der Tibber-Websocket-API kommen, was bedeutet, dass die Verbindung mit dem &amp;quot;Code 1001&amp;quot; und dem Grund &amp;quot;Going away&amp;quot; geschlossen wird. In diesem Fall kann der Client nach einer zufälligen Verzoegerung von 1 bis 60 Sekunden (Jitter angewendet) die Verbindung wiederherstellen, um zu vermeiden, dass alle Clients gleichzeitig die Verbindung wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
In PHP habe ich wenig bis gar nichts gefunden. Ich wollte mir auch einfach die Nutzung einer GraphQL-Library ersparen. Python habe ich erst einmal an den Schluss verschoben und einem Integration in FHEM den Vorzug gegeben.&lt;br /&gt;
&lt;br /&gt;
===Perl (FHEM)===&lt;br /&gt;
Hier der Code der tatsaechlich funktioniert, obwohl er an ein paar Stellen nicht so vorgeht wie ich aus den Tibber-Docs erwartet hatte.&lt;br /&gt;
&lt;br /&gt;
HINWEIS: Dies ist der &amp;quot;nackte&amp;quot; Perl-Code. In der &amp;quot;Originaldatei&amp;quot; sind noch die &amp;quot;FHEM-Escapes&amp;quot; doppelter Strichpunkt und Zeilenfolgezeichen \ enthalten. Eine Eingabe ueber die FHEM-Kommandozeile hat bei mir nicht funktioniert. Erst das direkte Einkopieren in die fhem.conf zeigte das nachfolgende, funktionierende Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
connect:cmd:.connect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	# establish connection to websocket&lt;br /&gt;
	# format must also include portnumber if a path is to be specified&lt;br /&gt;
	$hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
	&lt;br /&gt;
	# callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
	# websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
	&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onDisconnect {&lt;br /&gt;
	my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect&lt;br /&gt;
	my $timerFunction = sub() {&lt;br /&gt;
		my ($hash) = @_;&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
		&lt;br /&gt;
		# only re-connect if device is not connected&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));&lt;br /&gt;
	};&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash, $timerFunction);&lt;br /&gt;
	&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onConnectionAck:websocketData:.*connection_ack.* {&lt;br /&gt;
	#websocketData contains the string &amp;quot;connection_ack&amp;quot;&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# do not proceed if connection is lost&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device not open&amp;quot; if (!defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	my $homeId = AttrVal($name, &amp;quot;homeId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myId = AttrVal($name, &amp;quot;myId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# build the query, do it in pieces, the comma at the end caused perl errors&lt;br /&gt;
	# so we put it together in this not very elegant way&lt;br /&gt;
	my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;&lt;br /&gt;
	$json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;&lt;br /&gt;
	$json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;&lt;br /&gt;
	$json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;&lt;br /&gt;
	$json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;&lt;br /&gt;
	$json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;&lt;br /&gt;
	$json .= &#039;}}&#039;;&lt;br /&gt;
	&lt;br /&gt;
	#send the string via websocket as ASCII&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
	DevIo_SimpleWrite($hash, $json, 2);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onNextLiveMeasurement:websocketData:.*next.*payload.*data.*liveMeasurement.* {&lt;br /&gt;
	#websocketData contains next-live-measurement-data&lt;br /&gt;
	my $val = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;{}&amp;quot;);&lt;br /&gt;
	my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};&lt;br /&gt;
	&lt;br /&gt;
	my $ret = &amp;quot;got values for:\n&amp;quot;;&lt;br /&gt;
	foreach my $k (sort keys %res) {&lt;br /&gt;
		$ret .= &amp;quot;$k\n&amp;quot;;&lt;br /&gt;
		readingsBulkUpdate($hash, makeReadingName($k), $res{$k});&lt;br /&gt;
	}&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Code Analyse====&lt;br /&gt;
&lt;br /&gt;
Es gibt 5 Eintrittspunkte:&lt;br /&gt;
* connect&lt;br /&gt;
* disconnect&lt;br /&gt;
* onDisconnect&lt;br /&gt;
* onConnectionAck&lt;br /&gt;
* onNextLiveMeasurement&lt;br /&gt;
&lt;br /&gt;
Die ersten beiden legen die Befehlsfolgen für ein Start-Kommando (set cmd connect, bzw. set cmd disconnect) oder Stop-Kommando fest.&lt;br /&gt;
Die letzten 3 reagieren auf die entsprechenden Events.&lt;br /&gt;
=====connect=====&lt;br /&gt;
&lt;br /&gt;
Lexikalische Variablen mittels my, bzw. my()&lt;br /&gt;
&lt;br /&gt;
 Jede Variable, die mit our deklariert oder auch &amp;quot;einfach so&amp;quot; ohne eine Deklaration verwendet wird, wird in die Symboltabelle des jeweils aktuellen Packages aufgenommen.&lt;br /&gt;
 Deklariert man dagegen eine Variable mit dem Operator my, so wird die entsprechende Variable in einer anderen Tabelle abgelegt, auf die kein expliziter Zugriff möglich ist. &lt;br /&gt;
 Neben der Tatsache, daß my-Variablen in einer eigenen Tabelle verwaltet werden, ist von besonderer Bedeutung, daß sie nur einen recht beschränkten Gültigkeitsbereich besitzen. Eine durch my erzeugte Variable steht nur in dem aktuellen Block (definiert durch geschweifte Klammern &amp;quot;{...}&amp;quot;), der aktuellen Datei oder innerhalb eines Arguments von eval() zur Verfügung. Außerhalb davon existieren diese Variablen nicht, es ist also (im Gegensatz zu our-Variablen) auch mit Hilfe des Package-Namens dort kein Zugriff möglich&lt;br /&gt;
 Es kann in einem Package durchaus zwei Variablen gleichen Namens geben: eine in der Symboltabelle und eine, die durch einen Aufruf von my entstanden ist. In einem solchen Falle wird bei einfacher Verwendung des Bezeichners auf die my-Variable zugegriffen. &lt;br /&gt;
&lt;br /&gt;
In $defs stehen alle Defines drin. In $hash wird also der Name des des aufrufenden Devices lokal hinterlegt und danach mit DevIO_IsOpen() der Status abgefragt. &lt;br /&gt;
$defs ist ein Hash. Ein Hash ist ein anderer Namen fuer ein assoziatives Array. Also eine ungeordnete Liste von Skalaren (Zahl oder String) die ueber einen Stringwert angesprochen (Lookup) werden koennen.  &lt;br /&gt;
&lt;br /&gt;
FHEM-Spezial:&lt;br /&gt;
 In $defs findet man alle Devices, z.b. $defs{&amp;quot;Rolladen_Tuere&amp;quot;} beinhaltet das Device.&lt;br /&gt;
 my @alle = keys %defs;&lt;br /&gt;
 my @teilmenge = defInfo(&#039;TYPE=notify&#039;,&#039;NAME&#039;); liefert ein array mit allen notify devices.&lt;br /&gt;
 z.B. my @teilmenge = defInfo(&#039;NAME=Rollladen.*&#039;,&#039;NAME&#039;);&lt;br /&gt;
&lt;br /&gt;
 return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
Sollte also das Device bereits im State &amp;quot;offen&amp;quot; sein, wird die connect-Routine abgebrochen. Dann erscheint in den Readings nicht Datum/Uhrzeit des Connects sondern die Meldung &amp;quot;Device already open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
 # establish connection to websocket&lt;br /&gt;
 # format must also include portnumber if a path is to be specified&lt;br /&gt;
 $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 HASH-Operationen (Beispiel):&lt;br /&gt;
 $href ={APR =&amp;gt; 4,AUG =&amp;gt; 8}; #anonymous hash&lt;br /&gt;
 $el = $href-&amp;gt;{APR}; $el = %{$href}{APR}; #access element of hash&lt;br /&gt;
 $href2 = {%{$href1}}; #copy hash&lt;br /&gt;
 if (ref($r) eq &amp;quot;HASH&amp;quot;) {} #checks if $r points to hash&lt;br /&gt;
&lt;br /&gt;
Dem Internal &amp;quot;DeviceName&amp;quot; wird das Ergebnis von AttrVal() zugewiesen. Bei mir ergab das:&lt;br /&gt;
 DeviceName wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&lt;br /&gt;
 FHEM-Referenz:&lt;br /&gt;
 AttrVal(&amp;lt;devicename&amp;gt;,&amp;lt;attribute&amp;gt;,&amp;lt;defaultvalue&amp;gt;)&lt;br /&gt;
 Gibt das entsprechende Attribut des Gerätes zurück&lt;br /&gt;
 { Value(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { OldValue(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { time_str2num(OldTimestamp(&amp;quot;wz&amp;quot;)) }&lt;br /&gt;
 { ReadingsVal(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, &amp;quot;20&amp;quot;)+0 }&lt;br /&gt;
 { ReadingsTimestamp(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, 0)}&lt;br /&gt;
 { AttrVal(&amp;quot;wz&amp;quot;, &amp;quot;room&amp;quot;, &amp;quot;none&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Das Attribut websocketURL ist bei mir wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions. Somit erklaert sich auch der Eintrag bei DeviceName. &amp;quot;wss:echo.websocket.org:443&amp;quot; ist nur der Defaultwert.&lt;br /&gt;
&lt;br /&gt;
Man haette zum Testen auch die folgenden beiden verwenden/eintragen koennen.&lt;br /&gt;
 There are free test servers that we can use to experiment with a WebSocket client, such as:&lt;br /&gt;
&lt;br /&gt;
    Hoppscotch – wss://echo-websocket.hoppscotch.io (GUI)&lt;br /&gt;
    λ if else – wss://ws.ifelse.io (GUI)&lt;br /&gt;
&lt;br /&gt;
 Both provide a GUI for testing via a browser. The former sends a timestamp to the client every second but doesn’t respond to client messages. The latter is a standard echo service that sends a copy of each received message back to the client. Both make sense in testing.&lt;br /&gt;
&lt;br /&gt;
Da curl nicht nativ websocket-Kommunikation unterstuetzt kann man auf folgende Tool ausweichen. Auch chrome bietet ein Websocket-Client-Plugin.&lt;br /&gt;
*wssh3&lt;br /&gt;
*websocat&lt;br /&gt;
*wscat&lt;br /&gt;
&lt;br /&gt;
 # special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
&lt;br /&gt;
 # callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
 # websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion wird direkt, ohne Namen, in das/den (?) Hash unter directReadFn abgelegt. &lt;br /&gt;
Die Callback-Funktion:&lt;br /&gt;
Hier wird mit Hilfe der Funktion DevIo_SimpleRead() die Rueckmeldung des Server eingelesen. Siehe [https://wiki.fhem.de/wiki/DevIo]&lt;br /&gt;
Hinweis zum Logging in FHEM: [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#Logging]&lt;br /&gt;
Die eigentliche Aktivitaet geschieht in den sieben Zeilen am Ende der Callback-Fkt. &lt;br /&gt;
 ReadingsAge(&amp;lt;devicename&amp;gt;,&amp;lt;reading&amp;gt;,&amp;lt;defaultvalue&amp;gt;) #gibt das Alter des Readings in Sekunden zurück. &lt;br /&gt;
 AttrVal hatten wir schon.&lt;br /&gt;
 isNext bekommt seinen Wert aus dem durch einen REGEX geliefert Wert des Buffers. Beispiel ($match) = ($dirty =~ /^(.*)$/s); &lt;br /&gt;
&lt;br /&gt;
 Die Funktion readingsBeginUpdate() bereitet die Definition mit dem Hash $hash auf ein Update von Readings vor. Dies betrifft insbesondere das Setzen von Umgebungsvariablen sowie dem aktuellen Zeitstempel als Änderungszeitpunkt. Der Aufruf dieser Funktion ist notwendig um eigentliche Updates mit der Funktion readingsBulkUpdate() auf der gewünschten Definition durchführen zu können. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#readingsBeginUpdate]&lt;br /&gt;
&lt;br /&gt;
Die Funktion readingsEndUpdate() beendet den Bulk-Update Prozess durch die Funktionen readingsBeginUpdate() &amp;amp; readingsBulkUpdate() und triggert optional die entsprechenden Events sämtlicher erzeugter Readings für die Definition $hash. Desweiteren werden nachgelagerte Tasks wie bspw. die Erzeugung von User-Readings (Attribut: userReadings), sowie die Erzeugung des STATE aufgrund des Attributs stateFormat durchgeführt. Sofern $do_trigger gesetzt ist, werden alle anstehenden Events nach Abschluss getriggert.&lt;br /&gt;
&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
Das ist die eigentliche Kontaktaufnahme. &lt;br /&gt;
&lt;br /&gt;
Dies dient vermutlich dazu, bis zum ersten Empfang von Daten, das Reading websocketDate zu leeren.&lt;br /&gt;
&lt;br /&gt;
 return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss ercheint dann im Reading connect die aktuelle Zeit.&lt;br /&gt;
&lt;br /&gt;
readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=====disconnect=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
 disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erlaeuterung:&lt;br /&gt;
&lt;br /&gt;
*Die Funktion RemoveInternalTimer löscht möglicherweise noch anstehende Timer welche mit dem Übergabeparameter $arg gescheduled sind. Optional kann man zusätzlich die Suche auf eine bestimmte Funktion $functionName weiter einschränken. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#RemoveInternalTimer]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von $hash ein und gibt diese zurück. [https://wiki.fhem.de/wiki/DevIo#DevIo_SimpleRead()]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition $hash. [https://wiki.fhem.de/wiki/DevIo#DevIo_CloseDev()]&lt;br /&gt;
&lt;br /&gt;
*POSIX:: [https://metacpan.org/pod/POSIX#strftime]&lt;br /&gt;
&lt;br /&gt;
=====onDisconnect=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=====onConnectionAck=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====onNextLiveMeasurement=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===JavaScript===&lt;br /&gt;
Zum Eingewoehnen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;ws://echo.websocket.org&#039;);&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung hergestellt.&#039;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
    socket.send(&#039;Hallo Server!&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&#039;Nachricht vom Server erhalten:&#039;, event.data);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und jetzt das Gerippe fuer Tibber mit Haut versehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
const WebSocket = require(&#039;ws&#039;);&lt;br /&gt;
&lt;br /&gt;
const query = `subscription {&lt;br /&gt;
    liveMeasurement(homeId: &amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;) {&lt;br /&gt;
      timestamp&lt;br /&gt;
      power&lt;br /&gt;
      accumulatedConsumption&lt;br /&gt;
      minPower&lt;br /&gt;
      maxPower&lt;br /&gt;
      averagePower&lt;br /&gt;
      voltagePhase1&lt;br /&gt;
      currentL1&lt;br /&gt;
      accumulatedCost&lt;br /&gt;
      currency&lt;br /&gt;
   } &lt;br /&gt;
}`;&lt;br /&gt;
&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&#039;,[&#039;graphql-transport-ws&#039;], { &lt;br /&gt;
        headers: {&lt;br /&gt;
                &#039;Content-Type&#039;: &#039;application/json&#039;,&lt;br /&gt;
                &#039;User-Agent&#039;: &#039;NodeJS tibberview&#039;,&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&amp;quot;WebSocket-Verbindung hergestellt.&amp;quot;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
   const connectionQuery = {&lt;br /&gt;
      type: &amp;quot;connection_init&amp;quot;,&lt;br /&gt;
      payload: {&lt;br /&gt;
          token: &amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;,&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    socket.send(JSON.stringify(connectionQuery));&lt;br /&gt;
    console.log(&amp;quot;Verbindung zum &#039;Tibber feed&#039; hergestellt.&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&amp;quot;Nachricht vom Server erhalten:&amp;quot;, event.data);&lt;br /&gt;
    const msg = JSON.parse(event.data);&lt;br /&gt;
&lt;br /&gt;
    if (msg.type === &amp;quot;connection_ack&amp;quot;) {&lt;br /&gt;
      console.log(&amp;quot;Tibber ACK empfangen: &amp;quot;, msg);&lt;br /&gt;
&lt;br /&gt;
      const realtimeDataQuery = {&lt;br /&gt;
        id: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&lt;br /&gt;
        type: &amp;quot;subscribe&amp;quot;,&lt;br /&gt;
        payload: { query }&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
      socket.send(JSON.stringify(realtimeDataQuery));&lt;br /&gt;
    }&lt;br /&gt;
    else if (msg.type === &amp;quot;next&amp;quot;) {&lt;br /&gt;
      if (!msg.payload.data) {&lt;br /&gt;
        console.log(&amp;quot;⚠  Nachricht hat keinen Inhalt.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      const data = msg.payload.data.liveMeasurement;&lt;br /&gt;
      socket.emit(&amp;quot;data&amp;quot;, data);&lt;br /&gt;
    } else {&lt;br /&gt;
      console.log(&amp;quot;Nachrichteninhalt: &amp;quot;, msg)&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm erzeugte folgende Ausgabe:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
worker:/mnt/1.5T_RAID/Programme/JavaScript/WebSocket # node tibberclient_demo.js &lt;br /&gt;
WebSocket-Verbindung hergestellt.&lt;br /&gt;
Verbindung zum &#039;Tibber feed&#039; hergestellt.&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;type&amp;quot;:&amp;quot;connection_ack&amp;quot;}&lt;br /&gt;
Tibber ACK empfangen:  { type: &#039;connection_ack&#039; }&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:48.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.6,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:51.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:52.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:230,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:53.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.2,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:54.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.1,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:55.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:230.2,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:56.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:57.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.9,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:58.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:59.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.4,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:00.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.7,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:01.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.6,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
^C&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An der Stelle mache ich jetzt einen Ausflug in PHP.&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
Nachdem der Websocket-Kontakt mit JavaScript funktioniert hat, kommt jetzt ein Serverprogramm was folgendes tun soll.&lt;br /&gt;
&lt;br /&gt;
*Abfrage der Basisdaten&lt;br /&gt;
*Objekt Types Builder&lt;br /&gt;
*Mutation&lt;br /&gt;
*Websocket Subsrciption&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===Python===&lt;br /&gt;
&lt;br /&gt;
Quelle: https://github.com/Danielhiversen/pyTibber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Client-Beispiel mit eingesetzem DEMO_TOKEN und user_agent&lt;br /&gt;
Obwohl DEMO_TOKEN in tibber.py definiert ist, wird es nicht uebernommen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import tibber&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def start():&lt;br /&gt;
  tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
  await tibber_connection.update_info()&lt;br /&gt;
  print(tibber_connection.name)&lt;br /&gt;
&lt;br /&gt;
  home = tibber_connection.get_homes()[0]&lt;br /&gt;
  await home.fetch_consumption_data()&lt;br /&gt;
  await home.update_info()&lt;br /&gt;
  print(home.address1)&lt;br /&gt;
&lt;br /&gt;
  await home.update_price_info()&lt;br /&gt;
  print(home.current_price_info)&lt;br /&gt;
  &lt;br /&gt;
  # await tibber_connection.close_connection()&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(start())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
import aiohttp&lt;br /&gt;
import tibber&lt;br /&gt;
&lt;br /&gt;
def _callback(pkg):&lt;br /&gt;
    print(pkg)&lt;br /&gt;
    data = pkg.get(&amp;quot;data&amp;quot;)&lt;br /&gt;
    if data is None:&lt;br /&gt;
        return&lt;br /&gt;
    print(data.get(&amp;quot;liveMeasurement&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def run():&lt;br /&gt;
    async with aiohttp.ClientSession() as session:&lt;br /&gt;
        tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, websession=session, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
        await tibber_connection.update_info()&lt;br /&gt;
    home = tibber_connection.get_homes()[0]&lt;br /&gt;
    await home.rt_subscribe(_callback)    &lt;br /&gt;
&lt;br /&gt;
    while True:&lt;br /&gt;
      await asyncio.sleep(10)&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(run())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4846</id>
		<title>Tibber Pulse (API)</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4846"/>
		<updated>2026-04-10T06:32:40Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Query/Mutation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Da ich aktuell vom Energieversorger einen zweiten Hausanschluss (40kW :-() gelegt bekommen habe und ich für die Wallboxen zur Foerderung einen 100% Oekostrom haben musste, habe ich kurzerhand einen Vertrag mit Tibber (mal zum Testen) abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
=Installation=&lt;br /&gt;
Pulse IR und Bridge&lt;br /&gt;
[[Datei:Tibber Pulse IR und Bridge.png|mini|Tibber Pulse IR und Bridge]]&lt;br /&gt;
Eigentlich verlief die Installation problemlos. Fuer mich war allerdings der Assistent in der Tibber-App zu kindisch. Das ist eher was für Apple-User. Ich haette mir an der ein oder anderen Stelle mehr Hardcore-Infos gewuenscht. Da der mehrstufige Prozess (WLAN-Bridge-Pulse-Zaehler) doch einige Solperfallen enthaelt, ist es fuer mich eher frustrierend jedesmal bei einem Fehler zurueck auf Los geschickt zu werden. Das geht definitiv besser. Ist aber wieder ein Beispiel, dass die Informatiker-Jobs heute nur noch durch 5-jaehrige Schimpansen (ungelernte ;-)) besetzt werden.&lt;br /&gt;
&lt;br /&gt;
Bei mir gab es letztendlich nur einen gravierenden Fehler. Von den mitgelieferten Akkus des Pulse war einer defekt. Bei mir sind das zwei AA-Zellen auf LiBasis! Ein Nachladen mit einem Lithium-1.5V-Lader hat keinen Erfolg gebracht. Erst der Austausch gegen 2 neue Zellen war erfolgreich.&lt;br /&gt;
&lt;br /&gt;
=App=&lt;br /&gt;
[[Datei:TibberAppIcon.png|mini|TibberAppIcon]]&lt;br /&gt;
Die App find ich ziemlich verquer. Sobald Rechnungsanschrift und Installationsorte unterschiedlich sind, geht das Chaos los. Auch der Support tut sich dann schwer. Ich traue mich gar nicht so richtig weitere Vertraege abzuschlieszen.&lt;br /&gt;
Alles ueber die App machen zu wollen/muessen ist ... Das gehoert fuer mich ins WebUserPortal.&lt;br /&gt;
&lt;br /&gt;
Was echt nervt ist, dass sich gefühlt jeden Monat das LookandFeel aendert. Schon die dritte oder vierte Designaenderung des Homescreens (Zuhause).&lt;br /&gt;
&lt;br /&gt;
=API=&lt;br /&gt;
Da die App eher suboptimal ist, habe ich recht zuegig damit angefangen mir die notwendigen Infos ueber die API auf eine Infoseite im Intranet zu holen. Ein Integration in FHEM und/oder HA steht noch aus.&lt;br /&gt;
&lt;br /&gt;
=Erste Schritte=&lt;br /&gt;
&lt;br /&gt;
Uebersicht [https://developer.tibber.com/docs/overview]&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung: Die Plattform wird über GraphQL verfügbar gemacht, eine Technologie, die von Facebook konzipiert und entwickelt wurde.&lt;br /&gt;
&lt;br /&gt;
==Was ist GraphQL?==&lt;br /&gt;
&lt;br /&gt;
[https://de.wikipedia.org/wiki/GraphQL Wikipedia]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/graphql GraphQL]&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zu GraphQL===&lt;br /&gt;
&lt;br /&gt;
GraphQL ist eine von Facebook entwickelte Abfragesprache und Laufzeitumgebung für APIs (Application Programming Interfaces). Im Gegensatz zu traditionellen RESTful APIs, bei denen die Client-Anwendung verschiedene Endpunkte aufruft, um Daten abzurufen oder zu manipulieren, ermöglicht GraphQL dem Client, genau die Daten abzurufen, die er benötigt, indem er eine einzige Abfrage an den Server sendet.&lt;br /&gt;
&lt;br /&gt;
Hier sind einige Hauptmerkmale von GraphQL:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;Declarative Datenabfrage&#039;&#039;&#039;: Der Client spezifiziert genau, welche Daten er vom Server erhalten möchte, indem er eine GraphQL-Abfrage definiert, die die benötigten Felder und deren Struktur beschreibt. Dadurch kann der Client genau die Daten erhalten, die er benötigt, ohne Overfetching oder Underfetching.&lt;br /&gt;
    &#039;&#039;&#039;Typsystem&#039;&#039;&#039;: GraphQL definiert ein Typsystem, das die Struktur und den Typ der Daten beschreibt, die von der API zurückgegeben werden können. Dies ermöglicht eine strikte Typisierung und verbesserte Dokumentation.&lt;br /&gt;
    &#039;&#039;&#039;Single-Endpoint-Architektur&#039;&#039;&#039;: Im Gegensatz zu RESTful APIs, die oft mehrere Endpunkte für verschiedene Ressourcentypen haben, hat eine GraphQL-API normalerweise nur einen Endpunkt, über den alle Abfragen und Mutationen gesendet werden.&lt;br /&gt;
    &#039;&#039;&#039;Echtzeitfähigkeit&#039;&#039;&#039;: GraphQL unterstützt auch Abonnements, die es ermöglichen, Daten in Echtzeit zu empfangen, wenn sich etwas ändert. Dies ist besonders nützlich für Anwendungsfälle wie Live-Chats, Benachrichtigungen und Echtzeit-Dashboard-Aktualisierungen.&lt;br /&gt;
    &#039;&#039;&#039;Entwicklungserfahrung&#039;&#039;&#039;: GraphQL bietet eine verbesserte Entwicklererfahrung, da Clients genau die Daten erhalten können, die sie benötigen, und Entwickler APIs schneller entwickeln können, ohne sich um Versionierung oder unerwartete Seiteneffekte durch Änderungen an der Backend-API kümmern zu müssen.&lt;br /&gt;
&lt;br /&gt;
Insgesamt bietet GraphQL eine flexible, effiziente und moderne Möglichkeit, APIs zu entwerfen und zu verwenden, die den Anforderungen moderner Anwendungen besser gerecht wird als traditionelle RESTful APIs.&lt;br /&gt;
&lt;br /&gt;
Auf der Tibber-Seite heiszt es:&lt;br /&gt;
&lt;br /&gt;
 The GraphQL query language is basically about selecting fields on objects.&lt;br /&gt;
&lt;br /&gt;
Was dies bedeutet kommt weiter unten. Weiter heiszt es auf der Tibber Seite.&lt;br /&gt;
&lt;br /&gt;
    GraphQL ist eine Abfragesprache für Ihre(? unsere) API und eine serverseitige Laufzeitumgebung zum Ausführen von Abfragen mithilfe eines Typsystems, das Sie für Ihre Daten definieren. GraphQL ist nicht an eine bestimmte Datenbank oder Speicher-Engine gebunden und wird stattdessen durch Ihren vorhandenen Code und Ihre Daten unterstützt.&lt;br /&gt;
&lt;br /&gt;
Mit der Uebersetzung von Google hadere ich noch. Der Satz kommt vermutlich ursrpruenglich von Facebook und wurde von Tibber 1:1 uebernommen.&lt;br /&gt;
&lt;br /&gt;
Der naechste Satz gibt schon mehr Aufschluss:&lt;br /&gt;
 Tibber hat sich für GraphQL als API entschieden, weil es unseren Integratoren und uns selbst Flexibilitaet bietet. Die Moeglichkeit, die gewuenschten Daten genau zu definieren, macht die Nutzung wesentlich einfacher als herkoemmliche [https://www.ibm.com/de-de/topics/rest-apis REST-basierte APIs]. Es ist auch sehr schoen, dass die serverseitige Implementierung von GraphQL sehr gut mit unserer Microservice-Architektur zusammenspielt :-)&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg nutze ich den [https://developer.tibber.com/explorer Api Explorer im TibberDev]. Nachdem man sich dort einen Personal Token angelegt hat, kann&#039;s direkt losgehen. Der [https://developer.tibber.com/explorer GraphiQL] hat auch eine Autovervollstaendigung. Sehr praktisch.&lt;br /&gt;
[[Datei:TibberDevAccount.png|mini|TibberDevAccount]]&lt;br /&gt;
Die Root-Types sind &lt;br /&gt;
*query: Query&lt;br /&gt;
*mutation: RootMutation&lt;br /&gt;
*subscription: RootSubscription&lt;br /&gt;
*fragment feht irgendwie in der Doc &lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist recht simpel und das Doc in knapp einer Stunde durchprobiert. So dass ich dann schnell an dem Punkt angelangt bin, dass ich ein Programm (PHP, Python oder JavaScript) brauche welches die fuer mich interessanten Daten in meine MySQL-DB uebertraegt. Ob ich zuerst anzeige und dann speichere, also sozusagen ueber die Datenbank puffere, oder umgekehrt muss ich noch festlegen.&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zum Subprotokoll===&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; ist ein WebSocket-Subprotokoll, das speziell für die Kommunikation zwischen Client und Server in GraphQL-Anwendungen entwickelt wurde. Es basiert auf dem WebSocket-Protokoll, das eine bidirektionale, persistente Verbindung zwischen einem Client und einem Server ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Im Kontext von GraphQL wird &amp;quot;graphql-transport-ws&amp;quot; verwendet, um GraphQL-Abfragen, Mutationen und Abonnements über WebSocket-Verbindungen zu übertragen. Im Gegensatz zum traditionellen HTTP-Protokoll, das eine separate Anfrage-Antwort-Struktur hat, ermöglicht WebSocket eine kontinuierliche Kommunikation zwischen Client und Server, was besonders nützlich ist, wenn Echtzeit-Aktualisierungen benötigt werden, wie bei Abonnements in GraphQL.&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; definiert spezifische Nachrichtenformate und Handshake-Verfahren, die für die GraphQL-Kommunikation über WebSockets erforderlich sind. Durch die Verwendung dieses Subprotokolls können Entwickler GraphQL-Anwendungen erstellen, die von den Echtzeitfähigkeiten von WebSocket profitieren, ohne auf die Vorteile und Flexibilität von GraphQL verzichten zu müssen.&lt;br /&gt;
&lt;br /&gt;
==Mehrfacher Hinweis Subprotokolwechsel Dezember 2022==&lt;br /&gt;
&lt;br /&gt;
 Die TibberAPI unterstuetzte zunaechst GraphQL-Websocket-Subscriptions mithilfe des Unterprotokolls graphql-ws. Diese Bibliothek wird/wurde jedoch archiviert und nicht mehr gepflegt. Daher wurde am 31. Maerz 2022 die Unterstützung für das Unterprotokoll graphql-transport-ws hinzugefuegt.&lt;br /&gt;
&lt;br /&gt;
 Die Unterstuetzung für das alte Protokoll wird im Dezember 2022 entfernt. Gleichzeitig aendert sich auch die URL für die Verbindung zum Websocket-Server. Zuvor konnte die Verbindung unter &#039;&#039;&#039;wss://api.tibber.com/v1-beta/gql/subscriptions&#039;&#039;&#039; hergestellt werden. Nach der Aenderung muss die URL ueber GraphQL abgefragt werden (siehe Beispiel unten) und der Client muss das Unterprotokoll graphql-transport-ws unterstuetzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight code=xml&amp;gt;&lt;br /&gt;
Frage:&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;websocketSubscriptionUrl&amp;quot;: &amp;quot;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Anfragenbegrenzung==&lt;br /&gt;
 Zum Schutz der API gilt eine Ratenbegrenzung von 100 Anfragen in 5 Minuten pro IP-Adresse. Beachten Sie, dass die Preise einmal täglich am Nachmittag berechnet werden (für Norwegen und Schweden sind sie zunächst vorläufig und werden später möglicherweise mit geringfügigen Änderungen nach Bestätigung der Wechselkurse endgültig festgelegt). Sie können „priceInfo.today“ und „priceInfo.tomorrow“ verwenden, um sie im Voraus abzurufen, anstatt nur „priceInfo.current“ für die aktuelle Stunde zu verwenden.&lt;br /&gt;
&lt;br /&gt;
=In medias res=&lt;br /&gt;
Zum Starten empfiehlt die Tibber-API-Doc, die fundamentalen Konzepte von GraphQL, die Kommunikation mit der API und den Explorer.&lt;br /&gt;
&lt;br /&gt;
Um die nachfolgenden Beispiele schnell zu verstehen, hier der Link zur [https://developer.tibber.com/docs/reference API-Referenz]&lt;br /&gt;
&lt;br /&gt;
 Bei der GraphQL-Abfragesprache geht es im Wesentlichen um die Auswahl von Feldern in Objekten.&lt;br /&gt;
&lt;br /&gt;
Auf der Docs-Reference-Seite gibt es einen interessanten Hinweis:&lt;br /&gt;
  This reference is auto-generated from https://api.tibber.com&lt;br /&gt;
&lt;br /&gt;
Es koennte als eine URL geben, die einem die aktuelle &#039;&#039;&#039;Tibber GraphQL Schema Reference&#039;&#039;&#039; direkt zur Auswertung ausgibt. Der Umweg ueber das Parsen der HTML-Ausgabe ginge zur Not natuerlich auch.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objekt Typen==&lt;br /&gt;
&lt;br /&gt;
Die Begriffsdifferenzierung Objekt und Object Types ist mir derzeit noch nicht klar. Ich werde wohl um die allgemeine [https://github.com/graphql/graphql-spec Spec von GraphQL] nicht herumkommen:-(&lt;br /&gt;
&lt;br /&gt;
Bei &#039;Home&#039; wird von einem Objekttyp gesprochen. Klar ist, man hat Objekte, die mit dem Schluesselwort type eingeleitet und dann beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 Einige Felder sind Skalare (Boolesche Werte, Zeichenfolgen, Ganzzahlen, Gleitkommazahlen), waehrend andere Objekttypen sind (Adresse, LegalEntity). Eine GraphQL-Abfrage muss vollstaendig auf Skalarebene sein. D.h. in einer Abfrage duerfen die abgefragten Felder keine Objekte (keine weiteren Objekttypen) sein, sondern hierarchisch heruntergebrochen auf weitere Inhalte bis es Skalare sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
This query is valid:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
This is not:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner   &amp;lt;- hier ist der Fehler. Dies ist kein Skalar!&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
The owner field is an object type and you would need to specify which scalar fields you would like returned:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner{&lt;br /&gt;
            name&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==query Datenabfrage (read)==&lt;br /&gt;
&lt;br /&gt;
query-&amp;gt;viewer&lt;br /&gt;
&lt;br /&gt;
Zuerst mit homes die Zaehlerplatz-iD holen und dan mit home (id: &amp;quot;&amp;lt;ID&amp;gt;&amp;quot;) nur die data dazu holen.&lt;br /&gt;
&lt;br /&gt;
Hello World;-)&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    login&lt;br /&gt;
    name&lt;br /&gt;
    userId&lt;br /&gt;
    accountType&lt;br /&gt;
    home (id: &amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;) {&lt;br /&gt;
      id&lt;br /&gt;
      features {&lt;br /&gt;
        realTimeConsumptionEnabled&lt;br /&gt;
      }&lt;br /&gt;
      subscriptions {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      currentSubscription {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      meteringPointData {&lt;br /&gt;
        consumptionEan&lt;br /&gt;
        gridCompany&lt;br /&gt;
        gridAreaCode&lt;br /&gt;
        priceAreaCode&lt;br /&gt;
        productionEan&lt;br /&gt;
        energyTaxType&lt;br /&gt;
        vatType&lt;br /&gt;
        estimatedAnnualConsumption&lt;br /&gt;
      }&lt;br /&gt;
      owner {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      mainFuseSize&lt;br /&gt;
    }&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant fand ich z.B.&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;priceAreaCode&amp;quot;: &amp;quot;Amprion&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;estimatedAnnualConsumption&amp;quot;: 500&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;mainFuseSize&amp;quot;: null&lt;br /&gt;
&lt;br /&gt;
und was da noch alles an Daten einzutragen ist.&lt;br /&gt;
 size: Int&lt;br /&gt;
 The size of the home in square meters&lt;br /&gt;
&lt;br /&gt;
==mutation Datenveraenderung (write)==&lt;br /&gt;
&lt;br /&gt;
Mutationen gibt es nur 3, wobei die erste bei mir entfaellt.&lt;br /&gt;
&lt;br /&gt;
 sendMeterReading(input: MeterReadingInput!): MeterReadingResponse!&lt;br /&gt;
 Send meter reading for home (only available for Norwegian users) &lt;br /&gt;
 &lt;br /&gt;
 updateHome(input: UpdateHomeInput!): Home!&lt;br /&gt;
 Update home information&lt;br /&gt;
 &lt;br /&gt;
 sendPushNotification(input: PushNotificationInput!): PushNotificationResponse!&lt;br /&gt;
 Send notification to Tibber app on registered devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==subscription==&lt;br /&gt;
&lt;br /&gt;
Subscription hat nur 2 Felder, wobei dauerhaft nur das erste interessant ist.&lt;br /&gt;
&lt;br /&gt;
 liveMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to real-time measurement stream from Pulse or Watty device&lt;br /&gt;
&lt;br /&gt;
 testMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to test stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
subscription{&lt;br /&gt;
  liveMeasurement(homeId: &amp;quot;&amp;lt;homeID&amp;gt;&amp;quot;){&lt;br /&gt;
    timestamp&lt;br /&gt;
    power&lt;br /&gt;
    lastMeterConsumption&lt;br /&gt;
    accumulatedConsumption&lt;br /&gt;
    accumulatedProduction&lt;br /&gt;
    accumulatedConsumptionLastHour&lt;br /&gt;
    accumulatedProductionLastHour&lt;br /&gt;
    accumulatedCost&lt;br /&gt;
    accumulatedReward&lt;br /&gt;
    currency&lt;br /&gt;
    minPower&lt;br /&gt;
    averagePower&lt;br /&gt;
    maxPower&lt;br /&gt;
    powerProduction&lt;br /&gt;
    powerReactive&lt;br /&gt;
    powerProductionReactive&lt;br /&gt;
    minPowerProduction&lt;br /&gt;
    maxPowerProduction&lt;br /&gt;
    lastMeterProduction&lt;br /&gt;
    powerFactor&lt;br /&gt;
    voltagePhase1&lt;br /&gt;
    voltagePhase2&lt;br /&gt;
    voltagePhase3&lt;br /&gt;
    currentL1&lt;br /&gt;
    currentL2&lt;br /&gt;
    currentL3&lt;br /&gt;
    signalStrength&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==fragment==&lt;br /&gt;
&lt;br /&gt;
?&lt;br /&gt;
&lt;br /&gt;
Dazu habe ich bisher keine weiteren Infos gefunden.&lt;br /&gt;
&lt;br /&gt;
=Programmierung=&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung steht bei mir hier die Client-Programmierung im Vordergrund.&lt;br /&gt;
&lt;br /&gt;
==Query/Mutation==&lt;br /&gt;
&lt;br /&gt;
Die Abfragen und Aenderungen zu programmieren sind schnell erledigt.&lt;br /&gt;
&lt;br /&gt;
Der Einstieg auf der Konsole gelingt mit curl recht gut. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
 curl \&lt;br /&gt;
-H &amp;quot;Authorization: Bearer xxxxx-yyyyyy_dhfakjebnrqauvhdjhfaklhdf \&lt;br /&gt;
-H &amp;quot;Content-Type: application/json&amp;quot; \&lt;br /&gt;
-X POST \&lt;br /&gt;
-d  &#039;mutation{ sendMeterReading(input:{homeId:&amp;quot;some home id&amp;quot;, reading: 123}){time reading  }}&#039; https://api.tibber.com/v1-beta/gql&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Subscription==&lt;br /&gt;
&lt;br /&gt;
Die Subscriptions sind schon ein wenig anspruchsvoller zu programmieren als Query und Mutation (QuM).&lt;br /&gt;
Ist QuM eine simple http-Anfrage mit einer Antwort im JSON-Format, ist es bei den Subscriptions ein Websocket, der einmal geoeffnet in unregelmaeszigen Abstaenden JSON-Pakete liefert.&lt;br /&gt;
&lt;br /&gt;
===Anforderungen für Websocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
* Clients muessen den User-Agent-Header festlegen, wenn sie die GraphQL-API aufrufen und eine Websocket-Verbindung oeffnen. Sowohl die Plattform als auch die Treiberversion muessen angegeben werden. Z.B. Homey/10.0.0 com.tibber/1.8.3&lt;br /&gt;
* Clients müssen das Unterprotokoll graphql-transport-ws implementieren&lt;br /&gt;
* Clients müssen Jitter und exponentielles Backoff implementieren, wenn sie Anfragen an die API wiederholen&lt;br /&gt;
* Clients müssen die WebSocket-Server-URL dynamisch abfragen&lt;br /&gt;
&lt;br /&gt;
===Best Practices für die Implementierung von WebSocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
Bei der Implementierung eines Clients zum Abonnieren von Live-Daten sind einige Szenarien zu beruecksichtigen:&lt;br /&gt;
* Keine Daten aufgrund von Netzwerkproblemen: Es kann sinnvoll sein, einen Wiederverbindungsmechanismus zu implementieren, falls mehrere Minuten lang keine Daten empfangen wurden. Bei einem erneuten Versuch muss zunaechst die alte Websocket-Verbindung zerstoert und ordnungsgemaeszer Jitter und exponentielle Verzoegerung implementiert werden.&lt;br /&gt;
* Ein Echtzeitgeraet wird moeglicherweise entfernt. Es wird empfohlen, vor dem erneuten Verbinden immer zu ueberpruefen, ob home.features.realTimeConsumptionEnabled einen echten Wert hat.&lt;br /&gt;
* Eine WebSocket-Verbindung kann aufgrund eines ungueltigen Authentifizierungstokens verboten sein (z. B. weil ein Benutzer möglicherweise sein Tibber-Konto entfernt hat). In diesem Fall darf der Client es nicht mit demselben Token erneut versuchen. Stattdessen muss der Autorisierungsprozess erneut ausgeführt werden, wobei der Benutzer neue Anmeldeinformationen eingibt.&lt;br /&gt;
* Es kann zu einem Neustart der Tibber-Websocket-API kommen, was bedeutet, dass die Verbindung mit dem &amp;quot;Code 1001&amp;quot; und dem Grund &amp;quot;Going away&amp;quot; geschlossen wird. In diesem Fall kann der Client nach einer zufälligen Verzoegerung von 1 bis 60 Sekunden (Jitter angewendet) die Verbindung wiederherstellen, um zu vermeiden, dass alle Clients gleichzeitig die Verbindung wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
In PHP habe ich wenig bis gar nichts gefunden. Ich wollte mir auch einfach die Nutzung einer GraphQL-Library ersparen. Python habe ich erst einmal an den Schluss verschoben und einem Integration in FHEM den Vorzug gegeben.&lt;br /&gt;
&lt;br /&gt;
===Perl (FHEM)===&lt;br /&gt;
Hier der Code der tatsaechlich funktioniert, obwohl er an ein paar Stellen nicht so vorgeht wie ich aus den Tibber-Docs erwartet hatte.&lt;br /&gt;
&lt;br /&gt;
HINWEIS: Dies ist der &amp;quot;nackte&amp;quot; Perl-Code. In der &amp;quot;Originaldatei&amp;quot; sind noch die &amp;quot;FHEM-Escapes&amp;quot; doppelter Strichpunkt und Zeilenfolgezeichen \ enthalten. Eine Eingabe ueber die FHEM-Kommandozeile hat bei mir nicht funktioniert. Erst das direkte Einkopieren in die fhem.conf zeigte das nachfolgende, funktionierende Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
connect:cmd:.connect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	# establish connection to websocket&lt;br /&gt;
	# format must also include portnumber if a path is to be specified&lt;br /&gt;
	$hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
	&lt;br /&gt;
	# callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
	# websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
	&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onDisconnect {&lt;br /&gt;
	my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect&lt;br /&gt;
	my $timerFunction = sub() {&lt;br /&gt;
		my ($hash) = @_;&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
		&lt;br /&gt;
		# only re-connect if device is not connected&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));&lt;br /&gt;
	};&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash, $timerFunction);&lt;br /&gt;
	&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onConnectionAck:websocketData:.*connection_ack.* {&lt;br /&gt;
	#websocketData contains the string &amp;quot;connection_ack&amp;quot;&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# do not proceed if connection is lost&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device not open&amp;quot; if (!defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	my $homeId = AttrVal($name, &amp;quot;homeId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myId = AttrVal($name, &amp;quot;myId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# build the query, do it in pieces, the comma at the end caused perl errors&lt;br /&gt;
	# so we put it together in this not very elegant way&lt;br /&gt;
	my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;&lt;br /&gt;
	$json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;&lt;br /&gt;
	$json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;&lt;br /&gt;
	$json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;&lt;br /&gt;
	$json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;&lt;br /&gt;
	$json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;&lt;br /&gt;
	$json .= &#039;}}&#039;;&lt;br /&gt;
	&lt;br /&gt;
	#send the string via websocket as ASCII&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
	DevIo_SimpleWrite($hash, $json, 2);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onNextLiveMeasurement:websocketData:.*next.*payload.*data.*liveMeasurement.* {&lt;br /&gt;
	#websocketData contains next-live-measurement-data&lt;br /&gt;
	my $val = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;{}&amp;quot;);&lt;br /&gt;
	my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};&lt;br /&gt;
	&lt;br /&gt;
	my $ret = &amp;quot;got values for:\n&amp;quot;;&lt;br /&gt;
	foreach my $k (sort keys %res) {&lt;br /&gt;
		$ret .= &amp;quot;$k\n&amp;quot;;&lt;br /&gt;
		readingsBulkUpdate($hash, makeReadingName($k), $res{$k});&lt;br /&gt;
	}&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Code Analyse====&lt;br /&gt;
&lt;br /&gt;
Es gibt 5 Eintrittspunkte:&lt;br /&gt;
* connect&lt;br /&gt;
* disconnect&lt;br /&gt;
* onDisconnect&lt;br /&gt;
* onConnectionAck&lt;br /&gt;
* onNextLiveMeasurement&lt;br /&gt;
&lt;br /&gt;
Die ersten beiden legen die Befehlsfolgen für ein Start-Kommando (set cmd connect, bzw. set cmd disconnect) oder Stop-Kommando fest.&lt;br /&gt;
Die letzten 3 reagieren auf die entsprechenden Events.&lt;br /&gt;
=====connect=====&lt;br /&gt;
&lt;br /&gt;
Lexikalische Variablen mittels my, bzw. my()&lt;br /&gt;
&lt;br /&gt;
 Jede Variable, die mit our deklariert oder auch &amp;quot;einfach so&amp;quot; ohne eine Deklaration verwendet wird, wird in die Symboltabelle des jeweils aktuellen Packages aufgenommen.&lt;br /&gt;
 Deklariert man dagegen eine Variable mit dem Operator my, so wird die entsprechende Variable in einer anderen Tabelle abgelegt, auf die kein expliziter Zugriff möglich ist. &lt;br /&gt;
 Neben der Tatsache, daß my-Variablen in einer eigenen Tabelle verwaltet werden, ist von besonderer Bedeutung, daß sie nur einen recht beschränkten Gültigkeitsbereich besitzen. Eine durch my erzeugte Variable steht nur in dem aktuellen Block (definiert durch geschweifte Klammern &amp;quot;{...}&amp;quot;), der aktuellen Datei oder innerhalb eines Arguments von eval() zur Verfügung. Außerhalb davon existieren diese Variablen nicht, es ist also (im Gegensatz zu our-Variablen) auch mit Hilfe des Package-Namens dort kein Zugriff möglich&lt;br /&gt;
 Es kann in einem Package durchaus zwei Variablen gleichen Namens geben: eine in der Symboltabelle und eine, die durch einen Aufruf von my entstanden ist. In einem solchen Falle wird bei einfacher Verwendung des Bezeichners auf die my-Variable zugegriffen. &lt;br /&gt;
&lt;br /&gt;
In $defs stehen alle Defines drin. In $hash wird also der Name des des aufrufenden Devices lokal hinterlegt und danach mit DevIO_IsOpen() der Status abgefragt. &lt;br /&gt;
$defs ist ein Hash. Ein Hash ist ein anderer Namen fuer ein assoziatives Array. Also eine ungeordnete Liste von Skalaren (Zahl oder String) die ueber einen Stringwert angesprochen (Lookup) werden koennen.  &lt;br /&gt;
&lt;br /&gt;
FHEM-Spezial:&lt;br /&gt;
 In $defs findet man alle Devices, z.b. $defs{&amp;quot;Rolladen_Tuere&amp;quot;} beinhaltet das Device.&lt;br /&gt;
 my @alle = keys %defs;&lt;br /&gt;
 my @teilmenge = defInfo(&#039;TYPE=notify&#039;,&#039;NAME&#039;); liefert ein array mit allen notify devices.&lt;br /&gt;
 z.B. my @teilmenge = defInfo(&#039;NAME=Rollladen.*&#039;,&#039;NAME&#039;);&lt;br /&gt;
&lt;br /&gt;
 return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
Sollte also das Device bereits im State &amp;quot;offen&amp;quot; sein, wird die connect-Routine abgebrochen. Dann erscheint in den Readings nicht Datum/Uhrzeit des Connects sondern die Meldung &amp;quot;Device already open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
 # establish connection to websocket&lt;br /&gt;
 # format must also include portnumber if a path is to be specified&lt;br /&gt;
 $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 HASH-Operationen (Beispiel):&lt;br /&gt;
 $href ={APR =&amp;gt; 4,AUG =&amp;gt; 8}; #anonymous hash&lt;br /&gt;
 $el = $href-&amp;gt;{APR}; $el = %{$href}{APR}; #access element of hash&lt;br /&gt;
 $href2 = {%{$href1}}; #copy hash&lt;br /&gt;
 if (ref($r) eq &amp;quot;HASH&amp;quot;) {} #checks if $r points to hash&lt;br /&gt;
&lt;br /&gt;
Dem Internal &amp;quot;DeviceName&amp;quot; wird das Ergebnis von AttrVal() zugewiesen. Bei mir ergab das:&lt;br /&gt;
 DeviceName wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&lt;br /&gt;
 FHEM-Referenz:&lt;br /&gt;
 AttrVal(&amp;lt;devicename&amp;gt;,&amp;lt;attribute&amp;gt;,&amp;lt;defaultvalue&amp;gt;)&lt;br /&gt;
 Gibt das entsprechende Attribut des Gerätes zurück&lt;br /&gt;
 { Value(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { OldValue(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { time_str2num(OldTimestamp(&amp;quot;wz&amp;quot;)) }&lt;br /&gt;
 { ReadingsVal(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, &amp;quot;20&amp;quot;)+0 }&lt;br /&gt;
 { ReadingsTimestamp(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, 0)}&lt;br /&gt;
 { AttrVal(&amp;quot;wz&amp;quot;, &amp;quot;room&amp;quot;, &amp;quot;none&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Das Attribut websocketURL ist bei mir wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions. Somit erklaert sich auch der Eintrag bei DeviceName. &amp;quot;wss:echo.websocket.org:443&amp;quot; ist nur der Defaultwert.&lt;br /&gt;
&lt;br /&gt;
Man haette zum Testen auch die folgenden beiden verwenden/eintragen koennen.&lt;br /&gt;
 There are free test servers that we can use to experiment with a WebSocket client, such as:&lt;br /&gt;
&lt;br /&gt;
    Hoppscotch – wss://echo-websocket.hoppscotch.io (GUI)&lt;br /&gt;
    λ if else – wss://ws.ifelse.io (GUI)&lt;br /&gt;
&lt;br /&gt;
 Both provide a GUI for testing via a browser. The former sends a timestamp to the client every second but doesn’t respond to client messages. The latter is a standard echo service that sends a copy of each received message back to the client. Both make sense in testing.&lt;br /&gt;
&lt;br /&gt;
Da curl nicht nativ websocket-Kommunikation unterstuetzt kann man auf folgende Tool ausweichen. Auch chrome bietet ein Websocket-Client-Plugin.&lt;br /&gt;
*wssh3&lt;br /&gt;
*websocat&lt;br /&gt;
*wscat&lt;br /&gt;
&lt;br /&gt;
 # special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
&lt;br /&gt;
 # callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
 # websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion wird direkt, ohne Namen, in das/den (?) Hash unter directReadFn abgelegt. &lt;br /&gt;
Die Callback-Funktion:&lt;br /&gt;
Hier wird mit Hilfe der Funktion DevIo_SimpleRead() die Rueckmeldung des Server eingelesen. Siehe [https://wiki.fhem.de/wiki/DevIo]&lt;br /&gt;
Hinweis zum Logging in FHEM: [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#Logging]&lt;br /&gt;
Die eigentliche Aktivitaet geschieht in den sieben Zeilen am Ende der Callback-Fkt. &lt;br /&gt;
 ReadingsAge(&amp;lt;devicename&amp;gt;,&amp;lt;reading&amp;gt;,&amp;lt;defaultvalue&amp;gt;) #gibt das Alter des Readings in Sekunden zurück. &lt;br /&gt;
 AttrVal hatten wir schon.&lt;br /&gt;
 isNext bekommt seinen Wert aus dem durch einen REGEX geliefert Wert des Buffers. Beispiel ($match) = ($dirty =~ /^(.*)$/s); &lt;br /&gt;
&lt;br /&gt;
 Die Funktion readingsBeginUpdate() bereitet die Definition mit dem Hash $hash auf ein Update von Readings vor. Dies betrifft insbesondere das Setzen von Umgebungsvariablen sowie dem aktuellen Zeitstempel als Änderungszeitpunkt. Der Aufruf dieser Funktion ist notwendig um eigentliche Updates mit der Funktion readingsBulkUpdate() auf der gewünschten Definition durchführen zu können. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#readingsBeginUpdate]&lt;br /&gt;
&lt;br /&gt;
Die Funktion readingsEndUpdate() beendet den Bulk-Update Prozess durch die Funktionen readingsBeginUpdate() &amp;amp; readingsBulkUpdate() und triggert optional die entsprechenden Events sämtlicher erzeugter Readings für die Definition $hash. Desweiteren werden nachgelagerte Tasks wie bspw. die Erzeugung von User-Readings (Attribut: userReadings), sowie die Erzeugung des STATE aufgrund des Attributs stateFormat durchgeführt. Sofern $do_trigger gesetzt ist, werden alle anstehenden Events nach Abschluss getriggert.&lt;br /&gt;
&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
Das ist die eigentliche Kontaktaufnahme. &lt;br /&gt;
&lt;br /&gt;
Dies dient vermutlich dazu, bis zum ersten Empfang von Daten, das Reading websocketDate zu leeren.&lt;br /&gt;
&lt;br /&gt;
 return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss ercheint dann im Reading connect die aktuelle Zeit.&lt;br /&gt;
&lt;br /&gt;
readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=====disconnect=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
 disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erlaeuterung:&lt;br /&gt;
&lt;br /&gt;
*Die Funktion RemoveInternalTimer löscht möglicherweise noch anstehende Timer welche mit dem Übergabeparameter $arg gescheduled sind. Optional kann man zusätzlich die Suche auf eine bestimmte Funktion $functionName weiter einschränken. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#RemoveInternalTimer]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von $hash ein und gibt diese zurück. [https://wiki.fhem.de/wiki/DevIo#DevIo_SimpleRead()]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition $hash. [https://wiki.fhem.de/wiki/DevIo#DevIo_CloseDev()]&lt;br /&gt;
&lt;br /&gt;
*POSIX:: [https://metacpan.org/pod/POSIX#strftime]&lt;br /&gt;
&lt;br /&gt;
=====onDisconnect=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=====onConnectionAck=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====onNextLiveMeasurement=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===JavaScript===&lt;br /&gt;
Zum Eingewoehnen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;ws://echo.websocket.org&#039;);&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung hergestellt.&#039;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
    socket.send(&#039;Hallo Server!&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&#039;Nachricht vom Server erhalten:&#039;, event.data);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und jetzt das Gerippe fuer Tibber mit Haut versehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
const WebSocket = require(&#039;ws&#039;);&lt;br /&gt;
&lt;br /&gt;
const query = `subscription {&lt;br /&gt;
    liveMeasurement(homeId: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;) {&lt;br /&gt;
      timestamp&lt;br /&gt;
      power&lt;br /&gt;
      accumulatedConsumption&lt;br /&gt;
      minPower&lt;br /&gt;
      maxPower&lt;br /&gt;
      averagePower&lt;br /&gt;
      voltagePhase1&lt;br /&gt;
      currentL1&lt;br /&gt;
      accumulatedCost&lt;br /&gt;
      currency&lt;br /&gt;
   } &lt;br /&gt;
}`;&lt;br /&gt;
&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&#039;,[&#039;graphql-transport-ws&#039;], { &lt;br /&gt;
        headers: {&lt;br /&gt;
                &#039;Content-Type&#039;: &#039;application/json&#039;,&lt;br /&gt;
                &#039;User-Agent&#039;: &#039;NodeJS tibberview&#039;,&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&amp;quot;WebSocket-Verbindung hergestellt.&amp;quot;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
   const connectionQuery = {&lt;br /&gt;
      type: &amp;quot;connection_init&amp;quot;,&lt;br /&gt;
      payload: {&lt;br /&gt;
          token: &amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;,&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    socket.send(JSON.stringify(connectionQuery));&lt;br /&gt;
    console.log(&amp;quot;Verbindung zum &#039;Tibber feed&#039; hergestellt.&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&amp;quot;Nachricht vom Server erhalten:&amp;quot;, event.data);&lt;br /&gt;
    const msg = JSON.parse(event.data);&lt;br /&gt;
&lt;br /&gt;
    if (msg.type === &amp;quot;connection_ack&amp;quot;) {&lt;br /&gt;
      console.log(&amp;quot;Tibber ACK empfangen: &amp;quot;, msg);&lt;br /&gt;
&lt;br /&gt;
      const realtimeDataQuery = {&lt;br /&gt;
        id: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&lt;br /&gt;
        type: &amp;quot;subscribe&amp;quot;,&lt;br /&gt;
        payload: { query }&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
      socket.send(JSON.stringify(realtimeDataQuery));&lt;br /&gt;
    }&lt;br /&gt;
    else if (msg.type === &amp;quot;next&amp;quot;) {&lt;br /&gt;
      if (!msg.payload.data) {&lt;br /&gt;
        console.log(&amp;quot;⚠  Nachricht hat keinen Inhalt.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      const data = msg.payload.data.liveMeasurement;&lt;br /&gt;
      socket.emit(&amp;quot;data&amp;quot;, data);&lt;br /&gt;
    } else {&lt;br /&gt;
      console.log(&amp;quot;Nachrichteninhalt: &amp;quot;, msg)&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm erzeugte folgende Ausgabe:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
worker:/mnt/1.5T_RAID/Programme/JavaScript/WebSocket # node tibberclient_demo.js &lt;br /&gt;
WebSocket-Verbindung hergestellt.&lt;br /&gt;
Verbindung zum &#039;Tibber feed&#039; hergestellt.&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;type&amp;quot;:&amp;quot;connection_ack&amp;quot;}&lt;br /&gt;
Tibber ACK empfangen:  { type: &#039;connection_ack&#039; }&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:48.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.6,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:51.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:52.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:230,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:53.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.2,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:54.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.1,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:55.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:230.2,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:56.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:57.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.9,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:58.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:59.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.4,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:00.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.7,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:01.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.6,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
^C&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An der Stelle mache ich jetzt einen Ausflug in PHP.&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
Nachdem der Websocket-Kontakt mit JavaScript funktioniert hat, kommt jetzt ein Serverprogramm was folgendes tun soll.&lt;br /&gt;
&lt;br /&gt;
*Abfrage der Basisdaten&lt;br /&gt;
*Objekt Types Builder&lt;br /&gt;
*Mutation&lt;br /&gt;
*Websocket Subsrciption&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===Python===&lt;br /&gt;
&lt;br /&gt;
Quelle: https://github.com/Danielhiversen/pyTibber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Client-Beispiel mit eingesetzem DEMO_TOKEN und user_agent&lt;br /&gt;
Obwohl DEMO_TOKEN in tibber.py definiert ist, wird es nicht uebernommen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import tibber&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def start():&lt;br /&gt;
  tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
  await tibber_connection.update_info()&lt;br /&gt;
  print(tibber_connection.name)&lt;br /&gt;
&lt;br /&gt;
  home = tibber_connection.get_homes()[0]&lt;br /&gt;
  await home.fetch_consumption_data()&lt;br /&gt;
  await home.update_info()&lt;br /&gt;
  print(home.address1)&lt;br /&gt;
&lt;br /&gt;
  await home.update_price_info()&lt;br /&gt;
  print(home.current_price_info)&lt;br /&gt;
  &lt;br /&gt;
  # await tibber_connection.close_connection()&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(start())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
import aiohttp&lt;br /&gt;
import tibber&lt;br /&gt;
&lt;br /&gt;
def _callback(pkg):&lt;br /&gt;
    print(pkg)&lt;br /&gt;
    data = pkg.get(&amp;quot;data&amp;quot;)&lt;br /&gt;
    if data is None:&lt;br /&gt;
        return&lt;br /&gt;
    print(data.get(&amp;quot;liveMeasurement&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def run():&lt;br /&gt;
    async with aiohttp.ClientSession() as session:&lt;br /&gt;
        tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, websession=session, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
        await tibber_connection.update_info()&lt;br /&gt;
    home = tibber_connection.get_homes()[0]&lt;br /&gt;
    await home.rt_subscribe(_callback)    &lt;br /&gt;
&lt;br /&gt;
    while True:&lt;br /&gt;
      await asyncio.sleep(10)&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(run())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4845</id>
		<title>Tibber Pulse (API)</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4845"/>
		<updated>2026-04-10T06:28:48Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* query Datenabfrage (read) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Da ich aktuell vom Energieversorger einen zweiten Hausanschluss (40kW :-() gelegt bekommen habe und ich für die Wallboxen zur Foerderung einen 100% Oekostrom haben musste, habe ich kurzerhand einen Vertrag mit Tibber (mal zum Testen) abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
=Installation=&lt;br /&gt;
Pulse IR und Bridge&lt;br /&gt;
[[Datei:Tibber Pulse IR und Bridge.png|mini|Tibber Pulse IR und Bridge]]&lt;br /&gt;
Eigentlich verlief die Installation problemlos. Fuer mich war allerdings der Assistent in der Tibber-App zu kindisch. Das ist eher was für Apple-User. Ich haette mir an der ein oder anderen Stelle mehr Hardcore-Infos gewuenscht. Da der mehrstufige Prozess (WLAN-Bridge-Pulse-Zaehler) doch einige Solperfallen enthaelt, ist es fuer mich eher frustrierend jedesmal bei einem Fehler zurueck auf Los geschickt zu werden. Das geht definitiv besser. Ist aber wieder ein Beispiel, dass die Informatiker-Jobs heute nur noch durch 5-jaehrige Schimpansen (ungelernte ;-)) besetzt werden.&lt;br /&gt;
&lt;br /&gt;
Bei mir gab es letztendlich nur einen gravierenden Fehler. Von den mitgelieferten Akkus des Pulse war einer defekt. Bei mir sind das zwei AA-Zellen auf LiBasis! Ein Nachladen mit einem Lithium-1.5V-Lader hat keinen Erfolg gebracht. Erst der Austausch gegen 2 neue Zellen war erfolgreich.&lt;br /&gt;
&lt;br /&gt;
=App=&lt;br /&gt;
[[Datei:TibberAppIcon.png|mini|TibberAppIcon]]&lt;br /&gt;
Die App find ich ziemlich verquer. Sobald Rechnungsanschrift und Installationsorte unterschiedlich sind, geht das Chaos los. Auch der Support tut sich dann schwer. Ich traue mich gar nicht so richtig weitere Vertraege abzuschlieszen.&lt;br /&gt;
Alles ueber die App machen zu wollen/muessen ist ... Das gehoert fuer mich ins WebUserPortal.&lt;br /&gt;
&lt;br /&gt;
Was echt nervt ist, dass sich gefühlt jeden Monat das LookandFeel aendert. Schon die dritte oder vierte Designaenderung des Homescreens (Zuhause).&lt;br /&gt;
&lt;br /&gt;
=API=&lt;br /&gt;
Da die App eher suboptimal ist, habe ich recht zuegig damit angefangen mir die notwendigen Infos ueber die API auf eine Infoseite im Intranet zu holen. Ein Integration in FHEM und/oder HA steht noch aus.&lt;br /&gt;
&lt;br /&gt;
=Erste Schritte=&lt;br /&gt;
&lt;br /&gt;
Uebersicht [https://developer.tibber.com/docs/overview]&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung: Die Plattform wird über GraphQL verfügbar gemacht, eine Technologie, die von Facebook konzipiert und entwickelt wurde.&lt;br /&gt;
&lt;br /&gt;
==Was ist GraphQL?==&lt;br /&gt;
&lt;br /&gt;
[https://de.wikipedia.org/wiki/GraphQL Wikipedia]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/graphql GraphQL]&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zu GraphQL===&lt;br /&gt;
&lt;br /&gt;
GraphQL ist eine von Facebook entwickelte Abfragesprache und Laufzeitumgebung für APIs (Application Programming Interfaces). Im Gegensatz zu traditionellen RESTful APIs, bei denen die Client-Anwendung verschiedene Endpunkte aufruft, um Daten abzurufen oder zu manipulieren, ermöglicht GraphQL dem Client, genau die Daten abzurufen, die er benötigt, indem er eine einzige Abfrage an den Server sendet.&lt;br /&gt;
&lt;br /&gt;
Hier sind einige Hauptmerkmale von GraphQL:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;Declarative Datenabfrage&#039;&#039;&#039;: Der Client spezifiziert genau, welche Daten er vom Server erhalten möchte, indem er eine GraphQL-Abfrage definiert, die die benötigten Felder und deren Struktur beschreibt. Dadurch kann der Client genau die Daten erhalten, die er benötigt, ohne Overfetching oder Underfetching.&lt;br /&gt;
    &#039;&#039;&#039;Typsystem&#039;&#039;&#039;: GraphQL definiert ein Typsystem, das die Struktur und den Typ der Daten beschreibt, die von der API zurückgegeben werden können. Dies ermöglicht eine strikte Typisierung und verbesserte Dokumentation.&lt;br /&gt;
    &#039;&#039;&#039;Single-Endpoint-Architektur&#039;&#039;&#039;: Im Gegensatz zu RESTful APIs, die oft mehrere Endpunkte für verschiedene Ressourcentypen haben, hat eine GraphQL-API normalerweise nur einen Endpunkt, über den alle Abfragen und Mutationen gesendet werden.&lt;br /&gt;
    &#039;&#039;&#039;Echtzeitfähigkeit&#039;&#039;&#039;: GraphQL unterstützt auch Abonnements, die es ermöglichen, Daten in Echtzeit zu empfangen, wenn sich etwas ändert. Dies ist besonders nützlich für Anwendungsfälle wie Live-Chats, Benachrichtigungen und Echtzeit-Dashboard-Aktualisierungen.&lt;br /&gt;
    &#039;&#039;&#039;Entwicklungserfahrung&#039;&#039;&#039;: GraphQL bietet eine verbesserte Entwicklererfahrung, da Clients genau die Daten erhalten können, die sie benötigen, und Entwickler APIs schneller entwickeln können, ohne sich um Versionierung oder unerwartete Seiteneffekte durch Änderungen an der Backend-API kümmern zu müssen.&lt;br /&gt;
&lt;br /&gt;
Insgesamt bietet GraphQL eine flexible, effiziente und moderne Möglichkeit, APIs zu entwerfen und zu verwenden, die den Anforderungen moderner Anwendungen besser gerecht wird als traditionelle RESTful APIs.&lt;br /&gt;
&lt;br /&gt;
Auf der Tibber-Seite heiszt es:&lt;br /&gt;
&lt;br /&gt;
 The GraphQL query language is basically about selecting fields on objects.&lt;br /&gt;
&lt;br /&gt;
Was dies bedeutet kommt weiter unten. Weiter heiszt es auf der Tibber Seite.&lt;br /&gt;
&lt;br /&gt;
    GraphQL ist eine Abfragesprache für Ihre(? unsere) API und eine serverseitige Laufzeitumgebung zum Ausführen von Abfragen mithilfe eines Typsystems, das Sie für Ihre Daten definieren. GraphQL ist nicht an eine bestimmte Datenbank oder Speicher-Engine gebunden und wird stattdessen durch Ihren vorhandenen Code und Ihre Daten unterstützt.&lt;br /&gt;
&lt;br /&gt;
Mit der Uebersetzung von Google hadere ich noch. Der Satz kommt vermutlich ursrpruenglich von Facebook und wurde von Tibber 1:1 uebernommen.&lt;br /&gt;
&lt;br /&gt;
Der naechste Satz gibt schon mehr Aufschluss:&lt;br /&gt;
 Tibber hat sich für GraphQL als API entschieden, weil es unseren Integratoren und uns selbst Flexibilitaet bietet. Die Moeglichkeit, die gewuenschten Daten genau zu definieren, macht die Nutzung wesentlich einfacher als herkoemmliche [https://www.ibm.com/de-de/topics/rest-apis REST-basierte APIs]. Es ist auch sehr schoen, dass die serverseitige Implementierung von GraphQL sehr gut mit unserer Microservice-Architektur zusammenspielt :-)&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg nutze ich den [https://developer.tibber.com/explorer Api Explorer im TibberDev]. Nachdem man sich dort einen Personal Token angelegt hat, kann&#039;s direkt losgehen. Der [https://developer.tibber.com/explorer GraphiQL] hat auch eine Autovervollstaendigung. Sehr praktisch.&lt;br /&gt;
[[Datei:TibberDevAccount.png|mini|TibberDevAccount]]&lt;br /&gt;
Die Root-Types sind &lt;br /&gt;
*query: Query&lt;br /&gt;
*mutation: RootMutation&lt;br /&gt;
*subscription: RootSubscription&lt;br /&gt;
*fragment feht irgendwie in der Doc &lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist recht simpel und das Doc in knapp einer Stunde durchprobiert. So dass ich dann schnell an dem Punkt angelangt bin, dass ich ein Programm (PHP, Python oder JavaScript) brauche welches die fuer mich interessanten Daten in meine MySQL-DB uebertraegt. Ob ich zuerst anzeige und dann speichere, also sozusagen ueber die Datenbank puffere, oder umgekehrt muss ich noch festlegen.&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zum Subprotokoll===&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; ist ein WebSocket-Subprotokoll, das speziell für die Kommunikation zwischen Client und Server in GraphQL-Anwendungen entwickelt wurde. Es basiert auf dem WebSocket-Protokoll, das eine bidirektionale, persistente Verbindung zwischen einem Client und einem Server ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Im Kontext von GraphQL wird &amp;quot;graphql-transport-ws&amp;quot; verwendet, um GraphQL-Abfragen, Mutationen und Abonnements über WebSocket-Verbindungen zu übertragen. Im Gegensatz zum traditionellen HTTP-Protokoll, das eine separate Anfrage-Antwort-Struktur hat, ermöglicht WebSocket eine kontinuierliche Kommunikation zwischen Client und Server, was besonders nützlich ist, wenn Echtzeit-Aktualisierungen benötigt werden, wie bei Abonnements in GraphQL.&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; definiert spezifische Nachrichtenformate und Handshake-Verfahren, die für die GraphQL-Kommunikation über WebSockets erforderlich sind. Durch die Verwendung dieses Subprotokolls können Entwickler GraphQL-Anwendungen erstellen, die von den Echtzeitfähigkeiten von WebSocket profitieren, ohne auf die Vorteile und Flexibilität von GraphQL verzichten zu müssen.&lt;br /&gt;
&lt;br /&gt;
==Mehrfacher Hinweis Subprotokolwechsel Dezember 2022==&lt;br /&gt;
&lt;br /&gt;
 Die TibberAPI unterstuetzte zunaechst GraphQL-Websocket-Subscriptions mithilfe des Unterprotokolls graphql-ws. Diese Bibliothek wird/wurde jedoch archiviert und nicht mehr gepflegt. Daher wurde am 31. Maerz 2022 die Unterstützung für das Unterprotokoll graphql-transport-ws hinzugefuegt.&lt;br /&gt;
&lt;br /&gt;
 Die Unterstuetzung für das alte Protokoll wird im Dezember 2022 entfernt. Gleichzeitig aendert sich auch die URL für die Verbindung zum Websocket-Server. Zuvor konnte die Verbindung unter &#039;&#039;&#039;wss://api.tibber.com/v1-beta/gql/subscriptions&#039;&#039;&#039; hergestellt werden. Nach der Aenderung muss die URL ueber GraphQL abgefragt werden (siehe Beispiel unten) und der Client muss das Unterprotokoll graphql-transport-ws unterstuetzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight code=xml&amp;gt;&lt;br /&gt;
Frage:&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;websocketSubscriptionUrl&amp;quot;: &amp;quot;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Anfragenbegrenzung==&lt;br /&gt;
 Zum Schutz der API gilt eine Ratenbegrenzung von 100 Anfragen in 5 Minuten pro IP-Adresse. Beachten Sie, dass die Preise einmal täglich am Nachmittag berechnet werden (für Norwegen und Schweden sind sie zunächst vorläufig und werden später möglicherweise mit geringfügigen Änderungen nach Bestätigung der Wechselkurse endgültig festgelegt). Sie können „priceInfo.today“ und „priceInfo.tomorrow“ verwenden, um sie im Voraus abzurufen, anstatt nur „priceInfo.current“ für die aktuelle Stunde zu verwenden.&lt;br /&gt;
&lt;br /&gt;
=In medias res=&lt;br /&gt;
Zum Starten empfiehlt die Tibber-API-Doc, die fundamentalen Konzepte von GraphQL, die Kommunikation mit der API und den Explorer.&lt;br /&gt;
&lt;br /&gt;
Um die nachfolgenden Beispiele schnell zu verstehen, hier der Link zur [https://developer.tibber.com/docs/reference API-Referenz]&lt;br /&gt;
&lt;br /&gt;
 Bei der GraphQL-Abfragesprache geht es im Wesentlichen um die Auswahl von Feldern in Objekten.&lt;br /&gt;
&lt;br /&gt;
Auf der Docs-Reference-Seite gibt es einen interessanten Hinweis:&lt;br /&gt;
  This reference is auto-generated from https://api.tibber.com&lt;br /&gt;
&lt;br /&gt;
Es koennte als eine URL geben, die einem die aktuelle &#039;&#039;&#039;Tibber GraphQL Schema Reference&#039;&#039;&#039; direkt zur Auswertung ausgibt. Der Umweg ueber das Parsen der HTML-Ausgabe ginge zur Not natuerlich auch.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objekt Typen==&lt;br /&gt;
&lt;br /&gt;
Die Begriffsdifferenzierung Objekt und Object Types ist mir derzeit noch nicht klar. Ich werde wohl um die allgemeine [https://github.com/graphql/graphql-spec Spec von GraphQL] nicht herumkommen:-(&lt;br /&gt;
&lt;br /&gt;
Bei &#039;Home&#039; wird von einem Objekttyp gesprochen. Klar ist, man hat Objekte, die mit dem Schluesselwort type eingeleitet und dann beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 Einige Felder sind Skalare (Boolesche Werte, Zeichenfolgen, Ganzzahlen, Gleitkommazahlen), waehrend andere Objekttypen sind (Adresse, LegalEntity). Eine GraphQL-Abfrage muss vollstaendig auf Skalarebene sein. D.h. in einer Abfrage duerfen die abgefragten Felder keine Objekte (keine weiteren Objekttypen) sein, sondern hierarchisch heruntergebrochen auf weitere Inhalte bis es Skalare sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
This query is valid:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
This is not:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner   &amp;lt;- hier ist der Fehler. Dies ist kein Skalar!&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
The owner field is an object type and you would need to specify which scalar fields you would like returned:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner{&lt;br /&gt;
            name&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==query Datenabfrage (read)==&lt;br /&gt;
&lt;br /&gt;
query-&amp;gt;viewer&lt;br /&gt;
&lt;br /&gt;
Zuerst mit homes die Zaehlerplatz-iD holen und dan mit home (id: &amp;quot;&amp;lt;ID&amp;gt;&amp;quot;) nur die data dazu holen.&lt;br /&gt;
&lt;br /&gt;
Hello World;-)&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    login&lt;br /&gt;
    name&lt;br /&gt;
    userId&lt;br /&gt;
    accountType&lt;br /&gt;
    home (id: &amp;quot;xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb&amp;quot;) {&lt;br /&gt;
      id&lt;br /&gt;
      features {&lt;br /&gt;
        realTimeConsumptionEnabled&lt;br /&gt;
      }&lt;br /&gt;
      subscriptions {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      currentSubscription {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      meteringPointData {&lt;br /&gt;
        consumptionEan&lt;br /&gt;
        gridCompany&lt;br /&gt;
        gridAreaCode&lt;br /&gt;
        priceAreaCode&lt;br /&gt;
        productionEan&lt;br /&gt;
        energyTaxType&lt;br /&gt;
        vatType&lt;br /&gt;
        estimatedAnnualConsumption&lt;br /&gt;
      }&lt;br /&gt;
      owner {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      mainFuseSize&lt;br /&gt;
    }&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant fand ich z.B.&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;priceAreaCode&amp;quot;: &amp;quot;Amprion&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;estimatedAnnualConsumption&amp;quot;: 500&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;mainFuseSize&amp;quot;: null&lt;br /&gt;
&lt;br /&gt;
und was da noch alles an Daten einzutragen ist.&lt;br /&gt;
 size: Int&lt;br /&gt;
 The size of the home in square meters&lt;br /&gt;
&lt;br /&gt;
==mutation Datenveraenderung (write)==&lt;br /&gt;
&lt;br /&gt;
Mutationen gibt es nur 3, wobei die erste bei mir entfaellt.&lt;br /&gt;
&lt;br /&gt;
 sendMeterReading(input: MeterReadingInput!): MeterReadingResponse!&lt;br /&gt;
 Send meter reading for home (only available for Norwegian users) &lt;br /&gt;
 &lt;br /&gt;
 updateHome(input: UpdateHomeInput!): Home!&lt;br /&gt;
 Update home information&lt;br /&gt;
 &lt;br /&gt;
 sendPushNotification(input: PushNotificationInput!): PushNotificationResponse!&lt;br /&gt;
 Send notification to Tibber app on registered devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==subscription==&lt;br /&gt;
&lt;br /&gt;
Subscription hat nur 2 Felder, wobei dauerhaft nur das erste interessant ist.&lt;br /&gt;
&lt;br /&gt;
 liveMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to real-time measurement stream from Pulse or Watty device&lt;br /&gt;
&lt;br /&gt;
 testMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to test stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
subscription{&lt;br /&gt;
  liveMeasurement(homeId: &amp;quot;&amp;lt;homeID&amp;gt;&amp;quot;){&lt;br /&gt;
    timestamp&lt;br /&gt;
    power&lt;br /&gt;
    lastMeterConsumption&lt;br /&gt;
    accumulatedConsumption&lt;br /&gt;
    accumulatedProduction&lt;br /&gt;
    accumulatedConsumptionLastHour&lt;br /&gt;
    accumulatedProductionLastHour&lt;br /&gt;
    accumulatedCost&lt;br /&gt;
    accumulatedReward&lt;br /&gt;
    currency&lt;br /&gt;
    minPower&lt;br /&gt;
    averagePower&lt;br /&gt;
    maxPower&lt;br /&gt;
    powerProduction&lt;br /&gt;
    powerReactive&lt;br /&gt;
    powerProductionReactive&lt;br /&gt;
    minPowerProduction&lt;br /&gt;
    maxPowerProduction&lt;br /&gt;
    lastMeterProduction&lt;br /&gt;
    powerFactor&lt;br /&gt;
    voltagePhase1&lt;br /&gt;
    voltagePhase2&lt;br /&gt;
    voltagePhase3&lt;br /&gt;
    currentL1&lt;br /&gt;
    currentL2&lt;br /&gt;
    currentL3&lt;br /&gt;
    signalStrength&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==fragment==&lt;br /&gt;
&lt;br /&gt;
?&lt;br /&gt;
&lt;br /&gt;
Dazu habe ich bisher keine weiteren Infos gefunden.&lt;br /&gt;
&lt;br /&gt;
=Programmierung=&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung steht bei mir hier die Client-Programmierung im Vordergrund.&lt;br /&gt;
&lt;br /&gt;
==Query/Mutation==&lt;br /&gt;
&lt;br /&gt;
Die Abfragen und Aenderungen zu programmieren sind schnell erledigt.&lt;br /&gt;
&lt;br /&gt;
Der Einstieg auf der Konsole gelingt mit curl recht gut. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
 curl \&lt;br /&gt;
-H &amp;quot;Authorization: Bearer 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE \&lt;br /&gt;
-H &amp;quot;Content-Type: application/json&amp;quot; \&lt;br /&gt;
-X POST \&lt;br /&gt;
-d  &#039;mutation{ sendMeterReading(input:{homeId:&amp;quot;some home id&amp;quot;, reading: 123}){time reading  }}&#039; https://api.tibber.com/v1-beta/gql&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Subscription==&lt;br /&gt;
&lt;br /&gt;
Die Subscriptions sind schon ein wenig anspruchsvoller zu programmieren als Query und Mutation (QuM).&lt;br /&gt;
Ist QuM eine simple http-Anfrage mit einer Antwort im JSON-Format, ist es bei den Subscriptions ein Websocket, der einmal geoeffnet in unregelmaeszigen Abstaenden JSON-Pakete liefert.&lt;br /&gt;
&lt;br /&gt;
===Anforderungen für Websocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
* Clients muessen den User-Agent-Header festlegen, wenn sie die GraphQL-API aufrufen und eine Websocket-Verbindung oeffnen. Sowohl die Plattform als auch die Treiberversion muessen angegeben werden. Z.B. Homey/10.0.0 com.tibber/1.8.3&lt;br /&gt;
* Clients müssen das Unterprotokoll graphql-transport-ws implementieren&lt;br /&gt;
* Clients müssen Jitter und exponentielles Backoff implementieren, wenn sie Anfragen an die API wiederholen&lt;br /&gt;
* Clients müssen die WebSocket-Server-URL dynamisch abfragen&lt;br /&gt;
&lt;br /&gt;
===Best Practices für die Implementierung von WebSocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
Bei der Implementierung eines Clients zum Abonnieren von Live-Daten sind einige Szenarien zu beruecksichtigen:&lt;br /&gt;
* Keine Daten aufgrund von Netzwerkproblemen: Es kann sinnvoll sein, einen Wiederverbindungsmechanismus zu implementieren, falls mehrere Minuten lang keine Daten empfangen wurden. Bei einem erneuten Versuch muss zunaechst die alte Websocket-Verbindung zerstoert und ordnungsgemaeszer Jitter und exponentielle Verzoegerung implementiert werden.&lt;br /&gt;
* Ein Echtzeitgeraet wird moeglicherweise entfernt. Es wird empfohlen, vor dem erneuten Verbinden immer zu ueberpruefen, ob home.features.realTimeConsumptionEnabled einen echten Wert hat.&lt;br /&gt;
* Eine WebSocket-Verbindung kann aufgrund eines ungueltigen Authentifizierungstokens verboten sein (z. B. weil ein Benutzer möglicherweise sein Tibber-Konto entfernt hat). In diesem Fall darf der Client es nicht mit demselben Token erneut versuchen. Stattdessen muss der Autorisierungsprozess erneut ausgeführt werden, wobei der Benutzer neue Anmeldeinformationen eingibt.&lt;br /&gt;
* Es kann zu einem Neustart der Tibber-Websocket-API kommen, was bedeutet, dass die Verbindung mit dem &amp;quot;Code 1001&amp;quot; und dem Grund &amp;quot;Going away&amp;quot; geschlossen wird. In diesem Fall kann der Client nach einer zufälligen Verzoegerung von 1 bis 60 Sekunden (Jitter angewendet) die Verbindung wiederherstellen, um zu vermeiden, dass alle Clients gleichzeitig die Verbindung wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
In PHP habe ich wenig bis gar nichts gefunden. Ich wollte mir auch einfach die Nutzung einer GraphQL-Library ersparen. Python habe ich erst einmal an den Schluss verschoben und einem Integration in FHEM den Vorzug gegeben.&lt;br /&gt;
&lt;br /&gt;
===Perl (FHEM)===&lt;br /&gt;
Hier der Code der tatsaechlich funktioniert, obwohl er an ein paar Stellen nicht so vorgeht wie ich aus den Tibber-Docs erwartet hatte.&lt;br /&gt;
&lt;br /&gt;
HINWEIS: Dies ist der &amp;quot;nackte&amp;quot; Perl-Code. In der &amp;quot;Originaldatei&amp;quot; sind noch die &amp;quot;FHEM-Escapes&amp;quot; doppelter Strichpunkt und Zeilenfolgezeichen \ enthalten. Eine Eingabe ueber die FHEM-Kommandozeile hat bei mir nicht funktioniert. Erst das direkte Einkopieren in die fhem.conf zeigte das nachfolgende, funktionierende Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
connect:cmd:.connect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	# establish connection to websocket&lt;br /&gt;
	# format must also include portnumber if a path is to be specified&lt;br /&gt;
	$hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
	&lt;br /&gt;
	# callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
	# websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
	&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onDisconnect {&lt;br /&gt;
	my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect&lt;br /&gt;
	my $timerFunction = sub() {&lt;br /&gt;
		my ($hash) = @_;&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
		&lt;br /&gt;
		# only re-connect if device is not connected&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));&lt;br /&gt;
	};&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash, $timerFunction);&lt;br /&gt;
	&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onConnectionAck:websocketData:.*connection_ack.* {&lt;br /&gt;
	#websocketData contains the string &amp;quot;connection_ack&amp;quot;&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# do not proceed if connection is lost&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device not open&amp;quot; if (!defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	my $homeId = AttrVal($name, &amp;quot;homeId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myId = AttrVal($name, &amp;quot;myId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# build the query, do it in pieces, the comma at the end caused perl errors&lt;br /&gt;
	# so we put it together in this not very elegant way&lt;br /&gt;
	my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;&lt;br /&gt;
	$json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;&lt;br /&gt;
	$json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;&lt;br /&gt;
	$json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;&lt;br /&gt;
	$json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;&lt;br /&gt;
	$json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;&lt;br /&gt;
	$json .= &#039;}}&#039;;&lt;br /&gt;
	&lt;br /&gt;
	#send the string via websocket as ASCII&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
	DevIo_SimpleWrite($hash, $json, 2);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onNextLiveMeasurement:websocketData:.*next.*payload.*data.*liveMeasurement.* {&lt;br /&gt;
	#websocketData contains next-live-measurement-data&lt;br /&gt;
	my $val = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;{}&amp;quot;);&lt;br /&gt;
	my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};&lt;br /&gt;
	&lt;br /&gt;
	my $ret = &amp;quot;got values for:\n&amp;quot;;&lt;br /&gt;
	foreach my $k (sort keys %res) {&lt;br /&gt;
		$ret .= &amp;quot;$k\n&amp;quot;;&lt;br /&gt;
		readingsBulkUpdate($hash, makeReadingName($k), $res{$k});&lt;br /&gt;
	}&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Code Analyse====&lt;br /&gt;
&lt;br /&gt;
Es gibt 5 Eintrittspunkte:&lt;br /&gt;
* connect&lt;br /&gt;
* disconnect&lt;br /&gt;
* onDisconnect&lt;br /&gt;
* onConnectionAck&lt;br /&gt;
* onNextLiveMeasurement&lt;br /&gt;
&lt;br /&gt;
Die ersten beiden legen die Befehlsfolgen für ein Start-Kommando (set cmd connect, bzw. set cmd disconnect) oder Stop-Kommando fest.&lt;br /&gt;
Die letzten 3 reagieren auf die entsprechenden Events.&lt;br /&gt;
=====connect=====&lt;br /&gt;
&lt;br /&gt;
Lexikalische Variablen mittels my, bzw. my()&lt;br /&gt;
&lt;br /&gt;
 Jede Variable, die mit our deklariert oder auch &amp;quot;einfach so&amp;quot; ohne eine Deklaration verwendet wird, wird in die Symboltabelle des jeweils aktuellen Packages aufgenommen.&lt;br /&gt;
 Deklariert man dagegen eine Variable mit dem Operator my, so wird die entsprechende Variable in einer anderen Tabelle abgelegt, auf die kein expliziter Zugriff möglich ist. &lt;br /&gt;
 Neben der Tatsache, daß my-Variablen in einer eigenen Tabelle verwaltet werden, ist von besonderer Bedeutung, daß sie nur einen recht beschränkten Gültigkeitsbereich besitzen. Eine durch my erzeugte Variable steht nur in dem aktuellen Block (definiert durch geschweifte Klammern &amp;quot;{...}&amp;quot;), der aktuellen Datei oder innerhalb eines Arguments von eval() zur Verfügung. Außerhalb davon existieren diese Variablen nicht, es ist also (im Gegensatz zu our-Variablen) auch mit Hilfe des Package-Namens dort kein Zugriff möglich&lt;br /&gt;
 Es kann in einem Package durchaus zwei Variablen gleichen Namens geben: eine in der Symboltabelle und eine, die durch einen Aufruf von my entstanden ist. In einem solchen Falle wird bei einfacher Verwendung des Bezeichners auf die my-Variable zugegriffen. &lt;br /&gt;
&lt;br /&gt;
In $defs stehen alle Defines drin. In $hash wird also der Name des des aufrufenden Devices lokal hinterlegt und danach mit DevIO_IsOpen() der Status abgefragt. &lt;br /&gt;
$defs ist ein Hash. Ein Hash ist ein anderer Namen fuer ein assoziatives Array. Also eine ungeordnete Liste von Skalaren (Zahl oder String) die ueber einen Stringwert angesprochen (Lookup) werden koennen.  &lt;br /&gt;
&lt;br /&gt;
FHEM-Spezial:&lt;br /&gt;
 In $defs findet man alle Devices, z.b. $defs{&amp;quot;Rolladen_Tuere&amp;quot;} beinhaltet das Device.&lt;br /&gt;
 my @alle = keys %defs;&lt;br /&gt;
 my @teilmenge = defInfo(&#039;TYPE=notify&#039;,&#039;NAME&#039;); liefert ein array mit allen notify devices.&lt;br /&gt;
 z.B. my @teilmenge = defInfo(&#039;NAME=Rollladen.*&#039;,&#039;NAME&#039;);&lt;br /&gt;
&lt;br /&gt;
 return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
Sollte also das Device bereits im State &amp;quot;offen&amp;quot; sein, wird die connect-Routine abgebrochen. Dann erscheint in den Readings nicht Datum/Uhrzeit des Connects sondern die Meldung &amp;quot;Device already open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
 # establish connection to websocket&lt;br /&gt;
 # format must also include portnumber if a path is to be specified&lt;br /&gt;
 $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 HASH-Operationen (Beispiel):&lt;br /&gt;
 $href ={APR =&amp;gt; 4,AUG =&amp;gt; 8}; #anonymous hash&lt;br /&gt;
 $el = $href-&amp;gt;{APR}; $el = %{$href}{APR}; #access element of hash&lt;br /&gt;
 $href2 = {%{$href1}}; #copy hash&lt;br /&gt;
 if (ref($r) eq &amp;quot;HASH&amp;quot;) {} #checks if $r points to hash&lt;br /&gt;
&lt;br /&gt;
Dem Internal &amp;quot;DeviceName&amp;quot; wird das Ergebnis von AttrVal() zugewiesen. Bei mir ergab das:&lt;br /&gt;
 DeviceName wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&lt;br /&gt;
 FHEM-Referenz:&lt;br /&gt;
 AttrVal(&amp;lt;devicename&amp;gt;,&amp;lt;attribute&amp;gt;,&amp;lt;defaultvalue&amp;gt;)&lt;br /&gt;
 Gibt das entsprechende Attribut des Gerätes zurück&lt;br /&gt;
 { Value(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { OldValue(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { time_str2num(OldTimestamp(&amp;quot;wz&amp;quot;)) }&lt;br /&gt;
 { ReadingsVal(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, &amp;quot;20&amp;quot;)+0 }&lt;br /&gt;
 { ReadingsTimestamp(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, 0)}&lt;br /&gt;
 { AttrVal(&amp;quot;wz&amp;quot;, &amp;quot;room&amp;quot;, &amp;quot;none&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Das Attribut websocketURL ist bei mir wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions. Somit erklaert sich auch der Eintrag bei DeviceName. &amp;quot;wss:echo.websocket.org:443&amp;quot; ist nur der Defaultwert.&lt;br /&gt;
&lt;br /&gt;
Man haette zum Testen auch die folgenden beiden verwenden/eintragen koennen.&lt;br /&gt;
 There are free test servers that we can use to experiment with a WebSocket client, such as:&lt;br /&gt;
&lt;br /&gt;
    Hoppscotch – wss://echo-websocket.hoppscotch.io (GUI)&lt;br /&gt;
    λ if else – wss://ws.ifelse.io (GUI)&lt;br /&gt;
&lt;br /&gt;
 Both provide a GUI for testing via a browser. The former sends a timestamp to the client every second but doesn’t respond to client messages. The latter is a standard echo service that sends a copy of each received message back to the client. Both make sense in testing.&lt;br /&gt;
&lt;br /&gt;
Da curl nicht nativ websocket-Kommunikation unterstuetzt kann man auf folgende Tool ausweichen. Auch chrome bietet ein Websocket-Client-Plugin.&lt;br /&gt;
*wssh3&lt;br /&gt;
*websocat&lt;br /&gt;
*wscat&lt;br /&gt;
&lt;br /&gt;
 # special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
&lt;br /&gt;
 # callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
 # websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion wird direkt, ohne Namen, in das/den (?) Hash unter directReadFn abgelegt. &lt;br /&gt;
Die Callback-Funktion:&lt;br /&gt;
Hier wird mit Hilfe der Funktion DevIo_SimpleRead() die Rueckmeldung des Server eingelesen. Siehe [https://wiki.fhem.de/wiki/DevIo]&lt;br /&gt;
Hinweis zum Logging in FHEM: [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#Logging]&lt;br /&gt;
Die eigentliche Aktivitaet geschieht in den sieben Zeilen am Ende der Callback-Fkt. &lt;br /&gt;
 ReadingsAge(&amp;lt;devicename&amp;gt;,&amp;lt;reading&amp;gt;,&amp;lt;defaultvalue&amp;gt;) #gibt das Alter des Readings in Sekunden zurück. &lt;br /&gt;
 AttrVal hatten wir schon.&lt;br /&gt;
 isNext bekommt seinen Wert aus dem durch einen REGEX geliefert Wert des Buffers. Beispiel ($match) = ($dirty =~ /^(.*)$/s); &lt;br /&gt;
&lt;br /&gt;
 Die Funktion readingsBeginUpdate() bereitet die Definition mit dem Hash $hash auf ein Update von Readings vor. Dies betrifft insbesondere das Setzen von Umgebungsvariablen sowie dem aktuellen Zeitstempel als Änderungszeitpunkt. Der Aufruf dieser Funktion ist notwendig um eigentliche Updates mit der Funktion readingsBulkUpdate() auf der gewünschten Definition durchführen zu können. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#readingsBeginUpdate]&lt;br /&gt;
&lt;br /&gt;
Die Funktion readingsEndUpdate() beendet den Bulk-Update Prozess durch die Funktionen readingsBeginUpdate() &amp;amp; readingsBulkUpdate() und triggert optional die entsprechenden Events sämtlicher erzeugter Readings für die Definition $hash. Desweiteren werden nachgelagerte Tasks wie bspw. die Erzeugung von User-Readings (Attribut: userReadings), sowie die Erzeugung des STATE aufgrund des Attributs stateFormat durchgeführt. Sofern $do_trigger gesetzt ist, werden alle anstehenden Events nach Abschluss getriggert.&lt;br /&gt;
&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
Das ist die eigentliche Kontaktaufnahme. &lt;br /&gt;
&lt;br /&gt;
Dies dient vermutlich dazu, bis zum ersten Empfang von Daten, das Reading websocketDate zu leeren.&lt;br /&gt;
&lt;br /&gt;
 return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss ercheint dann im Reading connect die aktuelle Zeit.&lt;br /&gt;
&lt;br /&gt;
readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=====disconnect=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
 disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erlaeuterung:&lt;br /&gt;
&lt;br /&gt;
*Die Funktion RemoveInternalTimer löscht möglicherweise noch anstehende Timer welche mit dem Übergabeparameter $arg gescheduled sind. Optional kann man zusätzlich die Suche auf eine bestimmte Funktion $functionName weiter einschränken. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#RemoveInternalTimer]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von $hash ein und gibt diese zurück. [https://wiki.fhem.de/wiki/DevIo#DevIo_SimpleRead()]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition $hash. [https://wiki.fhem.de/wiki/DevIo#DevIo_CloseDev()]&lt;br /&gt;
&lt;br /&gt;
*POSIX:: [https://metacpan.org/pod/POSIX#strftime]&lt;br /&gt;
&lt;br /&gt;
=====onDisconnect=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=====onConnectionAck=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====onNextLiveMeasurement=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===JavaScript===&lt;br /&gt;
Zum Eingewoehnen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;ws://echo.websocket.org&#039;);&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung hergestellt.&#039;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
    socket.send(&#039;Hallo Server!&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&#039;Nachricht vom Server erhalten:&#039;, event.data);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und jetzt das Gerippe fuer Tibber mit Haut versehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
const WebSocket = require(&#039;ws&#039;);&lt;br /&gt;
&lt;br /&gt;
const query = `subscription {&lt;br /&gt;
    liveMeasurement(homeId: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;) {&lt;br /&gt;
      timestamp&lt;br /&gt;
      power&lt;br /&gt;
      accumulatedConsumption&lt;br /&gt;
      minPower&lt;br /&gt;
      maxPower&lt;br /&gt;
      averagePower&lt;br /&gt;
      voltagePhase1&lt;br /&gt;
      currentL1&lt;br /&gt;
      accumulatedCost&lt;br /&gt;
      currency&lt;br /&gt;
   } &lt;br /&gt;
}`;&lt;br /&gt;
&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&#039;,[&#039;graphql-transport-ws&#039;], { &lt;br /&gt;
        headers: {&lt;br /&gt;
                &#039;Content-Type&#039;: &#039;application/json&#039;,&lt;br /&gt;
                &#039;User-Agent&#039;: &#039;NodeJS tibberview&#039;,&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&amp;quot;WebSocket-Verbindung hergestellt.&amp;quot;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
   const connectionQuery = {&lt;br /&gt;
      type: &amp;quot;connection_init&amp;quot;,&lt;br /&gt;
      payload: {&lt;br /&gt;
          token: &amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;,&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    socket.send(JSON.stringify(connectionQuery));&lt;br /&gt;
    console.log(&amp;quot;Verbindung zum &#039;Tibber feed&#039; hergestellt.&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&amp;quot;Nachricht vom Server erhalten:&amp;quot;, event.data);&lt;br /&gt;
    const msg = JSON.parse(event.data);&lt;br /&gt;
&lt;br /&gt;
    if (msg.type === &amp;quot;connection_ack&amp;quot;) {&lt;br /&gt;
      console.log(&amp;quot;Tibber ACK empfangen: &amp;quot;, msg);&lt;br /&gt;
&lt;br /&gt;
      const realtimeDataQuery = {&lt;br /&gt;
        id: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&lt;br /&gt;
        type: &amp;quot;subscribe&amp;quot;,&lt;br /&gt;
        payload: { query }&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
      socket.send(JSON.stringify(realtimeDataQuery));&lt;br /&gt;
    }&lt;br /&gt;
    else if (msg.type === &amp;quot;next&amp;quot;) {&lt;br /&gt;
      if (!msg.payload.data) {&lt;br /&gt;
        console.log(&amp;quot;⚠  Nachricht hat keinen Inhalt.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      const data = msg.payload.data.liveMeasurement;&lt;br /&gt;
      socket.emit(&amp;quot;data&amp;quot;, data);&lt;br /&gt;
    } else {&lt;br /&gt;
      console.log(&amp;quot;Nachrichteninhalt: &amp;quot;, msg)&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm erzeugte folgende Ausgabe:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
worker:/mnt/1.5T_RAID/Programme/JavaScript/WebSocket # node tibberclient_demo.js &lt;br /&gt;
WebSocket-Verbindung hergestellt.&lt;br /&gt;
Verbindung zum &#039;Tibber feed&#039; hergestellt.&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;type&amp;quot;:&amp;quot;connection_ack&amp;quot;}&lt;br /&gt;
Tibber ACK empfangen:  { type: &#039;connection_ack&#039; }&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:48.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.6,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:51.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:52.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:230,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:53.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.2,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:54.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.1,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:55.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:230.2,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:56.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:57.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.9,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:58.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:59.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.4,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:00.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.7,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:01.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.6,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
^C&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An der Stelle mache ich jetzt einen Ausflug in PHP.&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
Nachdem der Websocket-Kontakt mit JavaScript funktioniert hat, kommt jetzt ein Serverprogramm was folgendes tun soll.&lt;br /&gt;
&lt;br /&gt;
*Abfrage der Basisdaten&lt;br /&gt;
*Objekt Types Builder&lt;br /&gt;
*Mutation&lt;br /&gt;
*Websocket Subsrciption&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===Python===&lt;br /&gt;
&lt;br /&gt;
Quelle: https://github.com/Danielhiversen/pyTibber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Client-Beispiel mit eingesetzem DEMO_TOKEN und user_agent&lt;br /&gt;
Obwohl DEMO_TOKEN in tibber.py definiert ist, wird es nicht uebernommen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import tibber&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def start():&lt;br /&gt;
  tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
  await tibber_connection.update_info()&lt;br /&gt;
  print(tibber_connection.name)&lt;br /&gt;
&lt;br /&gt;
  home = tibber_connection.get_homes()[0]&lt;br /&gt;
  await home.fetch_consumption_data()&lt;br /&gt;
  await home.update_info()&lt;br /&gt;
  print(home.address1)&lt;br /&gt;
&lt;br /&gt;
  await home.update_price_info()&lt;br /&gt;
  print(home.current_price_info)&lt;br /&gt;
  &lt;br /&gt;
  # await tibber_connection.close_connection()&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(start())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
import aiohttp&lt;br /&gt;
import tibber&lt;br /&gt;
&lt;br /&gt;
def _callback(pkg):&lt;br /&gt;
    print(pkg)&lt;br /&gt;
    data = pkg.get(&amp;quot;data&amp;quot;)&lt;br /&gt;
    if data is None:&lt;br /&gt;
        return&lt;br /&gt;
    print(data.get(&amp;quot;liveMeasurement&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def run():&lt;br /&gt;
    async with aiohttp.ClientSession() as session:&lt;br /&gt;
        tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, websession=session, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
        await tibber_connection.update_info()&lt;br /&gt;
    home = tibber_connection.get_homes()[0]&lt;br /&gt;
    await home.rt_subscribe(_callback)    &lt;br /&gt;
&lt;br /&gt;
    while True:&lt;br /&gt;
      await asyncio.sleep(10)&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(run())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4844</id>
		<title>Tibber Pulse (API)</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4844"/>
		<updated>2026-04-10T06:20:07Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Objekt Typen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Da ich aktuell vom Energieversorger einen zweiten Hausanschluss (40kW :-() gelegt bekommen habe und ich für die Wallboxen zur Foerderung einen 100% Oekostrom haben musste, habe ich kurzerhand einen Vertrag mit Tibber (mal zum Testen) abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
=Installation=&lt;br /&gt;
Pulse IR und Bridge&lt;br /&gt;
[[Datei:Tibber Pulse IR und Bridge.png|mini|Tibber Pulse IR und Bridge]]&lt;br /&gt;
Eigentlich verlief die Installation problemlos. Fuer mich war allerdings der Assistent in der Tibber-App zu kindisch. Das ist eher was für Apple-User. Ich haette mir an der ein oder anderen Stelle mehr Hardcore-Infos gewuenscht. Da der mehrstufige Prozess (WLAN-Bridge-Pulse-Zaehler) doch einige Solperfallen enthaelt, ist es fuer mich eher frustrierend jedesmal bei einem Fehler zurueck auf Los geschickt zu werden. Das geht definitiv besser. Ist aber wieder ein Beispiel, dass die Informatiker-Jobs heute nur noch durch 5-jaehrige Schimpansen (ungelernte ;-)) besetzt werden.&lt;br /&gt;
&lt;br /&gt;
Bei mir gab es letztendlich nur einen gravierenden Fehler. Von den mitgelieferten Akkus des Pulse war einer defekt. Bei mir sind das zwei AA-Zellen auf LiBasis! Ein Nachladen mit einem Lithium-1.5V-Lader hat keinen Erfolg gebracht. Erst der Austausch gegen 2 neue Zellen war erfolgreich.&lt;br /&gt;
&lt;br /&gt;
=App=&lt;br /&gt;
[[Datei:TibberAppIcon.png|mini|TibberAppIcon]]&lt;br /&gt;
Die App find ich ziemlich verquer. Sobald Rechnungsanschrift und Installationsorte unterschiedlich sind, geht das Chaos los. Auch der Support tut sich dann schwer. Ich traue mich gar nicht so richtig weitere Vertraege abzuschlieszen.&lt;br /&gt;
Alles ueber die App machen zu wollen/muessen ist ... Das gehoert fuer mich ins WebUserPortal.&lt;br /&gt;
&lt;br /&gt;
Was echt nervt ist, dass sich gefühlt jeden Monat das LookandFeel aendert. Schon die dritte oder vierte Designaenderung des Homescreens (Zuhause).&lt;br /&gt;
&lt;br /&gt;
=API=&lt;br /&gt;
Da die App eher suboptimal ist, habe ich recht zuegig damit angefangen mir die notwendigen Infos ueber die API auf eine Infoseite im Intranet zu holen. Ein Integration in FHEM und/oder HA steht noch aus.&lt;br /&gt;
&lt;br /&gt;
=Erste Schritte=&lt;br /&gt;
&lt;br /&gt;
Uebersicht [https://developer.tibber.com/docs/overview]&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung: Die Plattform wird über GraphQL verfügbar gemacht, eine Technologie, die von Facebook konzipiert und entwickelt wurde.&lt;br /&gt;
&lt;br /&gt;
==Was ist GraphQL?==&lt;br /&gt;
&lt;br /&gt;
[https://de.wikipedia.org/wiki/GraphQL Wikipedia]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/graphql GraphQL]&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zu GraphQL===&lt;br /&gt;
&lt;br /&gt;
GraphQL ist eine von Facebook entwickelte Abfragesprache und Laufzeitumgebung für APIs (Application Programming Interfaces). Im Gegensatz zu traditionellen RESTful APIs, bei denen die Client-Anwendung verschiedene Endpunkte aufruft, um Daten abzurufen oder zu manipulieren, ermöglicht GraphQL dem Client, genau die Daten abzurufen, die er benötigt, indem er eine einzige Abfrage an den Server sendet.&lt;br /&gt;
&lt;br /&gt;
Hier sind einige Hauptmerkmale von GraphQL:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;Declarative Datenabfrage&#039;&#039;&#039;: Der Client spezifiziert genau, welche Daten er vom Server erhalten möchte, indem er eine GraphQL-Abfrage definiert, die die benötigten Felder und deren Struktur beschreibt. Dadurch kann der Client genau die Daten erhalten, die er benötigt, ohne Overfetching oder Underfetching.&lt;br /&gt;
    &#039;&#039;&#039;Typsystem&#039;&#039;&#039;: GraphQL definiert ein Typsystem, das die Struktur und den Typ der Daten beschreibt, die von der API zurückgegeben werden können. Dies ermöglicht eine strikte Typisierung und verbesserte Dokumentation.&lt;br /&gt;
    &#039;&#039;&#039;Single-Endpoint-Architektur&#039;&#039;&#039;: Im Gegensatz zu RESTful APIs, die oft mehrere Endpunkte für verschiedene Ressourcentypen haben, hat eine GraphQL-API normalerweise nur einen Endpunkt, über den alle Abfragen und Mutationen gesendet werden.&lt;br /&gt;
    &#039;&#039;&#039;Echtzeitfähigkeit&#039;&#039;&#039;: GraphQL unterstützt auch Abonnements, die es ermöglichen, Daten in Echtzeit zu empfangen, wenn sich etwas ändert. Dies ist besonders nützlich für Anwendungsfälle wie Live-Chats, Benachrichtigungen und Echtzeit-Dashboard-Aktualisierungen.&lt;br /&gt;
    &#039;&#039;&#039;Entwicklungserfahrung&#039;&#039;&#039;: GraphQL bietet eine verbesserte Entwicklererfahrung, da Clients genau die Daten erhalten können, die sie benötigen, und Entwickler APIs schneller entwickeln können, ohne sich um Versionierung oder unerwartete Seiteneffekte durch Änderungen an der Backend-API kümmern zu müssen.&lt;br /&gt;
&lt;br /&gt;
Insgesamt bietet GraphQL eine flexible, effiziente und moderne Möglichkeit, APIs zu entwerfen und zu verwenden, die den Anforderungen moderner Anwendungen besser gerecht wird als traditionelle RESTful APIs.&lt;br /&gt;
&lt;br /&gt;
Auf der Tibber-Seite heiszt es:&lt;br /&gt;
&lt;br /&gt;
 The GraphQL query language is basically about selecting fields on objects.&lt;br /&gt;
&lt;br /&gt;
Was dies bedeutet kommt weiter unten. Weiter heiszt es auf der Tibber Seite.&lt;br /&gt;
&lt;br /&gt;
    GraphQL ist eine Abfragesprache für Ihre(? unsere) API und eine serverseitige Laufzeitumgebung zum Ausführen von Abfragen mithilfe eines Typsystems, das Sie für Ihre Daten definieren. GraphQL ist nicht an eine bestimmte Datenbank oder Speicher-Engine gebunden und wird stattdessen durch Ihren vorhandenen Code und Ihre Daten unterstützt.&lt;br /&gt;
&lt;br /&gt;
Mit der Uebersetzung von Google hadere ich noch. Der Satz kommt vermutlich ursrpruenglich von Facebook und wurde von Tibber 1:1 uebernommen.&lt;br /&gt;
&lt;br /&gt;
Der naechste Satz gibt schon mehr Aufschluss:&lt;br /&gt;
 Tibber hat sich für GraphQL als API entschieden, weil es unseren Integratoren und uns selbst Flexibilitaet bietet. Die Moeglichkeit, die gewuenschten Daten genau zu definieren, macht die Nutzung wesentlich einfacher als herkoemmliche [https://www.ibm.com/de-de/topics/rest-apis REST-basierte APIs]. Es ist auch sehr schoen, dass die serverseitige Implementierung von GraphQL sehr gut mit unserer Microservice-Architektur zusammenspielt :-)&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg nutze ich den [https://developer.tibber.com/explorer Api Explorer im TibberDev]. Nachdem man sich dort einen Personal Token angelegt hat, kann&#039;s direkt losgehen. Der [https://developer.tibber.com/explorer GraphiQL] hat auch eine Autovervollstaendigung. Sehr praktisch.&lt;br /&gt;
[[Datei:TibberDevAccount.png|mini|TibberDevAccount]]&lt;br /&gt;
Die Root-Types sind &lt;br /&gt;
*query: Query&lt;br /&gt;
*mutation: RootMutation&lt;br /&gt;
*subscription: RootSubscription&lt;br /&gt;
*fragment feht irgendwie in der Doc &lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist recht simpel und das Doc in knapp einer Stunde durchprobiert. So dass ich dann schnell an dem Punkt angelangt bin, dass ich ein Programm (PHP, Python oder JavaScript) brauche welches die fuer mich interessanten Daten in meine MySQL-DB uebertraegt. Ob ich zuerst anzeige und dann speichere, also sozusagen ueber die Datenbank puffere, oder umgekehrt muss ich noch festlegen.&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zum Subprotokoll===&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; ist ein WebSocket-Subprotokoll, das speziell für die Kommunikation zwischen Client und Server in GraphQL-Anwendungen entwickelt wurde. Es basiert auf dem WebSocket-Protokoll, das eine bidirektionale, persistente Verbindung zwischen einem Client und einem Server ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Im Kontext von GraphQL wird &amp;quot;graphql-transport-ws&amp;quot; verwendet, um GraphQL-Abfragen, Mutationen und Abonnements über WebSocket-Verbindungen zu übertragen. Im Gegensatz zum traditionellen HTTP-Protokoll, das eine separate Anfrage-Antwort-Struktur hat, ermöglicht WebSocket eine kontinuierliche Kommunikation zwischen Client und Server, was besonders nützlich ist, wenn Echtzeit-Aktualisierungen benötigt werden, wie bei Abonnements in GraphQL.&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; definiert spezifische Nachrichtenformate und Handshake-Verfahren, die für die GraphQL-Kommunikation über WebSockets erforderlich sind. Durch die Verwendung dieses Subprotokolls können Entwickler GraphQL-Anwendungen erstellen, die von den Echtzeitfähigkeiten von WebSocket profitieren, ohne auf die Vorteile und Flexibilität von GraphQL verzichten zu müssen.&lt;br /&gt;
&lt;br /&gt;
==Mehrfacher Hinweis Subprotokolwechsel Dezember 2022==&lt;br /&gt;
&lt;br /&gt;
 Die TibberAPI unterstuetzte zunaechst GraphQL-Websocket-Subscriptions mithilfe des Unterprotokolls graphql-ws. Diese Bibliothek wird/wurde jedoch archiviert und nicht mehr gepflegt. Daher wurde am 31. Maerz 2022 die Unterstützung für das Unterprotokoll graphql-transport-ws hinzugefuegt.&lt;br /&gt;
&lt;br /&gt;
 Die Unterstuetzung für das alte Protokoll wird im Dezember 2022 entfernt. Gleichzeitig aendert sich auch die URL für die Verbindung zum Websocket-Server. Zuvor konnte die Verbindung unter &#039;&#039;&#039;wss://api.tibber.com/v1-beta/gql/subscriptions&#039;&#039;&#039; hergestellt werden. Nach der Aenderung muss die URL ueber GraphQL abgefragt werden (siehe Beispiel unten) und der Client muss das Unterprotokoll graphql-transport-ws unterstuetzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight code=xml&amp;gt;&lt;br /&gt;
Frage:&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;websocketSubscriptionUrl&amp;quot;: &amp;quot;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Anfragenbegrenzung==&lt;br /&gt;
 Zum Schutz der API gilt eine Ratenbegrenzung von 100 Anfragen in 5 Minuten pro IP-Adresse. Beachten Sie, dass die Preise einmal täglich am Nachmittag berechnet werden (für Norwegen und Schweden sind sie zunächst vorläufig und werden später möglicherweise mit geringfügigen Änderungen nach Bestätigung der Wechselkurse endgültig festgelegt). Sie können „priceInfo.today“ und „priceInfo.tomorrow“ verwenden, um sie im Voraus abzurufen, anstatt nur „priceInfo.current“ für die aktuelle Stunde zu verwenden.&lt;br /&gt;
&lt;br /&gt;
=In medias res=&lt;br /&gt;
Zum Starten empfiehlt die Tibber-API-Doc, die fundamentalen Konzepte von GraphQL, die Kommunikation mit der API und den Explorer.&lt;br /&gt;
&lt;br /&gt;
Um die nachfolgenden Beispiele schnell zu verstehen, hier der Link zur [https://developer.tibber.com/docs/reference API-Referenz]&lt;br /&gt;
&lt;br /&gt;
 Bei der GraphQL-Abfragesprache geht es im Wesentlichen um die Auswahl von Feldern in Objekten.&lt;br /&gt;
&lt;br /&gt;
Auf der Docs-Reference-Seite gibt es einen interessanten Hinweis:&lt;br /&gt;
  This reference is auto-generated from https://api.tibber.com&lt;br /&gt;
&lt;br /&gt;
Es koennte als eine URL geben, die einem die aktuelle &#039;&#039;&#039;Tibber GraphQL Schema Reference&#039;&#039;&#039; direkt zur Auswertung ausgibt. Der Umweg ueber das Parsen der HTML-Ausgabe ginge zur Not natuerlich auch.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objekt Typen==&lt;br /&gt;
&lt;br /&gt;
Die Begriffsdifferenzierung Objekt und Object Types ist mir derzeit noch nicht klar. Ich werde wohl um die allgemeine [https://github.com/graphql/graphql-spec Spec von GraphQL] nicht herumkommen:-(&lt;br /&gt;
&lt;br /&gt;
Bei &#039;Home&#039; wird von einem Objekttyp gesprochen. Klar ist, man hat Objekte, die mit dem Schluesselwort type eingeleitet und dann beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 Einige Felder sind Skalare (Boolesche Werte, Zeichenfolgen, Ganzzahlen, Gleitkommazahlen), waehrend andere Objekttypen sind (Adresse, LegalEntity). Eine GraphQL-Abfrage muss vollstaendig auf Skalarebene sein. D.h. in einer Abfrage duerfen die abgefragten Felder keine Objekte (keine weiteren Objekttypen) sein, sondern hierarchisch heruntergebrochen auf weitere Inhalte bis es Skalare sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
This query is valid:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
This is not:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner   &amp;lt;- hier ist der Fehler. Dies ist kein Skalar!&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
The owner field is an object type and you would need to specify which scalar fields you would like returned:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner{&lt;br /&gt;
            name&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==query Datenabfrage (read)==&lt;br /&gt;
&lt;br /&gt;
query-&amp;gt;viewer&lt;br /&gt;
&lt;br /&gt;
Zuerst mit homes die Zaehlerplatz-iD holen und dan mit home (id: &amp;quot;&amp;lt;ID&amp;gt;&amp;quot;) nur die data dazu holen.&lt;br /&gt;
&lt;br /&gt;
Hello World;-)&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    login&lt;br /&gt;
    name&lt;br /&gt;
    userId&lt;br /&gt;
    accountType&lt;br /&gt;
    home (id: &amp;quot;1cdd8b6d-956c-447f-9b95-9a57801eaf4c&amp;quot;) {&lt;br /&gt;
      id&lt;br /&gt;
      features {&lt;br /&gt;
        realTimeConsumptionEnabled&lt;br /&gt;
      }&lt;br /&gt;
      subscriptions {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      currentSubscription {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      meteringPointData {&lt;br /&gt;
        consumptionEan&lt;br /&gt;
        gridCompany&lt;br /&gt;
        gridAreaCode&lt;br /&gt;
        priceAreaCode&lt;br /&gt;
        productionEan&lt;br /&gt;
        energyTaxType&lt;br /&gt;
        vatType&lt;br /&gt;
        estimatedAnnualConsumption&lt;br /&gt;
      }&lt;br /&gt;
      owner {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      mainFuseSize&lt;br /&gt;
    }&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant fand ich z.B.&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;priceAreaCode&amp;quot;: &amp;quot;Amprion&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;estimatedAnnualConsumption&amp;quot;: 500&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;mainFuseSize&amp;quot;: null&lt;br /&gt;
&lt;br /&gt;
und was da noch alles an Daten einzutragen ist.&lt;br /&gt;
 size: Int&lt;br /&gt;
 The size of the home in square meters&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
==mutation Datenveraenderung (write)==&lt;br /&gt;
&lt;br /&gt;
Mutationen gibt es nur 3, wobei die erste bei mir entfaellt.&lt;br /&gt;
&lt;br /&gt;
 sendMeterReading(input: MeterReadingInput!): MeterReadingResponse!&lt;br /&gt;
 Send meter reading for home (only available for Norwegian users) &lt;br /&gt;
 &lt;br /&gt;
 updateHome(input: UpdateHomeInput!): Home!&lt;br /&gt;
 Update home information&lt;br /&gt;
 &lt;br /&gt;
 sendPushNotification(input: PushNotificationInput!): PushNotificationResponse!&lt;br /&gt;
 Send notification to Tibber app on registered devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==subscription==&lt;br /&gt;
&lt;br /&gt;
Subscription hat nur 2 Felder, wobei dauerhaft nur das erste interessant ist.&lt;br /&gt;
&lt;br /&gt;
 liveMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to real-time measurement stream from Pulse or Watty device&lt;br /&gt;
&lt;br /&gt;
 testMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to test stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
subscription{&lt;br /&gt;
  liveMeasurement(homeId: &amp;quot;&amp;lt;homeID&amp;gt;&amp;quot;){&lt;br /&gt;
    timestamp&lt;br /&gt;
    power&lt;br /&gt;
    lastMeterConsumption&lt;br /&gt;
    accumulatedConsumption&lt;br /&gt;
    accumulatedProduction&lt;br /&gt;
    accumulatedConsumptionLastHour&lt;br /&gt;
    accumulatedProductionLastHour&lt;br /&gt;
    accumulatedCost&lt;br /&gt;
    accumulatedReward&lt;br /&gt;
    currency&lt;br /&gt;
    minPower&lt;br /&gt;
    averagePower&lt;br /&gt;
    maxPower&lt;br /&gt;
    powerProduction&lt;br /&gt;
    powerReactive&lt;br /&gt;
    powerProductionReactive&lt;br /&gt;
    minPowerProduction&lt;br /&gt;
    maxPowerProduction&lt;br /&gt;
    lastMeterProduction&lt;br /&gt;
    powerFactor&lt;br /&gt;
    voltagePhase1&lt;br /&gt;
    voltagePhase2&lt;br /&gt;
    voltagePhase3&lt;br /&gt;
    currentL1&lt;br /&gt;
    currentL2&lt;br /&gt;
    currentL3&lt;br /&gt;
    signalStrength&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==fragment==&lt;br /&gt;
&lt;br /&gt;
?&lt;br /&gt;
&lt;br /&gt;
Dazu habe ich bisher keine weiteren Infos gefunden.&lt;br /&gt;
&lt;br /&gt;
=Programmierung=&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung steht bei mir hier die Client-Programmierung im Vordergrund.&lt;br /&gt;
&lt;br /&gt;
==Query/Mutation==&lt;br /&gt;
&lt;br /&gt;
Die Abfragen und Aenderungen zu programmieren sind schnell erledigt.&lt;br /&gt;
&lt;br /&gt;
Der Einstieg auf der Konsole gelingt mit curl recht gut. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
 curl \&lt;br /&gt;
-H &amp;quot;Authorization: Bearer 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE \&lt;br /&gt;
-H &amp;quot;Content-Type: application/json&amp;quot; \&lt;br /&gt;
-X POST \&lt;br /&gt;
-d  &#039;mutation{ sendMeterReading(input:{homeId:&amp;quot;some home id&amp;quot;, reading: 123}){time reading  }}&#039; https://api.tibber.com/v1-beta/gql&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Subscription==&lt;br /&gt;
&lt;br /&gt;
Die Subscriptions sind schon ein wenig anspruchsvoller zu programmieren als Query und Mutation (QuM).&lt;br /&gt;
Ist QuM eine simple http-Anfrage mit einer Antwort im JSON-Format, ist es bei den Subscriptions ein Websocket, der einmal geoeffnet in unregelmaeszigen Abstaenden JSON-Pakete liefert.&lt;br /&gt;
&lt;br /&gt;
===Anforderungen für Websocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
* Clients muessen den User-Agent-Header festlegen, wenn sie die GraphQL-API aufrufen und eine Websocket-Verbindung oeffnen. Sowohl die Plattform als auch die Treiberversion muessen angegeben werden. Z.B. Homey/10.0.0 com.tibber/1.8.3&lt;br /&gt;
* Clients müssen das Unterprotokoll graphql-transport-ws implementieren&lt;br /&gt;
* Clients müssen Jitter und exponentielles Backoff implementieren, wenn sie Anfragen an die API wiederholen&lt;br /&gt;
* Clients müssen die WebSocket-Server-URL dynamisch abfragen&lt;br /&gt;
&lt;br /&gt;
===Best Practices für die Implementierung von WebSocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
Bei der Implementierung eines Clients zum Abonnieren von Live-Daten sind einige Szenarien zu beruecksichtigen:&lt;br /&gt;
* Keine Daten aufgrund von Netzwerkproblemen: Es kann sinnvoll sein, einen Wiederverbindungsmechanismus zu implementieren, falls mehrere Minuten lang keine Daten empfangen wurden. Bei einem erneuten Versuch muss zunaechst die alte Websocket-Verbindung zerstoert und ordnungsgemaeszer Jitter und exponentielle Verzoegerung implementiert werden.&lt;br /&gt;
* Ein Echtzeitgeraet wird moeglicherweise entfernt. Es wird empfohlen, vor dem erneuten Verbinden immer zu ueberpruefen, ob home.features.realTimeConsumptionEnabled einen echten Wert hat.&lt;br /&gt;
* Eine WebSocket-Verbindung kann aufgrund eines ungueltigen Authentifizierungstokens verboten sein (z. B. weil ein Benutzer möglicherweise sein Tibber-Konto entfernt hat). In diesem Fall darf der Client es nicht mit demselben Token erneut versuchen. Stattdessen muss der Autorisierungsprozess erneut ausgeführt werden, wobei der Benutzer neue Anmeldeinformationen eingibt.&lt;br /&gt;
* Es kann zu einem Neustart der Tibber-Websocket-API kommen, was bedeutet, dass die Verbindung mit dem &amp;quot;Code 1001&amp;quot; und dem Grund &amp;quot;Going away&amp;quot; geschlossen wird. In diesem Fall kann der Client nach einer zufälligen Verzoegerung von 1 bis 60 Sekunden (Jitter angewendet) die Verbindung wiederherstellen, um zu vermeiden, dass alle Clients gleichzeitig die Verbindung wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
In PHP habe ich wenig bis gar nichts gefunden. Ich wollte mir auch einfach die Nutzung einer GraphQL-Library ersparen. Python habe ich erst einmal an den Schluss verschoben und einem Integration in FHEM den Vorzug gegeben.&lt;br /&gt;
&lt;br /&gt;
===Perl (FHEM)===&lt;br /&gt;
Hier der Code der tatsaechlich funktioniert, obwohl er an ein paar Stellen nicht so vorgeht wie ich aus den Tibber-Docs erwartet hatte.&lt;br /&gt;
&lt;br /&gt;
HINWEIS: Dies ist der &amp;quot;nackte&amp;quot; Perl-Code. In der &amp;quot;Originaldatei&amp;quot; sind noch die &amp;quot;FHEM-Escapes&amp;quot; doppelter Strichpunkt und Zeilenfolgezeichen \ enthalten. Eine Eingabe ueber die FHEM-Kommandozeile hat bei mir nicht funktioniert. Erst das direkte Einkopieren in die fhem.conf zeigte das nachfolgende, funktionierende Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
connect:cmd:.connect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	# establish connection to websocket&lt;br /&gt;
	# format must also include portnumber if a path is to be specified&lt;br /&gt;
	$hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
	&lt;br /&gt;
	# callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
	# websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
	&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onDisconnect {&lt;br /&gt;
	my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect&lt;br /&gt;
	my $timerFunction = sub() {&lt;br /&gt;
		my ($hash) = @_;&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
		&lt;br /&gt;
		# only re-connect if device is not connected&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));&lt;br /&gt;
	};&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash, $timerFunction);&lt;br /&gt;
	&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onConnectionAck:websocketData:.*connection_ack.* {&lt;br /&gt;
	#websocketData contains the string &amp;quot;connection_ack&amp;quot;&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# do not proceed if connection is lost&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device not open&amp;quot; if (!defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	my $homeId = AttrVal($name, &amp;quot;homeId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myId = AttrVal($name, &amp;quot;myId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# build the query, do it in pieces, the comma at the end caused perl errors&lt;br /&gt;
	# so we put it together in this not very elegant way&lt;br /&gt;
	my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;&lt;br /&gt;
	$json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;&lt;br /&gt;
	$json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;&lt;br /&gt;
	$json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;&lt;br /&gt;
	$json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;&lt;br /&gt;
	$json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;&lt;br /&gt;
	$json .= &#039;}}&#039;;&lt;br /&gt;
	&lt;br /&gt;
	#send the string via websocket as ASCII&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
	DevIo_SimpleWrite($hash, $json, 2);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onNextLiveMeasurement:websocketData:.*next.*payload.*data.*liveMeasurement.* {&lt;br /&gt;
	#websocketData contains next-live-measurement-data&lt;br /&gt;
	my $val = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;{}&amp;quot;);&lt;br /&gt;
	my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};&lt;br /&gt;
	&lt;br /&gt;
	my $ret = &amp;quot;got values for:\n&amp;quot;;&lt;br /&gt;
	foreach my $k (sort keys %res) {&lt;br /&gt;
		$ret .= &amp;quot;$k\n&amp;quot;;&lt;br /&gt;
		readingsBulkUpdate($hash, makeReadingName($k), $res{$k});&lt;br /&gt;
	}&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Code Analyse====&lt;br /&gt;
&lt;br /&gt;
Es gibt 5 Eintrittspunkte:&lt;br /&gt;
* connect&lt;br /&gt;
* disconnect&lt;br /&gt;
* onDisconnect&lt;br /&gt;
* onConnectionAck&lt;br /&gt;
* onNextLiveMeasurement&lt;br /&gt;
&lt;br /&gt;
Die ersten beiden legen die Befehlsfolgen für ein Start-Kommando (set cmd connect, bzw. set cmd disconnect) oder Stop-Kommando fest.&lt;br /&gt;
Die letzten 3 reagieren auf die entsprechenden Events.&lt;br /&gt;
=====connect=====&lt;br /&gt;
&lt;br /&gt;
Lexikalische Variablen mittels my, bzw. my()&lt;br /&gt;
&lt;br /&gt;
 Jede Variable, die mit our deklariert oder auch &amp;quot;einfach so&amp;quot; ohne eine Deklaration verwendet wird, wird in die Symboltabelle des jeweils aktuellen Packages aufgenommen.&lt;br /&gt;
 Deklariert man dagegen eine Variable mit dem Operator my, so wird die entsprechende Variable in einer anderen Tabelle abgelegt, auf die kein expliziter Zugriff möglich ist. &lt;br /&gt;
 Neben der Tatsache, daß my-Variablen in einer eigenen Tabelle verwaltet werden, ist von besonderer Bedeutung, daß sie nur einen recht beschränkten Gültigkeitsbereich besitzen. Eine durch my erzeugte Variable steht nur in dem aktuellen Block (definiert durch geschweifte Klammern &amp;quot;{...}&amp;quot;), der aktuellen Datei oder innerhalb eines Arguments von eval() zur Verfügung. Außerhalb davon existieren diese Variablen nicht, es ist also (im Gegensatz zu our-Variablen) auch mit Hilfe des Package-Namens dort kein Zugriff möglich&lt;br /&gt;
 Es kann in einem Package durchaus zwei Variablen gleichen Namens geben: eine in der Symboltabelle und eine, die durch einen Aufruf von my entstanden ist. In einem solchen Falle wird bei einfacher Verwendung des Bezeichners auf die my-Variable zugegriffen. &lt;br /&gt;
&lt;br /&gt;
In $defs stehen alle Defines drin. In $hash wird also der Name des des aufrufenden Devices lokal hinterlegt und danach mit DevIO_IsOpen() der Status abgefragt. &lt;br /&gt;
$defs ist ein Hash. Ein Hash ist ein anderer Namen fuer ein assoziatives Array. Also eine ungeordnete Liste von Skalaren (Zahl oder String) die ueber einen Stringwert angesprochen (Lookup) werden koennen.  &lt;br /&gt;
&lt;br /&gt;
FHEM-Spezial:&lt;br /&gt;
 In $defs findet man alle Devices, z.b. $defs{&amp;quot;Rolladen_Tuere&amp;quot;} beinhaltet das Device.&lt;br /&gt;
 my @alle = keys %defs;&lt;br /&gt;
 my @teilmenge = defInfo(&#039;TYPE=notify&#039;,&#039;NAME&#039;); liefert ein array mit allen notify devices.&lt;br /&gt;
 z.B. my @teilmenge = defInfo(&#039;NAME=Rollladen.*&#039;,&#039;NAME&#039;);&lt;br /&gt;
&lt;br /&gt;
 return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
Sollte also das Device bereits im State &amp;quot;offen&amp;quot; sein, wird die connect-Routine abgebrochen. Dann erscheint in den Readings nicht Datum/Uhrzeit des Connects sondern die Meldung &amp;quot;Device already open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
 # establish connection to websocket&lt;br /&gt;
 # format must also include portnumber if a path is to be specified&lt;br /&gt;
 $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 HASH-Operationen (Beispiel):&lt;br /&gt;
 $href ={APR =&amp;gt; 4,AUG =&amp;gt; 8}; #anonymous hash&lt;br /&gt;
 $el = $href-&amp;gt;{APR}; $el = %{$href}{APR}; #access element of hash&lt;br /&gt;
 $href2 = {%{$href1}}; #copy hash&lt;br /&gt;
 if (ref($r) eq &amp;quot;HASH&amp;quot;) {} #checks if $r points to hash&lt;br /&gt;
&lt;br /&gt;
Dem Internal &amp;quot;DeviceName&amp;quot; wird das Ergebnis von AttrVal() zugewiesen. Bei mir ergab das:&lt;br /&gt;
 DeviceName wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&lt;br /&gt;
 FHEM-Referenz:&lt;br /&gt;
 AttrVal(&amp;lt;devicename&amp;gt;,&amp;lt;attribute&amp;gt;,&amp;lt;defaultvalue&amp;gt;)&lt;br /&gt;
 Gibt das entsprechende Attribut des Gerätes zurück&lt;br /&gt;
 { Value(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { OldValue(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { time_str2num(OldTimestamp(&amp;quot;wz&amp;quot;)) }&lt;br /&gt;
 { ReadingsVal(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, &amp;quot;20&amp;quot;)+0 }&lt;br /&gt;
 { ReadingsTimestamp(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, 0)}&lt;br /&gt;
 { AttrVal(&amp;quot;wz&amp;quot;, &amp;quot;room&amp;quot;, &amp;quot;none&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Das Attribut websocketURL ist bei mir wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions. Somit erklaert sich auch der Eintrag bei DeviceName. &amp;quot;wss:echo.websocket.org:443&amp;quot; ist nur der Defaultwert.&lt;br /&gt;
&lt;br /&gt;
Man haette zum Testen auch die folgenden beiden verwenden/eintragen koennen.&lt;br /&gt;
 There are free test servers that we can use to experiment with a WebSocket client, such as:&lt;br /&gt;
&lt;br /&gt;
    Hoppscotch – wss://echo-websocket.hoppscotch.io (GUI)&lt;br /&gt;
    λ if else – wss://ws.ifelse.io (GUI)&lt;br /&gt;
&lt;br /&gt;
 Both provide a GUI for testing via a browser. The former sends a timestamp to the client every second but doesn’t respond to client messages. The latter is a standard echo service that sends a copy of each received message back to the client. Both make sense in testing.&lt;br /&gt;
&lt;br /&gt;
Da curl nicht nativ websocket-Kommunikation unterstuetzt kann man auf folgende Tool ausweichen. Auch chrome bietet ein Websocket-Client-Plugin.&lt;br /&gt;
*wssh3&lt;br /&gt;
*websocat&lt;br /&gt;
*wscat&lt;br /&gt;
&lt;br /&gt;
 # special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
&lt;br /&gt;
 # callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
 # websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion wird direkt, ohne Namen, in das/den (?) Hash unter directReadFn abgelegt. &lt;br /&gt;
Die Callback-Funktion:&lt;br /&gt;
Hier wird mit Hilfe der Funktion DevIo_SimpleRead() die Rueckmeldung des Server eingelesen. Siehe [https://wiki.fhem.de/wiki/DevIo]&lt;br /&gt;
Hinweis zum Logging in FHEM: [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#Logging]&lt;br /&gt;
Die eigentliche Aktivitaet geschieht in den sieben Zeilen am Ende der Callback-Fkt. &lt;br /&gt;
 ReadingsAge(&amp;lt;devicename&amp;gt;,&amp;lt;reading&amp;gt;,&amp;lt;defaultvalue&amp;gt;) #gibt das Alter des Readings in Sekunden zurück. &lt;br /&gt;
 AttrVal hatten wir schon.&lt;br /&gt;
 isNext bekommt seinen Wert aus dem durch einen REGEX geliefert Wert des Buffers. Beispiel ($match) = ($dirty =~ /^(.*)$/s); &lt;br /&gt;
&lt;br /&gt;
 Die Funktion readingsBeginUpdate() bereitet die Definition mit dem Hash $hash auf ein Update von Readings vor. Dies betrifft insbesondere das Setzen von Umgebungsvariablen sowie dem aktuellen Zeitstempel als Änderungszeitpunkt. Der Aufruf dieser Funktion ist notwendig um eigentliche Updates mit der Funktion readingsBulkUpdate() auf der gewünschten Definition durchführen zu können. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#readingsBeginUpdate]&lt;br /&gt;
&lt;br /&gt;
Die Funktion readingsEndUpdate() beendet den Bulk-Update Prozess durch die Funktionen readingsBeginUpdate() &amp;amp; readingsBulkUpdate() und triggert optional die entsprechenden Events sämtlicher erzeugter Readings für die Definition $hash. Desweiteren werden nachgelagerte Tasks wie bspw. die Erzeugung von User-Readings (Attribut: userReadings), sowie die Erzeugung des STATE aufgrund des Attributs stateFormat durchgeführt. Sofern $do_trigger gesetzt ist, werden alle anstehenden Events nach Abschluss getriggert.&lt;br /&gt;
&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
Das ist die eigentliche Kontaktaufnahme. &lt;br /&gt;
&lt;br /&gt;
Dies dient vermutlich dazu, bis zum ersten Empfang von Daten, das Reading websocketDate zu leeren.&lt;br /&gt;
&lt;br /&gt;
 return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss ercheint dann im Reading connect die aktuelle Zeit.&lt;br /&gt;
&lt;br /&gt;
readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=====disconnect=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
 disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erlaeuterung:&lt;br /&gt;
&lt;br /&gt;
*Die Funktion RemoveInternalTimer löscht möglicherweise noch anstehende Timer welche mit dem Übergabeparameter $arg gescheduled sind. Optional kann man zusätzlich die Suche auf eine bestimmte Funktion $functionName weiter einschränken. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#RemoveInternalTimer]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von $hash ein und gibt diese zurück. [https://wiki.fhem.de/wiki/DevIo#DevIo_SimpleRead()]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition $hash. [https://wiki.fhem.de/wiki/DevIo#DevIo_CloseDev()]&lt;br /&gt;
&lt;br /&gt;
*POSIX:: [https://metacpan.org/pod/POSIX#strftime]&lt;br /&gt;
&lt;br /&gt;
=====onDisconnect=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=====onConnectionAck=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====onNextLiveMeasurement=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===JavaScript===&lt;br /&gt;
Zum Eingewoehnen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;ws://echo.websocket.org&#039;);&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung hergestellt.&#039;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
    socket.send(&#039;Hallo Server!&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&#039;Nachricht vom Server erhalten:&#039;, event.data);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und jetzt das Gerippe fuer Tibber mit Haut versehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
const WebSocket = require(&#039;ws&#039;);&lt;br /&gt;
&lt;br /&gt;
const query = `subscription {&lt;br /&gt;
    liveMeasurement(homeId: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;) {&lt;br /&gt;
      timestamp&lt;br /&gt;
      power&lt;br /&gt;
      accumulatedConsumption&lt;br /&gt;
      minPower&lt;br /&gt;
      maxPower&lt;br /&gt;
      averagePower&lt;br /&gt;
      voltagePhase1&lt;br /&gt;
      currentL1&lt;br /&gt;
      accumulatedCost&lt;br /&gt;
      currency&lt;br /&gt;
   } &lt;br /&gt;
}`;&lt;br /&gt;
&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&#039;,[&#039;graphql-transport-ws&#039;], { &lt;br /&gt;
        headers: {&lt;br /&gt;
                &#039;Content-Type&#039;: &#039;application/json&#039;,&lt;br /&gt;
                &#039;User-Agent&#039;: &#039;NodeJS tibberview&#039;,&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&amp;quot;WebSocket-Verbindung hergestellt.&amp;quot;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
   const connectionQuery = {&lt;br /&gt;
      type: &amp;quot;connection_init&amp;quot;,&lt;br /&gt;
      payload: {&lt;br /&gt;
          token: &amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;,&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    socket.send(JSON.stringify(connectionQuery));&lt;br /&gt;
    console.log(&amp;quot;Verbindung zum &#039;Tibber feed&#039; hergestellt.&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&amp;quot;Nachricht vom Server erhalten:&amp;quot;, event.data);&lt;br /&gt;
    const msg = JSON.parse(event.data);&lt;br /&gt;
&lt;br /&gt;
    if (msg.type === &amp;quot;connection_ack&amp;quot;) {&lt;br /&gt;
      console.log(&amp;quot;Tibber ACK empfangen: &amp;quot;, msg);&lt;br /&gt;
&lt;br /&gt;
      const realtimeDataQuery = {&lt;br /&gt;
        id: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&lt;br /&gt;
        type: &amp;quot;subscribe&amp;quot;,&lt;br /&gt;
        payload: { query }&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
      socket.send(JSON.stringify(realtimeDataQuery));&lt;br /&gt;
    }&lt;br /&gt;
    else if (msg.type === &amp;quot;next&amp;quot;) {&lt;br /&gt;
      if (!msg.payload.data) {&lt;br /&gt;
        console.log(&amp;quot;⚠  Nachricht hat keinen Inhalt.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      const data = msg.payload.data.liveMeasurement;&lt;br /&gt;
      socket.emit(&amp;quot;data&amp;quot;, data);&lt;br /&gt;
    } else {&lt;br /&gt;
      console.log(&amp;quot;Nachrichteninhalt: &amp;quot;, msg)&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm erzeugte folgende Ausgabe:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
worker:/mnt/1.5T_RAID/Programme/JavaScript/WebSocket # node tibberclient_demo.js &lt;br /&gt;
WebSocket-Verbindung hergestellt.&lt;br /&gt;
Verbindung zum &#039;Tibber feed&#039; hergestellt.&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;type&amp;quot;:&amp;quot;connection_ack&amp;quot;}&lt;br /&gt;
Tibber ACK empfangen:  { type: &#039;connection_ack&#039; }&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:48.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.6,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:51.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:52.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:230,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:53.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.2,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:54.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.1,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:55.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:230.2,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:56.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:57.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.9,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:58.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:59.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.4,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:00.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.7,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:01.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.6,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
^C&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An der Stelle mache ich jetzt einen Ausflug in PHP.&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
Nachdem der Websocket-Kontakt mit JavaScript funktioniert hat, kommt jetzt ein Serverprogramm was folgendes tun soll.&lt;br /&gt;
&lt;br /&gt;
*Abfrage der Basisdaten&lt;br /&gt;
*Objekt Types Builder&lt;br /&gt;
*Mutation&lt;br /&gt;
*Websocket Subsrciption&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===Python===&lt;br /&gt;
&lt;br /&gt;
Quelle: https://github.com/Danielhiversen/pyTibber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Client-Beispiel mit eingesetzem DEMO_TOKEN und user_agent&lt;br /&gt;
Obwohl DEMO_TOKEN in tibber.py definiert ist, wird es nicht uebernommen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import tibber&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def start():&lt;br /&gt;
  tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
  await tibber_connection.update_info()&lt;br /&gt;
  print(tibber_connection.name)&lt;br /&gt;
&lt;br /&gt;
  home = tibber_connection.get_homes()[0]&lt;br /&gt;
  await home.fetch_consumption_data()&lt;br /&gt;
  await home.update_info()&lt;br /&gt;
  print(home.address1)&lt;br /&gt;
&lt;br /&gt;
  await home.update_price_info()&lt;br /&gt;
  print(home.current_price_info)&lt;br /&gt;
  &lt;br /&gt;
  # await tibber_connection.close_connection()&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(start())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
import aiohttp&lt;br /&gt;
import tibber&lt;br /&gt;
&lt;br /&gt;
def _callback(pkg):&lt;br /&gt;
    print(pkg)&lt;br /&gt;
    data = pkg.get(&amp;quot;data&amp;quot;)&lt;br /&gt;
    if data is None:&lt;br /&gt;
        return&lt;br /&gt;
    print(data.get(&amp;quot;liveMeasurement&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def run():&lt;br /&gt;
    async with aiohttp.ClientSession() as session:&lt;br /&gt;
        tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, websession=session, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
        await tibber_connection.update_info()&lt;br /&gt;
    home = tibber_connection.get_homes()[0]&lt;br /&gt;
    await home.rt_subscribe(_callback)    &lt;br /&gt;
&lt;br /&gt;
    while True:&lt;br /&gt;
      await asyncio.sleep(10)&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(run())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4843</id>
		<title>Tibber Pulse (API)</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4843"/>
		<updated>2026-04-10T06:09:08Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Da ich aktuell vom Energieversorger einen zweiten Hausanschluss (40kW :-() gelegt bekommen habe und ich für die Wallboxen zur Foerderung einen 100% Oekostrom haben musste, habe ich kurzerhand einen Vertrag mit Tibber (mal zum Testen) abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
=Installation=&lt;br /&gt;
Pulse IR und Bridge&lt;br /&gt;
[[Datei:Tibber Pulse IR und Bridge.png|mini|Tibber Pulse IR und Bridge]]&lt;br /&gt;
Eigentlich verlief die Installation problemlos. Fuer mich war allerdings der Assistent in der Tibber-App zu kindisch. Das ist eher was für Apple-User. Ich haette mir an der ein oder anderen Stelle mehr Hardcore-Infos gewuenscht. Da der mehrstufige Prozess (WLAN-Bridge-Pulse-Zaehler) doch einige Solperfallen enthaelt, ist es fuer mich eher frustrierend jedesmal bei einem Fehler zurueck auf Los geschickt zu werden. Das geht definitiv besser. Ist aber wieder ein Beispiel, dass die Informatiker-Jobs heute nur noch durch 5-jaehrige Schimpansen (ungelernte ;-)) besetzt werden.&lt;br /&gt;
&lt;br /&gt;
Bei mir gab es letztendlich nur einen gravierenden Fehler. Von den mitgelieferten Akkus des Pulse war einer defekt. Bei mir sind das zwei AA-Zellen auf LiBasis! Ein Nachladen mit einem Lithium-1.5V-Lader hat keinen Erfolg gebracht. Erst der Austausch gegen 2 neue Zellen war erfolgreich.&lt;br /&gt;
&lt;br /&gt;
=App=&lt;br /&gt;
[[Datei:TibberAppIcon.png|mini|TibberAppIcon]]&lt;br /&gt;
Die App find ich ziemlich verquer. Sobald Rechnungsanschrift und Installationsorte unterschiedlich sind, geht das Chaos los. Auch der Support tut sich dann schwer. Ich traue mich gar nicht so richtig weitere Vertraege abzuschlieszen.&lt;br /&gt;
Alles ueber die App machen zu wollen/muessen ist ... Das gehoert fuer mich ins WebUserPortal.&lt;br /&gt;
&lt;br /&gt;
Was echt nervt ist, dass sich gefühlt jeden Monat das LookandFeel aendert. Schon die dritte oder vierte Designaenderung des Homescreens (Zuhause).&lt;br /&gt;
&lt;br /&gt;
=API=&lt;br /&gt;
Da die App eher suboptimal ist, habe ich recht zuegig damit angefangen mir die notwendigen Infos ueber die API auf eine Infoseite im Intranet zu holen. Ein Integration in FHEM und/oder HA steht noch aus.&lt;br /&gt;
&lt;br /&gt;
=Erste Schritte=&lt;br /&gt;
&lt;br /&gt;
Uebersicht [https://developer.tibber.com/docs/overview]&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung: Die Plattform wird über GraphQL verfügbar gemacht, eine Technologie, die von Facebook konzipiert und entwickelt wurde.&lt;br /&gt;
&lt;br /&gt;
==Was ist GraphQL?==&lt;br /&gt;
&lt;br /&gt;
[https://de.wikipedia.org/wiki/GraphQL Wikipedia]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/graphql GraphQL]&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zu GraphQL===&lt;br /&gt;
&lt;br /&gt;
GraphQL ist eine von Facebook entwickelte Abfragesprache und Laufzeitumgebung für APIs (Application Programming Interfaces). Im Gegensatz zu traditionellen RESTful APIs, bei denen die Client-Anwendung verschiedene Endpunkte aufruft, um Daten abzurufen oder zu manipulieren, ermöglicht GraphQL dem Client, genau die Daten abzurufen, die er benötigt, indem er eine einzige Abfrage an den Server sendet.&lt;br /&gt;
&lt;br /&gt;
Hier sind einige Hauptmerkmale von GraphQL:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;Declarative Datenabfrage&#039;&#039;&#039;: Der Client spezifiziert genau, welche Daten er vom Server erhalten möchte, indem er eine GraphQL-Abfrage definiert, die die benötigten Felder und deren Struktur beschreibt. Dadurch kann der Client genau die Daten erhalten, die er benötigt, ohne Overfetching oder Underfetching.&lt;br /&gt;
    &#039;&#039;&#039;Typsystem&#039;&#039;&#039;: GraphQL definiert ein Typsystem, das die Struktur und den Typ der Daten beschreibt, die von der API zurückgegeben werden können. Dies ermöglicht eine strikte Typisierung und verbesserte Dokumentation.&lt;br /&gt;
    &#039;&#039;&#039;Single-Endpoint-Architektur&#039;&#039;&#039;: Im Gegensatz zu RESTful APIs, die oft mehrere Endpunkte für verschiedene Ressourcentypen haben, hat eine GraphQL-API normalerweise nur einen Endpunkt, über den alle Abfragen und Mutationen gesendet werden.&lt;br /&gt;
    &#039;&#039;&#039;Echtzeitfähigkeit&#039;&#039;&#039;: GraphQL unterstützt auch Abonnements, die es ermöglichen, Daten in Echtzeit zu empfangen, wenn sich etwas ändert. Dies ist besonders nützlich für Anwendungsfälle wie Live-Chats, Benachrichtigungen und Echtzeit-Dashboard-Aktualisierungen.&lt;br /&gt;
    &#039;&#039;&#039;Entwicklungserfahrung&#039;&#039;&#039;: GraphQL bietet eine verbesserte Entwicklererfahrung, da Clients genau die Daten erhalten können, die sie benötigen, und Entwickler APIs schneller entwickeln können, ohne sich um Versionierung oder unerwartete Seiteneffekte durch Änderungen an der Backend-API kümmern zu müssen.&lt;br /&gt;
&lt;br /&gt;
Insgesamt bietet GraphQL eine flexible, effiziente und moderne Möglichkeit, APIs zu entwerfen und zu verwenden, die den Anforderungen moderner Anwendungen besser gerecht wird als traditionelle RESTful APIs.&lt;br /&gt;
&lt;br /&gt;
Auf der Tibber-Seite heiszt es:&lt;br /&gt;
&lt;br /&gt;
 The GraphQL query language is basically about selecting fields on objects.&lt;br /&gt;
&lt;br /&gt;
Was dies bedeutet kommt weiter unten. Weiter heiszt es auf der Tibber Seite.&lt;br /&gt;
&lt;br /&gt;
    GraphQL ist eine Abfragesprache für Ihre(? unsere) API und eine serverseitige Laufzeitumgebung zum Ausführen von Abfragen mithilfe eines Typsystems, das Sie für Ihre Daten definieren. GraphQL ist nicht an eine bestimmte Datenbank oder Speicher-Engine gebunden und wird stattdessen durch Ihren vorhandenen Code und Ihre Daten unterstützt.&lt;br /&gt;
&lt;br /&gt;
Mit der Uebersetzung von Google hadere ich noch. Der Satz kommt vermutlich ursrpruenglich von Facebook und wurde von Tibber 1:1 uebernommen.&lt;br /&gt;
&lt;br /&gt;
Der naechste Satz gibt schon mehr Aufschluss:&lt;br /&gt;
 Tibber hat sich für GraphQL als API entschieden, weil es unseren Integratoren und uns selbst Flexibilitaet bietet. Die Moeglichkeit, die gewuenschten Daten genau zu definieren, macht die Nutzung wesentlich einfacher als herkoemmliche [https://www.ibm.com/de-de/topics/rest-apis REST-basierte APIs]. Es ist auch sehr schoen, dass die serverseitige Implementierung von GraphQL sehr gut mit unserer Microservice-Architektur zusammenspielt :-)&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg nutze ich den [https://developer.tibber.com/explorer Api Explorer im TibberDev]. Nachdem man sich dort einen Personal Token angelegt hat, kann&#039;s direkt losgehen. Der [https://developer.tibber.com/explorer GraphiQL] hat auch eine Autovervollstaendigung. Sehr praktisch.&lt;br /&gt;
[[Datei:TibberDevAccount.png|mini|TibberDevAccount]]&lt;br /&gt;
Die Root-Types sind &lt;br /&gt;
*query: Query&lt;br /&gt;
*mutation: RootMutation&lt;br /&gt;
*subscription: RootSubscription&lt;br /&gt;
*fragment feht irgendwie in der Doc &lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist recht simpel und das Doc in knapp einer Stunde durchprobiert. So dass ich dann schnell an dem Punkt angelangt bin, dass ich ein Programm (PHP, Python oder JavaScript) brauche welches die fuer mich interessanten Daten in meine MySQL-DB uebertraegt. Ob ich zuerst anzeige und dann speichere, also sozusagen ueber die Datenbank puffere, oder umgekehrt muss ich noch festlegen.&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zum Subprotokoll===&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; ist ein WebSocket-Subprotokoll, das speziell für die Kommunikation zwischen Client und Server in GraphQL-Anwendungen entwickelt wurde. Es basiert auf dem WebSocket-Protokoll, das eine bidirektionale, persistente Verbindung zwischen einem Client und einem Server ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Im Kontext von GraphQL wird &amp;quot;graphql-transport-ws&amp;quot; verwendet, um GraphQL-Abfragen, Mutationen und Abonnements über WebSocket-Verbindungen zu übertragen. Im Gegensatz zum traditionellen HTTP-Protokoll, das eine separate Anfrage-Antwort-Struktur hat, ermöglicht WebSocket eine kontinuierliche Kommunikation zwischen Client und Server, was besonders nützlich ist, wenn Echtzeit-Aktualisierungen benötigt werden, wie bei Abonnements in GraphQL.&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; definiert spezifische Nachrichtenformate und Handshake-Verfahren, die für die GraphQL-Kommunikation über WebSockets erforderlich sind. Durch die Verwendung dieses Subprotokolls können Entwickler GraphQL-Anwendungen erstellen, die von den Echtzeitfähigkeiten von WebSocket profitieren, ohne auf die Vorteile und Flexibilität von GraphQL verzichten zu müssen.&lt;br /&gt;
&lt;br /&gt;
==Mehrfacher Hinweis Subprotokolwechsel Dezember 2022==&lt;br /&gt;
&lt;br /&gt;
 Die TibberAPI unterstuetzte zunaechst GraphQL-Websocket-Subscriptions mithilfe des Unterprotokolls graphql-ws. Diese Bibliothek wird/wurde jedoch archiviert und nicht mehr gepflegt. Daher wurde am 31. Maerz 2022 die Unterstützung für das Unterprotokoll graphql-transport-ws hinzugefuegt.&lt;br /&gt;
&lt;br /&gt;
 Die Unterstuetzung für das alte Protokoll wird im Dezember 2022 entfernt. Gleichzeitig aendert sich auch die URL für die Verbindung zum Websocket-Server. Zuvor konnte die Verbindung unter &#039;&#039;&#039;wss://api.tibber.com/v1-beta/gql/subscriptions&#039;&#039;&#039; hergestellt werden. Nach der Aenderung muss die URL ueber GraphQL abgefragt werden (siehe Beispiel unten) und der Client muss das Unterprotokoll graphql-transport-ws unterstuetzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight code=xml&amp;gt;&lt;br /&gt;
Frage:&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;websocketSubscriptionUrl&amp;quot;: &amp;quot;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Anfragenbegrenzung==&lt;br /&gt;
 Zum Schutz der API gilt eine Ratenbegrenzung von 100 Anfragen in 5 Minuten pro IP-Adresse. Beachten Sie, dass die Preise einmal täglich am Nachmittag berechnet werden (für Norwegen und Schweden sind sie zunächst vorläufig und werden später möglicherweise mit geringfügigen Änderungen nach Bestätigung der Wechselkurse endgültig festgelegt). Sie können „priceInfo.today“ und „priceInfo.tomorrow“ verwenden, um sie im Voraus abzurufen, anstatt nur „priceInfo.current“ für die aktuelle Stunde zu verwenden.&lt;br /&gt;
&lt;br /&gt;
=In medias res=&lt;br /&gt;
Zum Starten empfiehlt die Tibber-API-Doc, die fundamentalen Konzepte von GraphQL, die Kommunikation mit der API und den Explorer.&lt;br /&gt;
&lt;br /&gt;
Um die nachfolgenden Beispiele schnell zu verstehen, hier der Link zur [https://developer.tibber.com/docs/reference API-Referenz]&lt;br /&gt;
&lt;br /&gt;
 Bei der GraphQL-Abfragesprache geht es im Wesentlichen um die Auswahl von Feldern in Objekten.&lt;br /&gt;
&lt;br /&gt;
Auf der Docs-Reference-Seite gibt es einen interessanten Hinweis:&lt;br /&gt;
  This reference is auto-generated from https://api.tibber.com&lt;br /&gt;
&lt;br /&gt;
Es koennte als eine URL geben, die einem die aktuelle &#039;&#039;&#039;Tibber GraphQL Schema Reference&#039;&#039;&#039; direkt zur Auswertung ausgibt. Der Umweg ueber das Parsen der HTML-Ausgabe ginge zur Not natuerlich auch.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objekt Typen==&lt;br /&gt;
&lt;br /&gt;
Die Begriffsdifferenzierung Objekt und Object Types ist mir derzeit noch nicht klar. Ich werde wohl um die allgemeine [https://github.com/graphql/graphql-spec Spec von GraphQL] nicht herumkommen:-(&lt;br /&gt;
&lt;br /&gt;
Bei &#039;Home&#039; wird von einem Objekttyp gesprochen. Klar ist man hat Objekte, die mit dem Schluesselwort type eingeleitet und dann beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 Einige Felder sind Skalare (Boolesche Werte, Zeichenfolgen, Ganzzahlen, Gleitkommazahlen), waehrend andere Objekttypen sind (Adresse, LegalEntity). Eine GraphQL-Abfrage muss vollstaendig auf Skalarebene sein. D.h. in einer Abfrage duerfen die abgefragten Felder keine Objekte (keine weiteren Objekttypen) sein, sondern hierarchisch heruntergebrochen auf weitere Inhalte bis es Skalare sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
This query is valid:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
This is not:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner   &amp;lt;- hier ist der Fehler. Dies ist kein Skalar!&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
The owner field is an object type and you would need to specify which scalar fields you would like returned:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner{&lt;br /&gt;
            name&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==query Datenabfrage (read)==&lt;br /&gt;
&lt;br /&gt;
query-&amp;gt;viewer&lt;br /&gt;
&lt;br /&gt;
Zuerst mit homes die Zaehlerplatz-iD holen und dan mit home (id: &amp;quot;&amp;lt;ID&amp;gt;&amp;quot;) nur die data dazu holen.&lt;br /&gt;
&lt;br /&gt;
Hello World;-)&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    login&lt;br /&gt;
    name&lt;br /&gt;
    userId&lt;br /&gt;
    accountType&lt;br /&gt;
    home (id: &amp;quot;1cdd8b6d-956c-447f-9b95-9a57801eaf4c&amp;quot;) {&lt;br /&gt;
      id&lt;br /&gt;
      features {&lt;br /&gt;
        realTimeConsumptionEnabled&lt;br /&gt;
      }&lt;br /&gt;
      subscriptions {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      currentSubscription {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      meteringPointData {&lt;br /&gt;
        consumptionEan&lt;br /&gt;
        gridCompany&lt;br /&gt;
        gridAreaCode&lt;br /&gt;
        priceAreaCode&lt;br /&gt;
        productionEan&lt;br /&gt;
        energyTaxType&lt;br /&gt;
        vatType&lt;br /&gt;
        estimatedAnnualConsumption&lt;br /&gt;
      }&lt;br /&gt;
      owner {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      mainFuseSize&lt;br /&gt;
    }&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant fand ich z.B.&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;priceAreaCode&amp;quot;: &amp;quot;Amprion&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;estimatedAnnualConsumption&amp;quot;: 500&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;mainFuseSize&amp;quot;: null&lt;br /&gt;
&lt;br /&gt;
und was da noch alles an Daten einzutragen ist.&lt;br /&gt;
 size: Int&lt;br /&gt;
 The size of the home in square meters&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
==mutation Datenveraenderung (write)==&lt;br /&gt;
&lt;br /&gt;
Mutationen gibt es nur 3, wobei die erste bei mir entfaellt.&lt;br /&gt;
&lt;br /&gt;
 sendMeterReading(input: MeterReadingInput!): MeterReadingResponse!&lt;br /&gt;
 Send meter reading for home (only available for Norwegian users) &lt;br /&gt;
 &lt;br /&gt;
 updateHome(input: UpdateHomeInput!): Home!&lt;br /&gt;
 Update home information&lt;br /&gt;
 &lt;br /&gt;
 sendPushNotification(input: PushNotificationInput!): PushNotificationResponse!&lt;br /&gt;
 Send notification to Tibber app on registered devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==subscription==&lt;br /&gt;
&lt;br /&gt;
Subscription hat nur 2 Felder, wobei dauerhaft nur das erste interessant ist.&lt;br /&gt;
&lt;br /&gt;
 liveMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to real-time measurement stream from Pulse or Watty device&lt;br /&gt;
&lt;br /&gt;
 testMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to test stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
subscription{&lt;br /&gt;
  liveMeasurement(homeId: &amp;quot;&amp;lt;homeID&amp;gt;&amp;quot;){&lt;br /&gt;
    timestamp&lt;br /&gt;
    power&lt;br /&gt;
    lastMeterConsumption&lt;br /&gt;
    accumulatedConsumption&lt;br /&gt;
    accumulatedProduction&lt;br /&gt;
    accumulatedConsumptionLastHour&lt;br /&gt;
    accumulatedProductionLastHour&lt;br /&gt;
    accumulatedCost&lt;br /&gt;
    accumulatedReward&lt;br /&gt;
    currency&lt;br /&gt;
    minPower&lt;br /&gt;
    averagePower&lt;br /&gt;
    maxPower&lt;br /&gt;
    powerProduction&lt;br /&gt;
    powerReactive&lt;br /&gt;
    powerProductionReactive&lt;br /&gt;
    minPowerProduction&lt;br /&gt;
    maxPowerProduction&lt;br /&gt;
    lastMeterProduction&lt;br /&gt;
    powerFactor&lt;br /&gt;
    voltagePhase1&lt;br /&gt;
    voltagePhase2&lt;br /&gt;
    voltagePhase3&lt;br /&gt;
    currentL1&lt;br /&gt;
    currentL2&lt;br /&gt;
    currentL3&lt;br /&gt;
    signalStrength&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==fragment==&lt;br /&gt;
&lt;br /&gt;
?&lt;br /&gt;
&lt;br /&gt;
Dazu habe ich bisher keine weiteren Infos gefunden.&lt;br /&gt;
&lt;br /&gt;
=Programmierung=&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung steht bei mir hier die Client-Programmierung im Vordergrund.&lt;br /&gt;
&lt;br /&gt;
==Query/Mutation==&lt;br /&gt;
&lt;br /&gt;
Die Abfragen und Aenderungen zu programmieren sind schnell erledigt.&lt;br /&gt;
&lt;br /&gt;
Der Einstieg auf der Konsole gelingt mit curl recht gut. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
 curl \&lt;br /&gt;
-H &amp;quot;Authorization: Bearer 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE \&lt;br /&gt;
-H &amp;quot;Content-Type: application/json&amp;quot; \&lt;br /&gt;
-X POST \&lt;br /&gt;
-d  &#039;mutation{ sendMeterReading(input:{homeId:&amp;quot;some home id&amp;quot;, reading: 123}){time reading  }}&#039; https://api.tibber.com/v1-beta/gql&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Subscription==&lt;br /&gt;
&lt;br /&gt;
Die Subscriptions sind schon ein wenig anspruchsvoller zu programmieren als Query und Mutation (QuM).&lt;br /&gt;
Ist QuM eine simple http-Anfrage mit einer Antwort im JSON-Format, ist es bei den Subscriptions ein Websocket, der einmal geoeffnet in unregelmaeszigen Abstaenden JSON-Pakete liefert.&lt;br /&gt;
&lt;br /&gt;
===Anforderungen für Websocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
* Clients muessen den User-Agent-Header festlegen, wenn sie die GraphQL-API aufrufen und eine Websocket-Verbindung oeffnen. Sowohl die Plattform als auch die Treiberversion muessen angegeben werden. Z.B. Homey/10.0.0 com.tibber/1.8.3&lt;br /&gt;
* Clients müssen das Unterprotokoll graphql-transport-ws implementieren&lt;br /&gt;
* Clients müssen Jitter und exponentielles Backoff implementieren, wenn sie Anfragen an die API wiederholen&lt;br /&gt;
* Clients müssen die WebSocket-Server-URL dynamisch abfragen&lt;br /&gt;
&lt;br /&gt;
===Best Practices für die Implementierung von WebSocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
Bei der Implementierung eines Clients zum Abonnieren von Live-Daten sind einige Szenarien zu beruecksichtigen:&lt;br /&gt;
* Keine Daten aufgrund von Netzwerkproblemen: Es kann sinnvoll sein, einen Wiederverbindungsmechanismus zu implementieren, falls mehrere Minuten lang keine Daten empfangen wurden. Bei einem erneuten Versuch muss zunaechst die alte Websocket-Verbindung zerstoert und ordnungsgemaeszer Jitter und exponentielle Verzoegerung implementiert werden.&lt;br /&gt;
* Ein Echtzeitgeraet wird moeglicherweise entfernt. Es wird empfohlen, vor dem erneuten Verbinden immer zu ueberpruefen, ob home.features.realTimeConsumptionEnabled einen echten Wert hat.&lt;br /&gt;
* Eine WebSocket-Verbindung kann aufgrund eines ungueltigen Authentifizierungstokens verboten sein (z. B. weil ein Benutzer möglicherweise sein Tibber-Konto entfernt hat). In diesem Fall darf der Client es nicht mit demselben Token erneut versuchen. Stattdessen muss der Autorisierungsprozess erneut ausgeführt werden, wobei der Benutzer neue Anmeldeinformationen eingibt.&lt;br /&gt;
* Es kann zu einem Neustart der Tibber-Websocket-API kommen, was bedeutet, dass die Verbindung mit dem &amp;quot;Code 1001&amp;quot; und dem Grund &amp;quot;Going away&amp;quot; geschlossen wird. In diesem Fall kann der Client nach einer zufälligen Verzoegerung von 1 bis 60 Sekunden (Jitter angewendet) die Verbindung wiederherstellen, um zu vermeiden, dass alle Clients gleichzeitig die Verbindung wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
In PHP habe ich wenig bis gar nichts gefunden. Ich wollte mir auch einfach die Nutzung einer GraphQL-Library ersparen. Python habe ich erst einmal an den Schluss verschoben und einem Integration in FHEM den Vorzug gegeben.&lt;br /&gt;
&lt;br /&gt;
===Perl (FHEM)===&lt;br /&gt;
Hier der Code der tatsaechlich funktioniert, obwohl er an ein paar Stellen nicht so vorgeht wie ich aus den Tibber-Docs erwartet hatte.&lt;br /&gt;
&lt;br /&gt;
HINWEIS: Dies ist der &amp;quot;nackte&amp;quot; Perl-Code. In der &amp;quot;Originaldatei&amp;quot; sind noch die &amp;quot;FHEM-Escapes&amp;quot; doppelter Strichpunkt und Zeilenfolgezeichen \ enthalten. Eine Eingabe ueber die FHEM-Kommandozeile hat bei mir nicht funktioniert. Erst das direkte Einkopieren in die fhem.conf zeigte das nachfolgende, funktionierende Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
connect:cmd:.connect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	# establish connection to websocket&lt;br /&gt;
	# format must also include portnumber if a path is to be specified&lt;br /&gt;
	$hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
	&lt;br /&gt;
	# callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
	# websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
	&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onDisconnect {&lt;br /&gt;
	my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect&lt;br /&gt;
	my $timerFunction = sub() {&lt;br /&gt;
		my ($hash) = @_;&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
		&lt;br /&gt;
		# only re-connect if device is not connected&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));&lt;br /&gt;
	};&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash, $timerFunction);&lt;br /&gt;
	&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onConnectionAck:websocketData:.*connection_ack.* {&lt;br /&gt;
	#websocketData contains the string &amp;quot;connection_ack&amp;quot;&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# do not proceed if connection is lost&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device not open&amp;quot; if (!defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	my $homeId = AttrVal($name, &amp;quot;homeId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myId = AttrVal($name, &amp;quot;myId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# build the query, do it in pieces, the comma at the end caused perl errors&lt;br /&gt;
	# so we put it together in this not very elegant way&lt;br /&gt;
	my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;&lt;br /&gt;
	$json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;&lt;br /&gt;
	$json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;&lt;br /&gt;
	$json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;&lt;br /&gt;
	$json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;&lt;br /&gt;
	$json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;&lt;br /&gt;
	$json .= &#039;}}&#039;;&lt;br /&gt;
	&lt;br /&gt;
	#send the string via websocket as ASCII&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
	DevIo_SimpleWrite($hash, $json, 2);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onNextLiveMeasurement:websocketData:.*next.*payload.*data.*liveMeasurement.* {&lt;br /&gt;
	#websocketData contains next-live-measurement-data&lt;br /&gt;
	my $val = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;{}&amp;quot;);&lt;br /&gt;
	my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};&lt;br /&gt;
	&lt;br /&gt;
	my $ret = &amp;quot;got values for:\n&amp;quot;;&lt;br /&gt;
	foreach my $k (sort keys %res) {&lt;br /&gt;
		$ret .= &amp;quot;$k\n&amp;quot;;&lt;br /&gt;
		readingsBulkUpdate($hash, makeReadingName($k), $res{$k});&lt;br /&gt;
	}&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Code Analyse====&lt;br /&gt;
&lt;br /&gt;
Es gibt 5 Eintrittspunkte:&lt;br /&gt;
* connect&lt;br /&gt;
* disconnect&lt;br /&gt;
* onDisconnect&lt;br /&gt;
* onConnectionAck&lt;br /&gt;
* onNextLiveMeasurement&lt;br /&gt;
&lt;br /&gt;
Die ersten beiden legen die Befehlsfolgen für ein Start-Kommando (set cmd connect, bzw. set cmd disconnect) oder Stop-Kommando fest.&lt;br /&gt;
Die letzten 3 reagieren auf die entsprechenden Events.&lt;br /&gt;
=====connect=====&lt;br /&gt;
&lt;br /&gt;
Lexikalische Variablen mittels my, bzw. my()&lt;br /&gt;
&lt;br /&gt;
 Jede Variable, die mit our deklariert oder auch &amp;quot;einfach so&amp;quot; ohne eine Deklaration verwendet wird, wird in die Symboltabelle des jeweils aktuellen Packages aufgenommen.&lt;br /&gt;
 Deklariert man dagegen eine Variable mit dem Operator my, so wird die entsprechende Variable in einer anderen Tabelle abgelegt, auf die kein expliziter Zugriff möglich ist. &lt;br /&gt;
 Neben der Tatsache, daß my-Variablen in einer eigenen Tabelle verwaltet werden, ist von besonderer Bedeutung, daß sie nur einen recht beschränkten Gültigkeitsbereich besitzen. Eine durch my erzeugte Variable steht nur in dem aktuellen Block (definiert durch geschweifte Klammern &amp;quot;{...}&amp;quot;), der aktuellen Datei oder innerhalb eines Arguments von eval() zur Verfügung. Außerhalb davon existieren diese Variablen nicht, es ist also (im Gegensatz zu our-Variablen) auch mit Hilfe des Package-Namens dort kein Zugriff möglich&lt;br /&gt;
 Es kann in einem Package durchaus zwei Variablen gleichen Namens geben: eine in der Symboltabelle und eine, die durch einen Aufruf von my entstanden ist. In einem solchen Falle wird bei einfacher Verwendung des Bezeichners auf die my-Variable zugegriffen. &lt;br /&gt;
&lt;br /&gt;
In $defs stehen alle Defines drin. In $hash wird also der Name des des aufrufenden Devices lokal hinterlegt und danach mit DevIO_IsOpen() der Status abgefragt. &lt;br /&gt;
$defs ist ein Hash. Ein Hash ist ein anderer Namen fuer ein assoziatives Array. Also eine ungeordnete Liste von Skalaren (Zahl oder String) die ueber einen Stringwert angesprochen (Lookup) werden koennen.  &lt;br /&gt;
&lt;br /&gt;
FHEM-Spezial:&lt;br /&gt;
 In $defs findet man alle Devices, z.b. $defs{&amp;quot;Rolladen_Tuere&amp;quot;} beinhaltet das Device.&lt;br /&gt;
 my @alle = keys %defs;&lt;br /&gt;
 my @teilmenge = defInfo(&#039;TYPE=notify&#039;,&#039;NAME&#039;); liefert ein array mit allen notify devices.&lt;br /&gt;
 z.B. my @teilmenge = defInfo(&#039;NAME=Rollladen.*&#039;,&#039;NAME&#039;);&lt;br /&gt;
&lt;br /&gt;
 return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
Sollte also das Device bereits im State &amp;quot;offen&amp;quot; sein, wird die connect-Routine abgebrochen. Dann erscheint in den Readings nicht Datum/Uhrzeit des Connects sondern die Meldung &amp;quot;Device already open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
 # establish connection to websocket&lt;br /&gt;
 # format must also include portnumber if a path is to be specified&lt;br /&gt;
 $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 HASH-Operationen (Beispiel):&lt;br /&gt;
 $href ={APR =&amp;gt; 4,AUG =&amp;gt; 8}; #anonymous hash&lt;br /&gt;
 $el = $href-&amp;gt;{APR}; $el = %{$href}{APR}; #access element of hash&lt;br /&gt;
 $href2 = {%{$href1}}; #copy hash&lt;br /&gt;
 if (ref($r) eq &amp;quot;HASH&amp;quot;) {} #checks if $r points to hash&lt;br /&gt;
&lt;br /&gt;
Dem Internal &amp;quot;DeviceName&amp;quot; wird das Ergebnis von AttrVal() zugewiesen. Bei mir ergab das:&lt;br /&gt;
 DeviceName wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&lt;br /&gt;
 FHEM-Referenz:&lt;br /&gt;
 AttrVal(&amp;lt;devicename&amp;gt;,&amp;lt;attribute&amp;gt;,&amp;lt;defaultvalue&amp;gt;)&lt;br /&gt;
 Gibt das entsprechende Attribut des Gerätes zurück&lt;br /&gt;
 { Value(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { OldValue(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { time_str2num(OldTimestamp(&amp;quot;wz&amp;quot;)) }&lt;br /&gt;
 { ReadingsVal(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, &amp;quot;20&amp;quot;)+0 }&lt;br /&gt;
 { ReadingsTimestamp(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, 0)}&lt;br /&gt;
 { AttrVal(&amp;quot;wz&amp;quot;, &amp;quot;room&amp;quot;, &amp;quot;none&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Das Attribut websocketURL ist bei mir wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions. Somit erklaert sich auch der Eintrag bei DeviceName. &amp;quot;wss:echo.websocket.org:443&amp;quot; ist nur der Defaultwert.&lt;br /&gt;
&lt;br /&gt;
Man haette zum Testen auch die folgenden beiden verwenden/eintragen koennen.&lt;br /&gt;
 There are free test servers that we can use to experiment with a WebSocket client, such as:&lt;br /&gt;
&lt;br /&gt;
    Hoppscotch – wss://echo-websocket.hoppscotch.io (GUI)&lt;br /&gt;
    λ if else – wss://ws.ifelse.io (GUI)&lt;br /&gt;
&lt;br /&gt;
 Both provide a GUI for testing via a browser. The former sends a timestamp to the client every second but doesn’t respond to client messages. The latter is a standard echo service that sends a copy of each received message back to the client. Both make sense in testing.&lt;br /&gt;
&lt;br /&gt;
Da curl nicht nativ websocket-Kommunikation unterstuetzt kann man auf folgende Tool ausweichen. Auch chrome bietet ein Websocket-Client-Plugin.&lt;br /&gt;
*wssh3&lt;br /&gt;
*websocat&lt;br /&gt;
*wscat&lt;br /&gt;
&lt;br /&gt;
 # special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
&lt;br /&gt;
 # callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
 # websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion wird direkt, ohne Namen, in das/den (?) Hash unter directReadFn abgelegt. &lt;br /&gt;
Die Callback-Funktion:&lt;br /&gt;
Hier wird mit Hilfe der Funktion DevIo_SimpleRead() die Rueckmeldung des Server eingelesen. Siehe [https://wiki.fhem.de/wiki/DevIo]&lt;br /&gt;
Hinweis zum Logging in FHEM: [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#Logging]&lt;br /&gt;
Die eigentliche Aktivitaet geschieht in den sieben Zeilen am Ende der Callback-Fkt. &lt;br /&gt;
 ReadingsAge(&amp;lt;devicename&amp;gt;,&amp;lt;reading&amp;gt;,&amp;lt;defaultvalue&amp;gt;) #gibt das Alter des Readings in Sekunden zurück. &lt;br /&gt;
 AttrVal hatten wir schon.&lt;br /&gt;
 isNext bekommt seinen Wert aus dem durch einen REGEX geliefert Wert des Buffers. Beispiel ($match) = ($dirty =~ /^(.*)$/s); &lt;br /&gt;
&lt;br /&gt;
 Die Funktion readingsBeginUpdate() bereitet die Definition mit dem Hash $hash auf ein Update von Readings vor. Dies betrifft insbesondere das Setzen von Umgebungsvariablen sowie dem aktuellen Zeitstempel als Änderungszeitpunkt. Der Aufruf dieser Funktion ist notwendig um eigentliche Updates mit der Funktion readingsBulkUpdate() auf der gewünschten Definition durchführen zu können. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#readingsBeginUpdate]&lt;br /&gt;
&lt;br /&gt;
Die Funktion readingsEndUpdate() beendet den Bulk-Update Prozess durch die Funktionen readingsBeginUpdate() &amp;amp; readingsBulkUpdate() und triggert optional die entsprechenden Events sämtlicher erzeugter Readings für die Definition $hash. Desweiteren werden nachgelagerte Tasks wie bspw. die Erzeugung von User-Readings (Attribut: userReadings), sowie die Erzeugung des STATE aufgrund des Attributs stateFormat durchgeführt. Sofern $do_trigger gesetzt ist, werden alle anstehenden Events nach Abschluss getriggert.&lt;br /&gt;
&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
Das ist die eigentliche Kontaktaufnahme. &lt;br /&gt;
&lt;br /&gt;
Dies dient vermutlich dazu, bis zum ersten Empfang von Daten, das Reading websocketDate zu leeren.&lt;br /&gt;
&lt;br /&gt;
 return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss ercheint dann im Reading connect die aktuelle Zeit.&lt;br /&gt;
&lt;br /&gt;
readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=====disconnect=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
 disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erlaeuterung:&lt;br /&gt;
&lt;br /&gt;
*Die Funktion RemoveInternalTimer löscht möglicherweise noch anstehende Timer welche mit dem Übergabeparameter $arg gescheduled sind. Optional kann man zusätzlich die Suche auf eine bestimmte Funktion $functionName weiter einschränken. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#RemoveInternalTimer]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von $hash ein und gibt diese zurück. [https://wiki.fhem.de/wiki/DevIo#DevIo_SimpleRead()]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition $hash. [https://wiki.fhem.de/wiki/DevIo#DevIo_CloseDev()]&lt;br /&gt;
&lt;br /&gt;
*POSIX:: [https://metacpan.org/pod/POSIX#strftime]&lt;br /&gt;
&lt;br /&gt;
=====onDisconnect=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=====onConnectionAck=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====onNextLiveMeasurement=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===JavaScript===&lt;br /&gt;
Zum Eingewoehnen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;ws://echo.websocket.org&#039;);&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung hergestellt.&#039;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
    socket.send(&#039;Hallo Server!&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&#039;Nachricht vom Server erhalten:&#039;, event.data);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und jetzt das Gerippe fuer Tibber mit Haut versehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
const WebSocket = require(&#039;ws&#039;);&lt;br /&gt;
&lt;br /&gt;
const query = `subscription {&lt;br /&gt;
    liveMeasurement(homeId: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;) {&lt;br /&gt;
      timestamp&lt;br /&gt;
      power&lt;br /&gt;
      accumulatedConsumption&lt;br /&gt;
      minPower&lt;br /&gt;
      maxPower&lt;br /&gt;
      averagePower&lt;br /&gt;
      voltagePhase1&lt;br /&gt;
      currentL1&lt;br /&gt;
      accumulatedCost&lt;br /&gt;
      currency&lt;br /&gt;
   } &lt;br /&gt;
}`;&lt;br /&gt;
&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&#039;,[&#039;graphql-transport-ws&#039;], { &lt;br /&gt;
        headers: {&lt;br /&gt;
                &#039;Content-Type&#039;: &#039;application/json&#039;,&lt;br /&gt;
                &#039;User-Agent&#039;: &#039;NodeJS tibberview&#039;,&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&amp;quot;WebSocket-Verbindung hergestellt.&amp;quot;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
   const connectionQuery = {&lt;br /&gt;
      type: &amp;quot;connection_init&amp;quot;,&lt;br /&gt;
      payload: {&lt;br /&gt;
          token: &amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;,&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    socket.send(JSON.stringify(connectionQuery));&lt;br /&gt;
    console.log(&amp;quot;Verbindung zum &#039;Tibber feed&#039; hergestellt.&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&amp;quot;Nachricht vom Server erhalten:&amp;quot;, event.data);&lt;br /&gt;
    const msg = JSON.parse(event.data);&lt;br /&gt;
&lt;br /&gt;
    if (msg.type === &amp;quot;connection_ack&amp;quot;) {&lt;br /&gt;
      console.log(&amp;quot;Tibber ACK empfangen: &amp;quot;, msg);&lt;br /&gt;
&lt;br /&gt;
      const realtimeDataQuery = {&lt;br /&gt;
        id: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&lt;br /&gt;
        type: &amp;quot;subscribe&amp;quot;,&lt;br /&gt;
        payload: { query }&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
      socket.send(JSON.stringify(realtimeDataQuery));&lt;br /&gt;
    }&lt;br /&gt;
    else if (msg.type === &amp;quot;next&amp;quot;) {&lt;br /&gt;
      if (!msg.payload.data) {&lt;br /&gt;
        console.log(&amp;quot;⚠  Nachricht hat keinen Inhalt.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      const data = msg.payload.data.liveMeasurement;&lt;br /&gt;
      socket.emit(&amp;quot;data&amp;quot;, data);&lt;br /&gt;
    } else {&lt;br /&gt;
      console.log(&amp;quot;Nachrichteninhalt: &amp;quot;, msg)&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm erzeugte folgende Ausgabe:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
worker:/mnt/1.5T_RAID/Programme/JavaScript/WebSocket # node tibberclient_demo.js &lt;br /&gt;
WebSocket-Verbindung hergestellt.&lt;br /&gt;
Verbindung zum &#039;Tibber feed&#039; hergestellt.&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;type&amp;quot;:&amp;quot;connection_ack&amp;quot;}&lt;br /&gt;
Tibber ACK empfangen:  { type: &#039;connection_ack&#039; }&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:48.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.6,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:51.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:52.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:230,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:53.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.2,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:54.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.1,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:55.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:230.2,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:56.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:57.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.9,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:58.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:59.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.4,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:00.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.7,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:01.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.6,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
^C&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An der Stelle mache ich jetzt einen Ausflug in PHP.&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
Nachdem der Websocket-Kontakt mit JavaScript funktioniert hat, kommt jetzt ein Serverprogramm was folgendes tun soll.&lt;br /&gt;
&lt;br /&gt;
*Abfrage der Basisdaten&lt;br /&gt;
*Objekt Types Builder&lt;br /&gt;
*Mutation&lt;br /&gt;
*Websocket Subsrciption&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===Python===&lt;br /&gt;
&lt;br /&gt;
Quelle: https://github.com/Danielhiversen/pyTibber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Client-Beispiel mit eingesetzem DEMO_TOKEN und user_agent&lt;br /&gt;
Obwohl DEMO_TOKEN in tibber.py definiert ist, wird es nicht uebernommen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import tibber&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def start():&lt;br /&gt;
  tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
  await tibber_connection.update_info()&lt;br /&gt;
  print(tibber_connection.name)&lt;br /&gt;
&lt;br /&gt;
  home = tibber_connection.get_homes()[0]&lt;br /&gt;
  await home.fetch_consumption_data()&lt;br /&gt;
  await home.update_info()&lt;br /&gt;
  print(home.address1)&lt;br /&gt;
&lt;br /&gt;
  await home.update_price_info()&lt;br /&gt;
  print(home.current_price_info)&lt;br /&gt;
  &lt;br /&gt;
  # await tibber_connection.close_connection()&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(start())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
import aiohttp&lt;br /&gt;
import tibber&lt;br /&gt;
&lt;br /&gt;
def _callback(pkg):&lt;br /&gt;
    print(pkg)&lt;br /&gt;
    data = pkg.get(&amp;quot;data&amp;quot;)&lt;br /&gt;
    if data is None:&lt;br /&gt;
        return&lt;br /&gt;
    print(data.get(&amp;quot;liveMeasurement&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def run():&lt;br /&gt;
    async with aiohttp.ClientSession() as session:&lt;br /&gt;
        tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, websession=session, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
        await tibber_connection.update_info()&lt;br /&gt;
    home = tibber_connection.get_homes()[0]&lt;br /&gt;
    await home.rt_subscribe(_callback)    &lt;br /&gt;
&lt;br /&gt;
    while True:&lt;br /&gt;
      await asyncio.sleep(10)&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(run())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4842</id>
		<title>Tibber Pulse (API)</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Tibber_Pulse_(API)&amp;diff=4842"/>
		<updated>2026-04-10T06:07:58Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Da ich aktuell vom Energieversorger einen zweiten Hausanschluss (40kW :-() gelegt bekommen habe und ich für die Wallboxen zur Foerderung einen 100% Oekostrom haben musste, habe ich kurzerhand einen Vertrag mit Tibber (mal zum Testen) abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
=Installation=&lt;br /&gt;
Pulse IR und Bridge&lt;br /&gt;
[[Datei:Tibber Pulse IR und Bridge.png|mini|Tibber Pulse IR und Bridge]]&lt;br /&gt;
Eigentlich verlief die Installation problemlos. Fuer mich war allerdings der Assistent in der Tibber-App zu kindisch. Das ist eher was für Apple-User. Ich haette mir an der ein oder anderen Stelle mehr Hardcore-Infos gewuenscht. Da der mehrstufige Prozess (WLAN-Bridge-Pulse-Zaehler) doch einige Solperfallen enthaelt, ist es fuer mich eher frustrierend jedesmal bei einem Fehler zurueck auf Los geschickt zu werden. Das geht definitiv besser. Ist aber wieder ein Beispiel, dass die Informatiker-Jobs heute nur noch durch 5-jaehrige Schimpansen (ungelernte ;-)) besetzt werden.&lt;br /&gt;
&lt;br /&gt;
Bei mir gab es letztendlich nur einen gravierenden Fehler. Von den mitgelieferten Akkus des Pulse war einer defekt. Bei mir sind das zwei AA-Zellen auf LiBasis! Ein Nachladen mit einem Lithium-1.5V-Lader hat keinen Erfolg gebracht. Erst der Austausch gegen 2 neue Zellen war erfolgreich.&lt;br /&gt;
&lt;br /&gt;
=App=&lt;br /&gt;
[[Datei:TibberAppIcon.png|mini|TibberAppIcon]]&lt;br /&gt;
Die App find ich ziemlich verquer. Sobald Rechnungsanschrift und Installationsorte unterschiedlich sind, geht das Chaos los. Auch der Support tut sich dann schwer. Ich traue mich gar nicht so richtig weitere Vertraege abzuschlieszen.&lt;br /&gt;
Alles ueber die App machen zu wollen/muessen ist ... Das gehoert fuer mich ins WebUserPortal.&lt;br /&gt;
&lt;br /&gt;
Was echt nervt ist, dass sich gefühlt jeden Monat das LookandFeel aendert. Schon die dritte oder vierte Designaenderung des Homescreens (Zuhause).&lt;br /&gt;
&lt;br /&gt;
=API=&lt;br /&gt;
Da die App eher suboptimal ist, habe ich recht zuegig damit angefangen mir die notwendigen Infos ueber die API auf eine Infoseite im Intranet zu holen. Ein Integration in FHEM steht noch aus.&lt;br /&gt;
&lt;br /&gt;
=Erste Schritte=&lt;br /&gt;
&lt;br /&gt;
Uebersicht [https://developer.tibber.com/docs/overview]&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung: Die Plattform wird über GraphQL verfügbar gemacht, eine Technologie, die von Facebook konzipiert und entwickelt wurde.&lt;br /&gt;
&lt;br /&gt;
==Was ist GraphQL?==&lt;br /&gt;
&lt;br /&gt;
[https://de.wikipedia.org/wiki/GraphQL Wikipedia]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/graphql GraphQL]&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zu GraphQL===&lt;br /&gt;
&lt;br /&gt;
GraphQL ist eine von Facebook entwickelte Abfragesprache und Laufzeitumgebung für APIs (Application Programming Interfaces). Im Gegensatz zu traditionellen RESTful APIs, bei denen die Client-Anwendung verschiedene Endpunkte aufruft, um Daten abzurufen oder zu manipulieren, ermöglicht GraphQL dem Client, genau die Daten abzurufen, die er benötigt, indem er eine einzige Abfrage an den Server sendet.&lt;br /&gt;
&lt;br /&gt;
Hier sind einige Hauptmerkmale von GraphQL:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;Declarative Datenabfrage&#039;&#039;&#039;: Der Client spezifiziert genau, welche Daten er vom Server erhalten möchte, indem er eine GraphQL-Abfrage definiert, die die benötigten Felder und deren Struktur beschreibt. Dadurch kann der Client genau die Daten erhalten, die er benötigt, ohne Overfetching oder Underfetching.&lt;br /&gt;
    &#039;&#039;&#039;Typsystem&#039;&#039;&#039;: GraphQL definiert ein Typsystem, das die Struktur und den Typ der Daten beschreibt, die von der API zurückgegeben werden können. Dies ermöglicht eine strikte Typisierung und verbesserte Dokumentation.&lt;br /&gt;
    &#039;&#039;&#039;Single-Endpoint-Architektur&#039;&#039;&#039;: Im Gegensatz zu RESTful APIs, die oft mehrere Endpunkte für verschiedene Ressourcentypen haben, hat eine GraphQL-API normalerweise nur einen Endpunkt, über den alle Abfragen und Mutationen gesendet werden.&lt;br /&gt;
    &#039;&#039;&#039;Echtzeitfähigkeit&#039;&#039;&#039;: GraphQL unterstützt auch Abonnements, die es ermöglichen, Daten in Echtzeit zu empfangen, wenn sich etwas ändert. Dies ist besonders nützlich für Anwendungsfälle wie Live-Chats, Benachrichtigungen und Echtzeit-Dashboard-Aktualisierungen.&lt;br /&gt;
    &#039;&#039;&#039;Entwicklungserfahrung&#039;&#039;&#039;: GraphQL bietet eine verbesserte Entwicklererfahrung, da Clients genau die Daten erhalten können, die sie benötigen, und Entwickler APIs schneller entwickeln können, ohne sich um Versionierung oder unerwartete Seiteneffekte durch Änderungen an der Backend-API kümmern zu müssen.&lt;br /&gt;
&lt;br /&gt;
Insgesamt bietet GraphQL eine flexible, effiziente und moderne Möglichkeit, APIs zu entwerfen und zu verwenden, die den Anforderungen moderner Anwendungen besser gerecht wird als traditionelle RESTful APIs.&lt;br /&gt;
&lt;br /&gt;
Auf der Tibber-Seite heiszt es:&lt;br /&gt;
&lt;br /&gt;
 The GraphQL query language is basically about selecting fields on objects.&lt;br /&gt;
&lt;br /&gt;
Was dies bedeutet kommt weiter unten. Weiter heiszt es auf der Tibber Seite.&lt;br /&gt;
&lt;br /&gt;
    GraphQL ist eine Abfragesprache für Ihre(? unsere) API und eine serverseitige Laufzeitumgebung zum Ausführen von Abfragen mithilfe eines Typsystems, das Sie für Ihre Daten definieren. GraphQL ist nicht an eine bestimmte Datenbank oder Speicher-Engine gebunden und wird stattdessen durch Ihren vorhandenen Code und Ihre Daten unterstützt.&lt;br /&gt;
&lt;br /&gt;
Mit der Uebersetzung von Google hadere ich noch. Der Satz kommt vermutlich ursrpruenglich von Facebook und wurde von Tibber 1:1 uebernommen.&lt;br /&gt;
&lt;br /&gt;
Der naechste Satz gibt schon mehr Aufschluss:&lt;br /&gt;
 Tibber hat sich für GraphQL als API entschieden, weil es unseren Integratoren und uns selbst Flexibilitaet bietet. Die Moeglichkeit, die gewuenschten Daten genau zu definieren, macht die Nutzung wesentlich einfacher als herkoemmliche [https://www.ibm.com/de-de/topics/rest-apis REST-basierte APIs]. Es ist auch sehr schoen, dass die serverseitige Implementierung von GraphQL sehr gut mit unserer Microservice-Architektur zusammenspielt :-)&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg nutze ich den [https://developer.tibber.com/explorer Api Explorer im TibberDev]. Nachdem man sich dort einen Personal Token angelegt hat, kann&#039;s direkt losgehen. Der [https://developer.tibber.com/explorer GraphiQL] hat auch eine Autovervollstaendigung. Sehr praktisch.&lt;br /&gt;
[[Datei:TibberDevAccount.png|mini|TibberDevAccount]]&lt;br /&gt;
Die Root-Types sind &lt;br /&gt;
*query: Query&lt;br /&gt;
*mutation: RootMutation&lt;br /&gt;
*subscription: RootSubscription&lt;br /&gt;
*fragment feht irgendwie in der Doc &lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist recht simpel und das Doc in knapp einer Stunde durchprobiert. So dass ich dann schnell an dem Punkt angelangt bin, dass ich ein Programm (PHP, Python oder JavaScript) brauche welches die fuer mich interessanten Daten in meine MySQL-DB uebertraegt. Ob ich zuerst anzeige und dann speichere, also sozusagen ueber die Datenbank puffere, oder umgekehrt muss ich noch festlegen.&lt;br /&gt;
&lt;br /&gt;
===ChatGPT zum Subprotokoll===&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; ist ein WebSocket-Subprotokoll, das speziell für die Kommunikation zwischen Client und Server in GraphQL-Anwendungen entwickelt wurde. Es basiert auf dem WebSocket-Protokoll, das eine bidirektionale, persistente Verbindung zwischen einem Client und einem Server ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Im Kontext von GraphQL wird &amp;quot;graphql-transport-ws&amp;quot; verwendet, um GraphQL-Abfragen, Mutationen und Abonnements über WebSocket-Verbindungen zu übertragen. Im Gegensatz zum traditionellen HTTP-Protokoll, das eine separate Anfrage-Antwort-Struktur hat, ermöglicht WebSocket eine kontinuierliche Kommunikation zwischen Client und Server, was besonders nützlich ist, wenn Echtzeit-Aktualisierungen benötigt werden, wie bei Abonnements in GraphQL.&lt;br /&gt;
&lt;br /&gt;
Das Subprotokoll &amp;quot;graphql-transport-ws&amp;quot; definiert spezifische Nachrichtenformate und Handshake-Verfahren, die für die GraphQL-Kommunikation über WebSockets erforderlich sind. Durch die Verwendung dieses Subprotokolls können Entwickler GraphQL-Anwendungen erstellen, die von den Echtzeitfähigkeiten von WebSocket profitieren, ohne auf die Vorteile und Flexibilität von GraphQL verzichten zu müssen.&lt;br /&gt;
&lt;br /&gt;
==Mehrfacher Hinweis Subprotokolwechsel Dezember 2022==&lt;br /&gt;
&lt;br /&gt;
 Die TibberAPI unterstuetzte zunaechst GraphQL-Websocket-Subscriptions mithilfe des Unterprotokolls graphql-ws. Diese Bibliothek wird/wurde jedoch archiviert und nicht mehr gepflegt. Daher wurde am 31. Maerz 2022 die Unterstützung für das Unterprotokoll graphql-transport-ws hinzugefuegt.&lt;br /&gt;
&lt;br /&gt;
 Die Unterstuetzung für das alte Protokoll wird im Dezember 2022 entfernt. Gleichzeitig aendert sich auch die URL für die Verbindung zum Websocket-Server. Zuvor konnte die Verbindung unter &#039;&#039;&#039;wss://api.tibber.com/v1-beta/gql/subscriptions&#039;&#039;&#039; hergestellt werden. Nach der Aenderung muss die URL ueber GraphQL abgefragt werden (siehe Beispiel unten) und der Client muss das Unterprotokoll graphql-transport-ws unterstuetzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight code=xml&amp;gt;&lt;br /&gt;
Frage:&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Antwort:&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;websocketSubscriptionUrl&amp;quot;: &amp;quot;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Anfragenbegrenzung==&lt;br /&gt;
 Zum Schutz der API gilt eine Ratenbegrenzung von 100 Anfragen in 5 Minuten pro IP-Adresse. Beachten Sie, dass die Preise einmal täglich am Nachmittag berechnet werden (für Norwegen und Schweden sind sie zunächst vorläufig und werden später möglicherweise mit geringfügigen Änderungen nach Bestätigung der Wechselkurse endgültig festgelegt). Sie können „priceInfo.today“ und „priceInfo.tomorrow“ verwenden, um sie im Voraus abzurufen, anstatt nur „priceInfo.current“ für die aktuelle Stunde zu verwenden.&lt;br /&gt;
&lt;br /&gt;
=In medias res=&lt;br /&gt;
Zum Starten empfiehlt die Tibber-API-Doc, die fundamentalen Konzepte von GraphQL, die Kommunikation mit der API und den Explorer.&lt;br /&gt;
&lt;br /&gt;
Um die nachfolgenden Beispiele schnell zu verstehen, hier der Link zur [https://developer.tibber.com/docs/reference API-Referenz]&lt;br /&gt;
&lt;br /&gt;
 Bei der GraphQL-Abfragesprache geht es im Wesentlichen um die Auswahl von Feldern in Objekten.&lt;br /&gt;
&lt;br /&gt;
Auf der Docs-Reference-Seite gibt es einen interessanten Hinweis:&lt;br /&gt;
  This reference is auto-generated from https://api.tibber.com&lt;br /&gt;
&lt;br /&gt;
Es koennte als eine URL geben, die einem die aktuelle &#039;&#039;&#039;Tibber GraphQL Schema Reference&#039;&#039;&#039; direkt zur Auswertung ausgibt. Der Umweg ueber das Parsen der HTML-Ausgabe ginge zur Not natuerlich auch.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objekt Typen==&lt;br /&gt;
&lt;br /&gt;
Die Begriffsdifferenzierung Objekt und Object Types ist mir derzeit noch nicht klar. Ich werde wohl um die allgemeine [https://github.com/graphql/graphql-spec Spec von GraphQL] nicht herumkommen:-(&lt;br /&gt;
&lt;br /&gt;
Bei &#039;Home&#039; wird von einem Objekttyp gesprochen. Klar ist man hat Objekte, die mit dem Schluesselwort type eingeleitet und dann beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 Einige Felder sind Skalare (Boolesche Werte, Zeichenfolgen, Ganzzahlen, Gleitkommazahlen), waehrend andere Objekttypen sind (Adresse, LegalEntity). Eine GraphQL-Abfrage muss vollstaendig auf Skalarebene sein. D.h. in einer Abfrage duerfen die abgefragten Felder keine Objekte (keine weiteren Objekttypen) sein, sondern hierarchisch heruntergebrochen auf weitere Inhalte bis es Skalare sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
This query is valid:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
This is not:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner   &amp;lt;- hier ist der Fehler. Dies ist kein Skalar!&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
The owner field is an object type and you would need to specify which scalar fields you would like returned:&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    homes{&lt;br /&gt;
        id&lt;br /&gt;
        owner{&lt;br /&gt;
            name&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==query Datenabfrage (read)==&lt;br /&gt;
&lt;br /&gt;
query-&amp;gt;viewer&lt;br /&gt;
&lt;br /&gt;
Zuerst mit homes die Zaehlerplatz-iD holen und dan mit home (id: &amp;quot;&amp;lt;ID&amp;gt;&amp;quot;) nur die data dazu holen.&lt;br /&gt;
&lt;br /&gt;
Hello World;-)&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    login&lt;br /&gt;
    name&lt;br /&gt;
    userId&lt;br /&gt;
    accountType&lt;br /&gt;
    home (id: &amp;quot;1cdd8b6d-956c-447f-9b95-9a57801eaf4c&amp;quot;) {&lt;br /&gt;
      id&lt;br /&gt;
      features {&lt;br /&gt;
        realTimeConsumptionEnabled&lt;br /&gt;
      }&lt;br /&gt;
      subscriptions {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      currentSubscription {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      meteringPointData {&lt;br /&gt;
        consumptionEan&lt;br /&gt;
        gridCompany&lt;br /&gt;
        gridAreaCode&lt;br /&gt;
        priceAreaCode&lt;br /&gt;
        productionEan&lt;br /&gt;
        energyTaxType&lt;br /&gt;
        vatType&lt;br /&gt;
        estimatedAnnualConsumption&lt;br /&gt;
      }&lt;br /&gt;
      owner {&lt;br /&gt;
        id&lt;br /&gt;
      }&lt;br /&gt;
      mainFuseSize&lt;br /&gt;
    }&lt;br /&gt;
    websocketSubscriptionUrl&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Interessant fand ich z.B.&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;priceAreaCode&amp;quot;: &amp;quot;Amprion&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;estimatedAnnualConsumption&amp;quot;: 500&lt;br /&gt;
&lt;br /&gt;
          &amp;quot;mainFuseSize&amp;quot;: null&lt;br /&gt;
&lt;br /&gt;
und was da noch alles an Daten einzutragen ist.&lt;br /&gt;
 size: Int&lt;br /&gt;
 The size of the home in square meters&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
==mutation Datenveraenderung (write)==&lt;br /&gt;
&lt;br /&gt;
Mutationen gibt es nur 3, wobei die erste bei mir entfaellt.&lt;br /&gt;
&lt;br /&gt;
 sendMeterReading(input: MeterReadingInput!): MeterReadingResponse!&lt;br /&gt;
 Send meter reading for home (only available for Norwegian users) &lt;br /&gt;
 &lt;br /&gt;
 updateHome(input: UpdateHomeInput!): Home!&lt;br /&gt;
 Update home information&lt;br /&gt;
 &lt;br /&gt;
 sendPushNotification(input: PushNotificationInput!): PushNotificationResponse!&lt;br /&gt;
 Send notification to Tibber app on registered devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==subscription==&lt;br /&gt;
&lt;br /&gt;
Subscription hat nur 2 Felder, wobei dauerhaft nur das erste interessant ist.&lt;br /&gt;
&lt;br /&gt;
 liveMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to real-time measurement stream from Pulse or Watty device&lt;br /&gt;
&lt;br /&gt;
 testMeasurement(homeId: ID!): LiveMeasurement&lt;br /&gt;
 Subscribe to test stream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=xml&amp;gt;&lt;br /&gt;
subscription{&lt;br /&gt;
  liveMeasurement(homeId: &amp;quot;&amp;lt;homeID&amp;gt;&amp;quot;){&lt;br /&gt;
    timestamp&lt;br /&gt;
    power&lt;br /&gt;
    lastMeterConsumption&lt;br /&gt;
    accumulatedConsumption&lt;br /&gt;
    accumulatedProduction&lt;br /&gt;
    accumulatedConsumptionLastHour&lt;br /&gt;
    accumulatedProductionLastHour&lt;br /&gt;
    accumulatedCost&lt;br /&gt;
    accumulatedReward&lt;br /&gt;
    currency&lt;br /&gt;
    minPower&lt;br /&gt;
    averagePower&lt;br /&gt;
    maxPower&lt;br /&gt;
    powerProduction&lt;br /&gt;
    powerReactive&lt;br /&gt;
    powerProductionReactive&lt;br /&gt;
    minPowerProduction&lt;br /&gt;
    maxPowerProduction&lt;br /&gt;
    lastMeterProduction&lt;br /&gt;
    powerFactor&lt;br /&gt;
    voltagePhase1&lt;br /&gt;
    voltagePhase2&lt;br /&gt;
    voltagePhase3&lt;br /&gt;
    currentL1&lt;br /&gt;
    currentL2&lt;br /&gt;
    currentL3&lt;br /&gt;
    signalStrength&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==fragment==&lt;br /&gt;
&lt;br /&gt;
?&lt;br /&gt;
&lt;br /&gt;
Dazu habe ich bisher keine weiteren Infos gefunden.&lt;br /&gt;
&lt;br /&gt;
=Programmierung=&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung steht bei mir hier die Client-Programmierung im Vordergrund.&lt;br /&gt;
&lt;br /&gt;
==Query/Mutation==&lt;br /&gt;
&lt;br /&gt;
Die Abfragen und Aenderungen zu programmieren sind schnell erledigt.&lt;br /&gt;
&lt;br /&gt;
Der Einstieg auf der Konsole gelingt mit curl recht gut. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
 curl \&lt;br /&gt;
-H &amp;quot;Authorization: Bearer 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE \&lt;br /&gt;
-H &amp;quot;Content-Type: application/json&amp;quot; \&lt;br /&gt;
-X POST \&lt;br /&gt;
-d  &#039;mutation{ sendMeterReading(input:{homeId:&amp;quot;some home id&amp;quot;, reading: 123}){time reading  }}&#039; https://api.tibber.com/v1-beta/gql&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Subscription==&lt;br /&gt;
&lt;br /&gt;
Die Subscriptions sind schon ein wenig anspruchsvoller zu programmieren als Query und Mutation (QuM).&lt;br /&gt;
Ist QuM eine simple http-Anfrage mit einer Antwort im JSON-Format, ist es bei den Subscriptions ein Websocket, der einmal geoeffnet in unregelmaeszigen Abstaenden JSON-Pakete liefert.&lt;br /&gt;
&lt;br /&gt;
===Anforderungen für Websocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
* Clients muessen den User-Agent-Header festlegen, wenn sie die GraphQL-API aufrufen und eine Websocket-Verbindung oeffnen. Sowohl die Plattform als auch die Treiberversion muessen angegeben werden. Z.B. Homey/10.0.0 com.tibber/1.8.3&lt;br /&gt;
* Clients müssen das Unterprotokoll graphql-transport-ws implementieren&lt;br /&gt;
* Clients müssen Jitter und exponentielles Backoff implementieren, wenn sie Anfragen an die API wiederholen&lt;br /&gt;
* Clients müssen die WebSocket-Server-URL dynamisch abfragen&lt;br /&gt;
&lt;br /&gt;
===Best Practices für die Implementierung von WebSocket-Abonnement-Clients===&lt;br /&gt;
&lt;br /&gt;
Bei der Implementierung eines Clients zum Abonnieren von Live-Daten sind einige Szenarien zu beruecksichtigen:&lt;br /&gt;
* Keine Daten aufgrund von Netzwerkproblemen: Es kann sinnvoll sein, einen Wiederverbindungsmechanismus zu implementieren, falls mehrere Minuten lang keine Daten empfangen wurden. Bei einem erneuten Versuch muss zunaechst die alte Websocket-Verbindung zerstoert und ordnungsgemaeszer Jitter und exponentielle Verzoegerung implementiert werden.&lt;br /&gt;
* Ein Echtzeitgeraet wird moeglicherweise entfernt. Es wird empfohlen, vor dem erneuten Verbinden immer zu ueberpruefen, ob home.features.realTimeConsumptionEnabled einen echten Wert hat.&lt;br /&gt;
* Eine WebSocket-Verbindung kann aufgrund eines ungueltigen Authentifizierungstokens verboten sein (z. B. weil ein Benutzer möglicherweise sein Tibber-Konto entfernt hat). In diesem Fall darf der Client es nicht mit demselben Token erneut versuchen. Stattdessen muss der Autorisierungsprozess erneut ausgeführt werden, wobei der Benutzer neue Anmeldeinformationen eingibt.&lt;br /&gt;
* Es kann zu einem Neustart der Tibber-Websocket-API kommen, was bedeutet, dass die Verbindung mit dem &amp;quot;Code 1001&amp;quot; und dem Grund &amp;quot;Going away&amp;quot; geschlossen wird. In diesem Fall kann der Client nach einer zufälligen Verzoegerung von 1 bis 60 Sekunden (Jitter angewendet) die Verbindung wiederherstellen, um zu vermeiden, dass alle Clients gleichzeitig die Verbindung wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
In PHP habe ich wenig bis gar nichts gefunden. Ich wollte mir auch einfach die Nutzung einer GraphQL-Library ersparen. Python habe ich erst einmal an den Schluss verschoben und einem Integration in FHEM den Vorzug gegeben.&lt;br /&gt;
&lt;br /&gt;
===Perl (FHEM)===&lt;br /&gt;
Hier der Code der tatsaechlich funktioniert, obwohl er an ein paar Stellen nicht so vorgeht wie ich aus den Tibber-Docs erwartet hatte.&lt;br /&gt;
&lt;br /&gt;
HINWEIS: Dies ist der &amp;quot;nackte&amp;quot; Perl-Code. In der &amp;quot;Originaldatei&amp;quot; sind noch die &amp;quot;FHEM-Escapes&amp;quot; doppelter Strichpunkt und Zeilenfolgezeichen \ enthalten. Eine Eingabe ueber die FHEM-Kommandozeile hat bei mir nicht funktioniert. Erst das direkte Einkopieren in die fhem.conf zeigte das nachfolgende, funktionierende Ergebnis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line&amp;gt;&lt;br /&gt;
connect:cmd:.connect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	# establish connection to websocket&lt;br /&gt;
	# format must also include portnumber if a path is to be specified&lt;br /&gt;
	$hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
	$hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
	&lt;br /&gt;
	# callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
	# websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
	&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onDisconnect {&lt;br /&gt;
	my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect&lt;br /&gt;
	my $timerFunction = sub() {&lt;br /&gt;
		my ($hash) = @_;&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
		&lt;br /&gt;
		# only re-connect if device is not connected&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));&lt;br /&gt;
	};&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash, $timerFunction);&lt;br /&gt;
	&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onConnectionAck:websocketData:.*connection_ack.* {&lt;br /&gt;
	#websocketData contains the string &amp;quot;connection_ack&amp;quot;&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# do not proceed if connection is lost&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	my $devState = DevIo_IsOpen($hash);&lt;br /&gt;
	return &amp;quot;Device not open&amp;quot; if (!defined($devState));&lt;br /&gt;
	&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	my $homeId = AttrVal($name, &amp;quot;homeId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	my $myId = AttrVal($name, &amp;quot;myId&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	# build the query, do it in pieces, the comma at the end caused perl errors&lt;br /&gt;
	# so we put it together in this not very elegant way&lt;br /&gt;
	my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;&lt;br /&gt;
	$json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;&lt;br /&gt;
	$json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;&lt;br /&gt;
	$json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;&lt;br /&gt;
	$json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;&lt;br /&gt;
	$json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;&lt;br /&gt;
	$json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;&lt;br /&gt;
	$json .= &#039;}}&#039;;&lt;br /&gt;
	&lt;br /&gt;
	#send the string via websocket as ASCII&lt;br /&gt;
	Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
	DevIo_SimpleWrite($hash, $json, 2);&lt;br /&gt;
	&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
},&lt;br /&gt;
onNextLiveMeasurement:websocketData:.*next.*payload.*data.*liveMeasurement.* {&lt;br /&gt;
	#websocketData contains next-live-measurement-data&lt;br /&gt;
	my $val = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;{}&amp;quot;);&lt;br /&gt;
	my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};&lt;br /&gt;
	&lt;br /&gt;
	my $ret = &amp;quot;got values for:\n&amp;quot;;&lt;br /&gt;
	foreach my $k (sort keys %res) {&lt;br /&gt;
		$ret .= &amp;quot;$k\n&amp;quot;;&lt;br /&gt;
		readingsBulkUpdate($hash, makeReadingName($k), $res{$k});&lt;br /&gt;
	}&lt;br /&gt;
	return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Code Analyse====&lt;br /&gt;
&lt;br /&gt;
Es gibt 5 Eintrittspunkte:&lt;br /&gt;
* connect&lt;br /&gt;
* disconnect&lt;br /&gt;
* onDisconnect&lt;br /&gt;
* onConnectionAck&lt;br /&gt;
* onNextLiveMeasurement&lt;br /&gt;
&lt;br /&gt;
Die ersten beiden legen die Befehlsfolgen für ein Start-Kommando (set cmd connect, bzw. set cmd disconnect) oder Stop-Kommando fest.&lt;br /&gt;
Die letzten 3 reagieren auf die entsprechenden Events.&lt;br /&gt;
=====connect=====&lt;br /&gt;
&lt;br /&gt;
Lexikalische Variablen mittels my, bzw. my()&lt;br /&gt;
&lt;br /&gt;
 Jede Variable, die mit our deklariert oder auch &amp;quot;einfach so&amp;quot; ohne eine Deklaration verwendet wird, wird in die Symboltabelle des jeweils aktuellen Packages aufgenommen.&lt;br /&gt;
 Deklariert man dagegen eine Variable mit dem Operator my, so wird die entsprechende Variable in einer anderen Tabelle abgelegt, auf die kein expliziter Zugriff möglich ist. &lt;br /&gt;
 Neben der Tatsache, daß my-Variablen in einer eigenen Tabelle verwaltet werden, ist von besonderer Bedeutung, daß sie nur einen recht beschränkten Gültigkeitsbereich besitzen. Eine durch my erzeugte Variable steht nur in dem aktuellen Block (definiert durch geschweifte Klammern &amp;quot;{...}&amp;quot;), der aktuellen Datei oder innerhalb eines Arguments von eval() zur Verfügung. Außerhalb davon existieren diese Variablen nicht, es ist also (im Gegensatz zu our-Variablen) auch mit Hilfe des Package-Namens dort kein Zugriff möglich&lt;br /&gt;
 Es kann in einem Package durchaus zwei Variablen gleichen Namens geben: eine in der Symboltabelle und eine, die durch einen Aufruf von my entstanden ist. In einem solchen Falle wird bei einfacher Verwendung des Bezeichners auf die my-Variable zugegriffen. &lt;br /&gt;
&lt;br /&gt;
In $defs stehen alle Defines drin. In $hash wird also der Name des des aufrufenden Devices lokal hinterlegt und danach mit DevIO_IsOpen() der Status abgefragt. &lt;br /&gt;
$defs ist ein Hash. Ein Hash ist ein anderer Namen fuer ein assoziatives Array. Also eine ungeordnete Liste von Skalaren (Zahl oder String) die ueber einen Stringwert angesprochen (Lookup) werden koennen.  &lt;br /&gt;
&lt;br /&gt;
FHEM-Spezial:&lt;br /&gt;
 In $defs findet man alle Devices, z.b. $defs{&amp;quot;Rolladen_Tuere&amp;quot;} beinhaltet das Device.&lt;br /&gt;
 my @alle = keys %defs;&lt;br /&gt;
 my @teilmenge = defInfo(&#039;TYPE=notify&#039;,&#039;NAME&#039;); liefert ein array mit allen notify devices.&lt;br /&gt;
 z.B. my @teilmenge = defInfo(&#039;NAME=Rollladen.*&#039;,&#039;NAME&#039;);&lt;br /&gt;
&lt;br /&gt;
 return &amp;quot;Device already open&amp;quot; if (defined($devState));&lt;br /&gt;
Sollte also das Device bereits im State &amp;quot;offen&amp;quot; sein, wird die connect-Routine abgebrochen. Dann erscheint in den Readings nicht Datum/Uhrzeit des Connects sondern die Meldung &amp;quot;Device already open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
 # establish connection to websocket&lt;br /&gt;
 # format must also include portnumber if a path is to be specified&lt;br /&gt;
 $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 HASH-Operationen (Beispiel):&lt;br /&gt;
 $href ={APR =&amp;gt; 4,AUG =&amp;gt; 8}; #anonymous hash&lt;br /&gt;
 $el = $href-&amp;gt;{APR}; $el = %{$href}{APR}; #access element of hash&lt;br /&gt;
 $href2 = {%{$href1}}; #copy hash&lt;br /&gt;
 if (ref($r) eq &amp;quot;HASH&amp;quot;) {} #checks if $r points to hash&lt;br /&gt;
&lt;br /&gt;
Dem Internal &amp;quot;DeviceName&amp;quot; wird das Ergebnis von AttrVal() zugewiesen. Bei mir ergab das:&lt;br /&gt;
 DeviceName wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&lt;br /&gt;
 FHEM-Referenz:&lt;br /&gt;
 AttrVal(&amp;lt;devicename&amp;gt;,&amp;lt;attribute&amp;gt;,&amp;lt;defaultvalue&amp;gt;)&lt;br /&gt;
 Gibt das entsprechende Attribut des Gerätes zurück&lt;br /&gt;
 { Value(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { OldValue(&amp;quot;wz&amp;quot;) }&lt;br /&gt;
 { time_str2num(OldTimestamp(&amp;quot;wz&amp;quot;)) }&lt;br /&gt;
 { ReadingsVal(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, &amp;quot;20&amp;quot;)+0 }&lt;br /&gt;
 { ReadingsTimestamp(&amp;quot;wz&amp;quot;, &amp;quot;measured-temp&amp;quot;, 0)}&lt;br /&gt;
 { AttrVal(&amp;quot;wz&amp;quot;, &amp;quot;room&amp;quot;, &amp;quot;none&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Das Attribut websocketURL ist bei mir wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions. Somit erklaert sich auch der Eintrag bei DeviceName. &amp;quot;wss:echo.websocket.org:443&amp;quot; ist nur der Defaultwert.&lt;br /&gt;
&lt;br /&gt;
Man haette zum Testen auch die folgenden beiden verwenden/eintragen koennen.&lt;br /&gt;
 There are free test servers that we can use to experiment with a WebSocket client, such as:&lt;br /&gt;
&lt;br /&gt;
    Hoppscotch – wss://echo-websocket.hoppscotch.io (GUI)&lt;br /&gt;
    λ if else – wss://ws.ifelse.io (GUI)&lt;br /&gt;
&lt;br /&gt;
 Both provide a GUI for testing via a browser. The former sends a timestamp to the client every second but doesn’t respond to client messages. The latter is a standard echo service that sends a copy of each received message back to the client. Both make sense in testing.&lt;br /&gt;
&lt;br /&gt;
Da curl nicht nativ websocket-Kommunikation unterstuetzt kann man auf folgende Tool ausweichen. Auch chrome bietet ein Websocket-Client-Plugin.&lt;br /&gt;
*wssh3&lt;br /&gt;
*websocat&lt;br /&gt;
*wscat&lt;br /&gt;
&lt;br /&gt;
 # special headers needed for Tibber, see also Developer Tools in Browser&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;&lt;br /&gt;
 $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;&lt;br /&gt;
&lt;br /&gt;
 # callback function when &amp;quot;select()&amp;quot; signals data for us&lt;br /&gt;
 # websocket Ping/Pongs are treated in DevIo but still call this function&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{directReadFn} = sub () {&lt;br /&gt;
		my $hash = $defs{$name};&lt;br /&gt;
		&lt;br /&gt;
		# we can read without closing the DevIo, because select() signalled data&lt;br /&gt;
		my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
		&lt;br /&gt;
		# if read fails, close device&lt;br /&gt;
		if(!defined($buf)) {&lt;br /&gt;
			DevIo_CloseDev($hash);&lt;br /&gt;
			$buf = &amp;quot;not_connected&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		#Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		# only update our reading if buffer is not empty and if last update is older than minInterval&lt;br /&gt;
		if ($buf ne &amp;quot;&amp;quot;) {&lt;br /&gt;
			my $websocketDataAge = ReadingsAge($name, &amp;quot;websocketData&amp;quot;, 3600);&lt;br /&gt;
			my $minInterval = AttrVal($name, &amp;quot;minInterval&amp;quot;, 0);&lt;br /&gt;
			my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);&lt;br /&gt;
			&lt;br /&gt;
			readingsBeginUpdate($hash);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);&lt;br /&gt;
			readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);&lt;br /&gt;
			readingsEndUpdate($hash, 1);&lt;br /&gt;
		}&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion wird direkt, ohne Namen, in das/den (?) Hash unter directReadFn abgelegt. &lt;br /&gt;
Die Callback-Funktion:&lt;br /&gt;
Hier wird mit Hilfe der Funktion DevIo_SimpleRead() die Rueckmeldung des Server eingelesen. Siehe [https://wiki.fhem.de/wiki/DevIo]&lt;br /&gt;
Hinweis zum Logging in FHEM: [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#Logging]&lt;br /&gt;
Die eigentliche Aktivitaet geschieht in den sieben Zeilen am Ende der Callback-Fkt. &lt;br /&gt;
 ReadingsAge(&amp;lt;devicename&amp;gt;,&amp;lt;reading&amp;gt;,&amp;lt;defaultvalue&amp;gt;) #gibt das Alter des Readings in Sekunden zurück. &lt;br /&gt;
 AttrVal hatten wir schon.&lt;br /&gt;
 isNext bekommt seinen Wert aus dem durch einen REGEX geliefert Wert des Buffers. Beispiel ($match) = ($dirty =~ /^(.*)$/s); &lt;br /&gt;
&lt;br /&gt;
 Die Funktion readingsBeginUpdate() bereitet die Definition mit dem Hash $hash auf ein Update von Readings vor. Dies betrifft insbesondere das Setzen von Umgebungsvariablen sowie dem aktuellen Zeitstempel als Änderungszeitpunkt. Der Aufruf dieser Funktion ist notwendig um eigentliche Updates mit der Funktion readingsBulkUpdate() auf der gewünschten Definition durchführen zu können. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#readingsBeginUpdate]&lt;br /&gt;
&lt;br /&gt;
Die Funktion readingsEndUpdate() beendet den Bulk-Update Prozess durch die Funktionen readingsBeginUpdate() &amp;amp; readingsBulkUpdate() und triggert optional die entsprechenden Events sämtlicher erzeugter Readings für die Definition $hash. Desweiteren werden nachgelagerte Tasks wie bspw. die Erzeugung von User-Readings (Attribut: userReadings), sowie die Erzeugung des STATE aufgrund des Attributs stateFormat durchgeführt. Sofern $do_trigger gesetzt ist, werden alle anstehenden Events nach Abschluss getriggert.&lt;br /&gt;
&lt;br /&gt;
	# open DevIo websocket&lt;br /&gt;
	DevIo_OpenDev($hash, 0, undef, sub(){&lt;br /&gt;
		my ($hash, $error) = @_;&lt;br /&gt;
		return &amp;quot;$error&amp;quot; if ($error);&lt;br /&gt;
		&lt;br /&gt;
		my $token = AttrVal($name, &amp;quot;token&amp;quot;, &amp;quot;???&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
Das ist die eigentliche Kontaktaufnahme. &lt;br /&gt;
&lt;br /&gt;
Dies dient vermutlich dazu, bis zum ersten Empfang von Daten, das Reading websocketDate zu leeren.&lt;br /&gt;
&lt;br /&gt;
 return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss ercheint dann im Reading connect die aktuelle Zeit.&lt;br /&gt;
&lt;br /&gt;
readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=====disconnect=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
 disconnect:cmd:.disconnect {&lt;br /&gt;
	my $hash = $defs{$name};&lt;br /&gt;
	RemoveInternalTimer($hash);&lt;br /&gt;
	DevIo_SimpleRead($hash);&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erlaeuterung:&lt;br /&gt;
&lt;br /&gt;
*Die Funktion RemoveInternalTimer löscht möglicherweise noch anstehende Timer welche mit dem Übergabeparameter $arg gescheduled sind. Optional kann man zusätzlich die Suche auf eine bestimmte Funktion $functionName weiter einschränken. [https://wiki.fhem.de/wiki/DevelopmentModuleAPI#RemoveInternalTimer]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von $hash ein und gibt diese zurück. [https://wiki.fhem.de/wiki/DevIo#DevIo_SimpleRead()]&lt;br /&gt;
&lt;br /&gt;
*Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition $hash. [https://wiki.fhem.de/wiki/DevIo#DevIo_CloseDev()]&lt;br /&gt;
&lt;br /&gt;
*POSIX:: [https://metacpan.org/pod/POSIX#strftime]&lt;br /&gt;
&lt;br /&gt;
=====onDisconnect=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=====onConnectionAck=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====onNextLiveMeasurement=====&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===JavaScript===&lt;br /&gt;
Zum Eingewoehnen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;ws://echo.websocket.org&#039;);&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung hergestellt.&#039;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
    socket.send(&#039;Hallo Server!&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&#039;Nachricht vom Server erhalten:&#039;, event.data);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und jetzt das Gerippe fuer Tibber mit Haut versehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=javascript&amp;gt;&lt;br /&gt;
const WebSocket = require(&#039;ws&#039;);&lt;br /&gt;
&lt;br /&gt;
const query = `subscription {&lt;br /&gt;
    liveMeasurement(homeId: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;) {&lt;br /&gt;
      timestamp&lt;br /&gt;
      power&lt;br /&gt;
      accumulatedConsumption&lt;br /&gt;
      minPower&lt;br /&gt;
      maxPower&lt;br /&gt;
      averagePower&lt;br /&gt;
      voltagePhase1&lt;br /&gt;
      currentL1&lt;br /&gt;
      accumulatedCost&lt;br /&gt;
      currency&lt;br /&gt;
   } &lt;br /&gt;
}`;&lt;br /&gt;
&lt;br /&gt;
// WebSocket-Verbindung herstellen&lt;br /&gt;
const socket = new WebSocket(&#039;wss://websocket-api.tibber.com/v1-beta/gql/subscriptions&#039;,[&#039;graphql-transport-ws&#039;], { &lt;br /&gt;
        headers: {&lt;br /&gt;
                &#039;Content-Type&#039;: &#039;application/json&#039;,&lt;br /&gt;
                &#039;User-Agent&#039;: &#039;NodeJS tibberview&#039;,&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// Event-Handler für Verbindungsereignisse&lt;br /&gt;
socket.onopen = function(event) {&lt;br /&gt;
    console.log(&amp;quot;WebSocket-Verbindung hergestellt.&amp;quot;);&lt;br /&gt;
    // Nachricht senden, sobald die Verbindung hergestellt ist&lt;br /&gt;
   const connectionQuery = {&lt;br /&gt;
      type: &amp;quot;connection_init&amp;quot;,&lt;br /&gt;
      payload: {&lt;br /&gt;
          token: &amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;,&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    socket.send(JSON.stringify(connectionQuery));&lt;br /&gt;
    console.log(&amp;quot;Verbindung zum &#039;Tibber feed&#039; hergestellt.&amp;quot;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onmessage = function(event) {&lt;br /&gt;
    console.log(&amp;quot;Nachricht vom Server erhalten:&amp;quot;, event.data);&lt;br /&gt;
    const msg = JSON.parse(event.data);&lt;br /&gt;
&lt;br /&gt;
    if (msg.type === &amp;quot;connection_ack&amp;quot;) {&lt;br /&gt;
      console.log(&amp;quot;Tibber ACK empfangen: &amp;quot;, msg);&lt;br /&gt;
&lt;br /&gt;
      const realtimeDataQuery = {&lt;br /&gt;
        id: &amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&lt;br /&gt;
        type: &amp;quot;subscribe&amp;quot;,&lt;br /&gt;
        payload: { query }&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
      socket.send(JSON.stringify(realtimeDataQuery));&lt;br /&gt;
    }&lt;br /&gt;
    else if (msg.type === &amp;quot;next&amp;quot;) {&lt;br /&gt;
      if (!msg.payload.data) {&lt;br /&gt;
        console.log(&amp;quot;⚠  Nachricht hat keinen Inhalt.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      const data = msg.payload.data.liveMeasurement;&lt;br /&gt;
      socket.emit(&amp;quot;data&amp;quot;, data);&lt;br /&gt;
    } else {&lt;br /&gt;
      console.log(&amp;quot;Nachrichteninhalt: &amp;quot;, msg)&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onclose = function(event) {&lt;br /&gt;
    console.log(&#039;WebSocket-Verbindung geschlossen.&#039;);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
socket.onerror = function(error) {&lt;br /&gt;
    console.error(&#039;WebSocket-Fehler aufgetreten:&#039;, error);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm erzeugte folgende Ausgabe:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
worker:/mnt/1.5T_RAID/Programme/JavaScript/WebSocket # node tibberclient_demo.js &lt;br /&gt;
WebSocket-Verbindung hergestellt.&lt;br /&gt;
Verbindung zum &#039;Tibber feed&#039; hergestellt.&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;type&amp;quot;:&amp;quot;connection_ack&amp;quot;}&lt;br /&gt;
Tibber ACK empfangen:  { type: &#039;connection_ack&#039; }&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:48.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.6,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:51.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:52.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.3,&amp;quot;voltagePhase1&amp;quot;:230,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:53.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.2,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:54.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306.1,&amp;quot;voltagePhase1&amp;quot;:230.3,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:55.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:230.2,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:56.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2306,&amp;quot;voltagePhase1&amp;quot;:229.8,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:57.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.9,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:58.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:46:59.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.8,&amp;quot;voltagePhase1&amp;quot;:230.4,&amp;quot;currentL1&amp;quot;:2.6,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:00.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.7,&amp;quot;voltagePhase1&amp;quot;:230.1,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
Nachricht vom Server erhalten: {&amp;quot;id&amp;quot;:&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;next&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;data&amp;quot;:{&amp;quot;liveMeasurement&amp;quot;:{&amp;quot;timestamp&amp;quot;:&amp;quot;2024-03-17T08:47:01.000+01:00&amp;quot;,&amp;quot;power&amp;quot;:0,&amp;quot;accumulatedConsumption&amp;quot;:20.258,&amp;quot;minPower&amp;quot;:0,&amp;quot;maxPower&amp;quot;:5150,&amp;quot;averagePower&amp;quot;:2305.6,&amp;quot;voltagePhase1&amp;quot;:229.9,&amp;quot;currentL1&amp;quot;:2.7,&amp;quot;accumulatedCost&amp;quot;:11.672781,&amp;quot;currency&amp;quot;:&amp;quot;SEK&amp;quot;}}}}&lt;br /&gt;
^C&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An der Stelle mache ich jetzt einen Ausflug in PHP.&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
Nachdem der Websocket-Kontakt mit JavaScript funktioniert hat, kommt jetzt ein Serverprogramm was folgendes tun soll.&lt;br /&gt;
&lt;br /&gt;
*Abfrage der Basisdaten&lt;br /&gt;
*Objekt Types Builder&lt;br /&gt;
*Mutation&lt;br /&gt;
*Websocket Subsrciption&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
===Python===&lt;br /&gt;
&lt;br /&gt;
Quelle: https://github.com/Danielhiversen/pyTibber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Client-Beispiel mit eingesetzem DEMO_TOKEN und user_agent&lt;br /&gt;
Obwohl DEMO_TOKEN in tibber.py definiert ist, wird es nicht uebernommen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import tibber&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def start():&lt;br /&gt;
  tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
  await tibber_connection.update_info()&lt;br /&gt;
  print(tibber_connection.name)&lt;br /&gt;
&lt;br /&gt;
  home = tibber_connection.get_homes()[0]&lt;br /&gt;
  await home.fetch_consumption_data()&lt;br /&gt;
  await home.update_info()&lt;br /&gt;
  print(home.address1)&lt;br /&gt;
&lt;br /&gt;
  await home.update_price_info()&lt;br /&gt;
  print(home.current_price_info)&lt;br /&gt;
  &lt;br /&gt;
  # await tibber_connection.close_connection()&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(start())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=python&amp;gt;&lt;br /&gt;
import tibber.const&lt;br /&gt;
import asyncio&lt;br /&gt;
&lt;br /&gt;
import aiohttp&lt;br /&gt;
import tibber&lt;br /&gt;
&lt;br /&gt;
def _callback(pkg):&lt;br /&gt;
    print(pkg)&lt;br /&gt;
    data = pkg.get(&amp;quot;data&amp;quot;)&lt;br /&gt;
    if data is None:&lt;br /&gt;
        return&lt;br /&gt;
    print(data.get(&amp;quot;liveMeasurement&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
async def run():&lt;br /&gt;
    async with aiohttp.ClientSession() as session:&lt;br /&gt;
        tibber_connection = tibber.Tibber(tibber.const.DEMO_TOKEN, websession=session, user_agent=&amp;quot;worker&amp;quot;)&lt;br /&gt;
        await tibber_connection.update_info()&lt;br /&gt;
    home = tibber_connection.get_homes()[0]&lt;br /&gt;
    await home.rt_subscribe(_callback)    &lt;br /&gt;
&lt;br /&gt;
    while True:&lt;br /&gt;
      await asyncio.sleep(10)&lt;br /&gt;
&lt;br /&gt;
loop = asyncio.run(run())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
</feed>