<?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-06-17T22:09:14Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.44.0</generator>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=WLAN_Aktor_Markise&amp;diff=4918</id>
		<title>WLAN Aktor Markise</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=WLAN_Aktor_Markise&amp;diff=4918"/>
		<updated>2026-06-13T15:55:01Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Fehlerquellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Leider hatte ich keinen Port mehr an den KNX-Rollladenaktoren zur Verfuegung, daher bin ich auf eine MQTT-WLAN-ESP-DuoRelais-Schaltung ausgewischen.&lt;br /&gt;
&lt;br /&gt;
=Hardware=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Software=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
//ShutterBlindsAwningFunction with LC-Relay-ESP01-2R-5V&lt;br /&gt;
//tested 2022-08-06&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;ESP8266WiFi.h&amp;gt;        // Include the Wi-Fi library&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;       // MQTT&lt;br /&gt;
#include &amp;quot;ESPDateTime.h&amp;quot;        // NTP&lt;br /&gt;
#include &amp;lt;ESP_EEPROM.h&amp;gt;         // to store non-volatile variables&lt;br /&gt;
#include &amp;quot;CRC.h&amp;quot;                // to check the reliability of the EEPROM-stored variables&lt;br /&gt;
#include &amp;quot;CRC8.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//CONFIGURATION&lt;br /&gt;
#define WLAN_SSID &amp;quot;XXX&amp;quot;&lt;br /&gt;
#define WLAN_PASSWORD &amp;quot;YYY&amp;quot;&lt;br /&gt;
#define MQTT_SERVER_IP &amp;quot;ZZZ&amp;quot;  // better to use the ip, DNS will slow down the speed&lt;br /&gt;
#define MQTT_MSG_BUFFER_SIZE	50&lt;br /&gt;
#define MQTT_DEBUG_MSG_BUFFER_SIZE 50&lt;br /&gt;
#define MQTTT_BASE_TOPIC &amp;quot;home/basement/0_terrace/awning/&amp;quot;  // my rooms are numbered so there is a consideration to replace the _ with / to use the 0 also for e.g. garden&lt;br /&gt;
#define MQTT_OUT_STATE MQTTT_BASE_TOPIC &amp;quot;state&amp;quot;&lt;br /&gt;
#define MQTT_OUT_RAW MQTTT_BASE_TOPIC &amp;quot;raw&amp;quot;&lt;br /&gt;
#define MQTT_OUT_ALIVE MQTTT_BASE_TOPIC &amp;quot;alive&amp;quot;&lt;br /&gt;
#define MQTT_OUT_RSSI MQTTT_BASE_TOPIC &amp;quot;RSSI&amp;quot;&lt;br /&gt;
#define MQTT_IN_TRIGGER MQTTT_BASE_TOPIC &amp;quot;trigger&amp;quot;&lt;br /&gt;
#define MQTT_IN_DIRECTION MQTTT_BASE_TOPIC &amp;quot;direction&amp;quot;&lt;br /&gt;
#define MQTT_IN_CONFIG MQTTT_BASE_TOPIC &amp;quot;config&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//#define SERIAL_DEBUG          // used in an early stage of development; conflicts with UART output for relais control&lt;br /&gt;
#define MQTT_DEBUG_OUTPUT     // the raw topic serves to output the values for debugging // TODO struct config&lt;br /&gt;
#define ALIVEINTERVAL 10000     // UpdateInterval in milliseconds of the topics &#039;alive&#039; // TODO struct config&lt;br /&gt;
&lt;br /&gt;
//WIFI&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
const char * ssid     = WLAN_SSID;         // The SSID (name) of the Wi-Fi network you want to connect to&lt;br /&gt;
const char * password = WLAN_PASSWORD;     // The password of the Wi-Fi network&lt;br /&gt;
String mac;&lt;br /&gt;
&lt;br /&gt;
//MQTT&lt;br /&gt;
const char * MQTTServer = MQTT_SERVER_IP;&lt;br /&gt;
PubSubClient MQTTClient(espClient); //Constructor&lt;br /&gt;
unsigned long lastMsg = 0;  // to remember the last timestamp of the &#039;ALVIE-publish&#039; (heartbeat)&lt;br /&gt;
char msg[MQTT_MSG_BUFFER_SIZE]; // old style; sorry for mixing c and c++&lt;br /&gt;
char msgConfig[MQTT_DEBUG_MSG_BUFFER_SIZE]; // to store the config parameters; in setup there is no possibility to send out, so store it to send it in loop&lt;br /&gt;
long int value = 0; // don&#039;t needed anymore; its a counter replaced by DateTime&lt;br /&gt;
&lt;br /&gt;
String topicBase = MQTTT_BASE_TOPIC ;&lt;br /&gt;
String inConfigTopic = MQTT_IN_CONFIG ;&lt;br /&gt;
String inTriggerTopic = MQTT_IN_TRIGGER ;&lt;br /&gt;
String inDirectionTopic = MQTT_IN_DIRECTION;&lt;br /&gt;
String outStateTopic = MQTT_OUT_STATE ;&lt;br /&gt;
String outRawTopic = MQTT_OUT_RAW ;&lt;br /&gt;
String outAliveTopic = MQTT_OUT_ALIVE ;&lt;br /&gt;
String outRSSITopic = MQTT_OUT_RSSI ;&lt;br /&gt;
String clientId;&lt;br /&gt;
&lt;br /&gt;
//RELAIS&lt;br /&gt;
void sendRelaisCommand(unsigned int relais,boolean onoff); // forward declaration&lt;br /&gt;
volatile byte state; // 0 STOP (0,X); 1 EXTEND (1,1); 2 STOP (0,X); 3 RETRACT (1,0)&lt;br /&gt;
volatile boolean relaisState[] = {false,false};&lt;br /&gt;
struct EEPROMValues{ // use a struct to store and retrieve to/from EEPROM&lt;br /&gt;
  //const char* mark=&amp;quot;#&amp;quot;; // Before C++11, members of a struct could not be default initialized. Instead they must be initialized after an instance struct is created.&lt;br /&gt;
  //EEPROM.put/get probably have issues with strings&lt;br /&gt;
  byte mark;  //or char mark;&lt;br /&gt;
  unsigned long timeActiveDir1; // Milliseconds active till relaisstate[] falls back to {false,false}&lt;br /&gt;
  unsigned long timeActiveDir2; // Milliseconds active till relaisstate[] false back to {false,false}&lt;br /&gt;
  unsigned long checksum;&lt;br /&gt;
} settings;&lt;br /&gt;
&lt;br /&gt;
//TIMER&lt;br /&gt;
unsigned long startTimeDir1 = 0;  // Starttime if relais1 goes active&lt;br /&gt;
unsigned long startTimeDir2 = 0;  // Starttime if relais1 goes active&lt;br /&gt;
&lt;br /&gt;
//CRC&lt;br /&gt;
CRC8 crc;&lt;br /&gt;
unsigned int eepromAddr = 0;  // address to store the config variables, mark and checksum (struct settings)&lt;br /&gt;
&lt;br /&gt;
//CONFIG&lt;br /&gt;
boolean sendOutOneTime = false; // flag to output the stored config only once after powerup/reset&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//CALLBACK The function is called when a new message arrives. All code to react within this function and/or transfer the parameter to outer variables.&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/trigger&#039; -m &#039;1&#039; // or -m 1 or -m 0&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/config&#039; -m &#039;7000 8000&#039;&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/direction&#039; -m &#039;up&#039;&lt;br /&gt;
void callback(char* topic, byte* payload, unsigned int length) { // topic ends with direction or config // payload from -m within apostrophes // lentgh integer number of bytes of payload&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.print(&amp;quot;Message arrived [&amp;quot;);&lt;br /&gt;
    Serial.print(topic);&lt;br /&gt;
    Serial.print(&amp;quot;] &amp;quot;);&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; length; i++) {&lt;br /&gt;
      Serial.print((char)payload[i]);  // print out the payload array byte by byte&lt;br /&gt;
    }&lt;br /&gt;
    Serial.println();&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  String payloadStr; // to transfer the byte-array to a String&lt;br /&gt;
  char* p = (char*)malloc(length); // copy the payload to the new buffer; actually not necessary&lt;br /&gt;
  memcpy(p,payload,length); // play safe, it does no harm&lt;br /&gt;
  payloadStr.concat(p,length); // String is better to do things like .toLowerCase();&lt;br /&gt;
&lt;br /&gt;
  // Trigger CONTROL&lt;br /&gt;
  if (strcmp(inTriggerTopic.c_str(),topic) == 0 &amp;amp;&amp;amp; (char)payload[0] == &#039;1&#039;) { //1 for trigger // 0 or other are not handled&lt;br /&gt;
    boolean stateSet = false; // if the state is detected and changed ignore all following state detections&lt;br /&gt;
    // Relais 1 determines the direction&lt;br /&gt;
    // Relais 2 switches on/off respectively move/stop&lt;br /&gt;
    // activating means first switch on relay 1, pause, activate relay 2&lt;br /&gt;
    // deactivating means first switch off relay 2, pause and only then relay 1 if necessary switch&lt;br /&gt;
    // state 0 both relays off (relay 2 then 1) start condition&lt;br /&gt;
    // state 1 relay 1 set relay 1 to move out/down postition, pause, activate relay 2&lt;br /&gt;
    // state 2 both relays off (relay 2 then 1) intermediate condition&lt;br /&gt;
    // state 3 relay 1 set relay 1 to move in/up postition, pause, activate relay 2&lt;br /&gt;
    // recommendation for pausetime is 500 ms&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; !relaisState[1] &amp;amp;&amp;amp; state != 0 &amp;amp;&amp;amp; state !=2 ){ //asynchron!!! correct it&lt;br /&gt;
      state = 0;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; !relaisState[1] &amp;amp;&amp;amp; !stateSet){&lt;br /&gt;
      if(state==0){ // State 0-&amp;gt;1&lt;br /&gt;
        sendRelaisCommand(1,true); // extend&lt;br /&gt;
        delay(100);&lt;br /&gt;
        sendRelaisCommand(2,true); // activate&lt;br /&gt;
        state=1;&lt;br /&gt;
        startTimeDir1 = millis();&lt;br /&gt;
      }&lt;br /&gt;
      if(state==2){ // state 2-&amp;gt;3&lt;br /&gt;
        sendRelaisCommand(1,false); // retract&lt;br /&gt;
        delay(100);&lt;br /&gt;
        sendRelaisCommand(2,true);  // activate&lt;br /&gt;
        state=3;&lt;br /&gt;
        startTimeDir2 = millis();&lt;br /&gt;
      }&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (relaisState[0] &amp;amp;&amp;amp; relaisState[1] &amp;amp;&amp;amp; !stateSet){ // state 1-&amp;gt;2&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false); //at this point the relais could be untouched, in state 1 and 3 it will be set again, to save energy switch both relays to off&lt;br /&gt;
      state=2;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; relaisState[1] &amp;amp;&amp;amp; !stateSet){ // state 3-&amp;gt;0&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false); //at this point the relais could be untouched, in state 1 and 3 it will be set again, to save energy switch both relays to off&lt;br /&gt;
      state=0;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,state);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
     Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
     Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    MQTTClient.publish(outStateTopic.c_str(), msg);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Direction CONTROL&lt;br /&gt;
  if (strcmp(inDirectionTopic.c_str(),topic) == 0) {&lt;br /&gt;
    uint8_t direction=0; // 1 means down(out); 2 means up(in)&lt;br /&gt;
    payloadStr.toLowerCase();&lt;br /&gt;
    //MQTTClient.publish(outRawTopic.c_str(), payloadStr.c_str());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    const char directions1[3][6]={&amp;quot;down&amp;quot;,&amp;quot;out&amp;quot;,&amp;quot;on&amp;quot;}; // to react to all of this commands&lt;br /&gt;
    const char directions2[3][6]={&amp;quot;up&amp;quot;,&amp;quot;in&amp;quot;,&amp;quot;off&amp;quot;};&lt;br /&gt;
    const char directions3[3][6]={&amp;quot;halt&amp;quot;,&amp;quot;stop&amp;quot;,&amp;quot;stopp&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
    uint8_t number; // -Wunused-but-set-variable&lt;br /&gt;
    number=sizeof(directions1)/sizeof(directions1[0]); // calculate the number of array members  // Test!!! can be deleted&lt;br /&gt;
    (void)number; // to suppress the warning&lt;br /&gt;
    #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,number);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
&lt;br /&gt;
    // only one of this tests can be true // after this block direction is 1, 2 or 3 depending on command&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions1)/sizeof(directions1[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions1[i-1])==0)direction=1;&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions2)/sizeof(directions2[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions2[i-1])==0)direction=2;&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions3)/sizeof(directions3[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions3[i-1])==0)direction=3;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    if (direction == 1){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR1&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      sendRelaisCommand(1,true); // move out/down&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(2,true); // activate&lt;br /&gt;
      startTimeDir1 = millis();&lt;br /&gt;
    }&lt;br /&gt;
    if (direction == 2){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR2&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(2,true);&lt;br /&gt;
      startTimeDir2 = millis();&lt;br /&gt;
    }&lt;br /&gt;
    if (direction == 3){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR3&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // config CONTROL&lt;br /&gt;
  if (strcmp(inConfigTopic.c_str(),topic) == 0){&lt;br /&gt;
    unsigned long maxTime[] = {180,180};&lt;br /&gt;
    const char* pL = payloadStr.c_str();  // get pointer to payload string // the payload is the combination of two integer space separated&lt;br /&gt;
    char* end;                            // declare a pointer&lt;br /&gt;
    for (uint8_t i = 1; i &amp;lt;= 2; i++){&lt;br /&gt;
      maxTime[i-1] = strtol(pL,&amp;amp;end,10); // &amp;amp;end is the reference to an object of type char*, whose value is set by the function to the next character in str after the numerical value.&lt;br /&gt;
      if (pL == end) break;&lt;br /&gt;
      pL = end; // the space at the beginning of second number is seemingly not a problem&lt;br /&gt;
    }&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{%ld:%ld}&amp;quot;,maxTime[0],maxTime[1]);&lt;br /&gt;
    MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    if (maxTime[0]&amp;gt;=0 &amp;amp;&amp;amp; maxTime[0]&amp;lt;=1440000 &amp;amp;&amp;amp; maxTime[1]&amp;gt;=0 &amp;amp;&amp;amp; maxTime[1]&amp;lt;=1440000){&lt;br /&gt;
      settings.timeActiveDir1=maxTime[0];&lt;br /&gt;
      settings.timeActiveDir2=maxTime[1];&lt;br /&gt;
      crc.reset();&lt;br /&gt;
      crc.add(settings.timeActiveDir1);&lt;br /&gt;
      crc.add(settings.timeActiveDir2);&lt;br /&gt;
      settings.checksum=crc.getCRC();&lt;br /&gt;
      #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
        snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{EEPROMput:%c:%ld:%ld:%ld}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
        MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
      #endif&lt;br /&gt;
      EEPROM.put(eepromAddr, settings); //write data to array in ram&lt;br /&gt;
      EEPROM.commit();  //write data from ram to flash memory. Do nothing if there are no changes to EEPROM data in ram&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void reconnect() {&lt;br /&gt;
  // Loop until we&#039;re reconnected&lt;br /&gt;
  while (!MQTTClient.connected()) {&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Attempting MQTT connection...&amp;quot;);&lt;br /&gt;
    #endif&lt;br /&gt;
&lt;br /&gt;
    // Attempt to connect&lt;br /&gt;
    if (MQTTClient.connect(mac.c_str()))  {&lt;br /&gt;
      #ifdef SERIAL_DEBUG&lt;br /&gt;
          Serial.println(&amp;quot;connected&amp;quot;);&lt;br /&gt;
      #endif&lt;br /&gt;
      // Once connected, publish an announcement...&lt;br /&gt;
      MQTTClient.publish(topicBase.c_str(), &amp;quot;CON&amp;quot;);&lt;br /&gt;
      // ... and resubscribe&lt;br /&gt;
      MQTTClient.subscribe(inTriggerTopic.c_str());&lt;br /&gt;
      MQTTClient.subscribe(inDirectionTopic.c_str());&lt;br /&gt;
      MQTTClient.subscribe(inConfigTopic.c_str());&lt;br /&gt;
    } else {&lt;br /&gt;
      #ifdef SERIAL_DEBUG&lt;br /&gt;
        Serial.print(&amp;quot;failed, rc=&amp;quot;);&lt;br /&gt;
        Serial.print(MQTTClient.state());&lt;br /&gt;
        Serial.println(&amp;quot; try again in 5 seconds&amp;quot;);&lt;br /&gt;
      #endif&lt;br /&gt;
      // Wait 5 seconds before retrying&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendRelaisCommand(unsigned int relais,boolean onoff){&lt;br /&gt;
  //default for relais 1&lt;br /&gt;
  byte commandOn[4]={160,1,1,162};  // A0 (160 Non-breaking space) command initiate, relay 1/2, on/off, checksum (Addition)&lt;br /&gt;
  byte commandOff[4]={160,1,0,161};&lt;br /&gt;
&lt;br /&gt;
  //change commands for relais 2&lt;br /&gt;
  if (relais==2){&lt;br /&gt;
      commandOn[1]=2;&lt;br /&gt;
      commandOff[1]=2;&lt;br /&gt;
      commandOn[3]=163;&lt;br /&gt;
      commandOff[3]=162;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (onoff){&lt;br /&gt;
      Serial.write(commandOn,4);&lt;br /&gt;
      relaisState[relais-1]=true;&lt;br /&gt;
  }else{&lt;br /&gt;
      Serial.write(commandOff,4);&lt;br /&gt;
      relaisState[relais-1]=false;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;RELAIS-%d_SET_%s&amp;quot;,relais,onoff?&amp;quot;ON&amp;quot;:&amp;quot;OFF&amp;quot;);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
     Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
     Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    MQTTClient.publish(outRawTopic.c_str(),msg);&lt;br /&gt;
  #endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupDateTime() {&lt;br /&gt;
  // setup this after wifi connected&lt;br /&gt;
  // you can use custom timeZone,server and timeout&lt;br /&gt;
  // DateTime.setTimeZone(-4);&lt;br /&gt;
  //   DateTime.setServer(&amp;quot;asia.pool.ntp.org&amp;quot;);&lt;br /&gt;
  //   DateTime.begin(15 * 1000);&lt;br /&gt;
  DateTime.setServer(&amp;quot;de.pool.ntp.org&amp;quot;);&lt;br /&gt;
  DateTime.setTimeZone(&amp;quot;CEST&amp;quot;);&lt;br /&gt;
  DateTime.begin(); // ( 	const unsigned int  	timeOutMs = DEFAULT_TIMEOUT	)&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    if (!DateTime.isTimeValid()) {&lt;br /&gt;
      Serial.println(&amp;quot;Failed to get time from server.&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.printf(&amp;quot;Date Now is %s\n&amp;quot;, DateTime.toISOString().c_str());&lt;br /&gt;
      Serial.printf(&amp;quot;Timestamp is %lld\n&amp;quot;, DateTime.now());&lt;br /&gt;
    }&lt;br /&gt;
  #endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//SETUP&lt;br /&gt;
void setup() {&lt;br /&gt;
&lt;br /&gt;
   //SERIAL&lt;br /&gt;
  Serial.begin(115200);         // Start the Serial communication to send messages to the computer&lt;br /&gt;
  delay(10);&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.println(&#039;\n&#039;);&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  //WIFI&lt;br /&gt;
  WiFi.mode(WIFI_STA);// ist scheinbar der defaultwert wird in letzter Version nicht benutzt&lt;br /&gt;
  WiFi.begin(ssid, password);             // Connect to the network&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.print(&amp;quot;Connecting to &amp;quot;);&lt;br /&gt;
    Serial.print(ssid); Serial.println(&amp;quot; ...&amp;quot;);&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      int i = 0;&lt;br /&gt;
      Serial.print(++i); Serial.print(&amp;quot;-&amp;gt;&amp;quot;);&lt;br /&gt;
    #endif&lt;br /&gt;
  }&lt;br /&gt;
  mac=WiFi.macAddress();&lt;br /&gt;
  //clientId += String(random(0xffff), HEX);&lt;br /&gt;
  clientId = &amp;quot;ESP01-&amp;quot; + mac;&lt;br /&gt;
&lt;br /&gt;
  randomSeed(micros());&lt;br /&gt;
&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.println(&#039;\n&#039;);&lt;br /&gt;
    Serial.println(&amp;quot;Connection established!&amp;quot;);&lt;br /&gt;
    Serial.print(&amp;quot;IP address:\t&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());         // Send the IP address of the ESP8266 to the computer&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  //MQTT&lt;br /&gt;
  MQTTClient.setServer(MQTTServer, 1883);&lt;br /&gt;
  MQTTClient.setCallback(callback);&lt;br /&gt;
&lt;br /&gt;
  //DATETIME&lt;br /&gt;
  setupDateTime();&lt;br /&gt;
&lt;br /&gt;
  //TIMESETTING&lt;br /&gt;
  EEPROM.begin(sizeof(settings)); // 2 + 8 + 8 + 8&lt;br /&gt;
  boolean reliable1 = false, reliable2 = false;&lt;br /&gt;
  EEPROM.get(eepromAddr, settings); //read data from array in ram and cast it into struct called settings&lt;br /&gt;
  #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
    // in setup() MQTT isn&#039;t connected so store the values and publish it in loop()&lt;br /&gt;
    snprintf (msgConfig, 50, &amp;quot;{EEPROMget:%c:%ld:%ld:%#lx}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
  #endif&lt;br /&gt;
  //if (strcmp(settings.mark,&amp;quot;#&amp;quot;) == 0){&lt;br /&gt;
  if (settings.mark == 35){&lt;br /&gt;
    crc.reset();&lt;br /&gt;
    crc.add(settings.timeActiveDir1);&lt;br /&gt;
    crc.add(settings.timeActiveDir2);&lt;br /&gt;
    if (crc.getCRC()==settings.checksum) reliable1 = true;&lt;br /&gt;
  }&lt;br /&gt;
  if (settings.timeActiveDir1 &amp;gt;= 50 &amp;amp;&amp;amp;     // values less than 50 ms  and greater than 24 hours are senseless&lt;br /&gt;
      settings.timeActiveDir1 &amp;lt;= 1440000 &amp;amp;&amp;amp;&lt;br /&gt;
      settings.timeActiveDir2 &amp;gt;= 50 &amp;amp;&amp;amp;&lt;br /&gt;
      settings.timeActiveDir2 &amp;lt;= 1440000) reliable2 = true;&lt;br /&gt;
&lt;br /&gt;
if (!(reliable1 &amp;amp;&amp;amp; reliable2)){ // if the Content of EEPROM is undefined, set it&lt;br /&gt;
  settings.mark = 35;&lt;br /&gt;
  settings.timeActiveDir1 = 180 * 1000; // default 3 minutes to switch off&lt;br /&gt;
  settings.timeActiveDir2 = 180 * 1000;&lt;br /&gt;
  crc.reset();&lt;br /&gt;
  crc.add(settings.timeActiveDir1);&lt;br /&gt;
  crc.add(settings.timeActiveDir2);&lt;br /&gt;
  settings.checksum=crc.getCRC();&lt;br /&gt;
  EEPROM.put(eepromAddr, settings); //write data structure to ram&lt;br /&gt;
  EEPROM.commit();  //write data from ram to flash memory. Do nothing if there are no changes to EEPROM data in ram&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
  //RELAIS setup&lt;br /&gt;
  Serial.write(0); // initialize the uContoller that controls the relays&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// LOOP&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
  if (!MQTTClient.connected()) {&lt;br /&gt;
    reconnect();&lt;br /&gt;
  }&lt;br /&gt;
  MQTTClient.loop();&lt;br /&gt;
&lt;br /&gt;
  // send out one time after setup&lt;br /&gt;
  if (!sendOutOneTime) {&lt;br /&gt;
    #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msgConfig);&lt;br /&gt;
      snprintf (msgConfig, 50, &amp;quot;{config:%c:%ld:%ld:%#lx}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msgConfig);&lt;br /&gt;
      sendOutOneTime = true;&lt;br /&gt;
    #endif&lt;br /&gt;
  }&lt;br /&gt;
  unsigned long now = millis();&lt;br /&gt;
&lt;br /&gt;
   if (now - lastMsg &amp;gt; ALIVEINTERVAL) {&lt;br /&gt;
     lastMsg = now;&lt;br /&gt;
     ++value;&lt;br /&gt;
     snprintf (msg, MQTT_MSG_BUFFER_SIZE, &amp;quot;%s&amp;quot;,DateTime.toISOString().c_str());&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
      Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
     MQTTClient.publish(outAliveTopic.c_str(), msg);&lt;br /&gt;
     snprintf (msg, MQTT_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,WiFi.RSSI());&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
      Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
     MQTTClient.publish(outRSSITopic.c_str(), msg);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   if ((now - startTimeDir1 &amp;gt; settings.timeActiveDir1) &amp;amp;&amp;amp; (startTimeDir1 != 0)){   // switch both relays to off after milliseconds defined in config (energy saving and safety aspects)&lt;br /&gt;
     #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{Fallback1:%ld:%ld:%ld}&amp;quot;,now,startTimeDir1,settings.timeActiveDir1);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    sendRelaisCommand(2,false);&lt;br /&gt;
    delay(100);&lt;br /&gt;
    sendRelaisCommand(1,false);&lt;br /&gt;
    delay(500);&lt;br /&gt;
    startTimeDir1 = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   if ((now - startTimeDir2 &amp;gt; settings.timeActiveDir2) &amp;amp;&amp;amp; (startTimeDir2 != 0)){    // switch both relays to off after milliseconds defined in config (energy saving and safety aspects)&lt;br /&gt;
     #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{Fallback2:%ld:%ld:%ld}&amp;quot;,now,startTimeDir2,settings.timeActiveDir2);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    sendRelaisCommand(2,false);&lt;br /&gt;
    delay(100);&lt;br /&gt;
    sendRelaisCommand(1,false);&lt;br /&gt;
    delay(500);&lt;br /&gt;
    startTimeDir2 = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Beschreibung==&lt;br /&gt;
&lt;br /&gt;
Die Software ist eine Firmware für einen **ESP8266 (ESP-01)**, die über MQTT eine **motorisierte Markise** oder einen ähnlichen Sonnenschutz steuert. &lt;br /&gt;
&lt;br /&gt;
## Hauptfunktion&lt;br /&gt;
&lt;br /&gt;
Der ESP8266 verbindet sich mit WLAN und einem MQTT-Broker und steuert über eine serielle Relaisplatine zwei Relais:&lt;br /&gt;
&lt;br /&gt;
* Relais 1 = Fahrtrichtung (Ausfahren / Einfahren)&lt;br /&gt;
* Relais 2 = Motor EIN/AUS&lt;br /&gt;
&lt;br /&gt;
Die Steuerung erfolgt per MQTT-Befehlen. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## MQTT-Schnittstelle&lt;br /&gt;
&lt;br /&gt;
Basis-Topic:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
home/basement/0_terrace/awning/&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Eingehende Befehle:&lt;br /&gt;
&lt;br /&gt;
| Topic     | Funktion                 |&lt;br /&gt;
| --------- | ------------------------ |&lt;br /&gt;
| trigger   | Zustandswechsel          |&lt;br /&gt;
| direction | Richtung vorgeben        |&lt;br /&gt;
| config    | Laufzeiten konfigurieren |&lt;br /&gt;
&lt;br /&gt;
Ausgehende Statusmeldungen:&lt;br /&gt;
&lt;br /&gt;
| Topic | Inhalt                    |&lt;br /&gt;
| ----- | ------------------------- |&lt;br /&gt;
| state | aktueller Zustand         |&lt;br /&gt;
| raw   | Debug-Ausgaben            |&lt;br /&gt;
| alive | Heartbeat mit Zeitstempel |&lt;br /&gt;
| RSSI  | WLAN-Empfangsstärke       |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Zustandsautomat&lt;br /&gt;
&lt;br /&gt;
Es existieren vier Zustände:&lt;br /&gt;
&lt;br /&gt;
| Zustand | Bedeutung |&lt;br /&gt;
| ------- | --------- |&lt;br /&gt;
| 0       | Stopp     |&lt;br /&gt;
| 1       | Ausfahren |&lt;br /&gt;
| 2       | Stopp     |&lt;br /&gt;
| 3       | Einfahren |&lt;br /&gt;
&lt;br /&gt;
Bei jedem MQTT-Trigger wird zum nächsten Zustand gewechselt.&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
0 → 1 → 2 → 3 → 0&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Dadurch genügt ein einziger Taster oder MQTT-Befehl zum Steuern der Markise. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Richtungssteuerung&lt;br /&gt;
&lt;br /&gt;
Per MQTT können direkte Befehle gesendet werden:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
down&lt;br /&gt;
out&lt;br /&gt;
on&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Markise ausfahren&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
up&lt;br /&gt;
in&lt;br /&gt;
off&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Markise einfahren&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
halt&lt;br /&gt;
stop&lt;br /&gt;
stopp&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Motor stoppen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Sicherheitsschaltung&lt;br /&gt;
&lt;br /&gt;
Für beide Richtungen existiert eine maximale Laufzeit:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
timeActiveDir1&lt;br /&gt;
timeActiveDir2&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Nach Ablauf dieser Zeit werden beide Relais automatisch abgeschaltet.&lt;br /&gt;
&lt;br /&gt;
Standard:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
180 Sekunden&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Damit wird verhindert, dass ein defekter MQTT-Befehl oder ein Softwarefehler den Motor dauerhaft laufen lässt. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## EEPROM-Speicherung&lt;br /&gt;
&lt;br /&gt;
Die Laufzeiten werden dauerhaft gespeichert:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
struct EEPROMValues&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Inhalt:&lt;br /&gt;
&lt;br /&gt;
* Kennzeichen (`mark`)&lt;br /&gt;
* Laufzeit Richtung 1&lt;br /&gt;
* Laufzeit Richtung 2&lt;br /&gt;
* CRC-Prüfsumme&lt;br /&gt;
&lt;br /&gt;
Beim Start prüft die Firmware die Daten auf Plausibilität und Integrität. Falls ungültig, werden Standardwerte erzeugt. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Netzwerkfunktionen&lt;br /&gt;
&lt;br /&gt;
Die Firmware:&lt;br /&gt;
&lt;br /&gt;
* verbindet sich mit WLAN&lt;br /&gt;
* verbindet sich mit MQTT&lt;br /&gt;
* synchronisiert die Uhr per NTP&lt;br /&gt;
* sendet alle 10 Sekunden einen Heartbeat&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
2025-03-01T12:34:56+01:00&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
sowie die aktuelle WLAN-Feldstärke. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Hardware&lt;br /&gt;
&lt;br /&gt;
Aus dem Code lässt sich folgende Hardware ableiten:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
ESP8266 (ESP-01)&lt;br /&gt;
        │&lt;br /&gt;
UART&lt;br /&gt;
        │&lt;br /&gt;
2-Kanal Relaiscontroller&lt;br /&gt;
        │&lt;br /&gt;
Markisenmotor&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Die Relaisplatine wird über ein proprietäres 4-Byte-Protokoll gesteuert:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
A0 Relais Zustand Checksumme&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
A0 01 01 A2&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
= Relais 1 EIN. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Zusammenfassung&lt;br /&gt;
&lt;br /&gt;
Die Software ist ein **MQTT-gesteuerter Markisencontroller auf Basis eines ESP8266**, der:&lt;br /&gt;
&lt;br /&gt;
* WLAN und MQTT nutzt,&lt;br /&gt;
* eine Markise ein- und ausfährt,&lt;br /&gt;
* Fahrzeiten im EEPROM speichert,&lt;br /&gt;
* automatisch abschaltet,&lt;br /&gt;
* Status- und Debuginformationen veröffentlicht,&lt;br /&gt;
* per Home Assistant, Node-RED oder FHEM steuerbar ist. &lt;br /&gt;
&lt;br /&gt;
Der Aufbau wirkt wie ein typisches Smart-Home-Projekt zur Nachrüstung einer vorhandenen Funk- oder Tastersteuerung einer Terrasse-Markise.&lt;br /&gt;
&lt;br /&gt;
==Fehlerquellen==&lt;br /&gt;
&lt;br /&gt;
Ja, es gibt einige relevante Fehlerquellen:&lt;br /&gt;
&lt;br /&gt;
1. **Speicherleck in `callback()`**&lt;br /&gt;
   `malloc(length)` wird nie mit `free(p)` freigegeben. Bei jedem MQTT-Befehl geht RAM verloren. Außerdem wird kein Byte für `\0` reserviert. Besser ganz ohne `malloc`:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
String payloadStr((char*)payload, length);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. **`state` ist nicht initialisiert**&lt;br /&gt;
   `volatile byte state;` bekommt keinen Startwert. Beim ersten Trigger kann der Zustand undefiniert sein. Besser:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
volatile byte state = 0;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. **Richtungsbefehl setzt `state` nicht passend**&lt;br /&gt;
   Bei `direction == 1/2/3` werden Relais geschaltet, aber `state` wird nicht auf 1, 3 oder 0/2 gesetzt. Danach kann der nächste `trigger` falsch reagieren. &lt;br /&gt;
&lt;br /&gt;
4. **Alte Timer laufen weiter**&lt;br /&gt;
   Wenn Richtung 1 gestartet wurde und danach Richtung 2 kommt, wird `startTimeDir1` nicht gelöscht. Später kann der alte Fallback unerwartet beide Relais ausschalten. Gleiches umgekehrt. &lt;br /&gt;
&lt;br /&gt;
5. **Prüfung `maxTime[0] &amp;gt;= 0` ist wirkungslos**&lt;br /&gt;
   `maxTime` ist `unsigned long`, also nie negativ. Wenn `strtol()` einen negativen Wert liefert, wird er beim Speichern in `unsigned long` zu einem großen Wert. &lt;br /&gt;
&lt;br /&gt;
6. **`sendRelaisCommand()` prüft Relaisnummer nicht**&lt;br /&gt;
   Bei versehentlich `sendRelaisCommand(0, ...)` oder `3` greift `relaisState[relais-1]` außerhalb des Arrays. Besser am Anfang:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
if (relais &amp;lt; 1 || relais &amp;gt; 2) return;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7. **Blockierende `delay()`-Aufrufe**&lt;br /&gt;
   Während `delay(500)` oder beim MQTT-Reconnect mit `delay(5000)` reagiert der Controller verzögert. Für eine Markise ist das noch akzeptabel, aber ein sofortiger Stopp kann dadurch verspätet kommen. &lt;br /&gt;
&lt;br /&gt;
8. **MQTT ohne Authentifizierung**&lt;br /&gt;
   Der Code verbindet sich nur mit `MQTTClient.connect(mac.c_str())`. Jeder im Netz, der die Topics kennt, könnte die Markise steuern. &lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Sofortkorrekturen wären: `state = 0`, `malloc` entfernen, Timer gegenseitig zurücksetzen, `state` bei Direktbefehlen aktualisieren und MQTT mit Benutzer/Passwort absichern.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=WLAN_Aktor_Markise&amp;diff=4917</id>
		<title>WLAN Aktor Markise</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=WLAN_Aktor_Markise&amp;diff=4917"/>
		<updated>2026-06-13T15:54:45Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Software-Beschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Leider hatte ich keinen Port mehr an den KNX-Rollladenaktoren zur Verfuegung, daher bin ich auf eine MQTT-WLAN-ESP-DuoRelais-Schaltung ausgewischen.&lt;br /&gt;
&lt;br /&gt;
=Hardware=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Software=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
//ShutterBlindsAwningFunction with LC-Relay-ESP01-2R-5V&lt;br /&gt;
//tested 2022-08-06&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;ESP8266WiFi.h&amp;gt;        // Include the Wi-Fi library&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;       // MQTT&lt;br /&gt;
#include &amp;quot;ESPDateTime.h&amp;quot;        // NTP&lt;br /&gt;
#include &amp;lt;ESP_EEPROM.h&amp;gt;         // to store non-volatile variables&lt;br /&gt;
#include &amp;quot;CRC.h&amp;quot;                // to check the reliability of the EEPROM-stored variables&lt;br /&gt;
#include &amp;quot;CRC8.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//CONFIGURATION&lt;br /&gt;
#define WLAN_SSID &amp;quot;XXX&amp;quot;&lt;br /&gt;
#define WLAN_PASSWORD &amp;quot;YYY&amp;quot;&lt;br /&gt;
#define MQTT_SERVER_IP &amp;quot;ZZZ&amp;quot;  // better to use the ip, DNS will slow down the speed&lt;br /&gt;
#define MQTT_MSG_BUFFER_SIZE	50&lt;br /&gt;
#define MQTT_DEBUG_MSG_BUFFER_SIZE 50&lt;br /&gt;
#define MQTTT_BASE_TOPIC &amp;quot;home/basement/0_terrace/awning/&amp;quot;  // my rooms are numbered so there is a consideration to replace the _ with / to use the 0 also for e.g. garden&lt;br /&gt;
#define MQTT_OUT_STATE MQTTT_BASE_TOPIC &amp;quot;state&amp;quot;&lt;br /&gt;
#define MQTT_OUT_RAW MQTTT_BASE_TOPIC &amp;quot;raw&amp;quot;&lt;br /&gt;
#define MQTT_OUT_ALIVE MQTTT_BASE_TOPIC &amp;quot;alive&amp;quot;&lt;br /&gt;
#define MQTT_OUT_RSSI MQTTT_BASE_TOPIC &amp;quot;RSSI&amp;quot;&lt;br /&gt;
#define MQTT_IN_TRIGGER MQTTT_BASE_TOPIC &amp;quot;trigger&amp;quot;&lt;br /&gt;
#define MQTT_IN_DIRECTION MQTTT_BASE_TOPIC &amp;quot;direction&amp;quot;&lt;br /&gt;
#define MQTT_IN_CONFIG MQTTT_BASE_TOPIC &amp;quot;config&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//#define SERIAL_DEBUG          // used in an early stage of development; conflicts with UART output for relais control&lt;br /&gt;
#define MQTT_DEBUG_OUTPUT     // the raw topic serves to output the values for debugging // TODO struct config&lt;br /&gt;
#define ALIVEINTERVAL 10000     // UpdateInterval in milliseconds of the topics &#039;alive&#039; // TODO struct config&lt;br /&gt;
&lt;br /&gt;
//WIFI&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
const char * ssid     = WLAN_SSID;         // The SSID (name) of the Wi-Fi network you want to connect to&lt;br /&gt;
const char * password = WLAN_PASSWORD;     // The password of the Wi-Fi network&lt;br /&gt;
String mac;&lt;br /&gt;
&lt;br /&gt;
//MQTT&lt;br /&gt;
const char * MQTTServer = MQTT_SERVER_IP;&lt;br /&gt;
PubSubClient MQTTClient(espClient); //Constructor&lt;br /&gt;
unsigned long lastMsg = 0;  // to remember the last timestamp of the &#039;ALVIE-publish&#039; (heartbeat)&lt;br /&gt;
char msg[MQTT_MSG_BUFFER_SIZE]; // old style; sorry for mixing c and c++&lt;br /&gt;
char msgConfig[MQTT_DEBUG_MSG_BUFFER_SIZE]; // to store the config parameters; in setup there is no possibility to send out, so store it to send it in loop&lt;br /&gt;
long int value = 0; // don&#039;t needed anymore; its a counter replaced by DateTime&lt;br /&gt;
&lt;br /&gt;
String topicBase = MQTTT_BASE_TOPIC ;&lt;br /&gt;
String inConfigTopic = MQTT_IN_CONFIG ;&lt;br /&gt;
String inTriggerTopic = MQTT_IN_TRIGGER ;&lt;br /&gt;
String inDirectionTopic = MQTT_IN_DIRECTION;&lt;br /&gt;
String outStateTopic = MQTT_OUT_STATE ;&lt;br /&gt;
String outRawTopic = MQTT_OUT_RAW ;&lt;br /&gt;
String outAliveTopic = MQTT_OUT_ALIVE ;&lt;br /&gt;
String outRSSITopic = MQTT_OUT_RSSI ;&lt;br /&gt;
String clientId;&lt;br /&gt;
&lt;br /&gt;
//RELAIS&lt;br /&gt;
void sendRelaisCommand(unsigned int relais,boolean onoff); // forward declaration&lt;br /&gt;
volatile byte state; // 0 STOP (0,X); 1 EXTEND (1,1); 2 STOP (0,X); 3 RETRACT (1,0)&lt;br /&gt;
volatile boolean relaisState[] = {false,false};&lt;br /&gt;
struct EEPROMValues{ // use a struct to store and retrieve to/from EEPROM&lt;br /&gt;
  //const char* mark=&amp;quot;#&amp;quot;; // Before C++11, members of a struct could not be default initialized. Instead they must be initialized after an instance struct is created.&lt;br /&gt;
  //EEPROM.put/get probably have issues with strings&lt;br /&gt;
  byte mark;  //or char mark;&lt;br /&gt;
  unsigned long timeActiveDir1; // Milliseconds active till relaisstate[] falls back to {false,false}&lt;br /&gt;
  unsigned long timeActiveDir2; // Milliseconds active till relaisstate[] false back to {false,false}&lt;br /&gt;
  unsigned long checksum;&lt;br /&gt;
} settings;&lt;br /&gt;
&lt;br /&gt;
//TIMER&lt;br /&gt;
unsigned long startTimeDir1 = 0;  // Starttime if relais1 goes active&lt;br /&gt;
unsigned long startTimeDir2 = 0;  // Starttime if relais1 goes active&lt;br /&gt;
&lt;br /&gt;
//CRC&lt;br /&gt;
CRC8 crc;&lt;br /&gt;
unsigned int eepromAddr = 0;  // address to store the config variables, mark and checksum (struct settings)&lt;br /&gt;
&lt;br /&gt;
//CONFIG&lt;br /&gt;
boolean sendOutOneTime = false; // flag to output the stored config only once after powerup/reset&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//CALLBACK The function is called when a new message arrives. All code to react within this function and/or transfer the parameter to outer variables.&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/trigger&#039; -m &#039;1&#039; // or -m 1 or -m 0&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/config&#039; -m &#039;7000 8000&#039;&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/direction&#039; -m &#039;up&#039;&lt;br /&gt;
void callback(char* topic, byte* payload, unsigned int length) { // topic ends with direction or config // payload from -m within apostrophes // lentgh integer number of bytes of payload&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.print(&amp;quot;Message arrived [&amp;quot;);&lt;br /&gt;
    Serial.print(topic);&lt;br /&gt;
    Serial.print(&amp;quot;] &amp;quot;);&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; length; i++) {&lt;br /&gt;
      Serial.print((char)payload[i]);  // print out the payload array byte by byte&lt;br /&gt;
    }&lt;br /&gt;
    Serial.println();&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  String payloadStr; // to transfer the byte-array to a String&lt;br /&gt;
  char* p = (char*)malloc(length); // copy the payload to the new buffer; actually not necessary&lt;br /&gt;
  memcpy(p,payload,length); // play safe, it does no harm&lt;br /&gt;
  payloadStr.concat(p,length); // String is better to do things like .toLowerCase();&lt;br /&gt;
&lt;br /&gt;
  // Trigger CONTROL&lt;br /&gt;
  if (strcmp(inTriggerTopic.c_str(),topic) == 0 &amp;amp;&amp;amp; (char)payload[0] == &#039;1&#039;) { //1 for trigger // 0 or other are not handled&lt;br /&gt;
    boolean stateSet = false; // if the state is detected and changed ignore all following state detections&lt;br /&gt;
    // Relais 1 determines the direction&lt;br /&gt;
    // Relais 2 switches on/off respectively move/stop&lt;br /&gt;
    // activating means first switch on relay 1, pause, activate relay 2&lt;br /&gt;
    // deactivating means first switch off relay 2, pause and only then relay 1 if necessary switch&lt;br /&gt;
    // state 0 both relays off (relay 2 then 1) start condition&lt;br /&gt;
    // state 1 relay 1 set relay 1 to move out/down postition, pause, activate relay 2&lt;br /&gt;
    // state 2 both relays off (relay 2 then 1) intermediate condition&lt;br /&gt;
    // state 3 relay 1 set relay 1 to move in/up postition, pause, activate relay 2&lt;br /&gt;
    // recommendation for pausetime is 500 ms&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; !relaisState[1] &amp;amp;&amp;amp; state != 0 &amp;amp;&amp;amp; state !=2 ){ //asynchron!!! correct it&lt;br /&gt;
      state = 0;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; !relaisState[1] &amp;amp;&amp;amp; !stateSet){&lt;br /&gt;
      if(state==0){ // State 0-&amp;gt;1&lt;br /&gt;
        sendRelaisCommand(1,true); // extend&lt;br /&gt;
        delay(100);&lt;br /&gt;
        sendRelaisCommand(2,true); // activate&lt;br /&gt;
        state=1;&lt;br /&gt;
        startTimeDir1 = millis();&lt;br /&gt;
      }&lt;br /&gt;
      if(state==2){ // state 2-&amp;gt;3&lt;br /&gt;
        sendRelaisCommand(1,false); // retract&lt;br /&gt;
        delay(100);&lt;br /&gt;
        sendRelaisCommand(2,true);  // activate&lt;br /&gt;
        state=3;&lt;br /&gt;
        startTimeDir2 = millis();&lt;br /&gt;
      }&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (relaisState[0] &amp;amp;&amp;amp; relaisState[1] &amp;amp;&amp;amp; !stateSet){ // state 1-&amp;gt;2&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false); //at this point the relais could be untouched, in state 1 and 3 it will be set again, to save energy switch both relays to off&lt;br /&gt;
      state=2;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; relaisState[1] &amp;amp;&amp;amp; !stateSet){ // state 3-&amp;gt;0&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false); //at this point the relais could be untouched, in state 1 and 3 it will be set again, to save energy switch both relays to off&lt;br /&gt;
      state=0;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,state);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
     Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
     Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    MQTTClient.publish(outStateTopic.c_str(), msg);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Direction CONTROL&lt;br /&gt;
  if (strcmp(inDirectionTopic.c_str(),topic) == 0) {&lt;br /&gt;
    uint8_t direction=0; // 1 means down(out); 2 means up(in)&lt;br /&gt;
    payloadStr.toLowerCase();&lt;br /&gt;
    //MQTTClient.publish(outRawTopic.c_str(), payloadStr.c_str());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    const char directions1[3][6]={&amp;quot;down&amp;quot;,&amp;quot;out&amp;quot;,&amp;quot;on&amp;quot;}; // to react to all of this commands&lt;br /&gt;
    const char directions2[3][6]={&amp;quot;up&amp;quot;,&amp;quot;in&amp;quot;,&amp;quot;off&amp;quot;};&lt;br /&gt;
    const char directions3[3][6]={&amp;quot;halt&amp;quot;,&amp;quot;stop&amp;quot;,&amp;quot;stopp&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
    uint8_t number; // -Wunused-but-set-variable&lt;br /&gt;
    number=sizeof(directions1)/sizeof(directions1[0]); // calculate the number of array members  // Test!!! can be deleted&lt;br /&gt;
    (void)number; // to suppress the warning&lt;br /&gt;
    #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,number);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
&lt;br /&gt;
    // only one of this tests can be true // after this block direction is 1, 2 or 3 depending on command&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions1)/sizeof(directions1[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions1[i-1])==0)direction=1;&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions2)/sizeof(directions2[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions2[i-1])==0)direction=2;&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions3)/sizeof(directions3[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions3[i-1])==0)direction=3;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    if (direction == 1){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR1&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      sendRelaisCommand(1,true); // move out/down&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(2,true); // activate&lt;br /&gt;
      startTimeDir1 = millis();&lt;br /&gt;
    }&lt;br /&gt;
    if (direction == 2){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR2&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(2,true);&lt;br /&gt;
      startTimeDir2 = millis();&lt;br /&gt;
    }&lt;br /&gt;
    if (direction == 3){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR3&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // config CONTROL&lt;br /&gt;
  if (strcmp(inConfigTopic.c_str(),topic) == 0){&lt;br /&gt;
    unsigned long maxTime[] = {180,180};&lt;br /&gt;
    const char* pL = payloadStr.c_str();  // get pointer to payload string // the payload is the combination of two integer space separated&lt;br /&gt;
    char* end;                            // declare a pointer&lt;br /&gt;
    for (uint8_t i = 1; i &amp;lt;= 2; i++){&lt;br /&gt;
      maxTime[i-1] = strtol(pL,&amp;amp;end,10); // &amp;amp;end is the reference to an object of type char*, whose value is set by the function to the next character in str after the numerical value.&lt;br /&gt;
      if (pL == end) break;&lt;br /&gt;
      pL = end; // the space at the beginning of second number is seemingly not a problem&lt;br /&gt;
    }&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{%ld:%ld}&amp;quot;,maxTime[0],maxTime[1]);&lt;br /&gt;
    MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    if (maxTime[0]&amp;gt;=0 &amp;amp;&amp;amp; maxTime[0]&amp;lt;=1440000 &amp;amp;&amp;amp; maxTime[1]&amp;gt;=0 &amp;amp;&amp;amp; maxTime[1]&amp;lt;=1440000){&lt;br /&gt;
      settings.timeActiveDir1=maxTime[0];&lt;br /&gt;
      settings.timeActiveDir2=maxTime[1];&lt;br /&gt;
      crc.reset();&lt;br /&gt;
      crc.add(settings.timeActiveDir1);&lt;br /&gt;
      crc.add(settings.timeActiveDir2);&lt;br /&gt;
      settings.checksum=crc.getCRC();&lt;br /&gt;
      #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
        snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{EEPROMput:%c:%ld:%ld:%ld}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
        MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
      #endif&lt;br /&gt;
      EEPROM.put(eepromAddr, settings); //write data to array in ram&lt;br /&gt;
      EEPROM.commit();  //write data from ram to flash memory. Do nothing if there are no changes to EEPROM data in ram&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void reconnect() {&lt;br /&gt;
  // Loop until we&#039;re reconnected&lt;br /&gt;
  while (!MQTTClient.connected()) {&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Attempting MQTT connection...&amp;quot;);&lt;br /&gt;
    #endif&lt;br /&gt;
&lt;br /&gt;
    // Attempt to connect&lt;br /&gt;
    if (MQTTClient.connect(mac.c_str()))  {&lt;br /&gt;
      #ifdef SERIAL_DEBUG&lt;br /&gt;
          Serial.println(&amp;quot;connected&amp;quot;);&lt;br /&gt;
      #endif&lt;br /&gt;
      // Once connected, publish an announcement...&lt;br /&gt;
      MQTTClient.publish(topicBase.c_str(), &amp;quot;CON&amp;quot;);&lt;br /&gt;
      // ... and resubscribe&lt;br /&gt;
      MQTTClient.subscribe(inTriggerTopic.c_str());&lt;br /&gt;
      MQTTClient.subscribe(inDirectionTopic.c_str());&lt;br /&gt;
      MQTTClient.subscribe(inConfigTopic.c_str());&lt;br /&gt;
    } else {&lt;br /&gt;
      #ifdef SERIAL_DEBUG&lt;br /&gt;
        Serial.print(&amp;quot;failed, rc=&amp;quot;);&lt;br /&gt;
        Serial.print(MQTTClient.state());&lt;br /&gt;
        Serial.println(&amp;quot; try again in 5 seconds&amp;quot;);&lt;br /&gt;
      #endif&lt;br /&gt;
      // Wait 5 seconds before retrying&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendRelaisCommand(unsigned int relais,boolean onoff){&lt;br /&gt;
  //default for relais 1&lt;br /&gt;
  byte commandOn[4]={160,1,1,162};  // A0 (160 Non-breaking space) command initiate, relay 1/2, on/off, checksum (Addition)&lt;br /&gt;
  byte commandOff[4]={160,1,0,161};&lt;br /&gt;
&lt;br /&gt;
  //change commands for relais 2&lt;br /&gt;
  if (relais==2){&lt;br /&gt;
      commandOn[1]=2;&lt;br /&gt;
      commandOff[1]=2;&lt;br /&gt;
      commandOn[3]=163;&lt;br /&gt;
      commandOff[3]=162;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (onoff){&lt;br /&gt;
      Serial.write(commandOn,4);&lt;br /&gt;
      relaisState[relais-1]=true;&lt;br /&gt;
  }else{&lt;br /&gt;
      Serial.write(commandOff,4);&lt;br /&gt;
      relaisState[relais-1]=false;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;RELAIS-%d_SET_%s&amp;quot;,relais,onoff?&amp;quot;ON&amp;quot;:&amp;quot;OFF&amp;quot;);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
     Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
     Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    MQTTClient.publish(outRawTopic.c_str(),msg);&lt;br /&gt;
  #endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupDateTime() {&lt;br /&gt;
  // setup this after wifi connected&lt;br /&gt;
  // you can use custom timeZone,server and timeout&lt;br /&gt;
  // DateTime.setTimeZone(-4);&lt;br /&gt;
  //   DateTime.setServer(&amp;quot;asia.pool.ntp.org&amp;quot;);&lt;br /&gt;
  //   DateTime.begin(15 * 1000);&lt;br /&gt;
  DateTime.setServer(&amp;quot;de.pool.ntp.org&amp;quot;);&lt;br /&gt;
  DateTime.setTimeZone(&amp;quot;CEST&amp;quot;);&lt;br /&gt;
  DateTime.begin(); // ( 	const unsigned int  	timeOutMs = DEFAULT_TIMEOUT	)&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    if (!DateTime.isTimeValid()) {&lt;br /&gt;
      Serial.println(&amp;quot;Failed to get time from server.&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.printf(&amp;quot;Date Now is %s\n&amp;quot;, DateTime.toISOString().c_str());&lt;br /&gt;
      Serial.printf(&amp;quot;Timestamp is %lld\n&amp;quot;, DateTime.now());&lt;br /&gt;
    }&lt;br /&gt;
  #endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//SETUP&lt;br /&gt;
void setup() {&lt;br /&gt;
&lt;br /&gt;
   //SERIAL&lt;br /&gt;
  Serial.begin(115200);         // Start the Serial communication to send messages to the computer&lt;br /&gt;
  delay(10);&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.println(&#039;\n&#039;);&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  //WIFI&lt;br /&gt;
  WiFi.mode(WIFI_STA);// ist scheinbar der defaultwert wird in letzter Version nicht benutzt&lt;br /&gt;
  WiFi.begin(ssid, password);             // Connect to the network&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.print(&amp;quot;Connecting to &amp;quot;);&lt;br /&gt;
    Serial.print(ssid); Serial.println(&amp;quot; ...&amp;quot;);&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      int i = 0;&lt;br /&gt;
      Serial.print(++i); Serial.print(&amp;quot;-&amp;gt;&amp;quot;);&lt;br /&gt;
    #endif&lt;br /&gt;
  }&lt;br /&gt;
  mac=WiFi.macAddress();&lt;br /&gt;
  //clientId += String(random(0xffff), HEX);&lt;br /&gt;
  clientId = &amp;quot;ESP01-&amp;quot; + mac;&lt;br /&gt;
&lt;br /&gt;
  randomSeed(micros());&lt;br /&gt;
&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.println(&#039;\n&#039;);&lt;br /&gt;
    Serial.println(&amp;quot;Connection established!&amp;quot;);&lt;br /&gt;
    Serial.print(&amp;quot;IP address:\t&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());         // Send the IP address of the ESP8266 to the computer&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  //MQTT&lt;br /&gt;
  MQTTClient.setServer(MQTTServer, 1883);&lt;br /&gt;
  MQTTClient.setCallback(callback);&lt;br /&gt;
&lt;br /&gt;
  //DATETIME&lt;br /&gt;
  setupDateTime();&lt;br /&gt;
&lt;br /&gt;
  //TIMESETTING&lt;br /&gt;
  EEPROM.begin(sizeof(settings)); // 2 + 8 + 8 + 8&lt;br /&gt;
  boolean reliable1 = false, reliable2 = false;&lt;br /&gt;
  EEPROM.get(eepromAddr, settings); //read data from array in ram and cast it into struct called settings&lt;br /&gt;
  #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
    // in setup() MQTT isn&#039;t connected so store the values and publish it in loop()&lt;br /&gt;
    snprintf (msgConfig, 50, &amp;quot;{EEPROMget:%c:%ld:%ld:%#lx}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
  #endif&lt;br /&gt;
  //if (strcmp(settings.mark,&amp;quot;#&amp;quot;) == 0){&lt;br /&gt;
  if (settings.mark == 35){&lt;br /&gt;
    crc.reset();&lt;br /&gt;
    crc.add(settings.timeActiveDir1);&lt;br /&gt;
    crc.add(settings.timeActiveDir2);&lt;br /&gt;
    if (crc.getCRC()==settings.checksum) reliable1 = true;&lt;br /&gt;
  }&lt;br /&gt;
  if (settings.timeActiveDir1 &amp;gt;= 50 &amp;amp;&amp;amp;     // values less than 50 ms  and greater than 24 hours are senseless&lt;br /&gt;
      settings.timeActiveDir1 &amp;lt;= 1440000 &amp;amp;&amp;amp;&lt;br /&gt;
      settings.timeActiveDir2 &amp;gt;= 50 &amp;amp;&amp;amp;&lt;br /&gt;
      settings.timeActiveDir2 &amp;lt;= 1440000) reliable2 = true;&lt;br /&gt;
&lt;br /&gt;
if (!(reliable1 &amp;amp;&amp;amp; reliable2)){ // if the Content of EEPROM is undefined, set it&lt;br /&gt;
  settings.mark = 35;&lt;br /&gt;
  settings.timeActiveDir1 = 180 * 1000; // default 3 minutes to switch off&lt;br /&gt;
  settings.timeActiveDir2 = 180 * 1000;&lt;br /&gt;
  crc.reset();&lt;br /&gt;
  crc.add(settings.timeActiveDir1);&lt;br /&gt;
  crc.add(settings.timeActiveDir2);&lt;br /&gt;
  settings.checksum=crc.getCRC();&lt;br /&gt;
  EEPROM.put(eepromAddr, settings); //write data structure to ram&lt;br /&gt;
  EEPROM.commit();  //write data from ram to flash memory. Do nothing if there are no changes to EEPROM data in ram&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
  //RELAIS setup&lt;br /&gt;
  Serial.write(0); // initialize the uContoller that controls the relays&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// LOOP&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
  if (!MQTTClient.connected()) {&lt;br /&gt;
    reconnect();&lt;br /&gt;
  }&lt;br /&gt;
  MQTTClient.loop();&lt;br /&gt;
&lt;br /&gt;
  // send out one time after setup&lt;br /&gt;
  if (!sendOutOneTime) {&lt;br /&gt;
    #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msgConfig);&lt;br /&gt;
      snprintf (msgConfig, 50, &amp;quot;{config:%c:%ld:%ld:%#lx}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msgConfig);&lt;br /&gt;
      sendOutOneTime = true;&lt;br /&gt;
    #endif&lt;br /&gt;
  }&lt;br /&gt;
  unsigned long now = millis();&lt;br /&gt;
&lt;br /&gt;
   if (now - lastMsg &amp;gt; ALIVEINTERVAL) {&lt;br /&gt;
     lastMsg = now;&lt;br /&gt;
     ++value;&lt;br /&gt;
     snprintf (msg, MQTT_MSG_BUFFER_SIZE, &amp;quot;%s&amp;quot;,DateTime.toISOString().c_str());&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
      Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
     MQTTClient.publish(outAliveTopic.c_str(), msg);&lt;br /&gt;
     snprintf (msg, MQTT_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,WiFi.RSSI());&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
      Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
     MQTTClient.publish(outRSSITopic.c_str(), msg);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   if ((now - startTimeDir1 &amp;gt; settings.timeActiveDir1) &amp;amp;&amp;amp; (startTimeDir1 != 0)){   // switch both relays to off after milliseconds defined in config (energy saving and safety aspects)&lt;br /&gt;
     #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{Fallback1:%ld:%ld:%ld}&amp;quot;,now,startTimeDir1,settings.timeActiveDir1);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    sendRelaisCommand(2,false);&lt;br /&gt;
    delay(100);&lt;br /&gt;
    sendRelaisCommand(1,false);&lt;br /&gt;
    delay(500);&lt;br /&gt;
    startTimeDir1 = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   if ((now - startTimeDir2 &amp;gt; settings.timeActiveDir2) &amp;amp;&amp;amp; (startTimeDir2 != 0)){    // switch both relays to off after milliseconds defined in config (energy saving and safety aspects)&lt;br /&gt;
     #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{Fallback2:%ld:%ld:%ld}&amp;quot;,now,startTimeDir2,settings.timeActiveDir2);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    sendRelaisCommand(2,false);&lt;br /&gt;
    delay(100);&lt;br /&gt;
    sendRelaisCommand(1,false);&lt;br /&gt;
    delay(500);&lt;br /&gt;
    startTimeDir2 = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Beschreibung==&lt;br /&gt;
&lt;br /&gt;
Die Software ist eine Firmware für einen **ESP8266 (ESP-01)**, die über MQTT eine **motorisierte Markise** oder einen ähnlichen Sonnenschutz steuert. &lt;br /&gt;
&lt;br /&gt;
## Hauptfunktion&lt;br /&gt;
&lt;br /&gt;
Der ESP8266 verbindet sich mit WLAN und einem MQTT-Broker und steuert über eine serielle Relaisplatine zwei Relais:&lt;br /&gt;
&lt;br /&gt;
* Relais 1 = Fahrtrichtung (Ausfahren / Einfahren)&lt;br /&gt;
* Relais 2 = Motor EIN/AUS&lt;br /&gt;
&lt;br /&gt;
Die Steuerung erfolgt per MQTT-Befehlen. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## MQTT-Schnittstelle&lt;br /&gt;
&lt;br /&gt;
Basis-Topic:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
home/basement/0_terrace/awning/&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Eingehende Befehle:&lt;br /&gt;
&lt;br /&gt;
| Topic     | Funktion                 |&lt;br /&gt;
| --------- | ------------------------ |&lt;br /&gt;
| trigger   | Zustandswechsel          |&lt;br /&gt;
| direction | Richtung vorgeben        |&lt;br /&gt;
| config    | Laufzeiten konfigurieren |&lt;br /&gt;
&lt;br /&gt;
Ausgehende Statusmeldungen:&lt;br /&gt;
&lt;br /&gt;
| Topic | Inhalt                    |&lt;br /&gt;
| ----- | ------------------------- |&lt;br /&gt;
| state | aktueller Zustand         |&lt;br /&gt;
| raw   | Debug-Ausgaben            |&lt;br /&gt;
| alive | Heartbeat mit Zeitstempel |&lt;br /&gt;
| RSSI  | WLAN-Empfangsstärke       |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Zustandsautomat&lt;br /&gt;
&lt;br /&gt;
Es existieren vier Zustände:&lt;br /&gt;
&lt;br /&gt;
| Zustand | Bedeutung |&lt;br /&gt;
| ------- | --------- |&lt;br /&gt;
| 0       | Stopp     |&lt;br /&gt;
| 1       | Ausfahren |&lt;br /&gt;
| 2       | Stopp     |&lt;br /&gt;
| 3       | Einfahren |&lt;br /&gt;
&lt;br /&gt;
Bei jedem MQTT-Trigger wird zum nächsten Zustand gewechselt.&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
0 → 1 → 2 → 3 → 0&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Dadurch genügt ein einziger Taster oder MQTT-Befehl zum Steuern der Markise. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Richtungssteuerung&lt;br /&gt;
&lt;br /&gt;
Per MQTT können direkte Befehle gesendet werden:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
down&lt;br /&gt;
out&lt;br /&gt;
on&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Markise ausfahren&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
up&lt;br /&gt;
in&lt;br /&gt;
off&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Markise einfahren&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
halt&lt;br /&gt;
stop&lt;br /&gt;
stopp&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Motor stoppen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Sicherheitsschaltung&lt;br /&gt;
&lt;br /&gt;
Für beide Richtungen existiert eine maximale Laufzeit:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
timeActiveDir1&lt;br /&gt;
timeActiveDir2&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Nach Ablauf dieser Zeit werden beide Relais automatisch abgeschaltet.&lt;br /&gt;
&lt;br /&gt;
Standard:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
180 Sekunden&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Damit wird verhindert, dass ein defekter MQTT-Befehl oder ein Softwarefehler den Motor dauerhaft laufen lässt. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## EEPROM-Speicherung&lt;br /&gt;
&lt;br /&gt;
Die Laufzeiten werden dauerhaft gespeichert:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
struct EEPROMValues&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Inhalt:&lt;br /&gt;
&lt;br /&gt;
* Kennzeichen (`mark`)&lt;br /&gt;
* Laufzeit Richtung 1&lt;br /&gt;
* Laufzeit Richtung 2&lt;br /&gt;
* CRC-Prüfsumme&lt;br /&gt;
&lt;br /&gt;
Beim Start prüft die Firmware die Daten auf Plausibilität und Integrität. Falls ungültig, werden Standardwerte erzeugt. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Netzwerkfunktionen&lt;br /&gt;
&lt;br /&gt;
Die Firmware:&lt;br /&gt;
&lt;br /&gt;
* verbindet sich mit WLAN&lt;br /&gt;
* verbindet sich mit MQTT&lt;br /&gt;
* synchronisiert die Uhr per NTP&lt;br /&gt;
* sendet alle 10 Sekunden einen Heartbeat&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
2025-03-01T12:34:56+01:00&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
sowie die aktuelle WLAN-Feldstärke. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Hardware&lt;br /&gt;
&lt;br /&gt;
Aus dem Code lässt sich folgende Hardware ableiten:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
ESP8266 (ESP-01)&lt;br /&gt;
        │&lt;br /&gt;
UART&lt;br /&gt;
        │&lt;br /&gt;
2-Kanal Relaiscontroller&lt;br /&gt;
        │&lt;br /&gt;
Markisenmotor&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Die Relaisplatine wird über ein proprietäres 4-Byte-Protokoll gesteuert:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
A0 Relais Zustand Checksumme&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
A0 01 01 A2&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
= Relais 1 EIN. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Zusammenfassung&lt;br /&gt;
&lt;br /&gt;
Die Software ist ein **MQTT-gesteuerter Markisencontroller auf Basis eines ESP8266**, der:&lt;br /&gt;
&lt;br /&gt;
* WLAN und MQTT nutzt,&lt;br /&gt;
* eine Markise ein- und ausfährt,&lt;br /&gt;
* Fahrzeiten im EEPROM speichert,&lt;br /&gt;
* automatisch abschaltet,&lt;br /&gt;
* Status- und Debuginformationen veröffentlicht,&lt;br /&gt;
* per Home Assistant, Node-RED oder FHEM steuerbar ist. &lt;br /&gt;
&lt;br /&gt;
Der Aufbau wirkt wie ein typisches Smart-Home-Projekt zur Nachrüstung einer vorhandenen Funk- oder Tastersteuerung einer Terrasse-Markise.&lt;br /&gt;
&lt;br /&gt;
==Fehlerquellen==&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=WLAN_Aktor_Markise&amp;diff=4916</id>
		<title>WLAN Aktor Markise</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=WLAN_Aktor_Markise&amp;diff=4916"/>
		<updated>2026-06-13T15:52:37Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Software */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Leider hatte ich keinen Port mehr an den KNX-Rollladenaktoren zur Verfuegung, daher bin ich auf eine MQTT-WLAN-ESP-DuoRelais-Schaltung ausgewischen.&lt;br /&gt;
&lt;br /&gt;
=Hardware=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Software=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
//ShutterBlindsAwningFunction with LC-Relay-ESP01-2R-5V&lt;br /&gt;
//tested 2022-08-06&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;ESP8266WiFi.h&amp;gt;        // Include the Wi-Fi library&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;       // MQTT&lt;br /&gt;
#include &amp;quot;ESPDateTime.h&amp;quot;        // NTP&lt;br /&gt;
#include &amp;lt;ESP_EEPROM.h&amp;gt;         // to store non-volatile variables&lt;br /&gt;
#include &amp;quot;CRC.h&amp;quot;                // to check the reliability of the EEPROM-stored variables&lt;br /&gt;
#include &amp;quot;CRC8.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//CONFIGURATION&lt;br /&gt;
#define WLAN_SSID &amp;quot;XXX&amp;quot;&lt;br /&gt;
#define WLAN_PASSWORD &amp;quot;YYY&amp;quot;&lt;br /&gt;
#define MQTT_SERVER_IP &amp;quot;ZZZ&amp;quot;  // better to use the ip, DNS will slow down the speed&lt;br /&gt;
#define MQTT_MSG_BUFFER_SIZE	50&lt;br /&gt;
#define MQTT_DEBUG_MSG_BUFFER_SIZE 50&lt;br /&gt;
#define MQTTT_BASE_TOPIC &amp;quot;home/basement/0_terrace/awning/&amp;quot;  // my rooms are numbered so there is a consideration to replace the _ with / to use the 0 also for e.g. garden&lt;br /&gt;
#define MQTT_OUT_STATE MQTTT_BASE_TOPIC &amp;quot;state&amp;quot;&lt;br /&gt;
#define MQTT_OUT_RAW MQTTT_BASE_TOPIC &amp;quot;raw&amp;quot;&lt;br /&gt;
#define MQTT_OUT_ALIVE MQTTT_BASE_TOPIC &amp;quot;alive&amp;quot;&lt;br /&gt;
#define MQTT_OUT_RSSI MQTTT_BASE_TOPIC &amp;quot;RSSI&amp;quot;&lt;br /&gt;
#define MQTT_IN_TRIGGER MQTTT_BASE_TOPIC &amp;quot;trigger&amp;quot;&lt;br /&gt;
#define MQTT_IN_DIRECTION MQTTT_BASE_TOPIC &amp;quot;direction&amp;quot;&lt;br /&gt;
#define MQTT_IN_CONFIG MQTTT_BASE_TOPIC &amp;quot;config&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//#define SERIAL_DEBUG          // used in an early stage of development; conflicts with UART output for relais control&lt;br /&gt;
#define MQTT_DEBUG_OUTPUT     // the raw topic serves to output the values for debugging // TODO struct config&lt;br /&gt;
#define ALIVEINTERVAL 10000     // UpdateInterval in milliseconds of the topics &#039;alive&#039; // TODO struct config&lt;br /&gt;
&lt;br /&gt;
//WIFI&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
const char * ssid     = WLAN_SSID;         // The SSID (name) of the Wi-Fi network you want to connect to&lt;br /&gt;
const char * password = WLAN_PASSWORD;     // The password of the Wi-Fi network&lt;br /&gt;
String mac;&lt;br /&gt;
&lt;br /&gt;
//MQTT&lt;br /&gt;
const char * MQTTServer = MQTT_SERVER_IP;&lt;br /&gt;
PubSubClient MQTTClient(espClient); //Constructor&lt;br /&gt;
unsigned long lastMsg = 0;  // to remember the last timestamp of the &#039;ALVIE-publish&#039; (heartbeat)&lt;br /&gt;
char msg[MQTT_MSG_BUFFER_SIZE]; // old style; sorry for mixing c and c++&lt;br /&gt;
char msgConfig[MQTT_DEBUG_MSG_BUFFER_SIZE]; // to store the config parameters; in setup there is no possibility to send out, so store it to send it in loop&lt;br /&gt;
long int value = 0; // don&#039;t needed anymore; its a counter replaced by DateTime&lt;br /&gt;
&lt;br /&gt;
String topicBase = MQTTT_BASE_TOPIC ;&lt;br /&gt;
String inConfigTopic = MQTT_IN_CONFIG ;&lt;br /&gt;
String inTriggerTopic = MQTT_IN_TRIGGER ;&lt;br /&gt;
String inDirectionTopic = MQTT_IN_DIRECTION;&lt;br /&gt;
String outStateTopic = MQTT_OUT_STATE ;&lt;br /&gt;
String outRawTopic = MQTT_OUT_RAW ;&lt;br /&gt;
String outAliveTopic = MQTT_OUT_ALIVE ;&lt;br /&gt;
String outRSSITopic = MQTT_OUT_RSSI ;&lt;br /&gt;
String clientId;&lt;br /&gt;
&lt;br /&gt;
//RELAIS&lt;br /&gt;
void sendRelaisCommand(unsigned int relais,boolean onoff); // forward declaration&lt;br /&gt;
volatile byte state; // 0 STOP (0,X); 1 EXTEND (1,1); 2 STOP (0,X); 3 RETRACT (1,0)&lt;br /&gt;
volatile boolean relaisState[] = {false,false};&lt;br /&gt;
struct EEPROMValues{ // use a struct to store and retrieve to/from EEPROM&lt;br /&gt;
  //const char* mark=&amp;quot;#&amp;quot;; // Before C++11, members of a struct could not be default initialized. Instead they must be initialized after an instance struct is created.&lt;br /&gt;
  //EEPROM.put/get probably have issues with strings&lt;br /&gt;
  byte mark;  //or char mark;&lt;br /&gt;
  unsigned long timeActiveDir1; // Milliseconds active till relaisstate[] falls back to {false,false}&lt;br /&gt;
  unsigned long timeActiveDir2; // Milliseconds active till relaisstate[] false back to {false,false}&lt;br /&gt;
  unsigned long checksum;&lt;br /&gt;
} settings;&lt;br /&gt;
&lt;br /&gt;
//TIMER&lt;br /&gt;
unsigned long startTimeDir1 = 0;  // Starttime if relais1 goes active&lt;br /&gt;
unsigned long startTimeDir2 = 0;  // Starttime if relais1 goes active&lt;br /&gt;
&lt;br /&gt;
//CRC&lt;br /&gt;
CRC8 crc;&lt;br /&gt;
unsigned int eepromAddr = 0;  // address to store the config variables, mark and checksum (struct settings)&lt;br /&gt;
&lt;br /&gt;
//CONFIG&lt;br /&gt;
boolean sendOutOneTime = false; // flag to output the stored config only once after powerup/reset&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//CALLBACK The function is called when a new message arrives. All code to react within this function and/or transfer the parameter to outer variables.&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/trigger&#039; -m &#039;1&#039; // or -m 1 or -m 0&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/config&#039; -m &#039;7000 8000&#039;&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/direction&#039; -m &#039;up&#039;&lt;br /&gt;
void callback(char* topic, byte* payload, unsigned int length) { // topic ends with direction or config // payload from -m within apostrophes // lentgh integer number of bytes of payload&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.print(&amp;quot;Message arrived [&amp;quot;);&lt;br /&gt;
    Serial.print(topic);&lt;br /&gt;
    Serial.print(&amp;quot;] &amp;quot;);&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; length; i++) {&lt;br /&gt;
      Serial.print((char)payload[i]);  // print out the payload array byte by byte&lt;br /&gt;
    }&lt;br /&gt;
    Serial.println();&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  String payloadStr; // to transfer the byte-array to a String&lt;br /&gt;
  char* p = (char*)malloc(length); // copy the payload to the new buffer; actually not necessary&lt;br /&gt;
  memcpy(p,payload,length); // play safe, it does no harm&lt;br /&gt;
  payloadStr.concat(p,length); // String is better to do things like .toLowerCase();&lt;br /&gt;
&lt;br /&gt;
  // Trigger CONTROL&lt;br /&gt;
  if (strcmp(inTriggerTopic.c_str(),topic) == 0 &amp;amp;&amp;amp; (char)payload[0] == &#039;1&#039;) { //1 for trigger // 0 or other are not handled&lt;br /&gt;
    boolean stateSet = false; // if the state is detected and changed ignore all following state detections&lt;br /&gt;
    // Relais 1 determines the direction&lt;br /&gt;
    // Relais 2 switches on/off respectively move/stop&lt;br /&gt;
    // activating means first switch on relay 1, pause, activate relay 2&lt;br /&gt;
    // deactivating means first switch off relay 2, pause and only then relay 1 if necessary switch&lt;br /&gt;
    // state 0 both relays off (relay 2 then 1) start condition&lt;br /&gt;
    // state 1 relay 1 set relay 1 to move out/down postition, pause, activate relay 2&lt;br /&gt;
    // state 2 both relays off (relay 2 then 1) intermediate condition&lt;br /&gt;
    // state 3 relay 1 set relay 1 to move in/up postition, pause, activate relay 2&lt;br /&gt;
    // recommendation for pausetime is 500 ms&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; !relaisState[1] &amp;amp;&amp;amp; state != 0 &amp;amp;&amp;amp; state !=2 ){ //asynchron!!! correct it&lt;br /&gt;
      state = 0;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; !relaisState[1] &amp;amp;&amp;amp; !stateSet){&lt;br /&gt;
      if(state==0){ // State 0-&amp;gt;1&lt;br /&gt;
        sendRelaisCommand(1,true); // extend&lt;br /&gt;
        delay(100);&lt;br /&gt;
        sendRelaisCommand(2,true); // activate&lt;br /&gt;
        state=1;&lt;br /&gt;
        startTimeDir1 = millis();&lt;br /&gt;
      }&lt;br /&gt;
      if(state==2){ // state 2-&amp;gt;3&lt;br /&gt;
        sendRelaisCommand(1,false); // retract&lt;br /&gt;
        delay(100);&lt;br /&gt;
        sendRelaisCommand(2,true);  // activate&lt;br /&gt;
        state=3;&lt;br /&gt;
        startTimeDir2 = millis();&lt;br /&gt;
      }&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (relaisState[0] &amp;amp;&amp;amp; relaisState[1] &amp;amp;&amp;amp; !stateSet){ // state 1-&amp;gt;2&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false); //at this point the relais could be untouched, in state 1 and 3 it will be set again, to save energy switch both relays to off&lt;br /&gt;
      state=2;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; relaisState[1] &amp;amp;&amp;amp; !stateSet){ // state 3-&amp;gt;0&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false); //at this point the relais could be untouched, in state 1 and 3 it will be set again, to save energy switch both relays to off&lt;br /&gt;
      state=0;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,state);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
     Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
     Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    MQTTClient.publish(outStateTopic.c_str(), msg);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Direction CONTROL&lt;br /&gt;
  if (strcmp(inDirectionTopic.c_str(),topic) == 0) {&lt;br /&gt;
    uint8_t direction=0; // 1 means down(out); 2 means up(in)&lt;br /&gt;
    payloadStr.toLowerCase();&lt;br /&gt;
    //MQTTClient.publish(outRawTopic.c_str(), payloadStr.c_str());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    const char directions1[3][6]={&amp;quot;down&amp;quot;,&amp;quot;out&amp;quot;,&amp;quot;on&amp;quot;}; // to react to all of this commands&lt;br /&gt;
    const char directions2[3][6]={&amp;quot;up&amp;quot;,&amp;quot;in&amp;quot;,&amp;quot;off&amp;quot;};&lt;br /&gt;
    const char directions3[3][6]={&amp;quot;halt&amp;quot;,&amp;quot;stop&amp;quot;,&amp;quot;stopp&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
    uint8_t number; // -Wunused-but-set-variable&lt;br /&gt;
    number=sizeof(directions1)/sizeof(directions1[0]); // calculate the number of array members  // Test!!! can be deleted&lt;br /&gt;
    (void)number; // to suppress the warning&lt;br /&gt;
    #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,number);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
&lt;br /&gt;
    // only one of this tests can be true // after this block direction is 1, 2 or 3 depending on command&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions1)/sizeof(directions1[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions1[i-1])==0)direction=1;&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions2)/sizeof(directions2[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions2[i-1])==0)direction=2;&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions3)/sizeof(directions3[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions3[i-1])==0)direction=3;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    if (direction == 1){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR1&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      sendRelaisCommand(1,true); // move out/down&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(2,true); // activate&lt;br /&gt;
      startTimeDir1 = millis();&lt;br /&gt;
    }&lt;br /&gt;
    if (direction == 2){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR2&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(2,true);&lt;br /&gt;
      startTimeDir2 = millis();&lt;br /&gt;
    }&lt;br /&gt;
    if (direction == 3){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR3&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // config CONTROL&lt;br /&gt;
  if (strcmp(inConfigTopic.c_str(),topic) == 0){&lt;br /&gt;
    unsigned long maxTime[] = {180,180};&lt;br /&gt;
    const char* pL = payloadStr.c_str();  // get pointer to payload string // the payload is the combination of two integer space separated&lt;br /&gt;
    char* end;                            // declare a pointer&lt;br /&gt;
    for (uint8_t i = 1; i &amp;lt;= 2; i++){&lt;br /&gt;
      maxTime[i-1] = strtol(pL,&amp;amp;end,10); // &amp;amp;end is the reference to an object of type char*, whose value is set by the function to the next character in str after the numerical value.&lt;br /&gt;
      if (pL == end) break;&lt;br /&gt;
      pL = end; // the space at the beginning of second number is seemingly not a problem&lt;br /&gt;
    }&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{%ld:%ld}&amp;quot;,maxTime[0],maxTime[1]);&lt;br /&gt;
    MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    if (maxTime[0]&amp;gt;=0 &amp;amp;&amp;amp; maxTime[0]&amp;lt;=1440000 &amp;amp;&amp;amp; maxTime[1]&amp;gt;=0 &amp;amp;&amp;amp; maxTime[1]&amp;lt;=1440000){&lt;br /&gt;
      settings.timeActiveDir1=maxTime[0];&lt;br /&gt;
      settings.timeActiveDir2=maxTime[1];&lt;br /&gt;
      crc.reset();&lt;br /&gt;
      crc.add(settings.timeActiveDir1);&lt;br /&gt;
      crc.add(settings.timeActiveDir2);&lt;br /&gt;
      settings.checksum=crc.getCRC();&lt;br /&gt;
      #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
        snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{EEPROMput:%c:%ld:%ld:%ld}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
        MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
      #endif&lt;br /&gt;
      EEPROM.put(eepromAddr, settings); //write data to array in ram&lt;br /&gt;
      EEPROM.commit();  //write data from ram to flash memory. Do nothing if there are no changes to EEPROM data in ram&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void reconnect() {&lt;br /&gt;
  // Loop until we&#039;re reconnected&lt;br /&gt;
  while (!MQTTClient.connected()) {&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Attempting MQTT connection...&amp;quot;);&lt;br /&gt;
    #endif&lt;br /&gt;
&lt;br /&gt;
    // Attempt to connect&lt;br /&gt;
    if (MQTTClient.connect(mac.c_str()))  {&lt;br /&gt;
      #ifdef SERIAL_DEBUG&lt;br /&gt;
          Serial.println(&amp;quot;connected&amp;quot;);&lt;br /&gt;
      #endif&lt;br /&gt;
      // Once connected, publish an announcement...&lt;br /&gt;
      MQTTClient.publish(topicBase.c_str(), &amp;quot;CON&amp;quot;);&lt;br /&gt;
      // ... and resubscribe&lt;br /&gt;
      MQTTClient.subscribe(inTriggerTopic.c_str());&lt;br /&gt;
      MQTTClient.subscribe(inDirectionTopic.c_str());&lt;br /&gt;
      MQTTClient.subscribe(inConfigTopic.c_str());&lt;br /&gt;
    } else {&lt;br /&gt;
      #ifdef SERIAL_DEBUG&lt;br /&gt;
        Serial.print(&amp;quot;failed, rc=&amp;quot;);&lt;br /&gt;
        Serial.print(MQTTClient.state());&lt;br /&gt;
        Serial.println(&amp;quot; try again in 5 seconds&amp;quot;);&lt;br /&gt;
      #endif&lt;br /&gt;
      // Wait 5 seconds before retrying&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendRelaisCommand(unsigned int relais,boolean onoff){&lt;br /&gt;
  //default for relais 1&lt;br /&gt;
  byte commandOn[4]={160,1,1,162};  // A0 (160 Non-breaking space) command initiate, relay 1/2, on/off, checksum (Addition)&lt;br /&gt;
  byte commandOff[4]={160,1,0,161};&lt;br /&gt;
&lt;br /&gt;
  //change commands for relais 2&lt;br /&gt;
  if (relais==2){&lt;br /&gt;
      commandOn[1]=2;&lt;br /&gt;
      commandOff[1]=2;&lt;br /&gt;
      commandOn[3]=163;&lt;br /&gt;
      commandOff[3]=162;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (onoff){&lt;br /&gt;
      Serial.write(commandOn,4);&lt;br /&gt;
      relaisState[relais-1]=true;&lt;br /&gt;
  }else{&lt;br /&gt;
      Serial.write(commandOff,4);&lt;br /&gt;
      relaisState[relais-1]=false;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;RELAIS-%d_SET_%s&amp;quot;,relais,onoff?&amp;quot;ON&amp;quot;:&amp;quot;OFF&amp;quot;);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
     Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
     Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    MQTTClient.publish(outRawTopic.c_str(),msg);&lt;br /&gt;
  #endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupDateTime() {&lt;br /&gt;
  // setup this after wifi connected&lt;br /&gt;
  // you can use custom timeZone,server and timeout&lt;br /&gt;
  // DateTime.setTimeZone(-4);&lt;br /&gt;
  //   DateTime.setServer(&amp;quot;asia.pool.ntp.org&amp;quot;);&lt;br /&gt;
  //   DateTime.begin(15 * 1000);&lt;br /&gt;
  DateTime.setServer(&amp;quot;de.pool.ntp.org&amp;quot;);&lt;br /&gt;
  DateTime.setTimeZone(&amp;quot;CEST&amp;quot;);&lt;br /&gt;
  DateTime.begin(); // ( 	const unsigned int  	timeOutMs = DEFAULT_TIMEOUT	)&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    if (!DateTime.isTimeValid()) {&lt;br /&gt;
      Serial.println(&amp;quot;Failed to get time from server.&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.printf(&amp;quot;Date Now is %s\n&amp;quot;, DateTime.toISOString().c_str());&lt;br /&gt;
      Serial.printf(&amp;quot;Timestamp is %lld\n&amp;quot;, DateTime.now());&lt;br /&gt;
    }&lt;br /&gt;
  #endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//SETUP&lt;br /&gt;
void setup() {&lt;br /&gt;
&lt;br /&gt;
   //SERIAL&lt;br /&gt;
  Serial.begin(115200);         // Start the Serial communication to send messages to the computer&lt;br /&gt;
  delay(10);&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.println(&#039;\n&#039;);&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  //WIFI&lt;br /&gt;
  WiFi.mode(WIFI_STA);// ist scheinbar der defaultwert wird in letzter Version nicht benutzt&lt;br /&gt;
  WiFi.begin(ssid, password);             // Connect to the network&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.print(&amp;quot;Connecting to &amp;quot;);&lt;br /&gt;
    Serial.print(ssid); Serial.println(&amp;quot; ...&amp;quot;);&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      int i = 0;&lt;br /&gt;
      Serial.print(++i); Serial.print(&amp;quot;-&amp;gt;&amp;quot;);&lt;br /&gt;
    #endif&lt;br /&gt;
  }&lt;br /&gt;
  mac=WiFi.macAddress();&lt;br /&gt;
  //clientId += String(random(0xffff), HEX);&lt;br /&gt;
  clientId = &amp;quot;ESP01-&amp;quot; + mac;&lt;br /&gt;
&lt;br /&gt;
  randomSeed(micros());&lt;br /&gt;
&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.println(&#039;\n&#039;);&lt;br /&gt;
    Serial.println(&amp;quot;Connection established!&amp;quot;);&lt;br /&gt;
    Serial.print(&amp;quot;IP address:\t&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());         // Send the IP address of the ESP8266 to the computer&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  //MQTT&lt;br /&gt;
  MQTTClient.setServer(MQTTServer, 1883);&lt;br /&gt;
  MQTTClient.setCallback(callback);&lt;br /&gt;
&lt;br /&gt;
  //DATETIME&lt;br /&gt;
  setupDateTime();&lt;br /&gt;
&lt;br /&gt;
  //TIMESETTING&lt;br /&gt;
  EEPROM.begin(sizeof(settings)); // 2 + 8 + 8 + 8&lt;br /&gt;
  boolean reliable1 = false, reliable2 = false;&lt;br /&gt;
  EEPROM.get(eepromAddr, settings); //read data from array in ram and cast it into struct called settings&lt;br /&gt;
  #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
    // in setup() MQTT isn&#039;t connected so store the values and publish it in loop()&lt;br /&gt;
    snprintf (msgConfig, 50, &amp;quot;{EEPROMget:%c:%ld:%ld:%#lx}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
  #endif&lt;br /&gt;
  //if (strcmp(settings.mark,&amp;quot;#&amp;quot;) == 0){&lt;br /&gt;
  if (settings.mark == 35){&lt;br /&gt;
    crc.reset();&lt;br /&gt;
    crc.add(settings.timeActiveDir1);&lt;br /&gt;
    crc.add(settings.timeActiveDir2);&lt;br /&gt;
    if (crc.getCRC()==settings.checksum) reliable1 = true;&lt;br /&gt;
  }&lt;br /&gt;
  if (settings.timeActiveDir1 &amp;gt;= 50 &amp;amp;&amp;amp;     // values less than 50 ms  and greater than 24 hours are senseless&lt;br /&gt;
      settings.timeActiveDir1 &amp;lt;= 1440000 &amp;amp;&amp;amp;&lt;br /&gt;
      settings.timeActiveDir2 &amp;gt;= 50 &amp;amp;&amp;amp;&lt;br /&gt;
      settings.timeActiveDir2 &amp;lt;= 1440000) reliable2 = true;&lt;br /&gt;
&lt;br /&gt;
if (!(reliable1 &amp;amp;&amp;amp; reliable2)){ // if the Content of EEPROM is undefined, set it&lt;br /&gt;
  settings.mark = 35;&lt;br /&gt;
  settings.timeActiveDir1 = 180 * 1000; // default 3 minutes to switch off&lt;br /&gt;
  settings.timeActiveDir2 = 180 * 1000;&lt;br /&gt;
  crc.reset();&lt;br /&gt;
  crc.add(settings.timeActiveDir1);&lt;br /&gt;
  crc.add(settings.timeActiveDir2);&lt;br /&gt;
  settings.checksum=crc.getCRC();&lt;br /&gt;
  EEPROM.put(eepromAddr, settings); //write data structure to ram&lt;br /&gt;
  EEPROM.commit();  //write data from ram to flash memory. Do nothing if there are no changes to EEPROM data in ram&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
  //RELAIS setup&lt;br /&gt;
  Serial.write(0); // initialize the uContoller that controls the relays&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// LOOP&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
  if (!MQTTClient.connected()) {&lt;br /&gt;
    reconnect();&lt;br /&gt;
  }&lt;br /&gt;
  MQTTClient.loop();&lt;br /&gt;
&lt;br /&gt;
  // send out one time after setup&lt;br /&gt;
  if (!sendOutOneTime) {&lt;br /&gt;
    #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msgConfig);&lt;br /&gt;
      snprintf (msgConfig, 50, &amp;quot;{config:%c:%ld:%ld:%#lx}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msgConfig);&lt;br /&gt;
      sendOutOneTime = true;&lt;br /&gt;
    #endif&lt;br /&gt;
  }&lt;br /&gt;
  unsigned long now = millis();&lt;br /&gt;
&lt;br /&gt;
   if (now - lastMsg &amp;gt; ALIVEINTERVAL) {&lt;br /&gt;
     lastMsg = now;&lt;br /&gt;
     ++value;&lt;br /&gt;
     snprintf (msg, MQTT_MSG_BUFFER_SIZE, &amp;quot;%s&amp;quot;,DateTime.toISOString().c_str());&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
      Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
     MQTTClient.publish(outAliveTopic.c_str(), msg);&lt;br /&gt;
     snprintf (msg, MQTT_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,WiFi.RSSI());&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
      Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
     MQTTClient.publish(outRSSITopic.c_str(), msg);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   if ((now - startTimeDir1 &amp;gt; settings.timeActiveDir1) &amp;amp;&amp;amp; (startTimeDir1 != 0)){   // switch both relays to off after milliseconds defined in config (energy saving and safety aspects)&lt;br /&gt;
     #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{Fallback1:%ld:%ld:%ld}&amp;quot;,now,startTimeDir1,settings.timeActiveDir1);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    sendRelaisCommand(2,false);&lt;br /&gt;
    delay(100);&lt;br /&gt;
    sendRelaisCommand(1,false);&lt;br /&gt;
    delay(500);&lt;br /&gt;
    startTimeDir1 = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   if ((now - startTimeDir2 &amp;gt; settings.timeActiveDir2) &amp;amp;&amp;amp; (startTimeDir2 != 0)){    // switch both relays to off after milliseconds defined in config (energy saving and safety aspects)&lt;br /&gt;
     #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{Fallback2:%ld:%ld:%ld}&amp;quot;,now,startTimeDir2,settings.timeActiveDir2);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    sendRelaisCommand(2,false);&lt;br /&gt;
    delay(100);&lt;br /&gt;
    sendRelaisCommand(1,false);&lt;br /&gt;
    delay(500);&lt;br /&gt;
    startTimeDir2 = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Software-Beschreibung=&lt;br /&gt;
&lt;br /&gt;
Die Software ist eine Firmware für einen **ESP8266 (ESP-01)**, die über MQTT eine **motorisierte Markise** oder einen ähnlichen Sonnenschutz steuert. &lt;br /&gt;
&lt;br /&gt;
## Hauptfunktion&lt;br /&gt;
&lt;br /&gt;
Der ESP8266 verbindet sich mit WLAN und einem MQTT-Broker und steuert über eine serielle Relaisplatine zwei Relais:&lt;br /&gt;
&lt;br /&gt;
* Relais 1 = Fahrtrichtung (Ausfahren / Einfahren)&lt;br /&gt;
* Relais 2 = Motor EIN/AUS&lt;br /&gt;
&lt;br /&gt;
Die Steuerung erfolgt per MQTT-Befehlen. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## MQTT-Schnittstelle&lt;br /&gt;
&lt;br /&gt;
Basis-Topic:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
home/basement/0_terrace/awning/&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Eingehende Befehle:&lt;br /&gt;
&lt;br /&gt;
| Topic     | Funktion                 |&lt;br /&gt;
| --------- | ------------------------ |&lt;br /&gt;
| trigger   | Zustandswechsel          |&lt;br /&gt;
| direction | Richtung vorgeben        |&lt;br /&gt;
| config    | Laufzeiten konfigurieren |&lt;br /&gt;
&lt;br /&gt;
Ausgehende Statusmeldungen:&lt;br /&gt;
&lt;br /&gt;
| Topic | Inhalt                    |&lt;br /&gt;
| ----- | ------------------------- |&lt;br /&gt;
| state | aktueller Zustand         |&lt;br /&gt;
| raw   | Debug-Ausgaben            |&lt;br /&gt;
| alive | Heartbeat mit Zeitstempel |&lt;br /&gt;
| RSSI  | WLAN-Empfangsstärke       |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Zustandsautomat&lt;br /&gt;
&lt;br /&gt;
Es existieren vier Zustände:&lt;br /&gt;
&lt;br /&gt;
| Zustand | Bedeutung |&lt;br /&gt;
| ------- | --------- |&lt;br /&gt;
| 0       | Stopp     |&lt;br /&gt;
| 1       | Ausfahren |&lt;br /&gt;
| 2       | Stopp     |&lt;br /&gt;
| 3       | Einfahren |&lt;br /&gt;
&lt;br /&gt;
Bei jedem MQTT-Trigger wird zum nächsten Zustand gewechselt.&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
0 → 1 → 2 → 3 → 0&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Dadurch genügt ein einziger Taster oder MQTT-Befehl zum Steuern der Markise. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Richtungssteuerung&lt;br /&gt;
&lt;br /&gt;
Per MQTT können direkte Befehle gesendet werden:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
down&lt;br /&gt;
out&lt;br /&gt;
on&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Markise ausfahren&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
up&lt;br /&gt;
in&lt;br /&gt;
off&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Markise einfahren&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
halt&lt;br /&gt;
stop&lt;br /&gt;
stopp&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
→ Motor stoppen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Sicherheitsschaltung&lt;br /&gt;
&lt;br /&gt;
Für beide Richtungen existiert eine maximale Laufzeit:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
timeActiveDir1&lt;br /&gt;
timeActiveDir2&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Nach Ablauf dieser Zeit werden beide Relais automatisch abgeschaltet.&lt;br /&gt;
&lt;br /&gt;
Standard:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
180 Sekunden&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Damit wird verhindert, dass ein defekter MQTT-Befehl oder ein Softwarefehler den Motor dauerhaft laufen lässt. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## EEPROM-Speicherung&lt;br /&gt;
&lt;br /&gt;
Die Laufzeiten werden dauerhaft gespeichert:&lt;br /&gt;
&lt;br /&gt;
```cpp&lt;br /&gt;
struct EEPROMValues&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Inhalt:&lt;br /&gt;
&lt;br /&gt;
* Kennzeichen (`mark`)&lt;br /&gt;
* Laufzeit Richtung 1&lt;br /&gt;
* Laufzeit Richtung 2&lt;br /&gt;
* CRC-Prüfsumme&lt;br /&gt;
&lt;br /&gt;
Beim Start prüft die Firmware die Daten auf Plausibilität und Integrität. Falls ungültig, werden Standardwerte erzeugt. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Netzwerkfunktionen&lt;br /&gt;
&lt;br /&gt;
Die Firmware:&lt;br /&gt;
&lt;br /&gt;
* verbindet sich mit WLAN&lt;br /&gt;
* verbindet sich mit MQTT&lt;br /&gt;
* synchronisiert die Uhr per NTP&lt;br /&gt;
* sendet alle 10 Sekunden einen Heartbeat&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
2025-03-01T12:34:56+01:00&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
sowie die aktuelle WLAN-Feldstärke. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Hardware&lt;br /&gt;
&lt;br /&gt;
Aus dem Code lässt sich folgende Hardware ableiten:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
ESP8266 (ESP-01)&lt;br /&gt;
        │&lt;br /&gt;
UART&lt;br /&gt;
        │&lt;br /&gt;
2-Kanal Relaiscontroller&lt;br /&gt;
        │&lt;br /&gt;
Markisenmotor&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Die Relaisplatine wird über ein proprietäres 4-Byte-Protokoll gesteuert:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
A0 Relais Zustand Checksumme&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
```text&lt;br /&gt;
A0 01 01 A2&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
= Relais 1 EIN. &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Zusammenfassung&lt;br /&gt;
&lt;br /&gt;
Die Software ist ein **MQTT-gesteuerter Markisencontroller auf Basis eines ESP8266**, der:&lt;br /&gt;
&lt;br /&gt;
* WLAN und MQTT nutzt,&lt;br /&gt;
* eine Markise ein- und ausfährt,&lt;br /&gt;
* Fahrzeiten im EEPROM speichert,&lt;br /&gt;
* automatisch abschaltet,&lt;br /&gt;
* Status- und Debuginformationen veröffentlicht,&lt;br /&gt;
* per Home Assistant, Node-RED oder FHEM steuerbar ist. &lt;br /&gt;
&lt;br /&gt;
Der Aufbau wirkt wie ein typisches Smart-Home-Projekt zur Nachrüstung einer vorhandenen Funk- oder Tastersteuerung einer Terrasse-Markise.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=WLAN_Aktor_Markise&amp;diff=4915</id>
		<title>WLAN Aktor Markise</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=WLAN_Aktor_Markise&amp;diff=4915"/>
		<updated>2026-06-13T15:49:28Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Die Seite wurde neu angelegt: „=Allgemeines= Leider hatte ich keinen Port mehr an den KNX-Rollladenaktoren zur Verfuegung, daher bin ich auf eine MQTT-WLAN-ESP-DuoRelais-Schaltung ausgewischen.  =Hardware=   =Software=  &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;   //ShutterBlindsAwningFunction with LC-Relay-ESP01-2R-5V //tested 2022-08-06  #include &amp;lt;ESP8266WiFi.h&amp;gt;        // Include the Wi-Fi library #include &amp;lt;PubSubClient.h&amp;gt;       // MQTT #include &amp;quot;ESPDateTime.h&amp;quot;        // NTP #include &amp;lt;ESP_EEPRO…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Leider hatte ich keinen Port mehr an den KNX-Rollladenaktoren zur Verfuegung, daher bin ich auf eine MQTT-WLAN-ESP-DuoRelais-Schaltung ausgewischen.&lt;br /&gt;
&lt;br /&gt;
=Hardware=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Software=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
//ShutterBlindsAwningFunction with LC-Relay-ESP01-2R-5V&lt;br /&gt;
//tested 2022-08-06&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;ESP8266WiFi.h&amp;gt;        // Include the Wi-Fi library&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;       // MQTT&lt;br /&gt;
#include &amp;quot;ESPDateTime.h&amp;quot;        // NTP&lt;br /&gt;
#include &amp;lt;ESP_EEPROM.h&amp;gt;         // to store non-volatile variables&lt;br /&gt;
#include &amp;quot;CRC.h&amp;quot;                // to check the reliability of the EEPROM-stored variables&lt;br /&gt;
#include &amp;quot;CRC8.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//CONFIGURATION&lt;br /&gt;
#define WLAN_SSID &amp;quot;XXX&amp;quot;&lt;br /&gt;
#define WLAN_PASSWORD &amp;quot;YYY&amp;quot;&lt;br /&gt;
#define MQTT_SERVER_IP &amp;quot;ZZZ&amp;quot;  // better to use the ip, DNS will slow down the speed&lt;br /&gt;
#define MQTT_MSG_BUFFER_SIZE	50&lt;br /&gt;
#define MQTT_DEBUG_MSG_BUFFER_SIZE 50&lt;br /&gt;
#define MQTTT_BASE_TOPIC &amp;quot;home/basement/0_terrace/awning/&amp;quot;  // my rooms are numbered so there is a consideration to replace the _ with / to use the 0 also for e.g. garden&lt;br /&gt;
#define MQTT_OUT_STATE MQTTT_BASE_TOPIC &amp;quot;state&amp;quot;&lt;br /&gt;
#define MQTT_OUT_RAW MQTTT_BASE_TOPIC &amp;quot;raw&amp;quot;&lt;br /&gt;
#define MQTT_OUT_ALIVE MQTTT_BASE_TOPIC &amp;quot;alive&amp;quot;&lt;br /&gt;
#define MQTT_OUT_RSSI MQTTT_BASE_TOPIC &amp;quot;RSSI&amp;quot;&lt;br /&gt;
#define MQTT_IN_TRIGGER MQTTT_BASE_TOPIC &amp;quot;trigger&amp;quot;&lt;br /&gt;
#define MQTT_IN_DIRECTION MQTTT_BASE_TOPIC &amp;quot;direction&amp;quot;&lt;br /&gt;
#define MQTT_IN_CONFIG MQTTT_BASE_TOPIC &amp;quot;config&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//#define SERIAL_DEBUG          // used in an early stage of development; conflicts with UART output for relais control&lt;br /&gt;
#define MQTT_DEBUG_OUTPUT     // the raw topic serves to output the values for debugging // TODO struct config&lt;br /&gt;
#define ALIVEINTERVAL 10000     // UpdateInterval in milliseconds of the topics &#039;alive&#039; // TODO struct config&lt;br /&gt;
&lt;br /&gt;
//WIFI&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
const char * ssid     = WLAN_SSID;         // The SSID (name) of the Wi-Fi network you want to connect to&lt;br /&gt;
const char * password = WLAN_PASSWORD;     // The password of the Wi-Fi network&lt;br /&gt;
String mac;&lt;br /&gt;
&lt;br /&gt;
//MQTT&lt;br /&gt;
const char * MQTTServer = MQTT_SERVER_IP;&lt;br /&gt;
PubSubClient MQTTClient(espClient); //Constructor&lt;br /&gt;
unsigned long lastMsg = 0;  // to remember the last timestamp of the &#039;ALVIE-publish&#039; (heartbeat)&lt;br /&gt;
char msg[MQTT_MSG_BUFFER_SIZE]; // old style; sorry for mixing c and c++&lt;br /&gt;
char msgConfig[MQTT_DEBUG_MSG_BUFFER_SIZE]; // to store the config parameters; in setup there is no possibility to send out, so store it to send it in loop&lt;br /&gt;
long int value = 0; // don&#039;t needed anymore; its a counter replaced by DateTime&lt;br /&gt;
&lt;br /&gt;
String topicBase = MQTTT_BASE_TOPIC ;&lt;br /&gt;
String inConfigTopic = MQTT_IN_CONFIG ;&lt;br /&gt;
String inTriggerTopic = MQTT_IN_TRIGGER ;&lt;br /&gt;
String inDirectionTopic = MQTT_IN_DIRECTION;&lt;br /&gt;
String outStateTopic = MQTT_OUT_STATE ;&lt;br /&gt;
String outRawTopic = MQTT_OUT_RAW ;&lt;br /&gt;
String outAliveTopic = MQTT_OUT_ALIVE ;&lt;br /&gt;
String outRSSITopic = MQTT_OUT_RSSI ;&lt;br /&gt;
String clientId;&lt;br /&gt;
&lt;br /&gt;
//RELAIS&lt;br /&gt;
void sendRelaisCommand(unsigned int relais,boolean onoff); // forward declaration&lt;br /&gt;
volatile byte state; // 0 STOP (0,X); 1 EXTEND (1,1); 2 STOP (0,X); 3 RETRACT (1,0)&lt;br /&gt;
volatile boolean relaisState[] = {false,false};&lt;br /&gt;
struct EEPROMValues{ // use a struct to store and retrieve to/from EEPROM&lt;br /&gt;
  //const char* mark=&amp;quot;#&amp;quot;; // Before C++11, members of a struct could not be default initialized. Instead they must be initialized after an instance struct is created.&lt;br /&gt;
  //EEPROM.put/get probably have issues with strings&lt;br /&gt;
  byte mark;  //or char mark;&lt;br /&gt;
  unsigned long timeActiveDir1; // Milliseconds active till relaisstate[] falls back to {false,false}&lt;br /&gt;
  unsigned long timeActiveDir2; // Milliseconds active till relaisstate[] false back to {false,false}&lt;br /&gt;
  unsigned long checksum;&lt;br /&gt;
} settings;&lt;br /&gt;
&lt;br /&gt;
//TIMER&lt;br /&gt;
unsigned long startTimeDir1 = 0;  // Starttime if relais1 goes active&lt;br /&gt;
unsigned long startTimeDir2 = 0;  // Starttime if relais1 goes active&lt;br /&gt;
&lt;br /&gt;
//CRC&lt;br /&gt;
CRC8 crc;&lt;br /&gt;
unsigned int eepromAddr = 0;  // address to store the config variables, mark and checksum (struct settings)&lt;br /&gt;
&lt;br /&gt;
//CONFIG&lt;br /&gt;
boolean sendOutOneTime = false; // flag to output the stored config only once after powerup/reset&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//CALLBACK The function is called when a new message arrives. All code to react within this function and/or transfer the parameter to outer variables.&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/trigger&#039; -m &#039;1&#039; // or -m 1 or -m 0&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/config&#039; -m &#039;7000 8000&#039;&lt;br /&gt;
// mosquitto_pub -h 192.168.178.x -t &#039;home/basement/0_terrace/awning/direction&#039; -m &#039;up&#039;&lt;br /&gt;
void callback(char* topic, byte* payload, unsigned int length) { // topic ends with direction or config // payload from -m within apostrophes // lentgh integer number of bytes of payload&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.print(&amp;quot;Message arrived [&amp;quot;);&lt;br /&gt;
    Serial.print(topic);&lt;br /&gt;
    Serial.print(&amp;quot;] &amp;quot;);&lt;br /&gt;
    for (unsigned int i = 0; i &amp;lt; length; i++) {&lt;br /&gt;
      Serial.print((char)payload[i]);  // print out the payload array byte by byte&lt;br /&gt;
    }&lt;br /&gt;
    Serial.println();&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  String payloadStr; // to transfer the byte-array to a String&lt;br /&gt;
  char* p = (char*)malloc(length); // copy the payload to the new buffer; actually not necessary&lt;br /&gt;
  memcpy(p,payload,length); // play safe, it does no harm&lt;br /&gt;
  payloadStr.concat(p,length); // String is better to do things like .toLowerCase();&lt;br /&gt;
&lt;br /&gt;
  // Trigger CONTROL&lt;br /&gt;
  if (strcmp(inTriggerTopic.c_str(),topic) == 0 &amp;amp;&amp;amp; (char)payload[0] == &#039;1&#039;) { //1 for trigger // 0 or other are not handled&lt;br /&gt;
    boolean stateSet = false; // if the state is detected and changed ignore all following state detections&lt;br /&gt;
    // Relais 1 determines the direction&lt;br /&gt;
    // Relais 2 switches on/off respectively move/stop&lt;br /&gt;
    // activating means first switch on relay 1, pause, activate relay 2&lt;br /&gt;
    // deactivating means first switch off relay 2, pause and only then relay 1 if necessary switch&lt;br /&gt;
    // state 0 both relays off (relay 2 then 1) start condition&lt;br /&gt;
    // state 1 relay 1 set relay 1 to move out/down postition, pause, activate relay 2&lt;br /&gt;
    // state 2 both relays off (relay 2 then 1) intermediate condition&lt;br /&gt;
    // state 3 relay 1 set relay 1 to move in/up postition, pause, activate relay 2&lt;br /&gt;
    // recommendation for pausetime is 500 ms&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; !relaisState[1] &amp;amp;&amp;amp; state != 0 &amp;amp;&amp;amp; state !=2 ){ //asynchron!!! correct it&lt;br /&gt;
      state = 0;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; !relaisState[1] &amp;amp;&amp;amp; !stateSet){&lt;br /&gt;
      if(state==0){ // State 0-&amp;gt;1&lt;br /&gt;
        sendRelaisCommand(1,true); // extend&lt;br /&gt;
        delay(100);&lt;br /&gt;
        sendRelaisCommand(2,true); // activate&lt;br /&gt;
        state=1;&lt;br /&gt;
        startTimeDir1 = millis();&lt;br /&gt;
      }&lt;br /&gt;
      if(state==2){ // state 2-&amp;gt;3&lt;br /&gt;
        sendRelaisCommand(1,false); // retract&lt;br /&gt;
        delay(100);&lt;br /&gt;
        sendRelaisCommand(2,true);  // activate&lt;br /&gt;
        state=3;&lt;br /&gt;
        startTimeDir2 = millis();&lt;br /&gt;
      }&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (relaisState[0] &amp;amp;&amp;amp; relaisState[1] &amp;amp;&amp;amp; !stateSet){ // state 1-&amp;gt;2&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false); //at this point the relais could be untouched, in state 1 and 3 it will be set again, to save energy switch both relays to off&lt;br /&gt;
      state=2;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    if (!relaisState[0] &amp;amp;&amp;amp; relaisState[1] &amp;amp;&amp;amp; !stateSet){ // state 3-&amp;gt;0&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false); //at this point the relais could be untouched, in state 1 and 3 it will be set again, to save energy switch both relays to off&lt;br /&gt;
      state=0;&lt;br /&gt;
      stateSet = true;&lt;br /&gt;
    }&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,state);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
     Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
     Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    MQTTClient.publish(outStateTopic.c_str(), msg);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Direction CONTROL&lt;br /&gt;
  if (strcmp(inDirectionTopic.c_str(),topic) == 0) {&lt;br /&gt;
    uint8_t direction=0; // 1 means down(out); 2 means up(in)&lt;br /&gt;
    payloadStr.toLowerCase();&lt;br /&gt;
    //MQTTClient.publish(outRawTopic.c_str(), payloadStr.c_str());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    const char directions1[3][6]={&amp;quot;down&amp;quot;,&amp;quot;out&amp;quot;,&amp;quot;on&amp;quot;}; // to react to all of this commands&lt;br /&gt;
    const char directions2[3][6]={&amp;quot;up&amp;quot;,&amp;quot;in&amp;quot;,&amp;quot;off&amp;quot;};&lt;br /&gt;
    const char directions3[3][6]={&amp;quot;halt&amp;quot;,&amp;quot;stop&amp;quot;,&amp;quot;stopp&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
    uint8_t number; // -Wunused-but-set-variable&lt;br /&gt;
    number=sizeof(directions1)/sizeof(directions1[0]); // calculate the number of array members  // Test!!! can be deleted&lt;br /&gt;
    (void)number; // to suppress the warning&lt;br /&gt;
    #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,number);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
&lt;br /&gt;
    // only one of this tests can be true // after this block direction is 1, 2 or 3 depending on command&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions1)/sizeof(directions1[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions1[i-1])==0)direction=1;&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions2)/sizeof(directions2[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions2[i-1])==0)direction=2;&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t i=1;i&amp;lt;=(sizeof(directions3)/sizeof(directions3[0]));i++){&lt;br /&gt;
      if (strcmp(payloadStr.c_str(),directions3[i-1])==0)direction=3;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    if (direction == 1){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR1&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      sendRelaisCommand(1,true); // move out/down&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(2,true); // activate&lt;br /&gt;
      startTimeDir1 = millis();&lt;br /&gt;
    }&lt;br /&gt;
    if (direction == 2){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR2&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(2,true);&lt;br /&gt;
      startTimeDir2 = millis();&lt;br /&gt;
    }&lt;br /&gt;
    if (direction == 3){&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), &amp;quot;DIR3&amp;quot;);&lt;br /&gt;
      sendRelaisCommand(2,false);&lt;br /&gt;
      delay(100);&lt;br /&gt;
      sendRelaisCommand(1,false);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // config CONTROL&lt;br /&gt;
  if (strcmp(inConfigTopic.c_str(),topic) == 0){&lt;br /&gt;
    unsigned long maxTime[] = {180,180};&lt;br /&gt;
    const char* pL = payloadStr.c_str();  // get pointer to payload string // the payload is the combination of two integer space separated&lt;br /&gt;
    char* end;                            // declare a pointer&lt;br /&gt;
    for (uint8_t i = 1; i &amp;lt;= 2; i++){&lt;br /&gt;
      maxTime[i-1] = strtol(pL,&amp;amp;end,10); // &amp;amp;end is the reference to an object of type char*, whose value is set by the function to the next character in str after the numerical value.&lt;br /&gt;
      if (pL == end) break;&lt;br /&gt;
      pL = end; // the space at the beginning of second number is seemingly not a problem&lt;br /&gt;
    }&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{%ld:%ld}&amp;quot;,maxTime[0],maxTime[1]);&lt;br /&gt;
    MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    if (maxTime[0]&amp;gt;=0 &amp;amp;&amp;amp; maxTime[0]&amp;lt;=1440000 &amp;amp;&amp;amp; maxTime[1]&amp;gt;=0 &amp;amp;&amp;amp; maxTime[1]&amp;lt;=1440000){&lt;br /&gt;
      settings.timeActiveDir1=maxTime[0];&lt;br /&gt;
      settings.timeActiveDir2=maxTime[1];&lt;br /&gt;
      crc.reset();&lt;br /&gt;
      crc.add(settings.timeActiveDir1);&lt;br /&gt;
      crc.add(settings.timeActiveDir2);&lt;br /&gt;
      settings.checksum=crc.getCRC();&lt;br /&gt;
      #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
        snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{EEPROMput:%c:%ld:%ld:%ld}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
        MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
      #endif&lt;br /&gt;
      EEPROM.put(eepromAddr, settings); //write data to array in ram&lt;br /&gt;
      EEPROM.commit();  //write data from ram to flash memory. Do nothing if there are no changes to EEPROM data in ram&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void reconnect() {&lt;br /&gt;
  // Loop until we&#039;re reconnected&lt;br /&gt;
  while (!MQTTClient.connected()) {&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Attempting MQTT connection...&amp;quot;);&lt;br /&gt;
    #endif&lt;br /&gt;
&lt;br /&gt;
    // Attempt to connect&lt;br /&gt;
    if (MQTTClient.connect(mac.c_str()))  {&lt;br /&gt;
      #ifdef SERIAL_DEBUG&lt;br /&gt;
          Serial.println(&amp;quot;connected&amp;quot;);&lt;br /&gt;
      #endif&lt;br /&gt;
      // Once connected, publish an announcement...&lt;br /&gt;
      MQTTClient.publish(topicBase.c_str(), &amp;quot;CON&amp;quot;);&lt;br /&gt;
      // ... and resubscribe&lt;br /&gt;
      MQTTClient.subscribe(inTriggerTopic.c_str());&lt;br /&gt;
      MQTTClient.subscribe(inDirectionTopic.c_str());&lt;br /&gt;
      MQTTClient.subscribe(inConfigTopic.c_str());&lt;br /&gt;
    } else {&lt;br /&gt;
      #ifdef SERIAL_DEBUG&lt;br /&gt;
        Serial.print(&amp;quot;failed, rc=&amp;quot;);&lt;br /&gt;
        Serial.print(MQTTClient.state());&lt;br /&gt;
        Serial.println(&amp;quot; try again in 5 seconds&amp;quot;);&lt;br /&gt;
      #endif&lt;br /&gt;
      // Wait 5 seconds before retrying&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendRelaisCommand(unsigned int relais,boolean onoff){&lt;br /&gt;
  //default for relais 1&lt;br /&gt;
  byte commandOn[4]={160,1,1,162};  // A0 (160 Non-breaking space) command initiate, relay 1/2, on/off, checksum (Addition)&lt;br /&gt;
  byte commandOff[4]={160,1,0,161};&lt;br /&gt;
&lt;br /&gt;
  //change commands for relais 2&lt;br /&gt;
  if (relais==2){&lt;br /&gt;
      commandOn[1]=2;&lt;br /&gt;
      commandOff[1]=2;&lt;br /&gt;
      commandOn[3]=163;&lt;br /&gt;
      commandOff[3]=162;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (onoff){&lt;br /&gt;
      Serial.write(commandOn,4);&lt;br /&gt;
      relaisState[relais-1]=true;&lt;br /&gt;
  }else{&lt;br /&gt;
      Serial.write(commandOff,4);&lt;br /&gt;
      relaisState[relais-1]=false;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
    snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;RELAIS-%d_SET_%s&amp;quot;,relais,onoff?&amp;quot;ON&amp;quot;:&amp;quot;OFF&amp;quot;);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
     Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
     Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    MQTTClient.publish(outRawTopic.c_str(),msg);&lt;br /&gt;
  #endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupDateTime() {&lt;br /&gt;
  // setup this after wifi connected&lt;br /&gt;
  // you can use custom timeZone,server and timeout&lt;br /&gt;
  // DateTime.setTimeZone(-4);&lt;br /&gt;
  //   DateTime.setServer(&amp;quot;asia.pool.ntp.org&amp;quot;);&lt;br /&gt;
  //   DateTime.begin(15 * 1000);&lt;br /&gt;
  DateTime.setServer(&amp;quot;de.pool.ntp.org&amp;quot;);&lt;br /&gt;
  DateTime.setTimeZone(&amp;quot;CEST&amp;quot;);&lt;br /&gt;
  DateTime.begin(); // ( 	const unsigned int  	timeOutMs = DEFAULT_TIMEOUT	)&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    if (!DateTime.isTimeValid()) {&lt;br /&gt;
      Serial.println(&amp;quot;Failed to get time from server.&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.printf(&amp;quot;Date Now is %s\n&amp;quot;, DateTime.toISOString().c_str());&lt;br /&gt;
      Serial.printf(&amp;quot;Timestamp is %lld\n&amp;quot;, DateTime.now());&lt;br /&gt;
    }&lt;br /&gt;
  #endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//SETUP&lt;br /&gt;
void setup() {&lt;br /&gt;
&lt;br /&gt;
   //SERIAL&lt;br /&gt;
  Serial.begin(115200);         // Start the Serial communication to send messages to the computer&lt;br /&gt;
  delay(10);&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.println(&#039;\n&#039;);&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  //WIFI&lt;br /&gt;
  WiFi.mode(WIFI_STA);// ist scheinbar der defaultwert wird in letzter Version nicht benutzt&lt;br /&gt;
  WiFi.begin(ssid, password);             // Connect to the network&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.print(&amp;quot;Connecting to &amp;quot;);&lt;br /&gt;
    Serial.print(ssid); Serial.println(&amp;quot; ...&amp;quot;);&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      int i = 0;&lt;br /&gt;
      Serial.print(++i); Serial.print(&amp;quot;-&amp;gt;&amp;quot;);&lt;br /&gt;
    #endif&lt;br /&gt;
  }&lt;br /&gt;
  mac=WiFi.macAddress();&lt;br /&gt;
  //clientId += String(random(0xffff), HEX);&lt;br /&gt;
  clientId = &amp;quot;ESP01-&amp;quot; + mac;&lt;br /&gt;
&lt;br /&gt;
  randomSeed(micros());&lt;br /&gt;
&lt;br /&gt;
  #ifdef SERIAL_DEBUG&lt;br /&gt;
    Serial.println(&#039;\n&#039;);&lt;br /&gt;
    Serial.println(&amp;quot;Connection established!&amp;quot;);&lt;br /&gt;
    Serial.print(&amp;quot;IP address:\t&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());         // Send the IP address of the ESP8266 to the computer&lt;br /&gt;
  #endif&lt;br /&gt;
&lt;br /&gt;
  //MQTT&lt;br /&gt;
  MQTTClient.setServer(MQTTServer, 1883);&lt;br /&gt;
  MQTTClient.setCallback(callback);&lt;br /&gt;
&lt;br /&gt;
  //DATETIME&lt;br /&gt;
  setupDateTime();&lt;br /&gt;
&lt;br /&gt;
  //TIMESETTING&lt;br /&gt;
  EEPROM.begin(sizeof(settings)); // 2 + 8 + 8 + 8&lt;br /&gt;
  boolean reliable1 = false, reliable2 = false;&lt;br /&gt;
  EEPROM.get(eepromAddr, settings); //read data from array in ram and cast it into struct called settings&lt;br /&gt;
  #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
    // in setup() MQTT isn&#039;t connected so store the values and publish it in loop()&lt;br /&gt;
    snprintf (msgConfig, 50, &amp;quot;{EEPROMget:%c:%ld:%ld:%#lx}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
  #endif&lt;br /&gt;
  //if (strcmp(settings.mark,&amp;quot;#&amp;quot;) == 0){&lt;br /&gt;
  if (settings.mark == 35){&lt;br /&gt;
    crc.reset();&lt;br /&gt;
    crc.add(settings.timeActiveDir1);&lt;br /&gt;
    crc.add(settings.timeActiveDir2);&lt;br /&gt;
    if (crc.getCRC()==settings.checksum) reliable1 = true;&lt;br /&gt;
  }&lt;br /&gt;
  if (settings.timeActiveDir1 &amp;gt;= 50 &amp;amp;&amp;amp;     // values less than 50 ms  and greater than 24 hours are senseless&lt;br /&gt;
      settings.timeActiveDir1 &amp;lt;= 1440000 &amp;amp;&amp;amp;&lt;br /&gt;
      settings.timeActiveDir2 &amp;gt;= 50 &amp;amp;&amp;amp;&lt;br /&gt;
      settings.timeActiveDir2 &amp;lt;= 1440000) reliable2 = true;&lt;br /&gt;
&lt;br /&gt;
if (!(reliable1 &amp;amp;&amp;amp; reliable2)){ // if the Content of EEPROM is undefined, set it&lt;br /&gt;
  settings.mark = 35;&lt;br /&gt;
  settings.timeActiveDir1 = 180 * 1000; // default 3 minutes to switch off&lt;br /&gt;
  settings.timeActiveDir2 = 180 * 1000;&lt;br /&gt;
  crc.reset();&lt;br /&gt;
  crc.add(settings.timeActiveDir1);&lt;br /&gt;
  crc.add(settings.timeActiveDir2);&lt;br /&gt;
  settings.checksum=crc.getCRC();&lt;br /&gt;
  EEPROM.put(eepromAddr, settings); //write data structure to ram&lt;br /&gt;
  EEPROM.commit();  //write data from ram to flash memory. Do nothing if there are no changes to EEPROM data in ram&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
  //RELAIS setup&lt;br /&gt;
  Serial.write(0); // initialize the uContoller that controls the relays&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// LOOP&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
  if (!MQTTClient.connected()) {&lt;br /&gt;
    reconnect();&lt;br /&gt;
  }&lt;br /&gt;
  MQTTClient.loop();&lt;br /&gt;
&lt;br /&gt;
  // send out one time after setup&lt;br /&gt;
  if (!sendOutOneTime) {&lt;br /&gt;
    #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msgConfig);&lt;br /&gt;
      snprintf (msgConfig, 50, &amp;quot;{config:%c:%ld:%ld:%#lx}&amp;quot;,settings.mark,settings.timeActiveDir1,settings.timeActiveDir2,settings.checksum);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msgConfig);&lt;br /&gt;
      sendOutOneTime = true;&lt;br /&gt;
    #endif&lt;br /&gt;
  }&lt;br /&gt;
  unsigned long now = millis();&lt;br /&gt;
&lt;br /&gt;
   if (now - lastMsg &amp;gt; ALIVEINTERVAL) {&lt;br /&gt;
     lastMsg = now;&lt;br /&gt;
     ++value;&lt;br /&gt;
     snprintf (msg, MQTT_MSG_BUFFER_SIZE, &amp;quot;%s&amp;quot;,DateTime.toISOString().c_str());&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
      Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
     MQTTClient.publish(outAliveTopic.c_str(), msg);&lt;br /&gt;
     snprintf (msg, MQTT_MSG_BUFFER_SIZE, &amp;quot;%d&amp;quot;,WiFi.RSSI());&lt;br /&gt;
    #ifdef SERIAL_DEBUG&lt;br /&gt;
      Serial.print(&amp;quot;Publish message: &amp;quot;);&lt;br /&gt;
      Serial.println(msg);&lt;br /&gt;
    #endif&lt;br /&gt;
     MQTTClient.publish(outRSSITopic.c_str(), msg);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   if ((now - startTimeDir1 &amp;gt; settings.timeActiveDir1) &amp;amp;&amp;amp; (startTimeDir1 != 0)){   // switch both relays to off after milliseconds defined in config (energy saving and safety aspects)&lt;br /&gt;
     #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{Fallback1:%ld:%ld:%ld}&amp;quot;,now,startTimeDir1,settings.timeActiveDir1);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    sendRelaisCommand(2,false);&lt;br /&gt;
    delay(100);&lt;br /&gt;
    sendRelaisCommand(1,false);&lt;br /&gt;
    delay(500);&lt;br /&gt;
    startTimeDir1 = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   if ((now - startTimeDir2 &amp;gt; settings.timeActiveDir2) &amp;amp;&amp;amp; (startTimeDir2 != 0)){    // switch both relays to off after milliseconds defined in config (energy saving and safety aspects)&lt;br /&gt;
     #ifdef MQTT_DEBUG_OUTPUT&lt;br /&gt;
      snprintf (msg, MQTT_DEBUG_MSG_BUFFER_SIZE, &amp;quot;{Fallback2:%ld:%ld:%ld}&amp;quot;,now,startTimeDir2,settings.timeActiveDir2);&lt;br /&gt;
      MQTTClient.publish(outRawTopic.c_str(), msg);&lt;br /&gt;
    #endif&lt;br /&gt;
    sendRelaisCommand(2,false);&lt;br /&gt;
    delay(100);&lt;br /&gt;
    sendRelaisCommand(1,false);&lt;br /&gt;
    delay(500);&lt;br /&gt;
    startTimeDir2 = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&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=PasswortSafe&amp;diff=4914</id>
		<title>PasswortSafe</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4914"/>
		<updated>2026-06-13T12:49:14Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* pass (Password Store) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Für mich ist immer noch ein Stueck Papier, evtl. geordnet in einem Rolodex die beste und sicherste Art Passwoerter zu speichern.&lt;br /&gt;
&lt;br /&gt;
Passwoerter die allerdings Zugang zu weniger sensiblen Daten erlauben im einem Stueck Software zu speichern macht das Handling etwas besser.&lt;br /&gt;
&lt;br /&gt;
 Passwortspeicher gehoeren nicht in die Cloud! &lt;br /&gt;
 Wenn ueberhaupt dann entsprechend mit einer Zusatzverschluesselung.&lt;br /&gt;
&lt;br /&gt;
So richtig anfreunden kann ich mich bisher nur mit KeePassXC, Bitwarden  und pass (Password Store). &lt;br /&gt;
&lt;br /&gt;
KDEWallet benutze ich nur wenn es unbedingt sein muss. Das Programm zickt bei mir zu oft rum.&lt;br /&gt;
&lt;br /&gt;
=KeePassXC=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=pass (Password Store)=&lt;br /&gt;
&lt;br /&gt;
 zypper in password-store&lt;br /&gt;
&lt;br /&gt;
Evtl. noch password-store-dmenu, QtPass, plasma-pass, ...&lt;br /&gt;
&lt;br /&gt;
Das pass init eine gpg-id verlangt, erst mal einen Sschluessel erzeugen.&lt;br /&gt;
&lt;br /&gt;
 gpg --full-generate-key&lt;br /&gt;
&lt;br /&gt;
 (9) ECC (sign and encrypt) oder (16) ECC and Kyber (Erlaeuterung siehe unten)&lt;br /&gt;
&lt;br /&gt;
 (1) Curve 25519 *default*&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chris@worker:~/wordlists&amp;gt; gpg --full-generate-key&lt;br /&gt;
gpg (GnuPG) 2.5.5; Copyright (C) 2025 g10 Code GmbH&lt;br /&gt;
This is free software: you are free to change and redistribute it.&lt;br /&gt;
There is NO WARRANTY, to the extent permitted by law.&lt;br /&gt;
&lt;br /&gt;
Please select what kind of key you want:&lt;br /&gt;
   (1) RSA and RSA&lt;br /&gt;
   (2) DSA and Elgamal&lt;br /&gt;
   (3) DSA (sign only)&lt;br /&gt;
   (4) RSA (sign only)&lt;br /&gt;
   (9) ECC (sign and encrypt) *default*&lt;br /&gt;
  (10) ECC (sign only)&lt;br /&gt;
  (14) Existing key from card&lt;br /&gt;
  (16) ECC and Kyber&lt;br /&gt;
Your selection? 9&lt;br /&gt;
Please select which elliptic curve you want:&lt;br /&gt;
   (1) Curve 25519 *default*&lt;br /&gt;
   (4) NIST P-384&lt;br /&gt;
   (6) Brainpool P-256&lt;br /&gt;
Your selection? 1&lt;br /&gt;
Please specify how long the key should be valid.&lt;br /&gt;
         0 = key does not expire&lt;br /&gt;
      &amp;lt;n&amp;gt;  = key expires in n days&lt;br /&gt;
      &amp;lt;n&amp;gt;w = key expires in n weeks&lt;br /&gt;
      &amp;lt;n&amp;gt;m = key expires in n months&lt;br /&gt;
      &amp;lt;n&amp;gt;y = key expires in n years&lt;br /&gt;
Key is valid for? (0) 0&lt;br /&gt;
Key does not expire at all&lt;br /&gt;
Is this correct? (y/N) y&lt;br /&gt;
&lt;br /&gt;
GnuPG needs to construct a user ID to identify your key.&lt;br /&gt;
&lt;br /&gt;
Real name: Chris ................&lt;br /&gt;
Email address: test@clx.de&lt;br /&gt;
Comment: ++++++++&lt;br /&gt;
You selected this USER-ID:&lt;br /&gt;
    &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O&lt;br /&gt;
We need to generate a lot of random bytes. It is a good idea to perform&lt;br /&gt;
some other action (type on the keyboard, move the mouse, utilize the&lt;br /&gt;
disks) during the prime generation; this gives the random number&lt;br /&gt;
generator a better chance to gain enough entropy.&lt;br /&gt;
We need to generate a lot of random bytes. It is a good idea to perform&lt;br /&gt;
some other action (type on the keyboard, move the mouse, utilize the&lt;br /&gt;
disks) during the prime generation; this gives the random number&lt;br /&gt;
generator a better chance to gain enough entropy.&lt;br /&gt;
gpg: directory &#039;/home/chris/.gnupg/openpgp-revocs.d&#039; created&lt;br /&gt;
gpg: revocation certificate stored as &#039;/home/chris/.gnupg/openpgp-revocs.d/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.rev&#039;&lt;br /&gt;
public and secret key created and signed.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigentlich ist pass so simpel, dass es nur fuer Passwoerter taugt, die man schnell in der Konsole/Clipboard parat haben will.&lt;br /&gt;
Als PasswortManager taugt es eher nicht, da man in der Regel URL,Kennung und Passwort in Kombination ablegt.&lt;br /&gt;
&lt;br /&gt;
=Passphrase=&lt;br /&gt;
&lt;br /&gt;
Eine sichere GPG-Passphrase schützt Ihren privaten Schlüssel. Sie sollte mindestens 20 bis 25 Zeichen lang sein, aus mehreren unzusammenhängenden Wörtern (Passphrase) bestehen und keine leicht erratbaren persönlichen Daten enthalten. Nutzen Sie zur Erstellung idealerweise einen Passwortmanager wie KeePassXC oder Bitwarden.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
=Links=&lt;br /&gt;
[Hardware Security Keys]&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4913</id>
		<title>PasswortSafe</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4913"/>
		<updated>2026-06-13T12:20:03Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* pass (Password Store) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Für mich ist immer noch ein Stueck Papier, evtl. geordnet in einem Rolodex die beste und sicherste Art Passwoerter zu speichern.&lt;br /&gt;
&lt;br /&gt;
Passwoerter die allerdings Zugang zu weniger sensiblen Daten erlauben im einem Stueck Software zu speichern macht das Handling etwas besser.&lt;br /&gt;
&lt;br /&gt;
 Passwortspeicher gehoeren nicht in die Cloud! &lt;br /&gt;
 Wenn ueberhaupt dann entsprechend mit einer Zusatzverschluesselung.&lt;br /&gt;
&lt;br /&gt;
So richtig anfreunden kann ich mich bisher nur mit KeePassXC, Bitwarden  und pass (Password Store). &lt;br /&gt;
&lt;br /&gt;
KDEWallet benutze ich nur wenn es unbedingt sein muss. Das Programm zickt bei mir zu oft rum.&lt;br /&gt;
&lt;br /&gt;
=KeePassXC=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=pass (Password Store)=&lt;br /&gt;
&lt;br /&gt;
 zypper in password-store&lt;br /&gt;
&lt;br /&gt;
Evtl. noch password-store-dmenu, QtPass, plasma-pass, ...&lt;br /&gt;
&lt;br /&gt;
Das pass init eine gpg-id verlangt, erst mal einen Sschluessel erzeugen.&lt;br /&gt;
&lt;br /&gt;
 gpg --full-generate-key&lt;br /&gt;
&lt;br /&gt;
 (9) ECC (sign and encrypt) oder (16) ECC and Kyber (Erlaeuterung siehe unten)&lt;br /&gt;
&lt;br /&gt;
 (1) Curve 25519 *default*&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chris@worker:~/wordlists&amp;gt; gpg --full-generate-key&lt;br /&gt;
gpg (GnuPG) 2.5.5; Copyright (C) 2025 g10 Code GmbH&lt;br /&gt;
This is free software: you are free to change and redistribute it.&lt;br /&gt;
There is NO WARRANTY, to the extent permitted by law.&lt;br /&gt;
&lt;br /&gt;
Please select what kind of key you want:&lt;br /&gt;
   (1) RSA and RSA&lt;br /&gt;
   (2) DSA and Elgamal&lt;br /&gt;
   (3) DSA (sign only)&lt;br /&gt;
   (4) RSA (sign only)&lt;br /&gt;
   (9) ECC (sign and encrypt) *default*&lt;br /&gt;
  (10) ECC (sign only)&lt;br /&gt;
  (14) Existing key from card&lt;br /&gt;
  (16) ECC and Kyber&lt;br /&gt;
Your selection? 9&lt;br /&gt;
Please select which elliptic curve you want:&lt;br /&gt;
   (1) Curve 25519 *default*&lt;br /&gt;
   (4) NIST P-384&lt;br /&gt;
   (6) Brainpool P-256&lt;br /&gt;
Your selection? 1&lt;br /&gt;
Please specify how long the key should be valid.&lt;br /&gt;
         0 = key does not expire&lt;br /&gt;
      &amp;lt;n&amp;gt;  = key expires in n days&lt;br /&gt;
      &amp;lt;n&amp;gt;w = key expires in n weeks&lt;br /&gt;
      &amp;lt;n&amp;gt;m = key expires in n months&lt;br /&gt;
      &amp;lt;n&amp;gt;y = key expires in n years&lt;br /&gt;
Key is valid for? (0) 0&lt;br /&gt;
Key does not expire at all&lt;br /&gt;
Is this correct? (y/N) y&lt;br /&gt;
&lt;br /&gt;
GnuPG needs to construct a user ID to identify your key.&lt;br /&gt;
&lt;br /&gt;
Real name: Chris ................&lt;br /&gt;
Email address: test@clx.de&lt;br /&gt;
Comment: ++++++++&lt;br /&gt;
You selected this USER-ID:&lt;br /&gt;
    &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O&lt;br /&gt;
We need to generate a lot of random bytes. It is a good idea to perform&lt;br /&gt;
some other action (type on the keyboard, move the mouse, utilize the&lt;br /&gt;
disks) during the prime generation; this gives the random number&lt;br /&gt;
generator a better chance to gain enough entropy.&lt;br /&gt;
We need to generate a lot of random bytes. It is a good idea to perform&lt;br /&gt;
some other action (type on the keyboard, move the mouse, utilize the&lt;br /&gt;
disks) during the prime generation; this gives the random number&lt;br /&gt;
generator a better chance to gain enough entropy.&lt;br /&gt;
gpg: directory &#039;/home/chris/.gnupg/openpgp-revocs.d&#039; created&lt;br /&gt;
gpg: revocation certificate stored as &#039;/home/chris/.gnupg/openpgp-revocs.d/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.rev&#039;&lt;br /&gt;
public and secret key created and signed.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Passphrase=&lt;br /&gt;
&lt;br /&gt;
Eine sichere GPG-Passphrase schützt Ihren privaten Schlüssel. Sie sollte mindestens 20 bis 25 Zeichen lang sein, aus mehreren unzusammenhängenden Wörtern (Passphrase) bestehen und keine leicht erratbaren persönlichen Daten enthalten. Nutzen Sie zur Erstellung idealerweise einen Passwortmanager wie KeePassXC oder Bitwarden.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
=Links=&lt;br /&gt;
[Hardware Security Keys]&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4912</id>
		<title>PasswortSafe</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4912"/>
		<updated>2026-06-13T12:19:42Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* pass (Password Store) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Für mich ist immer noch ein Stueck Papier, evtl. geordnet in einem Rolodex die beste und sicherste Art Passwoerter zu speichern.&lt;br /&gt;
&lt;br /&gt;
Passwoerter die allerdings Zugang zu weniger sensiblen Daten erlauben im einem Stueck Software zu speichern macht das Handling etwas besser.&lt;br /&gt;
&lt;br /&gt;
 Passwortspeicher gehoeren nicht in die Cloud! &lt;br /&gt;
 Wenn ueberhaupt dann entsprechend mit einer Zusatzverschluesselung.&lt;br /&gt;
&lt;br /&gt;
So richtig anfreunden kann ich mich bisher nur mit KeePassXC, Bitwarden  und pass (Password Store). &lt;br /&gt;
&lt;br /&gt;
KDEWallet benutze ich nur wenn es unbedingt sein muss. Das Programm zickt bei mir zu oft rum.&lt;br /&gt;
&lt;br /&gt;
=KeePassXC=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=pass (Password Store)=&lt;br /&gt;
&lt;br /&gt;
 zypper in password-store&lt;br /&gt;
&lt;br /&gt;
Evtl. noch password-store-dmenu, QtPass, plasma-pass, ...&lt;br /&gt;
&lt;br /&gt;
Das pass init eine gpg-id verlangt, erst mal einen Sschluessel erzeugen.&lt;br /&gt;
&lt;br /&gt;
 gpg --full-generate-key&lt;br /&gt;
&lt;br /&gt;
 (9) ECC (sign and encrypt) oder (16) ECC and Kyber (Erlaeuterung siehe unten)&lt;br /&gt;
&lt;br /&gt;
 (1) Curve 25519 *default*&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chris@worker:~/wordlists&amp;gt; gpg --full-generate-key&lt;br /&gt;
gpg (GnuPG) 2.5.5; Copyright (C) 2025 g10 Code GmbH&lt;br /&gt;
This is free software: you are free to change and redistribute it.&lt;br /&gt;
There is NO WARRANTY, to the extent permitted by law.&lt;br /&gt;
&lt;br /&gt;
Please select what kind of key you want:&lt;br /&gt;
   (1) RSA and RSA&lt;br /&gt;
   (2) DSA and Elgamal&lt;br /&gt;
   (3) DSA (sign only)&lt;br /&gt;
   (4) RSA (sign only)&lt;br /&gt;
   (9) ECC (sign and encrypt) *default*&lt;br /&gt;
  (10) ECC (sign only)&lt;br /&gt;
  (14) Existing key from card&lt;br /&gt;
  (16) ECC and Kyber&lt;br /&gt;
Your selection? 9&lt;br /&gt;
Please select which elliptic curve you want:&lt;br /&gt;
   (1) Curve 25519 *default*&lt;br /&gt;
   (4) NIST P-384&lt;br /&gt;
   (6) Brainpool P-256&lt;br /&gt;
Your selection? 1&lt;br /&gt;
Please specify how long the key should be valid.&lt;br /&gt;
         0 = key does not expire&lt;br /&gt;
      &amp;lt;n&amp;gt;  = key expires in n days&lt;br /&gt;
      &amp;lt;n&amp;gt;w = key expires in n weeks&lt;br /&gt;
      &amp;lt;n&amp;gt;m = key expires in n months&lt;br /&gt;
      &amp;lt;n&amp;gt;y = key expires in n years&lt;br /&gt;
Key is valid for? (0) 0&lt;br /&gt;
Key does not expire at all&lt;br /&gt;
Is this correct? (y/N) y&lt;br /&gt;
&lt;br /&gt;
GnuPG needs to construct a user ID to identify your key.&lt;br /&gt;
&lt;br /&gt;
Real name: Chris ................&lt;br /&gt;
Email address: test@clx.de&lt;br /&gt;
Comment: ++++++++&lt;br /&gt;
You selected this USER-ID:&lt;br /&gt;
    &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O&lt;br /&gt;
We need to generate a lot of random bytes. It is a good idea to perform&lt;br /&gt;
some other action (type on the keyboard, move the mouse, utilize the&lt;br /&gt;
disks) during the prime generation; this gives the random number&lt;br /&gt;
generator a better chance to gain enough entropy.&lt;br /&gt;
We need to generate a lot of random bytes. It is a good idea to perform&lt;br /&gt;
some other action (type on the keyboard, move the mouse, utilize the&lt;br /&gt;
disks) during the prime generation; this gives the random number&lt;br /&gt;
generator a better chance to gain enough entropy.&lt;br /&gt;
gpg: directory &#039;/home/chris/.gnupg/openpgp-revocs.d&#039; created&lt;br /&gt;
gpg: revocation certificate stored as &#039;/home/chris/.gnupg/openpgp-revocs.d/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.rev&#039;&lt;br /&gt;
public and secret key created and signed.&lt;br /&gt;
/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Passphrase=&lt;br /&gt;
&lt;br /&gt;
Eine sichere GPG-Passphrase schützt Ihren privaten Schlüssel. Sie sollte mindestens 20 bis 25 Zeichen lang sein, aus mehreren unzusammenhängenden Wörtern (Passphrase) bestehen und keine leicht erratbaren persönlichen Daten enthalten. Nutzen Sie zur Erstellung idealerweise einen Passwortmanager wie KeePassXC oder Bitwarden.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
=Links=&lt;br /&gt;
[Hardware Security Keys]&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4911</id>
		<title>PasswortSafe</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4911"/>
		<updated>2026-06-13T10:56:59Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Für mich ist immer noch ein Stueck Papier, evtl. geordnet in einem Rolodex die beste und sicherste Art Passwoerter zu speichern.&lt;br /&gt;
&lt;br /&gt;
Passwoerter die allerdings Zugang zu weniger sensiblen Daten erlauben im einem Stueck Software zu speichern macht das Handling etwas besser.&lt;br /&gt;
&lt;br /&gt;
 Passwortspeicher gehoeren nicht in die Cloud! &lt;br /&gt;
 Wenn ueberhaupt dann entsprechend mit einer Zusatzverschluesselung.&lt;br /&gt;
&lt;br /&gt;
So richtig anfreunden kann ich mich bisher nur mit KeePassXC, Bitwarden  und pass (Password Store). &lt;br /&gt;
&lt;br /&gt;
KDEWallet benutze ich nur wenn es unbedingt sein muss. Das Programm zickt bei mir zu oft rum.&lt;br /&gt;
&lt;br /&gt;
=KeePassXC=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=pass (Password Store)=&lt;br /&gt;
&lt;br /&gt;
 zypper in password-store&lt;br /&gt;
&lt;br /&gt;
Evtl. noch password-store-dmenu, QtPass, plasma-pass, ...&lt;br /&gt;
&lt;br /&gt;
Das pass init eine gpg-id verlangt, erst mal einen Sschluessel erzeugen.&lt;br /&gt;
&lt;br /&gt;
 gpg --full-generate-key&lt;br /&gt;
&lt;br /&gt;
 (9) ECC (sign and encrypt) oder (16) ECC and Kyber (Erlaeuterung siehe unten)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Passphrase=&lt;br /&gt;
&lt;br /&gt;
Eine sichere GPG-Passphrase schützt Ihren privaten Schlüssel. Sie sollte mindestens 20 bis 25 Zeichen lang sein, aus mehreren unzusammenhängenden Wörtern (Passphrase) bestehen und keine leicht erratbaren persönlichen Daten enthalten. Nutzen Sie zur Erstellung idealerweise einen Passwortmanager wie KeePassXC oder Bitwarden.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
=Links=&lt;br /&gt;
[Hardware Security Keys]&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4910</id>
		<title>PasswortSafe</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=PasswortSafe&amp;diff=4910"/>
		<updated>2026-06-13T10:56:27Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Die Seite wurde neu angelegt: „=Allgemeines= Für mich ist immer noch ein Stueck Papier, evtl. geordnet in einem Rolodex die beste und sicherste Art Passwoerter zu speichern.  Passwoerter die allerdings Zugang zu weniger sensiblen Daten erlauben im einem Stueck Software zu speichern macht das Handling etwas besser.   Passwortspeicher gehoeren nicht in die Cloud!   Wenn ueberhaupt dann entsprechend mit einer Zusatzverschluesselung.  So richtig anfreunden kann ich mich bisher nur mit Kee…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Für mich ist immer noch ein Stueck Papier, evtl. geordnet in einem Rolodex die beste und sicherste Art Passwoerter zu speichern.&lt;br /&gt;
&lt;br /&gt;
Passwoerter die allerdings Zugang zu weniger sensiblen Daten erlauben im einem Stueck Software zu speichern macht das Handling etwas besser.&lt;br /&gt;
&lt;br /&gt;
 Passwortspeicher gehoeren nicht in die Cloud! &lt;br /&gt;
 Wenn ueberhaupt dann entsprechend mit einer Zusatzverschluesselung.&lt;br /&gt;
&lt;br /&gt;
So richtig anfreunden kann ich mich bisher nur mit KeePassXC, Bitwarden  und pass (Password Store). &lt;br /&gt;
&lt;br /&gt;
KDEWallet benutze ich nur wenn es unbedingt sein muss. Das Programm zickt bei zu oft rum.&lt;br /&gt;
&lt;br /&gt;
=KeePassXC=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=pass (Password Store)=&lt;br /&gt;
&lt;br /&gt;
 zypper in password-store&lt;br /&gt;
&lt;br /&gt;
Evtl. noch password-store-dmenu, QtPass, plasma-pass, ...&lt;br /&gt;
&lt;br /&gt;
Das pass init eine gpg-id verlangt, erst mal einen Sschluessel erzeugen.&lt;br /&gt;
&lt;br /&gt;
 gpg --full-generate-key&lt;br /&gt;
&lt;br /&gt;
 (9) ECC (sign and encrypt) oder (16) ECC and Kyber (Erlaeuterung siehe unten)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Passphrase=&lt;br /&gt;
&lt;br /&gt;
Eine sichere GPG-Passphrase schützt Ihren privaten Schlüssel. Sie sollte mindestens 20 bis 25 Zeichen lang sein, aus mehreren unzusammenhängenden Wörtern (Passphrase) bestehen und keine leicht erratbaren persönlichen Daten enthalten. Nutzen Sie zur Erstellung idealerweise einen Passwortmanager wie KeePassXC oder Bitwarden.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
=Links=&lt;br /&gt;
[Hardware Security Keys]&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Auvisio_GVD510&amp;diff=4909</id>
		<title>Auvisio GVD510</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Auvisio_GVD510&amp;diff=4909"/>
		<updated>2026-06-05T18:16:08Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
Basic Info.&lt;br /&gt;
Trademark&lt;br /&gt;
Hostlord&lt;br /&gt;
Company Introduction: Shenzhen HostLord Technology Development Co., Ltd is a nongovemmental enterprise integrated in research, development, production and distribution since 1999. With out own research and development institution and factory, we&#039;re trying to be an international pioneer lf digital products. At present, our leading products are involving digital camera, PC Camera, Digital Video, MP3 player, digital pen-recorder, removable memory device (flash memory, SM\MMC\SD\CF, removable HD) and etc. &lt;br /&gt;
Specification&lt;br /&gt;
CE, FCC, UL,&lt;br /&gt;
Origin&lt;br /&gt;
China&lt;br /&gt;
Product Description&lt;br /&gt;
Video Glasses (GVD-510)&lt;br /&gt;
Product Profile:&lt;br /&gt;
With big screen and high resolution display&lt;br /&gt;
GVD Cyberman(TM) with 40&amp;quot; screen and 920K pixels resolution offers you clear and exquisite pictures, which makes your watching seem true.&lt;br /&gt;
3D stereoscopic viewing function:&lt;br /&gt;
GVD supports VGA signal switching formate, compatible for hot Direct 3D games and Open GL language 3D games, which makes game players enjoy an excellent viewing environment. Furthermore, if working with other stereo-driving software, you can also enjoy 3D movies, pictures or any others.&lt;br /&gt;
Lots of signal input mode:&lt;br /&gt;
GVD are available for VGA/ S-Video/ Video and many other input mode, which can be connected to Playstation, xBox console, DVD player, PCS and etc. It meets your different requirements.&lt;br /&gt;
Elaborate design and portable structure&lt;br /&gt;
Elaborate design, light and portable structure of GVD inherit from GVD, which enables enjoying entertainment and work in whatever pose.&lt;br /&gt;
&lt;br /&gt;
 Product Description&lt;br /&gt;
Video Glasses (GVD-510)3D Video/ DVD player video player/ portable video player-40&amp;quot; 2m Away Video Glasses&lt;br /&gt;
Specifications:&lt;br /&gt;
&lt;br /&gt;
Product Features:&lt;br /&gt;
1. Display size: 40&amp;quot;, 2m away&lt;br /&gt;
2. Pixels: 640H*480V*RGB(922K)&lt;br /&gt;
3. Angle of view: 28 degrees&lt;br /&gt;
4. Video input: NTSC/PAL Composite video&lt;br /&gt;
5. Sound function: double channel stereo&lt;br /&gt;
6. Adjustment function: variable volume, picture contrast and brightness&lt;br /&gt;
7. AC power supply: 100V-220V/50Hz (input through adapter)&lt;br /&gt;
8. Power input: 9V DC&lt;br /&gt;
9. Basic accessories: AC adapter, eye patches, cables&lt;br /&gt;
10. Optional accessories: LP1100 lithium battery, PC adapting cable, wireless devices.&lt;br /&gt;
11. Power Consumption: 1. 5W&lt;br /&gt;
12. Packing size: 285 x 200 x 85mm&lt;br /&gt;
&lt;br /&gt;
Product Profile:&lt;br /&gt;
With big screen and high resolution display&lt;br /&gt;
GVD510 Cyberman™ with 40&amp;quot; screen and 920K pixels resolution offers you clear and exquisite pictures, which makes your watching seem true.&lt;br /&gt;
Two versions for your option&lt;br /&gt;
GVD510 has two versions AV and VGA for buyer&#039;s options according to different needs, which make GVD510 meet plenty of user groups. If you need to use it with AV player, choose AV version, on the contrary choose VGA version for computer usage.&lt;br /&gt;
Elaborate design and portable structure&lt;br /&gt;
Elaborate design, light and portable structure of GVD510 inherit from GVD310 and GVD410, which enables enjoying entertainment and work in whatever pose.&lt;br /&gt;
Hi-capacity lithium battery for permanent power supply and convenient usage&lt;br /&gt;
The lithium battery LP1100 can supply power for 5 consecutive hours, making the user more convenient while traveling. &lt;br /&gt;
&lt;br /&gt;
Requirements for 3D stereoscopic function:&lt;br /&gt;
A) PC and related compatible system.&lt;br /&gt;
B) Must be operated under Windows XP system.&lt;br /&gt;
C) Must be using nVidia series 3D picture speeding-up graphic card.&lt;br /&gt;
D) ForceWare display driver and 3D stereoscopic driving.&lt;br /&gt;
&lt;br /&gt;
Kopin Head-Mounted 3D Display&lt;br /&gt;
	 &lt;br /&gt;
Kopin, the largest US manufacturer of microdisplays for mobile consumer and military electronics, has unveiled the CyberMan GVD510-3D stereoscopic head-mounted display (HMD).&lt;br /&gt;
&lt;br /&gt;
The GVD510-3D enables gamers and movie buffs to experience immersive stereo 3D at an affordable price. The PC-compatible headset can be used with over a thousand games and is now available to consumers worldwide for $699 (£399).&lt;br /&gt;
&lt;br /&gt;
The CyberMan GVD510-3D is built on the ‘Page Flipped 3D Stereo VGA’ signal format which was developed for the LCD shutter glass system. The headset uses a signal format that is available on nVidia-based graphics cards.&lt;br /&gt;
&lt;br /&gt;
In addition, the product has a automatic scaling function that makes it compatible with various VGA display output formats and it can also accept composite video and S-Video, which can be driven by the Microsoft’s Xbox, including the new Xbox 360, and Sony’s PlayStation 2 videogames console.&lt;br /&gt;
&lt;br /&gt;
The coolest feature of the GVD510 Cyberman HMD is that an image should look exactly like a 40-inch screen from 6-feet away. By utilising two high-resolution (920K pixels) LCD displays, the head mounted display promises a clear and detailed images, which should help to make the viewing experience realistic.&lt;br /&gt;
&lt;br /&gt;
The GVD510 Cyberman HMD is available in two different versions, AV and VGA, which are specifically configured to perform optimally in each application. If you need to use the head mounted display with an AV player, choose the AV version. If you want to use it with a computer choose the VGA version. Each head-mounted display possess the same design.&lt;br /&gt;
&lt;br /&gt;
An optional high-capacity Lithium battery and charger are also provided, and the battery should last around 5 hours - plenty of time to get through a whole Lord Of The Rings movie!&lt;br /&gt;
&lt;br /&gt;
Technical specifications of the headset include a resolution of 640x480 pixels, 28-degree viewing angles, NTSC and PAL composite video inputs, 2-channel stereo sound (shame there’s no surround sound), and adjustment functions (variable volume, picture contrast and brightness). More details can be found here.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Links=&lt;br /&gt;
[https://www.youtube.com/watch?v=rLVTHe9Z-ts]&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Auvisio_GVD510&amp;diff=4908</id>
		<title>Auvisio GVD510</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Auvisio_GVD510&amp;diff=4908"/>
		<updated>2026-06-05T17:59:25Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
Basic Info.&lt;br /&gt;
Trademark&lt;br /&gt;
Hostlord&lt;br /&gt;
Company Introduction: Shenzhen HostLord Technology Development Co., Ltd is a nongovemmental enterprise integrated in research, development, production and distribution since 1999. With out own research and development institution and factory, we&#039;re trying to be an international pioneer lf digital products. At present, our leading products are involving digital camera, PC Camera, Digital Video, MP3 player, digital pen-recorder, removable memory device (flash memory, SM\MMC\SD\CF, removable HD) and etc. &lt;br /&gt;
Specification&lt;br /&gt;
CE, FCC, UL,&lt;br /&gt;
Origin&lt;br /&gt;
China&lt;br /&gt;
Product Description&lt;br /&gt;
Video Glasses (GVD-510)&lt;br /&gt;
Product Profile:&lt;br /&gt;
With big screen and high resolution display&lt;br /&gt;
GVD Cyberman(TM) with 40&amp;quot; screen and 920K pixels resolution offers you clear and exquisite pictures, which makes your watching seem true.&lt;br /&gt;
3D stereoscopic viewing function:&lt;br /&gt;
GVD supports VGA signal switching formate, compatible for hot Direct 3D games and Open GL language 3D games, which makes game players enjoy an excellent viewing environment. Furthermore, if working with other stereo-driving software, you can also enjoy 3D movies, pictures or any others.&lt;br /&gt;
Lots of signal input mode:&lt;br /&gt;
GVD are available for VGA/ S-Video/ Video and many other input mode, which can be connected to Playstation, xBox console, DVD player, PCS and etc. It meets your different requirements.&lt;br /&gt;
Elaborate design and portable structure&lt;br /&gt;
Elaborate design, light and portable structure of GVD inherit from GVD, which enables enjoying entertainment and work in whatever pose.&lt;br /&gt;
&lt;br /&gt;
 Product Description&lt;br /&gt;
Video Glasses (GVD-510)3D Video/ DVD player video player/ portable video player-40&amp;quot; 2m Away Video Glasses&lt;br /&gt;
Specifications:&lt;br /&gt;
&lt;br /&gt;
Product Features:&lt;br /&gt;
1. Display size: 40&amp;quot;, 2m away&lt;br /&gt;
2. Pixels: 640H*480V*RGB(922K)&lt;br /&gt;
3. Angle of view: 28 degrees&lt;br /&gt;
4. Video input: NTSC/PAL Composite video&lt;br /&gt;
5. Sound function: double channel stereo&lt;br /&gt;
6. Adjustment function: variable volume, picture contrast and brightness&lt;br /&gt;
7. AC power supply: 100V-220V/50Hz (input through adapter)&lt;br /&gt;
8. Power input: 9V DC&lt;br /&gt;
9. Basic accessories: AC adapter, eye patches, cables&lt;br /&gt;
10. Optional accessories: LP1100 lithium battery, PC adapting cable, wireless devices.&lt;br /&gt;
11. Power Consumption: 1. 5W&lt;br /&gt;
12. Packing size: 285 x 200 x 85mm&lt;br /&gt;
&lt;br /&gt;
Product Profile:&lt;br /&gt;
With big screen and high resolution display&lt;br /&gt;
GVD510 Cyberman™ with 40&amp;quot; screen and 920K pixels resolution offers you clear and exquisite pictures, which makes your watching seem true.&lt;br /&gt;
Two versions for your option&lt;br /&gt;
GVD510 has two versions AV and VGA for buyer&#039;s options according to different needs, which make GVD510 meet plenty of user groups. If you need to use it with AV player, choose AV version, on the contrary choose VGA version for computer usage.&lt;br /&gt;
Elaborate design and portable structure&lt;br /&gt;
Elaborate design, light and portable structure of GVD510 inherit from GVD310 and GVD410, which enables enjoying entertainment and work in whatever pose.&lt;br /&gt;
Hi-capacity lithium battery for permanent power supply and convenient usage&lt;br /&gt;
The lithium battery LP1100 can supply power for 5 consecutive hours, making the user more convenient while traveling. &lt;br /&gt;
&lt;br /&gt;
Requirements for 3D stereoscopic function:&lt;br /&gt;
A) PC and related compatible system.&lt;br /&gt;
B) Must be operated under Windows XP system.&lt;br /&gt;
C) Must be using nVidia series 3D picture speeding-up graphic card.&lt;br /&gt;
D) ForceWare display driver and 3D stereoscopic driving.&lt;br /&gt;
&lt;br /&gt;
Kopin Head-Mounted 3D Display&lt;br /&gt;
	 &lt;br /&gt;
Kopin, the largest US manufacturer of microdisplays for mobile consumer and military electronics, has unveiled the CyberMan GVD510-3D stereoscopic head-mounted display (HMD).&lt;br /&gt;
&lt;br /&gt;
The GVD510-3D enables gamers and movie buffs to experience immersive stereo 3D at an affordable price. The PC-compatible headset can be used with over a thousand games and is now available to consumers worldwide for $699 (£399).&lt;br /&gt;
&lt;br /&gt;
The CyberMan GVD510-3D is built on the ‘Page Flipped 3D Stereo VGA’ signal format which was developed for the LCD shutter glass system. The headset uses a signal format that is available on nVidia-based graphics cards.&lt;br /&gt;
&lt;br /&gt;
In addition, the product has a automatic scaling function that makes it compatible with various VGA display output formats and it can also accept composite video and S-Video, which can be driven by the Microsoft’s Xbox, including the new Xbox 360, and Sony’s PlayStation 2 videogames console.&lt;br /&gt;
&lt;br /&gt;
The coolest feature of the GVD510 Cyberman HMD is that an image should look exactly like a 40-inch screen from 6-feet away. By utilising two high-resolution (920K pixels) LCD displays, the head mounted display promises a clear and detailed images, which should help to make the viewing experience realistic.&lt;br /&gt;
&lt;br /&gt;
The GVD510 Cyberman HMD is available in two different versions, AV and VGA, which are specifically configured to perform optimally in each application. If you need to use the head mounted display with an AV player, choose the AV version. If you want to use it with a computer choose the VGA version. Each head-mounted display possess the same design.&lt;br /&gt;
&lt;br /&gt;
An optional high-capacity Lithium battery and charger are also provided, and the battery should last around 5 hours - plenty of time to get through a whole Lord Of The Rings movie!&lt;br /&gt;
&lt;br /&gt;
Technical specifications of the headset include a resolution of 640x480 pixels, 28-degree viewing angles, NTSC and PAL composite video inputs, 2-channel stereo sound (shame there’s no surround sound), and adjustment functions (variable volume, picture contrast and brightness). More details can be found here.&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=Auvisio_GVD510&amp;diff=4907</id>
		<title>Auvisio GVD510</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Auvisio_GVD510&amp;diff=4907"/>
		<updated>2026-06-05T17:48:16Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
Basic Info.&lt;br /&gt;
Trademark&lt;br /&gt;
Hostlord&lt;br /&gt;
Specification&lt;br /&gt;
CE, FCC, UL,&lt;br /&gt;
Origin&lt;br /&gt;
China&lt;br /&gt;
Product Description&lt;br /&gt;
Video Glasses (GVD-510)&lt;br /&gt;
Product Profile:&lt;br /&gt;
With big screen and high resolution display&lt;br /&gt;
GVD Cyberman(TM) with 40&amp;quot; screen and 920K pixels resolution offers you clear and exquisite pictures, which makes your watching seem true.&lt;br /&gt;
3D stereoscopic viewing function:&lt;br /&gt;
GVD supports VGA signal switching formate, compatible for hot Direct 3D games and Open GL language 3D games, which makes game players enjoy an excellent viewing environment. Furthermore, if working with other stereo-driving software, you can also enjoy 3D movies, pictures or any others.&lt;br /&gt;
Lots of signal input mode:&lt;br /&gt;
GVD are available for VGA/ S-Video/ Video and many other input mode, which can be connected to Playstation, xBox console, DVD player, PCS and etc. It meets your different requirements.&lt;br /&gt;
Elaborate design and portable structure&lt;br /&gt;
Elaborate design, light and portable structure of GVD inherit from GVD, which enables enjoying entertainment and work in whatever pose.&lt;br /&gt;
&lt;br /&gt;
Requirements for 3D stereoscopic function:&lt;br /&gt;
A) PC and related compatible system.&lt;br /&gt;
B) Must be operated under Windows XP system.&lt;br /&gt;
C) Must be using nVidia series 3D picture speeding-up graphic card.&lt;br /&gt;
D) ForceWare display drive and 3D stereoscopic driving.&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=Auvisio_GVD510&amp;diff=4906</id>
		<title>Auvisio GVD510</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Auvisio_GVD510&amp;diff=4906"/>
		<updated>2026-06-05T16:48:51Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Die Seite wurde neu angelegt: „ Basic Info. Trademark Hostlord Specification CE, FCC, UL, Origin China Product Description Video Glasses (GVD-510) Product Profile: With big screen and high resolution display GVD Cyberma with 40&amp;quot; screen and 920K pixels resolution offers you clear and exquisite pictures, which makes your watching seem true. 3D stereoscopic viewing funtion: GVD supports VGA signal switching formate, compatible for hot Direct 3D games and Open GL language 3D games, which m…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Basic Info.&lt;br /&gt;
Trademark&lt;br /&gt;
Hostlord&lt;br /&gt;
Specification&lt;br /&gt;
CE, FCC, UL,&lt;br /&gt;
Origin&lt;br /&gt;
China&lt;br /&gt;
Product Description&lt;br /&gt;
Video Glasses (GVD-510)&lt;br /&gt;
Product Profile:&lt;br /&gt;
With big screen and high resolution display&lt;br /&gt;
GVD Cyberma with 40&amp;quot; screen and 920K pixels resolution offers you clear and exquisite pictures, which makes your watching seem true.&lt;br /&gt;
3D stereoscopic viewing funtion:&lt;br /&gt;
GVD supports VGA signal switching formate, compatible for hot Direct 3D games and Open GL language 3D games, which makes game players enjoy an excellent viewing environment. Furthermore, if working with other stereo-driving software, you can also enjoy 3D movies, pictures or any others.&lt;br /&gt;
Lots of signal input mode:&lt;br /&gt;
GVD are available for VGA/ S-Video/ Video and many other input mode, which can be connected to Playstation, xBox console, DVD player, PCS and etc. It meets your different requirements.&lt;br /&gt;
Elaborate design and portable structure&lt;br /&gt;
Elaborate design, light and portable structure of GVD inherit from GVD, which enables enjoying entertainment and work in whatever pose.&lt;br /&gt;
&lt;br /&gt;
Requirements for 3D stereoscopic function:&lt;br /&gt;
A) PC and related compatible system.&lt;br /&gt;
B) Must be operated under Windows XP system.&lt;br /&gt;
C) Must be using nVidia series 3D picture speeding-up graphic card.&lt;br /&gt;
D) ForceWare display drive and 3D stereoscopic driving.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Reolink&amp;diff=4905</id>
		<title>Reolink</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Reolink&amp;diff=4905"/>
		<updated>2026-05-20T11:21:05Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* RLC-810A */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Kameras=&lt;br /&gt;
&lt;br /&gt;
==RLC-810A==&lt;br /&gt;
* Build No.build 23062505&lt;br /&gt;
* Hardware No.IPC_56064M8MP&lt;br /&gt;
* Config Versionv3.1.0.0&lt;br /&gt;
* Firmware Versionv3.1.0.2368_23062505&lt;br /&gt;
* DetailsIPC_56064M8MPS18E1W03400000008&lt;br /&gt;
&lt;br /&gt;
Die von den KIs vorgeschlagene Streaming-URLs&lt;br /&gt;
&lt;br /&gt;
 Main Stream&lt;br /&gt;
 rtsp://admin:password@ip_address:554/h264Preview_01_main&lt;br /&gt;
&lt;br /&gt;
 Sub Stream&lt;br /&gt;
 rtsp://admin:password@ip_address:554/h264Preview_01_sub&lt;br /&gt;
&lt;br /&gt;
funktionieren bei mir.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
mediamtx&lt;br /&gt;
&lt;br /&gt;
rtsp://192.168.178.10:8889/cam&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Paketmanager_zypper&amp;diff=4904</id>
		<title>Paketmanager zypper</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Paketmanager_zypper&amp;diff=4904"/>
		<updated>2026-05-17T17:39:03Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Sammlung meiner oft benutzten zypper-Kommandos&lt;br /&gt;
&lt;br /&gt;
 zypper lr -d # list repos with details man sieht so auch die Pfade&lt;br /&gt;
&lt;br /&gt;
 zypper -v install --force &amp;lt;package&amp;gt; # ueberinstallieren&lt;br /&gt;
&lt;br /&gt;
 zypper search-packages &amp;lt;package&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 zypper mr --enable &amp;lt;#&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=Nebenbeibemerkt=&lt;br /&gt;
&lt;br /&gt;
 [https://www.cyberciti.biz/faq/how-to-convert-man-page-to-html-on-linux-and-unix/] &lt;br /&gt;
 [https://pandoc.org/MANUAL.html pandoc]&lt;br /&gt;
 [https://software.opensuse.org/package/hxtools?locale=cs hxtools (man2html)]&lt;br /&gt;
&lt;br /&gt;
 man zypper|man2html|pandoc -f html -t mediawiki -o zypper.wiki&lt;br /&gt;
&lt;br /&gt;
==pandoc==&lt;br /&gt;
&lt;br /&gt;
 -s standalone mit html, head, body, usw.&lt;br /&gt;
 -o output filename&lt;br /&gt;
 -f from format&lt;br /&gt;
 -t to format&lt;br /&gt;
 -r read veraltet, durch -f ersetzt&lt;br /&gt;
 -w write veraltet, durch -t ersetzt&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
=Manpage=&lt;br /&gt;
&lt;br /&gt;
ZYPPER(8) ZYPPER ZYPPER(8)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
NAME zypper - Command-line interface to ZYpp system management library (libzypp)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
SYNOPSIS zypper [--global-opts] command [--command-opts] [command-arguments]&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
zypper subcommand [--command-opts] [command-arguments]&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
zypper help command&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
DESCRIPTION zypper is a command-line interface to ZYpp system management library (libzypp). It can be used to install, update, remove software, manage repositories, perform various queries, and more.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
CONCEPTS Most of the following concepts are common for all applications based on the libzypp package management library, but there are some zypper specifics.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
System Packages The set of installed packages on a system is sometimes denoted as repository @System or System Packages. In contrast to available repositories providing packages which can be installed, @System provides packages which can only be deleted. Installed packages which are not provided by at least one of the available repositories (neither in different version or as renamed package) are often denoted as being unwanted, orphaned or dropped.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note: If you install packages which are not shipped within a repository - manually built or downloaded ones -, collect them within a local directory and add this directory as a plaindir repository to your system. This way the packages will not be seen as being orphaned and are protected during a dist-upgrade.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Creating such a plaindir repo is quite easy: $ mkdir /LocalRepo $ zypper addrepo --refresh /LocalRepo LocalRepo&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If you need to make sure the package to install is taken from LocalRepo, simply use a REPO:PACKAGE style argument: $ cp example.rpm /LocalRepo $ zypper install LocalRepo:example&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Depending on the kind of packages you keep in a local repo you may want to adjust the way GPG signature checks are performed. See section GPG checks for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Repositories Libzypp works with repository metadata, this is information about packages and their relations extracted from RPM packages and other data like patch information, pattern definitions, etc. These data are stored together with the RPM files in folders called repositories. Repositories can be placed on various media like an HTTP or FTP server, DVD, or a folder on a local disc.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
There is a special set of commands in zypper intended to manipulate repositories. Also many commands and options take a repository as an argument. See section COMMANDS, subsection Repository Management for more details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
GPG checks Disabling GPG checks is not recommended. Signing data enables the recipient to verify that no modifications occurred after the data were signed. Accepting data with no, wrong or unknown signature can lead to a corrupted system and in extreme cases even to a system compromise.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Zypp verifies the authenticity of repository metadata by checking their GPG signature. If the repository metadata are signed with a trusted key and successfully verified, packages from this repository are accepted for installation if they match the checksum provided in the metadata. Using unsigned repositories needs to be confirmed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If the repository metadata are not signed, the GPG signature of each downloaded rpm package is checked before accepting it for installation. Packages from unsigned repositories need a valid GPG signature. Using unsigned packages needs to be confirmed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The above is the default behavior defined by settings in /etc/zypp/zypp.conf.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The addrepo and modifyrepo commands provide further options to tune the behavior per repository. It is for example possible to relax the need to confirm installing unsigned packages for a specific repository. But if you do so, you should be very certain that an attacker can hardly modify the package data within the repository or on the way to your machine. See section COMMANDS for details about the command options.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Resource Identifiers (URI) To specify locations of repositories or other resources (RPM files, .repo files) you can use any type of URI supported by libzypp. In addition Zypper accepts a special URI identifying openSUSE Build Service (OBS) repositories in the addrepo command. These URIs have the form of obs://project/[platform].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
See section COMMANDS, subsection Repository Management for a complete list and examples of supported URI formats.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Refresh Refreshing a repository means downloading metadata of packages from the medium (if needed), storing it in local cache (typically under /var/cache/zypp/raw/alias directory) and preparsing the metadata into .solv files (building the solv cache), typically under /var/cache/zypp/solv/alias.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The metadata get refreshed either automatically or on user request. An automatic refresh takes place right before reading metadata from the database if the auto-refresh is enabled for the repository and the metadata is reported to be out of date. If the auto-refresh is disabled, the repository will only be refreshed on user request. You can request a refresh by calling zypper refresh (see the documentation of the refresh command for details).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The repository metadata are checked for changes before actually doing the refresh. A change is detected by downloading one or two metadata index files (small files) and comparing the checksums of the cached ones and the remote ones. If the files differ, the repository is out of date and will be refreshed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To delay the up-to-date check (and thus the automatic refresh) for a certain number of minutes, edit the value of the repo.refresh.delay attribute of ZYpp config file (/etc/zypp/zypp.conf). This means, zypper will not even try to download and check the index files, and you will be able to use zypper for operations like search or info without internet access or root privileges.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
HINT: zypper -vv ref will show the mirrors used to download the metadata.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Services Services are one level above repositories and serve to manage repositories or to do some special tasks. Libzypp currently supports Repository Index Service (RIS) and Plugin Service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Repository Index Service (RIS) is a special type of repository which contains a list of other repositories. This list can be generated dynamically by the server according to some URI parameters or user name, or can be static. Once such service is added to your system, zypper takes care of adding, modifying, or removing these repositories on your system to reflect the current list. See section Service Management and https://en.opensuse.org/openSUSE:Standards_Repository_Index_Service for more details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Package Types Zypper works with several types of resource objects, called resolvables. A resolvable might be a package, patch, pattern, product; basically any kind of object with dependencies to other objects.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
package An ordinary RPM package.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
patch A released patch conflicts with the affected/vulnerable versions of a collection of packages. As long as any of these affected/vulnerable versions are installed, the conflict triggers and the patch is classified as needed, or as unwanted if the patch is locked.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Selecting the patch, the conflict is resolved by updating all installed and affected/vulnerable packages to a version providing the fix. When updating the packages zypper always aims for the latest available version. Resolved patches are classified as either applied or not needed, depending on whether they refer to actually installed packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
So installation, update or removal of packages may change the classification of patches referring to these packages. Since libyzpp-17.23.0 the /var/log/zypp/history remembers if a committed transaction changes a patchs classification. If history data are available, patch tables show a column telling since when the patch is in it’s current state.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Depending on the kind of defect, patches are classified by category and severity. Commonly used values for category are security, (recommended, bugfix), (optional, feature, enhancement) document or yast. Commonly used values for severity are critical, important, moderate, low or unspecified. Names listed in parentheses are used synonymously.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that the patch command does not apply optional patches (category optional or feature) by default. If you actually want to consider all optional patches as being needed, say patch --with-optional. Specific patches can be applied using the install command (e.g. zypper install patch:openSUSE-2014-7).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If the issuer decides to retract a released patch, the patch status will be shown as retracted. The packages provided by the retracted patch are still visible but also tagged as having been retracted (R). The resolver will avoid selecting retracted packages automatically. If you are sure that a retracted package should be installed on your system, you must explicitly select it.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
pattern A group of packages required or recommended to install some functionality.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
product A group of packages which are necessary to install a product.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
srcpackage Source code package (.src.rpm). This type works in search and install commands.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
application Legacy: Since libzypp-17.7.0 this type is no longer available.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Throughout this manual we will often refer to resolvables simply as packages and to resolvable types as package type or kind. These type names can be used as arguments of --type option in several commands like install, info, or search. Commands should also allow one to specify resolvables as KIND:NAME (e.g. patch:openSUSE-2014-7).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Package Dependencies Software packages depend on each other in various ways. Packages usually require or recommend other packages, but they can also conflict with them. Packages may support specific hardware or language settings. Zypper uses a dependency solver to find out which packages need to be installed to satisfy the user’s request.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If you do not request a specific version of a package the solver will pick a reasonable one. The solvers general attitude when resolving a job is to focus on installing the best version of the requested package and to add or update dependencies as they are needed. Aside from this Focus on Job, which is the default, two other focus modes are available:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In Focus on Installed mode the solver focuses on applying as little changes to the installed packages as needed. Choosing an older version of a requested package is valid if it’s dependencies require less changes to the system. The solver will try to avoid updating already installed packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In Focus on Update mode the solver focuses on updating the requested package and all its dependencies as much as possible. Beware, installing a single package in this mode may easily lead to a mini system update.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
For a single command the focus mode can be set using the --solver-focus MODE switch. Valid modes are Job, Installed or Update. If you want to change the default mode for your system, set [/etc/zypp/zypp.conf:solver.focus] to the desired value.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Automatically installed packages Packages added by the dependency solver in order to resolve a user’s request are remembered as having been automatically installed. They may later be removed, if no more user installed packages depend on them (e.g. by zypper remove --clean-deps).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In the Status column the search command distinguishes between user installed packages (i+) and automatically installed packages (i).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Package File Conflicts File conflicts happen when two packages attempt to install files with the same name but different contents. This may happen if you are installing a newer version of a package without erasing the older version, of if two unrelated packages each install a file with the same name.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
As checking for file conflicts requires access to the full filelist of each package being installed, zypper will be able to check for file conflicts only if all packages are downloaded in advance (see --download-in-advance). If you are doing a --dry-run no packages are downloaded, so the file conflict check will skip packages not available in the packages cache. To get a meaningful file conflict check use --dry-run together with --download-only.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
As the reason for file conflicts usually is a poor package design or lack of coordination between the people building the packages, they are not easy to resolve. By using the --replacefiles option you can force zypper to replace the conflicting files. Nevertheless this may damage the package whose file gets replaced.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
COMMANDS zypper provides a number of commands. Each command accepts the options listed in the GLOBAL OPTIONS section. These options must be specified before the command name. In addition, many commands have specific options, which are listed in this section. These command-specific options must be specified after the name of the command and before any of the command arguments.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Zypper also provides limited support for writing extensions/subcommands in any language. See section SUBCOMMANDS for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
General Commands help [command] Shows help texts. If invoked without any argument (just zypper or zypper help), zypper displays global help text which lists all available global options and commands.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If invoked with a command name argument, zypper displays help for the specified command, if such command exists. Long as well as short variants of the command names can be used.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
For your convenience, zypper help can also be invoked in any of the following ways:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper -h|--help [command]&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper [command] -h|--help&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
shell (sh) Starts a shell for entering multiple commands in one session. Exit the shell using exit, quit, or Ctrl-D.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The shell support is not complete so expect bugs there. However, there’s no urgent need to use the shell since libzypp became so fast thanks to the SAT solver and its tools (openSUSE 11.0), but still, you’re welcome to experiment with it.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Package Management Commands info (if) [options] name[-version[-release]]... Show detailed information for specified packages. By default, only packages that exactly match the provided names are shown. To include packages that partially match, use the --match-substrings option or wildcards (* or ?) within the name.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If no version constraint is specified, information about the best available package is shown. Note that both the version and release numbers must always match exactly.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If a query returns no results, zypper returns ZYPPER_EXIT_INF_CAP_NOT_FOUND unless the global option --ignore-unknown is set.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-s, --match-substrings Include packages that partially match the provided names.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --type type Type of package (default: package). See section Package Types for list of available package types.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--provides Show symbols the package provides.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--requires Show symbols the package requires.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--conflicts Show symbols the package conflicts with.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--obsoletes Show symbols the package obsoletes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends Show symbols the package recommends.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--suggests Show symbols the package suggests.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--supplements Show symbols the package supplements.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--enhances Show symbols the package enhances.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper info workrave Show information about package workrave&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper info -t patch libzypp Show information about patch libzypp&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper info -t pattern lamp_server Show information about pattern lamp_server&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
install (in) [options] name|capability|rpm_file_uri... Install or update packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The packages can be selected by their name or by a capability they provide.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
A capability is formed by &amp;amp;quot;NAME[.ARCH][ OP EDITION]&amp;amp;quot;, where ARCH is an architecture code, OP is one of &amp;amp;lt;, &amp;amp;lt;=, =, &amp;amp;gt;=, or &amp;amp;gt; and EDITION is &amp;amp;quot;VERSION[-RELEASE]&amp;amp;quot;. For example: zypper=0.8.8-2.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The NAME component of a capability is not only a package name but any symbol provided by packages: /bin/vi, libcurl.so.3, perl(Time::ParseDate). Just remember to quote to protect the special characters from the shell, for example: zypper\&amp;amp;gt;0.8.10 or &#039;zypper&amp;amp;gt;0.8.10&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If EDITION is not specified, the newest installable version will be installed. This also means that if the package is already installed and newer versions are available, it will get upgraded to the newest installable version.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If ARCH is not specified, or the last dot of the capability name string is not followed by known architecture, the solver will treat the whole string as a capability name. If the ARCH is known, the solver will select a package matching that architecture and complain if such package cannot be found.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If you want to make sure a package from a specific REPOSITORY is picked, use REPOSITORY:PACKAGENAME as argument.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Zypper is also able to install plain RPM files while trying to satisfy their dependencies using packages from defined repositories. You can install a plain RPM file by specifying its location in the install command arguments either as a local path or an URI. E.g.:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper install ~/rpms/foo.rpm http://some.site/bar.rpm&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Zypper will report packages that it cannot find. Further, in interactive mode, zypper proceeds with installation of the rest of requested packages, and it will abort immediately in non-interactive mode. In both cases zypper returns ZYPPER_EXIT_INF_CAP_NOT_FOUND after finishing the operation.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Zypper will collect the files in a temporary plaindir repository and mark the respective packages for installation. If --download-only is used, the downloaded packages will be available in /var/cache/zypper/RPMS until you actually install them or call zypper clean to clear the package caches. They will not become part of the global package cache at /var/cache/zypp/packages (see also the global --pkg-cache-dir option).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In the install command, you can also specify packages you wish to remove by prepending their names by a - or ! character. For example:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper install \!Firefox&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In contrast to zypper remove Firefox which removes Firefox and its dependent packages, the install command will try to keep dependent packages installed by looking for Firefox alternatives.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that if you choose to use - with the first package you specify, you need to write -- before it to prevent its interpretation as a command option:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper install -- -boring-game great-game great-game-manual&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Using --repo is discouraged as it currently hides unmentioned repositories from the resolver, leading to inexpertly decisions. In the future --repo will become an alias for --from.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If you want to install a package from a specific repository, use REPOSITORY:PACKAGENAME as argument.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --type type Type of package to install (default: package). See section Package Types for list of available package types. Use zypper se -t type [name] to look for available items of this type and zypper info -t type name to display more detailed information about the item.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If patch is specified, zypper will install and/or remove packages to satisfy specified patch. This is a way to ensure that specific bug fix is installed. Use zypper list-patches to look for applicable patches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If product or pattern are specified, zypper ensures that all required (and optionally recommended) packages are installed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --name Select packages by their name, don’t try to select by capabilities.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-f, --force Install even if the item is already installed (reinstall), downgraded or changes vendor or architecture.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--oldpackage Allows one to replace a newer item with an older one. Handy if you are doing a rollback. Unlike --force it will not enforce a reinstall, if the item is already installed with the requested version.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--from alias|name|#|URI Select packages from specified repository. If strings specified as arguments to the install command match packages in repositories specified in this option, they will be marked for installation. This option currently implies --name, but allows using wildcards for specifying packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-C, --capability Select packages by capabilities.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-l, --auto-agree-with-licenses Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install. This option is particularly useful for administrators installing the same set of packages on multiple machines (by an automated process) and have the licenses confirmed before.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--auto-agree-with-product-licenses Automatically accept product licenses only. This is used by tools like SUSEconnect, which ask for confirmation before the product gets registered. So there’s no need to confirm the product license again at install time.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--replacefiles Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error. --download-as-needed disables the file conflict check because access to all packages file lists is needed in advance in order to perform the check.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --dry-run Test the installation, do not actually install any package. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--details Show the detailed installation summary.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-unsigned-rpm Silently install unsigned .rpm packages given as commandline parameters. The option does not affect unsigned packages provided by repositories. To allow a repository to provide unsigned packages without explicit confirmation, set the repositories --gpgcheck-allow-unsigned-package option. See the addrepo and modifyrepo commands for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Solver related options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Download-and-install mode options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --download-only Only download the packages for later installation (see also the global --pkg-cache-dir option).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If used together with --dry-run a meaningful file conflict check can be performed (see section Package File Conflicts).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--download-in-advance First download all packages, then start installing. This is the default.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--download-in-heaps Download a minimal set of packages that can be installed without leaving the system in broken state, and install them. Then download and install another heap until all are installed. This helps to keep the system in consistent state without the need to download all packages in advance, which combines the advantages of --download-in-advance and --download-as-needed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
While the resolver is not capable of building heaps, this behaves the same as --download-in-advance.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--download-as-needed Download one package, install it immediately, and continue with the rest until all are installed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--download mode Use the specified download-and-install mode. Available modes are: only, in-advance, in-heaps, as-needed. See corresponding --download-mode options for their description.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper install -t pattern lamp_server Install lamp_server pattern.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper install --no-recommends gv Install GhostScript viewer, but ignore recommended packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper install virtualbox-ose-2.0.6&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper install virtualbox-ose=2.0.6&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper install virtualbox-ose = 2.0.6 Install version 2.0.6 of virtualbox-ose package.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
source-install (si) name... Install specified source packages and their build dependencies. If the name of a binary package is given, the corresponding source package is looked up and installed instead.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command will try to find the newest available versions of the source packages and uses rpm -i to install them, optionally together with all the packages that are required to build the source package. The default location where rpm installs source packages to is /usr/src/packages/{SPECS,SOURCES}, but the values can be changed in your local rpm configuration. In case of doubt try executing rpm --eval &amp;amp;quot;%{_specdir} and %{_sourcedir}&amp;amp;quot;.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that the source packages must be available in repositories you are using. You can check whether a repository contains any source packages using the following command:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper search -t srcpackage -r alias|name|#|URI&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper search -t srcpackage -r alias|name|#|URI&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --build-deps-only Install only build dependencies of specified packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --no-build-deps Don’t install build dependencies.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--download-only Only download the packages, do not install.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper si -d dbus-1 Install build dependencies of dbus-1 source package.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
verify (ve) [options] Check whether dependencies of installed packages are satisfied.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In case that any dependency problems are found, zypper suggests packages to install or remove to fix them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --dry-run Test the repair, do not actually do anything to the system. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--details Show the detailed installation summary.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Solver related options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
install-new-recommends (inr) [options] Install newly added packages recommended by already installed ones. This command basically re-evaluates the recommendations of all installed packages and fills up the system accordingly. You don’t want to call this unconditionally on small or minimal systems, as it may easily add a large number of packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Called as zypper inr --no-recommends, it restricts the command to just look for packages supporting available hardware, languages or filesystems. Useful after having added e.g. new hardware or driver repos. This is also the default behavior if you have set [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --dry-run Test the installation, do not actually install anything. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--details Show the detailed installation summary.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Solver related options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
remove (rm) [options] name...&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
remove (rm) [options] --capability capability... Remove (uninstall) packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The remove command will uninstall the selected and their dependent packages. It will not try to install alternatives in order to keep dependent packages installed. If you want this, use zypper install !name.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The packages can be selected by their name or by a capability they provide. For details on package selection see the install command description.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --type type Type of package (default: package). See section Package Types for list of available package types.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Since patches are not installed in sense of copying files or recording a database entry, they cannot be uninstalled, even though zypper shows them as installed. The installed status is determined solely based on the installed status of its required dependencies. If these dependencies are satisfied, the patch is rendered installed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --name Select packages by their name (default).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-C, --capability Select packages by capabilities.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --dry-run Test the removal of packages, do not actually remove anything.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--details Show the detailed installation summary.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Solver related options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-u, --clean-deps Automatically remove dependencies which become unneeded after removal of requested packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-U, --no-clean-deps No automatic removal of unneeded dependencies.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
removeptf (rmptf) [options] &amp;amp;lt;PTF|CAPABILITY&amp;amp;gt; ... Remove (not only) PTFs.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
A remove command which prefers replacing dependant packages to removing them as well. In fact this is a full featured install command with the remove modifier (-) applied to its plain arguments. So removeptf foo is the same as install — -foo. This is why the command accepts the same options as the install command. It is the recommended way to remove a PTF (Program Temporary Fix).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
A PTF is typically removed as soon as the fix it provides is applied to the latest official update of the dependant packages. But you don’t want the dependant packages to be removed together with the PTF, which is what the remove command would do. The removeptf command however will aim to replace the dependant packages by their official update versions.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
supports every option the install command supports&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
purge-kernels [options] Autoremoves installed kernels.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Automatically cleans up installed kernels according to the rules defined in [zypp.conf:multiversion.kernels] which can be given as &amp;amp;lt;version&amp;amp;gt;, latest[-N], running, oldest[+N].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--details Show the detailed installation summary.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --dry-run Test the removal of packages, do not actually remove anything.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Update Management Commands list-updates (lu) [options] List available updates.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command will list only installable updates, i.e. updates which have no dependency problems, or which do not change package vendor. This list is what the update command will propose to install. To list all packages for which newer version are available, use --all option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --type type Type of package (default: package). See section Package Types for list of available package types.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If patch is specified, zypper acts as if the list-patches command was executed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-a, --all List all packages for which newer versions are available, regardless whether they are installable or not.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--best-effort See the update command for description.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
update (up) [options] [packagename]... Update installed packages with newer versions, where possible.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command will not update packages which would require change of package vendor unless the vendor is specified in /etc/zypp/vendors.d, or which would require manual resolution of problems with dependencies. Such non-installable updates will then be listed in separate section of the summary as &amp;amp;quot;The following package updates will NOT be installed:&amp;amp;quot;.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To update individual packages, specify one or more package names. You can use the * and ? wildcard characters in the package names to specify multiple packages matching the pattern.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --type type Type of package (default: package). See section Package Types for list of available package types.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If patch is specified, zypper acts as if the patches command was executed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--skip-interactive This will skip interactive patches, that is, those that need reboot, contain a message, or update a package whose license needs to be confirmed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--with-interactive Avoid skipping of interactive patches when in non-interactive mode.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-l, --auto-agree-with-licenses Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install. This option is particularly useful for administrators installing the same set of packages on multiple machines (by an automated process) and have the licenses confirmed before.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--auto-agree-with-product-licenses Automatically accept product licenses only. This is used by tools like SUSEconnect, which ask for confirmation before the product gets registered. So there’s no need to confirm the product license again at install time.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--replacefiles Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error. --download-as-needed disables the fileconflict check because access to all packages filelists is needed in advance in order to perform the check.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --dry-run Test the update, do not actually install or update any package. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--details Show the detailed installation summary.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--best-effort Do a best effort approach to update. This method does not explicitly select packages with best version and architecture, but instead requests installation of a package with higher version than the installed one and leaves the rest on the dependency solver. This method is always used for packages, and is optional for products and patterns. It is not applicable to patches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Solver related options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command description.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
list-patches (lp) [options] List all applicable patches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command is similar to zypper list-updates -t patch.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that optional arguments of some of the following options must be specified using = instead of a space.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-b, --bugzilla[=#[,...]] List applicable patches for all Bugzilla issues, or issues whose number matches the given string.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--cve[=#[,...]] List applicable patches for all CVE issues, or issues whose number matches the given string.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--date YYYY-MM-DD[,...] List only patches issued up to, but not including, the specified date.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-g, --category category[,...] List only patches with this category. See section Package Types for a list of commonly used category values.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--severity severity[,...] List only patches with this severity. See section Package Types for a list of commonly used severity values.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--issue[=string[,...]] Look for issues whose number, summary, or description matches the specified string. Issues found by number are displayed separately from those found by descriptions. In the latter case, use zypper patch-info patchname to get information about issues the patch fixes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-a, *--all By default, only patches that are applicable on your system are listed. This option causes all available released patches to be listed. This option can be combined with all the rest of the list-updates command options.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--with-optional, --without-optional Whether applicable optional patches should be treated as needed or be excluded. The default is to exclude optional patches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
patch-check (pchk) Check for patches. Displays a count of applicable patches and how many of them have the security category.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
See also the EXIT CODES section for details on exit status of 0, 100, and 101 returned by this command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--updatestack-only Check only for patches which affect the package management itself.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--with-optional, --without-optional Whether applicable optional patches should be treated as needed or be excluded. The default is to exclude optional patches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Check for patches only in the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
patch [options] Install all available needed patches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
When updating the affected/vulnerable packages described by a patch, zypper always aims for the latest available version. See section Package Types for more details about how patches operate.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If there are patches that affect the package management itself, those will be installed first and you will be asked to run the patch command again.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command is similar to zypper update -t patch.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--updatestack-only Install only patches which affect the package management itself and exit.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--skip-not-applicable-patches Skip needed patches which do not apply without conflict. In normal operation mode those conflicts have to be resolved interactively, otherwise the patch command fails.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In scripted and unattended environments it may be desired to try to apply at least as many patches as possible so the system is not left completely without updates. It is highly recommended to run zypper patch-check afterwards in order to see whether needed patches are still waiting to be resolved interactively.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--with-update Additionally try to update all packages not covered by patches. This is basically the same as running zypper update afterwards.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The option is ignored, if the patch command must update the update stack first, thus it can not be combined with the --updatestack-only option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--with-optional, --without-optional Whether applicable optional patches should be treated as needed or be excluded. The default is to exclude optional patches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-b, --bugzilla #[,...] Select applicable patches for a Bugzilla issue specified by number. Use list-patches --bugzilla command to get a list of applicable patches for specific issues.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--cve #[,...] Select applicable patches for a MITRE’s CVE issue specified by number. Use list-patches --cve command to get a list of applicable patches for specific issues.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--date YYYY-MM-DD[,...] Select only patches patches issued up to, but not including, the specified date.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-g, --category category[,...] Select only patches with this category. Use list-patches --category command to get a list of available patches with a specific category. See section Package Types for a list of commonly used category values.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--severity severity[,...] Select only patches with this severity. Use list-patches --severity command to get a list of available patches with a specific severity. See section Package Types for a list of commonly used severity values.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--skip-interactive This will skip interactive patches, that is, those that need reboot, contain a message, or update a package whose license needs to be confirmed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--with-interactive Avoid skipping of interactive patches when in non-interactive mode.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-l, --auto-agree-with-licenses Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install. This option is particularly useful for administrators installing the same set of packages on multiple machines (by an automated process) and have the licenses confirmed before.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--auto-agree-with-product-licenses Automatically accept product licenses only. This is used by tools like SUSEconnect, which ask for confirmation before the product gets registered. So there’s no need to confirm the product license again at install time.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--replacefiles Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error. --download-as-needed disables the fileconflict check because access to all packages filelists is needed in advance in order to perform the check.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --dry-run Test the update, do not actually update. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--details Show the detailed installation summary.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Solver related options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command description.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
dist-upgrade (dup) [options] Perform a distribution upgrade. This command applies the state of (specified) repositories onto the system; upgrades (or even downgrades) installed packages to versions found in repositories, removes packages that are no longer in the repositories and pose a dependency problem for the upgrade, handles package splits and renames, etc.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If no repositories are specified via the --from option, zypper will do a global upgrade with all defined repositories. This global form of dup will also consider unchanged installed packages and re-evaluate their dependencies. This can be a problem if the system contains conflicting repositories, like repositories for two different distribution releases. This often happens if one forgets to remove an older release repository after adding a new one, say openSUSE 13.1 and openSUSE 13.2.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
For all repositories which have the distribution version within their URL (like https://download.opensuse.org/distribution/13.1/repo/oss) using the $releasever variable instead may be helpful (https://download.opensuse.org/distribution/$releasever/repo/oss). The variable is per default substituted by the current distributions version (13.1).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This value can be temporarily overwritten in the current zypper command by using the --releasever global option. Calling zypper --releasever 13.2... will cause these repos to use the new location (https://download.opensuse.org/distribution/13.2/repo/oss) without the need to add/remove anything. But you’ll need to use --releasever 13.2 with every zypper command until the distribution upgrade was actually performed. Once the dup is done, $releasever will default to the new distribution version 13.2 and --releasever is no longer needed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
It might be less tedious to persistently set $releasever to the target distribution value, so --releasever is not needed at all. See section Repository Management for more info about variable substitution and the definition of custom variables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note: Performing a distribution upgrade will automatically create a solver test case at /var/log/updateTestcase-YYYY-MM-DD-hh-mm-ss (the date and time the command was executed).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note: distribution upgrades in openSUSE are currently only supported between consecutive releases. To upgrade multiple releases, upgrade each consecutive release one at a time. For more details see http://en.opensuse.org/SDB:System_upgrade and the openSUSE release notes at http://doc.opensuse.org/release-notes/.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note: Due to the treatment of orphaned packages dist-upgrade depends on a proper repository setup more than any other command. It must not continue if enabled repositories fail to refresh. This may severely damage the system. If a failing repository is actually not needed, it must be disabled.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--from alias|name|#|URI The option can be used multiple times and restricts the upgrade to the specified repositories only. Nevertheless all enabled repositories are visible to the resolver and will be considered to satisfy dependency problems.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--remove-orphaned Remove orphaned packages. Installed packages which are not provided by at least one of the available repositories are considered to be orphaned or dropped. dist-upgrade removes orphaned packages if they prevent the upgrade of wanted packages. With this option all unneeded orphaned packages are removed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;Note:&#039; Packages which are not shipped within a repository - manually built or downloaded ones -, can be collected in a plaindir repository to prevent them from being treated as orphaned. See section *System Packages* for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Using --repo is discouraged as it currently hides unmentioned repositories from the resolver, leading to inexpertly decisions. This is because packages originally installed from the hidden repos will now be treated as orphaned or dropped. They can be silently removed if involved in a dependency conflict. In the future --repo will become an alias for --from.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-l, --auto-agree-with-licenses Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install. This option is particularly useful for administrators installing the same set of packages on multiple machines (by an automated process) and have the licenses confirmed before.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--auto-agree-with-product-licenses Automatically accept product licenses only. This is used by tools like SUSEconnect, which ask for confirmation before the product gets registered. So there’s no need to confirm the product license again at install time.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--replacefiles Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error. --download-as-needed disables the fileconflict check because access to all packages filelists is needed in advance in order to perform the check.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --dry-run Test the upgrade, do not actually install or update any package. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--details Show the detailed installation summary.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Solver related options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command description.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper dup --from factory --from packman Upgrade the system to the latest versions provided by the factory and packman repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Query Commands search (se) [options] [querystring|capability]... Search for packages matching any of the given search strings. * and ? wildcard characters can be used within search strings. If the search string is enclosed in / (e.g. /^k.*e$/) it’s interpreted as a regular expression. See the install command for details about how to specify a capability.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If the search string starts with a /, a filename is assumed and the search will automatically look into the file list of packages. Otherwise use -f to search in the packages file lists as well.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Results of the search are printed in a table with columns Status, Name, Summary and Type of package. In case the query result is empty zypper returns ZYPPER_EXIT_INF_CAP_NOT_FOUND, unless the --ignore-unknown global option is set.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In the detailed view (se -s) all available instances of matching packages are shown; each version in each repository on a separate line, with columns Status, Name, Type, Version, Architecture and Repository. For installed packages Repository shows either a repository that provides exactly the installed version of the package, or, if the exact version is not provided by any known repo, (System Packages) (or @System). Those installed packages not provided by any repo are often denoted as being unwanted, orphaned or dropped.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The Status column can contain the following values:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
i+ installed by user request&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
i installed automatically (by the resolver, see section Automatically installed packages)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
v a different version is installed&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
empty neither of the above cases&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
! a patch in needed state&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
.l is shown in the 2nd column if the item is locked (see section Package Locks Management)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
.P is shown in the 2nd column if the item is part of a PTF (A program temporary fix which must be explicitly selected and will otherwise not be considered in dependency resolution).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
.R is shown in the 2nd column if the item has been retracted (see patch in section Package Types)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The v status is only shown if the version or the repository matters (see --details or --repo), and the installed instance differs from the one listed in version or repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The verbose view (se -v; implies -s) will also show the matches themselves within the packages metadata.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command accepts the following options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--match-substrings Matches for search strings may be partial words (default).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--match-words Matches for search strings may only be whole words.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-x, --match-exact Searches for an exact name of the package.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--provides Search for packages which provide the search strings.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--requires Search for packages which require the search strings.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends Search for packages which recommend the search strings.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--suggests Search for packages which suggest the search strings.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--conflicts Search for packages conflicting with the search strings.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--obsoletes Search for packages which obsolete the search strings.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--supplements Search for packages which supplement the search strings.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--enhances Search for packages which enhances the search strings.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--provides-pkg Search for all packages that provide any of the provides of the package(s) matched by the input parameters.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--requires-pkg Search for all packages that require any of the provides of the package(s) matched by the input parameters.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommends-pkg Search for all packages that recommend any of the provides of the package(s) matched by the input parameters.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--supplements-pkg Search for all packages that supplement any of the provides of the package(s) matched by the input parameters.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--conflicts-pkg Search for all packages that conflict with any of the package(s) matched by the input parameters.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--obsoletes-pkg Search for all packages that obsolete any of the package(s) matched by the input parameters.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--suggests-pkg Search for all packages that suggest any of the provides of the package(s) matched by the input parameters.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--enhances-pkg Search for all packages that enhance any of the provides of the package(s) matched by the input parameters.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --name Useful together with dependency options, otherwise searching in package name is default.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-f, --file-list Search in the file list of packages. Note that the full file list is available for installed packages only. For remote packages only an abstract of their file list is available within the metadata (files containing /etc/, /bin/, or /sbin/).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --search-descriptions Search also in summaries and descriptions.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-C, --case-sensitive Perform case-sensitive search.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-i, --installed-only Show only installed packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-u, --not-installed-only Show only packages which are not installed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The old option name --uninstalled-only is still acceptable, but should be considered deprecated.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --type type Search only for packages of specified type. See section Package Types for a list of available package types. Multiple --type options are allowed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
See also the type-specific query commands like packages, patterns, etc.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--sort-by-name Sort packages by name (default).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--sort-by-repo Sort packages by repository, not by name.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-s, --details Show all available versions of matching packages, each version in each repository on a separate line.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-v, --verbose Like --details with additional information where the search has matched (useful when searching for dependencies, e.g. --provides).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper se &#039;yast*&#039; Search for YaST packages (quote the string to prevent the shell from expanding the wildcard).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper se -s --match-exact kernel-default Show all available versions of package kernel-default&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper se -dC --match-words RSI Look for RSI acronym (case-sensitively), also in summaries and descriptions.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
packages (pa) [options] [repository]... List all available packages or all packages from specified repositories. Similar to zypper search -s -t package.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Just another means to specify repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-i, --installed-only Show only installed packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-u, --not-installed-only Show only packages which are not installed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The old option name --uninstalled-only is still acceptable, but should be considered deprecated.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
*--autoinstalled Show installed packages which were automatically selected by the resolver.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--userinstalled Show installed packages which were explicitly selected by the user.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--system Show installed packages which are not provided by any repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--orphaned Show system packages which are orphaned (without repository and without update candidate). If combined with --system, the status of orphaned packages is tagged with (o).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--suggested Show packages which are suggested.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--recommended Show packages which are recommended.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--unneeded Show packages which are unneeded.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
patches (pch) [options] [repository]... List all available patches from specified repositories, including those not needed. Short for zypper lp -a.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name_|#|URI Just another means to specify repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
patterns (pt) [options] [repository]... List all available patterns or all patterns from specified repositories. Similar to zypper search -s -t pattern.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Just another means to specify repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-i, --installed-only Show only installed patterns.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-u, --not-installed-only Show only patterns which are not installed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The old option name --uninstalled-only is still acceptable, but should be considered deprecated.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
products (pd) [options] [repository]... List all available products or all products from specified repositories. Similar to zypper search -s -t product, but shows also the type of the product (base, add-on).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Just another means to specify repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-i, --installed-only Show only installed products.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-u, --not-installed-only Show only products which are not installed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The old option name --uninstalled-only is still acceptable, but should be considered deprecated.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--xmlfwd tag XML output only: Literally forward the XML tag, if it is found in an installed products .prod-file (in /etc/products.d).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Using this option, for each installed product an &amp;amp;lt;xmlfwd&amp;amp;gt; node will be created inside the &amp;amp;lt;product&amp;amp;gt; output node of the product.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Tag defines the name (or /-separated path) of a xml-tag inside an installed products .prod-file. If the tag is present inside the products .prod-file, the tag and it’s content is literally forwarded into the products &amp;amp;lt;xmlfwd&amp;amp;gt; output node.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The option may be specified multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper -x pd --xmlfwd name --xmlfwd register/target&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
what-provides (wp) capability List all packages providing the specified capability (case-insensitive search). See also the install command for info about specifying capabilities.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The command line is automatically transformed into the corresponding search command:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper what-provides &#039;zypper&amp;amp;gt;1.6&#039; $ zypper search --provides --match-exact &#039;zypper&amp;amp;gt;1.6&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
For a case-sensitive search call $ zypper search --provides --match-exact --case-sensitive &#039;zypper&amp;amp;gt;1.6&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Repository Management Zypper is able to work with YaST, RPM-MD (yum) software repositories, and plain directories containing .rpm files (no symlinks).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Repositories are primarily identified using their URI or alias. Alias serves as a shorthand for the long URI or name of the repository. The name of the repository should briefly describe the repository and is shown to the user in tables and messages. The name is not required, and if not known, the alias is shown instead. The alias is required and uniquely identifies the repository on the system.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The alias, name, URI, or the number from zypper repos list can be used to specify a repository as an argument of various zypper commands and options like refresh, --repo, or --from.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Apart from the above, repositories have several other properties which can be set using the commands described in this section below, or by manually editing the repository definition files (.repo files, see section FILES).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Variable substitution: You can use the following variables within a .repo or .service files name and URI values:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$arch Use this variable to refer to the system’s CPU architecture.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$basearch Use this variable to refer to the base architecture of the system. For example, iX86 machines have a base architecture of i386, while AMD64 and Intel64 have x86_64.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$releasever, $releasever_major, $releasever_minor Use this variable to refer to the version of your openSUSE or SUSE Linux. The value is obtained from the /product/version XML-node in /etc/products.d/baseproduct.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This is useful for related repositories like packman (http://ftp.gwdg.de/pub/linux/packman/suse/$releasever), which shall always fit the installed distribution, even after a distribution upgrade.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To help performing a distribution upgrade, the value of $releasever can be persistently set to a user defined value by creating a custom variable with that name (see below). This way you can easily switch all repositories using $releasever to the new version (provided the server layouts did not change and new repos are already available).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
For a single zypper command the value of $releasever can be temporarily overwritten by using the --releasever global option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In addition $releasever_major will be set to the leading portion up to (but not including) the 1st dot; $releasever_minor to the trailing portion after the 1st dot. If there’s no dot in $releasever, $releasever_major is the same as $releasever and $releasever_minor is empty.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Custom Variables A custom repository variable is defined by creating a file in /etc/zypp/vars.d. The variable name equals the file name. The files first line (up to but not including the newline character) defines the variables value. Valid variable(file) names consist of alphanumeric chars and underscore only.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This is how you can set a custom variable, e.g. $releasever to a value of 99.0: echo &amp;amp;quot;99.0&amp;amp;quot; &amp;amp;gt;/etc/zypp/vars.d/releasever&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To remove the custom variable, simply delete its file in /etc/zypp/vars.d: rm /etc/zypp/vars.d/releasever&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To check where you already use $releasever call: zypper --releasever @--HERE--@ lr -u&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Remember to protect the $ when using these variables on a shell command line: zypper ar -f http://ftp.gwdg.de/pub/linux/packman/suse/\$releasever packman&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If a variable is followed by an alphanumeric character or underscore it needs to be enclosed in {}: zypper ar -f http://ftp.gwdg.de/pub/linux/packman/suse/\${releasever}_packman&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Bash style definition of default ${variable:-word} and alternate ${variable:+word} values: SLE-${releasever_major}${releasever_minor:+-SP-$releasever_minor}&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
NOTE: Variable substitution within an URIs authority is limited to host and port. Bash style definition of default and alternate values is not supported. No variables can be used in an URIs scheme, user and password.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Supported URI formats: scheme:@]host[:port]]/path[?query][#fragment] Special characters occurring in URI components (like a &#039;@&#039; in a password) must be %-encoded (&#039;%40&#039;).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
CD or DVD drive Optionally with devices list for probing.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• cd:/// dvd:/subdir?devices=/dev/sr0,/dev/sr1&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
FTP/HTTP/HTTPS directory tree The ftp URL scheme supports absolute and relative paths to the default ftp server directory (RFC1738, Section 3.2.2). To use an absolute path, you have to prepend the path with an additional slash, what results in a /%2f combination (second / encoded to %2f) at the begin of the URL path. This is important, especially in user authenticated ftp, where the users home is usually the default directory of the server (except when the server chroots into the users home directory).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Explicit proxy settings may be passed via optional parameters proxy, proxyport, proxyuser and proxypass.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
HTTP authentication methods to use can be defined as comma separated list via optional parameter auth. Valid methods are e.g. basic, digest, ntlm, negotiate. Note, that this list depends on the list of methods supported by the curl library.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
SSL verification behavior can be changed using the ssl_verify option (this should be used with care). Valid values are yes (the secure default), host, peer or no. Host just checks that the &amp;amp;quot;Common Name&amp;amp;quot; field or a &amp;amp;quot;Subject Alternate Name&amp;amp;quot; field in the servers certificate matches the host name in the URL. Peer just verifies whether the certificate provided by the server is authentic against the chain of digital signatures found in ssl_capath. No performs no checks at all. Yes is the secure default, performing host and peer check.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
For SSL client certificate authentication use the options ssl_clientcert to define the path to the ssl client certificate and ssl_clientkey to define the path to the SSL client key. Use ssl_capath to change the directory holding the CA certificates (default is /etc/ssl/certs).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• ftp://user:pass@server/path/to/media/dir ftp://user:pass@server/%2fhome/user/path/to/media/dir http://user:pass@server/path https://user:pass@server/path?proxy=foo&amp;amp;amp;proxyuser=me&amp;amp;amp;proxypass=pw https://server/path?ssl_clientcert=/entitlement/1234.pem&amp;amp;amp;ssl_clientkey=/entitlement/1234-key.pem&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Disk volume (partition) Mandatory device parameter specifying the name of the block device to mount. The name of the optional filesystem defaults to &amp;amp;quot;auto&amp;amp;quot;.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• hd:/subdir?device=/dev/sda1&amp;amp;amp;filesystem=reiserfs&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Local directory tree&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• dir:/directory/name&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Media in an ISO image (loopback mounted) Mandatory iso parameter specifying the name of the iso file. Optional url parameter specifying the URL to the directory containing the iso file. Optional mnt parameter specifying the preferred attach point for the source media url. Optional filesystem name of the filesystem used in the iso file. Defaults to &amp;amp;quot;auto&amp;amp;quot;.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• iso:/?iso=CD1.iso&amp;amp;amp;url=nfs://server/path/to/media iso:/?iso=CD1.iso&amp;amp;amp;url=hd:/?device=/dev/hda iso:/subdir?iso=DVD1.iso&amp;amp;amp;url=nfs://nfs-server/directory&amp;amp;amp;mnt=/nfs/attach/point&amp;amp;amp;filesystem=udf&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
NFS exported directory tree To use NFSv4 either use schema tnfsv4:// or pass an optional parameter type=nfs4. Additional mountoptions can be passed as comma separated list. Defaults to &amp;amp;quot;ro&amp;amp;quot;.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• nfs://nfs-server/exported/path nfs://nfs-server/exported/path?mountoptions=ro&amp;amp;amp;type=nfs4 nfs4://nfs-server/exported/path?mountoptions=ro&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
CIFS/SMB directory tree There is no difference between cifs and smb scheme (any more). In both cases the cifs filesystem is used. Additional mountoptions can be passed as comma separated list. Defaults to &amp;amp;quot;ro,guest&amp;amp;quot;. Specify &amp;amp;quot;noguest&amp;amp;quot; to turn off &amp;amp;quot;guest&amp;amp;quot;. This is necessary if Samba is configured to reject guest connections.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Optional workgroup or domain parameter set the name of the workgroup. As alternative to passing username:password in the URI authority the parameters user and pass can be used.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• smb://servername/share/path/on/the/share cifs://usern:passw@servername/share/path/on/the/share?mountoptions=ro,noguest cifs://usern:passw@servername/share/path/on/the/share?workgroup=mygroup cifs://servername/share/path/on/the/share?user=usern&amp;amp;amp;pass=passw&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
OpenSUSE Build Build Service (OBS) repositories Zypper also accepts special URIs identifying openSUSE Build Service (OBS) repositories in the addrepo command. These URIs have the form of obs://project/[platform], where project is the name of the OBS project and platform is the target platform (OS) for which the repository is intended.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If platform is omitted, openSUSE_$releasever is used unless a value for obs.platform is defined in zypper.conf. If you are following openSUSE_Factory or openSUSE_Tumbleweed you may need to set these as your default platform. But we can only guess, how the directory containing the repository that fits your distribution is named on the server. In case of doubt you need to look up the right URL in a browser.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• obs://zypp:Head/ obs://zypp:Head/openSUSE_Factory obs://zypp:Head/openSUSE_Factory_Staging_Gcc49_standard&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Commands: addrepo (ar) [options] URI alias&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
addrepo (ar) [options] FILE*.repo* Add a new repository specified by URI and assign specified alias to it or specify URI to a .repo file.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Newly added repositories have auto-refresh disabled by default (except for repositories imported from a .repo, having the auto-refresh enabled). To enable auto-refresh use addrepo -f, or the --refresh option of the modifyrepo command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Also, this command does not automatically refresh the newly added repositories. The repositories will get refreshed when used for the first time, or you can use the refresh command after finishing your modifications with *repo commands.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo file.repo Read URI and alias from specified .repo file&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-c, --check Probe given URI.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-C, --no-check Don’t probe URI, probe later during refresh.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --name name Specify descriptive name for the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-e, --enable Enable the repository (the default).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --disable Add the repository as disabled. Repositories are added as enabled by default.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-f, --refresh Enable autorefresh of the repository. The autorefresh is disabled by default when adding new repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-F, --no-refresh Disable auto-refresh for the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-p, --priority [1-2147483647]|0 Set the priority of the repository. Priority of 1 is the highest, and 2147483647 is the lowest. -p 0 will set the priority back to the default (99). Packages from repositories with higher priority will be used even if there are better versions available in a repository with a lower priority.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-k, --keep-packages Enable RPM files caching for the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-K, --no-keep-packages Disable RPM files caching.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-g, --gpgcheck Enable GPG check for this repository. The behavior as described in section GPG checks.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpgcheck-strict Enable strict GPG check for this repository. Even packages from signed repositories need a valid GPG signature and using unsigned packages must be confirmed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpgcheck-allow-unsigned Short hand for --gpgcheck-allow-unsigned-repo --gpgcheck-allow-unsigned-package&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpgcheck-allow-unsigned-repo Enable GPG check but allow the repository metadata to be unsigned.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpgcheck-allow-unsigned-package Enable GPG check but allow installing unsigned packages from this repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-G, --no-gpgcheck Disable GPG check for this repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Disabling GPG checks is not recommended. Signing data enables the recipient to verify that no modifications occurred after the data were signed. Accepting data with no, wrong or unknown signature can lead to a corrupted system and in extreme cases even to a system compromise.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--default-gpgcheck Use the global GPG check settings defined in /etc/zypp/zypp.conf. This is the default.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Unless you have modified your zypp.conf settings, this is the same as --gpgcheck, the behavior as described in section GPG checks.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper ar -c -n &#039;Packman 11.1 repo&#039; http://packman.iu-bremen.de/suse/11.1 packman Add a HTTP repository, probe it, name it Packman 11.1 repo, and use packman as alias.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper ar https://download.opensuse.org/repositories/zypp:/svn/openSUSE_Factory/zypp:svn.repo&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper ar myreposbackup.repo Add repositories from a .repo file.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
removerepo (rr) [options] alias|name|#|URI... Delete repositories specified by aliases, names, numbers, URIs or one of the aggregate options.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--loose-auth Ignore user authentication data in the URI&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--loose-query Ignore query string in the URI&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-a, --all Apply changes to all repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-l, --local Apply changes to all local repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --remote Apply changes to all remote repositories (http/https/ftp).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-m, --medium-type type Apply changes to repositories of specified type. The type corresponds to the repository URI scheme identifier like http, dvd, etc. You can find complete list of valid types at http://en.opensuse.org/openSUSE:Libzypp_URIs.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
repos (lr) [options] [repo]... List all defined repositories or show detailed information about those specified as arguments&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The following data can be printed for each repository found on the system: # (repository number), Alias (unique identifier), Name, Enabled (whether the repository is enabled), GPG Check (whether GPG check for repository metadata (r) and/or downloaded rpm packages (p) is enabled), Refresh (whether auto-refresh is enabled for the repository), Priority, Type (repository meta-data type: rpm-md, yast2, plaindir). Which of the data is shown is determined by command line options listed below and the main.repoListColumns setting from zypper.conf. By default, #, Alias, Name, Enabled, GPG Check and Refresh is shown.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Repository number is a unique identifier of the repository in current set of repositories. If you add, remove or change a repository, the numbers may change. Keep that in mind when using the numbers with the repository handling commands. On the other hand, using the alias instead of the number is always safe.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To show detailed information about specific repositories, specify them as arguments, either by alias, name, number from simple zypper lr, or by URI; e.g. fB zypper lr factory, or zypper lr 2.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-e, --export FILE.repo|- This option causes zypper to write repository definition of all defined repositories into a single file in repo file format. If - is specified instead of a file name, the repositories will be written to the standard output.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-a, --alias Add alias column to the output.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --name Add name column to the output.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-u, --uri Add base URI column to the output.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-p, --priority Add repository priority column to the output.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --refresh Add the autorefresh column to the output.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-k, --keep-packages Add the keep-packages column to the output. The column will show a Yes if keep-packages is explicitly enabled for this repository. Otherwise a - or a + if a .keep_packages file in the pkg-cache directory exists. (see section FILES)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --details Show more information like URI, priority, type, etc.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-E, --show-enabled-only Show enabled repositories only.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-U, --sort-by-uri Add base URI column and sort the list it.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-P, --sort-by-priority Add repository priority column and sort the list by it.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-A, --sort-by-alias Sort the list by alias.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-N, --sort-by-name Sort the list by name.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper repos -e myreposbackup.repo Backup your repository setup:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper lr -pu List repositories with their URIs and priorities:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
renamerepo (nr) alias|name|#|URI new-alias Assign new alias to the repository specified by alias, name, number, or URI.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper nr 8 myrepo Rename repository number 8 to myrepo (useful if the repo has some dreadful alias which is not usable on the command line).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
modifyrepo (mr) options alias|name|#|URI...&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
modifyrepo (mr) options --all|--remote|--local|--medium-type Modify properties of repositories specified by alias, name, number, or URI or one of the aggregate options.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --name name Set a descriptive name for the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-e, --enable Enable the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --disable Disable the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-f, --refresh (legacy: -r) Enable auto-refresh for the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-F, --no-refresh (legacy: -R) Disable auto-refresh for the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-p, --priority [1-2147483647]|0 Set the priority of the repository. Priority of 1 is the highest, and 2147483647 is the lowest. -p 0 will set the priority back to the default (99). Packages from repositories with higher priority will be used even if there are better versions available in a repository with a lower priority.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-k, --keep-packages Enable RPM files caching.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-K, --no-keep-packages Disable RPM files caching.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-g, --gpgcheck Enable GPG check for this repository. The behavior as described in section GPG checks.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpgcheck-strict Enable strict GPG check for this repository. Even packages from signed repositories need a valid GPG signature and using unsigned packages must be confirmed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpgcheck-allow-unsigned Short hand for --gpgcheck-allow-unsigned-repo --gpgcheck-allow-unsigned-package&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpgcheck-allow-unsigned-repo Enable GPG check but allow the repository metadata to be unsigned.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpgcheck-allow-unsigned-package Enable GPG check but allow installing unsigned packages from this repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-G, --no-gpgcheck Disable GPG check for this repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Disabling GPG checks is not recommended. Signing data enables the recipient to verify that no modifications occurred after the data were signed. Accepting data with no, wrong or unknown signature can lead to a corrupted system and in extreme cases even to a system compromise.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--default-gpgcheck Use the global GPG check settings defined in /etc/zypp/zypp.conf. This is the default.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Unless you have modified your zypp.conf settings, this is the same as --gpgcheck, the behavior as described in section GPG checks.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-a, --all Apply changes to all repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-l, --local Apply changes to all local repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --remote Apply changes to all remote repositories (http/https/ftp).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-m, --medium-type type Apply changes to repositories of specified type. The type corresponds to the repository URI scheme identifier like http, dvd, etc. You can find complete list of valid types at http://en.opensuse.org/openSUSE:Libzypp_URIs.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper mr -kt Enable keeping of packages for all remote repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper mr -er updates Enable repository updates and switch on autorefresh for the repo.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper mr -da Disable all repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
refresh (ref) [alias|name|#|URI]... Refresh repositories specified by their alias, name, number, or URI. If no repositories are specified, all enabled repositories will be refreshed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-f, --force Force a complete refresh of specified repositories. This option will cause both the download of raw metadata and parsing of the metadata to be forced even if everything indicates a refresh is not needed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-b, --force-build Force only reparsing of cached metadata and rebuilding of the database. Raw metadata download will not be forced.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --force-download Force only download of current copy of repository metadata. Parsing and rebuild of the database will not be forced.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-B, --build-only Only parse the metadata and build the database, don’t download raw metadata into the cache. This will enable you to repair damaged database from cached data without accessing network at all.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --download-only Only download the raw metadata, don’t parse it or build the database.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--include-all-archs Multi-Arch repos: Download raw metadata for all offered architectures even if the repo supports filtering. Otherwise we’d download only the metadata for architectures compatible with the local system. You do not need this unless you want zypper to provide the full metadata for the purpose of mirroring the repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-s, --services Refresh also services before refreshing repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
clean (cc) [options] [alias|name|#|URI]... Clean the local caches for all known or specified repositories. By default, only caches of downloaded packages are cleaned.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-m, --metadata Clean repository metadata cache instead of package cache.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-M, --raw-metadata Clean repository raw metadata cache instead of package cache.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-a, --all Clean both repository metadata and package caches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Service Management The services, addservice, removeservice, modifyservice, and refresh-services commands serve for manipulating services. A service is specified by its URI and needs to have a unique alias defined (among both services and repositories).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Standalone repositories (not belonging to any service) are treated like services, too. The ls command will list them, ms command will modify them, etc. Repository specific options, like --keep-packages are not available here, though. You can use repository handling commands to manipulate them.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
addservice (as) [options] URI alias Adds a service specified by URI to the system. The alias must be unique and serves to identify the service. If a service with the same alias and URI already exists, the command behaves like modifyservice and updates the settings accordingly.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Newly added services are not refreshed automatically. Use the refresh-services command to refresh them. Zypper does not access the service URI when adding the service, so the type of the services is unknown until it is refreshed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --name name Specify descriptive name for the service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-e, --enable Enable the service (this is the default).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --disable Add the service as disabled.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-f, --refresh Enable auto-refresh of the service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-F, --no-refresh Disable auto-refresh of the service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
removeservice (rs) [options] alias|name|#|URI... Remove specified service from the system. Removing a service will also remove of all of its repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--loose-auth Ignore user authentication data in the URI.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--loose-query Ignore query string in the URI.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
modifyservice (ms) options alias|name|#|URI&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
modifyservice (ms) options --all|--remote|--local|--medium-type Modify properties of specified services.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Common Options These options are common to all types of services and repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --name name Set a descriptive name for the service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-e, --enable Enable a disabled service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --disable Disable the service (but don’t remove it).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-f, --refresh (legacy: -r) Enable auto-refresh of the service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-F, --no-refresh (legacy: -R) Disable auto-refresh of the service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-a, --all Apply changes to all services.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-l, --local Apply changes to all local services.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --remote Apply changes to all remote services.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-m, --medium-type type Apply changes to services of specified type.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
RIS Service Specific Options These options are ignored by services other than Repository Index Services.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-i, --ar-to-enable alias Schedule an RIS service repository to be enabled at next service refresh.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-I, --ar-to-disable alias Schedule an RIS service repository to be disabled at next service refresh.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-j, --rr-to-enable alias Remove a RIS service repository to enable.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-J, --rr-to-disable alias Remove a RIS service repository to disable.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-k, --cl-to-enable Clear the list of RIS repositories to enable.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-K, --cl-to-disable Clear the list of RIS repositories to disable.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
services (ls) [options] List services defined on the system.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-u, --uri Show also base URI of repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-p, --priority Show also repository priority.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --details Show more information like URI, priority, type.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --with-repos Show also repositories belonging to the services.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-P, --sort-by-priority Sort the list by repository priority.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-E, --show-enabled-only Show enabled services only. If used together with --with-repos a disabled services owning (manually) enabled repositories are shown as well.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-U, --sort-by-uri Sort the list by URI.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-N, --sort-by-name Sort the list by name.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
refresh-services (refs) [options] alias|name|#|URI... Refreshing a service means executing the service’s special task.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
RIS services add, remove, or modify repositories on your system based on current content of the repository index. A differing enabled/disabled state caused by manually calling modify-repo on a service repository however will not be reverted unless the --restore-status option is used, or the repository index explicitly requests the change.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Services only manage defined repositories, they do not refresh them. To refresh also repositories, use --with-repos option or the refresh command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-f, --force Force a complete refresh of specified services. This option will cause both the download of raw metadata and parsing of the metadata to be forced even if everything indicates a refresh is not needed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --with-repos Refresh also the service repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --restore-status Also restore service repositories enabled/disabled state to the repository index default. Useful after you manually changed some service repositories enabled state.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Package Locks Management Package locks serve the purpose of preventing changes to the set of installed packages on the system. Locks are stored as queries in /etc/zypp/locks file (see also locks(5)). Packages matching a query are then forbidden to change their installed status; an installed package can’t be removed or upgraded, not installed package can’t be installed. When requesting to install, upgrade or remove such locked package, you will get a dependency problem dialog.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
A lock-spec is formed by &amp;amp;quot;NAME [OP EDITION]&amp;amp;quot;, where NAME may also be a glob pattern using * and ? wildcard characters. Non-package types may to have their kind-string prepended (e.g. patch:foo or product:baa) or use the commands --type option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The basic form will lock all editions of the matching items. You can optionally restrict the lock to match a specific edition or edition range using =, &amp;amp;lt;, &amp;amp;lt;=, &amp;amp;gt;, &amp;amp;gt;= or != followed by the edition.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If you use blanks around the operator you need to quote the string or escape the blanks according to the rules of the shell you are using.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
locks (ll) List currently active package locks.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-m, --matches Show the number of resolvables matched by each lock. This option requires loading the repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-s, --solvables List the resolvables matched by each lock. This option requires loading the repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
addlock (al) [options] lock-spec... Add a package lock. Specify packages to lock as explained above.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Restrict the lock to the specified repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --type type Lock only packages of specified type (default: package). See section Package Types for list of available package types.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-m, --comment comment Add a comment for package lock.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
removelock (rl) [options] lock-number|lock-spec... Remove a package lock. Specify the lock to remove by its number obtained with zypper locks or by the lock-spec.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Restrict the lock to the specified repository.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --type type Restrict the lock to packages of specified type (default: package). See section Package Types for list of available package types.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
cleanlocks (cl) Remove unused locks.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command looks for locks that do not currently (with regard to repositories used) lock any package and for each such lock it asks user whether to remove it.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Locale Management These commands give information about requested locales and the possibility to manage those. A locale is defined by a language code. For many packages there are locale dependent packages available which provide translations or dictionaries. To get these installed, the locale for the desired language must be marked as requested by the package manager library.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
locales (lloc) [OPTIONS] [LOCALE] ... List requested locales. Called without argument, lists the locales which are already marked as requested. Specifying certain locale(s) prints information only for this(these).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-a, --all List all available locales.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-p, --packages Show corresponding packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
addlocale (aloc) [OPTIONS] &amp;amp;lt;LOCALE&amp;amp;gt; ... Add specified locale(s) to the list of requested locales..&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --no-packages Do not install corresponding packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
removelocale (rloc) [OPTIONS] &amp;amp;lt;LOCALE&amp;amp;gt; ... Remove specified locale(s) from the list of requested locales..&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --no-packages Do not remove corresponding packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper locales List requested locales.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper locales --packages de en Get the lists of packages which are available for de and en (exact match).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper locales en_ Get all locales with lang code en that have their own country code, excluding the fallback en.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper locales en* Get all locales with lang code en with or without country code.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper aloc --packages de_CH Request de_CH and install language dependent packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Other Commands system-architecture Print the detected system architecture.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
versioncmp (vcmp) VERSION1 VERSION2 Compare the versions supplied as arguments and tell whether VERSION1 is older or newer than VERSION2 or the two version strings match. The exit code is 0 if the versions are equal, 11 if VERSION1 is newer, and 12 if VERSION2 is newer.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The default output is in human-friendly form. If --terse global option is used, the result is an integer number, negative/positive if VERSION1 is older/newer than VERSION2, zero if they match.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-m, --match Takes missing release number as any release.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
For example:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper vcmp -m 0.15.3 0.15.3-2 0.15.3 matches 0.15.3-2&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper vcmp 0.15.3 0.15.3-2 0.15.3 is older than 0.15.3-2&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
targetos (tos) Shows the ID string of the target operating system. The string is defined by the XPath:/product/register/target entry in (current-rootdir)/etc/products.d/baseproduct.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If the baseproduct does not provide this entry, or if no baseproduct is installed at all, the value is empty if the --terse global option is used.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
In not-terse mode the distribution label is shown instead of an empty value, if a baseproduct is installed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-l, --label Show the baseproducts distribution and short label instead.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
licenses Prints a report about licenses and EULA&#039;s of installed packages to standard output.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
First, a list of all packages and their licenses and/or EULAs is shown. This is followed by a summary, including the total number of installed packages, the number of installed packages with EULAs that required a confirmation from the user. Since the EULAs are not stored on the system and can only be read from repository metadata, the summary includes also the number of installed packages that have their counterpart in repositories. The report ends with a list of all licenses uses by the installed packages.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This command can be useful for companies redistributing a custom distribution (like appliances) to figure out what licenses they are bound by.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
download [OPTIONS] Download rpms specified on the commandline to a local directory.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Per default packages are downloaded to the libzypp package cache (/var/cache/zypp/packages; for non-root users $XDG_CACHE_HOME/zypp/packages), but this can be changed by using the global --pkg-cache-dir option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Parsable XML-output produced by zypper --xmlout will include a &amp;amp;lt;download-result&amp;amp;gt; node for each package zypper tried to download. Upon success the location of the downloaded package is found in the path attribute of the &amp;amp;lt;localfile&amp;amp;gt; subnode (xpath: download-result/localpath@path):&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;lt;download-result&amp;amp;gt; &amp;amp;lt;solvable&amp;amp;gt; &amp;amp;lt;kind&amp;amp;gt;package&amp;amp;lt;/kind&amp;amp;gt; &amp;amp;lt;name&amp;amp;gt;zypper&amp;amp;lt;/name&amp;amp;gt; &amp;amp;lt;edition epoch=&amp;amp;quot;0&amp;amp;quot; version=&amp;amp;quot;1.9.17&amp;amp;quot; release=&amp;amp;quot;26.1&amp;amp;quot;/&amp;amp;gt; &amp;amp;lt;arch&amp;amp;gt;x86_64&amp;amp;lt;/arch&amp;amp;gt; &amp;amp;lt;repository name=&amp;amp;quot;repo-oss-update (13.1)&amp;amp;quot; alias=&amp;amp;quot;openSUSE:repo-oss-update&amp;amp;quot;/&amp;amp;gt; &amp;amp;lt;/solvable&amp;amp;gt; &amp;amp;lt;localfile path=&amp;amp;quot;/var/cache/zypp/pac.../zypper-1.9.17-26.1.x86_64.rpm&amp;amp;quot;/&amp;amp;gt; &amp;amp;lt;/download-result&amp;amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--all-matches Download all versions matching the commandline arguments. Otherwise only the best version of each matching package is downloaded.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--dry-run Don’t download any package, just report what would be done.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number or URI. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--from alias|name|#|URI Select packages from the specified repository only. This option can be used multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
source-download [OPTIONS] Download source rpms for all installed packages to a local directory.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --directory dir Download all source rpms to this directory. Default is /var/cache/zypper/source-download.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--delete Delete extraneous source rpms in the local directory. This is the default.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-delete Do not delete extraneous source rpms.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--status Don’t download any source rpms, but show which source rpms are missing or extraneous.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
ps [OPTIONS] After each upgrade or removal of packages, there may be running processes on the system which continue to use meanwhile deleted files. zypper ps lists all processes using deleted files, together with the corresponding files, and a service name hint, in case it’s a known service. This gives a hint which services may need to be restarted after an update. Usually programs which continue to use deleted shared libraries. The list contains the following information:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
PID ID of the process&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
PPID ID of the parent process&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
UID ID of the user running the process&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Login Login name of the user running the process&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Command Command used to execute the process&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Service Service name, if command is associated with a system service&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Files The list of the deleted files&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-s, --short Create a short table not showing the deleted files. Given twice, show only processes which are associated with a system service. Given three times, list the associated system service names only.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--print format For each associated system service print format on the standard output, followed by a newline. Any %s directive in format is replaced by the system service name.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-d, --debugFile filename Output a file with all proc entries that make it into the final set of used open files. This can be submitted as additional information in a bug report.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Examples:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper ps -ss Show only processes associated with a system service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper ps -sss Short for zypper ps --print &amp;amp;quot;%s&amp;amp;quot;; list services which might need a restart.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
$ zypper ps --print &amp;amp;quot;systemctl status %s&amp;amp;quot; Let zypper print the commands to retrieve status information for services which might need a restart.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
needs-rebooting Checks if the reboot-needed flag was set by a previous update or install of a core library or service.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The reboot-needed flag is set if a package that provides installhint(reboot-needed) is updated or installed. Additionally there is a predefined list (/etc/zypp/needreboot) of well-known packages which cause the reboot-needed flag being set unconditionally. The exit code ZYPPER_EXIT_INF_REBOOT_NEEDED indicates that a reboot is suggested, otherwise the exit code is set to ZYPPER_EXIT_OK.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
It is recommended for scripts to use this command to test whether a system reboot is suggested. Use --quiet to suppress the normal output.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Subcommands subcommand Lists available subcommands in /usr/lib/zypper/commands and from elsewhere on your $PATH. See section SUBCOMMANDS for details.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
GLOBAL OPTIONS -h, --help Help. If a command is specified together with --help option, command specific help is displayed.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-V, --version Print zypper version number and exit.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-c, --config file Use the specified zypper config file instead of the default zypper.conf. Other command line options specified together with --config and having their counterpart in the zypper config file are still preferred.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The order of preference with --config is as follows:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
1. Command line options&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
2. --config file&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
3. [/etc/zypp/zypp.conf] (system-wide defaults for all libzypp based applications)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Use and location of the system-wide /etc/zypp/zypp.conf can not be changed this way. It’s mentioned here just because some zypper command line options allow one to overwrite system-wide defaults defined in zypp.conf.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
See also FILES section for more information.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-v, --verbose Increase verbosity. For debugging output specify this option twice. zypper -vv ref for example will show the mirrors used to download the metadata.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-q, --quiet Suppress normal output. Brief (esp. result notification) messages and error messages will still be printed, though. If used together with conflicting --verbose option, the --verbose option takes preference.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--color, --no-color Whether to use colors in output if tty supports it. For details see the [color] section in zypper.conf.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-A, --no-abbrev Do not abbreviate text in tables. By default zypper will try to abbreviate texts in some columns so that the table fits the width of the screen. If you need to see the whole text, use this option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-t, --terse Terse output for machine consumption. Implies --no-abbrev and --no-color.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-s, --table-style integer Choose among different predefined line drawing character sets to use when drawing a table. The table style is identified by an integer number. Style 0 is the default, styles 1-9 use combinations of different box drawing characters whose shape may depend on the font the terminal is using. Style 10 separates columns by a colon and style 11 draws no lines at all.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-n, --non-interactive Switches to non-interactive mode. In this mode zypper doesn’t ask user to type answers to various prompts, but uses default answers automatically. Those default answers also depend on other options like --no-gpg-checks or --ignore-unknown.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--non-interactive-include-reboot-patches In non-interactive mode do not skip patches which have the rebootSuggested-flag set. Otherwise these patches are considered to be interactive, like patches including a licenses or some message to confirm. NOTE: This option does not turn on non-interactive mode.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-x, --xmlout Switches to XML output. This option is useful for scripts or graphical frontends using zypper.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-i, --ignore-unknown Ignore unknown packages. This option is useful for scripts, because when installing in --non-interactive mode zypper expects each command line argument to match at least one known package. Unknown names or globbing expressions with no match are treated as an error unless this option is used.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
For search and info commands the option makes zypper return ZYPPER_EXIT_OK rather than ZYPPER_EXIT_INF_CAP_NOT_FOUND if the query did not produce at least one match.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-D, --reposd-dir dir Use the specified directory to look for the repository definition (.repo) files. The default value is /etc/zypp/repos.d.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-C, --cache-dir dir Use an alternative root directory for all caches. The default value is /var/cache/zypp.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--raw-cache-dir dir Use the specified directory for storing raw copies of repository metadata files. The default value is /var/cache/zypp/raw.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--solv-cache-dir dir Use the specified directory to store the repository metadata cache database files (solv files). The default value is /var/cache/zypp/solv.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--pkg-cache-dir dir Use the specified directory for storing rpm packages downloaded from repositories (see addrepo --keep-packages). The default value is /var/cache/zypp/packages. + Packages are stored in subdirectories named after the repositories alias and using the same path as on the repositories medium.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--userdata string User data is expected to be a simple string without special chars or embedded newlines and may serve as transaction id. It will be written to all install history log entries created throughout this specific zypper call. It will also be passed on to zypp plugins executed during commit. This will enable e.g. a btrfs plugin to tag created snapshots with this string. For zypper itself this string has no special meaning.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Repository Options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-gpg-checks Ignore GPG check failures and continue. If a GPG issue occurs when using this option zypper prints and logs a warning and automatically continues without interrupting the operation. Use this option with caution, as you can easily overlook security problems by using it. (see section GPG checks)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--gpg-auto-import-keys If new repository signing key is found, do not ask what to do; trust and import it automatically. This option causes that the new key is imported also in non-interactive mode, where it would otherwise got rejected.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-p, --plus-repo URI Use an additional repository for this operation. The repository aliased tmp# and named by the specified URI will be added for this operation and removed at the end. You can specify this option multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--plus-content tag Additionally use disabled repositories denoted by tag for this operation. If tag matches a repositories alias, name or URL, or is a keyword defined in the repositories metadata, the repository will be temporarily enabled for this operation. The repository will then be refreshed and used according to the commands rules. You can specify this option multiple times.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
If a disabled repositories metadata are not available in the local cache, they will be downloaded to scan for matching keywords. Otherwise the keyword scan will use the metadata available in the local cache. Only if used together with the refresh command, a keyword scan will refresh all disabled repositories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To refresh all disabled repositories metadata: zypper --plus-content &#039;&#039; ref&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To include a disabled repository repo-debug in a search: zypper --plus-content repo-debug search ...&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To search only in a disabled repository repo-debug: zypper --plus-content repo-debug search -r repo-debug ...&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To enable all repos providing the debug keyword: zypper in --plus-content debug some -debuginfo or -debugsource package&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--disable-repositories Do not read metadata from repositories. This option will prevent loading of packages from repositories, thus making zypper work only with the installed packages (if --disable-system-resolvables was not specified).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-refresh Do not auto-refresh repositories (ignore the auto-refresh setting). Useful to save time when doing operations like search, if there is not a need to have a completely up to date metadata.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-cd Ignore CD/DVD repositories. When this option is specified, zypper acts as if the CD/DVD repositories were not defined at all.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--no-remote Ignore remote repositories like http, ftp, smb and similar. This makes using zypper easier when being offline. When this option is specified, zypper acts as if the remote repositories were not defined at all.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--releasever version For the current command set the value of the $releasever repository variable to version. This can be used to switch to new distribution repositories when performing a distribution upgrade. See the dist-upgrade (dup) command and section Repository Management for more details about using the $releasever repository variable.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
To check where you already use $releasever call: zypper --releasever @--HERE--@ lr -u&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Target Options:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
-R, --root dir Operates on a different root directory. This option influences the location of the repos.d directory and the metadata cache directory and also causes rpm to be run with the --root option to do the actual installation or removal of packages. See also the FILES section.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--installroot dir Behaves like --root but shares the repositories with the host system.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
--disable-system-resolvables This option serves mainly for testing purposes. It will cause zypper to act as if there were no packages installed in the system. Use with caution as you can damage your system using this option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
SUBCOMMANDS Zypper subcommands are inspired by git(1). Subcommands are standalone executables that live in the zypper_execdir (/usr/lib/zypper/commands). For subcommands zypper provides a wrapper that knows where the subcommands live, and runs them by passing command options and arguments to them. If a subcommand is not found in the zypper_execdir, the wrapper will look in the rest of your $PATH for it. Thus, it’s possible to write local zypper extensions that don’t live in system space. This can be disabled by setting &amp;amp;quot;subcommand.seachSubcommandInPath&amp;amp;quot; to &amp;amp;quot;no&amp;amp;quot; in the zypper.conf.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This is how to add your own subcommand zypper mytask:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• The executable must be named zypper-mytask.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• The executable must be located your $PATH.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• A manpage for zypper-mytask should be provided and explaining the commands options and return values. It will be shown when calling zypper help mytask.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• Zypper built-in commands take precedence over subcommands with the same name.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
• It’s fine to call zypper or use libzypp from within your subcommand.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
You can use the built-in zypper subcommand command to get a list of all subcommands in zypper_execdir and from elsewhere on your $PATH.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Using zypper global-options together with subcommands, as well as executing subcommands in zypper shell is currently not supported.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
FILES /etc/zypp/zypper.conf, $HOME/.zypper.conf Global (system-wide) and user’s configuration file for zypper. These files are read when zypper starts up and --config option is not used.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
User’s settings are preferred over global settings. Similarly, command line options override the settings in either of these files. To sum it up, the order of preference is as follows (from highest to lowest):&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
1. Command line options&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
2. $HOME/.zypper.conf&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
3. /etc/zypp/zypper.conf&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
4. [/etc/zypp/zypp.conf] (system-wide defaults for all libzypp based applications)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
See the comments in /etc/zypp/zypper.conf for a list and description of available options.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Note&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
The system-wide /etc/zypp/zypp.conf is mentioned here just because some zypper command line options allow one to overwrite system-wide defaults defined there. zypp.conf and zypper.conf have different content and serve different purpose.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/etc/zypp/zypp.conf ZYpp configuration file affecting all libzypp based applications. See the comments in the file for description of configurable properties. Many locations of files and directories listed in this section are configurable via zypp.conf. The location for this file itself can be redefined only by setting $ZYPP_CONF in the environment.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/etc/zypp/locks File with package lock definitions. The package lock commands (locks, addlock, removelock, etc.) should be used to manipulate this file.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This file is used by all ZYpp-based applications.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/etc/zypp/repos.d Directory containing repository definition (*.repo) files. You can use the Repository Management commands to manipulate these files, or you can edit them yourself. In either case, after doing the modifications, executing *zypper refresh* is strongly recommended.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
You can use the --reposd-dir global option to use an alternative directory for this purpose or the --root option to make this directory relative to the specified root directory.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This directory is used by all ZYpp-based applications.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/etc/zypp/services.d Directory containing service definition (*.service) files. You can use the Service Management Commands to manipulate these files, or you can edit them yourself. Running *zypper refs* is recommended after modifications have been done.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This directory is used by all ZYpp-based applications.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/usr/lib/zypper/commands System directory containing zypper extensions (see section SUBCOMMANDS)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/var/cache/zypp/raw Directory for storing raw metadata contained in repositories. Use the --raw-cache-dir global option to use an alternative directory for this purpose or the --root option to make this directory relative to the specified root directory.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This directory is used by all ZYpp-based applications.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/var/cache/zypp/solv Directory containing preparsed metadata in form of solv files.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This directory is used by all ZYpp-based applications.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/var/cache/zypp/packages If the keep-packages property is set for a repository (see the modifyrepo command), all the RPM file downloaded during installation will be kept here. Packages are stored in subdirectories named after their repositories alias.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Subdirectories of removed or otherwise unknown repositories are cleaned automatically. This auto-cleanup can be prevented by creating a file named .no_auto_prune in the pkg-cache directory.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Creating a .keep_packages file in the pkg-cache directory will keep downloaded packages for all repos cached here, overwriting the repositories keep-packages property.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
See also the clean command for cleaning these cache directories.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
This directory is used by all ZYpp-based applications.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/var/log/zypper.log Zypper log file. It should be attached to all bugreports. (see also zypper-log(8)).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/var/log/zypper.solverTestCase Solver testcase created by using the --debug-solver option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/var/log/updateTestcase-YYYY-MM-DD-hh-mm-ss Solver testcase auto created when performing a zypper dup.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/var/log/zypp/history Installation history log.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
~/.zypper_history Command history for the zypper shell (see the shell command).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/etc/zypp/needreboot File with a list of packages that will set the reboot-needed flag when installed or upgraded.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
/etc/zypp/needreboot.d Directory that can be used to define packages that trigger the reboot-needed flag by adding additional files containing the required package names.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
EXIT CODES There are several exit codes defined for zypper built-in commands for use e.g. within scripts. These codes are defined in header file src/zypper-main.h found in zypper source package. Codes below 100 denote an error, codes above 100 provide a specific information, 0 represents a normal successful run. Following is a list of these codes with descriptions:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
0 - ZYPPER_EXIT_OK Successful run of zypper with no special info.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
1 - ZYPPER_EXIT_ERR_BUG Unexpected situation occurred, probably caused by a bug.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
2 - ZYPPER_EXIT_ERR_SYNTAX zypper was invoked with an invalid command or option, or a bad syntax.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
3 - ZYPPER_EXIT_ERR_INVALID_ARGS Some of provided arguments were invalid. E.g. an invalid URI was provided to the addrepo command.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
4 - ZYPPER_EXIT_ERR_ZYPP A problem is reported by ZYPP library.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
5 - ZYPPER_EXIT_ERR_PRIVILEGES User invoking zypper has insufficient privileges for specified operation.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
6 - ZYPPER_EXIT_NO_REPOS No repositories are defined.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
7 - ZYPPER_EXIT_ZYPP_LOCKED The ZYPP library is locked, e.g. packagekit is running.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
8 - ZYPPER_EXIT_ERR_COMMIT An error occurred during installation or removal of packages. You may run zypper verify to repair any dependency problems.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
100 - ZYPPER_EXIT_INF_UPDATE_NEEDED Returned by the patch-check command if there are patches available for installation.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
101 - ZYPPER_EXIT_INF_SEC_UPDATE_NEEDED Returned by the patch-check command if there are security patches available for installation.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
102 - ZYPPER_EXIT_INF_REBOOT_NEEDED Returned by the needs-rebooting command, if a system reboot is suggested.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Legacy: Returned after the successful installation of a patch which requires reboot of computer. This legacy behavior is kept, but it’s drawback is that it covers patches only, no packages. The new needs-rebooting command is the recommended way to test whether a system reboot is suggested.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
103 - ZYPPER_EXIT_INF_RESTART_NEEDED Returned after a successful installation of a patch which requires restart of the package manager itself. This means that one of patches to be installed affects the package manager itself and the command used (e.g. zypper update) needs to be executed once again to install any remaining patches.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
104 - ZYPPER_EXIT_INF_CAP_NOT_FOUND Returned by the install and the remove command in case any of the arguments does not match any of the available (or installed) package names or other capabilities. In search and info commands it indicates that no match was found. See also the --ignore-unknown global option.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
105 - ZYPPER_EXIT_ON_SIGNAL Returned upon exiting after receiving a SIGINT or SIGTERM.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
106 - ZYPPER_EXIT_INF_REPOS_SKIPPED Some repository had to be disabled temporarily because it failed to refresh. You should check your repository configuration (e.g. zypper ref -f).&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
107 - ZYPPER_EXIT_INF_RPM_SCRIPT_FAILED Installation basically succeeded, but some of the packages %post install scripts returned an error. These packages were successfully unpacked to disk and are registered in the rpm database, but due to the failed install script they may not work as expected. The failed scripts output might reveal what actually went wrong. Any scripts output is also logged to /var/log/zypp/history.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Zypper subcommands (see section SUBCOMMANDS) may return different codes which should be described in the commands man page. Call zypper help subcommand to see the subcommands man page if one is provided.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
HOMEPAGE https://github.com/openSUSE/zypper&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
AUTHORS The zypper project was started by Martin Vidner, Jan Kupec, Michael Andres, Duncan Mac-Vicar Prett, Josef Reidinger and Stanislav Visnovsky. Many people have later contributed to it.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
SEE ALSO locks(5), zypper-log(8), YaST2(8)&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
SUSE Linux 2026-03-06 ZYPPER(8)&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Paketmanager_zypper&amp;diff=4903</id>
		<title>Paketmanager zypper</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Paketmanager_zypper&amp;diff=4903"/>
		<updated>2026-05-17T17:01:10Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Die Seite wurde neu angelegt: „=Allgemeines= Sammlung meiner oft benutzten zypper-Kommandos   zypper lr -d # list repos with details man sieht so auch die Pfade   zypper -v install --force &amp;lt;package&amp;gt; # ueberinstallieren   zypper search-packages &amp;lt;package&amp;gt;   zypper mr --enable &amp;lt;#&amp;gt;   =Manpage=  ZYPPER(8) ZYPPER ZYPPER(8)  NAME zypper - Command-line interface to ZYpp system management library (libzypp)  SYNOPSIS zypper [--global-opts] command [--command-opts] [command-arguments]  zypper sub…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Sammlung meiner oft benutzten zypper-Kommandos&lt;br /&gt;
&lt;br /&gt;
 zypper lr -d # list repos with details man sieht so auch die Pfade&lt;br /&gt;
&lt;br /&gt;
 zypper -v install --force &amp;lt;package&amp;gt; # ueberinstallieren&lt;br /&gt;
&lt;br /&gt;
 zypper search-packages &amp;lt;package&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 zypper mr --enable &amp;lt;#&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=Manpage=&lt;br /&gt;
&lt;br /&gt;
ZYPPER(8) ZYPPER ZYPPER(8)&lt;br /&gt;
&lt;br /&gt;
NAME zypper - Command-line interface to ZYpp system management library (libzypp)&lt;br /&gt;
&lt;br /&gt;
SYNOPSIS zypper [--global-opts] command [--command-opts] [command-arguments]&lt;br /&gt;
&lt;br /&gt;
zypper subcommand [--command-opts] [command-arguments]&lt;br /&gt;
&lt;br /&gt;
zypper help command&lt;br /&gt;
&lt;br /&gt;
DESCRIPTION zypper is a command-line interface to ZYpp system management library (libzypp). It can be used to install, update, remove software, manage repositories, perform various queries, and more.&lt;br /&gt;
&lt;br /&gt;
CONCEPTS Most of the following concepts are common for all applications based on the libzypp package management library, but there are some zypper specifics.&lt;br /&gt;
&lt;br /&gt;
System Packages The set of installed packages on a system is sometimes denoted as repository @System or System Packages. In contrast to available repositories providing packages which can be installed, @System provides packages which can only be deleted. Installed packages which are not provided by at least one of the available repositories (neither in different version or as renamed package) are often denoted as being unwanted, orphaned or dropped.&lt;br /&gt;
&lt;br /&gt;
Note: If you install packages which are not shipped within a repository - manually built or downloaded ones -, collect them within a local directory and add this directory as a plaindir repository to your system. This way the packages will not be seen as being orphaned and are protected during a dist-upgrade.&lt;br /&gt;
&lt;br /&gt;
Creating such a plaindir repo is quite easy: $ mkdir /LocalRepo $ zypper addrepo --refresh /LocalRepo LocalRepo&lt;br /&gt;
&lt;br /&gt;
If you need to make sure the package to install is taken from LocalRepo, simply use a REPO:PACKAGE style argument: $ cp example.rpm /LocalRepo $ zypper install LocalRepo:example&lt;br /&gt;
&lt;br /&gt;
Depending on the kind of packages you keep in a local repo you may want to adjust the way GPG signature checks are performed. See section GPG checks for details.&lt;br /&gt;
&lt;br /&gt;
Repositories Libzypp works with repository metadata, this is information about packages and their relations extracted from RPM packages and other data like patch information, pattern definitions, etc. These data are stored together with the RPM files in folders called repositories. Repositories can be placed on various media like an HTTP or FTP server, DVD, or a folder on a local disc.&lt;br /&gt;
&lt;br /&gt;
There is a special set of commands in zypper intended to manipulate repositories. Also many commands and options take a repository as an argument. See section COMMANDS, subsection Repository Management for more details.&lt;br /&gt;
&lt;br /&gt;
GPG checks Disabling GPG checks is not recommended. Signing data enables the recipient to verify that no modifications occurred after the data were signed. Accepting data with no, wrong or unknown signature can lead to a corrupted system and in extreme cases even to a system compromise.&lt;br /&gt;
&lt;br /&gt;
Zypp verifies the authenticity of repository metadata by checking their GPG signature. If the repository metadata are signed with a trusted key and successfully verified, packages from this repository are accepted for installation if they match the checksum provided in the metadata. Using unsigned repositories needs to be confirmed.&lt;br /&gt;
&lt;br /&gt;
If the repository metadata are not signed, the GPG signature of each downloaded rpm package is checked before accepting it for installation. Packages from unsigned repositories need a valid GPG signature. Using unsigned packages needs to be confirmed.&lt;br /&gt;
&lt;br /&gt;
The above is the default behavior defined by settings in /etc/zypp/zypp.conf.&lt;br /&gt;
&lt;br /&gt;
The addrepo and modifyrepo commands provide further options to tune the behavior per repository. It is for example possible to relax the need to confirm installing unsigned packages for a specific repository. But if you do so, you should be very certain that an attacker can hardly modify the package data within the repository or on the way to your machine. See section COMMANDS for details about the command options.&lt;br /&gt;
&lt;br /&gt;
Resource Identifiers (URI) To specify locations of repositories or other resources (RPM files, .repo files) you can use any type of URI supported by libzypp. In addition Zypper accepts a special URI identifying openSUSE Build Service (OBS) repositories in the addrepo command. These URIs have the form of obs://project/[platform].&lt;br /&gt;
&lt;br /&gt;
See section COMMANDS, subsection Repository Management for a complete list and examples of supported URI formats.&lt;br /&gt;
&lt;br /&gt;
Refresh Refreshing a repository means downloading metadata of packages from the medium (if needed), storing it in local cache (typically under /var/cache/zypp/raw/alias directory) and preparsing the metadata into .solv files (building the solv cache), typically under /var/cache/zypp/solv/alias.&lt;br /&gt;
&lt;br /&gt;
The metadata get refreshed either automatically or on user request. An automatic refresh takes place right before reading metadata from the database if the auto-refresh is enabled for the repository and the metadata is reported to be out of date. If the auto-refresh is disabled, the repository will only be refreshed on user request. You can request a refresh by calling zypper refresh (see the documentation of the refresh command for details).&lt;br /&gt;
&lt;br /&gt;
The repository metadata are checked for changes before actually doing the refresh. A change is detected by downloading one or two metadata index files (small files) and comparing the checksums of the cached ones and the remote ones. If the files differ, the repository is out of date and will be refreshed.&lt;br /&gt;
&lt;br /&gt;
To delay the up-to-date check (and thus the automatic refresh) for a certain number of minutes, edit the value of the repo.refresh.delay attribute of ZYpp config file (/etc/zypp/zypp.conf). This means, zypper will not even try to download and check the index files, and you will be able to use zypper for operations like search or info without internet access or root privileges.&lt;br /&gt;
&lt;br /&gt;
HINT: zypper -vv ref will show the mirrors used to download the metadata.&lt;br /&gt;
&lt;br /&gt;
Services Services are one level above repositories and serve to manage repositories or to do some special tasks. Libzypp currently supports Repository Index Service (RIS) and Plugin Service.&lt;br /&gt;
&lt;br /&gt;
Repository Index Service (RIS) is a special type of repository which contains a list of other repositories. This list can be generated dynamically by the server according to some URI parameters or user name, or can be static. Once such service is added to your system, zypper takes care of adding, modifying, or removing these repositories on your system to reflect the current list. See section Service Management and https://en.opensuse.org/openSUSE:Standards_Repository_Index_Service for more details.&lt;br /&gt;
&lt;br /&gt;
Package Types Zypper works with several types of resource objects, called resolvables. A resolvable might be a package, patch, pattern, product; basically any kind of object with dependencies to other objects.&lt;br /&gt;
&lt;br /&gt;
package An ordinary RPM package.&lt;br /&gt;
&lt;br /&gt;
patch A released patch conflicts with the affected/vulnerable versions of a collection of packages. As long as any of these affected/vulnerable versions are installed, the conflict triggers and the patch is classified as needed, or as unwanted if the patch is locked.&lt;br /&gt;
&lt;br /&gt;
Selecting the patch, the conflict is resolved by updating all installed and affected/vulnerable packages to a version providing the fix. When updating the packages zypper always aims for the latest available version. Resolved patches are classified as either applied or not needed, depending on whether they refer to actually installed packages.&lt;br /&gt;
&lt;br /&gt;
So installation, update or removal of packages may change the classification of patches referring to these packages. Since libyzpp-17.23.0 the /var/log/zypp/history remembers if a committed transaction changes a patchs classification. If history data are available, patch tables show a column telling since when the patch is in it’s current state.&lt;br /&gt;
&lt;br /&gt;
Depending on the kind of defect, patches are classified by category and severity. Commonly used values for category are security, (recommended, bugfix), (optional, feature, enhancement) document or yast. Commonly used values for severity are critical, important, moderate, low or unspecified. Names listed in parentheses are used synonymously.&lt;br /&gt;
&lt;br /&gt;
Note that the patch command does not apply optional patches (category optional or feature) by default. If you actually want to consider all optional patches as being needed, say patch --with-optional. Specific patches can be applied using the install command (e.g. zypper install patch:openSUSE-2014-7).&lt;br /&gt;
&lt;br /&gt;
If the issuer decides to retract a released patch, the patch status will be shown as retracted. The packages provided by the retracted patch are still visible but also tagged as having been retracted (R). The resolver will avoid selecting retracted packages automatically. If you are sure that a retracted package should be installed on your system, you must explicitly select it.&lt;br /&gt;
&lt;br /&gt;
pattern A group of packages required or recommended to install some functionality.&lt;br /&gt;
&lt;br /&gt;
product A group of packages which are necessary to install a product.&lt;br /&gt;
&lt;br /&gt;
srcpackage Source code package (.src.rpm). This type works in search and install commands.&lt;br /&gt;
&lt;br /&gt;
application Legacy: Since libzypp-17.7.0 this type is no longer available.&lt;br /&gt;
&lt;br /&gt;
Throughout this manual we will often refer to resolvables simply as packages and to resolvable types as package type or kind. These type names can be used as arguments of --type option in several commands like install, info, or search. Commands should also allow one to specify resolvables as KIND:NAME (e.g. patch:openSUSE-2014-7).&lt;br /&gt;
&lt;br /&gt;
Package Dependencies Software packages depend on each other in various ways. Packages usually require or recommend other packages, but they can also conflict with them. Packages may support specific hardware or language settings. Zypper uses a dependency solver to find out which packages need to be installed to satisfy the user’s request.&lt;br /&gt;
&lt;br /&gt;
If you do not request a specific version of a package the solver will pick a reasonable one. The solvers general attitude when resolving a job is to focus on installing the best version of the requested package and to add or update dependencies as they are needed. Aside from this Focus on Job, which is the default, two other focus modes are available:&lt;br /&gt;
&lt;br /&gt;
In Focus on Installed mode the solver focuses on applying as little changes to the installed packages as needed. Choosing an older version of a requested package is valid if it’s dependencies require less changes to the system. The solver will try to avoid updating already installed packages.&lt;br /&gt;
&lt;br /&gt;
In Focus on Update mode the solver focuses on updating the requested package and all its dependencies as much as possible. Beware, installing a single package in this mode may easily lead to a mini system update.&lt;br /&gt;
&lt;br /&gt;
For a single command the focus mode can be set using the --solver-focus MODE switch. Valid modes are Job, Installed or Update. If you want to change the default mode for your system, set [/etc/zypp/zypp.conf:solver.focus] to the desired value.&lt;br /&gt;
&lt;br /&gt;
Automatically installed packages Packages added by the dependency solver in order to resolve a user’s request are remembered as having been automatically installed. They may later be removed, if no more user installed packages depend on them (e.g. by zypper remove --clean-deps).&lt;br /&gt;
&lt;br /&gt;
In the Status column the search command distinguishes between user installed packages (i+) and automatically installed packages (i).&lt;br /&gt;
&lt;br /&gt;
Package File Conflicts File conflicts happen when two packages attempt to install files with the same name but different contents. This may happen if you are installing a newer version of a package without erasing the older version, of if two unrelated packages each install a file with the same name.&lt;br /&gt;
&lt;br /&gt;
As checking for file conflicts requires access to the full filelist of each package being installed, zypper will be able to check for file conflicts only if all packages are downloaded in advance (see --download-in-advance). If you are doing a --dry-run no packages are downloaded, so the file conflict check will skip packages not available in the packages cache. To get a meaningful file conflict check use --dry-run together with --download-only.&lt;br /&gt;
&lt;br /&gt;
As the reason for file conflicts usually is a poor package design or lack of coordination between the people building the packages, they are not easy to resolve. By using the --replacefiles option you can force zypper to replace the conflicting files. Nevertheless this may damage the package whose file gets replaced.&lt;br /&gt;
&lt;br /&gt;
COMMANDS zypper provides a number of commands. Each command accepts the options listed in the GLOBAL OPTIONS section. These options must be specified before the command name. In addition, many commands have specific options, which are listed in this section. These command-specific options must be specified after the name of the command and before any of the command arguments.&lt;br /&gt;
&lt;br /&gt;
Zypper also provides limited support for writing extensions/subcommands in any language. See section SUBCOMMANDS for details.&lt;br /&gt;
&lt;br /&gt;
General Commands help [command] Shows help texts. If invoked without any argument (just zypper or zypper help), zypper displays global help text which lists all available global options and commands.&lt;br /&gt;
&lt;br /&gt;
If invoked with a command name argument, zypper displays help for the specified command, if such command exists. Long as well as short variants of the command names can be used.&lt;br /&gt;
&lt;br /&gt;
For your convenience, zypper help can also be invoked in any of the following ways:&lt;br /&gt;
&lt;br /&gt;
$ zypper -h|--help [command]&lt;br /&gt;
&lt;br /&gt;
$ zypper [command] -h|--help&lt;br /&gt;
&lt;br /&gt;
shell (sh) Starts a shell for entering multiple commands in one session. Exit the shell using exit, quit, or Ctrl-D.&lt;br /&gt;
&lt;br /&gt;
The shell support is not complete so expect bugs there. However, there’s no urgent need to use the shell since libzypp became so fast thanks to the SAT solver and its tools (openSUSE 11.0), but still, you’re welcome to experiment with it.&lt;br /&gt;
&lt;br /&gt;
Package Management Commands info (if) [options] name[-version[-release]]... Show detailed information for specified packages. By default, only packages that exactly match the provided names are shown. To include packages that partially match, use the --match-substrings option or wildcards (* or ?) within the name.&lt;br /&gt;
&lt;br /&gt;
If no version constraint is specified, information about the best available package is shown. Note that both the version and release numbers must always match exactly.&lt;br /&gt;
&lt;br /&gt;
If a query returns no results, zypper returns ZYPPER_EXIT_INF_CAP_NOT_FOUND unless the global option --ignore-unknown is set.&lt;br /&gt;
&lt;br /&gt;
-s, --match-substrings Include packages that partially match the provided names.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
-t, --type type Type of package (default: package). See section Package Types for list of available package types.&lt;br /&gt;
&lt;br /&gt;
--provides Show symbols the package provides.&lt;br /&gt;
&lt;br /&gt;
--requires Show symbols the package requires.&lt;br /&gt;
&lt;br /&gt;
--conflicts Show symbols the package conflicts with.&lt;br /&gt;
&lt;br /&gt;
--obsoletes Show symbols the package obsoletes.&lt;br /&gt;
&lt;br /&gt;
--recommends Show symbols the package recommends.&lt;br /&gt;
&lt;br /&gt;
--suggests Show symbols the package suggests.&lt;br /&gt;
&lt;br /&gt;
--supplements Show symbols the package supplements.&lt;br /&gt;
&lt;br /&gt;
--enhances Show symbols the package enhances.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper info workrave Show information about package workrave&lt;br /&gt;
&lt;br /&gt;
$ zypper info -t patch libzypp Show information about patch libzypp&lt;br /&gt;
&lt;br /&gt;
$ zypper info -t pattern lamp_server Show information about pattern lamp_server&lt;br /&gt;
&lt;br /&gt;
install (in) [options] name|capability|rpm_file_uri... Install or update packages.&lt;br /&gt;
&lt;br /&gt;
The packages can be selected by their name or by a capability they provide.&lt;br /&gt;
&lt;br /&gt;
A capability is formed by &amp;amp;quot;NAME[.ARCH][ OP EDITION]&amp;amp;quot;, where ARCH is an architecture code, OP is one of &amp;amp;lt;, &amp;amp;lt;=, =, &amp;amp;gt;=, or &amp;amp;gt; and EDITION is &amp;amp;quot;VERSION[-RELEASE]&amp;amp;quot;. For example: zypper=0.8.8-2.&lt;br /&gt;
&lt;br /&gt;
The NAME component of a capability is not only a package name but any symbol provided by packages: /bin/vi, libcurl.so.3, perl(Time::ParseDate). Just remember to quote to protect the special characters from the shell, for example: zypper&amp;amp;gt;0.8.10 or &#039;zypper&amp;amp;gt;0.8.10&#039;.&lt;br /&gt;
&lt;br /&gt;
If EDITION is not specified, the newest installable version will be installed. This also means that if the package is already installed and newer versions are available, it will get upgraded to the newest installable version.&lt;br /&gt;
&lt;br /&gt;
If ARCH is not specified, or the last dot of the capability name string is not followed by known architecture, the solver will treat the whole string as a capability name. If the ARCH is known, the solver will select a package matching that architecture and complain if such package cannot be found.&lt;br /&gt;
&lt;br /&gt;
If you want to make sure a package from a specific REPOSITORY is picked, use REPOSITORY:PACKAGENAME as argument.&lt;br /&gt;
&lt;br /&gt;
Zypper is also able to install plain RPM files while trying to satisfy their dependencies using packages from defined repositories. You can install a plain RPM file by specifying its location in the install command arguments either as a local path or an URI. E.g.:&lt;br /&gt;
&lt;br /&gt;
$ zypper install ~/rpms/foo.rpm http://some.site/bar.rpm&lt;br /&gt;
&lt;br /&gt;
Zypper will report packages that it cannot find. Further, in interactive mode, zypper proceeds with installation of the rest of requested packages, and it will abort immediately in non-interactive mode. In both cases zypper returns ZYPPER_EXIT_INF_CAP_NOT_FOUND after finishing the operation.&lt;br /&gt;
&lt;br /&gt;
Zypper will collect the files in a temporary plaindir repository and mark the respective packages for installation. If --download-only is used, the downloaded packages will be available in /var/cache/zypper/RPMS until you actually install them or call zypper clean to clear the package caches. They will not become part of the global package cache at /var/cache/zypp/packages (see also the global --pkg-cache-dir option).&lt;br /&gt;
&lt;br /&gt;
In the install command, you can also specify packages you wish to remove by prepending their names by a - or ! character. For example:&lt;br /&gt;
&lt;br /&gt;
$ zypper install !Firefox&lt;br /&gt;
&lt;br /&gt;
In contrast to zypper remove Firefox which removes Firefox and its dependent packages, the install command will try to keep dependent packages installed by looking for Firefox alternatives.&lt;br /&gt;
&lt;br /&gt;
Note that if you choose to use - with the first package you specify, you need to write -- before it to prevent its interpretation as a command option:&lt;br /&gt;
&lt;br /&gt;
$ zypper install -- -boring-game great-game great-game-manual&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
Using --repo is discouraged as it currently hides unmentioned repositories from the resolver, leading to inexpertly decisions. In the future --repo will become an alias for --from.&lt;br /&gt;
&lt;br /&gt;
If you want to install a package from a specific repository, use REPOSITORY:PACKAGENAME as argument.&lt;br /&gt;
&lt;br /&gt;
-t, --type type Type of package to install (default: package). See section Package Types for list of available package types. Use zypper se -t type [name] to look for available items of this type and zypper info -t type name to display more detailed information about the item.&lt;br /&gt;
&lt;br /&gt;
If patch is specified, zypper will install and/or remove packages to satisfy specified patch. This is a way to ensure that specific bug fix is installed. Use zypper list-patches to look for applicable patches.&lt;br /&gt;
&lt;br /&gt;
If product or pattern are specified, zypper ensures that all required (and optionally recommended) packages are installed.&lt;br /&gt;
&lt;br /&gt;
-n, --name Select packages by their name, don’t try to select by capabilities.&lt;br /&gt;
&lt;br /&gt;
-f, --force Install even if the item is already installed (reinstall), downgraded or changes vendor or architecture.&lt;br /&gt;
&lt;br /&gt;
--oldpackage Allows one to replace a newer item with an older one. Handy if you are doing a rollback. Unlike --force it will not enforce a reinstall, if the item is already installed with the requested version.&lt;br /&gt;
&lt;br /&gt;
--from alias|name|#|URI Select packages from specified repository. If strings specified as arguments to the install command match packages in repositories specified in this option, they will be marked for installation. This option currently implies --name, but allows using wildcards for specifying packages.&lt;br /&gt;
&lt;br /&gt;
-C, --capability Select packages by capabilities.&lt;br /&gt;
&lt;br /&gt;
-l, --auto-agree-with-licenses Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install. This option is particularly useful for administrators installing the same set of packages on multiple machines (by an automated process) and have the licenses confirmed before.&lt;br /&gt;
&lt;br /&gt;
--auto-agree-with-product-licenses Automatically accept product licenses only. This is used by tools like SUSEconnect, which ask for confirmation before the product gets registered. So there’s no need to confirm the product license again at install time.&lt;br /&gt;
&lt;br /&gt;
--replacefiles Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error. --download-as-needed disables the file conflict check because access to all packages file lists is needed in advance in order to perform the check.&lt;br /&gt;
&lt;br /&gt;
-D, --dry-run Test the installation, do not actually install any package. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&lt;br /&gt;
&lt;br /&gt;
--details Show the detailed installation summary.&lt;br /&gt;
&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&lt;br /&gt;
&lt;br /&gt;
--allow-unsigned-rpm Silently install unsigned .rpm packages given as commandline parameters. The option does not affect unsigned packages provided by repositories. To allow a repository to provide unsigned packages without explicit confirmation, set the repositories --gpgcheck-allow-unsigned-package option. See the addrepo and modifyrepo commands for details.&lt;br /&gt;
&lt;br /&gt;
Solver related options:&lt;br /&gt;
&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&lt;br /&gt;
&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&lt;br /&gt;
&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&lt;br /&gt;
&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&lt;br /&gt;
&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
Download-and-install mode options:&lt;br /&gt;
&lt;br /&gt;
-d, --download-only Only download the packages for later installation (see also the global --pkg-cache-dir option).&lt;br /&gt;
&lt;br /&gt;
If used together with --dry-run a meaningful file conflict check can be performed (see section Package File Conflicts).&lt;br /&gt;
&lt;br /&gt;
--download-in-advance First download all packages, then start installing. This is the default.&lt;br /&gt;
&lt;br /&gt;
--download-in-heaps Download a minimal set of packages that can be installed without leaving the system in broken state, and install them. Then download and install another heap until all are installed. This helps to keep the system in consistent state without the need to download all packages in advance, which combines the advantages of --download-in-advance and --download-as-needed.&lt;br /&gt;
&lt;br /&gt;
Note&lt;br /&gt;
&lt;br /&gt;
While the resolver is not capable of building heaps, this behaves the same as --download-in-advance.&lt;br /&gt;
&lt;br /&gt;
--download-as-needed Download one package, install it immediately, and continue with the rest until all are installed.&lt;br /&gt;
&lt;br /&gt;
--download mode Use the specified download-and-install mode. Available modes are: only, in-advance, in-heaps, as-needed. See corresponding --download-mode options for their description.&lt;br /&gt;
&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&lt;br /&gt;
&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&lt;br /&gt;
&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper install -t pattern lamp_server Install lamp_server pattern.&lt;br /&gt;
&lt;br /&gt;
$ zypper install --no-recommends gv Install GhostScript viewer, but ignore recommended packages.&lt;br /&gt;
&lt;br /&gt;
$ zypper install virtualbox-ose-2.0.6&lt;br /&gt;
&lt;br /&gt;
$ zypper install virtualbox-ose=2.0.6&lt;br /&gt;
&lt;br /&gt;
$ zypper install virtualbox-ose = 2.0.6 Install version 2.0.6 of virtualbox-ose package.&lt;br /&gt;
&lt;br /&gt;
source-install (si) name... Install specified source packages and their build dependencies. If the name of a binary package is given, the corresponding source package is looked up and installed instead.&lt;br /&gt;
&lt;br /&gt;
This command will try to find the newest available versions of the source packages and uses rpm -i to install them, optionally together with all the packages that are required to build the source package. The default location where rpm installs source packages to is /usr/src/packages/{SPECS,SOURCES}, but the values can be changed in your local rpm configuration. In case of doubt try executing rpm --eval &amp;amp;quot;%{_specdir} and %{_sourcedir}&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that the source packages must be available in repositories you are using. You can check whether a repository contains any source packages using the following command:&lt;br /&gt;
&lt;br /&gt;
$ zypper search -t srcpackage -r alias|name|#|URI&lt;br /&gt;
&lt;br /&gt;
$ zypper search -t srcpackage -r alias|name|#|URI&lt;br /&gt;
&lt;br /&gt;
-d, --build-deps-only Install only build dependencies of specified packages.&lt;br /&gt;
&lt;br /&gt;
-D, --no-build-deps Don’t install build dependencies.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
--download-only Only download the packages, do not install.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper si -d dbus-1 Install build dependencies of dbus-1 source package.&lt;br /&gt;
&lt;br /&gt;
verify (ve) [options] Check whether dependencies of installed packages are satisfied.&lt;br /&gt;
&lt;br /&gt;
In case that any dependency problems are found, zypper suggests packages to install or remove to fix them.&lt;br /&gt;
&lt;br /&gt;
-D, --dry-run Test the repair, do not actually do anything to the system. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&lt;br /&gt;
&lt;br /&gt;
--details Show the detailed installation summary.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&lt;br /&gt;
&lt;br /&gt;
Solver related options:&lt;br /&gt;
&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&lt;br /&gt;
&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&lt;br /&gt;
&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&lt;br /&gt;
&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&lt;br /&gt;
&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&lt;br /&gt;
&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&lt;br /&gt;
&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&lt;br /&gt;
&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command.&lt;br /&gt;
&lt;br /&gt;
install-new-recommends (inr) [options] Install newly added packages recommended by already installed ones. This command basically re-evaluates the recommendations of all installed packages and fills up the system accordingly. You don’t want to call this unconditionally on small or minimal systems, as it may easily add a large number of packages.&lt;br /&gt;
&lt;br /&gt;
Called as zypper inr --no-recommends, it restricts the command to just look for packages supporting available hardware, languages or filesystems. Useful after having added e.g. new hardware or driver repos. This is also the default behavior if you have set [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
-D, --dry-run Test the installation, do not actually install anything. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&lt;br /&gt;
&lt;br /&gt;
--details Show the detailed installation summary.&lt;br /&gt;
&lt;br /&gt;
Solver related options:&lt;br /&gt;
&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&lt;br /&gt;
&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&lt;br /&gt;
&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&lt;br /&gt;
&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&lt;br /&gt;
&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&lt;br /&gt;
&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&lt;br /&gt;
&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&lt;br /&gt;
&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command.&lt;br /&gt;
&lt;br /&gt;
remove (rm) [options] name...&lt;br /&gt;
&lt;br /&gt;
remove (rm) [options] --capability capability... Remove (uninstall) packages.&lt;br /&gt;
&lt;br /&gt;
The remove command will uninstall the selected and their dependent packages. It will not try to install alternatives in order to keep dependent packages installed. If you want this, use zypper install !name.&lt;br /&gt;
&lt;br /&gt;
The packages can be selected by their name or by a capability they provide. For details on package selection see the install command description.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
-t, --type type Type of package (default: package). See section Package Types for list of available package types.&lt;br /&gt;
&lt;br /&gt;
Since patches are not installed in sense of copying files or recording a database entry, they cannot be uninstalled, even though zypper shows them as installed. The installed status is determined solely based on the installed status of its required dependencies. If these dependencies are satisfied, the patch is rendered installed.&lt;br /&gt;
&lt;br /&gt;
-n, --name Select packages by their name (default).&lt;br /&gt;
&lt;br /&gt;
-C, --capability Select packages by capabilities.&lt;br /&gt;
&lt;br /&gt;
-D, --dry-run Test the removal of packages, do not actually remove anything.&lt;br /&gt;
&lt;br /&gt;
--details Show the detailed installation summary.&lt;br /&gt;
&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&lt;br /&gt;
&lt;br /&gt;
Solver related options:&lt;br /&gt;
&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&lt;br /&gt;
&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&lt;br /&gt;
&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&lt;br /&gt;
&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&lt;br /&gt;
&lt;br /&gt;
-u, --clean-deps Automatically remove dependencies which become unneeded after removal of requested packages.&lt;br /&gt;
&lt;br /&gt;
-U, --no-clean-deps No automatic removal of unneeded dependencies.&lt;br /&gt;
&lt;br /&gt;
removeptf (rmptf) [options] &amp;amp;lt;PTF|CAPABILITY&amp;amp;gt; ... Remove (not only) PTFs.&lt;br /&gt;
&lt;br /&gt;
A remove command which prefers replacing dependant packages to removing them as well. In fact this is a full featured install command with the remove modifier (-) applied to its plain arguments. So removeptf foo is the same as install — -foo. This is why the command accepts the same options as the install command. It is the recommended way to remove a PTF (Program Temporary Fix).&lt;br /&gt;
&lt;br /&gt;
A PTF is typically removed as soon as the fix it provides is applied to the latest official update of the dependant packages. But you don’t want the dependant packages to be removed together with the PTF, which is what the remove command would do. The removeptf command however will aim to replace the dependant packages by their official update versions.&lt;br /&gt;
&lt;br /&gt;
supports every option the install command supports&lt;br /&gt;
&lt;br /&gt;
purge-kernels [options] Autoremoves installed kernels.&lt;br /&gt;
&lt;br /&gt;
Automatically cleans up installed kernels according to the rules defined in [zypp.conf:multiversion.kernels] which can be given as &amp;amp;lt;version&amp;amp;gt;, latest[-N], running, oldest[+N].&lt;br /&gt;
&lt;br /&gt;
--details Show the detailed installation summary.&lt;br /&gt;
&lt;br /&gt;
-D, --dry-run Test the removal of packages, do not actually remove anything.&lt;br /&gt;
&lt;br /&gt;
Update Management Commands list-updates (lu) [options] List available updates.&lt;br /&gt;
&lt;br /&gt;
This command will list only installable updates, i.e. updates which have no dependency problems, or which do not change package vendor. This list is what the update command will propose to install. To list all packages for which newer version are available, use --all option.&lt;br /&gt;
&lt;br /&gt;
-t, --type type Type of package (default: package). See section Package Types for list of available package types.&lt;br /&gt;
&lt;br /&gt;
If patch is specified, zypper acts as if the list-patches command was executed.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
-a, --all List all packages for which newer versions are available, regardless whether they are installable or not.&lt;br /&gt;
&lt;br /&gt;
--best-effort See the update command for description.&lt;br /&gt;
&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&lt;br /&gt;
&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&lt;br /&gt;
&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&lt;br /&gt;
&lt;br /&gt;
update (up) [options] [packagename]... Update installed packages with newer versions, where possible.&lt;br /&gt;
&lt;br /&gt;
This command will not update packages which would require change of package vendor unless the vendor is specified in /etc/zypp/vendors.d, or which would require manual resolution of problems with dependencies. Such non-installable updates will then be listed in separate section of the summary as &amp;amp;quot;The following package updates will NOT be installed:&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To update individual packages, specify one or more package names. You can use the * and ? wildcard characters in the package names to specify multiple packages matching the pattern.&lt;br /&gt;
&lt;br /&gt;
-t, --type type Type of package (default: package). See section Package Types for list of available package types.&lt;br /&gt;
&lt;br /&gt;
If patch is specified, zypper acts as if the patches command was executed.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
--skip-interactive This will skip interactive patches, that is, those that need reboot, contain a message, or update a package whose license needs to be confirmed.&lt;br /&gt;
&lt;br /&gt;
--with-interactive Avoid skipping of interactive patches when in non-interactive mode.&lt;br /&gt;
&lt;br /&gt;
-l, --auto-agree-with-licenses Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install. This option is particularly useful for administrators installing the same set of packages on multiple machines (by an automated process) and have the licenses confirmed before.&lt;br /&gt;
&lt;br /&gt;
--auto-agree-with-product-licenses Automatically accept product licenses only. This is used by tools like SUSEconnect, which ask for confirmation before the product gets registered. So there’s no need to confirm the product license again at install time.&lt;br /&gt;
&lt;br /&gt;
--replacefiles Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error. --download-as-needed disables the fileconflict check because access to all packages filelists is needed in advance in order to perform the check.&lt;br /&gt;
&lt;br /&gt;
-D, --dry-run Test the update, do not actually install or update any package. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&lt;br /&gt;
&lt;br /&gt;
--details Show the detailed installation summary.&lt;br /&gt;
&lt;br /&gt;
--best-effort Do a best effort approach to update. This method does not explicitly select packages with best version and architecture, but instead requests installation of a package with higher version than the installed one and leaves the rest on the dependency solver. This method is always used for packages, and is optional for products and patterns. It is not applicable to patches.&lt;br /&gt;
&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&lt;br /&gt;
&lt;br /&gt;
Solver related options:&lt;br /&gt;
&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&lt;br /&gt;
&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&lt;br /&gt;
&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&lt;br /&gt;
&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&lt;br /&gt;
&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&lt;br /&gt;
&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&lt;br /&gt;
&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&lt;br /&gt;
&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command description.&lt;br /&gt;
&lt;br /&gt;
list-patches (lp) [options] List all applicable patches.&lt;br /&gt;
&lt;br /&gt;
This command is similar to zypper list-updates -t patch.&lt;br /&gt;
&lt;br /&gt;
Note that optional arguments of some of the following options must be specified using = instead of a space.&lt;br /&gt;
&lt;br /&gt;
-b, --bugzilla[=#[,...]] List applicable patches for all Bugzilla issues, or issues whose number matches the given string.&lt;br /&gt;
&lt;br /&gt;
--cve[=#[,...]] List applicable patches for all CVE issues, or issues whose number matches the given string.&lt;br /&gt;
&lt;br /&gt;
--date YYYY-MM-DD[,...] List only patches issued up to, but not including, the specified date.&lt;br /&gt;
&lt;br /&gt;
-g, --category category[,...] List only patches with this category. See section Package Types for a list of commonly used category values.&lt;br /&gt;
&lt;br /&gt;
--severity severity[,...] List only patches with this severity. See section Package Types for a list of commonly used severity values.&lt;br /&gt;
&lt;br /&gt;
--issue[=string[,...]] Look for issues whose number, summary, or description matches the specified string. Issues found by number are displayed separately from those found by descriptions. In the latter case, use zypper patch-info patchname to get information about issues the patch fixes.&lt;br /&gt;
&lt;br /&gt;
-a, *--all By default, only patches that are applicable on your system are listed. This option causes all available released patches to be listed. This option can be combined with all the rest of the list-updates command options.&lt;br /&gt;
&lt;br /&gt;
--with-optional, --without-optional Whether applicable optional patches should be treated as needed or be excluded. The default is to exclude optional patches.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
patch-check (pchk) Check for patches. Displays a count of applicable patches and how many of them have the security category.&lt;br /&gt;
&lt;br /&gt;
See also the EXIT CODES section for details on exit status of 0, 100, and 101 returned by this command.&lt;br /&gt;
&lt;br /&gt;
--updatestack-only Check only for patches which affect the package management itself.&lt;br /&gt;
&lt;br /&gt;
--with-optional, --without-optional Whether applicable optional patches should be treated as needed or be excluded. The default is to exclude optional patches.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Check for patches only in the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
patch [options] Install all available needed patches.&lt;br /&gt;
&lt;br /&gt;
When updating the affected/vulnerable packages described by a patch, zypper always aims for the latest available version. See section Package Types for more details about how patches operate.&lt;br /&gt;
&lt;br /&gt;
If there are patches that affect the package management itself, those will be installed first and you will be asked to run the patch command again.&lt;br /&gt;
&lt;br /&gt;
This command is similar to zypper update -t patch.&lt;br /&gt;
&lt;br /&gt;
--updatestack-only Install only patches which affect the package management itself and exit.&lt;br /&gt;
&lt;br /&gt;
--skip-not-applicable-patches Skip needed patches which do not apply without conflict. In normal operation mode those conflicts have to be resolved interactively, otherwise the patch command fails.&lt;br /&gt;
&lt;br /&gt;
In scripted and unattended environments it may be desired to try to apply at least as many patches as possible so the system is not left completely without updates. It is highly recommended to run zypper patch-check afterwards in order to see whether needed patches are still waiting to be resolved interactively.&lt;br /&gt;
&lt;br /&gt;
--with-update Additionally try to update all packages not covered by patches. This is basically the same as running zypper update afterwards.&lt;br /&gt;
&lt;br /&gt;
The option is ignored, if the patch command must update the update stack first, thus it can not be combined with the --updatestack-only option.&lt;br /&gt;
&lt;br /&gt;
--with-optional, --without-optional Whether applicable optional patches should be treated as needed or be excluded. The default is to exclude optional patches.&lt;br /&gt;
&lt;br /&gt;
-b, --bugzilla #[,...] Select applicable patches for a Bugzilla issue specified by number. Use list-patches --bugzilla command to get a list of applicable patches for specific issues.&lt;br /&gt;
&lt;br /&gt;
--cve #[,...] Select applicable patches for a MITRE’s CVE issue specified by number. Use list-patches --cve command to get a list of applicable patches for specific issues.&lt;br /&gt;
&lt;br /&gt;
--date YYYY-MM-DD[,...] Select only patches patches issued up to, but not including, the specified date.&lt;br /&gt;
&lt;br /&gt;
-g, --category category[,...] Select only patches with this category. Use list-patches --category command to get a list of available patches with a specific category. See section Package Types for a list of commonly used category values.&lt;br /&gt;
&lt;br /&gt;
--severity severity[,...] Select only patches with this severity. Use list-patches --severity command to get a list of available patches with a specific severity. See section Package Types for a list of commonly used severity values.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
--skip-interactive This will skip interactive patches, that is, those that need reboot, contain a message, or update a package whose license needs to be confirmed.&lt;br /&gt;
&lt;br /&gt;
--with-interactive Avoid skipping of interactive patches when in non-interactive mode.&lt;br /&gt;
&lt;br /&gt;
-l, --auto-agree-with-licenses Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install. This option is particularly useful for administrators installing the same set of packages on multiple machines (by an automated process) and have the licenses confirmed before.&lt;br /&gt;
&lt;br /&gt;
--auto-agree-with-product-licenses Automatically accept product licenses only. This is used by tools like SUSEconnect, which ask for confirmation before the product gets registered. So there’s no need to confirm the product license again at install time.&lt;br /&gt;
&lt;br /&gt;
--replacefiles Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error. --download-as-needed disables the fileconflict check because access to all packages filelists is needed in advance in order to perform the check.&lt;br /&gt;
&lt;br /&gt;
-D, --dry-run Test the update, do not actually update. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&lt;br /&gt;
&lt;br /&gt;
--details Show the detailed installation summary.&lt;br /&gt;
&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&lt;br /&gt;
&lt;br /&gt;
Solver related options:&lt;br /&gt;
&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&lt;br /&gt;
&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&lt;br /&gt;
&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&lt;br /&gt;
&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&lt;br /&gt;
&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&lt;br /&gt;
&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&lt;br /&gt;
&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&lt;br /&gt;
&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command description.&lt;br /&gt;
&lt;br /&gt;
dist-upgrade (dup) [options] Perform a distribution upgrade. This command applies the state of (specified) repositories onto the system; upgrades (or even downgrades) installed packages to versions found in repositories, removes packages that are no longer in the repositories and pose a dependency problem for the upgrade, handles package splits and renames, etc.&lt;br /&gt;
&lt;br /&gt;
If no repositories are specified via the --from option, zypper will do a global upgrade with all defined repositories. This global form of dup will also consider unchanged installed packages and re-evaluate their dependencies. This can be a problem if the system contains conflicting repositories, like repositories for two different distribution releases. This often happens if one forgets to remove an older release repository after adding a new one, say openSUSE 13.1 and openSUSE 13.2.&lt;br /&gt;
&lt;br /&gt;
For all repositories which have the distribution version within their URL (like https://download.opensuse.org/distribution/13.1/repo/oss) using the $releasever variable instead may be helpful (https://download.opensuse.org/distribution/$releasever/repo/oss). The variable is per default substituted by the current distributions version (13.1).&lt;br /&gt;
&lt;br /&gt;
This value can be temporarily overwritten in the current zypper command by using the --releasever global option. Calling zypper --releasever 13.2... will cause these repos to use the new location (https://download.opensuse.org/distribution/13.2/repo/oss) without the need to add/remove anything. But you’ll need to use --releasever 13.2 with every zypper command until the distribution upgrade was actually performed. Once the dup is done, $releasever will default to the new distribution version 13.2 and --releasever is no longer needed.&lt;br /&gt;
&lt;br /&gt;
It might be less tedious to persistently set $releasever to the target distribution value, so --releasever is not needed at all. See section Repository Management for more info about variable substitution and the definition of custom variables.&lt;br /&gt;
&lt;br /&gt;
Note: Performing a distribution upgrade will automatically create a solver test case at /var/log/updateTestcase-YYYY-MM-DD-hh-mm-ss (the date and time the command was executed).&lt;br /&gt;
&lt;br /&gt;
Note: distribution upgrades in openSUSE are currently only supported between consecutive releases. To upgrade multiple releases, upgrade each consecutive release one at a time. For more details see http://en.opensuse.org/SDB:System_upgrade and the openSUSE release notes at http://doc.opensuse.org/release-notes/.&lt;br /&gt;
&lt;br /&gt;
Note: Due to the treatment of orphaned packages dist-upgrade depends on a proper repository setup more than any other command. It must not continue if enabled repositories fail to refresh. This may severely damage the system. If a failing repository is actually not needed, it must be disabled.&lt;br /&gt;
&lt;br /&gt;
--from alias|name|#|URI The option can be used multiple times and restricts the upgrade to the specified repositories only. Nevertheless all enabled repositories are visible to the resolver and will be considered to satisfy dependency problems.&lt;br /&gt;
&lt;br /&gt;
--remove-orphaned Remove orphaned packages. Installed packages which are not provided by at least one of the available repositories are considered to be orphaned or dropped. dist-upgrade removes orphaned packages if they prevent the upgrade of wanted packages. With this option all unneeded orphaned packages are removed.&lt;br /&gt;
&lt;br /&gt;
&#039;Note:&#039; Packages which are not shipped within a repository - manually built or downloaded ones -, can be collected in a plaindir repository to prevent them from being treated as orphaned. See section *System Packages* for details.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI.&lt;br /&gt;
&lt;br /&gt;
Using --repo is discouraged as it currently hides unmentioned repositories from the resolver, leading to inexpertly decisions. This is because packages originally installed from the hidden repos will now be treated as orphaned or dropped. They can be silently removed if involved in a dependency conflict. In the future --repo will become an alias for --from.&lt;br /&gt;
&lt;br /&gt;
-l, --auto-agree-with-licenses Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install. This option is particularly useful for administrators installing the same set of packages on multiple machines (by an automated process) and have the licenses confirmed before.&lt;br /&gt;
&lt;br /&gt;
--auto-agree-with-product-licenses Automatically accept product licenses only. This is used by tools like SUSEconnect, which ask for confirmation before the product gets registered. So there’s no need to confirm the product license again at install time.&lt;br /&gt;
&lt;br /&gt;
--replacefiles Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error. --download-as-needed disables the fileconflict check because access to all packages filelists is needed in advance in order to perform the check.&lt;br /&gt;
&lt;br /&gt;
-D, --dry-run Test the upgrade, do not actually install or update any package. If used together with --download-only a meaningful file conflict check can be performed (see section Package File Conflicts).&lt;br /&gt;
&lt;br /&gt;
-y, --no-confirm Don’t require user interaction. It’s recommended to use the --non-interactive global option instead. Global options are passed before the command (zypper --non-interactive COMMAND ...). Unlike the no-confirm command option, the global option can be used together with any zypper command.&lt;br /&gt;
&lt;br /&gt;
--details Show the detailed installation summary.&lt;br /&gt;
&lt;br /&gt;
Solver related options:&lt;br /&gt;
&lt;br /&gt;
--debug-solver Create solver test case for debugging. Use this option if you think the dependencies were not solved correctly. When using this option no packages will be installed or removed. Instead a solver test case is written to /var/log/zypper.solverTestCase. You can pack the directory and attach it to your bug report.&lt;br /&gt;
&lt;br /&gt;
--force-resolution Force the solver to find a solution (even an aggressive one) rather than asking.&lt;br /&gt;
&lt;br /&gt;
In order to perform the requested job the solver is allowed to violate any otherwise active policy. This includes the allowance to remove packages but also not to respect even explicitly set policies (by --no-allow-policy or in config files). It is not recommended to use this option in unattended environments.&lt;br /&gt;
&lt;br /&gt;
The allowance to remove dependent packages is the default when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
-R, --no-force-resolution Do not force the solver to find a solution. Instead, report dependency problems and prompt the user to resolve them manually. This is the default except when removing packages (zypper remove).&lt;br /&gt;
&lt;br /&gt;
--solver-focus MODE Set the solvers general attitude when resolving a job. Valid modes are Job, Installed or Update. See section Package Dependencies for details.&lt;br /&gt;
&lt;br /&gt;
--recommends Install also recommended packages in addition to the required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
--no-recommends Do not install recommended packages, but only required ones. The default behavior is determined by [zypp.conf:solver.onlyRequires].&lt;br /&gt;
&lt;br /&gt;
Expert Options: Don’t use them unless you know you need them.&lt;br /&gt;
&lt;br /&gt;
--allow-downgrade, --no-allow-downgrade Whether to allow downgrading installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-name-change, --no-allow-name-change Whether to allow changing the names of installed resolvables. Setting this to no will not replace packages which have been renamed.&lt;br /&gt;
&lt;br /&gt;
--allow-arch-change, --no-allow-arch-change Whether to allow changing the architecture of installed resolvables.&lt;br /&gt;
&lt;br /&gt;
--allow-vendor-change, --no-allow-vendor-change Whether to allow changing the vendor of installed resolvables. Setting this to no might be useful if you do not want packages from foreign repos being changed to the distributions version (or vice versa).&lt;br /&gt;
&lt;br /&gt;
This command also accepts the Download-and-install mode options described in the install command description.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper dup --from factory --from packman Upgrade the system to the latest versions provided by the factory and packman repositories.&lt;br /&gt;
&lt;br /&gt;
Query Commands search (se) [options] [querystring|capability]... Search for packages matching any of the given search strings. * and ? wildcard characters can be used within search strings. If the search string is enclosed in / (e.g. /^k.*e$/) it’s interpreted as a regular expression. See the install command for details about how to specify a capability.&lt;br /&gt;
&lt;br /&gt;
If the search string starts with a /, a filename is assumed and the search will automatically look into the file list of packages. Otherwise use -f to search in the packages file lists as well.&lt;br /&gt;
&lt;br /&gt;
Results of the search are printed in a table with columns Status, Name, Summary and Type of package. In case the query result is empty zypper returns ZYPPER_EXIT_INF_CAP_NOT_FOUND, unless the --ignore-unknown global option is set.&lt;br /&gt;
&lt;br /&gt;
In the detailed view (se -s) all available instances of matching packages are shown; each version in each repository on a separate line, with columns Status, Name, Type, Version, Architecture and Repository. For installed packages Repository shows either a repository that provides exactly the installed version of the package, or, if the exact version is not provided by any known repo, (System Packages) (or @System). Those installed packages not provided by any repo are often denoted as being unwanted, orphaned or dropped.&lt;br /&gt;
&lt;br /&gt;
The Status column can contain the following values:&lt;br /&gt;
&lt;br /&gt;
i+ installed by user request&lt;br /&gt;
&lt;br /&gt;
i installed automatically (by the resolver, see section Automatically installed packages)&lt;br /&gt;
&lt;br /&gt;
v a different version is installed&lt;br /&gt;
&lt;br /&gt;
empty neither of the above cases&lt;br /&gt;
&lt;br /&gt;
! a patch in needed state&lt;br /&gt;
&lt;br /&gt;
.l is shown in the 2nd column if the item is locked (see section Package Locks Management)&lt;br /&gt;
&lt;br /&gt;
.P is shown in the 2nd column if the item is part of a PTF (A program temporary fix which must be explicitly selected and will otherwise not be considered in dependency resolution).&lt;br /&gt;
&lt;br /&gt;
.R is shown in the 2nd column if the item has been retracted (see patch in section Package Types)&lt;br /&gt;
&lt;br /&gt;
The v status is only shown if the version or the repository matters (see --details or --repo), and the installed instance differs from the one listed in version or repository.&lt;br /&gt;
&lt;br /&gt;
The verbose view (se -v; implies -s) will also show the matches themselves within the packages metadata.&lt;br /&gt;
&lt;br /&gt;
This command accepts the following options:&lt;br /&gt;
&lt;br /&gt;
--match-substrings Matches for search strings may be partial words (default).&lt;br /&gt;
&lt;br /&gt;
--match-words Matches for search strings may only be whole words.&lt;br /&gt;
&lt;br /&gt;
-x, --match-exact Searches for an exact name of the package.&lt;br /&gt;
&lt;br /&gt;
--provides Search for packages which provide the search strings.&lt;br /&gt;
&lt;br /&gt;
--requires Search for packages which require the search strings.&lt;br /&gt;
&lt;br /&gt;
--recommends Search for packages which recommend the search strings.&lt;br /&gt;
&lt;br /&gt;
--suggests Search for packages which suggest the search strings.&lt;br /&gt;
&lt;br /&gt;
--conflicts Search for packages conflicting with the search strings.&lt;br /&gt;
&lt;br /&gt;
--obsoletes Search for packages which obsolete the search strings.&lt;br /&gt;
&lt;br /&gt;
--supplements Search for packages which supplement the search strings.&lt;br /&gt;
&lt;br /&gt;
--enhances Search for packages which enhances the search strings.&lt;br /&gt;
&lt;br /&gt;
--provides-pkg Search for all packages that provide any of the provides of the package(s) matched by the input parameters.&lt;br /&gt;
&lt;br /&gt;
--requires-pkg Search for all packages that require any of the provides of the package(s) matched by the input parameters.&lt;br /&gt;
&lt;br /&gt;
--recommends-pkg Search for all packages that recommend any of the provides of the package(s) matched by the input parameters.&lt;br /&gt;
&lt;br /&gt;
--supplements-pkg Search for all packages that supplement any of the provides of the package(s) matched by the input parameters.&lt;br /&gt;
&lt;br /&gt;
--conflicts-pkg Search for all packages that conflict with any of the package(s) matched by the input parameters.&lt;br /&gt;
&lt;br /&gt;
--obsoletes-pkg Search for all packages that obsolete any of the package(s) matched by the input parameters.&lt;br /&gt;
&lt;br /&gt;
--suggests-pkg Search for all packages that suggest any of the provides of the package(s) matched by the input parameters.&lt;br /&gt;
&lt;br /&gt;
--enhances-pkg Search for all packages that enhance any of the provides of the package(s) matched by the input parameters.&lt;br /&gt;
&lt;br /&gt;
-n, --name Useful together with dependency options, otherwise searching in package name is default.&lt;br /&gt;
&lt;br /&gt;
-f, --file-list Search in the file list of packages. Note that the full file list is available for installed packages only. For remote packages only an abstract of their file list is available within the metadata (files containing /etc/, /bin/, or /sbin/).&lt;br /&gt;
&lt;br /&gt;
-d, --search-descriptions Search also in summaries and descriptions.&lt;br /&gt;
&lt;br /&gt;
-C, --case-sensitive Perform case-sensitive search.&lt;br /&gt;
&lt;br /&gt;
-i, --installed-only Show only installed packages.&lt;br /&gt;
&lt;br /&gt;
-u, --not-installed-only Show only packages which are not installed.&lt;br /&gt;
&lt;br /&gt;
The old option name --uninstalled-only is still acceptable, but should be considered deprecated.&lt;br /&gt;
&lt;br /&gt;
-t, --type type Search only for packages of specified type. See section Package Types for a list of available package types. Multiple --type options are allowed.&lt;br /&gt;
&lt;br /&gt;
See also the type-specific query commands like packages, patterns, etc.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number, or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
--sort-by-name Sort packages by name (default).&lt;br /&gt;
&lt;br /&gt;
--sort-by-repo Sort packages by repository, not by name.&lt;br /&gt;
&lt;br /&gt;
-s, --details Show all available versions of matching packages, each version in each repository on a separate line.&lt;br /&gt;
&lt;br /&gt;
-v, --verbose Like --details with additional information where the search has matched (useful when searching for dependencies, e.g. --provides).&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper se &#039;yast*&#039; Search for YaST packages (quote the string to prevent the shell from expanding the wildcard).&lt;br /&gt;
&lt;br /&gt;
$ zypper se -s --match-exact kernel-default Show all available versions of package kernel-default&lt;br /&gt;
&lt;br /&gt;
$ zypper se -dC --match-words RSI Look for RSI acronym (case-sensitively), also in summaries and descriptions.&lt;br /&gt;
&lt;br /&gt;
packages (pa) [options] [repository]... List all available packages or all packages from specified repositories. Similar to zypper search -s -t package.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Just another means to specify repositories.&lt;br /&gt;
&lt;br /&gt;
-i, --installed-only Show only installed packages.&lt;br /&gt;
&lt;br /&gt;
-u, --not-installed-only Show only packages which are not installed.&lt;br /&gt;
&lt;br /&gt;
The old option name --uninstalled-only is still acceptable, but should be considered deprecated.&lt;br /&gt;
&lt;br /&gt;
*--autoinstalled Show installed packages which were automatically selected by the resolver.&lt;br /&gt;
&lt;br /&gt;
--userinstalled Show installed packages which were explicitly selected by the user.&lt;br /&gt;
&lt;br /&gt;
--system Show installed packages which are not provided by any repository.&lt;br /&gt;
&lt;br /&gt;
--orphaned Show system packages which are orphaned (without repository and without update candidate). If combined with --system, the status of orphaned packages is tagged with (o).&lt;br /&gt;
&lt;br /&gt;
--suggested Show packages which are suggested.&lt;br /&gt;
&lt;br /&gt;
--recommended Show packages which are recommended.&lt;br /&gt;
&lt;br /&gt;
--unneeded Show packages which are unneeded.&lt;br /&gt;
&lt;br /&gt;
patches (pch) [options] [repository]... List all available patches from specified repositories, including those not needed. Short for zypper lp -a.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name_|#|URI Just another means to specify repositories.&lt;br /&gt;
&lt;br /&gt;
patterns (pt) [options] [repository]... List all available patterns or all patterns from specified repositories. Similar to zypper search -s -t pattern.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Just another means to specify repositories.&lt;br /&gt;
&lt;br /&gt;
-i, --installed-only Show only installed patterns.&lt;br /&gt;
&lt;br /&gt;
-u, --not-installed-only Show only patterns which are not installed.&lt;br /&gt;
&lt;br /&gt;
The old option name --uninstalled-only is still acceptable, but should be considered deprecated.&lt;br /&gt;
&lt;br /&gt;
products (pd) [options] [repository]... List all available products or all products from specified repositories. Similar to zypper search -s -t product, but shows also the type of the product (base, add-on).&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Just another means to specify repositories.&lt;br /&gt;
&lt;br /&gt;
-i, --installed-only Show only installed products.&lt;br /&gt;
&lt;br /&gt;
-u, --not-installed-only Show only products which are not installed.&lt;br /&gt;
&lt;br /&gt;
The old option name --uninstalled-only is still acceptable, but should be considered deprecated.&lt;br /&gt;
&lt;br /&gt;
--xmlfwd tag XML output only: Literally forward the XML tag, if it is found in an installed products .prod-file (in /etc/products.d).&lt;br /&gt;
&lt;br /&gt;
Using this option, for each installed product an &amp;amp;lt;xmlfwd&amp;amp;gt; node will be created inside the &amp;amp;lt;product&amp;amp;gt; output node of the product.&lt;br /&gt;
&lt;br /&gt;
Tag defines the name (or /-separated path) of a xml-tag inside an installed products .prod-file. If the tag is present inside the products .prod-file, the tag and it’s content is literally forwarded into the products &amp;amp;lt;xmlfwd&amp;amp;gt; output node.&lt;br /&gt;
&lt;br /&gt;
The option may be specified multiple times.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper -x pd --xmlfwd name --xmlfwd register/target&lt;br /&gt;
&lt;br /&gt;
what-provides (wp) capability List all packages providing the specified capability (case-insensitive search). See also the install command for info about specifying capabilities.&lt;br /&gt;
&lt;br /&gt;
The command line is automatically transformed into the corresponding search command:&lt;br /&gt;
&lt;br /&gt;
$ zypper what-provides &#039;zypper&amp;amp;gt;1.6&#039; $ zypper search --provides --match-exact &#039;zypper&amp;amp;gt;1.6&#039;&lt;br /&gt;
&lt;br /&gt;
For a case-sensitive search call $ zypper search --provides --match-exact --case-sensitive &#039;zypper&amp;amp;gt;1.6&#039;&lt;br /&gt;
&lt;br /&gt;
Repository Management Zypper is able to work with YaST, RPM-MD (yum) software repositories, and plain directories containing .rpm files (no symlinks).&lt;br /&gt;
&lt;br /&gt;
Repositories are primarily identified using their URI or alias. Alias serves as a shorthand for the long URI or name of the repository. The name of the repository should briefly describe the repository and is shown to the user in tables and messages. The name is not required, and if not known, the alias is shown instead. The alias is required and uniquely identifies the repository on the system.&lt;br /&gt;
&lt;br /&gt;
The alias, name, URI, or the number from zypper repos list can be used to specify a repository as an argument of various zypper commands and options like refresh, --repo, or --from.&lt;br /&gt;
&lt;br /&gt;
Apart from the above, repositories have several other properties which can be set using the commands described in this section below, or by manually editing the repository definition files (.repo files, see section FILES).&lt;br /&gt;
&lt;br /&gt;
Variable substitution: You can use the following variables within a .repo or .service files name and URI values:&lt;br /&gt;
&lt;br /&gt;
$arch Use this variable to refer to the system’s CPU architecture.&lt;br /&gt;
&lt;br /&gt;
$basearch Use this variable to refer to the base architecture of the system. For example, iX86 machines have a base architecture of i386, while AMD64 and Intel64 have x86_64.&lt;br /&gt;
&lt;br /&gt;
$releasever, $releasever_major, $releasever_minor Use this variable to refer to the version of your openSUSE or SUSE Linux. The value is obtained from the /product/version XML-node in /etc/products.d/baseproduct.&lt;br /&gt;
&lt;br /&gt;
This is useful for related repositories like packman (http://ftp.gwdg.de/pub/linux/packman/suse/$releasever), which shall always fit the installed distribution, even after a distribution upgrade.&lt;br /&gt;
&lt;br /&gt;
To help performing a distribution upgrade, the value of $releasever can be persistently set to a user defined value by creating a custom variable with that name (see below). This way you can easily switch all repositories using $releasever to the new version (provided the server layouts did not change and new repos are already available).&lt;br /&gt;
&lt;br /&gt;
For a single zypper command the value of $releasever can be temporarily overwritten by using the --releasever global option.&lt;br /&gt;
&lt;br /&gt;
In addition $releasever_major will be set to the leading portion up to (but not including) the 1st dot; $releasever_minor to the trailing portion after the 1st dot. If there’s no dot in $releasever, $releasever_major is the same as $releasever and $releasever_minor is empty.&lt;br /&gt;
&lt;br /&gt;
Custom Variables A custom repository variable is defined by creating a file in /etc/zypp/vars.d. The variable name equals the file name. The files first line (up to but not including the newline character) defines the variables value. Valid variable(file) names consist of alphanumeric chars and underscore only.&lt;br /&gt;
&lt;br /&gt;
This is how you can set a custom variable, e.g. $releasever to a value of 99.0: echo &amp;amp;quot;99.0&amp;amp;quot; &amp;amp;gt;/etc/zypp/vars.d/releasever&lt;br /&gt;
&lt;br /&gt;
To remove the custom variable, simply delete its file in /etc/zypp/vars.d: rm /etc/zypp/vars.d/releasever&lt;br /&gt;
&lt;br /&gt;
To check where you already use $releasever call: zypper --releasever @--HERE--@ lr -u&lt;br /&gt;
&lt;br /&gt;
Remember to protect the $ when using these variables on a shell command line: zypper ar -f http://ftp.gwdg.de/pub/linux/packman/suse/$releasever packman&lt;br /&gt;
&lt;br /&gt;
If a variable is followed by an alphanumeric character or underscore it needs to be enclosed in {}: zypper ar -f http://ftp.gwdg.de/pub/linux/packman/suse/${releasever}_packman&lt;br /&gt;
&lt;br /&gt;
Bash style definition of default ${variable:-word} and alternate ${variable:+word} values: SLE-${releasever_major}${releasever_minor:+-SP-$releasever_minor}&lt;br /&gt;
&lt;br /&gt;
NOTE: Variable substitution within an URIs authority is limited to host and port. Bash style definition of default and alternate values is not supported. No variables can be used in an URIs scheme, user and password.&lt;br /&gt;
&lt;br /&gt;
Supported URI formats: scheme:@]host[:port]]/path[?query][#fragment] Special characters occurring in URI components (like a &#039;@&#039; in a password) must be %-encoded (&#039;%40&#039;).&lt;br /&gt;
&lt;br /&gt;
CD or DVD drive Optionally with devices list for probing.&lt;br /&gt;
&lt;br /&gt;
• cd:/// dvd:/subdir?devices=/dev/sr0,/dev/sr1&lt;br /&gt;
&lt;br /&gt;
FTP/HTTP/HTTPS directory tree The ftp URL scheme supports absolute and relative paths to the default ftp server directory (RFC1738, Section 3.2.2). To use an absolute path, you have to prepend the path with an additional slash, what results in a /%2f combination (second / encoded to %2f) at the begin of the URL path. This is important, especially in user authenticated ftp, where the users home is usually the default directory of the server (except when the server chroots into the users home directory).&lt;br /&gt;
&lt;br /&gt;
Explicit proxy settings may be passed via optional parameters proxy, proxyport, proxyuser and proxypass.&lt;br /&gt;
&lt;br /&gt;
HTTP authentication methods to use can be defined as comma separated list via optional parameter auth. Valid methods are e.g. basic, digest, ntlm, negotiate. Note, that this list depends on the list of methods supported by the curl library.&lt;br /&gt;
&lt;br /&gt;
SSL verification behavior can be changed using the ssl_verify option (this should be used with care). Valid values are yes (the secure default), host, peer or no. Host just checks that the &amp;amp;quot;Common Name&amp;amp;quot; field or a &amp;amp;quot;Subject Alternate Name&amp;amp;quot; field in the servers certificate matches the host name in the URL. Peer just verifies whether the certificate provided by the server is authentic against the chain of digital signatures found in ssl_capath. No performs no checks at all. Yes is the secure default, performing host and peer check.&lt;br /&gt;
&lt;br /&gt;
For SSL client certificate authentication use the options ssl_clientcert to define the path to the ssl client certificate and ssl_clientkey to define the path to the SSL client key. Use ssl_capath to change the directory holding the CA certificates (default is /etc/ssl/certs).&lt;br /&gt;
&lt;br /&gt;
• ftp://user:pass@server/path/to/media/dir ftp://user:pass@server/%2fhome/user/path/to/media/dir http://user:pass@server/path https://user:pass@server/path?proxy=foo&amp;amp;amp;proxyuser=me&amp;amp;amp;proxypass=pw https://server/path?ssl_clientcert=/entitlement/1234.pem&amp;amp;amp;ssl_clientkey=/entitlement/1234-key.pem&lt;br /&gt;
&lt;br /&gt;
Disk volume (partition) Mandatory device parameter specifying the name of the block device to mount. The name of the optional filesystem defaults to &amp;amp;quot;auto&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
• hd:/subdir?device=/dev/sda1&amp;amp;amp;filesystem=reiserfs&lt;br /&gt;
&lt;br /&gt;
Local directory tree&lt;br /&gt;
&lt;br /&gt;
• dir:/directory/name&lt;br /&gt;
&lt;br /&gt;
Media in an ISO image (loopback mounted) Mandatory iso parameter specifying the name of the iso file. Optional url parameter specifying the URL to the directory containing the iso file. Optional mnt parameter specifying the preferred attach point for the source media url. Optional filesystem name of the filesystem used in the iso file. Defaults to &amp;amp;quot;auto&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
• iso:/?iso=CD1.iso&amp;amp;amp;url=nfs://server/path/to/media iso:/?iso=CD1.iso&amp;amp;amp;url=hd:/?device=/dev/hda iso:/subdir?iso=DVD1.iso&amp;amp;amp;url=nfs://nfs-server/directory&amp;amp;amp;mnt=/nfs/attach/point&amp;amp;amp;filesystem=udf&lt;br /&gt;
&lt;br /&gt;
NFS exported directory tree To use NFSv4 either use schema tnfsv4:// or pass an optional parameter type=nfs4. Additional mountoptions can be passed as comma separated list. Defaults to &amp;amp;quot;ro&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
• nfs://nfs-server/exported/path nfs://nfs-server/exported/path?mountoptions=ro&amp;amp;amp;type=nfs4 nfs4://nfs-server/exported/path?mountoptions=ro&lt;br /&gt;
&lt;br /&gt;
CIFS/SMB directory tree There is no difference between cifs and smb scheme (any more). In both cases the cifs filesystem is used. Additional mountoptions can be passed as comma separated list. Defaults to &amp;amp;quot;ro,guest&amp;amp;quot;. Specify &amp;amp;quot;noguest&amp;amp;quot; to turn off &amp;amp;quot;guest&amp;amp;quot;. This is necessary if Samba is configured to reject guest connections.&lt;br /&gt;
&lt;br /&gt;
Optional workgroup or domain parameter set the name of the workgroup. As alternative to passing username:password in the URI authority the parameters user and pass can be used.&lt;br /&gt;
&lt;br /&gt;
• smb://servername/share/path/on/the/share cifs://usern:passw@servername/share/path/on/the/share?mountoptions=ro,noguest cifs://usern:passw@servername/share/path/on/the/share?workgroup=mygroup cifs://servername/share/path/on/the/share?user=usern&amp;amp;amp;pass=passw&lt;br /&gt;
&lt;br /&gt;
OpenSUSE Build Build Service (OBS) repositories Zypper also accepts special URIs identifying openSUSE Build Service (OBS) repositories in the addrepo command. These URIs have the form of obs://project/[platform], where project is the name of the OBS project and platform is the target platform (OS) for which the repository is intended.&lt;br /&gt;
&lt;br /&gt;
If platform is omitted, openSUSE_$releasever is used unless a value for obs.platform is defined in zypper.conf. If you are following openSUSE_Factory or openSUSE_Tumbleweed you may need to set these as your default platform. But we can only guess, how the directory containing the repository that fits your distribution is named on the server. In case of doubt you need to look up the right URL in a browser.&lt;br /&gt;
&lt;br /&gt;
• obs://zypp:Head/ obs://zypp:Head/openSUSE_Factory obs://zypp:Head/openSUSE_Factory_Staging_Gcc49_standard&lt;br /&gt;
&lt;br /&gt;
Commands: addrepo (ar) [options] URI alias&lt;br /&gt;
&lt;br /&gt;
addrepo (ar) [options] FILE*.repo* Add a new repository specified by URI and assign specified alias to it or specify URI to a .repo file.&lt;br /&gt;
&lt;br /&gt;
Newly added repositories have auto-refresh disabled by default (except for repositories imported from a .repo, having the auto-refresh enabled). To enable auto-refresh use addrepo -f, or the --refresh option of the modifyrepo command.&lt;br /&gt;
&lt;br /&gt;
Also, this command does not automatically refresh the newly added repositories. The repositories will get refreshed when used for the first time, or you can use the refresh command after finishing your modifications with *repo commands.&lt;br /&gt;
&lt;br /&gt;
-r, --repo file.repo Read URI and alias from specified .repo file&lt;br /&gt;
&lt;br /&gt;
-c, --check Probe given URI.&lt;br /&gt;
&lt;br /&gt;
-C, --no-check Don’t probe URI, probe later during refresh.&lt;br /&gt;
&lt;br /&gt;
-n, --name name Specify descriptive name for the repository.&lt;br /&gt;
&lt;br /&gt;
-e, --enable Enable the repository (the default).&lt;br /&gt;
&lt;br /&gt;
-d, --disable Add the repository as disabled. Repositories are added as enabled by default.&lt;br /&gt;
&lt;br /&gt;
-f, --refresh Enable autorefresh of the repository. The autorefresh is disabled by default when adding new repositories.&lt;br /&gt;
&lt;br /&gt;
-F, --no-refresh Disable auto-refresh for the repository.&lt;br /&gt;
&lt;br /&gt;
-p, --priority [1-2147483647]|0 Set the priority of the repository. Priority of 1 is the highest, and 2147483647 is the lowest. -p 0 will set the priority back to the default (99). Packages from repositories with higher priority will be used even if there are better versions available in a repository with a lower priority.&lt;br /&gt;
&lt;br /&gt;
-k, --keep-packages Enable RPM files caching for the repository.&lt;br /&gt;
&lt;br /&gt;
-K, --no-keep-packages Disable RPM files caching.&lt;br /&gt;
&lt;br /&gt;
-g, --gpgcheck Enable GPG check for this repository. The behavior as described in section GPG checks.&lt;br /&gt;
&lt;br /&gt;
--gpgcheck-strict Enable strict GPG check for this repository. Even packages from signed repositories need a valid GPG signature and using unsigned packages must be confirmed.&lt;br /&gt;
&lt;br /&gt;
--gpgcheck-allow-unsigned Short hand for --gpgcheck-allow-unsigned-repo --gpgcheck-allow-unsigned-package&lt;br /&gt;
&lt;br /&gt;
--gpgcheck-allow-unsigned-repo Enable GPG check but allow the repository metadata to be unsigned.&lt;br /&gt;
&lt;br /&gt;
--gpgcheck-allow-unsigned-package Enable GPG check but allow installing unsigned packages from this repository.&lt;br /&gt;
&lt;br /&gt;
-G, --no-gpgcheck Disable GPG check for this repository.&lt;br /&gt;
&lt;br /&gt;
Disabling GPG checks is not recommended. Signing data enables the recipient to verify that no modifications occurred after the data were signed. Accepting data with no, wrong or unknown signature can lead to a corrupted system and in extreme cases even to a system compromise.&lt;br /&gt;
&lt;br /&gt;
--default-gpgcheck Use the global GPG check settings defined in /etc/zypp/zypp.conf. This is the default.&lt;br /&gt;
&lt;br /&gt;
Unless you have modified your zypp.conf settings, this is the same as --gpgcheck, the behavior as described in section GPG checks.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper ar -c -n &#039;Packman 11.1 repo&#039; http://packman.iu-bremen.de/suse/11.1 packman Add a HTTP repository, probe it, name it Packman 11.1 repo, and use packman as alias.&lt;br /&gt;
&lt;br /&gt;
$ zypper ar https://download.opensuse.org/repositories/zypp:/svn/openSUSE_Factory/zypp:svn.repo&lt;br /&gt;
&lt;br /&gt;
$ zypper ar myreposbackup.repo Add repositories from a .repo file.&lt;br /&gt;
&lt;br /&gt;
removerepo (rr) [options] alias|name|#|URI... Delete repositories specified by aliases, names, numbers, URIs or one of the aggregate options.&lt;br /&gt;
&lt;br /&gt;
--loose-auth Ignore user authentication data in the URI&lt;br /&gt;
&lt;br /&gt;
--loose-query Ignore query string in the URI&lt;br /&gt;
&lt;br /&gt;
-a, --all Apply changes to all repositories.&lt;br /&gt;
&lt;br /&gt;
-l, --local Apply changes to all local repositories.&lt;br /&gt;
&lt;br /&gt;
-t, --remote Apply changes to all remote repositories (http/https/ftp).&lt;br /&gt;
&lt;br /&gt;
-m, --medium-type type Apply changes to repositories of specified type. The type corresponds to the repository URI scheme identifier like http, dvd, etc. You can find complete list of valid types at http://en.opensuse.org/openSUSE:Libzypp_URIs.&lt;br /&gt;
&lt;br /&gt;
repos (lr) [options] [repo]... List all defined repositories or show detailed information about those specified as arguments&lt;br /&gt;
&lt;br /&gt;
The following data can be printed for each repository found on the system: # (repository number), Alias (unique identifier), Name, Enabled (whether the repository is enabled), GPG Check (whether GPG check for repository metadata (r) and/or downloaded rpm packages (p) is enabled), Refresh (whether auto-refresh is enabled for the repository), Priority, Type (repository meta-data type: rpm-md, yast2, plaindir). Which of the data is shown is determined by command line options listed below and the main.repoListColumns setting from zypper.conf. By default, #, Alias, Name, Enabled, GPG Check and Refresh is shown.&lt;br /&gt;
&lt;br /&gt;
Repository number is a unique identifier of the repository in current set of repositories. If you add, remove or change a repository, the numbers may change. Keep that in mind when using the numbers with the repository handling commands. On the other hand, using the alias instead of the number is always safe.&lt;br /&gt;
&lt;br /&gt;
To show detailed information about specific repositories, specify them as arguments, either by alias, name, number from simple zypper lr, or by URI; e.g. fB zypper lr factory, or zypper lr 2.&lt;br /&gt;
&lt;br /&gt;
-e, --export FILE.repo|- This option causes zypper to write repository definition of all defined repositories into a single file in repo file format. If - is specified instead of a file name, the repositories will be written to the standard output.&lt;br /&gt;
&lt;br /&gt;
-a, --alias Add alias column to the output.&lt;br /&gt;
&lt;br /&gt;
-n, --name Add name column to the output.&lt;br /&gt;
&lt;br /&gt;
-u, --uri Add base URI column to the output.&lt;br /&gt;
&lt;br /&gt;
-p, --priority Add repository priority column to the output.&lt;br /&gt;
&lt;br /&gt;
-r, --refresh Add the autorefresh column to the output.&lt;br /&gt;
&lt;br /&gt;
-k, --keep-packages Add the keep-packages column to the output. The column will show a Yes if keep-packages is explicitly enabled for this repository. Otherwise a - or a + if a .keep_packages file in the pkg-cache directory exists. (see section FILES)&lt;br /&gt;
&lt;br /&gt;
-d, --details Show more information like URI, priority, type, etc.&lt;br /&gt;
&lt;br /&gt;
-E, --show-enabled-only Show enabled repositories only.&lt;br /&gt;
&lt;br /&gt;
-U, --sort-by-uri Add base URI column and sort the list it.&lt;br /&gt;
&lt;br /&gt;
-P, --sort-by-priority Add repository priority column and sort the list by it.&lt;br /&gt;
&lt;br /&gt;
-A, --sort-by-alias Sort the list by alias.&lt;br /&gt;
&lt;br /&gt;
-N, --sort-by-name Sort the list by name.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper repos -e myreposbackup.repo Backup your repository setup:&lt;br /&gt;
&lt;br /&gt;
$ zypper lr -pu List repositories with their URIs and priorities:&lt;br /&gt;
&lt;br /&gt;
renamerepo (nr) alias|name|#|URI new-alias Assign new alias to the repository specified by alias, name, number, or URI.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper nr 8 myrepo Rename repository number 8 to myrepo (useful if the repo has some dreadful alias which is not usable on the command line).&lt;br /&gt;
&lt;br /&gt;
modifyrepo (mr) options alias|name|#|URI...&lt;br /&gt;
&lt;br /&gt;
modifyrepo (mr) options --all|--remote|--local|--medium-type Modify properties of repositories specified by alias, name, number, or URI or one of the aggregate options.&lt;br /&gt;
&lt;br /&gt;
-n, --name name Set a descriptive name for the repository.&lt;br /&gt;
&lt;br /&gt;
-e, --enable Enable the repository.&lt;br /&gt;
&lt;br /&gt;
-d, --disable Disable the repository.&lt;br /&gt;
&lt;br /&gt;
-f, --refresh (legacy: -r) Enable auto-refresh for the repository.&lt;br /&gt;
&lt;br /&gt;
-F, --no-refresh (legacy: -R) Disable auto-refresh for the repository.&lt;br /&gt;
&lt;br /&gt;
-p, --priority [1-2147483647]|0 Set the priority of the repository. Priority of 1 is the highest, and 2147483647 is the lowest. -p 0 will set the priority back to the default (99). Packages from repositories with higher priority will be used even if there are better versions available in a repository with a lower priority.&lt;br /&gt;
&lt;br /&gt;
-k, --keep-packages Enable RPM files caching.&lt;br /&gt;
&lt;br /&gt;
-K, --no-keep-packages Disable RPM files caching.&lt;br /&gt;
&lt;br /&gt;
-g, --gpgcheck Enable GPG check for this repository. The behavior as described in section GPG checks.&lt;br /&gt;
&lt;br /&gt;
--gpgcheck-strict Enable strict GPG check for this repository. Even packages from signed repositories need a valid GPG signature and using unsigned packages must be confirmed.&lt;br /&gt;
&lt;br /&gt;
--gpgcheck-allow-unsigned Short hand for --gpgcheck-allow-unsigned-repo --gpgcheck-allow-unsigned-package&lt;br /&gt;
&lt;br /&gt;
--gpgcheck-allow-unsigned-repo Enable GPG check but allow the repository metadata to be unsigned.&lt;br /&gt;
&lt;br /&gt;
--gpgcheck-allow-unsigned-package Enable GPG check but allow installing unsigned packages from this repository.&lt;br /&gt;
&lt;br /&gt;
-G, --no-gpgcheck Disable GPG check for this repository.&lt;br /&gt;
&lt;br /&gt;
Disabling GPG checks is not recommended. Signing data enables the recipient to verify that no modifications occurred after the data were signed. Accepting data with no, wrong or unknown signature can lead to a corrupted system and in extreme cases even to a system compromise.&lt;br /&gt;
&lt;br /&gt;
--default-gpgcheck Use the global GPG check settings defined in /etc/zypp/zypp.conf. This is the default.&lt;br /&gt;
&lt;br /&gt;
Unless you have modified your zypp.conf settings, this is the same as --gpgcheck, the behavior as described in section GPG checks.&lt;br /&gt;
&lt;br /&gt;
-a, --all Apply changes to all repositories.&lt;br /&gt;
&lt;br /&gt;
-l, --local Apply changes to all local repositories.&lt;br /&gt;
&lt;br /&gt;
-t, --remote Apply changes to all remote repositories (http/https/ftp).&lt;br /&gt;
&lt;br /&gt;
-m, --medium-type type Apply changes to repositories of specified type. The type corresponds to the repository URI scheme identifier like http, dvd, etc. You can find complete list of valid types at http://en.opensuse.org/openSUSE:Libzypp_URIs.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper mr -kt Enable keeping of packages for all remote repositories.&lt;br /&gt;
&lt;br /&gt;
$ zypper mr -er updates Enable repository updates and switch on autorefresh for the repo.&lt;br /&gt;
&lt;br /&gt;
$ zypper mr -da Disable all repositories.&lt;br /&gt;
&lt;br /&gt;
refresh (ref) [alias|name|#|URI]... Refresh repositories specified by their alias, name, number, or URI. If no repositories are specified, all enabled repositories will be refreshed.&lt;br /&gt;
&lt;br /&gt;
-f, --force Force a complete refresh of specified repositories. This option will cause both the download of raw metadata and parsing of the metadata to be forced even if everything indicates a refresh is not needed.&lt;br /&gt;
&lt;br /&gt;
-b, --force-build Force only reparsing of cached metadata and rebuilding of the database. Raw metadata download will not be forced.&lt;br /&gt;
&lt;br /&gt;
-d, --force-download Force only download of current copy of repository metadata. Parsing and rebuild of the database will not be forced.&lt;br /&gt;
&lt;br /&gt;
-B, --build-only Only parse the metadata and build the database, don’t download raw metadata into the cache. This will enable you to repair damaged database from cached data without accessing network at all.&lt;br /&gt;
&lt;br /&gt;
-D, --download-only Only download the raw metadata, don’t parse it or build the database.&lt;br /&gt;
&lt;br /&gt;
--include-all-archs Multi-Arch repos: Download raw metadata for all offered architectures even if the repo supports filtering. Otherwise we’d download only the metadata for architectures compatible with the local system. You do not need this unless you want zypper to provide the full metadata for the purpose of mirroring the repository.&lt;br /&gt;
&lt;br /&gt;
-s, --services Refresh also services before refreshing repositories.&lt;br /&gt;
&lt;br /&gt;
clean (cc) [options] [alias|name|#|URI]... Clean the local caches for all known or specified repositories. By default, only caches of downloaded packages are cleaned.&lt;br /&gt;
&lt;br /&gt;
-m, --metadata Clean repository metadata cache instead of package cache.&lt;br /&gt;
&lt;br /&gt;
-M, --raw-metadata Clean repository raw metadata cache instead of package cache.&lt;br /&gt;
&lt;br /&gt;
-a, --all Clean both repository metadata and package caches.&lt;br /&gt;
&lt;br /&gt;
Service Management The services, addservice, removeservice, modifyservice, and refresh-services commands serve for manipulating services. A service is specified by its URI and needs to have a unique alias defined (among both services and repositories).&lt;br /&gt;
&lt;br /&gt;
Standalone repositories (not belonging to any service) are treated like services, too. The ls command will list them, ms command will modify them, etc. Repository specific options, like --keep-packages are not available here, though. You can use repository handling commands to manipulate them.&lt;br /&gt;
&lt;br /&gt;
addservice (as) [options] URI alias Adds a service specified by URI to the system. The alias must be unique and serves to identify the service. If a service with the same alias and URI already exists, the command behaves like modifyservice and updates the settings accordingly.&lt;br /&gt;
&lt;br /&gt;
Newly added services are not refreshed automatically. Use the refresh-services command to refresh them. Zypper does not access the service URI when adding the service, so the type of the services is unknown until it is refreshed.&lt;br /&gt;
&lt;br /&gt;
-n, --name name Specify descriptive name for the service.&lt;br /&gt;
&lt;br /&gt;
-e, --enable Enable the service (this is the default).&lt;br /&gt;
&lt;br /&gt;
-d, --disable Add the service as disabled.&lt;br /&gt;
&lt;br /&gt;
-f, --refresh Enable auto-refresh of the service.&lt;br /&gt;
&lt;br /&gt;
-F, --no-refresh Disable auto-refresh of the service.&lt;br /&gt;
&lt;br /&gt;
removeservice (rs) [options] alias|name|#|URI... Remove specified service from the system. Removing a service will also remove of all of its repositories.&lt;br /&gt;
&lt;br /&gt;
--loose-auth Ignore user authentication data in the URI.&lt;br /&gt;
&lt;br /&gt;
--loose-query Ignore query string in the URI.&lt;br /&gt;
&lt;br /&gt;
modifyservice (ms) options alias|name|#|URI&lt;br /&gt;
&lt;br /&gt;
modifyservice (ms) options --all|--remote|--local|--medium-type Modify properties of specified services.&lt;br /&gt;
&lt;br /&gt;
Common Options These options are common to all types of services and repositories.&lt;br /&gt;
&lt;br /&gt;
-n, --name name Set a descriptive name for the service.&lt;br /&gt;
&lt;br /&gt;
-e, --enable Enable a disabled service.&lt;br /&gt;
&lt;br /&gt;
-d, --disable Disable the service (but don’t remove it).&lt;br /&gt;
&lt;br /&gt;
-f, --refresh (legacy: -r) Enable auto-refresh of the service.&lt;br /&gt;
&lt;br /&gt;
-F, --no-refresh (legacy: -R) Disable auto-refresh of the service.&lt;br /&gt;
&lt;br /&gt;
-a, --all Apply changes to all services.&lt;br /&gt;
&lt;br /&gt;
-l, --local Apply changes to all local services.&lt;br /&gt;
&lt;br /&gt;
-t, --remote Apply changes to all remote services.&lt;br /&gt;
&lt;br /&gt;
-m, --medium-type type Apply changes to services of specified type.&lt;br /&gt;
&lt;br /&gt;
RIS Service Specific Options These options are ignored by services other than Repository Index Services.&lt;br /&gt;
&lt;br /&gt;
-i, --ar-to-enable alias Schedule an RIS service repository to be enabled at next service refresh.&lt;br /&gt;
&lt;br /&gt;
-I, --ar-to-disable alias Schedule an RIS service repository to be disabled at next service refresh.&lt;br /&gt;
&lt;br /&gt;
-j, --rr-to-enable alias Remove a RIS service repository to enable.&lt;br /&gt;
&lt;br /&gt;
-J, --rr-to-disable alias Remove a RIS service repository to disable.&lt;br /&gt;
&lt;br /&gt;
-k, --cl-to-enable Clear the list of RIS repositories to enable.&lt;br /&gt;
&lt;br /&gt;
-K, --cl-to-disable Clear the list of RIS repositories to disable.&lt;br /&gt;
&lt;br /&gt;
services (ls) [options] List services defined on the system.&lt;br /&gt;
&lt;br /&gt;
-u, --uri Show also base URI of repositories.&lt;br /&gt;
&lt;br /&gt;
-p, --priority Show also repository priority.&lt;br /&gt;
&lt;br /&gt;
-d, --details Show more information like URI, priority, type.&lt;br /&gt;
&lt;br /&gt;
-r, --with-repos Show also repositories belonging to the services.&lt;br /&gt;
&lt;br /&gt;
-P, --sort-by-priority Sort the list by repository priority.&lt;br /&gt;
&lt;br /&gt;
-E, --show-enabled-only Show enabled services only. If used together with --with-repos a disabled services owning (manually) enabled repositories are shown as well.&lt;br /&gt;
&lt;br /&gt;
-U, --sort-by-uri Sort the list by URI.&lt;br /&gt;
&lt;br /&gt;
-N, --sort-by-name Sort the list by name.&lt;br /&gt;
&lt;br /&gt;
refresh-services (refs) [options] alias|name|#|URI... Refreshing a service means executing the service’s special task.&lt;br /&gt;
&lt;br /&gt;
RIS services add, remove, or modify repositories on your system based on current content of the repository index. A differing enabled/disabled state caused by manually calling modify-repo on a service repository however will not be reverted unless the --restore-status option is used, or the repository index explicitly requests the change.&lt;br /&gt;
&lt;br /&gt;
Services only manage defined repositories, they do not refresh them. To refresh also repositories, use --with-repos option or the refresh command.&lt;br /&gt;
&lt;br /&gt;
-f, --force Force a complete refresh of specified services. This option will cause both the download of raw metadata and parsing of the metadata to be forced even if everything indicates a refresh is not needed.&lt;br /&gt;
&lt;br /&gt;
-r, --with-repos Refresh also the service repositories.&lt;br /&gt;
&lt;br /&gt;
-R, --restore-status Also restore service repositories enabled/disabled state to the repository index default. Useful after you manually changed some service repositories enabled state.&lt;br /&gt;
&lt;br /&gt;
Package Locks Management Package locks serve the purpose of preventing changes to the set of installed packages on the system. Locks are stored as queries in /etc/zypp/locks file (see also locks(5)). Packages matching a query are then forbidden to change their installed status; an installed package can’t be removed or upgraded, not installed package can’t be installed. When requesting to install, upgrade or remove such locked package, you will get a dependency problem dialog.&lt;br /&gt;
&lt;br /&gt;
A lock-spec is formed by &amp;amp;quot;NAME [OP EDITION]&amp;amp;quot;, where NAME may also be a glob pattern using * and ? wildcard characters. Non-package types may to have their kind-string prepended (e.g. patch:foo or product:baa) or use the commands --type option.&lt;br /&gt;
&lt;br /&gt;
The basic form will lock all editions of the matching items. You can optionally restrict the lock to match a specific edition or edition range using =, &amp;amp;lt;, &amp;amp;lt;=, &amp;amp;gt;, &amp;amp;gt;= or != followed by the edition.&lt;br /&gt;
&lt;br /&gt;
Note&lt;br /&gt;
&lt;br /&gt;
If you use blanks around the operator you need to quote the string or escape the blanks according to the rules of the shell you are using.&lt;br /&gt;
&lt;br /&gt;
locks (ll) List currently active package locks.&lt;br /&gt;
&lt;br /&gt;
-m, --matches Show the number of resolvables matched by each lock. This option requires loading the repositories.&lt;br /&gt;
&lt;br /&gt;
-s, --solvables List the resolvables matched by each lock. This option requires loading the repositories.&lt;br /&gt;
&lt;br /&gt;
addlock (al) [options] lock-spec... Add a package lock. Specify packages to lock as explained above.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Restrict the lock to the specified repository.&lt;br /&gt;
&lt;br /&gt;
-t, --type type Lock only packages of specified type (default: package). See section Package Types for list of available package types.&lt;br /&gt;
&lt;br /&gt;
-m, --comment comment Add a comment for package lock.&lt;br /&gt;
&lt;br /&gt;
removelock (rl) [options] lock-number|lock-spec... Remove a package lock. Specify the lock to remove by its number obtained with zypper locks or by the lock-spec.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Restrict the lock to the specified repository.&lt;br /&gt;
&lt;br /&gt;
-t, --type type Restrict the lock to packages of specified type (default: package). See section Package Types for list of available package types.&lt;br /&gt;
&lt;br /&gt;
cleanlocks (cl) Remove unused locks.&lt;br /&gt;
&lt;br /&gt;
This command looks for locks that do not currently (with regard to repositories used) lock any package and for each such lock it asks user whether to remove it.&lt;br /&gt;
&lt;br /&gt;
Locale Management These commands give information about requested locales and the possibility to manage those. A locale is defined by a language code. For many packages there are locale dependent packages available which provide translations or dictionaries. To get these installed, the locale for the desired language must be marked as requested by the package manager library.&lt;br /&gt;
&lt;br /&gt;
locales (lloc) [OPTIONS] [LOCALE] ... List requested locales. Called without argument, lists the locales which are already marked as requested. Specifying certain locale(s) prints information only for this(these).&lt;br /&gt;
&lt;br /&gt;
-a, --all List all available locales.&lt;br /&gt;
&lt;br /&gt;
-p, --packages Show corresponding packages.&lt;br /&gt;
&lt;br /&gt;
addlocale (aloc) [OPTIONS] &amp;amp;lt;LOCALE&amp;amp;gt; ... Add specified locale(s) to the list of requested locales..&lt;br /&gt;
&lt;br /&gt;
-n, --no-packages Do not install corresponding packages.&lt;br /&gt;
&lt;br /&gt;
removelocale (rloc) [OPTIONS] &amp;amp;lt;LOCALE&amp;amp;gt; ... Remove specified locale(s) from the list of requested locales..&lt;br /&gt;
&lt;br /&gt;
-n, --no-packages Do not remove corresponding packages.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper locales List requested locales.&lt;br /&gt;
&lt;br /&gt;
$ zypper locales --packages de en Get the lists of packages which are available for de and en (exact match).&lt;br /&gt;
&lt;br /&gt;
$ zypper locales en_ Get all locales with lang code en that have their own country code, excluding the fallback en.&lt;br /&gt;
&lt;br /&gt;
$ zypper locales en* Get all locales with lang code en with or without country code.&lt;br /&gt;
&lt;br /&gt;
$ zypper aloc --packages de_CH Request de_CH and install language dependent packages.&lt;br /&gt;
&lt;br /&gt;
Other Commands system-architecture Print the detected system architecture.&lt;br /&gt;
&lt;br /&gt;
versioncmp (vcmp) VERSION1 VERSION2 Compare the versions supplied as arguments and tell whether VERSION1 is older or newer than VERSION2 or the two version strings match. The exit code is 0 if the versions are equal, 11 if VERSION1 is newer, and 12 if VERSION2 is newer.&lt;br /&gt;
&lt;br /&gt;
The default output is in human-friendly form. If --terse global option is used, the result is an integer number, negative/positive if VERSION1 is older/newer than VERSION2, zero if they match.&lt;br /&gt;
&lt;br /&gt;
-m, --match Takes missing release number as any release.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
$ zypper vcmp -m 0.15.3 0.15.3-2 0.15.3 matches 0.15.3-2&lt;br /&gt;
&lt;br /&gt;
$ zypper vcmp 0.15.3 0.15.3-2 0.15.3 is older than 0.15.3-2&lt;br /&gt;
&lt;br /&gt;
targetos (tos) Shows the ID string of the target operating system. The string is defined by the XPath:/product/register/target entry in (current-rootdir)/etc/products.d/baseproduct.&lt;br /&gt;
&lt;br /&gt;
If the baseproduct does not provide this entry, or if no baseproduct is installed at all, the value is empty if the --terse global option is used.&lt;br /&gt;
&lt;br /&gt;
In not-terse mode the distribution label is shown instead of an empty value, if a baseproduct is installed.&lt;br /&gt;
&lt;br /&gt;
-l, --label Show the baseproducts distribution and short label instead.&lt;br /&gt;
&lt;br /&gt;
licenses Prints a report about licenses and EULA&#039;s of installed packages to standard output.&lt;br /&gt;
&lt;br /&gt;
First, a list of all packages and their licenses and/or EULAs is shown. This is followed by a summary, including the total number of installed packages, the number of installed packages with EULAs that required a confirmation from the user. Since the EULAs are not stored on the system and can only be read from repository metadata, the summary includes also the number of installed packages that have their counterpart in repositories. The report ends with a list of all licenses uses by the installed packages.&lt;br /&gt;
&lt;br /&gt;
This command can be useful for companies redistributing a custom distribution (like appliances) to figure out what licenses they are bound by.&lt;br /&gt;
&lt;br /&gt;
download [OPTIONS] Download rpms specified on the commandline to a local directory.&lt;br /&gt;
&lt;br /&gt;
Per default packages are downloaded to the libzypp package cache (/var/cache/zypp/packages; for non-root users $XDG_CACHE_HOME/zypp/packages), but this can be changed by using the global --pkg-cache-dir option.&lt;br /&gt;
&lt;br /&gt;
Parsable XML-output produced by zypper --xmlout will include a &amp;amp;lt;download-result&amp;amp;gt; node for each package zypper tried to download. Upon success the location of the downloaded package is found in the path attribute of the &amp;amp;lt;localfile&amp;amp;gt; subnode (xpath: download-result/localpath@path):&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;download-result&amp;amp;gt; &amp;amp;lt;solvable&amp;amp;gt; &amp;amp;lt;kind&amp;amp;gt;package&amp;amp;lt;/kind&amp;amp;gt; &amp;amp;lt;name&amp;amp;gt;zypper&amp;amp;lt;/name&amp;amp;gt; &amp;amp;lt;edition epoch=&amp;amp;quot;0&amp;amp;quot; version=&amp;amp;quot;1.9.17&amp;amp;quot; release=&amp;amp;quot;26.1&amp;amp;quot;/&amp;amp;gt; &amp;amp;lt;arch&amp;amp;gt;x86_64&amp;amp;lt;/arch&amp;amp;gt; &amp;amp;lt;repository name=&amp;amp;quot;repo-oss-update (13.1)&amp;amp;quot; alias=&amp;amp;quot;openSUSE:repo-oss-update&amp;amp;quot;/&amp;amp;gt; &amp;amp;lt;/solvable&amp;amp;gt; &amp;amp;lt;localfile path=&amp;amp;quot;/var/cache/zypp/pac.../zypper-1.9.17-26.1.x86_64.rpm&amp;amp;quot;/&amp;amp;gt; &amp;amp;lt;/download-result&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
--all-matches Download all versions matching the commandline arguments. Otherwise only the best version of each matching package is downloaded.&lt;br /&gt;
&lt;br /&gt;
--dry-run Don’t download any package, just report what would be done.&lt;br /&gt;
&lt;br /&gt;
-r, --repo alias|name|#|URI Work only with the repository specified by the alias, name, number or URI. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
--from alias|name|#|URI Select packages from the specified repository only. This option can be used multiple times.&lt;br /&gt;
&lt;br /&gt;
source-download [OPTIONS] Download source rpms for all installed packages to a local directory.&lt;br /&gt;
&lt;br /&gt;
-d, --directory dir Download all source rpms to this directory. Default is /var/cache/zypper/source-download.&lt;br /&gt;
&lt;br /&gt;
--delete Delete extraneous source rpms in the local directory. This is the default.&lt;br /&gt;
&lt;br /&gt;
--no-delete Do not delete extraneous source rpms.&lt;br /&gt;
&lt;br /&gt;
--status Don’t download any source rpms, but show which source rpms are missing or extraneous.&lt;br /&gt;
&lt;br /&gt;
ps [OPTIONS] After each upgrade or removal of packages, there may be running processes on the system which continue to use meanwhile deleted files. zypper ps lists all processes using deleted files, together with the corresponding files, and a service name hint, in case it’s a known service. This gives a hint which services may need to be restarted after an update. Usually programs which continue to use deleted shared libraries. The list contains the following information:&lt;br /&gt;
&lt;br /&gt;
PID ID of the process&lt;br /&gt;
&lt;br /&gt;
PPID ID of the parent process&lt;br /&gt;
&lt;br /&gt;
UID ID of the user running the process&lt;br /&gt;
&lt;br /&gt;
Login Login name of the user running the process&lt;br /&gt;
&lt;br /&gt;
Command Command used to execute the process&lt;br /&gt;
&lt;br /&gt;
Service Service name, if command is associated with a system service&lt;br /&gt;
&lt;br /&gt;
Files The list of the deleted files&lt;br /&gt;
&lt;br /&gt;
-s, --short Create a short table not showing the deleted files. Given twice, show only processes which are associated with a system service. Given three times, list the associated system service names only.&lt;br /&gt;
&lt;br /&gt;
--print format For each associated system service print format on the standard output, followed by a newline. Any %s directive in format is replaced by the system service name.&lt;br /&gt;
&lt;br /&gt;
-d, --debugFile filename Output a file with all proc entries that make it into the final set of used open files. This can be submitted as additional information in a bug report.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
$ zypper ps -ss Show only processes associated with a system service.&lt;br /&gt;
&lt;br /&gt;
$ zypper ps -sss Short for zypper ps --print &amp;amp;quot;%s&amp;amp;quot;; list services which might need a restart.&lt;br /&gt;
&lt;br /&gt;
$ zypper ps --print &amp;amp;quot;systemctl status %s&amp;amp;quot; Let zypper print the commands to retrieve status information for services which might need a restart.&lt;br /&gt;
&lt;br /&gt;
needs-rebooting Checks if the reboot-needed flag was set by a previous update or install of a core library or service.&lt;br /&gt;
&lt;br /&gt;
The reboot-needed flag is set if a package that provides installhint(reboot-needed) is updated or installed. Additionally there is a predefined list (/etc/zypp/needreboot) of well-known packages which cause the reboot-needed flag being set unconditionally. The exit code ZYPPER_EXIT_INF_REBOOT_NEEDED indicates that a reboot is suggested, otherwise the exit code is set to ZYPPER_EXIT_OK.&lt;br /&gt;
&lt;br /&gt;
It is recommended for scripts to use this command to test whether a system reboot is suggested. Use --quiet to suppress the normal output.&lt;br /&gt;
&lt;br /&gt;
Subcommands subcommand Lists available subcommands in /usr/lib/zypper/commands and from elsewhere on your $PATH. See section SUBCOMMANDS for details.&lt;br /&gt;
&lt;br /&gt;
GLOBAL OPTIONS -h, --help Help. If a command is specified together with --help option, command specific help is displayed.&lt;br /&gt;
&lt;br /&gt;
-V, --version Print zypper version number and exit.&lt;br /&gt;
&lt;br /&gt;
-c, --config file Use the specified zypper config file instead of the default zypper.conf. Other command line options specified together with --config and having their counterpart in the zypper config file are still preferred.&lt;br /&gt;
&lt;br /&gt;
The order of preference with --config is as follows:&lt;br /&gt;
&lt;br /&gt;
1. Command line options&lt;br /&gt;
&lt;br /&gt;
2. --config file&lt;br /&gt;
&lt;br /&gt;
3. [/etc/zypp/zypp.conf] (system-wide defaults for all libzypp based applications)&lt;br /&gt;
&lt;br /&gt;
Note&lt;br /&gt;
&lt;br /&gt;
Use and location of the system-wide /etc/zypp/zypp.conf can not be changed this way. It’s mentioned here just because some zypper command line options allow one to overwrite system-wide defaults defined in zypp.conf.&lt;br /&gt;
&lt;br /&gt;
See also FILES section for more information.&lt;br /&gt;
&lt;br /&gt;
-v, --verbose Increase verbosity. For debugging output specify this option twice. zypper -vv ref for example will show the mirrors used to download the metadata.&lt;br /&gt;
&lt;br /&gt;
-q, --quiet Suppress normal output. Brief (esp. result notification) messages and error messages will still be printed, though. If used together with conflicting --verbose option, the --verbose option takes preference.&lt;br /&gt;
&lt;br /&gt;
--color, --no-color Whether to use colors in output if tty supports it. For details see the [color] section in zypper.conf.&lt;br /&gt;
&lt;br /&gt;
-A, --no-abbrev Do not abbreviate text in tables. By default zypper will try to abbreviate texts in some columns so that the table fits the width of the screen. If you need to see the whole text, use this option.&lt;br /&gt;
&lt;br /&gt;
-t, --terse Terse output for machine consumption. Implies --no-abbrev and --no-color.&lt;br /&gt;
&lt;br /&gt;
-s, --table-style integer Choose among different predefined line drawing character sets to use when drawing a table. The table style is identified by an integer number. Style 0 is the default, styles 1-9 use combinations of different box drawing characters whose shape may depend on the font the terminal is using. Style 10 separates columns by a colon and style 11 draws no lines at all.&lt;br /&gt;
&lt;br /&gt;
-n, --non-interactive Switches to non-interactive mode. In this mode zypper doesn’t ask user to type answers to various prompts, but uses default answers automatically. Those default answers also depend on other options like --no-gpg-checks or --ignore-unknown.&lt;br /&gt;
&lt;br /&gt;
--non-interactive-include-reboot-patches In non-interactive mode do not skip patches which have the rebootSuggested-flag set. Otherwise these patches are considered to be interactive, like patches including a licenses or some message to confirm. NOTE: This option does not turn on non-interactive mode.&lt;br /&gt;
&lt;br /&gt;
-x, --xmlout Switches to XML output. This option is useful for scripts or graphical frontends using zypper.&lt;br /&gt;
&lt;br /&gt;
-i, --ignore-unknown Ignore unknown packages. This option is useful for scripts, because when installing in --non-interactive mode zypper expects each command line argument to match at least one known package. Unknown names or globbing expressions with no match are treated as an error unless this option is used.&lt;br /&gt;
&lt;br /&gt;
For search and info commands the option makes zypper return ZYPPER_EXIT_OK rather than ZYPPER_EXIT_INF_CAP_NOT_FOUND if the query did not produce at least one match.&lt;br /&gt;
&lt;br /&gt;
-D, --reposd-dir dir Use the specified directory to look for the repository definition (.repo) files. The default value is /etc/zypp/repos.d.&lt;br /&gt;
&lt;br /&gt;
-C, --cache-dir dir Use an alternative root directory for all caches. The default value is /var/cache/zypp.&lt;br /&gt;
&lt;br /&gt;
--raw-cache-dir dir Use the specified directory for storing raw copies of repository metadata files. The default value is /var/cache/zypp/raw.&lt;br /&gt;
&lt;br /&gt;
--solv-cache-dir dir Use the specified directory to store the repository metadata cache database files (solv files). The default value is /var/cache/zypp/solv.&lt;br /&gt;
&lt;br /&gt;
--pkg-cache-dir dir Use the specified directory for storing rpm packages downloaded from repositories (see addrepo --keep-packages). The default value is /var/cache/zypp/packages. + Packages are stored in subdirectories named after the repositories alias and using the same path as on the repositories medium.&lt;br /&gt;
&lt;br /&gt;
--userdata string User data is expected to be a simple string without special chars or embedded newlines and may serve as transaction id. It will be written to all install history log entries created throughout this specific zypper call. It will also be passed on to zypp plugins executed during commit. This will enable e.g. a btrfs plugin to tag created snapshots with this string. For zypper itself this string has no special meaning.&lt;br /&gt;
&lt;br /&gt;
Repository Options:&lt;br /&gt;
&lt;br /&gt;
--no-gpg-checks Ignore GPG check failures and continue. If a GPG issue occurs when using this option zypper prints and logs a warning and automatically continues without interrupting the operation. Use this option with caution, as you can easily overlook security problems by using it. (see section GPG checks)&lt;br /&gt;
&lt;br /&gt;
--gpg-auto-import-keys If new repository signing key is found, do not ask what to do; trust and import it automatically. This option causes that the new key is imported also in non-interactive mode, where it would otherwise got rejected.&lt;br /&gt;
&lt;br /&gt;
-p, --plus-repo URI Use an additional repository for this operation. The repository aliased tmp# and named by the specified URI will be added for this operation and removed at the end. You can specify this option multiple times.&lt;br /&gt;
&lt;br /&gt;
--plus-content tag Additionally use disabled repositories denoted by tag for this operation. If tag matches a repositories alias, name or URL, or is a keyword defined in the repositories metadata, the repository will be temporarily enabled for this operation. The repository will then be refreshed and used according to the commands rules. You can specify this option multiple times.&lt;br /&gt;
&lt;br /&gt;
If a disabled repositories metadata are not available in the local cache, they will be downloaded to scan for matching keywords. Otherwise the keyword scan will use the metadata available in the local cache. Only if used together with the refresh command, a keyword scan will refresh all disabled repositories.&lt;br /&gt;
&lt;br /&gt;
To refresh all disabled repositories metadata: zypper --plus-content &#039;&#039; ref&lt;br /&gt;
&lt;br /&gt;
To include a disabled repository repo-debug in a search: zypper --plus-content repo-debug search ...&lt;br /&gt;
&lt;br /&gt;
To search only in a disabled repository repo-debug: zypper --plus-content repo-debug search -r repo-debug ...&lt;br /&gt;
&lt;br /&gt;
To enable all repos providing the debug keyword: zypper in --plus-content debug some -debuginfo or -debugsource package&lt;br /&gt;
&lt;br /&gt;
--disable-repositories Do not read metadata from repositories. This option will prevent loading of packages from repositories, thus making zypper work only with the installed packages (if --disable-system-resolvables was not specified).&lt;br /&gt;
&lt;br /&gt;
--no-refresh Do not auto-refresh repositories (ignore the auto-refresh setting). Useful to save time when doing operations like search, if there is not a need to have a completely up to date metadata.&lt;br /&gt;
&lt;br /&gt;
--no-cd Ignore CD/DVD repositories. When this option is specified, zypper acts as if the CD/DVD repositories were not defined at all.&lt;br /&gt;
&lt;br /&gt;
--no-remote Ignore remote repositories like http, ftp, smb and similar. This makes using zypper easier when being offline. When this option is specified, zypper acts as if the remote repositories were not defined at all.&lt;br /&gt;
&lt;br /&gt;
--releasever version For the current command set the value of the $releasever repository variable to version. This can be used to switch to new distribution repositories when performing a distribution upgrade. See the dist-upgrade (dup) command and section Repository Management for more details about using the $releasever repository variable.&lt;br /&gt;
&lt;br /&gt;
To check where you already use $releasever call: zypper --releasever @--HERE--@ lr -u&lt;br /&gt;
&lt;br /&gt;
Target Options:&lt;br /&gt;
&lt;br /&gt;
-R, --root dir Operates on a different root directory. This option influences the location of the repos.d directory and the metadata cache directory and also causes rpm to be run with the --root option to do the actual installation or removal of packages. See also the FILES section.&lt;br /&gt;
&lt;br /&gt;
--installroot dir Behaves like --root but shares the repositories with the host system.&lt;br /&gt;
&lt;br /&gt;
--disable-system-resolvables This option serves mainly for testing purposes. It will cause zypper to act as if there were no packages installed in the system. Use with caution as you can damage your system using this option.&lt;br /&gt;
&lt;br /&gt;
SUBCOMMANDS Zypper subcommands are inspired by git(1). Subcommands are standalone executables that live in the zypper_execdir (/usr/lib/zypper/commands). For subcommands zypper provides a wrapper that knows where the subcommands live, and runs them by passing command options and arguments to them. If a subcommand is not found in the zypper_execdir, the wrapper will look in the rest of your $PATH for it. Thus, it’s possible to write local zypper extensions that don’t live in system space. This can be disabled by setting &amp;amp;quot;subcommand.seachSubcommandInPath&amp;amp;quot; to &amp;amp;quot;no&amp;amp;quot; in the zypper.conf.&lt;br /&gt;
&lt;br /&gt;
This is how to add your own subcommand zypper mytask:&lt;br /&gt;
&lt;br /&gt;
• The executable must be named zypper-mytask.&lt;br /&gt;
&lt;br /&gt;
• The executable must be located your $PATH.&lt;br /&gt;
&lt;br /&gt;
• A manpage for zypper-mytask should be provided and explaining the commands options and return values. It will be shown when calling zypper help mytask.&lt;br /&gt;
&lt;br /&gt;
• Zypper built-in commands take precedence over subcommands with the same name.&lt;br /&gt;
&lt;br /&gt;
• It’s fine to call zypper or use libzypp from within your subcommand.&lt;br /&gt;
&lt;br /&gt;
You can use the built-in zypper subcommand command to get a list of all subcommands in zypper_execdir and from elsewhere on your $PATH.&lt;br /&gt;
&lt;br /&gt;
Using zypper global-options together with subcommands, as well as executing subcommands in zypper shell is currently not supported.&lt;br /&gt;
&lt;br /&gt;
FILES /etc/zypp/zypper.conf, $HOME/.zypper.conf Global (system-wide) and user’s configuration file for zypper. These files are read when zypper starts up and --config option is not used.&lt;br /&gt;
&lt;br /&gt;
User’s settings are preferred over global settings. Similarly, command line options override the settings in either of these files. To sum it up, the order of preference is as follows (from highest to lowest):&lt;br /&gt;
&lt;br /&gt;
1. Command line options&lt;br /&gt;
&lt;br /&gt;
2. $HOME/.zypper.conf&lt;br /&gt;
&lt;br /&gt;
3. /etc/zypp/zypper.conf&lt;br /&gt;
&lt;br /&gt;
4. [/etc/zypp/zypp.conf] (system-wide defaults for all libzypp based applications)&lt;br /&gt;
&lt;br /&gt;
See the comments in /etc/zypp/zypper.conf for a list and description of available options.&lt;br /&gt;
&lt;br /&gt;
Note&lt;br /&gt;
&lt;br /&gt;
The system-wide /etc/zypp/zypp.conf is mentioned here just because some zypper command line options allow one to overwrite system-wide defaults defined there. zypp.conf and zypper.conf have different content and serve different purpose.&lt;br /&gt;
&lt;br /&gt;
/etc/zypp/zypp.conf ZYpp configuration file affecting all libzypp based applications. See the comments in the file for description of configurable properties. Many locations of files and directories listed in this section are configurable via zypp.conf. The location for this file itself can be redefined only by setting $ZYPP_CONF in the environment.&lt;br /&gt;
&lt;br /&gt;
/etc/zypp/locks File with package lock definitions. The package lock commands (locks, addlock, removelock, etc.) should be used to manipulate this file.&lt;br /&gt;
&lt;br /&gt;
This file is used by all ZYpp-based applications.&lt;br /&gt;
&lt;br /&gt;
/etc/zypp/repos.d Directory containing repository definition (*.repo) files. You can use the Repository Management commands to manipulate these files, or you can edit them yourself. In either case, after doing the modifications, executing *zypper refresh* is strongly recommended.&lt;br /&gt;
&lt;br /&gt;
You can use the --reposd-dir global option to use an alternative directory for this purpose or the --root option to make this directory relative to the specified root directory.&lt;br /&gt;
&lt;br /&gt;
This directory is used by all ZYpp-based applications.&lt;br /&gt;
&lt;br /&gt;
/etc/zypp/services.d Directory containing service definition (*.service) files. You can use the Service Management Commands to manipulate these files, or you can edit them yourself. Running *zypper refs* is recommended after modifications have been done.&lt;br /&gt;
&lt;br /&gt;
This directory is used by all ZYpp-based applications.&lt;br /&gt;
&lt;br /&gt;
/usr/lib/zypper/commands System directory containing zypper extensions (see section SUBCOMMANDS)&lt;br /&gt;
&lt;br /&gt;
/var/cache/zypp/raw Directory for storing raw metadata contained in repositories. Use the --raw-cache-dir global option to use an alternative directory for this purpose or the --root option to make this directory relative to the specified root directory.&lt;br /&gt;
&lt;br /&gt;
This directory is used by all ZYpp-based applications.&lt;br /&gt;
&lt;br /&gt;
/var/cache/zypp/solv Directory containing preparsed metadata in form of solv files.&lt;br /&gt;
&lt;br /&gt;
This directory is used by all ZYpp-based applications.&lt;br /&gt;
&lt;br /&gt;
/var/cache/zypp/packages If the keep-packages property is set for a repository (see the modifyrepo command), all the RPM file downloaded during installation will be kept here. Packages are stored in subdirectories named after their repositories alias.&lt;br /&gt;
&lt;br /&gt;
Subdirectories of removed or otherwise unknown repositories are cleaned automatically. This auto-cleanup can be prevented by creating a file named .no_auto_prune in the pkg-cache directory.&lt;br /&gt;
&lt;br /&gt;
Creating a .keep_packages file in the pkg-cache directory will keep downloaded packages for all repos cached here, overwriting the repositories keep-packages property.&lt;br /&gt;
&lt;br /&gt;
See also the clean command for cleaning these cache directories.&lt;br /&gt;
&lt;br /&gt;
This directory is used by all ZYpp-based applications.&lt;br /&gt;
&lt;br /&gt;
/var/log/zypper.log Zypper log file. It should be attached to all bugreports. (see also zypper-log(8)).&lt;br /&gt;
&lt;br /&gt;
/var/log/zypper.solverTestCase Solver testcase created by using the --debug-solver option.&lt;br /&gt;
&lt;br /&gt;
/var/log/updateTestcase-YYYY-MM-DD-hh-mm-ss Solver testcase auto created when performing a zypper dup.&lt;br /&gt;
&lt;br /&gt;
/var/log/zypp/history Installation history log.&lt;br /&gt;
&lt;br /&gt;
~/.zypper_history Command history for the zypper shell (see the shell command).&lt;br /&gt;
&lt;br /&gt;
/etc/zypp/needreboot File with a list of packages that will set the reboot-needed flag when installed or upgraded.&lt;br /&gt;
&lt;br /&gt;
/etc/zypp/needreboot.d Directory that can be used to define packages that trigger the reboot-needed flag by adding additional files containing the required package names.&lt;br /&gt;
&lt;br /&gt;
EXIT CODES There are several exit codes defined for zypper built-in commands for use e.g. within scripts. These codes are defined in header file src/zypper-main.h found in zypper source package. Codes below 100 denote an error, codes above 100 provide a specific information, 0 represents a normal successful run. Following is a list of these codes with descriptions:&lt;br /&gt;
&lt;br /&gt;
0 - ZYPPER_EXIT_OK Successful run of zypper with no special info.&lt;br /&gt;
&lt;br /&gt;
1 - ZYPPER_EXIT_ERR_BUG Unexpected situation occurred, probably caused by a bug.&lt;br /&gt;
&lt;br /&gt;
2 - ZYPPER_EXIT_ERR_SYNTAX zypper was invoked with an invalid command or option, or a bad syntax.&lt;br /&gt;
&lt;br /&gt;
3 - ZYPPER_EXIT_ERR_INVALID_ARGS Some of provided arguments were invalid. E.g. an invalid URI was provided to the addrepo command.&lt;br /&gt;
&lt;br /&gt;
4 - ZYPPER_EXIT_ERR_ZYPP A problem is reported by ZYPP library.&lt;br /&gt;
&lt;br /&gt;
5 - ZYPPER_EXIT_ERR_PRIVILEGES User invoking zypper has insufficient privileges for specified operation.&lt;br /&gt;
&lt;br /&gt;
6 - ZYPPER_EXIT_NO_REPOS No repositories are defined.&lt;br /&gt;
&lt;br /&gt;
7 - ZYPPER_EXIT_ZYPP_LOCKED The ZYPP library is locked, e.g. packagekit is running.&lt;br /&gt;
&lt;br /&gt;
8 - ZYPPER_EXIT_ERR_COMMIT An error occurred during installation or removal of packages. You may run zypper verify to repair any dependency problems.&lt;br /&gt;
&lt;br /&gt;
100 - ZYPPER_EXIT_INF_UPDATE_NEEDED Returned by the patch-check command if there are patches available for installation.&lt;br /&gt;
&lt;br /&gt;
101 - ZYPPER_EXIT_INF_SEC_UPDATE_NEEDED Returned by the patch-check command if there are security patches available for installation.&lt;br /&gt;
&lt;br /&gt;
102 - ZYPPER_EXIT_INF_REBOOT_NEEDED Returned by the needs-rebooting command, if a system reboot is suggested.&lt;br /&gt;
&lt;br /&gt;
Legacy: Returned after the successful installation of a patch which requires reboot of computer. This legacy behavior is kept, but it’s drawback is that it covers patches only, no packages. The new needs-rebooting command is the recommended way to test whether a system reboot is suggested.&lt;br /&gt;
&lt;br /&gt;
103 - ZYPPER_EXIT_INF_RESTART_NEEDED Returned after a successful installation of a patch which requires restart of the package manager itself. This means that one of patches to be installed affects the package manager itself and the command used (e.g. zypper update) needs to be executed once again to install any remaining patches.&lt;br /&gt;
&lt;br /&gt;
104 - ZYPPER_EXIT_INF_CAP_NOT_FOUND Returned by the install and the remove command in case any of the arguments does not match any of the available (or installed) package names or other capabilities. In search and info commands it indicates that no match was found. See also the --ignore-unknown global option.&lt;br /&gt;
&lt;br /&gt;
105 - ZYPPER_EXIT_ON_SIGNAL Returned upon exiting after receiving a SIGINT or SIGTERM.&lt;br /&gt;
&lt;br /&gt;
106 - ZYPPER_EXIT_INF_REPOS_SKIPPED Some repository had to be disabled temporarily because it failed to refresh. You should check your repository configuration (e.g. zypper ref -f).&lt;br /&gt;
&lt;br /&gt;
107 - ZYPPER_EXIT_INF_RPM_SCRIPT_FAILED Installation basically succeeded, but some of the packages %post install scripts returned an error. These packages were successfully unpacked to disk and are registered in the rpm database, but due to the failed install script they may not work as expected. The failed scripts output might reveal what actually went wrong. Any scripts output is also logged to /var/log/zypp/history.&lt;br /&gt;
&lt;br /&gt;
Zypper subcommands (see section SUBCOMMANDS) may return different codes which should be described in the commands man page. Call zypper help subcommand to see the subcommands man page if one is provided.&lt;br /&gt;
&lt;br /&gt;
HOMEPAGE https://github.com/openSUSE/zypper&lt;br /&gt;
&lt;br /&gt;
AUTHORS The zypper project was started by Martin Vidner, Jan Kupec, Michael Andres, Duncan Mac-Vicar Prett, Josef Reidinger and Stanislav Visnovsky. Many people have later contributed to it.&lt;br /&gt;
&lt;br /&gt;
SEE ALSO locks(5), zypper-log(8), YaST2(8)&lt;br /&gt;
&lt;br /&gt;
SUSE Linux 2026-03-06 ZYPPER(8)&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(LINUX)_systemctl&amp;diff=4902</id>
		<title>(LINUX) systemctl</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(LINUX)_systemctl&amp;diff=4902"/>
		<updated>2026-05-14T11:05:16Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Grafische Oberflaeche */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Eigentlich koennte ich diese Seite in eine neue Kategorie &#039;weird things&#039; packen.&lt;br /&gt;
&lt;br /&gt;
Um systemctl und damit systemd besser einordnen zu koennen, dient vielleicht folgender [https://www.linux-magazin.de/ausgaben/2011/11/init-systeme/ Artikel].&lt;br /&gt;
&lt;br /&gt;
=Motivation=&lt;br /&gt;
Ich habe 08/2021 meinen Server auf eine neue alte Hardware umgezogen. Da die Server-Hardware (XW8400 auf XW8600; ja ich geb es zu, alt ist gar kein Ausdruck, aber 8 Kerne und 16 GB RAM sind fuer meinen Server ausreichend; vorerst;-)) sich unwesentlich geaendert hat, war es fast damit getan die Festplatten umzustecken. Die wesentlichen Anpassungen bezogen sich auf BIOS und Netzwerk. Nur die grafische Oberflaeche wollte partout nicht starten, daher der Titel.&lt;br /&gt;
&lt;br /&gt;
=BIOS=&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=Netzwerk=&lt;br /&gt;
Da die Zuordnung der Netzdevices ueber die MAC-Adresse geschieht, waren die erwartungsgemaesz falsch. Ich hatte nach dem Start eth3, eth4 und eth5 anstatt eth0-2. BTW: Die dritte Karte ist eine LWL-Karte zur direkten Anbindung an das Backbone. &lt;br /&gt;
Die Zuordnungsdatei ist bei OpenSuse 15.3 unter&lt;br /&gt;
# /etc/udev/rules.d/70-persistent-net.rules&lt;br /&gt;
zu finden.&lt;br /&gt;
Dort habe ich die alten Eintraege geloescht/auskommentiert(#) und die neuen Zuordnungen eingetragen.&lt;br /&gt;
Die Uebernahme war nach einem Reboot richtig. Vielleicht waere es auch ohne Reboot gegangen, evtl. mit udevadm trigger&lt;br /&gt;
&lt;br /&gt;
Bei der Gelegenheit bin ich noch ueber eine Beschriftung der unteren der beiden RJ45-Netzwerkbuchsen mit ASF gestolpert. ASF steht fuer [https://en.wikipedia.org/wiki/Alert_Standard_Format Alert Standard Format] der DMTF nicht zu verwechseln mit DTMF;-)&lt;br /&gt;
&lt;br /&gt;
=Grafische Oberflaeche=&lt;br /&gt;
&lt;br /&gt;
Es sei erwaehnt, dass ein startx mit einem User zum Erfolg fuehrt, aber das ist ja keine Dauerloseung.&lt;br /&gt;
Es handelt sich also nicht um die mit jedem Update/Upgrade auftauchenden NVIDA-Treiber Probleme. &lt;br /&gt;
&lt;br /&gt;
Erst einmal das default target kontrolliert.&lt;br /&gt;
 server:~ # systemctl get-default&lt;br /&gt;
 graphical.target&lt;br /&gt;
Das stimmt soweit. Wenn ein target &amp;quot;sein Ziel&amp;quot; nicht erreicht liegt es oft an fehlerhaften Services. &lt;br /&gt;
&lt;br /&gt;
 systemctl list-dependencies graphical.target&lt;br /&gt;
&lt;br /&gt;
In der Ausgabe (Liste von Abhaengigkeiten) sind Dienste, etc. mit einer &amp;quot;farbigen&amp;quot; Markierung versehen.&lt;br /&gt;
* gruen in Ordnung&lt;br /&gt;
* rot Fehler &lt;br /&gt;
* schwarz/grau nicht aktiv(iert)&lt;br /&gt;
&lt;br /&gt;
Ich habe alle &amp;quot;roten Dienste&amp;quot; disabled. z.B.&lt;br /&gt;
 systemctl disable dhcpd&lt;br /&gt;
&lt;br /&gt;
Aber obwohl danach alle Abhaengigkeiten gruen oder schwarz waren, hat es nicht den gewuenschten Erfolg gebracht.&lt;br /&gt;
&lt;br /&gt;
Dann mal weitersuchen. Wo hat systemd seine configs versteckt?&lt;br /&gt;
&lt;br /&gt;
 server:~ # pkg-config systemd --variable=systemdsystemconfdir&lt;br /&gt;
 /etc/systemd/system&lt;br /&gt;
&lt;br /&gt;
 In der Regel in /usr/local/lib/systemd/system und /usr/lib/systemd/system&lt;br /&gt;
&lt;br /&gt;
 Full list of directories is provided in systemd.unit(5).&lt;br /&gt;
&lt;br /&gt;
In dem Verzeichnis /etc/systemd/system gibt es ein Verzeichnis graphical.target.wants in dem sich eine Service-Datei befindet.&lt;br /&gt;
 server:/etc/systemd/system # ll graphical.target.wants/&lt;br /&gt;
 insgesamt 0&lt;br /&gt;
 lrwxrwxrwx 1 root root 47 28. Apr 2019  display-manager.service -&amp;gt; /usr/lib/systemd/system/display-manager.service&lt;br /&gt;
&lt;br /&gt;
Daraufhin mal den Status gescheckt.&lt;br /&gt;
 systemctl status display-manager.service&lt;br /&gt;
Und weird#unknown display-manager active and running. Mit Alt-F7 sieht man tatsaechlich einen Cursor blinken.&lt;br /&gt;
&lt;br /&gt;
 The Simple Desktop Display Manager (SDDM) a display manager. It is the recommended display manager for the KDE Plasma and LXQt desktop environments. &lt;br /&gt;
&lt;br /&gt;
Next weird# &lt;br /&gt;
 server:/etc/systemd/system # psgrep sddm&lt;br /&gt;
 1747 ?        Sl     0:00 /usr/bin/sddm&lt;br /&gt;
 &lt;br /&gt;
 server:/etc/systemd/system # systemctl status sddm&lt;br /&gt;
 ● sddm.service - Simple Desktop Display Manager&lt;br /&gt;
     Loaded: loaded (/usr/lib/systemd/system/sddm.service; disabled; vendor preset: disabled)&lt;br /&gt;
     Active: inactive (dead)&lt;br /&gt;
       Docs: man:sddm(1)&lt;br /&gt;
             man:sddm.conf(5)&lt;br /&gt;
&lt;br /&gt;
Grrrrrrrrr!&lt;br /&gt;
&lt;br /&gt;
Zurueck auf Anfang. Bei ersten Booten (mit fehlerhaften Diensten) ist das System im Emegency-Mode gelandet.&lt;br /&gt;
&lt;br /&gt;
 Emergency and rescue targets&lt;br /&gt;
 Both of these shells require the root password. The emergency target keeps the root ﬁle system mounted read-only, while rescue. target waits for sysinit. target to complete ﬁrst so that more of the system will be initialized, for example, logging, ﬁle systems, etc.&lt;br /&gt;
&lt;br /&gt;
 server:/etc/systemd/system # systemctl --failed&lt;br /&gt;
  UNIT LOAD ACTIVE SUB DESCRIPTION&lt;br /&gt;
  0 loaded units listed.&lt;br /&gt;
&lt;br /&gt;
Bei OpenSuse gibt es keine Xwrapper.config -&amp;gt; weird#++&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=lircd=&lt;br /&gt;
2026-05-14&lt;br /&gt;
Heute wollte ich mal meinen lircd-Start automatisieren. &lt;br /&gt;
Da ich noch so eine alte Homebrew Loesung am Start habe, muss ich zum Aktivieren folgende Schritte ausfuehren.&lt;br /&gt;
&lt;br /&gt;
 systemctl stop lircd&lt;br /&gt;
 setserial /dev/ttyS0 uart none&lt;br /&gt;
 modprobe serial_ir&lt;br /&gt;
 systemctl start lircd&lt;br /&gt;
 irexec /etc/lirc/irexec.lircrc&lt;br /&gt;
&lt;br /&gt;
Der letzte Befehl dient mir als eine Art IR-Proxy. Die Befehle der IR-Fernbedienung werden entgegengenommen und per IP oder IR weitergegegeben.&lt;br /&gt;
&lt;br /&gt;
Um es gleich vorweg zu nehmen, ChatGPT hat (fast) auf ganzer Linie versagt.&lt;br /&gt;
&lt;br /&gt;
Der erste Vorschlag von ChatGPT die Befehle in eine Script zu packen, empfand ich zwar nicht so prickelnd, waere aber in der Nachbetrachtung der einfachere Weg gewesen.&lt;br /&gt;
&lt;br /&gt;
Datei erstellen:&lt;br /&gt;
&lt;br /&gt;
 sudo nano /usr/local/bin/lirc-init.sh&lt;br /&gt;
&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# UART deaktivieren&lt;br /&gt;
setserial /dev/ttyS0 uart none&lt;br /&gt;
&lt;br /&gt;
# IR-Treiber laden&lt;br /&gt;
modprobe serial_ir&lt;br /&gt;
&lt;br /&gt;
# kurzen Moment warten&lt;br /&gt;
sleep 1&lt;br /&gt;
&lt;br /&gt;
# lircd starten&lt;br /&gt;
systemctl start lircd.service&lt;br /&gt;
&lt;br /&gt;
# irexec starten&lt;br /&gt;
irexec /etc/lirc/irexec.lircrc &amp;amp;&lt;br /&gt;
&lt;br /&gt;
Ausführbar machen:&lt;br /&gt;
&lt;br /&gt;
sudo chmod +x /usr/local/bin/lirc-init.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Auch einen eigenen systemd Service zu erstellen waere denkbar gewesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datei:&lt;br /&gt;
&lt;br /&gt;
sudo nano /etc/systemd/system/lirc-init.service&lt;br /&gt;
&lt;br /&gt;
Inhalt:&lt;br /&gt;
&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=LIRC Initialisierung&lt;br /&gt;
Before=lircd.service&lt;br /&gt;
After=multi-user.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=oneshot&lt;br /&gt;
ExecStart=/usr/local/bin/lirc-init.sh&lt;br /&gt;
RemainAfterExit=yes&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am elegantesten fand die ich den folgenden Loesungsvorschlag.&lt;br /&gt;
Um es vorweg zu nehmen, so hat es nicht funktioniert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. Override-Datei erzeugen&lt;br /&gt;
sudo systemctl edit lircd.service&lt;br /&gt;
&lt;br /&gt;
Dadurch öffnet sich ein Editor.&lt;br /&gt;
&lt;br /&gt;
2. Folgenden Inhalt einfügen&lt;br /&gt;
[Service]&lt;br /&gt;
ExecStartPre=/usr/bin/setserial /dev/ttyS0 uart none&lt;br /&gt;
ExecStartPre=/usr/sbin/modprobe serial_ir&lt;br /&gt;
ExecStartPost=/usr/bin/irexec /etc/lirc/irexec.lircrc&lt;br /&gt;
3. systemd neu laden&lt;br /&gt;
sudo systemctl daemon-reload&lt;br /&gt;
4. Dienst neu starten&lt;br /&gt;
sudo systemctl restart lircd.service&lt;br /&gt;
5. Prüfen&lt;br /&gt;
&lt;br /&gt;
Status anzeigen:&lt;br /&gt;
&lt;br /&gt;
systemctl status lircd.service&lt;br /&gt;
&lt;br /&gt;
Logs ansehen:&lt;br /&gt;
&lt;br /&gt;
journalctl -u lircd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch ExecStartPost zu ersetzen war irgendwie nicht mein Ding.&lt;br /&gt;
&lt;br /&gt;
 ExecStartPost=/usr/bin/bash -c &#039;/usr/bin/irexec /etc/lirc/irexec.lircrc &amp;amp;&#039;&lt;br /&gt;
&lt;br /&gt;
Bei der ganzen Aktion habe ich doch tatsaechlich eine neue Befehlsvarianten kennengelernt.&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl edit lircd.service&lt;br /&gt;
&lt;br /&gt;
Ich habe das irexec in einen eigenen service gepackt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=LIRC irexec&lt;br /&gt;
After=lircd.service&lt;br /&gt;
Wants=lircd.service&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
ExecStart=/usr/bin/irexec /etc/lirc/irexec.lircrc&lt;br /&gt;
Restart=always&lt;br /&gt;
RestartSec=3&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach..&lt;br /&gt;
&lt;br /&gt;
 sudo systemctl daemon-reload&lt;br /&gt;
 sudo systemctl restart lircd.service&lt;br /&gt;
 sudo systemctl restart irexec.service&lt;br /&gt;
&lt;br /&gt;
Und dann eine Fehlermeldung, die mich locker eine Stunde gekostet hatte. ChatGPT war keine grosze Hilfe, hat aber an einer Stelle etwas ausgespuckt, was mich auf die richtige Faehrte gefuehrt hat.&lt;br /&gt;
&lt;br /&gt;
 modprobe: FATAL: Module serial_ir not found in directory /lib/modules/6.4.0-150600.23.84-default&lt;br /&gt;
&lt;br /&gt;
Modprobe aus der Kommandozeile funktionierte, aber als ExecStartPre-Parameter nicht. Die Fehlermeldung ist irrefuehrend und hat auch ChatGPT den Fehler staendig bei falschen Pfaden, Dateinamen, Kernelversionen, ... suchen lassen.&lt;br /&gt;
&lt;br /&gt;
Der Fehler lag in der eingeschraenkten Berechtigung (!!!) durch den Parameter ProtectKernelModules.&lt;br /&gt;
&lt;br /&gt;
Sobald ich den Parameter auf no gestellt hatte, tat auch modprobe seinen Dienst.&lt;br /&gt;
&lt;br /&gt;
 ProtectKernelModules=no #!!!!!!!&lt;br /&gt;
&lt;br /&gt;
Warum ChatGPT nicht gleich den Hinweis &amp;quot;Schaue nach dem Parameter ProtectKernelModules!&amp;quot; oder warum modprobe nicht den Fehler &amp;quot;Permission denied!&amp;quot; ausgibt, bleibt fuer mich unbeantwortet.&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(LINUX)_systemctl&amp;diff=4901</id>
		<title>(LINUX) systemctl</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(LINUX)_systemctl&amp;diff=4901"/>
		<updated>2026-05-14T10:32:16Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Grafische Oberflaeche */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Eigentlich koennte ich diese Seite in eine neue Kategorie &#039;weird things&#039; packen.&lt;br /&gt;
&lt;br /&gt;
Um systemctl und damit systemd besser einordnen zu koennen, dient vielleicht folgender [https://www.linux-magazin.de/ausgaben/2011/11/init-systeme/ Artikel].&lt;br /&gt;
&lt;br /&gt;
=Motivation=&lt;br /&gt;
Ich habe 08/2021 meinen Server auf eine neue alte Hardware umgezogen. Da die Server-Hardware (XW8400 auf XW8600; ja ich geb es zu, alt ist gar kein Ausdruck, aber 8 Kerne und 16 GB RAM sind fuer meinen Server ausreichend; vorerst;-)) sich unwesentlich geaendert hat, war es fast damit getan die Festplatten umzustecken. Die wesentlichen Anpassungen bezogen sich auf BIOS und Netzwerk. Nur die grafische Oberflaeche wollte partout nicht starten, daher der Titel.&lt;br /&gt;
&lt;br /&gt;
=BIOS=&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=Netzwerk=&lt;br /&gt;
Da die Zuordnung der Netzdevices ueber die MAC-Adresse geschieht, waren die erwartungsgemaesz falsch. Ich hatte nach dem Start eth3, eth4 und eth5 anstatt eth0-2. BTW: Die dritte Karte ist eine LWL-Karte zur direkten Anbindung an das Backbone. &lt;br /&gt;
Die Zuordnungsdatei ist bei OpenSuse 15.3 unter&lt;br /&gt;
# /etc/udev/rules.d/70-persistent-net.rules&lt;br /&gt;
zu finden.&lt;br /&gt;
Dort habe ich die alten Eintraege geloescht/auskommentiert(#) und die neuen Zuordnungen eingetragen.&lt;br /&gt;
Die Uebernahme war nach einem Reboot richtig. Vielleicht waere es auch ohne Reboot gegangen, evtl. mit udevadm trigger&lt;br /&gt;
&lt;br /&gt;
Bei der Gelegenheit bin ich noch ueber eine Beschriftung der unteren der beiden RJ45-Netzwerkbuchsen mit ASF gestolpert. ASF steht fuer [https://en.wikipedia.org/wiki/Alert_Standard_Format Alert Standard Format] der DMTF nicht zu verwechseln mit DTMF;-)&lt;br /&gt;
&lt;br /&gt;
=Grafische Oberflaeche=&lt;br /&gt;
&lt;br /&gt;
Es sei erwaehnt, dass ein startx mit einem User zum Erfolg fuehrt, aber das ist ja keine Dauerloseung.&lt;br /&gt;
Es handelt sich also nicht um die mit jedem Update/Upgrade auftauchenden NVIDA-Treiber Probleme. &lt;br /&gt;
&lt;br /&gt;
Erst einmal das default target kontrolliert.&lt;br /&gt;
 server:~ # systemctl get-default&lt;br /&gt;
 graphical.target&lt;br /&gt;
Das stimmt soweit. Wenn ein target &amp;quot;sein Ziel&amp;quot; nicht erreicht liegt es oft an fehlerhaften Services. &lt;br /&gt;
&lt;br /&gt;
 systemctl list-dependencies graphical.target&lt;br /&gt;
&lt;br /&gt;
In der Ausgabe (Liste von Abhaengigkeiten) sind Dienste, etc. mit einer &amp;quot;farbigen&amp;quot; Markierung versehen.&lt;br /&gt;
* gruen in Ordnung&lt;br /&gt;
* rot Fehler &lt;br /&gt;
* schwarz/grau nicht aktiv(iert)&lt;br /&gt;
&lt;br /&gt;
Ich habe alle &amp;quot;roten Dienste&amp;quot; disabled. z.B.&lt;br /&gt;
 systemctl disable dhcpd&lt;br /&gt;
&lt;br /&gt;
Aber obwohl danach alle Abhaengigkeiten gruen oder schwarz waren, hat es nicht den gewuenschten Erfolg gebracht.&lt;br /&gt;
&lt;br /&gt;
Dann mal weitersuchen. Wo hat systemd seine configs versteckt?&lt;br /&gt;
&lt;br /&gt;
 server:~ # pkg-config systemd --variable=systemdsystemconfdir&lt;br /&gt;
 /etc/systemd/system&lt;br /&gt;
&lt;br /&gt;
 In der Regel in /usr/local/lib/systemd/system und /usr/lib/systemd/system&lt;br /&gt;
&lt;br /&gt;
 Full list of directories is provided in systemd.unit(5).&lt;br /&gt;
&lt;br /&gt;
In dem Verzeichnis /etc/systemd/system gibt es ein Verzeichnis graphical.target.wants in dem sich eine Service-Datei befindet.&lt;br /&gt;
 server:/etc/systemd/system # ll graphical.target.wants/&lt;br /&gt;
 insgesamt 0&lt;br /&gt;
 lrwxrwxrwx 1 root root 47 28. Apr 2019  display-manager.service -&amp;gt; /usr/lib/systemd/system/display-manager.service&lt;br /&gt;
&lt;br /&gt;
Daraufhin mal den Status gescheckt.&lt;br /&gt;
 systemctl status display-manager.service&lt;br /&gt;
Und weird#unknown display-manager active and running. Mit Alt-F7 sieht man tatsaechlich einen Cursor blinken.&lt;br /&gt;
&lt;br /&gt;
 The Simple Desktop Display Manager (SDDM) a display manager. It is the recommended display manager for the KDE Plasma and LXQt desktop environments. &lt;br /&gt;
&lt;br /&gt;
Next weird# &lt;br /&gt;
 server:/etc/systemd/system # psgrep sddm&lt;br /&gt;
 1747 ?        Sl     0:00 /usr/bin/sddm&lt;br /&gt;
 &lt;br /&gt;
 server:/etc/systemd/system # systemctl status sddm&lt;br /&gt;
 ● sddm.service - Simple Desktop Display Manager&lt;br /&gt;
     Loaded: loaded (/usr/lib/systemd/system/sddm.service; disabled; vendor preset: disabled)&lt;br /&gt;
     Active: inactive (dead)&lt;br /&gt;
       Docs: man:sddm(1)&lt;br /&gt;
             man:sddm.conf(5)&lt;br /&gt;
&lt;br /&gt;
Grrrrrrrrr!&lt;br /&gt;
&lt;br /&gt;
Zurueck auf Anfang. Bei ersten Booten (mit fehlerhaften Diensten) ist das System im Emegency-Mode gelandet.&lt;br /&gt;
&lt;br /&gt;
 Emergency and rescue targets&lt;br /&gt;
 Both of these shells require the root password. The emergency target keeps the root ﬁle system mounted read-only, while rescue. target waits for sysinit. target to complete ﬁrst so that more of the system will be initialized, for example, logging, ﬁle systems, etc.&lt;br /&gt;
&lt;br /&gt;
 server:/etc/systemd/system # systemctl --failed&lt;br /&gt;
  UNIT LOAD ACTIVE SUB DESCRIPTION&lt;br /&gt;
  0 loaded units listed.&lt;br /&gt;
&lt;br /&gt;
Bei OpenSuse gibt es keine Xwrapper.config -&amp;gt; weird#++&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(LINUX)_systemctl&amp;diff=4900</id>
		<title>(LINUX) systemctl</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(LINUX)_systemctl&amp;diff=4900"/>
		<updated>2026-05-14T10:28:02Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Grafische Oberflaeche */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Eigentlich koennte ich diese Seite in eine neue Kategorie &#039;weird things&#039; packen.&lt;br /&gt;
&lt;br /&gt;
Um systemctl und damit systemd besser einordnen zu koennen, dient vielleicht folgender [https://www.linux-magazin.de/ausgaben/2011/11/init-systeme/ Artikel].&lt;br /&gt;
&lt;br /&gt;
=Motivation=&lt;br /&gt;
Ich habe 08/2021 meinen Server auf eine neue alte Hardware umgezogen. Da die Server-Hardware (XW8400 auf XW8600; ja ich geb es zu, alt ist gar kein Ausdruck, aber 8 Kerne und 16 GB RAM sind fuer meinen Server ausreichend; vorerst;-)) sich unwesentlich geaendert hat, war es fast damit getan die Festplatten umzustecken. Die wesentlichen Anpassungen bezogen sich auf BIOS und Netzwerk. Nur die grafische Oberflaeche wollte partout nicht starten, daher der Titel.&lt;br /&gt;
&lt;br /&gt;
=BIOS=&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=Netzwerk=&lt;br /&gt;
Da die Zuordnung der Netzdevices ueber die MAC-Adresse geschieht, waren die erwartungsgemaesz falsch. Ich hatte nach dem Start eth3, eth4 und eth5 anstatt eth0-2. BTW: Die dritte Karte ist eine LWL-Karte zur direkten Anbindung an das Backbone. &lt;br /&gt;
Die Zuordnungsdatei ist bei OpenSuse 15.3 unter&lt;br /&gt;
# /etc/udev/rules.d/70-persistent-net.rules&lt;br /&gt;
zu finden.&lt;br /&gt;
Dort habe ich die alten Eintraege geloescht/auskommentiert(#) und die neuen Zuordnungen eingetragen.&lt;br /&gt;
Die Uebernahme war nach einem Reboot richtig. Vielleicht waere es auch ohne Reboot gegangen, evtl. mit udevadm trigger&lt;br /&gt;
&lt;br /&gt;
Bei der Gelegenheit bin ich noch ueber eine Beschriftung der unteren der beiden RJ45-Netzwerkbuchsen mit ASF gestolpert. ASF steht fuer [https://en.wikipedia.org/wiki/Alert_Standard_Format Alert Standard Format] der DMTF nicht zu verwechseln mit DTMF;-)&lt;br /&gt;
&lt;br /&gt;
=Grafische Oberflaeche=&lt;br /&gt;
&lt;br /&gt;
Es sei erwaehnt, dass ein startx mit einem User zum Erfolg fuehrt, aber das ist ja keine Dauerloseung.&lt;br /&gt;
Es handelt sich also nicht um die mit jedem Update/Upgrade auftauchenden NVIDA-Treiber Probleme. &lt;br /&gt;
&lt;br /&gt;
Erst einmal das default target kontrolliert.&lt;br /&gt;
 server:~ # systemctl get-default&lt;br /&gt;
 graphical.target&lt;br /&gt;
Das stimmt soweit. Wenn ein target &amp;quot;sein Ziel&amp;quot; nicht erreicht liegt es oft an fehlerhaften Services. &lt;br /&gt;
&lt;br /&gt;
 systemctl list-dependencies graphical.target&lt;br /&gt;
&lt;br /&gt;
In der Ausgabe (Liste von Abhaengigkeiten) sind Dienste, etc. mit einer &amp;quot;farbigen&amp;quot; Markierung versehen.&lt;br /&gt;
* gruen in Ordnung&lt;br /&gt;
* rot Fehler &lt;br /&gt;
* schwarz/grau nicht aktiv(iert)&lt;br /&gt;
&lt;br /&gt;
Ich habe alle &amp;quot;roten Dienste&amp;quot; disabled. z.B.&lt;br /&gt;
 systemctl disable dhcpd&lt;br /&gt;
&lt;br /&gt;
Aber obwohl danach alle Abhaengigkeiten gruen oder schwarz waren, hat es nicht den gewuenschten Erfolg gebracht.&lt;br /&gt;
&lt;br /&gt;
Dann mal weitersuchen. Wo hat systemd seine configs versteckt?&lt;br /&gt;
&lt;br /&gt;
 server:~ # pkg-config systemd --variable=systemdsystemconfdir&lt;br /&gt;
 /etc/systemd/system&lt;br /&gt;
&lt;br /&gt;
 In der Regel in /usr/local/lib/systemd/system und /usr/lib/systemd/system&lt;br /&gt;
&lt;br /&gt;
 Full list of directories is provided in systemd.unit(5).&lt;br /&gt;
&lt;br /&gt;
In dem Verzeichnis /etc/systemd/system gibt es ein Verzeichnis graphical.target.wants in dem sich eine Service-Datei befindet.&lt;br /&gt;
 server:/etc/systemd/system # ll graphical.target.wants/&lt;br /&gt;
 insgesamt 0&lt;br /&gt;
 lrwxrwxrwx 1 root root 47 28. Apr 2019  display-manager.service -&amp;gt; /usr/lib/systemd/system/display-manager.service&lt;br /&gt;
&lt;br /&gt;
Darauf hin mal den Status gescheckt.&lt;br /&gt;
 systemctl status display-manager.service&lt;br /&gt;
Und weird#unknown display-manager active and running. Mit Alt-F7 sieht man tatsaechlich einen Cursor blinken.&lt;br /&gt;
&lt;br /&gt;
 The Simple Desktop Display Manager (SDDM) a display manager. It is the recommended display manager for the KDE Plasma and LXQt desktop environments. &lt;br /&gt;
&lt;br /&gt;
Next weird# &lt;br /&gt;
 server:/etc/systemd/system # psgrep sddm&lt;br /&gt;
 1747 ?        Sl     0:00 /usr/bin/sddm&lt;br /&gt;
&lt;br /&gt;
server:/etc/systemd/system # systemctl status sddm&lt;br /&gt;
● sddm.service - Simple Desktop Display Manager&lt;br /&gt;
     Loaded: loaded (/usr/lib/systemd/system/sddm.service; disabled; vendor preset: disabled)&lt;br /&gt;
     Active: inactive (dead)&lt;br /&gt;
       Docs: man:sddm(1)&lt;br /&gt;
             man:sddm.conf(5)&lt;br /&gt;
&lt;br /&gt;
Grrrrrrrrr!&lt;br /&gt;
&lt;br /&gt;
Zurueck auf Anfang. Bei ersten Booten (mit fehlerhaften Diensten) ist das System im Emegency-Mode gelandet.&lt;br /&gt;
&lt;br /&gt;
 Emergency and rescue targets&lt;br /&gt;
Both of these shells require the root password. The emergency target keeps the root ﬁle system mounted read-only, while rescue. target waits for sysinit. target to complete ﬁrst so that more of the system will be initialized, for example, logging, ﬁle systems, etc.&lt;br /&gt;
&lt;br /&gt;
 server:/etc/systemd/system # systemctl --failed&lt;br /&gt;
  UNIT LOAD ACTIVE SUB DESCRIPTION&lt;br /&gt;
  0 loaded units listed.&lt;br /&gt;
&lt;br /&gt;
Bei OpenSuse gibt es keine Xwrapper.config -&amp;gt; weird#++&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=(LINUX)_systemctl&amp;diff=4899</id>
		<title>(LINUX) systemctl</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=(LINUX)_systemctl&amp;diff=4899"/>
		<updated>2026-05-14T10:25:51Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Motivation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgemeines=&lt;br /&gt;
Eigentlich koennte ich diese Seite in eine neue Kategorie &#039;weird things&#039; packen.&lt;br /&gt;
&lt;br /&gt;
Um systemctl und damit systemd besser einordnen zu koennen, dient vielleicht folgender [https://www.linux-magazin.de/ausgaben/2011/11/init-systeme/ Artikel].&lt;br /&gt;
&lt;br /&gt;
=Motivation=&lt;br /&gt;
Ich habe 08/2021 meinen Server auf eine neue alte Hardware umgezogen. Da die Server-Hardware (XW8400 auf XW8600; ja ich geb es zu, alt ist gar kein Ausdruck, aber 8 Kerne und 16 GB RAM sind fuer meinen Server ausreichend; vorerst;-)) sich unwesentlich geaendert hat, war es fast damit getan die Festplatten umzustecken. Die wesentlichen Anpassungen bezogen sich auf BIOS und Netzwerk. Nur die grafische Oberflaeche wollte partout nicht starten, daher der Titel.&lt;br /&gt;
&lt;br /&gt;
=BIOS=&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=Netzwerk=&lt;br /&gt;
Da die Zuordnung der Netzdevices ueber die MAC-Adresse geschieht, waren die erwartungsgemaesz falsch. Ich hatte nach dem Start eth3, eth4 und eth5 anstatt eth0-2. BTW: Die dritte Karte ist eine LWL-Karte zur direkten Anbindung an das Backbone. &lt;br /&gt;
Die Zuordnungsdatei ist bei OpenSuse 15.3 unter&lt;br /&gt;
# /etc/udev/rules.d/70-persistent-net.rules&lt;br /&gt;
zu finden.&lt;br /&gt;
Dort habe ich die alten Eintraege geloescht/auskommentiert(#) und die neuen Zuordnungen eingetragen.&lt;br /&gt;
Die Uebernahme war nach einem Reboot richtig. Vielleicht waere es auch ohne Reboot gegangen, evtl. mit udevadm trigger&lt;br /&gt;
&lt;br /&gt;
Bei der Gelegenheit bin ich noch ueber eine Beschriftung der unteren der beiden RJ45-Netzwerkbuchsen mit ASF gestolpert. ASF steht fuer [https://en.wikipedia.org/wiki/Alert_Standard_Format Alert Standard Format] der DMTF nicht zu verwechseln mit DTMF;-)&lt;br /&gt;
&lt;br /&gt;
=Grafische Oberflaeche=&lt;br /&gt;
&lt;br /&gt;
Es sei erwaehnt, dass ein startx mit einem User zum Erfolg fuehrt, aber das ist ja keine Dauerloseung.&lt;br /&gt;
Es handelt sich also nicht um die mit jedem Update/Upgrade auftauchenden NVIDA-Treiber Probleme. &lt;br /&gt;
&lt;br /&gt;
Erst einmal das default target kontrolliert.&lt;br /&gt;
 server:~ # systemctl get-default&lt;br /&gt;
 graphical.target&lt;br /&gt;
Das stimmt soweit. Wenn ein target &amp;quot;sein Ziel&amp;quot; nicht erreicht liegt es oft an fehlerhaften Services. &lt;br /&gt;
&lt;br /&gt;
 systemctl list-dependencies graphical.target&lt;br /&gt;
&lt;br /&gt;
In der Ausgabe (Liste von Abhaengigkeiten) sind Dienste, etc. mit einer &amp;quot;farbigen&amp;quot; Markierung versehen.&lt;br /&gt;
* gruen in Orndung&lt;br /&gt;
* rot Fehler &lt;br /&gt;
* schwarz/grau nicht aktiv(iert)&lt;br /&gt;
&lt;br /&gt;
Ich habe alle &amp;quot;roten Dienste&amp;quot; disabled. z.B.&lt;br /&gt;
 systemctl disable dhcpd&lt;br /&gt;
&lt;br /&gt;
Aber obwohl danach alle Abhaengigkeiten gruen oder schwarz waren, hat es nicht den gewuenschten Erfolg gebracht.&lt;br /&gt;
&lt;br /&gt;
Dann mal weitersuchen. Wo hat systemd seine configs versteckt?&lt;br /&gt;
&lt;br /&gt;
 server:~ # pkg-config systemd --variable=systemdsystemconfdir&lt;br /&gt;
 /etc/systemd/system&lt;br /&gt;
&lt;br /&gt;
 In der Regel in /usr/local/lib/systemd/system und /usr/lib/systemd/system&lt;br /&gt;
&lt;br /&gt;
 Full list of directories is provided in systemd.unit(5).&lt;br /&gt;
&lt;br /&gt;
In dem Verzeichnis /etc/systemd/system gibt es ein Verzeichnis graphical.target.wants in dem sich eine Service-Datei befindet.&lt;br /&gt;
 server:/etc/systemd/system # ll graphical.target.wants/&lt;br /&gt;
 insgesamt 0&lt;br /&gt;
 lrwxrwxrwx 1 root root 47 28. Apr 2019  display-manager.service -&amp;gt; /usr/lib/systemd/system/display-manager.service&lt;br /&gt;
&lt;br /&gt;
Darauf hin mal den Status gescheckt.&lt;br /&gt;
 systemctl status display-manager.service&lt;br /&gt;
Und weird#unknown display-manager active and running. Mit Alt-F7 sieht man tatsaechlich einen Cursor blinken.&lt;br /&gt;
&lt;br /&gt;
 The Simple Desktop Display Manager (SDDM) a display manager. It is the recommended display manager for the KDE Plasma and LXQt desktop environments. &lt;br /&gt;
&lt;br /&gt;
Next weird# &lt;br /&gt;
 server:/etc/systemd/system # psgrep sddm&lt;br /&gt;
 1747 ?        Sl     0:00 /usr/bin/sddm&lt;br /&gt;
&lt;br /&gt;
server:/etc/systemd/system # systemctl status sddm&lt;br /&gt;
● sddm.service - Simple Desktop Display Manager&lt;br /&gt;
     Loaded: loaded (/usr/lib/systemd/system/sddm.service; disabled; vendor preset: disabled)&lt;br /&gt;
     Active: inactive (dead)&lt;br /&gt;
       Docs: man:sddm(1)&lt;br /&gt;
             man:sddm.conf(5)&lt;br /&gt;
&lt;br /&gt;
Grrrrrrrrr!&lt;br /&gt;
&lt;br /&gt;
Zurueck auf Anfang. Bei ersten Booten (mit fehlerhaften Diensten) ist das System im Emegency-Mode gelandet.&lt;br /&gt;
&lt;br /&gt;
 Emergency and rescue targets&lt;br /&gt;
Both of these shells require the root password. The emergency target keeps the root ﬁle system mounted read-only, while rescue. target waits for sysinit. target to complete ﬁrst so that more of the system will be initialized, for example, logging, ﬁle systems, etc.&lt;br /&gt;
&lt;br /&gt;
 server:/etc/systemd/system # systemctl --failed&lt;br /&gt;
  UNIT LOAD ACTIVE SUB DESCRIPTION&lt;br /&gt;
  0 loaded units listed.&lt;br /&gt;
&lt;br /&gt;
Bei OpenSuse gibt es keine Xwrapper.config -&amp;gt; weird#++&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Hauptseite&amp;diff=4898</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Hauptseite&amp;diff=4898"/>
		<updated>2026-05-14T10:20:51Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:SchimpansenProgrammierer.png|200px|rechts|5-jaehriger Schimpanse|link=5-jaehriger Schimpanse]]&lt;br /&gt;
&lt;br /&gt;
==Allgemeines==&lt;br /&gt;
Dieses Wiki erhaelt permanent Erweiterungen. Derzeit beinhaltet dieses Wiki {{NUMBEROFARTICLES}} Artikel. Ich nutze dieses Wiki als persoenliche Informationsquelle zu fast allen Dingen mit denen ich mich so beschaeftige, um sie einfach oeffentlich und schnell zur Hand zu haben. Viele Seiten befinden sich noch im Intranet-Wiki und lassen sich aufgrund von Urheberrechtsbestimmungen nicht ohne Ueberarbeitung in dieses oeffentliche Wiki uebertragen. Wenn also Fragen auftauchen, dann schreibt das bitte in die Kommentare. Ich pruefe dann gezielt, ob ich hierzu einen Beitrag freischalten kann. &lt;br /&gt;
&lt;br /&gt;
Meine Site ist &#039;&#039;&#039;bannerfree&#039;&#039;&#039; und &#039;&#039;&#039;genderfree&#039;&#039;&#039;. Es gibt keine Affiliate-Links und maximal, wenn man eine Empfehlung so deuten will, mal Schleichwerbung. An einem Verzicht auf Umlaute und scharfes S (SZ) arbeite ich.&lt;br /&gt;
&lt;br /&gt;
===Hinweis zu Suchanfragen===&lt;br /&gt;
&lt;br /&gt;
Sinnvoll ist es bei der Wikisuche mindestens &#039;&#039;&#039;4&#039;&#039;&#039; Buchstaben mit voran- und nachgestelltem * (Wildcard) nutzen.&lt;br /&gt;
Zum Beispiel &amp;quot;*Ardu*&amp;quot;, &amp;quot;*FHEM*&amp;quot;, &amp;quot;*ENC2*&amp;quot;.&lt;br /&gt;
Woerter mit 3 Buchstaben findet man auch. Grosz- und Kleinschreibung wird nicht unterschieden. In den runden Klammern vor dem eigentlichen WikiBeitrag steht ein Gruppenname, so sind zusammengehoerende Beitraege besser erkennbar.&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Alle_Seiten|Alle Seiten im Ueberblick]]&lt;br /&gt;
&lt;br /&gt;
==CLX LAB==&lt;br /&gt;
Heute habe ich die Idee eines virtuellen Labors mal verankert. So richtig weisz ich noch nicht wie es genau funktionieren soll, aber der Anfang ist gemacht.&lt;br /&gt;
Einfach ne eMail an tibb(at)clx.de und schon kann&#039;s losgehen. Notwendiges Hardware-Equipment kann ich zur Verfueggung stellen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:CLXLAB Icon.webp|mini]]&lt;br /&gt;
&lt;br /&gt;
Ich suche Mitarbeitende für folgende Projekte:&lt;br /&gt;
- Insect Detect&lt;br /&gt;
- Meshtastic&lt;br /&gt;
- Fox ESS API&lt;br /&gt;
- Victon Energy Cerbo GX Programming&lt;br /&gt;
uvm.&lt;br /&gt;
&lt;br /&gt;
Fundierte Hard- und Softwarekenntnisse sind Voraussetzung.&lt;br /&gt;
&lt;br /&gt;
20260101&lt;br /&gt;
&lt;br /&gt;
Einen Gedanken, den ich ab 2026 mal intensiver verfolgen werde, ist die, ich nenne sie mal, &amp;quot;&#039;&#039;&#039;Selbstlernautomation&#039;&#039;&#039;&amp;quot;. &lt;br /&gt;
Nach meiner aktuellen Vorstellung bedeutet dies, die Realisierung eines Systemes (Regelungssystem, Roboter, Rover, ), welches bei moeglichst optimaler Hardwareanpassung (Aktuatoren, Sensoren) an die Umgebung und Aufgabe, unter zuhilfenahme von Erfahrungsdaten (Datenarchive, Wetterprognosen, ...), einen KI-basierten Regelungsmechanismus nutzt, um die gestellte Aufgabe besser als ein darauf geschulter Mensch zu erfuellen. Weiter geht&#039;s auf der Einstiegsseite im CLXLAB zu [[(CLXLAB) Selbstlernautomation|Selbstlernautomation]].&lt;br /&gt;
&lt;br /&gt;
==Fundstueck der Woche==&lt;br /&gt;
2026-01-04&lt;br /&gt;
&lt;br /&gt;
Sehr informative Wetterseite. [https://www.ventusky.com/de Ventusky]&lt;br /&gt;
&lt;br /&gt;
2025-12-03&lt;br /&gt;
 Und schon wieder Apple. Bei der Firma hat man wohl zu viele Sammeltassen im Schrank. Im Einstellungstest wird angeblich folgendes gefragt: In einer Klasse sind 24 Mädchen und 36 Jungen. Zusammen sind es 100. Wie kann das sein? &lt;br /&gt;
 Da ist eine Personalchefin wohl übers Ziel hinausgeschossen. Mit mathematisch formal korrekten Angaben (Kennzeichnung der Wertangabe durch einen vertikalen Strich mit Hinweis auf das verwendete Zahlensystem) ist die Loesung simpel. Scheinbar knacken Schimpansen den Apple-Einstellungstest am besten.&lt;br /&gt;
&lt;br /&gt;
2025-12-01&lt;br /&gt;
 Und da sind sie wieder die Schimpansen. Diesmal bei Apple. Das kommt davon wenn man als Programmierer nur noch KlickiBuntis einstellt.&lt;br /&gt;
 &lt;br /&gt;
 Bug in iOS: Bei iOS 16–17 gab es mehrfach das Problem, dass nur wenige Einträge angezeigt wurden, die Liste aber viel länger war. &lt;br /&gt;
&lt;br /&gt;
2025-10-07&lt;br /&gt;
 SuS kommen nach der 4. Stunde zum Lehrer. &amp;quot;Wir koennen unmoeglich jetzt den GLN schreiben!&amp;quot; &amp;quot;Unser Mathelehrer hat uns die ersten 4 Stunden gequaelt.&amp;quot; &amp;quot;Wir sind jetzt zu keinem klaren Gedanken mehr faehig!&amp;quot;&lt;br /&gt;
 Lehrer: &amp;quot;Ach Gott, was hat er denn mit euch gemacht?&amp;quot; SuS: &amp;quot;Irgendetwas mit &#039;T&#039;!&amp;quot;&lt;br /&gt;
 Lehrer: &amp;quot;Tangente?&amp;quot; SuS: &amp;quot;Nee, ich glaub Integral oder so.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2025-09-06&lt;br /&gt;
&lt;br /&gt;
 Schenkelklopfer im Buch &amp;quot;Factfulness&amp;quot; [https://en.wikipedia.org/wiki/Factfulness] erstes Kapitel: &amp;quot;Wissenschaftler, Schimpansen und Sie&amp;quot;. Puh, nochmal Glueck gehabt.&lt;br /&gt;
&lt;br /&gt;
2025-08-17&lt;br /&gt;
&lt;br /&gt;
 Nach Jahren mit [https://app.diagrams.net/ draw.io], [https://lucidchart.com lucidcharts], [https://miro.com/ miro] u. v. a. habe ich heute zum ersten Mal hier im Wiki ein Flussdigramm mit [https://www.mermaidchart.com/ mermaid] und [https://graphviz.org/ GraphViz] mit Hilfe der [https://www.mediawiki.org/wiki/Extension:Kroki Kroki-Extension] integriert. Ich bin begeistert.&lt;br /&gt;
&lt;br /&gt;
2025-06-20&lt;br /&gt;
&lt;br /&gt;
 1234 ist kein Passwort! Es ist eine PIN! lol &lt;br /&gt;
&lt;br /&gt;
2025-06-08&lt;br /&gt;
&lt;br /&gt;
 Free Online OCR [https://tools.pdf24.org/de/ocr-pdf] Zum Konvertieren von Foto/Grafik nach Dokument, ohne sensiblen Inhalt, sehr praktikabel.&lt;br /&gt;
&lt;br /&gt;
2025-05-25 &lt;br /&gt;
 &lt;br /&gt;
 Die genialste Erfindung der Chinesen war die GDPR (General Data Protection Regulation, DSGVO). Seit sie diese in die europaeischen Koepfe gepflanzt haben, ist ihr Technologievorsprung nicht mehr einzuholen.  &lt;br /&gt;
&lt;br /&gt;
2025-05-04&lt;br /&gt;
&lt;br /&gt;
 Online Tester fuer htaccess  [https://htaccess.madewithlove.com/]&lt;br /&gt;
 &lt;br /&gt;
2025-01-05&lt;br /&gt;
&lt;br /&gt;
 Langweilst Du dich auch so, wie mich. &lt;br /&gt;
&lt;br /&gt;
2025-01-03&lt;br /&gt;
&lt;br /&gt;
 So viel Schwachsinn hab ich in meinem Leben noch nicht gesehen und die meinen noch sie machens besonders gudd.&lt;br /&gt;
2024-09-06&lt;br /&gt;
&lt;br /&gt;
 The oldest computer was owned by Adam and Eve. It was an Apple. Only one wrong byte and everything was crashed!&lt;br /&gt;
&lt;br /&gt;
2024-07-13&lt;br /&gt;
&lt;br /&gt;
 Der drollige Troll: trollig oder drollig? &lt;br /&gt;
 *Trollig von Troll (nordisches Fabelwesen (ungehobelt, grob oder ungeschickt)) Hat eine leicht negative oder scherzhafte Konnotation.&lt;br /&gt;
 *Drollig von Drol(l) (altdeutsch Possenreißer, Spaszmacher, (Hof-)Narr, Clown) Hat eine positive, niedliche oder humorvolle Bedeutung. &lt;br /&gt;
&lt;br /&gt;
2024-06-28&lt;br /&gt;
&lt;br /&gt;
 Suno AI Explore [https://suno.com/explore] eignet sich durchaus als Radioersatz!.&lt;br /&gt;
&lt;br /&gt;
2024-01-01&lt;br /&gt;
&lt;br /&gt;
 Nach einer (sehr) langen Suche nach einem (dauerhaft) hoerbaren Sender aus dem Groszraum Los Angeles:&lt;br /&gt;
 Popstar!Radio Hollywood https://streaming.live365.com/a40380&lt;br /&gt;
&lt;br /&gt;
2023-04-24&lt;br /&gt;
&lt;br /&gt;
 Ausbildungsklasse: Mechatroniker&amp;lt;br&amp;gt;&lt;br /&gt;
 Thema: Kräftezerlegung&amp;lt;br&amp;gt;&lt;br /&gt;
 Schuelerreaktion: Geben Sie (Lehrer) sich keine Muehe, Physik habe ich noch nie verstanden!&lt;br /&gt;
&lt;br /&gt;
2023-04-21&lt;br /&gt;
&lt;br /&gt;
 Verzweifelter Lehrer im Lehrerzimmer nach der Korrektur eines Leistungsnachweises:&amp;lt;br&amp;gt;&lt;br /&gt;
 Jetzt habe ich mit denen wochenlang Kraefteparallelogramme geuebt und die geben mir in der Klassenarbeit lauder Drache ab. Arrrrrr!&lt;br /&gt;
&lt;br /&gt;
2023-01-28&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;Meine Erfahrung zeigt, dass auf den groeszten Haufen immer noch einer drauf geschissen wird.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2022-11-16&lt;br /&gt;
&lt;br /&gt;
 „Wir leben alle unter dem gleichen Himmel, aber wir haben nicht alle den gleichen Horizont.“ Konrad Adenauer&lt;br /&gt;
&lt;br /&gt;
2022-10-02&lt;br /&gt;
&lt;br /&gt;
 Logik-Simulator: [https://academo.org/demos/logic-gate-simulator/]&lt;br /&gt;
&lt;br /&gt;
2022-08-04&lt;br /&gt;
&lt;br /&gt;
 Die Selbsthilfegruppe „UTF-8-Probleme“ trifft sich diesmal abweichend im grÃƒÂ¼nen Saal.&lt;br /&gt;
&lt;br /&gt;
2021-04-03&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;Bildung im 3D-Raum ist um einiges besser als Unterricht per Zoom-Call&amp;quot;, sagt Ball. Cybersecurity-Experte Baggili stimmt dem zu: &amp;quot;Der wahre Mehrwert des Metaverses zeigt sich vor allem dann, wenn es dafür eingesetzt wird, unser Leben zu bereichern&amp;quot;, sagt er. &lt;br /&gt;
&lt;br /&gt;
[[Fundstücke Archiv]]&lt;br /&gt;
&lt;br /&gt;
==Weisheiten aus dem Schulalltag==&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Lehrer&#039;&#039;&#039;: Lest euch zur Eingewoehnung in das neue Thema im Buch einmal die Seiten 42 bis 45 durch.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;Schüler&#039;&#039;&#039;: Wie 4 Seiten lesen! Das ist nicht Ihr Ernst.&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Schüler&#039;&#039;&#039;: Herr ... ! Das was Sie mit uns machen ist Quaelerei! &lt;br /&gt;
 Frueher habe ich 3 Kopien bekommen, musste die für die Arbeit auswendig lernen, habe meine 1 bekommen und gut war. &lt;br /&gt;
 Heute muss ich mir den Stoff selbst erarbeiten und den Quatsch fuer die Arbeit auch noch verstehen!&lt;br /&gt;
&lt;br /&gt;
mehr -&amp;gt; [[Schenkelklopfer]]&lt;br /&gt;
&lt;br /&gt;
==Die Leiden des Supporters==&lt;br /&gt;
&lt;br /&gt;
[[Datei:LED Rotation Daumenhoch 7Seg 200.gif|mini]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Kunde&#039;&#039;&#039;: Das Ding tut nicht was es soll!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Supporter&#039;&#039;&#039;: Haben Sie im Handbuch die Anleitung gelesen?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Kunde&#039;&#039;&#039;: JA! Ziehen Sie den Nippel durch die Lasche und dann noch 10 Seiten Blabla.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Supporter&#039;&#039;&#039;: Ja genau! Ihre Lösung steht im Blabla!&lt;br /&gt;
&lt;br /&gt;
==Die drei großen Lügen der EDV-Administratoren==&lt;br /&gt;
&lt;br /&gt;
# Ich weiß woran es liegt!&lt;br /&gt;
# Ich komme sofort!&lt;br /&gt;
# Jetzt funktioniert es wieder!&lt;br /&gt;
&lt;br /&gt;
Variante 2:&lt;br /&gt;
&lt;br /&gt;
1) Ich glaub, ich weiß, woran das liegt.&lt;br /&gt;
&lt;br /&gt;
2) Ich komme gleich vorbei.&lt;br /&gt;
&lt;br /&gt;
3) Jetzt sollte es funktionieren.&lt;br /&gt;
&lt;br /&gt;
==3 Arten von Fehlern==&lt;br /&gt;
* &#039;&#039;&#039;Heisenbugs&#039;&#039;&#039;: Fehler, die verschwinden, wenn man versucht, sie zu debuggen.&lt;br /&gt;
* &#039;&#039;&#039;Schrödingbugs&#039;&#039;&#039;: Fehler, die nur auftauchen, wenn man versucht, etwas anderes zu debuggen.&lt;br /&gt;
* &#039;&#039;&#039;Mandelbugs&#039;&#039;&#039;: komplexe Fehler, die sich mehr und mehr ins Chaotische steigern, je genauer man hinsieht.&lt;br /&gt;
Quelle: Damian Conway: Perl - Best Practices&lt;br /&gt;
&lt;br /&gt;
==Elektrikerweisheit==&lt;br /&gt;
&lt;br /&gt;
Strom macht ganz schnell klein, schwarz und haesslich ;)&lt;br /&gt;
&lt;br /&gt;
==Ich werd bekloppt!==&lt;br /&gt;
&lt;br /&gt;
Seit Jahren kämpfe ich als (Mathematik-)Lehrer gegen eine zunehmende Seuche, in der Sekundarstufe 2 (Klassen 10+). Jahr für Jahr können immer weniger Schueler mit mathematischen Formeln und Termen umgehen. Seit ein paar Jahren kommt noch hinzu, dass durch die Handy- und Tablet-Seuche immer weniger Schueler etwas mit einem PC anfangen koennen und das logische Denken beim Entwickeln von Algorithmen viele voellig ueberfordert. Folglich sind die Schuelerzahlen im Bereich Technik und Informatik stark ruecklaeufig. &lt;br /&gt;
&lt;br /&gt;
Und jetzt finde ich durch Zufall diesen [https://scienceblogs.de/mathlog/2020/03/13/macht-mengenlehre-krank/ Artikel], weil einer meiner letzten Ideen war, mich notgedrungen in Mathematik von Zahlen und Buchstaben zu trennen und eine abstrakte Form des Umgangs mit (mathematischen) Zusammenhaengen zu suchen. Ich erinnerte mich an meine Grundschulzeit und die Mengenlehre. &lt;br /&gt;
&lt;br /&gt;
Ich profitiere heute noch davon, in der Grundschule diesen &amp;quot;Mathematikunterricht&amp;quot; gehabt zu haben. Und wenn man die Kommentare unter dem Artikel liest, sind nicht wenige meiner Meinung und erstaunlicherweise viele scheinbar auch im Bereich der Softwareentwicklung taetig.&lt;br /&gt;
Ich lehne mich mal weit aus dem Fenster: &amp;quot;In der Grundschule braucht man in Mathematik nur Mengenlehre und Kopfrechnen als gesunde Basis!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Weisheiten zum Nachdenken==&lt;br /&gt;
&lt;br /&gt;
 Mit deinem Gehirn waere mir langweilig.&lt;br /&gt;
&lt;br /&gt;
 Neid ist die höchste Form der Anerkennung.&lt;br /&gt;
&lt;br /&gt;
 Geruechte sind oft Luegen mit Widerhaken.&lt;br /&gt;
&lt;br /&gt;
 Nichtbeachtung ist die hoechste Form der Missachtung.&lt;br /&gt;
&lt;br /&gt;
==Spruch des Tages==&lt;br /&gt;
&lt;br /&gt;
 Beim Nutzen von KI basierten Systemen gelten aehnliche Prinzipien wie in der Finanzwelt: Die Gebildeten machen Quantenspruenge der Erkenntnis, so wie die Reichen immer reicher werden, während Kreti und Plethi über zu wenig Geld jammern und stolz wie Oskar sind, wenn sie mithilfe von Bildgeneratoren einem Frosch Schamhaare auf den Kopf gezaubert haben.&lt;br /&gt;
&lt;br /&gt;
 Der o. g. Spruch kommt mir vor wie eine angepasste Form von: Der Laie staunt, der Fachmann wundert sich.&lt;br /&gt;
&lt;br /&gt;
 Holt mal einer den Pfarrer fuer die Reste des Deputys&lt;br /&gt;
&lt;br /&gt;
 Trivialitaeten sind des Kleingeists Himmelreich&lt;br /&gt;
&lt;br /&gt;
==Kaffeespende==&lt;br /&gt;
&lt;br /&gt;
Wer bei mir mal vorbei kommen moechte, dem spendiere ich einen Kaffee.&lt;br /&gt;
&lt;br /&gt;
[https://citymapper.com/directions?startcoord=&amp;amp;endcoord=49.400455%2C7.17435&amp;amp;endname=%2F%2F%2Fbeginn.krippe.ortschaft BECOMEACOFFEE ;-)]&lt;br /&gt;
&lt;br /&gt;
Wer unbedingt Geld los werden will, sollte das lieber jemandem geben, der es dringender benoetigt.&lt;br /&gt;
&lt;br /&gt;
z.B. [https://www.paypal.com/DE/fundraiser/charity/3961454 world&#039;s education for kids e.V.]&lt;br /&gt;
&lt;br /&gt;
Und nein ich stehe nicht in Verbindung mit einer der Spendenorganisationen und auch nicht mit [https://www.paypal.com/fundraiser/hub Paypal Spenden].&lt;br /&gt;
&lt;br /&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=Aermotor_Wires_Tracker&amp;diff=4897</id>
		<title>Aermotor Wires Tracker</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Aermotor_Wires_Tracker&amp;diff=4897"/>
		<updated>2026-05-09T06:55:46Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Allgmeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgmeines=&lt;br /&gt;
Ich bin beim Ali ueber das Geraet gestoplert und habe mir gedacht, fuer 12 Euro kann ich das mal ausprobieren.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
[[File:AermotorWiresTracker.jpg|400px]] &lt;br /&gt;
&lt;br /&gt;
Bei einem drahtlosen Tracker von Aermotor (wie dem AER102 oder EM415PRO) bezeichnen die Begriffe „Tone“ und „Cont“ (oft fälschlicherweise als „Count“ gelesen) die beiden primären Betriebsmodi des Senders. [1, 2, 3]&lt;br /&gt;
&lt;br /&gt;
==Tone (Tracing Mode)==&lt;br /&gt;
Die Tonfunktion dient zum aktiven Aufspüren eines bestimmten Kabels. [2, 4]&lt;br /&gt;
&lt;br /&gt;
* Funktionsweise: Der Sender sendet ein hochfrequentes Audiosignal (entweder einen einzelnen Dauerton oder zwei abwechselnde Töne) durch das Kabel.&lt;br /&gt;
&lt;br /&gt;
* Anwendung: Wenn der Sender auf „Ton“ eingestellt ist, scannen Sie mit der Handsonde des Empfängers die Nähe des Kabels. Der Empfänger gibt einen Piepton oder ein vibrierendes Geräusch aus, das lauter wird, je näher Sie dem richtigen Kabel oder der Bruchstelle kommen. [1, 2, 3, 4, 5, 6, 7]&lt;br /&gt;
&lt;br /&gt;
==Cont / Continuity (Testing Mode)==&lt;br /&gt;
Die Einstellung CONT (kurz für Durchgangsprüfung) dient zur Überprüfung, ob ein Stromkreis geschlossen oder unterbrochen ist. [3, 8, 9]&lt;br /&gt;
&lt;br /&gt;
* Funktion: Sie prüft den Stromkreis zwischen den beiden Klemmen des Senders.&lt;br /&gt;
&lt;br /&gt;
* Interpretation der LED:&lt;br /&gt;
&lt;br /&gt;
* Geschlossener Stromkreis: Bei geschlossenem Stromkreis (keine Unterbrechung) leuchtet die „CONT“-LED am Sender in der Regel hell (oft grün oder rot, abhängig von der Polarität).&lt;br /&gt;
&lt;br /&gt;
* Unterbrechung: Leuchtet die LED nicht oder nur schwach, deutet dies auf eine Unterbrechung oder einen hohen Widerstand im Kabel hin.&lt;br /&gt;
&lt;br /&gt;
=Zusammenfassung=&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Feature [1, 2, 3, 10] | Meaning | Primary Use |&lt;br /&gt;
&lt;br /&gt;
|---|---|---|&lt;br /&gt;
&lt;br /&gt;
| Tone | Signal Transmission | Tracing wires behind panels or finding a specific wire in a bundle. |&lt;br /&gt;
&lt;br /&gt;
| Cont | Continuity Check | Checking for breaks (open circuits) or shorts without needing the receiver. |&lt;br /&gt;
&lt;br /&gt;
=Links=&lt;br /&gt;
&lt;br /&gt;
[1] [https://www.aliexpres.com](https://www.aliexpres.com/item/1005005757654493.html)&lt;br /&gt;
&lt;br /&gt;
[2] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;br /&gt;
&lt;br /&gt;
[3] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;br /&gt;
&lt;br /&gt;
[4] [https://www.aliexpres.com](https://www.aliexpres.com/item/1005005757654493.html)&lt;br /&gt;
&lt;br /&gt;
[5] [https://www.youtube.com](https://www.youtube.com/watch?v=qQHcZgMC-Iw&amp;amp;t=29)&lt;br /&gt;
&lt;br /&gt;
[6] [https://www.flukenetworks.com](https://www.flukenetworks.com/knowledge-base/dtx-cableanalyzer/tone-generator-dtx-cableanalyzer)&lt;br /&gt;
&lt;br /&gt;
[7] [https://www.triplett.com](https://www.triplett.com/blogs/news/a-step-by-step-guide-to-use-a-wire-tracer)&lt;br /&gt;
&lt;br /&gt;
[8] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/downloads/TIS888InstructionManual.pdf)&lt;br /&gt;
&lt;br /&gt;
[9] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/downloads/TIS888InstructionManual.pdf)&lt;br /&gt;
&lt;br /&gt;
[10] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/knowledgebase-article/what-is-a-circuit-tester-or-tone-generator)&lt;br /&gt;
&lt;br /&gt;
[11] [https://images-na.ssl-images-amazon.com](https://images-na.ssl-images-amazon.com/images/I/71oGZIKeXPL.pdf)&lt;br /&gt;
&lt;br /&gt;
[12] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Aermotor_Wires_Tracker&amp;diff=4896</id>
		<title>Aermotor Wires Tracker</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Aermotor_Wires_Tracker&amp;diff=4896"/>
		<updated>2026-05-09T06:55:13Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* Zusammenfassung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgmeines=&lt;br /&gt;
Ich bin beim Ali ueber das Geraet gestoplert und mir gedacht, fuer 12 Euro kann ich das mal ausprobieren.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
[[File:AermotorWiresTracker.jpg|400px]] &lt;br /&gt;
&lt;br /&gt;
Bei einem drahtlosen Tracker von Aermotor (wie dem AER102 oder EM415PRO) bezeichnen die Begriffe „Tone“ und „Cont“ (oft fälschlicherweise als „Count“ gelesen) die beiden primären Betriebsmodi des Senders. [1, 2, 3]&lt;br /&gt;
&lt;br /&gt;
==Tone (Tracing Mode)==&lt;br /&gt;
Die Tonfunktion dient zum aktiven Aufspüren eines bestimmten Kabels. [2, 4]&lt;br /&gt;
&lt;br /&gt;
* Funktionsweise: Der Sender sendet ein hochfrequentes Audiosignal (entweder einen einzelnen Dauerton oder zwei abwechselnde Töne) durch das Kabel.&lt;br /&gt;
&lt;br /&gt;
* Anwendung: Wenn der Sender auf „Ton“ eingestellt ist, scannen Sie mit der Handsonde des Empfängers die Nähe des Kabels. Der Empfänger gibt einen Piepton oder ein vibrierendes Geräusch aus, das lauter wird, je näher Sie dem richtigen Kabel oder der Bruchstelle kommen. [1, 2, 3, 4, 5, 6, 7]&lt;br /&gt;
&lt;br /&gt;
==Cont / Continuity (Testing Mode)==&lt;br /&gt;
Die Einstellung CONT (kurz für Durchgangsprüfung) dient zur Überprüfung, ob ein Stromkreis geschlossen oder unterbrochen ist. [3, 8, 9]&lt;br /&gt;
&lt;br /&gt;
* Funktion: Sie prüft den Stromkreis zwischen den beiden Klemmen des Senders.&lt;br /&gt;
&lt;br /&gt;
* Interpretation der LED:&lt;br /&gt;
&lt;br /&gt;
* Geschlossener Stromkreis: Bei geschlossenem Stromkreis (keine Unterbrechung) leuchtet die „CONT“-LED am Sender in der Regel hell (oft grün oder rot, abhängig von der Polarität).&lt;br /&gt;
&lt;br /&gt;
* Unterbrechung: Leuchtet die LED nicht oder nur schwach, deutet dies auf eine Unterbrechung oder einen hohen Widerstand im Kabel hin.&lt;br /&gt;
&lt;br /&gt;
=Zusammenfassung=&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Feature [1, 2, 3, 10] | Meaning | Primary Use |&lt;br /&gt;
&lt;br /&gt;
|---|---|---|&lt;br /&gt;
&lt;br /&gt;
| Tone | Signal Transmission | Tracing wires behind panels or finding a specific wire in a bundle. |&lt;br /&gt;
&lt;br /&gt;
| Cont | Continuity Check | Checking for breaks (open circuits) or shorts without needing the receiver. |&lt;br /&gt;
&lt;br /&gt;
=Links=&lt;br /&gt;
&lt;br /&gt;
[1] [https://www.aliexpres.com](https://www.aliexpres.com/item/1005005757654493.html)&lt;br /&gt;
&lt;br /&gt;
[2] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;br /&gt;
&lt;br /&gt;
[3] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;br /&gt;
&lt;br /&gt;
[4] [https://www.aliexpres.com](https://www.aliexpres.com/item/1005005757654493.html)&lt;br /&gt;
&lt;br /&gt;
[5] [https://www.youtube.com](https://www.youtube.com/watch?v=qQHcZgMC-Iw&amp;amp;t=29)&lt;br /&gt;
&lt;br /&gt;
[6] [https://www.flukenetworks.com](https://www.flukenetworks.com/knowledge-base/dtx-cableanalyzer/tone-generator-dtx-cableanalyzer)&lt;br /&gt;
&lt;br /&gt;
[7] [https://www.triplett.com](https://www.triplett.com/blogs/news/a-step-by-step-guide-to-use-a-wire-tracer)&lt;br /&gt;
&lt;br /&gt;
[8] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/downloads/TIS888InstructionManual.pdf)&lt;br /&gt;
&lt;br /&gt;
[9] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/downloads/TIS888InstructionManual.pdf)&lt;br /&gt;
&lt;br /&gt;
[10] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/knowledgebase-article/what-is-a-circuit-tester-or-tone-generator)&lt;br /&gt;
&lt;br /&gt;
[11] [https://images-na.ssl-images-amazon.com](https://images-na.ssl-images-amazon.com/images/I/71oGZIKeXPL.pdf)&lt;br /&gt;
&lt;br /&gt;
[12] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Aermotor_Wires_Tracker&amp;diff=4895</id>
		<title>Aermotor Wires Tracker</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Aermotor_Wires_Tracker&amp;diff=4895"/>
		<updated>2026-05-09T06:54:35Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Die Seite wurde neu angelegt: „=Allgmeines= Ich bin beim Ali ueber das Geraet gestoplert und mir gedacht, fuer 12 Euro kann ich das mal ausprobieren.  TODO  400px   Bei einem drahtlosen Tracker von Aermotor (wie dem AER102 oder EM415PRO) bezeichnen die Begriffe „Tone“ und „Cont“ (oft fälschlicherweise als „Count“ gelesen) die beiden primären Betriebsmodi des Senders. [1, 2, 3]  ==Tone (Tracing Mode)== Die Tonfunktion dient zum aktiven Auf…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Allgmeines=&lt;br /&gt;
Ich bin beim Ali ueber das Geraet gestoplert und mir gedacht, fuer 12 Euro kann ich das mal ausprobieren.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
[[File:AermotorWiresTracker.jpg|400px]] &lt;br /&gt;
&lt;br /&gt;
Bei einem drahtlosen Tracker von Aermotor (wie dem AER102 oder EM415PRO) bezeichnen die Begriffe „Tone“ und „Cont“ (oft fälschlicherweise als „Count“ gelesen) die beiden primären Betriebsmodi des Senders. [1, 2, 3]&lt;br /&gt;
&lt;br /&gt;
==Tone (Tracing Mode)==&lt;br /&gt;
Die Tonfunktion dient zum aktiven Aufspüren eines bestimmten Kabels. [2, 4]&lt;br /&gt;
&lt;br /&gt;
* Funktionsweise: Der Sender sendet ein hochfrequentes Audiosignal (entweder einen einzelnen Dauerton oder zwei abwechselnde Töne) durch das Kabel.&lt;br /&gt;
&lt;br /&gt;
* Anwendung: Wenn der Sender auf „Ton“ eingestellt ist, scannen Sie mit der Handsonde des Empfängers die Nähe des Kabels. Der Empfänger gibt einen Piepton oder ein vibrierendes Geräusch aus, das lauter wird, je näher Sie dem richtigen Kabel oder der Bruchstelle kommen. [1, 2, 3, 4, 5, 6, 7]&lt;br /&gt;
&lt;br /&gt;
==Cont / Continuity (Testing Mode)==&lt;br /&gt;
Die Einstellung CONT (kurz für Durchgangsprüfung) dient zur Überprüfung, ob ein Stromkreis geschlossen oder unterbrochen ist. [3, 8, 9]&lt;br /&gt;
&lt;br /&gt;
* Funktion: Sie prüft den Stromkreis zwischen den beiden Klemmen des Senders.&lt;br /&gt;
&lt;br /&gt;
* Interpretation der LED:&lt;br /&gt;
&lt;br /&gt;
* Geschlossener Stromkreis: Bei geschlossenem Stromkreis (keine Unterbrechung) leuchtet die „CONT“-LED am Sender in der Regel hell (oft grün oder rot, abhängig von der Polarität).&lt;br /&gt;
&lt;br /&gt;
* Unterbrechung: Leuchtet die LED nicht oder nur schwach, deutet dies auf eine Unterbrechung oder einen hohen Widerstand im Kabel hin.&lt;br /&gt;
&lt;br /&gt;
=Zusammenfassung=&lt;br /&gt;
Tabelle TODO&lt;br /&gt;
&lt;br /&gt;
| Feature [1, 2, 3, 10] | Meaning | Primary Use |&lt;br /&gt;
|---|---|---|&lt;br /&gt;
| Tone | Signal Transmission | Tracing wires behind panels or finding a specific wire in a bundle. |&lt;br /&gt;
| Cont | Continuity Check | Checking for breaks (open circuits) or shorts without needing the receiver. |&lt;br /&gt;
&lt;br /&gt;
=Links=&lt;br /&gt;
&lt;br /&gt;
[1] [https://www.aliexpres.com](https://www.aliexpres.com/item/1005005757654493.html)&lt;br /&gt;
&lt;br /&gt;
[2] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;br /&gt;
&lt;br /&gt;
[3] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;br /&gt;
&lt;br /&gt;
[4] [https://www.aliexpres.com](https://www.aliexpres.com/item/1005005757654493.html)&lt;br /&gt;
&lt;br /&gt;
[5] [https://www.youtube.com](https://www.youtube.com/watch?v=qQHcZgMC-Iw&amp;amp;t=29)&lt;br /&gt;
&lt;br /&gt;
[6] [https://www.flukenetworks.com](https://www.flukenetworks.com/knowledge-base/dtx-cableanalyzer/tone-generator-dtx-cableanalyzer)&lt;br /&gt;
&lt;br /&gt;
[7] [https://www.triplett.com](https://www.triplett.com/blogs/news/a-step-by-step-guide-to-use-a-wire-tracer)&lt;br /&gt;
&lt;br /&gt;
[8] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/downloads/TIS888InstructionManual.pdf)&lt;br /&gt;
&lt;br /&gt;
[9] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/downloads/TIS888InstructionManual.pdf)&lt;br /&gt;
&lt;br /&gt;
[10] [https://www.testinstrumentsolutions.co.uk](https://www.testinstrumentsolutions.co.uk/knowledgebase-article/what-is-a-circuit-tester-or-tone-generator)&lt;br /&gt;
&lt;br /&gt;
[11] [https://images-na.ssl-images-amazon.com](https://images-na.ssl-images-amazon.com/images/I/71oGZIKeXPL.pdf)&lt;br /&gt;
&lt;br /&gt;
[12] [https://m.tvcmall.com](https://m.tvcmall.com/details/aermotor-aer102-em415pro-car-circuit-tester-line-checker-detector-monitoring-tool-sku671200683a.html)&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Datei:AermotorWiresTracker.jpg&amp;diff=4894</id>
		<title>Datei:AermotorWiresTracker.jpg</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Datei:AermotorWiresTracker.jpg&amp;diff=4894"/>
		<updated>2026-05-09T06:47:44Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Datei hochgeladen mit MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Datei hochgeladen mit MsUpload&lt;/div&gt;</summary>
		<author><name>Chris T. Ludwig</name></author>
	</entry>
	<entry>
		<id>https://tippvomtibb.de/wiki/index.php?title=Reolink&amp;diff=4893</id>
		<title>Reolink</title>
		<link rel="alternate" type="text/html" href="https://tippvomtibb.de/wiki/index.php?title=Reolink&amp;diff=4893"/>
		<updated>2026-05-03T05:59:22Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: Die Seite wurde neu angelegt: „=Kameras=  ==RLC-810A== Build No.build 23062505 Hardware No.IPC_56064M8MP Config Versionv3.1.0.0 Firmware Versionv3.1.0.2368_23062505 DetailsIPC_56064M8MPS18E1W03400000008  Die von den KIs vorgeschlagene Streaming-URLs   Main Stream  rtsp://admin:password@ip_address:554/h264Preview_01_main   Sub Stream  rtsp://admin:password@ip_address:554/h264Preview_01_sub  funktionieren bei mir“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Kameras=&lt;br /&gt;
&lt;br /&gt;
==RLC-810A==&lt;br /&gt;
Build No.build 23062505&lt;br /&gt;
Hardware No.IPC_56064M8MP&lt;br /&gt;
Config Versionv3.1.0.0&lt;br /&gt;
Firmware Versionv3.1.0.2368_23062505&lt;br /&gt;
DetailsIPC_56064M8MPS18E1W03400000008&lt;br /&gt;
&lt;br /&gt;
Die von den KIs vorgeschlagene Streaming-URLs&lt;br /&gt;
&lt;br /&gt;
 Main Stream&lt;br /&gt;
 rtsp://admin:password@ip_address:554/h264Preview_01_main&lt;br /&gt;
&lt;br /&gt;
 Sub Stream&lt;br /&gt;
 rtsp://admin:password@ip_address:554/h264Preview_01_sub&lt;br /&gt;
&lt;br /&gt;
funktionieren bei mir&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=4892</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=4892"/>
		<updated>2026-05-02T13:06:03Z</updated>

		<summary type="html">&lt;p&gt;Chris T. Ludwig: /* grid.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_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>
</feed>