<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Rstooks21</id>
	<title>FHEMWiki - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Rstooks21"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Rstooks21"/>
	<updated>2026-04-16T05:28:57Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39048</id>
		<title>Benutzer:Rstooks21/Kiosk UI</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39048"/>
		<updated>2024-01-23T18:48:13Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Further details about lighting process control&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle en}}&lt;br /&gt;
&lt;br /&gt;
==Kiosk UI==&lt;br /&gt;
This is an approach to the dynamic creation of a Kiosk UI that can be used by anyone visiting the home. There will be multiple, small, possibly wall mounted Kiosk screens - one in each physical room. The Kiosk UI controls only the devices that are located in the same [FHEM] Room.&lt;br /&gt;
&lt;br /&gt;
== The Objective ==&lt;br /&gt;
To use the new FHEM Tablet UI (FTUI3) to create a tabbed UI showing just the devices in the same FHEM room.  This tabbed UI allows the most important device controls to be shown by default and at the same time allow other devices to be controlled in as few clicks/screen touches as possible.  As well as this, each Panel should not ever display scroll bars.&lt;br /&gt;
&lt;br /&gt;
The rooms selected for testing had some or all of:&lt;br /&gt;
&lt;br /&gt;
* Lighting (Shelly RGDW LED Controller and Shelly DImmer 2, defined to FHEM as Shelly devices)&lt;br /&gt;
* Audio (Logitech Media Players defined to FHEM as SBPLAYER devices)&lt;br /&gt;
* Heating (EQ3 MAX! WallMountedThermostat and RadiatorThermostat, defined as MAX devices)&lt;br /&gt;
&lt;br /&gt;
With a simple call, the Kiosk displays the default Panel, with dynamic tabs for other device control Panels (a tab will only be shown if the room has that type of device).  The default Panel will be implemented in the following order:&lt;br /&gt;
&lt;br /&gt;
* Lighting first&lt;br /&gt;
* If no Lighting, then Audio&lt;br /&gt;
* If no Lighting or Audio, then Heating&lt;br /&gt;
* If no supported devices are in the room, then the clock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This web address is intended to be used in a browser in Kiosk mode&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
http[s]://fhem-location:8083/fhem/kiosk/kiosk.html?room=Kitchen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The &amp;quot;?room=Kitchen&amp;quot; is used to filter devices in the FHEM room &amp;quot;Kitchen&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== The Panels ==&lt;br /&gt;
All the panels time out to the primary panel after 60 seconds of non use.&lt;br /&gt;
&lt;br /&gt;
=== The Lighting Panels ===&lt;br /&gt;
&lt;br /&gt;
==== Main Panel ====&lt;br /&gt;
The main Lighting Panel is a simple On/Off switch and dimmer slider.  The single control will control all of the lights in the room equally&lt;br /&gt;
&lt;br /&gt;
* turning them all off or on&lt;br /&gt;
* dimming all lights in proportion&lt;br /&gt;
&lt;br /&gt;
[[Datei:KioskLightingMainPanel.png|alternativtext=An on/off switch and a slider used for dimming the lights|ohne|mini|An FTUI3 Main Lighting control panel ]]&lt;br /&gt;
Note the four dynamic tabs: &lt;br /&gt;
&lt;br /&gt;
* Lighting (currently active) &lt;br /&gt;
* Music&lt;br /&gt;
* Heating&lt;br /&gt;
* Settings - The Lighting tab has further settings that can be adjusted.&lt;br /&gt;
&lt;br /&gt;
==== Settings/Scene Selector Panel ====&lt;br /&gt;
Selecting the &amp;quot;Settings&amp;quot; tab allows the user to select a scene - a combination of light settings that have been previously saved (by the user in the Kiosk UI)&lt;br /&gt;
[[Datei:KioskLightingScenes.png|alternativtext=A grid of four buttons in a panel display|ohne|mini|FTUI Grids and Buttons used to save and recall scenes]]&lt;br /&gt;
The light settings for all lights in the room are recalled by a short press to any of the buttons. &lt;br /&gt;
&lt;br /&gt;
The current light settings for all lights in the room are stored as a scene by pressing and holding the relevant button. This causes the scene to be saved and the button to glow with the average light colour and intensity for all lights that are on.&lt;br /&gt;
&lt;br /&gt;
==== The Details Panel ====&lt;br /&gt;
From the Scene Selector Panel it is possible to control every light in the room individually.   The Details Panels are flexed to display up to three lights each with as many Panels as needed:&lt;br /&gt;
[[Datei:KioskLightingDetail.png|alternativtext=An on/off switch and four sliders in Red, Green, Blue and White|ohne|mini|FTUI3 Switch and Sliders for RGBW control of a single led strip]]&lt;br /&gt;
&lt;br /&gt;
=== The Audio Panel ===&lt;br /&gt;
The Audio Panel uses an IFRAME to embed the Logitech Media Server web interface into the panel.   Only the player in the room can be controlled - the other players cannot be unless they are synchronised.   This is a bit of a cheat as the facilities in FHEM to control the player and display album art are not used.  But why reinvent?&lt;br /&gt;
[[Datei:KioskAudioPlayer.png|alternativtext=A media player display with a second tab for heating control|ohne|mini|The IFRAME containing the web interface at the top (90% height) and the two tabs at the bottom.  One for the Music and a second for the Heating.]]&lt;br /&gt;
&lt;br /&gt;
=== The Heating Panel ===&lt;br /&gt;
The Heating Panel prioritises control of the room&#039;s temperature using a Wall Mounted Thermostat, if one is available.  If not, it is assumed that one of the Radiator Thermostats can be used and that all, if more than one, are linked together.&lt;br /&gt;
&lt;br /&gt;
The Heating Panel just allows the setting of the temperature in the same room.  None of the other room&#039;s temperatures may be set, nor can any &amp;quot;scenes&amp;quot; be set.&lt;br /&gt;
[[Datei:KioskHeatingThermostat.png|alternativtext=A dial with a gradient to show actual temperature and a needle to show desired temperature|ohne|mini|An FTUI3 Compound Thermostat showing Actual and Desired Temperature]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
The FTUI3 code, available from GITHUB should be downloaded and added to FHEM following the instructions here:   https://github.com/knowthelist/ftui&lt;br /&gt;
&lt;br /&gt;
Create an HTTPSRV address for the kioskUI:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
define kioskUI HTTPSRV kiosk /opt/fhem/www/ftui Kiosk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
All files are in this zip&lt;br /&gt;
&lt;br /&gt;
There is a lot of detail about how it works in here to aid understanding.   &lt;br /&gt;
&lt;br /&gt;
It could also help a user understand how the basic framework can be extended to include additional devices as there are examples of a simple IFRAME Panel, a relatively simple Thermostat Panel and a complex set of Lighting Panels.&lt;br /&gt;
&lt;br /&gt;
The FTUI-TAB-VIEWs are more designed to be operated from a single set of FTUI-TAB elements - you click one and it becomes active, displaying the correct TAB-VIEW.  This operating model did not readily lend itself to dynamic tabs - visible on some panels but not others, so new FTUI-TAB elements appear with every FTUI-TAB-VIEW&lt;br /&gt;
&lt;br /&gt;
=== Kiosk HTML ===&lt;br /&gt;
The HTML is divided into three sections&lt;br /&gt;
&lt;br /&gt;
==== Initializing FTUI3 ====&lt;br /&gt;
In the &amp;lt;HEAD&amp;gt; the FTUI3 code and META tags that configure it are defined.   This implementation choses not to use Toast messages.  The viewport is defined as non-scalable as well.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot;&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-height, height=device-width, user-scalable = no, initial-scale = 1, maximum-scale = 1, minimum-scale = 1&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;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://&amp;lt;your-url&amp;gt;:8083/fhem/&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;refresh_interval&amp;quot; content=&amp;quot;60&amp;quot;&amp;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;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Initialize the Kiosk ====&lt;br /&gt;
.This code is also in the &amp;lt;HEAD&amp;gt;.  Should a Kiosk specific theme be required, this can be included here &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;!-- SET UP THE KIOSK --&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;KIOSK&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_class.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/init.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_functions.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;./kiosk/kiosk_styles.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;!-- &amp;lt;link href=&amp;quot;./kiosk/kiosk-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt; --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== body - The Outline Panels ====&lt;br /&gt;
The &amp;lt;BODY&amp;gt; contains outlines of the Panels, ready for customisation by the JavaScript.&lt;br /&gt;
&lt;br /&gt;
The body has a kiosk_onLoad() function - this is part of the Kiosk customisation and is called to customise the predefined panels once the outlines have been loaded.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;45&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;body&amp;quot; onload=&amp;quot;kiosk_onLoad()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The first tab defined is a clock.  This should only ever be shown if there are no controllable devices in the room.  It&#039;s included so that this is obvious (for instance if something has gone wrong with the Kiosk definition).  There are also three clickable tabs in this panel.  These allow the configuration to be manually checked when something has gone wrong.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;46&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Clock&amp;quot; active&amp;gt;    &lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;height:90vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot; color=&amp;quot;primary&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;ee&amp;quot; size=&amp;quot;6&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;DD&amp;quot; size=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-clock format=&amp;quot;hh:mm&amp;quot; size=&amp;quot;9&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-tab-row&amp;quot; style=&amp;quot;height:10vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile  col=&amp;quot;4&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Lighting-Main&amp;quot;  id=&amp;quot;click-lighting&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;lightbulb&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;2&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Player-Main&amp;quot; id=&amp;quot;click-player&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;music&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;3&amp;quot;  width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Heating-Main&amp;quot;  id=&amp;quot;click-heating&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;fire&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile col=&amp;quot;1&amp;quot; width=&amp;quot;0.25&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Clock&amp;quot; id=&amp;quot;click-clock&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;clock-o&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt; &lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
  &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The next section defines the main panels for the Lighting, &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;81&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Main&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;25%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&amp;quot;LightingPower&amp;quot;&lt;br /&gt;
                       value=&amp;quot;off&amp;quot; fill=&amp;quot;outline&amp;quot; shape=&amp;quot;circle&amp;quot; states=&amp;quot;on,off&amp;quot; color=&amp;quot;gray&amp;quot;&lt;br /&gt;
                       @value-change=&#039;powerChange($event)&#039;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;power-off&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;75%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-slider  id=&amp;quot;LightingDimmer&amp;quot;&lt;br /&gt;
                        value=&amp;quot;1&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot;&lt;br /&gt;
                        color=&amp;quot;white&amp;quot;&lt;br /&gt;
                        @value-change=&amp;quot;dimmerChange($event)&amp;quot;&lt;br /&gt;
                        &amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-slider&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Scenes&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;cog&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Settings&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Heating &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;108&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Heating-Main&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row id=&amp;quot;Heating-Tabs&amp;quot;class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;and Audio&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;112&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Player-Main&amp;quot; &amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;iframe  id=&amp;quot;Player-kiosk&amp;quot; src=&amp;quot;&amp;quot; style=&amp;quot;height:90vh;width:100vw;overflow:hidden;padding:0px;border:none;margin:0px&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Player-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Scenes Tab and Success Popup (line 164) are also defined.  At line 150, the tab selector for the first Lighting Detail Panel is created:&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;119&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Scenes&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;gap:5em;&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-grid margin=&amp;quot;5&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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene1_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 1&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene1&#039; style=&#039;box-shadow:0px 0px 10px 5px #B26464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot; @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;Press to Select&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;1&amp;quot; col=&amp;quot;2&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene2_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 2&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene2&#039; style=&#039;box-shadow:0px 0px 10px 5px #FF6464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;1&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene3_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 3&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene3&#039; style=&#039;box-shadow:0px 0px 10px 5px #64C9C9;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene4_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 4&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene4&#039; style=&#039;box-shadow:0px 0px 10px 5px #1F6F3F;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;3&amp;quot; col=&amp;quot;1&amp;quot; width=&#039;2&#039; height=&#039;0.1&#039;&amp;gt;&lt;br /&gt;
          Long hold to set&lt;br /&gt;
        &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Scenes-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Main&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;reply&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Go Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Settings-Detail0-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Setting-Detail0&amp;quot;  timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;tasks&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Details&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;ftui-label id=&amp;quot;scene-success-click&amp;quot; @click=&#039;scene_success.open()&#039; style=&amp;quot;display:none&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_fail&amp;quot; timeout=&amp;quot;15&amp;quot; color=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Failed To Save&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Failed to Save Scene&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_success&amp;quot; timeout=&amp;quot;5&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Saved&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Scene Saved&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kiosk JavaScript ===&lt;br /&gt;
The JavaScript is split into three files:&lt;br /&gt;
&lt;br /&gt;
* light_class.js is a class that is used to define each light controlled by the UI.  It contains functions used to aid the communication with FHEM and FTUI3 - mutation observers and simple functions for hex conversion&lt;br /&gt;
* init.js is all the code used to set up the UI and initialise the display Panels&lt;br /&gt;
* light_functions.js is the code use to manage the lighting Panels as these are far more complex and don&#039;t rely on native FTUI3&lt;br /&gt;
&lt;br /&gt;
==== light_class.js ====&lt;br /&gt;
&lt;br /&gt;
===== Properties =====&lt;br /&gt;
The light class has these properties:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
this.name = &#039;&#039;;&lt;br /&gt;
this.mode = &#039;&#039;;&lt;br /&gt;
this.red = 0;&lt;br /&gt;
this.green = 0;&lt;br /&gt;
this.blue = 0;&lt;br /&gt;
this.white = 0;&lt;br /&gt;
this.state = &#039;off&#039;;&lt;br /&gt;
this.scene1 = &#039;off&#039;;&lt;br /&gt;
this.scene2 = &#039;off&#039;;&lt;br /&gt;
this.scene3 = &#039;off&#039;;&lt;br /&gt;
this.scene4 = &#039;off&#039;;&lt;br /&gt;
this.state = &#039;off&#039;;&lt;br /&gt;
this.observerConfig = {&lt;br /&gt;
      attributes: true // this is to watch for attribute changes.&lt;br /&gt;
      ,attributeOldValue: true&lt;br /&gt;
  }&lt;br /&gt;
//set by front end only&lt;br /&gt;
this.was = &#039;off&#039;;&lt;br /&gt;
this.sliderObserver = &#039;&#039;;&lt;br /&gt;
this.stateObserver = &#039;&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;These are set from FHEM during the initialisation of each light by the doInit() function in init.js&lt;br /&gt;
&lt;br /&gt;
The last three properties are set by the front end only and are there to manage continuity of state:&lt;br /&gt;
&lt;br /&gt;
* this.was is set when the master power button is set to &amp;quot;off&amp;quot;.  It records whether the individual light was on or off at the time.  When the master power button is subsequently set to &amp;quot;on&amp;quot;, this.was is used to only turn on lights that were previously on.  Unless all were off, in which case all are turned on.&lt;br /&gt;
* this.sliderObserver and stateObserver are used to pick up changes made in FHEM, or via a physical switch then communicated by the Shelly device to FHEM, to the detailed light values (RGBW and On/Off).  The FTUI3 events @value-change do not get triggered when this happens (which is a good thing).   When the user is interacting with the KioskUI, FHEM can sometimes  send the results too quickly, leading to circular updates.  These observers are disconnected temporarily to prevent this.&lt;br /&gt;
&lt;br /&gt;
===== Methods =====&lt;br /&gt;
These methods are used to help with managing the light&lt;br /&gt;
&lt;br /&gt;
* set dim - used by the master dimmer slider to recalculate the new values for RGBW after the slider has changed value.  The dimmer currently sets minimum and maximum values than can be dimmed based on the color composition of the target light.  This is to help prevent a change in the colour composition that could be caused by a min-&amp;gt;max-&amp;gt;min dim sequence.   Sometimes, change is inevitable though.&lt;br /&gt;
* rgbw2hex, rgb2hex and w2hex are used to convert the internal RGBW channel values to a hex value that can be transmitted to FHEM&lt;br /&gt;
** ideally the rgb color picker ftui component would have been used, but integrating the white channel would still be required for an RGBW light - for now the display looks more integrated across all types of light as separate channels&lt;br /&gt;
* template.  This returns the light template used to initialise the light detail.  The FTUI3 components returned are dependent on whether the light is RGBW, RGB or just a dimmer.&lt;br /&gt;
&lt;br /&gt;
==== init.js ====&lt;br /&gt;
init.js has three distinct phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1 - actions taken as the code is loaded&lt;br /&gt;
* Phase 2 - actions taken once the &amp;lt;BODY&amp;gt; has loaded&lt;br /&gt;
* Phase 3 - actions taken once the FTUI3 app has finished initializing&lt;br /&gt;
&lt;br /&gt;
===== Phase 1 - Code Load =====&lt;br /&gt;
&lt;br /&gt;
* Two global objects are defined&lt;br /&gt;
** Lights is a collection of all the light definitions&lt;br /&gt;
** Kiosk holds global parameters and settings&lt;br /&gt;
&lt;br /&gt;
* The room is retrieved from the URL Query String&lt;br /&gt;
* An event listener is defined to wait for the FTUI3 app to complete initialisation&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// Gloabl Objects&lt;br /&gt;
var lights = {}&lt;br /&gt;
var Kiosk = {}&lt;br /&gt;
&lt;br /&gt;
// Funtion to get the Room&lt;br /&gt;
var getUrlParameter = function getUrlParameter(sParam) {&lt;br /&gt;
  var sPageURL = window.location.search.substring(1),&lt;br /&gt;
      sURLVariables = sPageURL.split(&#039;&amp;amp;&#039;),&lt;br /&gt;
      sParameterName,&lt;br /&gt;
      i;&lt;br /&gt;
&lt;br /&gt;
  for (i = 0; i &amp;lt; sURLVariables.length; i++) {&lt;br /&gt;
      sParameterName = sURLVariables[i].split(&#039;=&#039;);&lt;br /&gt;
&lt;br /&gt;
      if (sParameterName[0] === sParam) {&lt;br /&gt;
          return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  return false;&lt;br /&gt;
};  &lt;br /&gt;
&lt;br /&gt;
// Get the CSRF token - this code is copied from the FTUI3 App&lt;br /&gt;
fetch(&amp;quot;http://&amp;lt;your-fhem-ip&amp;gt;:8083/fhem?XHR=1&amp;quot;).then(response =&amp;gt; {&lt;br /&gt;
    Kiosk.ftwcsrf = response.headers.get(&#039;X-FHEM-csrfToken&#039;);&lt;br /&gt;
    console.log(&#039;Got csrf from FHEM:&#039; + Kiosk.ftwcsrf);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
//Set the CSRF token in the Kiosk object&lt;br /&gt;
Kiosk.thisRoom=getUrlParameter(&#039;room&#039;)&lt;br /&gt;
&lt;br /&gt;
//Watch for the FTUI3 end of initialisation event&lt;br /&gt;
document.addEventListener(&#039;ftuiPageInitialized&#039;,() =&amp;gt; onFTUIReady())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Phase 2 - After the Body is loaded =====&lt;br /&gt;
Three functions&lt;br /&gt;
&lt;br /&gt;
* kiosk_onLoad() is called by the browser when the body has finished loading&lt;br /&gt;
* This waits (up to 50 seconds) until the csrf token has been set in the Kiosk object using function isCSRFSet() &lt;br /&gt;
* Then it calls the doInit() function&lt;br /&gt;
&lt;br /&gt;
The doInit() function fetches relevant details from FHEM (line 56) for the types of devices to be monitored that are in the room specified by Kiosk.thisRoom.  This ensures only one call to FHEM for the information&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;34&amp;quot;&amp;gt;&lt;br /&gt;
function isCSRFSet(timeout) {&lt;br /&gt;
    var start = Date.now();&lt;br /&gt;
    return new Promise(checkCSRF); // set the promise object within the ensureFooIsSet object&lt;br /&gt;
 &lt;br /&gt;
    function checkCSRF(resolve, reject) {&lt;br /&gt;
        if (Kiosk &amp;amp;&amp;amp; Kiosk.ftwcsrf)&lt;br /&gt;
            resolve(Kiosk.ftwcsrf);&lt;br /&gt;
        else if (timeout &amp;amp;&amp;amp; (Date.now() - start) &amp;gt;= timeout)&lt;br /&gt;
            reject(new Error(&amp;quot;timeout&amp;quot;));&lt;br /&gt;
        else&lt;br /&gt;
            setTimeout(checkCSRF.bind(this, resolve, reject), 30);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
function kiosk_onLoad(){&lt;br /&gt;
    isCSRFSet(50000).then(function(){&lt;br /&gt;
        doInit()&lt;br /&gt;
    }&lt;br /&gt;
    )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function doInit(){&lt;br /&gt;
    fetch(`http://192.168.1.2:8083/fhem?XHR=1&amp;amp;cmd=jsonlist2+room=${Kiosk.thisRoom}+TYPE+type+SHELLY+NAME+PLAYERNAME+model+scene1+scene2+scene3+scene4+state+L-white+L-red+L-green+L-blue&amp;amp;fwcsrf=${Kiosk.ftwcsrf}`).then(res =&amp;gt; {&lt;br /&gt;
        return res.json()&lt;br /&gt;
      }).then((response) =&amp;gt; {&lt;br /&gt;
        o=JSON.parse(JSON.stringify(response))&lt;br /&gt;
        console.log(&#039;res: &#039; + JSON.stringify(response))&lt;br /&gt;
        var hasPlayer=false&lt;br /&gt;
        var hasLighting=false&lt;br /&gt;
        var hasHeating=false&lt;br /&gt;
        var hasThermostat=false&lt;br /&gt;
        var lightType=&amp;quot;&amp;quot;&lt;br /&gt;
        for (i=0;i&amp;lt;o.Results.length;i++){&lt;br /&gt;
            &lt;br /&gt;
            [... init code]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;For each device returned, the doInit() code sets up the Panels required to manage them through the Kiosk UI.  The code here is not in the sequence in init.js, but grouped together to show how the process fits together&lt;br /&gt;
&lt;br /&gt;
====== Audio ======&lt;br /&gt;
Whilst looping over the Results, check whether there is a player&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;155&amp;quot;&amp;gt;&lt;br /&gt;
        // any players&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;SB_PLAYER&#039;){&lt;br /&gt;
          hasPlayer=true&lt;br /&gt;
          var playerName=o.Results[i].Internals.PLAYERNAME&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Once all results are in, if a player was found, customise the Panel&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;209&amp;quot;&amp;gt;&lt;br /&gt;
      if (hasPlayer===true){&lt;br /&gt;
        var tabPrefix=&#039;Player&#039;&lt;br /&gt;
        //set up the tab&lt;br /&gt;
        document.getElementById(&amp;quot;Player-kiosk&amp;quot;).src=&amp;quot;http://&amp;lt;your web address&amp;gt;:9000/material/?single&amp;amp;page=now-playing&amp;amp;player=&amp;quot;+playerName&lt;br /&gt;
        &lt;br /&gt;
        ...&lt;br /&gt;
        // add the active (non-clickable) tab to the Player tab row&lt;br /&gt;
        createPlayerTab(tabPrefix,&#039;kiosk-tab-player-active&#039;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Add Tabs to the other panels so that those panels can switch to the payer Panel.  For instance &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;179&amp;quot;&amp;gt;&lt;br /&gt;
if (hasLighting===true){&lt;br /&gt;
    ...&lt;br /&gt;
    &lt;br /&gt;
    if (hasPlayer===true){&lt;br /&gt;
          createPlayerTab(tabPrefix,&#039;kiosk-tab-player-inactive&#039;,timeout=60)&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Helper function to create the player tab&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;305&amp;quot;&amp;gt;&lt;br /&gt;
function createPlayerTab(view,state,timeout=&amp;quot;0&amp;quot;){&lt;br /&gt;
    divHtml = &amp;quot;&amp;lt;ftui-column id=&#039;&amp;quot;+view+&amp;quot;-Player-Tab&#039; class=&#039;&amp;quot;+state+&amp;quot;&#039;&amp;gt;&amp;lt;ftui-tab class=&#039;kiosk-button&#039; timeout=&#039;&amp;quot;+timeout+&amp;quot;&#039; view=&#039;Player-Main&#039;&amp;gt;&amp;lt;ftui-icon name=&#039;music&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&amp;lt;/ftui-tab&amp;gt;&amp;lt;/ftui-column&amp;gt;&amp;quot;&lt;br /&gt;
    document.getElementById(view+&#039;-Tabs&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Heating ======&lt;br /&gt;
This works in roughly the same way, except with slightly different code.&lt;br /&gt;
&lt;br /&gt;
This selects a wall mounted thermostat as the device to control, if present.  Otherwise it uses the last found radiator thermostat&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;161&amp;quot;&amp;gt;&lt;br /&gt;
        // any heating&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;MAX&#039;){&lt;br /&gt;
            hasHeating=true&lt;br /&gt;
            if (hasThermostat===false){&lt;br /&gt;
              var heatingName=o.Results[i].Internals.NAME&lt;br /&gt;
            }&lt;br /&gt;
            if (o.Results[i].Internals.type==&#039;WallMountedThermostat&#039;){&lt;br /&gt;
              hasThermostat=true&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Heating Panel is defined thus:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;230&amp;quot;&amp;gt;&lt;br /&gt;
     if (hasHeating===true){&lt;br /&gt;
        addHeatingControl(heatingName)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Heating code creates the full panel, rather than customising an existing one as the Audio one does.  The Heating panel uses two, differently scaled FTUI-KNOB elements placed one on top of the other.   &lt;br /&gt;
&lt;br /&gt;
* The bottom one, scaled to use all the width and height, is the actual temperature display and is read only&lt;br /&gt;
* The top one, scaled to 70% of the width and height, has a transparent background, and displays the desiredTemperature.  This is clickable to change the temperature.  Ideally new properties of FTUI-KNOB could be included so the actual/desired temperatures can be displayed in the same way.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;313&amp;quot;&amp;gt;&lt;br /&gt;
function addHeatingControl(heatingName){&lt;br /&gt;
  divHtml=&#039;&amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&#039;+&lt;br /&gt;
    &#039;&amp;lt;ftui-knob height=&amp;quot;100vh&amp;quot; width=&amp;quot;100vw&amp;quot; stroke-width=&amp;quot;15&amp;quot; ticks=&amp;quot;25&amp;quot; style=&amp;quot;z-index:1;position:absolute;&amp;quot; has-arc readonly has-scale-text has-scale color=&amp;quot;cold-hot&amp;quot; [value]=&amp;quot;&#039;+heatingName+&#039;:temperature&amp;quot; max=&amp;quot;30&amp;quot; min=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-knob&amp;gt;&#039; +&lt;br /&gt;
    &#039;&amp;lt;ftui-knob height=&amp;quot;70vh&amp;quot; width=&amp;quot;70vw&amp;quot; stroke-width=&amp;quot;5&amp;quot; style=&amp;quot;z-index: 2;background:transparent;position:absolute;&amp;quot; has-needle has-value-text unit=&amp;quot;°C&amp;quot; unit-offset-y=&amp;quot;40&amp;quot; value-size=&amp;quot;5em&amp;quot; unit-size=&amp;quot;2em&amp;quot; value-decimals=&amp;quot;1&amp;quot; step=&amp;quot;0.5&amp;quot; [(value)]=&amp;quot;&#039;+heatingName+&#039;:desiredTemperature&amp;quot; max=&amp;quot;30&amp;quot; min=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-knob&amp;gt;&#039;+&lt;br /&gt;
    &#039;&amp;lt;/ftui-row&amp;gt;&#039;&lt;br /&gt;
    document.getElementById(&#039;Heating-Main&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The insertion of tabs uses the same tests and similar functions&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;301&amp;quot;&amp;gt;&lt;br /&gt;
function createHeatingTab(view,state,timeout=&amp;quot;0&amp;quot;){&lt;br /&gt;
    divHtml = &amp;quot;&amp;lt;ftui-column id=&#039;&amp;quot;+view+&amp;quot;-Heating-Tab&#039; class=&#039;&amp;quot;+state+&amp;quot;&#039;&amp;gt;&amp;lt;ftui-tab class=&#039;kiosk-button&#039; timeout=&#039;&amp;quot;+timeout+&amp;quot;&#039; view=&#039;Heating-Main&#039;&amp;gt;&amp;lt;ftui-icon name=&#039;fire&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&amp;lt;ftui-label&amp;gt;Heating&amp;lt;/ftui-label&amp;gt;&amp;lt;/ftui-tab&amp;gt;&amp;lt;/ftui-column&amp;gt;&amp;quot;&lt;br /&gt;
    document.getElementById(view+&#039;-Tabs&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Lighting ======&lt;br /&gt;
This is the most complex of the Panels to set up.   &lt;br /&gt;
&lt;br /&gt;
The results from the fetch are added to a new light() object and the light object added to the list of lights for the room.  Note this includes: RGBW levels, the state (on/off) and the settings of each scene for this light.&lt;br /&gt;
&lt;br /&gt;
Then the mutation observers for the light details are created and references saved to the light() object.&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;
// any lights&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;Shelly&#039;){&lt;br /&gt;
            hasLighting=true&lt;br /&gt;
            // most advanced defines the model&lt;br /&gt;
            if (lightType!=&#039;shellyrgbw&#039;){&lt;br /&gt;
                lightType=o.Results[i].Attributes.model&lt;br /&gt;
            }&lt;br /&gt;
            var thisLight= new light()&lt;br /&gt;
            thisLight.model=o.Results[i].Attributes.model&lt;br /&gt;
            thisLight.name=o.Results[i].Internals.NAME&lt;br /&gt;
            thisLight.R=(o.Results[i].Readings[&#039;L-red&#039;])?o.Results[i].Readings[&#039;L-red&#039;].Value:0&lt;br /&gt;
            thisLight.B=(o.Results[i].Readings[&#039;L-blue&#039;])?o.Results[i].Readings[&#039;L-blue&#039;].Value:0&lt;br /&gt;
            thisLight.G=(o.Results[i].Readings[&#039;L-green&#039;])?o.Results[i].Readings[&#039;L-green&#039;].Value:0&lt;br /&gt;
            thisLight.W=(o.Results[i].Readings[&#039;L-white&#039;])?o.Results[i].Readings[&#039;L-white&#039;].Value:0&lt;br /&gt;
            thisLight.scene1=(o.Results[i].Readings.scene1)?o.Results[i].Readings.scene1.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene2=(o.Results[i].Readings.scene2)?o.Results[i].Readings.scene2.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene3=(o.Results[i].Readings.scene3)?o.Results[i].Readings.scene3.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene4=(o.Results[i].Readings.scene4)?o.Results[i].Readings.scene4.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.state=(o.Results[i].Readings.state)?o.Results[i].Readings.state.Value:&#039;off&#039;&lt;br /&gt;
            lights[thisLight.name] = thisLight&lt;br /&gt;
           &lt;br /&gt;
            let mySliderObserver = new MutationObserver( function(mutations)  {&lt;br /&gt;
                  mutations.forEach(function(mutation){&lt;br /&gt;
                    if (mutation.attributeName==&#039;value&#039;){&lt;br /&gt;
                      const [key, attribute] = mutation.target.id.split(&#039;:&#039;)&lt;br /&gt;
                      lights[key][attribute] = mutation.target.value&lt;br /&gt;
                      updateFhem(&amp;quot;set &amp;quot;+key+&amp;quot; rgbw &amp;quot;+lights[key].hex );&lt;br /&gt;
                      setDimmerLevel()&lt;br /&gt;
                    }&lt;br /&gt;
                  }&lt;br /&gt;
                )&lt;br /&gt;
              }&lt;br /&gt;
            )&lt;br /&gt;
            lights[thisLight.name].sliderObserver = mySliderObserver&lt;br /&gt;
&lt;br /&gt;
            let myStateObserver = new MutationObserver(function(mutations)  {&lt;br /&gt;
              mutations.forEach(function(mutation){&lt;br /&gt;
                if (mutation.attributeName==&#039;value&#039;){&lt;br /&gt;
                  //save approprite value&lt;br /&gt;
                  var key = mutation.target.id.split(&#039;:&#039;)[0]&lt;br /&gt;
                  lights[key].state = mutation.target.value&lt;br /&gt;
                  var anyOn=false&lt;br /&gt;
                  for (var key in lights){&lt;br /&gt;
                    if (lights[key].state==&#039;on&#039;){&lt;br /&gt;
                      anyOn=true&lt;br /&gt;
                      break;&lt;br /&gt;
                    }&lt;br /&gt;
                  }&lt;br /&gt;
                setPowerColor(anyOn)&lt;br /&gt;
                setDimmerLevel()&lt;br /&gt;
                }&lt;br /&gt;
              })&lt;br /&gt;
             }&lt;br /&gt;
            )&lt;br /&gt;
            lights[thisLight.name].stateObserver = myStateObserver&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Once the results have all been processed&lt;br /&gt;
&lt;br /&gt;
* the scene colours are set on the Lighting Scenes Panel&lt;br /&gt;
* the individual controls for each light are added to a Lighting Details Panel (up to three lights per panel):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;174&amp;quot;&amp;gt;&lt;br /&gt;
if (hasLighting===true){&lt;br /&gt;
        var tabPrefix=&#039;Lighting&#039;&lt;br /&gt;
        //options first&lt;br /&gt;
        setSceneColors()&lt;br /&gt;
        //create the details panes&lt;br /&gt;
        for (let i=0; i &amp;lt; Object.keys(lights).length;i++){&lt;br /&gt;
          if (i%3 == 0){&lt;br /&gt;
            //set up a new detail tab every three tlights&lt;br /&gt;
            var counter=Math.floor(i/3)&lt;br /&gt;
            createLightingDetailsTab(counter)&lt;br /&gt;
          }&lt;br /&gt;
          createLightingDetail(counter,Object.keys(lights)[i])&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The individual controls are set by the light() object .template() method - this ensures the right controls are placed on the panel for the type of light&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;361&amp;quot;&amp;gt;&lt;br /&gt;
function createLightingDetail(counter,key){&lt;br /&gt;
  document.getElementById(`Lighting-Setting_Detail${counter}-Body`).insertAdjacentHTML(&#039;beforeend&#039;,lights[key].template())&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Details Tab is created and a forward reference created on the previous tab, if required:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;320&amp;quot;&amp;gt;&lt;br /&gt;
function createLightingDetailsTab(counter){&lt;br /&gt;
    switch (counter){&lt;br /&gt;
    case 0:&lt;br /&gt;
        var goBack=&amp;quot;Lighting-Scenes&amp;quot;&lt;br /&gt;
        var goForwardId = false&lt;br /&gt;
        break;&lt;br /&gt;
    default: &lt;br /&gt;
        var lastPanel = counter - 1;&lt;br /&gt;
        var goBack=`Lighting-Setting-Detail${lastPanel}`&lt;br /&gt;
        var goForward=`Lighting-Setting-Detail${counter}`&lt;br /&gt;
        var goForwardId = `Lighting-Setting-${lastPanel}-Tabs`&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.body.insertAdjacentHTML(&#039;beforeend&#039;,&lt;br /&gt;
    `&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Setting-Detail${counter}&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Setting_Detail${counter}-Body&amp;quot; class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;flex-direction:column&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Settings-Detail${counter}-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Setting-${counter}-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Setting-${counter}-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;${goBack}&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-icon name=&#039;long-arrow-left&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-label&amp;gt;Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;`&lt;br /&gt;
    )&lt;br /&gt;
    if (goForward){&lt;br /&gt;
    document.getElementById(goForwardId).insertAdjacentHTML(&#039;beforeend&#039;,&lt;br /&gt;
    `&amp;lt;ftui-column id=&amp;quot;Lighting-Setting-${lastPanel}-Forward-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;${goForward}&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-icon name=&#039;long-arrow-right&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-label&amp;gt;Next page&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-column&amp;gt;`&lt;br /&gt;
    )&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;After this, tab references similar to Audio and Heating are added. &lt;br /&gt;
&lt;br /&gt;
===== Phase 3 - After FTUI is initialised =====&lt;br /&gt;
Some actions can only be performed once FTUI is ready to process them.  One of these appears to be changing the header color on an FTUI-GRID, so it has to wait until after the FTUI event &#039;ftuiPageInitialized&#039; has been dispatched.&lt;br /&gt;
&lt;br /&gt;
If the current light setting matches the same scene setting for all lights in the room, the scene header is set to &amp;quot;success&amp;quot; to indicate it is currently selected.  If not, no action is taken&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
function onFTUIReady(){&lt;br /&gt;
    console.log(&amp;quot;My FTUI Ready&amp;quot;)&lt;br /&gt;
    //for each scene in each light, confirm that the current light setting matches the same scene (if any)&lt;br /&gt;
    var matchScene=false&lt;br /&gt;
    for (key in lights){&lt;br /&gt;
        if (matchScene==-1){&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        var scenes=[lights[key].scene1,lights[key].scene2,lights[key].scene3,lights[key].scene4]&lt;br /&gt;
        if (!matchScene){&lt;br /&gt;
            matchScene = scenes.indexOf(lights[key].hex)&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
            matchScene = scenes[matchScene].indexOf(lights[key].hex)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    scene_to_select = [&#039;scene1&#039;,&#039;scene2&#039;,&#039;scene3&#039;,&#039;scene4&#039;][matchScene]&lt;br /&gt;
    switch (scene_to_select){&lt;br /&gt;
        case &#039;&#039;: &lt;br /&gt;
            break;&lt;br /&gt;
        case undefined: &lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            document.getElementById(scene_to_select+&#039;_header&#039;).color=&#039;success&#039;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== light_functions_js ====&lt;br /&gt;
The light functions work together to make sure the UI reflects FHEM consistently and vice versa.  The principle of how it works is described in this diagram for state changes:&lt;br /&gt;
&lt;br /&gt;
[[Datei:State changes.png|alternativtext=A diagram showing how @value-change and the state mutation observer work together to keep FHEM, the UI and the light() object in sync.|rahmenlos|600x600px]]&lt;br /&gt;
&lt;br /&gt;
* If the master power button is clicked in the UI, the change is propagated to the detail tab for all lights.   &lt;br /&gt;
** The change is also sent directly to FHEM and updated in the relevant light() objects&lt;br /&gt;
* If the state is changed in FHEM, this is sent to the Lighting Detail Panel, observed by the state mutation observer and the light() object updated&lt;br /&gt;
** The change is also reflected in the master Lighting Panel if required&lt;br /&gt;
** This does not result in a cycle, as the @value-change event is not triggered for a state change programatically applied - only for an event in the UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
However, for the light channels on the Lighting Detail Panel, things get more complicated as the value selected by the user cannot be sent directly to FHEM, but first has to be set in the light object so the hex rgbw value can be calculated:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Kiosk Lighting Slider changes.png|alternativtext=A diagram showing how @value-change and the slider mutation observer work together to keep FHEM, the UI and the light() object in sync.|rahmenlos|600x600px]]&lt;br /&gt;
&lt;br /&gt;
# The user changes a slider.  @value-change changes the light() value for that channel.&lt;br /&gt;
# ... and then sends the new light().hex value to FHEM and sets the master dimmer level&lt;br /&gt;
# FHEM sends the new light value to the Lighting Detail Panel&lt;br /&gt;
# the sliderObserver also changes the light() object and sets the master dimmer level&lt;br /&gt;
&lt;br /&gt;
$$ NEED TO CONFIRM AND EXPLORE THE ANTI-CYCLING LOGIC $$&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These light functions are used to set the individual light settings when the master power and dimmer slider are changed:&lt;br /&gt;
&lt;br /&gt;
* powerChange - changes all &amp;quot;on&amp;quot; lights to &amp;quot;off&amp;quot; and vice versa&lt;br /&gt;
* dimmerChange - changes the values of ALL light channels in proportion to the change to the dimmer&lt;br /&gt;
&lt;br /&gt;
These functions are used to reflect changes to the individual lights in the master power and slider&lt;br /&gt;
&lt;br /&gt;
* setPowerColor - sets the power color to be &amp;quot;off&amp;quot; if all lights are off, or &amp;quot;on&amp;quot; if any on&lt;br /&gt;
* setDimmerLevel - set the slider position to the max of all light channels for all &amp;quot;on&amp;quot; lights or the max of all lights channels for all lights if they are all off.&lt;br /&gt;
&lt;br /&gt;
=== Kiosk CSS ===&lt;br /&gt;
&lt;br /&gt;
== Improvements required ==&lt;br /&gt;
&lt;br /&gt;
# The Heating Thermostat scales well for square displays, but other aspect ratios are not handled well.&lt;br /&gt;
# There is code duplication between this implementation and the FTUI3 App.  This should be eliminated.&lt;br /&gt;
# The hard-coded timeouts should be &amp;lt;META&amp;gt; driven.&lt;br /&gt;
# Control of further devices, using the same principles should be possible.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:Kiosk_Lighting_Slider_changes.png&amp;diff=39047</id>
		<title>Datei:Kiosk Lighting Slider changes.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:Kiosk_Lighting_Slider_changes.png&amp;diff=39047"/>
		<updated>2024-01-23T18:32:53Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The dataflow for changes to the Kiosk Lighting Sliders&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:State_changes.png&amp;diff=39045</id>
		<title>Datei:State changes.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:State_changes.png&amp;diff=39045"/>
		<updated>2024-01-23T18:21:34Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Rstooks21 lud eine neue Version von Datei:State changes.png hoch&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kiosk UI Sate changes data flow&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39044</id>
		<title>Benutzer:Rstooks21/Kiosk UI</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39044"/>
		<updated>2024-01-23T18:20:20Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Typo changes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle en}}&lt;br /&gt;
&lt;br /&gt;
==Kiosk UI==&lt;br /&gt;
This is an approach to the dynamic creation of a Kiosk UI that can be used by anyone visiting the home. There will be multiple, small, possibly wall mounted Kiosk screens - one in each physical room. The Kiosk UI controls only the devices that are located in the same [FHEM] Room.&lt;br /&gt;
&lt;br /&gt;
== The Objective ==&lt;br /&gt;
To use the new FHEM Tablet UI (FTUI3) to create a tabbed UI showing just the devices in the same FHEM room.  This tabbed UI allows the most important device controls to be shown by default and at the same time allow other devices to be controlled in as few clicks/screen touches as possible.  As well as this, each Panel should not ever display scroll bars.&lt;br /&gt;
&lt;br /&gt;
The rooms selected for testing had some or all of:&lt;br /&gt;
&lt;br /&gt;
* Lighting (Shelly RGDW LED Controller and Shelly DImmer 2, defined to FHEM as Shelly devices)&lt;br /&gt;
* Audio (Logitech Media Players defined to FHEM as SBPLAYER devices)&lt;br /&gt;
* Heating (EQ3 MAX! WallMountedThermostat and RadiatorThermostat, defined as MAX devices)&lt;br /&gt;
&lt;br /&gt;
With a simple call, the Kiosk displays the default Panel, with dynamic tabs for other device control Panels (a tab will only be shown if the room has that type of device).  The default Panel will be implemented in the following order:&lt;br /&gt;
&lt;br /&gt;
* Lighting first&lt;br /&gt;
* If no Lighting, then Audio&lt;br /&gt;
* If no Lighting or Audio, then Heating&lt;br /&gt;
* If no supported devices are in the room, then the clock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This web address is intended to be used in a browser in Kiosk mode&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
http[s]://fhem-location:8083/fhem/kiosk/kiosk.html?room=Kitchen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The &amp;quot;?room=Kitchen&amp;quot; is used to filter devices in the FHEM room &amp;quot;Kitchen&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== The Panels ==&lt;br /&gt;
All the panels time out to the primary panel after 60 seconds of non use.&lt;br /&gt;
&lt;br /&gt;
=== The Lighting Panels ===&lt;br /&gt;
&lt;br /&gt;
==== Main Panel ====&lt;br /&gt;
The main Lighting Panel is a simple On/Off switch and dimmer slider.  The single control will control all of the lights in the room equally&lt;br /&gt;
&lt;br /&gt;
* turning them all off or on&lt;br /&gt;
* dimming all lights in proportion&lt;br /&gt;
&lt;br /&gt;
[[Datei:KioskLightingMainPanel.png|alternativtext=An on/off switch and a slider used for dimming the lights|ohne|mini|An FTUI3 Main Lighting control panel ]]&lt;br /&gt;
Note the four dynamic tabs: &lt;br /&gt;
&lt;br /&gt;
* Lighting (currently active) &lt;br /&gt;
* Music&lt;br /&gt;
* Heating&lt;br /&gt;
* Settings - The Lighting tab has further settings that can be adjusted.&lt;br /&gt;
&lt;br /&gt;
==== Settings/Scene Selector Panel ====&lt;br /&gt;
Selecting the &amp;quot;Settings&amp;quot; tab allows the user to select a scene - a combination of light settings that have been previously saved (by the user in the Kiosk UI)&lt;br /&gt;
[[Datei:KioskLightingScenes.png|alternativtext=A grid of four buttons in a panel display|ohne|mini|FTUI Grids and Buttons used to save and recall scenes]]&lt;br /&gt;
The light settings for all lights in the room are recalled by a short press to any of the buttons. &lt;br /&gt;
&lt;br /&gt;
The current light settings for all lights in the room are stored as a scene by pressing and holding the relevant button. This causes the scene to be saved and the button to glow with the average light colour and intensity for all lights that are on.&lt;br /&gt;
&lt;br /&gt;
==== The Details Panel ====&lt;br /&gt;
From the Scene Selector Panel it is possible to control every light in the room individually.   The Details Panels are flexed to display up to three lights each with as many Panels as needed:&lt;br /&gt;
[[Datei:KioskLightingDetail.png|alternativtext=An on/off switch and four sliders in Red, Green, Blue and White|ohne|mini|FTUI3 Switch and Sliders for RGBW control of a single led strip]]&lt;br /&gt;
&lt;br /&gt;
=== The Audio Panel ===&lt;br /&gt;
The Audio Panel uses an IFRAME to embed the Logitech Media Server web interface into the panel.   Only the player in the room can be controlled - the other players cannot be unless they are synchronised.   This is a bit of a cheat as the facilities in FHEM to control the player and display album art are not used.  But why reinvent?&lt;br /&gt;
[[Datei:KioskAudioPlayer.png|alternativtext=A media player display with a second tab for heating control|ohne|mini|The IFRAME containing the web interface at the top (90% height) and the two tabs at the bottom.  One for the Music and a second for the Heating.]]&lt;br /&gt;
&lt;br /&gt;
=== The Heating Panel ===&lt;br /&gt;
The Heating Panel prioritises control of the room&#039;s temperature using a Wall Mounted Thermostat, if one is available.  If not, it is assumed that one of the Radiator Thermostats can be used and that all, if more than one, are linked together.&lt;br /&gt;
&lt;br /&gt;
The Heating Panel just allows the setting of the temperature in the same room.  None of the other room&#039;s temperatures may be set, nor can any &amp;quot;scenes&amp;quot; be set.&lt;br /&gt;
[[Datei:KioskHeatingThermostat.png|alternativtext=A dial with a gradient to show actual temperature and a needle to show desired temperature|ohne|mini|An FTUI3 Compound Thermostat showing Actual and Desired Temperature]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
The FTUI3 code, available from GITHUB should be downloaded and added to FHEM following the instructions here:   https://github.com/knowthelist/ftui&lt;br /&gt;
&lt;br /&gt;
Create an HTTPSRV address for the kioskUI:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
define kioskUI HTTPSRV kiosk /opt/fhem/www/ftui Kiosk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
All files are in this zip&lt;br /&gt;
&lt;br /&gt;
There is a lot of detail about how it works in here to aid understanding.   &lt;br /&gt;
&lt;br /&gt;
It could also help a user understand how the basic framework can be extended to include additional devices as there are examples of a simple IFRAME Panel, a relatively simple Thermostat Panel and a complex set of Lighting Panels.&lt;br /&gt;
&lt;br /&gt;
The FTUI-TAB-VIEWs are more designed to be operated from a single set of FTUI-TAB elements - you click one and it becomes active, displaying the correct TAB-VIEW.  This operating model did not readily lend itself to dynamic tabs - visible on some panels but not others, so new FTUI-TAB elements appear with every FTUI-TAB-VIEW&lt;br /&gt;
&lt;br /&gt;
=== Kiosk HTML ===&lt;br /&gt;
The HTML is divided into three sections&lt;br /&gt;
&lt;br /&gt;
==== Initializing FTUI3 ====&lt;br /&gt;
In the &amp;lt;HEAD&amp;gt; the FTUI3 code and META tags that configure it are defined.   This implementation choses not to use Toast messages.  The viewport is defined as non-scalable as well.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot;&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-height, height=device-width, user-scalable = no, initial-scale = 1, maximum-scale = 1, minimum-scale = 1&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;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://&amp;lt;your-url&amp;gt;:8083/fhem/&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;refresh_interval&amp;quot; content=&amp;quot;60&amp;quot;&amp;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;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Initialize the Kiosk ====&lt;br /&gt;
.This code is also in the &amp;lt;HEAD&amp;gt;.  Should a Kiosk specific theme be required, this can be included here &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;!-- SET UP THE KIOSK --&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;KIOSK&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_class.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/init.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_functions.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;./kiosk/kiosk_styles.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;!-- &amp;lt;link href=&amp;quot;./kiosk/kiosk-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt; --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== body - The Outline Panels ====&lt;br /&gt;
The &amp;lt;BODY&amp;gt; contains outlines of the Panels, ready for customisation by the JavaScript.&lt;br /&gt;
&lt;br /&gt;
The body has a kiosk_onLoad() function - this is part of the Kiosk customisation and is called to customise the predefined panels once the outlines have been loaded.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;45&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;body&amp;quot; onload=&amp;quot;kiosk_onLoad()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The first tab defined is a clock.  This should only ever be shown if there are no controllable devices in the room.  It&#039;s included so that this is obvious (for instance if something has gone wrong with the Kiosk definition).  There are also three clickable tabs in this panel.  These allow the configuration to be manually checked when something has gone wrong.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;46&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Clock&amp;quot; active&amp;gt;    &lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;height:90vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot; color=&amp;quot;primary&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;ee&amp;quot; size=&amp;quot;6&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;DD&amp;quot; size=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-clock format=&amp;quot;hh:mm&amp;quot; size=&amp;quot;9&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-tab-row&amp;quot; style=&amp;quot;height:10vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile  col=&amp;quot;4&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Lighting-Main&amp;quot;  id=&amp;quot;click-lighting&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;lightbulb&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;2&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Player-Main&amp;quot; id=&amp;quot;click-player&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;music&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;3&amp;quot;  width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Heating-Main&amp;quot;  id=&amp;quot;click-heating&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;fire&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile col=&amp;quot;1&amp;quot; width=&amp;quot;0.25&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Clock&amp;quot; id=&amp;quot;click-clock&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;clock-o&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt; &lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
  &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The next section defines the main panels for the Lighting, &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;81&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Main&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;25%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&amp;quot;LightingPower&amp;quot;&lt;br /&gt;
                       value=&amp;quot;off&amp;quot; fill=&amp;quot;outline&amp;quot; shape=&amp;quot;circle&amp;quot; states=&amp;quot;on,off&amp;quot; color=&amp;quot;gray&amp;quot;&lt;br /&gt;
                       @value-change=&#039;powerChange($event)&#039;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;power-off&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;75%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-slider  id=&amp;quot;LightingDimmer&amp;quot;&lt;br /&gt;
                        value=&amp;quot;1&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot;&lt;br /&gt;
                        color=&amp;quot;white&amp;quot;&lt;br /&gt;
                        @value-change=&amp;quot;dimmerChange($event)&amp;quot;&lt;br /&gt;
                        &amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-slider&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Scenes&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;cog&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Settings&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Heating &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;108&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Heating-Main&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row id=&amp;quot;Heating-Tabs&amp;quot;class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;and Audio&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;112&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Player-Main&amp;quot; &amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;iframe  id=&amp;quot;Player-kiosk&amp;quot; src=&amp;quot;&amp;quot; style=&amp;quot;height:90vh;width:100vw;overflow:hidden;padding:0px;border:none;margin:0px&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Player-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Scenes Tab and Success Popup (line 164) are also defined.  At line 150, the tab selector for the first Lighting Detail Panel is created:&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;119&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Scenes&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;gap:5em;&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-grid margin=&amp;quot;5&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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene1_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 1&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene1&#039; style=&#039;box-shadow:0px 0px 10px 5px #B26464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot; @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;Press to Select&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;1&amp;quot; col=&amp;quot;2&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene2_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 2&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene2&#039; style=&#039;box-shadow:0px 0px 10px 5px #FF6464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;1&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene3_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 3&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene3&#039; style=&#039;box-shadow:0px 0px 10px 5px #64C9C9;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene4_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 4&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene4&#039; style=&#039;box-shadow:0px 0px 10px 5px #1F6F3F;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;3&amp;quot; col=&amp;quot;1&amp;quot; width=&#039;2&#039; height=&#039;0.1&#039;&amp;gt;&lt;br /&gt;
          Long hold to set&lt;br /&gt;
        &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Scenes-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Main&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;reply&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Go Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Settings-Detail0-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Setting-Detail0&amp;quot;  timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;tasks&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Details&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;ftui-label id=&amp;quot;scene-success-click&amp;quot; @click=&#039;scene_success.open()&#039; style=&amp;quot;display:none&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_fail&amp;quot; timeout=&amp;quot;15&amp;quot; color=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Failed To Save&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Failed to Save Scene&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_success&amp;quot; timeout=&amp;quot;5&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Saved&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Scene Saved&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kiosk JavaScript ===&lt;br /&gt;
The JavaScript is split into three files:&lt;br /&gt;
&lt;br /&gt;
* light_class.js is a class that is used to define each light controlled by the UI.  It contains functions used to aid the communication with FHEM and FTUI3 - mutation observers and simple functions for hex conversion&lt;br /&gt;
* init.js is all the code used to set up the UI and initialise the display Panels&lt;br /&gt;
* light_functions.js is the code use to manage the lighting Panels as these are far more complex and don&#039;t rely on native FTUI3&lt;br /&gt;
&lt;br /&gt;
==== light_class.js ====&lt;br /&gt;
&lt;br /&gt;
===== Properties =====&lt;br /&gt;
The light class has these properties:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
this.name = &#039;&#039;;&lt;br /&gt;
this.mode = &#039;&#039;;&lt;br /&gt;
this.red = 0;&lt;br /&gt;
this.green = 0;&lt;br /&gt;
this.blue = 0;&lt;br /&gt;
this.white = 0;&lt;br /&gt;
this.state = &#039;off&#039;;&lt;br /&gt;
this.scene1 = &#039;off&#039;;&lt;br /&gt;
this.scene2 = &#039;off&#039;;&lt;br /&gt;
this.scene3 = &#039;off&#039;;&lt;br /&gt;
this.scene4 = &#039;off&#039;;&lt;br /&gt;
this.state = &#039;off&#039;;&lt;br /&gt;
this.observerConfig = {&lt;br /&gt;
      attributes: true // this is to watch for attribute changes.&lt;br /&gt;
      ,attributeOldValue: true&lt;br /&gt;
  }&lt;br /&gt;
//set by front end only&lt;br /&gt;
this.was = &#039;off&#039;;&lt;br /&gt;
this.sliderObserver = &#039;&#039;;&lt;br /&gt;
this.stateObserver = &#039;&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;These are set from FHEM during the initialisation of each light by the doInit() function in init.js&lt;br /&gt;
&lt;br /&gt;
The last three properties are set by the front end only and are there to manage continuity of state:&lt;br /&gt;
&lt;br /&gt;
* this.was is set when the master power button is set to &amp;quot;off&amp;quot;.  It records whether the individual light was on or off at the time.  When the master power button is subsequently set to &amp;quot;on&amp;quot;, this.was is used to only turn on lights that were previously on.  Unless all were off, in which case all are turned on.&lt;br /&gt;
* this.sliderObserver and stateObserver are used to pick up changes made in FHEM, or via a physical switch then communicated by the Shelly device to FHEM, to the detailed light values (RGBW and On/Off).  The FTUI3 events @value-change do not get triggered when this happens (which is a good thing).   When the user is interacting with the KioskUI, FHEM can sometimes  send the results too quickly, leading to circular updates.  These observers are disconnected temporarily to prevent this.&lt;br /&gt;
&lt;br /&gt;
===== Methods =====&lt;br /&gt;
These methods are used to help with managing the light&lt;br /&gt;
&lt;br /&gt;
* set dim - used by the master dimmer slider to recalculate the new values for RGBW after the slider has changed value.  The dimmer currently sets minimum and maximum values than can be dimmed based on the color composition of the target light.  This is to help prevent a change in the colour composition that could be caused by a min-&amp;gt;max-&amp;gt;min dim sequence.   Sometimes, change is inevitable though.&lt;br /&gt;
* rgbw2hex, rgb2hex and w2hex are used to convert the internal RGBW channel values to a hex value that can be transmitted to FHEM&lt;br /&gt;
** ideally the rgb color picker ftui component would have been used, but integrating the white channel would still be required for an RGBW light - for now the display looks more integrated across all types of light as separate channels&lt;br /&gt;
* template.  This returns the light template used to initialise the light detail.  The FTUI3 components returned are dependent on whether the light is RGBW, RGB or just a dimmer.&lt;br /&gt;
&lt;br /&gt;
==== init.js ====&lt;br /&gt;
init.js has three distinct phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1 - actions taken as the code is loaded&lt;br /&gt;
* Phase 2 - actions taken once the &amp;lt;BODY&amp;gt; has loaded&lt;br /&gt;
* Phase 3 - actions taken once the FTUI3 app has finished initializing&lt;br /&gt;
&lt;br /&gt;
===== Phase 1 - Code Load =====&lt;br /&gt;
&lt;br /&gt;
* Two global objects are defined&lt;br /&gt;
** Lights is a collection of all the light definitions&lt;br /&gt;
** Kiosk holds global parameters and settings&lt;br /&gt;
&lt;br /&gt;
* The room is retrieved from the URL Query String&lt;br /&gt;
* An event listener is defined to wait for the FTUI3 app to complete initialisation&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// Gloabl Objects&lt;br /&gt;
var lights = {}&lt;br /&gt;
var Kiosk = {}&lt;br /&gt;
&lt;br /&gt;
// Funtion to get the Room&lt;br /&gt;
var getUrlParameter = function getUrlParameter(sParam) {&lt;br /&gt;
  var sPageURL = window.location.search.substring(1),&lt;br /&gt;
      sURLVariables = sPageURL.split(&#039;&amp;amp;&#039;),&lt;br /&gt;
      sParameterName,&lt;br /&gt;
      i;&lt;br /&gt;
&lt;br /&gt;
  for (i = 0; i &amp;lt; sURLVariables.length; i++) {&lt;br /&gt;
      sParameterName = sURLVariables[i].split(&#039;=&#039;);&lt;br /&gt;
&lt;br /&gt;
      if (sParameterName[0] === sParam) {&lt;br /&gt;
          return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  return false;&lt;br /&gt;
};  &lt;br /&gt;
&lt;br /&gt;
// Get the CSRF token - this code is copied from the FTUI3 App&lt;br /&gt;
fetch(&amp;quot;http://&amp;lt;your-fhem-ip&amp;gt;:8083/fhem?XHR=1&amp;quot;).then(response =&amp;gt; {&lt;br /&gt;
    Kiosk.ftwcsrf = response.headers.get(&#039;X-FHEM-csrfToken&#039;);&lt;br /&gt;
    console.log(&#039;Got csrf from FHEM:&#039; + Kiosk.ftwcsrf);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
//Set the CSRF token in the Kiosk object&lt;br /&gt;
Kiosk.thisRoom=getUrlParameter(&#039;room&#039;)&lt;br /&gt;
&lt;br /&gt;
//Watch for the FTUI3 end of initialisation event&lt;br /&gt;
document.addEventListener(&#039;ftuiPageInitialized&#039;,() =&amp;gt; onFTUIReady())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Phase 2 - After the Body is loaded =====&lt;br /&gt;
Three functions&lt;br /&gt;
&lt;br /&gt;
* kiosk_onLoad() is called by the browser when the body has finished loading&lt;br /&gt;
* This waits (up to 50 seconds) until the csrf token has been set in the Kiosk object using function isCSRFSet() &lt;br /&gt;
* Then it calls the doInit() function&lt;br /&gt;
&lt;br /&gt;
The doInit() function fetches relevant details from FHEM (line 56) for the types of devices to be monitored that are in the room specified by Kiosk.thisRoom.  This ensures only one call to FHEM for the information&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;34&amp;quot;&amp;gt;&lt;br /&gt;
function isCSRFSet(timeout) {&lt;br /&gt;
    var start = Date.now();&lt;br /&gt;
    return new Promise(checkCSRF); // set the promise object within the ensureFooIsSet object&lt;br /&gt;
 &lt;br /&gt;
    function checkCSRF(resolve, reject) {&lt;br /&gt;
        if (Kiosk &amp;amp;&amp;amp; Kiosk.ftwcsrf)&lt;br /&gt;
            resolve(Kiosk.ftwcsrf);&lt;br /&gt;
        else if (timeout &amp;amp;&amp;amp; (Date.now() - start) &amp;gt;= timeout)&lt;br /&gt;
            reject(new Error(&amp;quot;timeout&amp;quot;));&lt;br /&gt;
        else&lt;br /&gt;
            setTimeout(checkCSRF.bind(this, resolve, reject), 30);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
function kiosk_onLoad(){&lt;br /&gt;
    isCSRFSet(50000).then(function(){&lt;br /&gt;
        doInit()&lt;br /&gt;
    }&lt;br /&gt;
    )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function doInit(){&lt;br /&gt;
    fetch(`http://192.168.1.2:8083/fhem?XHR=1&amp;amp;cmd=jsonlist2+room=${Kiosk.thisRoom}+TYPE+type+SHELLY+NAME+PLAYERNAME+model+scene1+scene2+scene3+scene4+state+L-white+L-red+L-green+L-blue&amp;amp;fwcsrf=${Kiosk.ftwcsrf}`).then(res =&amp;gt; {&lt;br /&gt;
        return res.json()&lt;br /&gt;
      }).then((response) =&amp;gt; {&lt;br /&gt;
        o=JSON.parse(JSON.stringify(response))&lt;br /&gt;
        console.log(&#039;res: &#039; + JSON.stringify(response))&lt;br /&gt;
        var hasPlayer=false&lt;br /&gt;
        var hasLighting=false&lt;br /&gt;
        var hasHeating=false&lt;br /&gt;
        var hasThermostat=false&lt;br /&gt;
        var lightType=&amp;quot;&amp;quot;&lt;br /&gt;
        for (i=0;i&amp;lt;o.Results.length;i++){&lt;br /&gt;
            &lt;br /&gt;
            [... init code]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;For each device returned, the doInit() code sets up the Panels required to manage them through the Kiosk UI.  The code here is not in the sequence in init.js, but grouped together to show how the process fits together&lt;br /&gt;
&lt;br /&gt;
====== Audio ======&lt;br /&gt;
Whilst looping over the Results, check whether there is a player&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;155&amp;quot;&amp;gt;&lt;br /&gt;
        // any players&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;SB_PLAYER&#039;){&lt;br /&gt;
          hasPlayer=true&lt;br /&gt;
          var playerName=o.Results[i].Internals.PLAYERNAME&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Once all results are in, if a player was found, customise the Panel&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;209&amp;quot;&amp;gt;&lt;br /&gt;
      if (hasPlayer===true){&lt;br /&gt;
        var tabPrefix=&#039;Player&#039;&lt;br /&gt;
        //set up the tab&lt;br /&gt;
        document.getElementById(&amp;quot;Player-kiosk&amp;quot;).src=&amp;quot;http://&amp;lt;your web address&amp;gt;:9000/material/?single&amp;amp;page=now-playing&amp;amp;player=&amp;quot;+playerName&lt;br /&gt;
        &lt;br /&gt;
        ...&lt;br /&gt;
        // add the active (non-clickable) tab to the Player tab row&lt;br /&gt;
        createPlayerTab(tabPrefix,&#039;kiosk-tab-player-active&#039;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Add Tabs to the other panels so that those panels can switch to the payer Panel.  For instance &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;179&amp;quot;&amp;gt;&lt;br /&gt;
if (hasLighting===true){&lt;br /&gt;
    ...&lt;br /&gt;
    &lt;br /&gt;
    if (hasPlayer===true){&lt;br /&gt;
          createPlayerTab(tabPrefix,&#039;kiosk-tab-player-inactive&#039;,timeout=60)&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Helper function to create the player tab&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;305&amp;quot;&amp;gt;&lt;br /&gt;
function createPlayerTab(view,state,timeout=&amp;quot;0&amp;quot;){&lt;br /&gt;
    divHtml = &amp;quot;&amp;lt;ftui-column id=&#039;&amp;quot;+view+&amp;quot;-Player-Tab&#039; class=&#039;&amp;quot;+state+&amp;quot;&#039;&amp;gt;&amp;lt;ftui-tab class=&#039;kiosk-button&#039; timeout=&#039;&amp;quot;+timeout+&amp;quot;&#039; view=&#039;Player-Main&#039;&amp;gt;&amp;lt;ftui-icon name=&#039;music&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&amp;lt;/ftui-tab&amp;gt;&amp;lt;/ftui-column&amp;gt;&amp;quot;&lt;br /&gt;
    document.getElementById(view+&#039;-Tabs&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Heating ======&lt;br /&gt;
This works in roughly the same way, except with slightly different code.&lt;br /&gt;
&lt;br /&gt;
This selects a wall mounted thermostat as the device to control, if present.  Otherwise it uses the last found radiator thermostat&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;161&amp;quot;&amp;gt;&lt;br /&gt;
        // any heating&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;MAX&#039;){&lt;br /&gt;
            hasHeating=true&lt;br /&gt;
            if (hasThermostat===false){&lt;br /&gt;
              var heatingName=o.Results[i].Internals.NAME&lt;br /&gt;
            }&lt;br /&gt;
            if (o.Results[i].Internals.type==&#039;WallMountedThermostat&#039;){&lt;br /&gt;
              hasThermostat=true&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Heating Panel is defined thus:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;230&amp;quot;&amp;gt;&lt;br /&gt;
     if (hasHeating===true){&lt;br /&gt;
        addHeatingControl(heatingName)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Heating code creates the full panel, rather than customising an existing one as the Audio one does.  The Heating panel uses two, differently scaled FTUI-KNOB elements placed one on top of the other.   &lt;br /&gt;
&lt;br /&gt;
* The bottom one, scaled to use all the width and height, is the actual temperature display and is read only&lt;br /&gt;
* The top one, scaled to 70% of the width and height, has a transparent background, and displays the desiredTemperature.  This is clickable to change the temperature.  Ideally new properties of FTUI-KNOB could be included so the actual/desired temperatures can be displayed in the same way.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;313&amp;quot;&amp;gt;&lt;br /&gt;
function addHeatingControl(heatingName){&lt;br /&gt;
  divHtml=&#039;&amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&#039;+&lt;br /&gt;
    &#039;&amp;lt;ftui-knob height=&amp;quot;100vh&amp;quot; width=&amp;quot;100vw&amp;quot; stroke-width=&amp;quot;15&amp;quot; ticks=&amp;quot;25&amp;quot; style=&amp;quot;z-index:1;position:absolute;&amp;quot; has-arc readonly has-scale-text has-scale color=&amp;quot;cold-hot&amp;quot; [value]=&amp;quot;&#039;+heatingName+&#039;:temperature&amp;quot; max=&amp;quot;30&amp;quot; min=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-knob&amp;gt;&#039; +&lt;br /&gt;
    &#039;&amp;lt;ftui-knob height=&amp;quot;70vh&amp;quot; width=&amp;quot;70vw&amp;quot; stroke-width=&amp;quot;5&amp;quot; style=&amp;quot;z-index: 2;background:transparent;position:absolute;&amp;quot; has-needle has-value-text unit=&amp;quot;°C&amp;quot; unit-offset-y=&amp;quot;40&amp;quot; value-size=&amp;quot;5em&amp;quot; unit-size=&amp;quot;2em&amp;quot; value-decimals=&amp;quot;1&amp;quot; step=&amp;quot;0.5&amp;quot; [(value)]=&amp;quot;&#039;+heatingName+&#039;:desiredTemperature&amp;quot; max=&amp;quot;30&amp;quot; min=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-knob&amp;gt;&#039;+&lt;br /&gt;
    &#039;&amp;lt;/ftui-row&amp;gt;&#039;&lt;br /&gt;
    document.getElementById(&#039;Heating-Main&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The insertion of tabs uses the same tests and similar functions&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;301&amp;quot;&amp;gt;&lt;br /&gt;
function createHeatingTab(view,state,timeout=&amp;quot;0&amp;quot;){&lt;br /&gt;
    divHtml = &amp;quot;&amp;lt;ftui-column id=&#039;&amp;quot;+view+&amp;quot;-Heating-Tab&#039; class=&#039;&amp;quot;+state+&amp;quot;&#039;&amp;gt;&amp;lt;ftui-tab class=&#039;kiosk-button&#039; timeout=&#039;&amp;quot;+timeout+&amp;quot;&#039; view=&#039;Heating-Main&#039;&amp;gt;&amp;lt;ftui-icon name=&#039;fire&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&amp;lt;ftui-label&amp;gt;Heating&amp;lt;/ftui-label&amp;gt;&amp;lt;/ftui-tab&amp;gt;&amp;lt;/ftui-column&amp;gt;&amp;quot;&lt;br /&gt;
    document.getElementById(view+&#039;-Tabs&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Lighting ======&lt;br /&gt;
This is the most complex of the Panels to set up.   &lt;br /&gt;
&lt;br /&gt;
The results from the fetch are added to a new light() object and the light object added to the list of lights for the room.  Note this includes: RGBW levels, the state (on/off) and the settings of each scene for this light.&lt;br /&gt;
&lt;br /&gt;
Then the mutation observers for the light details are created and references saved to the light() object.&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;
// any lights&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;Shelly&#039;){&lt;br /&gt;
            hasLighting=true&lt;br /&gt;
            // most advanced defines the model&lt;br /&gt;
            if (lightType!=&#039;shellyrgbw&#039;){&lt;br /&gt;
                lightType=o.Results[i].Attributes.model&lt;br /&gt;
            }&lt;br /&gt;
            var thisLight= new light()&lt;br /&gt;
            thisLight.model=o.Results[i].Attributes.model&lt;br /&gt;
            thisLight.name=o.Results[i].Internals.NAME&lt;br /&gt;
            thisLight.R=(o.Results[i].Readings[&#039;L-red&#039;])?o.Results[i].Readings[&#039;L-red&#039;].Value:0&lt;br /&gt;
            thisLight.B=(o.Results[i].Readings[&#039;L-blue&#039;])?o.Results[i].Readings[&#039;L-blue&#039;].Value:0&lt;br /&gt;
            thisLight.G=(o.Results[i].Readings[&#039;L-green&#039;])?o.Results[i].Readings[&#039;L-green&#039;].Value:0&lt;br /&gt;
            thisLight.W=(o.Results[i].Readings[&#039;L-white&#039;])?o.Results[i].Readings[&#039;L-white&#039;].Value:0&lt;br /&gt;
            thisLight.scene1=(o.Results[i].Readings.scene1)?o.Results[i].Readings.scene1.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene2=(o.Results[i].Readings.scene2)?o.Results[i].Readings.scene2.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene3=(o.Results[i].Readings.scene3)?o.Results[i].Readings.scene3.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene4=(o.Results[i].Readings.scene4)?o.Results[i].Readings.scene4.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.state=(o.Results[i].Readings.state)?o.Results[i].Readings.state.Value:&#039;off&#039;&lt;br /&gt;
            lights[thisLight.name] = thisLight&lt;br /&gt;
           &lt;br /&gt;
            let mySliderObserver = new MutationObserver( function(mutations)  {&lt;br /&gt;
                  mutations.forEach(function(mutation){&lt;br /&gt;
                    if (mutation.attributeName==&#039;value&#039;){&lt;br /&gt;
                      const [key, attribute] = mutation.target.id.split(&#039;:&#039;)&lt;br /&gt;
                      lights[key][attribute] = mutation.target.value&lt;br /&gt;
                      updateFhem(&amp;quot;set &amp;quot;+key+&amp;quot; rgbw &amp;quot;+lights[key].hex );&lt;br /&gt;
                      setDimmerLevel()&lt;br /&gt;
                    }&lt;br /&gt;
                  }&lt;br /&gt;
                )&lt;br /&gt;
              }&lt;br /&gt;
            )&lt;br /&gt;
            lights[thisLight.name].sliderObserver = mySliderObserver&lt;br /&gt;
&lt;br /&gt;
            let myStateObserver = new MutationObserver(function(mutations)  {&lt;br /&gt;
              mutations.forEach(function(mutation){&lt;br /&gt;
                if (mutation.attributeName==&#039;value&#039;){&lt;br /&gt;
                  //save approprite value&lt;br /&gt;
                  var key = mutation.target.id.split(&#039;:&#039;)[0]&lt;br /&gt;
                  lights[key].state = mutation.target.value&lt;br /&gt;
                  var anyOn=false&lt;br /&gt;
                  for (var key in lights){&lt;br /&gt;
                    if (lights[key].state==&#039;on&#039;){&lt;br /&gt;
                      anyOn=true&lt;br /&gt;
                      break;&lt;br /&gt;
                    }&lt;br /&gt;
                  }&lt;br /&gt;
                setPowerColor(anyOn)&lt;br /&gt;
                setDimmerLevel()&lt;br /&gt;
                }&lt;br /&gt;
              })&lt;br /&gt;
             }&lt;br /&gt;
            )&lt;br /&gt;
            lights[thisLight.name].stateObserver = myStateObserver&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Once the results have all been processed&lt;br /&gt;
&lt;br /&gt;
* the scene colours are set on the Lighting Scenes Panel&lt;br /&gt;
* the individual controls for each light are added to a Lighting Details Panel (up to three lights per panel):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;174&amp;quot;&amp;gt;&lt;br /&gt;
if (hasLighting===true){&lt;br /&gt;
        var tabPrefix=&#039;Lighting&#039;&lt;br /&gt;
        //options first&lt;br /&gt;
        setSceneColors()&lt;br /&gt;
        //create the details panes&lt;br /&gt;
        for (let i=0; i &amp;lt; Object.keys(lights).length;i++){&lt;br /&gt;
          if (i%3 == 0){&lt;br /&gt;
            //set up a new detail tab every three tlights&lt;br /&gt;
            var counter=Math.floor(i/3)&lt;br /&gt;
            createLightingDetailsTab(counter)&lt;br /&gt;
          }&lt;br /&gt;
          createLightingDetail(counter,Object.keys(lights)[i])&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The individual controls are set by the light() object .template() method - this ensures the right controls are placed on the panel for the type of light&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;361&amp;quot;&amp;gt;&lt;br /&gt;
function createLightingDetail(counter,key){&lt;br /&gt;
  document.getElementById(`Lighting-Setting_Detail${counter}-Body`).insertAdjacentHTML(&#039;beforeend&#039;,lights[key].template())&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Details Tab is created and a forward reference created on the previous tab, if required:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;320&amp;quot;&amp;gt;&lt;br /&gt;
function createLightingDetailsTab(counter){&lt;br /&gt;
    switch (counter){&lt;br /&gt;
    case 0:&lt;br /&gt;
        var goBack=&amp;quot;Lighting-Scenes&amp;quot;&lt;br /&gt;
        var goForwardId = false&lt;br /&gt;
        break;&lt;br /&gt;
    default: &lt;br /&gt;
        var lastPanel = counter - 1;&lt;br /&gt;
        var goBack=`Lighting-Setting-Detail${lastPanel}`&lt;br /&gt;
        var goForward=`Lighting-Setting-Detail${counter}`&lt;br /&gt;
        var goForwardId = `Lighting-Setting-${lastPanel}-Tabs`&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.body.insertAdjacentHTML(&#039;beforeend&#039;,&lt;br /&gt;
    `&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Setting-Detail${counter}&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Setting_Detail${counter}-Body&amp;quot; class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;flex-direction:column&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Settings-Detail${counter}-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Setting-${counter}-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Setting-${counter}-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;${goBack}&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-icon name=&#039;long-arrow-left&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-label&amp;gt;Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;`&lt;br /&gt;
    )&lt;br /&gt;
    if (goForward){&lt;br /&gt;
    document.getElementById(goForwardId).insertAdjacentHTML(&#039;beforeend&#039;,&lt;br /&gt;
    `&amp;lt;ftui-column id=&amp;quot;Lighting-Setting-${lastPanel}-Forward-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;${goForward}&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-icon name=&#039;long-arrow-right&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-label&amp;gt;Next page&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-column&amp;gt;`&lt;br /&gt;
    )&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;After this, tab references similar to Audio and Heating are added. &lt;br /&gt;
&lt;br /&gt;
===== Phase 3 - After FTUI is initialised =====&lt;br /&gt;
Some actions can only be performed once FTUI is ready to process them.  One of these appears to be changing the header color on an FTUI-GRID, so it has to wait until after the FTUI event &#039;ftuiPageInitialized&#039; has been dispatched.&lt;br /&gt;
&lt;br /&gt;
If the current light setting matches the same scene setting for all lights in the room, the scene header is set to &amp;quot;success&amp;quot; to indicate it is currently selected.  If not, no action is taken&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
function onFTUIReady(){&lt;br /&gt;
    console.log(&amp;quot;My FTUI Ready&amp;quot;)&lt;br /&gt;
    //for each scene in each light, confirm that the current light setting matches the same scene (if any)&lt;br /&gt;
    var matchScene=false&lt;br /&gt;
    for (key in lights){&lt;br /&gt;
        if (matchScene==-1){&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        var scenes=[lights[key].scene1,lights[key].scene2,lights[key].scene3,lights[key].scene4]&lt;br /&gt;
        if (!matchScene){&lt;br /&gt;
            matchScene = scenes.indexOf(lights[key].hex)&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
            matchScene = scenes[matchScene].indexOf(lights[key].hex)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    scene_to_select = [&#039;scene1&#039;,&#039;scene2&#039;,&#039;scene3&#039;,&#039;scene4&#039;][matchScene]&lt;br /&gt;
    switch (scene_to_select){&lt;br /&gt;
        case &#039;&#039;: &lt;br /&gt;
            break;&lt;br /&gt;
        case undefined: &lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            document.getElementById(scene_to_select+&#039;_header&#039;).color=&#039;success&#039;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== light_functions_js ====&lt;br /&gt;
The light functions work together to make sure the UI reflects FHEM consistently and vice versa.  The principle of how it works is described in this diagram for state changes:&lt;br /&gt;
&lt;br /&gt;
If the master power button is clicked in the UI, the change is propagated to the detail tab for all lights.   The change is also sent directly to FHEM and updated in the light() objec&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These light functions are used to set the individual light settings when the master power and dimmer slider are changed:&lt;br /&gt;
&lt;br /&gt;
* powerChange - changes all &amp;quot;on&amp;quot; lights to &amp;quot;off&amp;quot; and vice versa&lt;br /&gt;
* dimmerChange - changes the values of ALL light channels in proportion to the change to the dimmer&lt;br /&gt;
&lt;br /&gt;
These functions are used to reflect changes to the individual lights in the master power and slider&lt;br /&gt;
&lt;br /&gt;
* setPowerColor - sets the power color to be &amp;quot;off&amp;quot; if all lights are off, or &amp;quot;on&amp;quot; if any on&lt;br /&gt;
* setDimmerLevel - set the slider position to the max of all light channels for all &amp;quot;on&amp;quot; lights or the max of all lights channels for all lights if they are all off.&lt;br /&gt;
&lt;br /&gt;
=== Kiosk CSS ===&lt;br /&gt;
&lt;br /&gt;
== Improvements required ==&lt;br /&gt;
&lt;br /&gt;
# The Heating Thermostat scales well for square displays, but other aspect ratios are not handled well.&lt;br /&gt;
# There is code duplication between this implementation and the FTUI3 App.  This should be eliminated.&lt;br /&gt;
# The hard-coded timeouts should be &amp;lt;META&amp;gt; driven.&lt;br /&gt;
# Control of further devices, using the same principles should be possible.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39043</id>
		<title>Benutzer:Rstooks21/Kiosk UI</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39043"/>
		<updated>2024-01-23T18:16:42Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: /* BODY - The Outline Panels */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle en}}&lt;br /&gt;
&lt;br /&gt;
==Kiosk UI==&lt;br /&gt;
This is an approach to the dynamic creation of a Kiosk UI that can be used by anyone visiting the home. There will be multiple, small, possibly wall mounted Kiosk screens - one in each physical room. The Kiosk UI controls only the devices that are located in the same [FHEM] Room.&lt;br /&gt;
&lt;br /&gt;
== The Objective ==&lt;br /&gt;
To use the new FHEM Tablet UI (FTUI3) to create a tabbed UI showing just the devices in the same FHEM room.  This tabbed UI allows the most important device controls to be shown by default and at the same time allow other devices to be controlled in as few clicks/screen touches as possible.  As well as this, each Panel should not ever display scroll bars.&lt;br /&gt;
&lt;br /&gt;
The rooms selected for testing had some or all of:&lt;br /&gt;
&lt;br /&gt;
* Lighting (Shelly RGDW LED Controller and Shelly DImmer 2, defined to FHEM as Shelly devices)&lt;br /&gt;
* Audio (Logitech Media Players defined to FHEM as SBPLAYER devices)&lt;br /&gt;
* Heating (EQ3 MAX! WallMountedThermostat and RadiatorThermostat, defined as MAX devices)&lt;br /&gt;
&lt;br /&gt;
With a simple call, the Kiosk displays the default Panel, with dynamic tabs for other device control Panels (a tab will only be shown if the room has that type of device).  The default Panel will be implemented in the following order:&lt;br /&gt;
&lt;br /&gt;
* Lighting first&lt;br /&gt;
* If no Lighting, then Audio&lt;br /&gt;
* If no Lighting or Audio, then Heating&lt;br /&gt;
* If no supported devices are in the room, then the clock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This web address is intended to be used in a browser in Kiosk mode&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
http[s]://fhem-location:8083/fhem/kiosk/kiosk.html?room=Kitchen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The &amp;quot;?room=Kitchen&amp;quot; is used to filter devices in the FHEM room &amp;quot;Kitchen&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== The Panels ==&lt;br /&gt;
All the panels time out to the primary panel after 60 seconds of non use.&lt;br /&gt;
&lt;br /&gt;
=== The Lighting Panels ===&lt;br /&gt;
&lt;br /&gt;
==== Main Panel ====&lt;br /&gt;
The main Lighting Panel is a simple On/Off switch and dimmer slider.  The single control will control all of the lights in the room equally&lt;br /&gt;
&lt;br /&gt;
* turning them all off or on&lt;br /&gt;
* dimming all lights in proportion&lt;br /&gt;
&lt;br /&gt;
[[Datei:KioskLightingMainPanel.png|alternativtext=An on/off switch and a slider used for dimming the lights|ohne|mini|An FTUI3 Main Lighting control panel ]]&lt;br /&gt;
Note the four dynamic tabs: &lt;br /&gt;
&lt;br /&gt;
* Lighting (currently active) &lt;br /&gt;
* Music&lt;br /&gt;
* Heating&lt;br /&gt;
* Settings - The Lighting tab has further settings that can be adjusted.&lt;br /&gt;
&lt;br /&gt;
==== Settings/Scene Selector Panel ====&lt;br /&gt;
Selecting the &amp;quot;Settings&amp;quot; tab allows the user to select a scene - a combination of light settings that have been previously saved (by the user in the Kiosk UI)&lt;br /&gt;
[[Datei:KioskLightingScenes.png|alternativtext=A grid of four buttons in a panel display|ohne|mini|FTUI Grids and Buttons used to save and recall scenes]]&lt;br /&gt;
The light settings for all lights in the room are recalled by a short press to any of the buttons. &lt;br /&gt;
&lt;br /&gt;
The current light settings for all lights in the room are stored as a scene by pressing and holding the relevant button. This causes the scene to be saved and the button to glow with the average light colour and intensity for all lights that are on.&lt;br /&gt;
&lt;br /&gt;
==== The Details Panel ====&lt;br /&gt;
From the Scene Selector Panel it is possible to control every light in the room individually.   The Details Panels are flexed to display up to three lights each with as many Panels as needed:&lt;br /&gt;
[[Datei:KioskLightingDetail.png|alternativtext=An on/off switch and four sliders in Red, Green, Blue and White|ohne|mini|FTUI3 Switch and Sliders for RGBW control of a single led strip]]&lt;br /&gt;
&lt;br /&gt;
=== The Audio Panel ===&lt;br /&gt;
The Audio Panel uses an IFRAME to embed the Logitech Media Server web interface into the panel.   Only the player in the room can be controlled - the other players cannot be unless they are synchronised.   This is a bit of a cheat as the facilities in FHEM to control the player and display album art are not used.  But why reinvent?&lt;br /&gt;
[[Datei:KioskAudioPlayer.png|alternativtext=A media player display with a second tab for heating control|ohne|mini|The IFRAME containing the web interface at the top (90% height) and the two tabs at the bottom.  One for the Music and a second for the Heating.]]&lt;br /&gt;
&lt;br /&gt;
=== The Heating Panel ===&lt;br /&gt;
The Heating Panel prioritises control of the room&#039;s temperature using a Wall Mounted Thermostat, if one is available.  If not, it is assumed that one of the Radiator Thermostats can be used and that all, if more than one, are linked together.&lt;br /&gt;
&lt;br /&gt;
The Heating Panel just allows the setting of the temperature in the same room.  None of the other room&#039;s temperatures may be set, nor can any &amp;quot;scenes&amp;quot; be set.&lt;br /&gt;
[[Datei:KioskHeatingThermostat.png|alternativtext=A dial with a gradient to show actual temperature and a needle to show desired temperature|ohne|mini|An FTUI3 Compound Thermostat showing Actual and Desired Temperature]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
The FTUI3 code, available from GITHUB should be downloaded and added to FHEM following the instructions here:   https://github.com/knowthelist/ftui&lt;br /&gt;
&lt;br /&gt;
Create an HTTPSRV address for the kioskUI:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
define kioskUI HTTPSRV kiosk /opt/fhem/www/ftui Kiosk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
All files are in this zip&lt;br /&gt;
&lt;br /&gt;
There is a lot of detail about how it works in here to aid understanding.   &lt;br /&gt;
&lt;br /&gt;
It could also help a user understand how the basic framework can be extended to include additional devices as there are examples of a simple IFRAME Panel, a relatively simple Thermostat Panel and a complex set of Lighting Panels.&lt;br /&gt;
&lt;br /&gt;
The FTUI-TAB-VIEWs are more designed to be operated from a single set of FTUI-TAB elements - you click one and it becomes active, displaying the correct TAB-VIEW.  This operating model did not readily lend itself to dynamic tabs - visible on some panels but not others, so new FTUI-TAB elements appear with every FTUI-TAB-VIEW&lt;br /&gt;
&lt;br /&gt;
=== Kiosk HTML ===&lt;br /&gt;
The HTML is divided into three sections&lt;br /&gt;
&lt;br /&gt;
==== Initializing FTUI3 ====&lt;br /&gt;
In the &amp;lt;HEAD&amp;gt; the FTUI3 code and META tags that configure it are defined.   This implementation choses not to use Toast messages.  The viewport is defined as non-scalable as well.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot;&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-height, height=device-width, user-scalable = no, initial-scale = 1, maximum-scale = 1, minimum-scale = 1&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;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://&amp;lt;your-url&amp;gt;:8083/fhem/&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;refresh_interval&amp;quot; content=&amp;quot;60&amp;quot;&amp;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;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Initialize the Kiosk ====&lt;br /&gt;
.This code is also in the &amp;lt;HEAD&amp;gt;.  Should a Kiosk specific theme be required, this can be included here &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;!-- SET UP THE KIOSK --&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;KIOSK&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_class.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/init.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_functions.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;./kiosk/kiosk_styles.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;!-- &amp;lt;link href=&amp;quot;./kiosk/kiosk-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt; --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== body - The Outline Panels ====&lt;br /&gt;
The &amp;lt;BODY&amp;gt; contains outlines of the Panels, ready for customisation by the JavaScript.&lt;br /&gt;
&lt;br /&gt;
The body has a kiosk_onLoad() function - this is part of the Kiosk customisation and is called to customise the predefined panels once the outlines have been loaded.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;45&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;body&amp;quot; onload=&amp;quot;kiosk_onLoad()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The first tab defined is a clock.  This should only ever be shown if there are no controllable devices in the room.  It&#039;s included so that this is obvious (for instance if something has gone wrong with the Kiosk definition).  There are also three clickable tabs in this panel.  These allow the configuration to be manually checked when something has gone wrong.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;46&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Clock&amp;quot; active&amp;gt;    &lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;height:90vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot; color=&amp;quot;primary&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;ee&amp;quot; size=&amp;quot;6&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;DD&amp;quot; size=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-clock format=&amp;quot;hh:mm&amp;quot; size=&amp;quot;9&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-tab-row&amp;quot; style=&amp;quot;height:10vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile  col=&amp;quot;4&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Lighting-Main&amp;quot;  id=&amp;quot;click-lighting&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;lightbulb&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;2&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Player-Main&amp;quot; id=&amp;quot;click-player&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;music&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;3&amp;quot;  width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Heating-Main&amp;quot;  id=&amp;quot;click-heating&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;fire&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile col=&amp;quot;1&amp;quot; width=&amp;quot;0.25&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Clock&amp;quot; id=&amp;quot;click-clock&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;clock-o&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt; &lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
  &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The next section defines the main panels for the Lighting, &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;81&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Main&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;25%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&amp;quot;LightingPower&amp;quot;&lt;br /&gt;
                       value=&amp;quot;off&amp;quot; fill=&amp;quot;outline&amp;quot; shape=&amp;quot;circle&amp;quot; states=&amp;quot;on,off&amp;quot; color=&amp;quot;gray&amp;quot;&lt;br /&gt;
                       @value-change=&#039;powerChange($event)&#039;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;power-off&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;75%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-slider  id=&amp;quot;LightingDimmer&amp;quot;&lt;br /&gt;
                        value=&amp;quot;1&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot;&lt;br /&gt;
                        color=&amp;quot;white&amp;quot;&lt;br /&gt;
                        @value-change=&amp;quot;dimmerChange($event)&amp;quot;&lt;br /&gt;
                        &amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-slider&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Scenes&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;cog&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Settings&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Heating &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;108&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Heating-Main&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row id=&amp;quot;Heating-Tabs&amp;quot;class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;and Audio&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;112&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Player-Main&amp;quot; &amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;iframe  id=&amp;quot;Player-kiosk&amp;quot; src=&amp;quot;&amp;quot; style=&amp;quot;height:90vh;width:100vw;overflow:hidden;padding:0px;border:none;margin:0px&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Player-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Scenes Tab and Success Popup (line 164) are also defined.  At line 150, the tab selector for the first Lighting Detail Panel is created:&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;119&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Scenes&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;gap:5em;&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-grid margin=&amp;quot;5&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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene1_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 1&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene1&#039; style=&#039;box-shadow:0px 0px 10px 5px #B26464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot; @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;Press to Select&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;1&amp;quot; col=&amp;quot;2&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene2_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 2&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene2&#039; style=&#039;box-shadow:0px 0px 10px 5px #FF6464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;1&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene3_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 3&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene3&#039; style=&#039;box-shadow:0px 0px 10px 5px #64C9C9;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene4_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 4&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene4&#039; style=&#039;box-shadow:0px 0px 10px 5px #1F6F3F;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;3&amp;quot; col=&amp;quot;1&amp;quot; width=&#039;2&#039; height=&#039;0.1&#039;&amp;gt;&lt;br /&gt;
          Long hold to set&lt;br /&gt;
        &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Scenes-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Main&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;reply&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Go Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Settings-Detail0-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Setting-Detail0&amp;quot;  timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;tasks&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Details&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;ftui-label id=&amp;quot;scene-success-click&amp;quot; @click=&#039;scene_success.open()&#039; style=&amp;quot;display:none&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_fail&amp;quot; timeout=&amp;quot;15&amp;quot; color=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Failed To Save&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Failed to Save Scene&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_success&amp;quot; timeout=&amp;quot;5&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Saved&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Scene Saved&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kiosk JavaScript ===&lt;br /&gt;
The JavaScript is split into three files:&lt;br /&gt;
&lt;br /&gt;
* light_class.js is a class that is used to define each light controlled by the UI.  It contains functions used to aid the communication with FHEM and FTUI3 - mutation observers and simple functions for hex conversion&lt;br /&gt;
* init.js is all the code used to set up the UI and initialise the display Panels&lt;br /&gt;
* light_functions.js is the code use to manage the lighting Panels as these are far more complex and don&#039;t rely on native FTUI3&lt;br /&gt;
&lt;br /&gt;
==== light_class.js ====&lt;br /&gt;
&lt;br /&gt;
===== Properties =====&lt;br /&gt;
The light class has these properties:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
this.name = &#039;&#039;;&lt;br /&gt;
this.mode = &#039;&#039;;&lt;br /&gt;
this.red = 0;&lt;br /&gt;
this.green = 0;&lt;br /&gt;
this.blue = 0;&lt;br /&gt;
this.white = 0;&lt;br /&gt;
this.state = &#039;off&#039;;&lt;br /&gt;
this.scene1 = &#039;off&#039;;&lt;br /&gt;
this.scene2 = &#039;off&#039;;&lt;br /&gt;
this.scene3 = &#039;off&#039;;&lt;br /&gt;
this.scene4 = &#039;off&#039;;&lt;br /&gt;
this.state = &#039;off&#039;;&lt;br /&gt;
this.observerConfig = {&lt;br /&gt;
      attributes: true // this is to watch for attribute changes.&lt;br /&gt;
      ,attributeOldValue: true&lt;br /&gt;
  }&lt;br /&gt;
//set by front end only&lt;br /&gt;
this.was = &#039;off&#039;;&lt;br /&gt;
this.sliderObserver = &#039;&#039;;&lt;br /&gt;
this.stateObserver = &#039;&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;These are set from FHEM during the initialisation of each light by the doInit() function in init.js&lt;br /&gt;
&lt;br /&gt;
The last three properties are set by the front end only and are there to manage continuity of state:&lt;br /&gt;
&lt;br /&gt;
* this.was is set when the master power button is set to &amp;quot;off&amp;quot;.  It records whether the individual light was on or off at the time.  When the master power button is subsequently set to &amp;quot;on&amp;quot;, this.was is used to only turn on lights that were previously on.  Unless all were off, in which case all are turned on.&lt;br /&gt;
* this.sliderObserver and stateObserver are used to pick up changes made in FHEM, or via a physical switch then communicated by the Shelly device to FHEM, to the detailed light values (RGBW and On/Off).  The FTUI3 events @value-change do not get triggered when this happens (which is a good thing).   When the user is interacting with the KioskUI, FHEM can sometimes  send the results too quickly, leading to circular updates.  These observers are disconnected temporarily to prevent this.&lt;br /&gt;
&lt;br /&gt;
===== Methods =====&lt;br /&gt;
These methods are used to help with managing the light&lt;br /&gt;
&lt;br /&gt;
* set dim - used by the master dimmer slider to recalculate the new values for RGBW after the slider has changed value.  The dimmer currently sets minimum and maximum values than can be dimmed based on the color composition of the target light.  This is to help prevent a change in the colour composition that could be caused by a min-&amp;gt;max-&amp;gt;min dim sequence.   Sometimes, change is inevitable though.&lt;br /&gt;
* rgbw2hex, rgb2hex and w2hex are used to convert the internal RGBW channel values to a hex value that can be transmitted to FHEM&lt;br /&gt;
** ideally the rgb color picker ftui component would have been used, but integrating the white channel would still be required for an RGBW light - for now the display looks more integrated across all types of light as separate channels&lt;br /&gt;
* template.  This returns the light template used to initialise the light detail.  The FTUI3 components returned are dependent on whether the light is RGBW, RGB or just a dimmer.&lt;br /&gt;
&lt;br /&gt;
==== init.js ====&lt;br /&gt;
init.js has three distinct phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1 - actions taken as the code is loaded&lt;br /&gt;
* Phase 2 - actions taken once the &amp;lt;BODY&amp;gt; has loaded&lt;br /&gt;
* Phase 3 - actions taken once the FTUI3 app has finished initializing&lt;br /&gt;
&lt;br /&gt;
===== Phase 1 - Code Load =====&lt;br /&gt;
&lt;br /&gt;
* Two global objects are defined&lt;br /&gt;
** Lights is a collection of all the light definitions&lt;br /&gt;
** Kiosk holds global parameters and settings&lt;br /&gt;
&lt;br /&gt;
* The room is retrieved from the URL Query String&lt;br /&gt;
* An event listener is defined to wait for the FTUI3 app to complete initialisation&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// Gloabl Objects&lt;br /&gt;
var lights = {}&lt;br /&gt;
var Kiosk = {}&lt;br /&gt;
&lt;br /&gt;
// Funtion to get the Room&lt;br /&gt;
var getUrlParameter = function getUrlParameter(sParam) {&lt;br /&gt;
  var sPageURL = window.location.search.substring(1),&lt;br /&gt;
      sURLVariables = sPageURL.split(&#039;&amp;amp;&#039;),&lt;br /&gt;
      sParameterName,&lt;br /&gt;
      i;&lt;br /&gt;
&lt;br /&gt;
  for (i = 0; i &amp;lt; sURLVariables.length; i++) {&lt;br /&gt;
      sParameterName = sURLVariables[i].split(&#039;=&#039;);&lt;br /&gt;
&lt;br /&gt;
      if (sParameterName[0] === sParam) {&lt;br /&gt;
          return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  return false;&lt;br /&gt;
};  &lt;br /&gt;
&lt;br /&gt;
// Get the CSRF token - this code is copied from the FTUI3 App&lt;br /&gt;
fetch(&amp;quot;http://&amp;lt;your-fhem-ip&amp;gt;:8083/fhem?XHR=1&amp;quot;).then(response =&amp;gt; {&lt;br /&gt;
    Kiosk.ftwcsrf = response.headers.get(&#039;X-FHEM-csrfToken&#039;);&lt;br /&gt;
    console.log(&#039;Got csrf from FHEM:&#039; + Kiosk.ftwcsrf);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
//Set the CSRF token in the Kiosk object&lt;br /&gt;
Kiosk.thisRoom=getUrlParameter(&#039;room&#039;)&lt;br /&gt;
&lt;br /&gt;
//Watch for the FTUI3 end of initialisation event&lt;br /&gt;
document.addEventListener(&#039;ftuiPageInitialized&#039;,() =&amp;gt; onFTUIReady())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Phase 2 - After the Body is loaded =====&lt;br /&gt;
Three functions&lt;br /&gt;
&lt;br /&gt;
* kiosk_onLoad() is called by the browser when the body has finished loading&lt;br /&gt;
* This waits (up to 50 seconds) until the csrf token has been set in the Kiosk object using function isCSRFSet() &lt;br /&gt;
* Then it calls the doInit() function&lt;br /&gt;
&lt;br /&gt;
The doInit() function fetches relevant details from FHEM (line 56) for the types of devices to be monitored that are in the room specified by Kiosk.thisRoom.  This ensures only one call to FHEM for the information&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;34&amp;quot;&amp;gt;&lt;br /&gt;
function isCSRFSet(timeout) {&lt;br /&gt;
    var start = Date.now();&lt;br /&gt;
    return new Promise(checkCSRF); // set the promise object within the ensureFooIsSet object&lt;br /&gt;
 &lt;br /&gt;
    function checkCSRF(resolve, reject) {&lt;br /&gt;
        if (Kiosk &amp;amp;&amp;amp; Kiosk.ftwcsrf)&lt;br /&gt;
            resolve(Kiosk.ftwcsrf);&lt;br /&gt;
        else if (timeout &amp;amp;&amp;amp; (Date.now() - start) &amp;gt;= timeout)&lt;br /&gt;
            reject(new Error(&amp;quot;timeout&amp;quot;));&lt;br /&gt;
        else&lt;br /&gt;
            setTimeout(checkCSRF.bind(this, resolve, reject), 30);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
function kiosk_onLoad(){&lt;br /&gt;
    isCSRFSet(50000).then(function(){&lt;br /&gt;
        doInit()&lt;br /&gt;
    }&lt;br /&gt;
    )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function doInit(){&lt;br /&gt;
    fetch(`http://192.168.1.2:8083/fhem?XHR=1&amp;amp;cmd=jsonlist2+room=${Kiosk.thisRoom}+TYPE+type+SHELLY+NAME+PLAYERNAME+model+scene1+scene2+scene3+scene4+state+L-white+L-red+L-green+L-blue&amp;amp;fwcsrf=${Kiosk.ftwcsrf}`).then(res =&amp;gt; {&lt;br /&gt;
        return res.json()&lt;br /&gt;
      }).then((response) =&amp;gt; {&lt;br /&gt;
        o=JSON.parse(JSON.stringify(response))&lt;br /&gt;
        console.log(&#039;res: &#039; + JSON.stringify(response))&lt;br /&gt;
        var hasPlayer=false&lt;br /&gt;
        var hasLighting=false&lt;br /&gt;
        var hasHeating=false&lt;br /&gt;
        var hasThermostat=false&lt;br /&gt;
        var lightType=&amp;quot;&amp;quot;&lt;br /&gt;
        for (i=0;i&amp;lt;o.Results.length;i++){&lt;br /&gt;
            &lt;br /&gt;
            [... init code]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;For each device returned, the doInit() code sets up the Panels required to manage them through the Kiosk UI.  The code here is not in the sequence in init.js, but grouped together to show how the process fits together&lt;br /&gt;
&lt;br /&gt;
====== Audio ======&lt;br /&gt;
Whilst looping over the Results, check whether there is a player&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;155&amp;quot;&amp;gt;&lt;br /&gt;
        // any players&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;SB_PLAYER&#039;){&lt;br /&gt;
          hasPlayer=true&lt;br /&gt;
          var playerName=o.Results[i].Internals.PLAYERNAME&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Once all results are in, if a player was found, customise the Panel&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;209&amp;quot;&amp;gt;&lt;br /&gt;
      if (hasPlayer===true){&lt;br /&gt;
        var tabPrefix=&#039;Player&#039;&lt;br /&gt;
        //set up the tab&lt;br /&gt;
        document.getElementById(&amp;quot;Player-kiosk&amp;quot;).src=&amp;quot;http://&amp;lt;your web address&amp;gt;:9000/material/?single&amp;amp;page=now-playing&amp;amp;player=&amp;quot;+playerName&lt;br /&gt;
        &lt;br /&gt;
        ...&lt;br /&gt;
        // add the active (non-clickable) tab to the Player tab row&lt;br /&gt;
        createPlayerTab(tabPrefix,&#039;kiosk-tab-player-active&#039;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Add Tabs to the other panels so that those panels can switch to the payer Panel.  For instance &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;179&amp;quot;&amp;gt;&lt;br /&gt;
if (hasLighting===true){&lt;br /&gt;
    ...&lt;br /&gt;
    &lt;br /&gt;
    if (hasPlayer===true){&lt;br /&gt;
          createPlayerTab(tabPrefix,&#039;kiosk-tab-player-inactive&#039;,timeout=60)&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Helper function to create the player tab&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;305&amp;quot;&amp;gt;&lt;br /&gt;
function createPlayerTab(view,state,timeout=&amp;quot;0&amp;quot;){&lt;br /&gt;
    divHtml = &amp;quot;&amp;lt;ftui-column id=&#039;&amp;quot;+view+&amp;quot;-Player-Tab&#039; class=&#039;&amp;quot;+state+&amp;quot;&#039;&amp;gt;&amp;lt;ftui-tab class=&#039;kiosk-button&#039; timeout=&#039;&amp;quot;+timeout+&amp;quot;&#039; view=&#039;Player-Main&#039;&amp;gt;&amp;lt;ftui-icon name=&#039;music&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&amp;lt;/ftui-tab&amp;gt;&amp;lt;/ftui-column&amp;gt;&amp;quot;&lt;br /&gt;
    document.getElementById(view+&#039;-Tabs&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Heating ======&lt;br /&gt;
This works in roughly the same way, except with slightly different code.&lt;br /&gt;
&lt;br /&gt;
This selects a wall mounted thermostat as the device to control, if present.  Otherwise it uses the last found radiator thermostat&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;161&amp;quot;&amp;gt;&lt;br /&gt;
        // any heating&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;MAX&#039;){&lt;br /&gt;
            hasHeating=true&lt;br /&gt;
            if (hasThermostat===false){&lt;br /&gt;
              var heatingName=o.Results[i].Internals.NAME&lt;br /&gt;
            }&lt;br /&gt;
            if (o.Results[i].Internals.type==&#039;WallMountedThermostat&#039;){&lt;br /&gt;
              hasThermostat=true&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Heating Panel is defined thus:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;230&amp;quot;&amp;gt;&lt;br /&gt;
     if (hasHeating===true){&lt;br /&gt;
        addHeatingControl(heatingName)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Heating code creates the full panel, rather than customising an existing one as the Audio one does.  The Heating panel uses two, differently scaled FTUI-KNOB elements placed one on top of the other.   &lt;br /&gt;
&lt;br /&gt;
* The bottom one, scaled to use all the width and height, is the actual temperature display and is read only&lt;br /&gt;
* The top one, scaled to 70% of the width and height, has a transparent background, and displays the desiredTemperature.  This is clickable to change the temperature.  Ideally new properties of FTUI-KNOB could be included so the actual/desired temperatures can be displayed in the same way.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;313&amp;quot;&amp;gt;&lt;br /&gt;
function addHeatingControl(heatingName){&lt;br /&gt;
  divHtml=&#039;&amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&#039;+&lt;br /&gt;
    &#039;&amp;lt;ftui-knob height=&amp;quot;100vh&amp;quot; width=&amp;quot;100vw&amp;quot; stroke-width=&amp;quot;15&amp;quot; ticks=&amp;quot;25&amp;quot; style=&amp;quot;z-index:1;position:absolute;&amp;quot; has-arc readonly has-scale-text has-scale color=&amp;quot;cold-hot&amp;quot; [value]=&amp;quot;&#039;+heatingName+&#039;:temperature&amp;quot; max=&amp;quot;30&amp;quot; min=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-knob&amp;gt;&#039; +&lt;br /&gt;
    &#039;&amp;lt;ftui-knob height=&amp;quot;70vh&amp;quot; width=&amp;quot;70vw&amp;quot; stroke-width=&amp;quot;5&amp;quot; style=&amp;quot;z-index: 2;background:transparent;position:absolute;&amp;quot; has-needle has-value-text unit=&amp;quot;°C&amp;quot; unit-offset-y=&amp;quot;40&amp;quot; value-size=&amp;quot;5em&amp;quot; unit-size=&amp;quot;2em&amp;quot; value-decimals=&amp;quot;1&amp;quot; step=&amp;quot;0.5&amp;quot; [(value)]=&amp;quot;&#039;+heatingName+&#039;:desiredTemperature&amp;quot; max=&amp;quot;30&amp;quot; min=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-knob&amp;gt;&#039;+&lt;br /&gt;
    &#039;&amp;lt;/ftui-row&amp;gt;&#039;&lt;br /&gt;
    document.getElementById(&#039;Heating-Main&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The insertion of tabs uses the same tests and similar functions&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;301&amp;quot;&amp;gt;&lt;br /&gt;
function createHeatingTab(view,state,timeout=&amp;quot;0&amp;quot;){&lt;br /&gt;
    divHtml = &amp;quot;&amp;lt;ftui-column id=&#039;&amp;quot;+view+&amp;quot;-Heating-Tab&#039; class=&#039;&amp;quot;+state+&amp;quot;&#039;&amp;gt;&amp;lt;ftui-tab class=&#039;kiosk-button&#039; timeout=&#039;&amp;quot;+timeout+&amp;quot;&#039; view=&#039;Heating-Main&#039;&amp;gt;&amp;lt;ftui-icon name=&#039;fire&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&amp;lt;ftui-label&amp;gt;Heating&amp;lt;/ftui-label&amp;gt;&amp;lt;/ftui-tab&amp;gt;&amp;lt;/ftui-column&amp;gt;&amp;quot;&lt;br /&gt;
    document.getElementById(view+&#039;-Tabs&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Lighting ======&lt;br /&gt;
This is the most complex of the Panels to set up.   &lt;br /&gt;
&lt;br /&gt;
The results from the fetch are added to a new light() object and the light object added to the list of lights for the room.  Note this includes: RGBW levels, the state (or ort off) and the settings of each scene for this light.&lt;br /&gt;
&lt;br /&gt;
Then the mutation observers for the light details are created and references saved to the light() object.&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;
// any lights&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;Shelly&#039;){&lt;br /&gt;
            hasLighting=true&lt;br /&gt;
            // most advanced defines the model&lt;br /&gt;
            if (lightType!=&#039;shellyrgbw&#039;){&lt;br /&gt;
                lightType=o.Results[i].Attributes.model&lt;br /&gt;
            }&lt;br /&gt;
            var thisLight= new light()&lt;br /&gt;
            thisLight.model=o.Results[i].Attributes.model&lt;br /&gt;
            thisLight.name=o.Results[i].Internals.NAME&lt;br /&gt;
            thisLight.R=(o.Results[i].Readings[&#039;L-red&#039;])?o.Results[i].Readings[&#039;L-red&#039;].Value:0&lt;br /&gt;
            thisLight.B=(o.Results[i].Readings[&#039;L-blue&#039;])?o.Results[i].Readings[&#039;L-blue&#039;].Value:0&lt;br /&gt;
            thisLight.G=(o.Results[i].Readings[&#039;L-green&#039;])?o.Results[i].Readings[&#039;L-green&#039;].Value:0&lt;br /&gt;
            thisLight.W=(o.Results[i].Readings[&#039;L-white&#039;])?o.Results[i].Readings[&#039;L-white&#039;].Value:0&lt;br /&gt;
            thisLight.scene1=(o.Results[i].Readings.scene1)?o.Results[i].Readings.scene1.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene2=(o.Results[i].Readings.scene2)?o.Results[i].Readings.scene2.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene3=(o.Results[i].Readings.scene3)?o.Results[i].Readings.scene3.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene4=(o.Results[i].Readings.scene4)?o.Results[i].Readings.scene4.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.state=(o.Results[i].Readings.state)?o.Results[i].Readings.state.Value:&#039;off&#039;&lt;br /&gt;
            lights[thisLight.name] = thisLight&lt;br /&gt;
           &lt;br /&gt;
            let mySliderObserver = new MutationObserver( function(mutations)  {&lt;br /&gt;
                  mutations.forEach(function(mutation){&lt;br /&gt;
                    if (mutation.attributeName==&#039;value&#039;){&lt;br /&gt;
                      const [key, attribute] = mutation.target.id.split(&#039;:&#039;)&lt;br /&gt;
                      lights[key][attribute] = mutation.target.value&lt;br /&gt;
                      updateFhem(&amp;quot;set &amp;quot;+key+&amp;quot; rgbw &amp;quot;+lights[key].hex );&lt;br /&gt;
                      setDimmerLevel()&lt;br /&gt;
                    }&lt;br /&gt;
                  }&lt;br /&gt;
                )&lt;br /&gt;
              }&lt;br /&gt;
            )&lt;br /&gt;
            lights[thisLight.name].sliderObserver = mySliderObserver&lt;br /&gt;
&lt;br /&gt;
            let myStateObserver = new MutationObserver(function(mutations)  {&lt;br /&gt;
              mutations.forEach(function(mutation){&lt;br /&gt;
                if (mutation.attributeName==&#039;value&#039;){&lt;br /&gt;
                  //save approprite value&lt;br /&gt;
                  var key = mutation.target.id.split(&#039;:&#039;)[0]&lt;br /&gt;
                  lights[key].state = mutation.target.value&lt;br /&gt;
                  var anyOn=false&lt;br /&gt;
                  for (var key in lights){&lt;br /&gt;
                    if (lights[key].state==&#039;on&#039;){&lt;br /&gt;
                      anyOn=true&lt;br /&gt;
                      break;&lt;br /&gt;
                    }&lt;br /&gt;
                  }&lt;br /&gt;
                setPowerColor(anyOn)&lt;br /&gt;
                setDimmerLevel()&lt;br /&gt;
                }&lt;br /&gt;
              })&lt;br /&gt;
             }&lt;br /&gt;
            )&lt;br /&gt;
            lights[thisLight.name].stateObserver = myStateObserver&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Once the results have all been processed&lt;br /&gt;
&lt;br /&gt;
* the scene colours are set on the Lighting Scenes Panel&lt;br /&gt;
* the individual controls for each light are added to a Lighting Details Panel (up to three lights per panel):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;174&amp;quot;&amp;gt;&lt;br /&gt;
if (hasLighting===true){&lt;br /&gt;
        var tabPrefix=&#039;Lighting&#039;&lt;br /&gt;
        //options first&lt;br /&gt;
        setSceneColors()&lt;br /&gt;
        //create the details panes&lt;br /&gt;
        for (let i=0; i &amp;lt; Object.keys(lights).length;i++){&lt;br /&gt;
          if (i%3 == 0){&lt;br /&gt;
            //set up a new detail tab every three tlights&lt;br /&gt;
            var counter=Math.floor(i/3)&lt;br /&gt;
            createLightingDetailsTab(counter)&lt;br /&gt;
          }&lt;br /&gt;
          createLightingDetail(counter,Object.keys(lights)[i])&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The individual controls are set by the light() object .template() method - this ensures the right controls are placed on the panel for the type of light&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;361&amp;quot;&amp;gt;&lt;br /&gt;
function createLightingDetail(counter,key){&lt;br /&gt;
  document.getElementById(`Lighting-Setting_Detail${counter}-Body`).insertAdjacentHTML(&#039;beforeend&#039;,lights[key].template())&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Details Tab is created and a forward reference created on the previous tab, if required:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;320&amp;quot;&amp;gt;&lt;br /&gt;
function createLightingDetailsTab(counter){&lt;br /&gt;
    switch (counter){&lt;br /&gt;
    case 0:&lt;br /&gt;
        var goBack=&amp;quot;Lighting-Scenes&amp;quot;&lt;br /&gt;
        var goForwardId = false&lt;br /&gt;
        break;&lt;br /&gt;
    default: &lt;br /&gt;
        var lastPanel = counter - 1;&lt;br /&gt;
        var goBack=`Lighting-Setting-Detail${lastPanel}`&lt;br /&gt;
        var goForward=`Lighting-Setting-Detail${counter}`&lt;br /&gt;
        var goForwardId = `Lighting-Setting-${lastPanel}-Tabs`&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.body.insertAdjacentHTML(&#039;beforeend&#039;,&lt;br /&gt;
    `&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Setting-Detail${counter}&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Setting_Detail${counter}-Body&amp;quot; class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;flex-direction:column&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Settings-Detail${counter}-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Setting-${counter}-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Setting-${counter}-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;${goBack}&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-icon name=&#039;long-arrow-left&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-label&amp;gt;Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;`&lt;br /&gt;
    )&lt;br /&gt;
    if (goForward){&lt;br /&gt;
    document.getElementById(goForwardId).insertAdjacentHTML(&#039;beforeend&#039;,&lt;br /&gt;
    `&amp;lt;ftui-column id=&amp;quot;Lighting-Setting-${lastPanel}-Forward-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;${goForward}&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-icon name=&#039;long-arrow-right&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-label&amp;gt;Next page&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-column&amp;gt;`&lt;br /&gt;
    )&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;After this, tab references similar to Audio and Heating are added. &lt;br /&gt;
&lt;br /&gt;
===== Phase 3 - After FTUI is initialised =====&lt;br /&gt;
Some actions can only be performed once FTUI is ready to process them.  One of these appears to be changing the header color on an FTUI-GRID, so it has to wait until after the FTUI event &#039;ftuiPageInitialized&#039; has been dispatched.&lt;br /&gt;
&lt;br /&gt;
If the current light setting matches the same scene setting for all lights in the room, the scene header is set to &amp;quot;success&amp;quot; to indicate it is currently selected.  If not, no action is taken&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
function onFTUIReady(){&lt;br /&gt;
    console.log(&amp;quot;My FTUI Ready&amp;quot;)&lt;br /&gt;
    //for each scene in each light, confirm that the current light setting matches the same scene (if any)&lt;br /&gt;
    var matchScene=false&lt;br /&gt;
    for (key in lights){&lt;br /&gt;
        if (matchScene==-1){&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        var scenes=[lights[key].scene1,lights[key].scene2,lights[key].scene3,lights[key].scene4]&lt;br /&gt;
        if (!matchScene){&lt;br /&gt;
            matchScene = scenes.indexOf(lights[key].hex)&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
            matchScene = scenes[matchScene].indexOf(lights[key].hex)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    scene_to_select = [&#039;scene1&#039;,&#039;scene2&#039;,&#039;scene3&#039;,&#039;scene4&#039;][matchScene]&lt;br /&gt;
    switch (scene_to_select){&lt;br /&gt;
        case &#039;&#039;: &lt;br /&gt;
            break;&lt;br /&gt;
        case undefined: &lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            document.getElementById(scene_to_select+&#039;_header&#039;).color=&#039;success&#039;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== light_functions_js ====&lt;br /&gt;
The light functions work together to make sure the UI reflects FHEM consistently and vice versa.  The principle of how it works is described in this diagram for state changes:&lt;br /&gt;
&lt;br /&gt;
If the master power button is clicked in the UI, the change is propagated to the detail tab for all lights.   The change is also sent directly to FHEM and updated in the light() objec&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These light functions are used to set the individual light settings when the master power and dimmer slider are changed:&lt;br /&gt;
&lt;br /&gt;
* powerChange - changes all &amp;quot;on&amp;quot; lights to &amp;quot;off&amp;quot; and vice versa&lt;br /&gt;
* dimmerChange - changes the values of ALL light channels in proportion to the change to the dimmer&lt;br /&gt;
&lt;br /&gt;
These functions are used to reflect changes to the individual lights in the master power and slider&lt;br /&gt;
&lt;br /&gt;
* setPowerColor - sets the power color to be &amp;quot;off&amp;quot; if all lights are off, or &amp;quot;on&amp;quot; if any on&lt;br /&gt;
* setDimmerLevel - set the slider position to the max of all light channels for all &amp;quot;on&amp;quot; lights or the max of all lights channels for all lights if they are all off.&lt;br /&gt;
&lt;br /&gt;
=== Kiosk CSS ===&lt;br /&gt;
&lt;br /&gt;
== Improvements required ==&lt;br /&gt;
&lt;br /&gt;
# The Heating Thermostat scales well for square displays, but other aspect ratios are not handled well.&lt;br /&gt;
# There is code duplication between this implementation and the FTUI3 App.  This should be eliminated.&lt;br /&gt;
# The hard-coded timeouts should be &amp;lt;META&amp;gt; driven.&lt;br /&gt;
# Control of further devices, using the same principles should be possible.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39042</id>
		<title>Benutzer:Rstooks21/Kiosk UI</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39042"/>
		<updated>2024-01-23T18:15:12Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Further descriptions of the Javascript&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle en}}&lt;br /&gt;
&lt;br /&gt;
==Kiosk UI==&lt;br /&gt;
This is an approach to the dynamic creation of a Kiosk UI that can be used by anyone visiting the home. There will be multiple, small, possibly wall mounted Kiosk screens - one in each physical room. The Kiosk UI controls only the devices that are located in the same [FHEM] Room.&lt;br /&gt;
&lt;br /&gt;
== The Objective ==&lt;br /&gt;
To use the new FHEM Tablet UI (FTUI3) to create a tabbed UI showing just the devices in the same FHEM room.  This tabbed UI allows the most important device controls to be shown by default and at the same time allow other devices to be controlled in as few clicks/screen touches as possible.  As well as this, each Panel should not ever display scroll bars.&lt;br /&gt;
&lt;br /&gt;
The rooms selected for testing had some or all of:&lt;br /&gt;
&lt;br /&gt;
* Lighting (Shelly RGDW LED Controller and Shelly DImmer 2, defined to FHEM as Shelly devices)&lt;br /&gt;
* Audio (Logitech Media Players defined to FHEM as SBPLAYER devices)&lt;br /&gt;
* Heating (EQ3 MAX! WallMountedThermostat and RadiatorThermostat, defined as MAX devices)&lt;br /&gt;
&lt;br /&gt;
With a simple call, the Kiosk displays the default Panel, with dynamic tabs for other device control Panels (a tab will only be shown if the room has that type of device).  The default Panel will be implemented in the following order:&lt;br /&gt;
&lt;br /&gt;
* Lighting first&lt;br /&gt;
* If no Lighting, then Audio&lt;br /&gt;
* If no Lighting or Audio, then Heating&lt;br /&gt;
* If no supported devices are in the room, then the clock.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This web address is intended to be used in a browser in Kiosk mode&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
http[s]://fhem-location:8083/fhem/kiosk/kiosk.html?room=Kitchen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The &amp;quot;?room=Kitchen&amp;quot; is used to filter devices in the FHEM room &amp;quot;Kitchen&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== The Panels ==&lt;br /&gt;
All the panels time out to the primary panel after 60 seconds of non use.&lt;br /&gt;
&lt;br /&gt;
=== The Lighting Panels ===&lt;br /&gt;
&lt;br /&gt;
==== Main Panel ====&lt;br /&gt;
The main Lighting Panel is a simple On/Off switch and dimmer slider.  The single control will control all of the lights in the room equally&lt;br /&gt;
&lt;br /&gt;
* turning them all off or on&lt;br /&gt;
* dimming all lights in proportion&lt;br /&gt;
&lt;br /&gt;
[[Datei:KioskLightingMainPanel.png|alternativtext=An on/off switch and a slider used for dimming the lights|ohne|mini|An FTUI3 Main Lighting control panel ]]&lt;br /&gt;
Note the four dynamic tabs: &lt;br /&gt;
&lt;br /&gt;
* Lighting (currently active) &lt;br /&gt;
* Music&lt;br /&gt;
* Heating&lt;br /&gt;
* Settings - The Lighting tab has further settings that can be adjusted.&lt;br /&gt;
&lt;br /&gt;
==== Settings/Scene Selector Panel ====&lt;br /&gt;
Selecting the &amp;quot;Settings&amp;quot; tab allows the user to select a scene - a combination of light settings that have been previously saved (by the user in the Kiosk UI)&lt;br /&gt;
[[Datei:KioskLightingScenes.png|alternativtext=A grid of four buttons in a panel display|ohne|mini|FTUI Grids and Buttons used to save and recall scenes]]&lt;br /&gt;
The light settings for all lights in the room are recalled by a short press to any of the buttons. &lt;br /&gt;
&lt;br /&gt;
The current light settings for all lights in the room are stored as a scene by pressing and holding the relevant button. This causes the scene to be saved and the button to glow with the average light colour and intensity for all lights that are on.&lt;br /&gt;
&lt;br /&gt;
==== The Details Panel ====&lt;br /&gt;
From the Scene Selector Panel it is possible to control every light in the room individually.   The Details Panels are flexed to display up to three lights each with as many Panels as needed:&lt;br /&gt;
[[Datei:KioskLightingDetail.png|alternativtext=An on/off switch and four sliders in Red, Green, Blue and White|ohne|mini|FTUI3 Switch and Sliders for RGBW control of a single led strip]]&lt;br /&gt;
&lt;br /&gt;
=== The Audio Panel ===&lt;br /&gt;
The Audio Panel uses an IFRAME to embed the Logitech Media Server web interface into the panel.   Only the player in the room can be controlled - the other players cannot be unless they are synchronised.   This is a bit of a cheat as the facilities in FHEM to control the player and display album art are not used.  But why reinvent?&lt;br /&gt;
[[Datei:KioskAudioPlayer.png|alternativtext=A media player display with a second tab for heating control|ohne|mini|The IFRAME containing the web interface at the top (90% height) and the two tabs at the bottom.  One for the Music and a second for the Heating.]]&lt;br /&gt;
&lt;br /&gt;
=== The Heating Panel ===&lt;br /&gt;
The Heating Panel prioritises control of the room&#039;s temperature using a Wall Mounted Thermostat, if one is available.  If not, it is assumed that one of the Radiator Thermostats can be used and that all, if more than one, are linked together.&lt;br /&gt;
&lt;br /&gt;
The Heating Panel just allows the setting of the temperature in the same room.  None of the other room&#039;s temperatures may be set, nor can any &amp;quot;scenes&amp;quot; be set.&lt;br /&gt;
[[Datei:KioskHeatingThermostat.png|alternativtext=A dial with a gradient to show actual temperature and a needle to show desired temperature|ohne|mini|An FTUI3 Compound Thermostat showing Actual and Desired Temperature]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
The FTUI3 code, available from GITHUB should be downloaded and added to FHEM following the instructions here:   https://github.com/knowthelist/ftui&lt;br /&gt;
&lt;br /&gt;
Create an HTTPSRV address for the kioskUI:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
define kioskUI HTTPSRV kiosk /opt/fhem/www/ftui Kiosk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
All files are in this zip&lt;br /&gt;
&lt;br /&gt;
There is a lot of detail about how it works in here to aid understanding.   &lt;br /&gt;
&lt;br /&gt;
It could also help a user understand how the basic framework can be extended to include additional devices as there are examples of a simple IFRAME Panel, a relatively simple Thermostat Panel and a complex set of Lighting Panels.&lt;br /&gt;
&lt;br /&gt;
The FTUI-TAB-VIEWs are more designed to be operated from a single set of FTUI-TAB elements - you click one and it becomes active, displaying the correct TAB-VIEW.  This operating model did not readily lend itself to dynamic tabs - visible on some panels but not others, so new FTUI-TAB elements appear with every FTUI-TAB-VIEW&lt;br /&gt;
&lt;br /&gt;
=== Kiosk HTML ===&lt;br /&gt;
The HTML is divided into three sections&lt;br /&gt;
&lt;br /&gt;
==== Initializing FTUI3 ====&lt;br /&gt;
In the &amp;lt;HEAD&amp;gt; the FTUI3 code and META tags that configure it are defined.   This implementation choses not to use Toast messages.  The viewport is defined as non-scalable as well.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot;&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-height, height=device-width, user-scalable = no, initial-scale = 1, maximum-scale = 1, minimum-scale = 1&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;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://&amp;lt;your-url&amp;gt;:8083/fhem/&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;refresh_interval&amp;quot; content=&amp;quot;60&amp;quot;&amp;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;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Initialize the Kiosk ====&lt;br /&gt;
.This code is also in the &amp;lt;HEAD&amp;gt;.  Should a Kiosk specific theme be required, this can be included here &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;!-- SET UP THE KIOSK --&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;KIOSK&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_class.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/init.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_functions.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;./kiosk/kiosk_styles.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;!-- &amp;lt;link href=&amp;quot;./kiosk/kiosk-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt; --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== BODY - The Outline Panels ====&lt;br /&gt;
The &amp;lt;BODY&amp;gt; contains outlines of the Panels, ready for customisation by the JavaScript.&lt;br /&gt;
&lt;br /&gt;
The body has a kiosk_onLoad() function - this is part of the Kiosk customisation and is called to customise the predefined panels once the outlines have been loaded.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;45&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;body&amp;quot; onload=&amp;quot;kiosk_onLoad()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The first tab defined is a clock.  This should only ever be shown if there are no controllable devices in the room.  It&#039;s included so that this is obvious (for instance if something has gone wrong with the Kiosk definition).  There are also three clickable tabs in this panel.  These allow the configuration to be manually checked when something has gone wrong.&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;46&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Clock&amp;quot; active&amp;gt;    &lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;height:90vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot; color=&amp;quot;primary&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;ee&amp;quot; size=&amp;quot;6&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;DD&amp;quot; size=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-clock format=&amp;quot;hh:mm&amp;quot; size=&amp;quot;9&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-tab-row&amp;quot; style=&amp;quot;height:10vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile  col=&amp;quot;4&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Lighting-Main&amp;quot;  id=&amp;quot;click-lighting&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;lightbulb&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;2&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Player-Main&amp;quot; id=&amp;quot;click-player&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;music&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;3&amp;quot;  width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-tab view=&amp;quot;Heating-Main&amp;quot;  id=&amp;quot;click-heating&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-icon name=&amp;quot;fire&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile col=&amp;quot;1&amp;quot; width=&amp;quot;0.25&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Clock&amp;quot; id=&amp;quot;click-clock&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;clock-o&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt; &lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
  &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The next section defines the main panels for the Lighting, &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;81&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Main&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;25%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&amp;quot;LightingPower&amp;quot;&lt;br /&gt;
                       value=&amp;quot;off&amp;quot; fill=&amp;quot;outline&amp;quot; shape=&amp;quot;circle&amp;quot; states=&amp;quot;on,off&amp;quot; color=&amp;quot;gray&amp;quot;&lt;br /&gt;
                       @value-change=&#039;powerChange($event)&#039;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;power-off&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;75%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-slider  id=&amp;quot;LightingDimmer&amp;quot;&lt;br /&gt;
                        value=&amp;quot;1&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot;&lt;br /&gt;
                        color=&amp;quot;white&amp;quot;&lt;br /&gt;
                        @value-change=&amp;quot;dimmerChange($event)&amp;quot;&lt;br /&gt;
                        &amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-slider&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Scenes&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;cog&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Settings&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Heating &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;108&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Heating-Main&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row id=&amp;quot;Heating-Tabs&amp;quot;class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;and Audio&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;112&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Player-Main&amp;quot; &amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;iframe  id=&amp;quot;Player-kiosk&amp;quot; src=&amp;quot;&amp;quot; style=&amp;quot;height:90vh;width:100vw;overflow:hidden;padding:0px;border:none;margin:0px&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Player-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Scenes Tab and Success Popup (line 164) are also defined.  At line 150, the tab selector for the first Lighting Detail Panel is created:&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;119&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Scenes&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;gap:5em;&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-grid margin=&amp;quot;5&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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene1_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 1&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene1&#039; style=&#039;box-shadow:0px 0px 10px 5px #B26464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot; @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;Press to Select&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;1&amp;quot; col=&amp;quot;2&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene2_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 2&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene2&#039; style=&#039;box-shadow:0px 0px 10px 5px #FF6464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;1&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene3_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 3&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene3&#039; style=&#039;box-shadow:0px 0px 10px 5px #64C9C9;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene4_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 4&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene4&#039; style=&#039;box-shadow:0px 0px 10px 5px #1F6F3F;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;3&amp;quot; col=&amp;quot;1&amp;quot; width=&#039;2&#039; height=&#039;0.1&#039;&amp;gt;&lt;br /&gt;
          Long hold to set&lt;br /&gt;
        &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Scenes-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Main&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;reply&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Go Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Settings-Detail0-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Setting-Detail0&amp;quot;  timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;tasks&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Details&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;ftui-label id=&amp;quot;scene-success-click&amp;quot; @click=&#039;scene_success.open()&#039; style=&amp;quot;display:none&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_fail&amp;quot; timeout=&amp;quot;15&amp;quot; color=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Failed To Save&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Failed to Save Scene&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_success&amp;quot; timeout=&amp;quot;5&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Saved&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Scene Saved&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kiosk JavaScript ===&lt;br /&gt;
The JavaScript is split into three files:&lt;br /&gt;
&lt;br /&gt;
* light_class.js is a class that is used to define each light controlled by the UI.  It contains functions used to aid the communication with FHEM and FTUI3 - mutation observers and simple functions for hex conversion&lt;br /&gt;
* init.js is all the code used to set up the UI and initialise the display Panels&lt;br /&gt;
* light_functions.js is the code use to manage the lighting Panels as these are far more complex and don&#039;t rely on native FTUI3&lt;br /&gt;
&lt;br /&gt;
==== light_class.js ====&lt;br /&gt;
&lt;br /&gt;
===== Properties =====&lt;br /&gt;
The light class has these properties:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
this.name = &#039;&#039;;&lt;br /&gt;
this.mode = &#039;&#039;;&lt;br /&gt;
this.red = 0;&lt;br /&gt;
this.green = 0;&lt;br /&gt;
this.blue = 0;&lt;br /&gt;
this.white = 0;&lt;br /&gt;
this.state = &#039;off&#039;;&lt;br /&gt;
this.scene1 = &#039;off&#039;;&lt;br /&gt;
this.scene2 = &#039;off&#039;;&lt;br /&gt;
this.scene3 = &#039;off&#039;;&lt;br /&gt;
this.scene4 = &#039;off&#039;;&lt;br /&gt;
this.state = &#039;off&#039;;&lt;br /&gt;
this.observerConfig = {&lt;br /&gt;
      attributes: true // this is to watch for attribute changes.&lt;br /&gt;
      ,attributeOldValue: true&lt;br /&gt;
  }&lt;br /&gt;
//set by front end only&lt;br /&gt;
this.was = &#039;off&#039;;&lt;br /&gt;
this.sliderObserver = &#039;&#039;;&lt;br /&gt;
this.stateObserver = &#039;&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;These are set from FHEM during the initialisation of each light by the doInit() function in init.js&lt;br /&gt;
&lt;br /&gt;
The last three properties are set by the front end only and are there to manage continuity of state:&lt;br /&gt;
&lt;br /&gt;
* this.was is set when the master power button is set to &amp;quot;off&amp;quot;.  It records whether the individual light was on or off at the time.  When the master power button is subsequently set to &amp;quot;on&amp;quot;, this.was is used to only turn on lights that were previously on.  Unless all were off, in which case all are turned on.&lt;br /&gt;
* this.sliderObserver and stateObserver are used to pick up changes made in FHEM, or via a physical switch then communicated by the Shelly device to FHEM, to the detailed light values (RGBW and On/Off).  The FTUI3 events @value-change do not get triggered when this happens (which is a good thing).   When the user is interacting with the KioskUI, FHEM can sometimes  send the results too quickly, leading to circular updates.  These observers are disconnected temporarily to prevent this.&lt;br /&gt;
&lt;br /&gt;
===== Methods =====&lt;br /&gt;
These methods are used to help with managing the light&lt;br /&gt;
&lt;br /&gt;
* set dim - used by the master dimmer slider to recalculate the new values for RGBW after the slider has changed value.  The dimmer currently sets minimum and maximum values than can be dimmed based on the color composition of the target light.  This is to help prevent a change in the colour composition that could be caused by a min-&amp;gt;max-&amp;gt;min dim sequence.   Sometimes, change is inevitable though.&lt;br /&gt;
* rgbw2hex, rgb2hex and w2hex are used to convert the internal RGBW channel values to a hex value that can be transmitted to FHEM&lt;br /&gt;
** ideally the rgb color picker ftui component would have been used, but integrating the white channel would still be required for an RGBW light - for now the display looks more integrated across all types of light as separate channels&lt;br /&gt;
* template.  This returns the light template used to initialise the light detail.  The FTUI3 components returned are dependent on whether the light is RGBW, RGB or just a dimmer.&lt;br /&gt;
&lt;br /&gt;
==== init.js ====&lt;br /&gt;
init.js has three distinct phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1 - actions taken as the code is loaded&lt;br /&gt;
* Phase 2 - actions taken once the &amp;lt;BODY&amp;gt; has loaded&lt;br /&gt;
* Phase 3 - actions taken once the FTUI3 app has finished initializing&lt;br /&gt;
&lt;br /&gt;
===== Phase 1 - Code Load =====&lt;br /&gt;
&lt;br /&gt;
* Two global objects are defined&lt;br /&gt;
** Lights is a collection of all the light definitions&lt;br /&gt;
** Kiosk holds global parameters and settings&lt;br /&gt;
&lt;br /&gt;
* The room is retrieved from the URL Query String&lt;br /&gt;
* An event listener is defined to wait for the FTUI3 app to complete initialisation&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
// Gloabl Objects&lt;br /&gt;
var lights = {}&lt;br /&gt;
var Kiosk = {}&lt;br /&gt;
&lt;br /&gt;
// Funtion to get the Room&lt;br /&gt;
var getUrlParameter = function getUrlParameter(sParam) {&lt;br /&gt;
  var sPageURL = window.location.search.substring(1),&lt;br /&gt;
      sURLVariables = sPageURL.split(&#039;&amp;amp;&#039;),&lt;br /&gt;
      sParameterName,&lt;br /&gt;
      i;&lt;br /&gt;
&lt;br /&gt;
  for (i = 0; i &amp;lt; sURLVariables.length; i++) {&lt;br /&gt;
      sParameterName = sURLVariables[i].split(&#039;=&#039;);&lt;br /&gt;
&lt;br /&gt;
      if (sParameterName[0] === sParam) {&lt;br /&gt;
          return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  return false;&lt;br /&gt;
};  &lt;br /&gt;
&lt;br /&gt;
// Get the CSRF token - this code is copied from the FTUI3 App&lt;br /&gt;
fetch(&amp;quot;http://&amp;lt;your-fhem-ip&amp;gt;:8083/fhem?XHR=1&amp;quot;).then(response =&amp;gt; {&lt;br /&gt;
    Kiosk.ftwcsrf = response.headers.get(&#039;X-FHEM-csrfToken&#039;);&lt;br /&gt;
    console.log(&#039;Got csrf from FHEM:&#039; + Kiosk.ftwcsrf);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
//Set the CSRF token in the Kiosk object&lt;br /&gt;
Kiosk.thisRoom=getUrlParameter(&#039;room&#039;)&lt;br /&gt;
&lt;br /&gt;
//Watch for the FTUI3 end of initialisation event&lt;br /&gt;
document.addEventListener(&#039;ftuiPageInitialized&#039;,() =&amp;gt; onFTUIReady())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Phase 2 - After the Body is loaded =====&lt;br /&gt;
Three functions&lt;br /&gt;
&lt;br /&gt;
* kiosk_onLoad() is called by the browser when the body has finished loading&lt;br /&gt;
* This waits (up to 50 seconds) until the csrf token has been set in the Kiosk object using function isCSRFSet() &lt;br /&gt;
* Then it calls the doInit() function&lt;br /&gt;
&lt;br /&gt;
The doInit() function fetches relevant details from FHEM (line 56) for the types of devices to be monitored that are in the room specified by Kiosk.thisRoom.  This ensures only one call to FHEM for the information&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;34&amp;quot;&amp;gt;&lt;br /&gt;
function isCSRFSet(timeout) {&lt;br /&gt;
    var start = Date.now();&lt;br /&gt;
    return new Promise(checkCSRF); // set the promise object within the ensureFooIsSet object&lt;br /&gt;
 &lt;br /&gt;
    function checkCSRF(resolve, reject) {&lt;br /&gt;
        if (Kiosk &amp;amp;&amp;amp; Kiosk.ftwcsrf)&lt;br /&gt;
            resolve(Kiosk.ftwcsrf);&lt;br /&gt;
        else if (timeout &amp;amp;&amp;amp; (Date.now() - start) &amp;gt;= timeout)&lt;br /&gt;
            reject(new Error(&amp;quot;timeout&amp;quot;));&lt;br /&gt;
        else&lt;br /&gt;
            setTimeout(checkCSRF.bind(this, resolve, reject), 30);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
function kiosk_onLoad(){&lt;br /&gt;
    isCSRFSet(50000).then(function(){&lt;br /&gt;
        doInit()&lt;br /&gt;
    }&lt;br /&gt;
    )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function doInit(){&lt;br /&gt;
    fetch(`http://192.168.1.2:8083/fhem?XHR=1&amp;amp;cmd=jsonlist2+room=${Kiosk.thisRoom}+TYPE+type+SHELLY+NAME+PLAYERNAME+model+scene1+scene2+scene3+scene4+state+L-white+L-red+L-green+L-blue&amp;amp;fwcsrf=${Kiosk.ftwcsrf}`).then(res =&amp;gt; {&lt;br /&gt;
        return res.json()&lt;br /&gt;
      }).then((response) =&amp;gt; {&lt;br /&gt;
        o=JSON.parse(JSON.stringify(response))&lt;br /&gt;
        console.log(&#039;res: &#039; + JSON.stringify(response))&lt;br /&gt;
        var hasPlayer=false&lt;br /&gt;
        var hasLighting=false&lt;br /&gt;
        var hasHeating=false&lt;br /&gt;
        var hasThermostat=false&lt;br /&gt;
        var lightType=&amp;quot;&amp;quot;&lt;br /&gt;
        for (i=0;i&amp;lt;o.Results.length;i++){&lt;br /&gt;
            &lt;br /&gt;
            [... init code]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;For each device returned, the doInit() code sets up the Panels required to manage them through the Kiosk UI.  The code here is not in the sequence in init.js, but grouped together to show how the process fits together&lt;br /&gt;
&lt;br /&gt;
====== Audio ======&lt;br /&gt;
Whilst looping over the Results, check whether there is a player&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;155&amp;quot;&amp;gt;&lt;br /&gt;
        // any players&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;SB_PLAYER&#039;){&lt;br /&gt;
          hasPlayer=true&lt;br /&gt;
          var playerName=o.Results[i].Internals.PLAYERNAME&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Once all results are in, if a player was found, customise the Panel&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;209&amp;quot;&amp;gt;&lt;br /&gt;
      if (hasPlayer===true){&lt;br /&gt;
        var tabPrefix=&#039;Player&#039;&lt;br /&gt;
        //set up the tab&lt;br /&gt;
        document.getElementById(&amp;quot;Player-kiosk&amp;quot;).src=&amp;quot;http://&amp;lt;your web address&amp;gt;:9000/material/?single&amp;amp;page=now-playing&amp;amp;player=&amp;quot;+playerName&lt;br /&gt;
        &lt;br /&gt;
        ...&lt;br /&gt;
        // add the active (non-clickable) tab to the Player tab row&lt;br /&gt;
        createPlayerTab(tabPrefix,&#039;kiosk-tab-player-active&#039;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Add Tabs to the other panels so that those panels can switch to the payer Panel.  For instance &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;179&amp;quot;&amp;gt;&lt;br /&gt;
if (hasLighting===true){&lt;br /&gt;
    ...&lt;br /&gt;
    &lt;br /&gt;
    if (hasPlayer===true){&lt;br /&gt;
          createPlayerTab(tabPrefix,&#039;kiosk-tab-player-inactive&#039;,timeout=60)&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Helper function to create the player tab&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;305&amp;quot;&amp;gt;&lt;br /&gt;
function createPlayerTab(view,state,timeout=&amp;quot;0&amp;quot;){&lt;br /&gt;
    divHtml = &amp;quot;&amp;lt;ftui-column id=&#039;&amp;quot;+view+&amp;quot;-Player-Tab&#039; class=&#039;&amp;quot;+state+&amp;quot;&#039;&amp;gt;&amp;lt;ftui-tab class=&#039;kiosk-button&#039; timeout=&#039;&amp;quot;+timeout+&amp;quot;&#039; view=&#039;Player-Main&#039;&amp;gt;&amp;lt;ftui-icon name=&#039;music&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&amp;lt;/ftui-tab&amp;gt;&amp;lt;/ftui-column&amp;gt;&amp;quot;&lt;br /&gt;
    document.getElementById(view+&#039;-Tabs&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Heating ======&lt;br /&gt;
This works in roughly the same way, except with slightly different code.&lt;br /&gt;
&lt;br /&gt;
This selects a wall mounted thermostat as the device to control, if present.  Otherwise it uses the last found radiator thermostat&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;161&amp;quot;&amp;gt;&lt;br /&gt;
        // any heating&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;MAX&#039;){&lt;br /&gt;
            hasHeating=true&lt;br /&gt;
            if (hasThermostat===false){&lt;br /&gt;
              var heatingName=o.Results[i].Internals.NAME&lt;br /&gt;
            }&lt;br /&gt;
            if (o.Results[i].Internals.type==&#039;WallMountedThermostat&#039;){&lt;br /&gt;
              hasThermostat=true&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Heating Panel is defined thus:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;230&amp;quot;&amp;gt;&lt;br /&gt;
     if (hasHeating===true){&lt;br /&gt;
        addHeatingControl(heatingName)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Heating code creates the full panel, rather than customising an existing one as the Audio one does.  The Heating panel uses two, differently scaled FTUI-KNOB elements placed one on top of the other.   &lt;br /&gt;
&lt;br /&gt;
* The bottom one, scaled to use all the width and height, is the actual temperature display and is read only&lt;br /&gt;
* The top one, scaled to 70% of the width and height, has a transparent background, and displays the desiredTemperature.  This is clickable to change the temperature.  Ideally new properties of FTUI-KNOB could be included so the actual/desired temperatures can be displayed in the same way.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;313&amp;quot;&amp;gt;&lt;br /&gt;
function addHeatingControl(heatingName){&lt;br /&gt;
  divHtml=&#039;&amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&#039;+&lt;br /&gt;
    &#039;&amp;lt;ftui-knob height=&amp;quot;100vh&amp;quot; width=&amp;quot;100vw&amp;quot; stroke-width=&amp;quot;15&amp;quot; ticks=&amp;quot;25&amp;quot; style=&amp;quot;z-index:1;position:absolute;&amp;quot; has-arc readonly has-scale-text has-scale color=&amp;quot;cold-hot&amp;quot; [value]=&amp;quot;&#039;+heatingName+&#039;:temperature&amp;quot; max=&amp;quot;30&amp;quot; min=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-knob&amp;gt;&#039; +&lt;br /&gt;
    &#039;&amp;lt;ftui-knob height=&amp;quot;70vh&amp;quot; width=&amp;quot;70vw&amp;quot; stroke-width=&amp;quot;5&amp;quot; style=&amp;quot;z-index: 2;background:transparent;position:absolute;&amp;quot; has-needle has-value-text unit=&amp;quot;°C&amp;quot; unit-offset-y=&amp;quot;40&amp;quot; value-size=&amp;quot;5em&amp;quot; unit-size=&amp;quot;2em&amp;quot; value-decimals=&amp;quot;1&amp;quot; step=&amp;quot;0.5&amp;quot; [(value)]=&amp;quot;&#039;+heatingName+&#039;:desiredTemperature&amp;quot; max=&amp;quot;30&amp;quot; min=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-knob&amp;gt;&#039;+&lt;br /&gt;
    &#039;&amp;lt;/ftui-row&amp;gt;&#039;&lt;br /&gt;
    document.getElementById(&#039;Heating-Main&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The insertion of tabs uses the same tests and similar functions&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;301&amp;quot;&amp;gt;&lt;br /&gt;
function createHeatingTab(view,state,timeout=&amp;quot;0&amp;quot;){&lt;br /&gt;
    divHtml = &amp;quot;&amp;lt;ftui-column id=&#039;&amp;quot;+view+&amp;quot;-Heating-Tab&#039; class=&#039;&amp;quot;+state+&amp;quot;&#039;&amp;gt;&amp;lt;ftui-tab class=&#039;kiosk-button&#039; timeout=&#039;&amp;quot;+timeout+&amp;quot;&#039; view=&#039;Heating-Main&#039;&amp;gt;&amp;lt;ftui-icon name=&#039;fire&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&amp;lt;ftui-label&amp;gt;Heating&amp;lt;/ftui-label&amp;gt;&amp;lt;/ftui-tab&amp;gt;&amp;lt;/ftui-column&amp;gt;&amp;quot;&lt;br /&gt;
    document.getElementById(view+&#039;-Tabs&#039;).insertAdjacentHTML(&#039;afterbegin&#039;,divHtml)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Lighting ======&lt;br /&gt;
This is the most complex of the Panels to set up.   &lt;br /&gt;
&lt;br /&gt;
The results from the fetch are added to a new light() object and the light object added to the list of lights for the room.  Note this includes: RGBW levels, the state (or ort off) and the settings of each scene for this light.&lt;br /&gt;
&lt;br /&gt;
Then the mutation observers for the light details are created and references saved to the light() object.&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;
// any lights&lt;br /&gt;
        if (o.Results[i].Internals.TYPE==&#039;Shelly&#039;){&lt;br /&gt;
            hasLighting=true&lt;br /&gt;
            // most advanced defines the model&lt;br /&gt;
            if (lightType!=&#039;shellyrgbw&#039;){&lt;br /&gt;
                lightType=o.Results[i].Attributes.model&lt;br /&gt;
            }&lt;br /&gt;
            var thisLight= new light()&lt;br /&gt;
            thisLight.model=o.Results[i].Attributes.model&lt;br /&gt;
            thisLight.name=o.Results[i].Internals.NAME&lt;br /&gt;
            thisLight.R=(o.Results[i].Readings[&#039;L-red&#039;])?o.Results[i].Readings[&#039;L-red&#039;].Value:0&lt;br /&gt;
            thisLight.B=(o.Results[i].Readings[&#039;L-blue&#039;])?o.Results[i].Readings[&#039;L-blue&#039;].Value:0&lt;br /&gt;
            thisLight.G=(o.Results[i].Readings[&#039;L-green&#039;])?o.Results[i].Readings[&#039;L-green&#039;].Value:0&lt;br /&gt;
            thisLight.W=(o.Results[i].Readings[&#039;L-white&#039;])?o.Results[i].Readings[&#039;L-white&#039;].Value:0&lt;br /&gt;
            thisLight.scene1=(o.Results[i].Readings.scene1)?o.Results[i].Readings.scene1.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene2=(o.Results[i].Readings.scene2)?o.Results[i].Readings.scene2.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene3=(o.Results[i].Readings.scene3)?o.Results[i].Readings.scene3.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.scene4=(o.Results[i].Readings.scene4)?o.Results[i].Readings.scene4.Value:&#039;off&#039;&lt;br /&gt;
            thisLight.state=(o.Results[i].Readings.state)?o.Results[i].Readings.state.Value:&#039;off&#039;&lt;br /&gt;
            lights[thisLight.name] = thisLight&lt;br /&gt;
           &lt;br /&gt;
            let mySliderObserver = new MutationObserver( function(mutations)  {&lt;br /&gt;
                  mutations.forEach(function(mutation){&lt;br /&gt;
                    if (mutation.attributeName==&#039;value&#039;){&lt;br /&gt;
                      const [key, attribute] = mutation.target.id.split(&#039;:&#039;)&lt;br /&gt;
                      lights[key][attribute] = mutation.target.value&lt;br /&gt;
                      updateFhem(&amp;quot;set &amp;quot;+key+&amp;quot; rgbw &amp;quot;+lights[key].hex );&lt;br /&gt;
                      setDimmerLevel()&lt;br /&gt;
                    }&lt;br /&gt;
                  }&lt;br /&gt;
                )&lt;br /&gt;
              }&lt;br /&gt;
            )&lt;br /&gt;
            lights[thisLight.name].sliderObserver = mySliderObserver&lt;br /&gt;
&lt;br /&gt;
            let myStateObserver = new MutationObserver(function(mutations)  {&lt;br /&gt;
              mutations.forEach(function(mutation){&lt;br /&gt;
                if (mutation.attributeName==&#039;value&#039;){&lt;br /&gt;
                  //save approprite value&lt;br /&gt;
                  var key = mutation.target.id.split(&#039;:&#039;)[0]&lt;br /&gt;
                  lights[key].state = mutation.target.value&lt;br /&gt;
                  var anyOn=false&lt;br /&gt;
                  for (var key in lights){&lt;br /&gt;
                    if (lights[key].state==&#039;on&#039;){&lt;br /&gt;
                      anyOn=true&lt;br /&gt;
                      break;&lt;br /&gt;
                    }&lt;br /&gt;
                  }&lt;br /&gt;
                setPowerColor(anyOn)&lt;br /&gt;
                setDimmerLevel()&lt;br /&gt;
                }&lt;br /&gt;
              })&lt;br /&gt;
             }&lt;br /&gt;
            )&lt;br /&gt;
            lights[thisLight.name].stateObserver = myStateObserver&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Once the results have all been processed&lt;br /&gt;
&lt;br /&gt;
* the scene colours are set on the Lighting Scenes Panel&lt;br /&gt;
* the individual controls for each light are added to a Lighting Details Panel (up to three lights per panel):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;174&amp;quot;&amp;gt;&lt;br /&gt;
if (hasLighting===true){&lt;br /&gt;
        var tabPrefix=&#039;Lighting&#039;&lt;br /&gt;
        //options first&lt;br /&gt;
        setSceneColors()&lt;br /&gt;
        //create the details panes&lt;br /&gt;
        for (let i=0; i &amp;lt; Object.keys(lights).length;i++){&lt;br /&gt;
          if (i%3 == 0){&lt;br /&gt;
            //set up a new detail tab every three tlights&lt;br /&gt;
            var counter=Math.floor(i/3)&lt;br /&gt;
            createLightingDetailsTab(counter)&lt;br /&gt;
          }&lt;br /&gt;
          createLightingDetail(counter,Object.keys(lights)[i])&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The individual controls are set by the light() object .template() method - this ensures the right controls are placed on the panel for the type of light&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;361&amp;quot;&amp;gt;&lt;br /&gt;
function createLightingDetail(counter,key){&lt;br /&gt;
  document.getElementById(`Lighting-Setting_Detail${counter}-Body`).insertAdjacentHTML(&#039;beforeend&#039;,lights[key].template())&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Details Tab is created and a forward reference created on the previous tab, if required:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;320&amp;quot;&amp;gt;&lt;br /&gt;
function createLightingDetailsTab(counter){&lt;br /&gt;
    switch (counter){&lt;br /&gt;
    case 0:&lt;br /&gt;
        var goBack=&amp;quot;Lighting-Scenes&amp;quot;&lt;br /&gt;
        var goForwardId = false&lt;br /&gt;
        break;&lt;br /&gt;
    default: &lt;br /&gt;
        var lastPanel = counter - 1;&lt;br /&gt;
        var goBack=`Lighting-Setting-Detail${lastPanel}`&lt;br /&gt;
        var goForward=`Lighting-Setting-Detail${counter}`&lt;br /&gt;
        var goForwardId = `Lighting-Setting-${lastPanel}-Tabs`&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.body.insertAdjacentHTML(&#039;beforeend&#039;,&lt;br /&gt;
    `&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Setting-Detail${counter}&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Setting_Detail${counter}-Body&amp;quot; class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;flex-direction:column&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Settings-Detail${counter}-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-row id=&amp;quot;Lighting-Setting-${counter}-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Setting-${counter}-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;${goBack}&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-icon name=&#039;long-arrow-left&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-label&amp;gt;Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;`&lt;br /&gt;
    )&lt;br /&gt;
    if (goForward){&lt;br /&gt;
    document.getElementById(goForwardId).insertAdjacentHTML(&#039;beforeend&#039;,&lt;br /&gt;
    `&amp;lt;ftui-column id=&amp;quot;Lighting-Setting-${lastPanel}-Forward-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;${goForward}&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-icon name=&#039;long-arrow-right&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
                &amp;lt;ftui-label&amp;gt;Next page&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-column&amp;gt;`&lt;br /&gt;
    )&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;After this, tab references similar to Audio and Heating are added. &lt;br /&gt;
&lt;br /&gt;
===== Phase 3 - After FTUI is initialised =====&lt;br /&gt;
Some actions can only be performed once FTUI is ready to process them.  One of these appears to be changing the header color on an FTUI-GRID, so it has to wait until after the FTUI event &#039;ftuiPageInitialized&#039; has been dispatched.&lt;br /&gt;
&lt;br /&gt;
If the current light setting matches the same scene setting for all lights in the room, the scene header is set to &amp;quot;success&amp;quot; to indicate it is currently selected.  If not, no action is taken&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
function onFTUIReady(){&lt;br /&gt;
    console.log(&amp;quot;My FTUI Ready&amp;quot;)&lt;br /&gt;
    //for each scene in each light, confirm that the current light setting matches the same scene (if any)&lt;br /&gt;
    var matchScene=false&lt;br /&gt;
    for (key in lights){&lt;br /&gt;
        if (matchScene==-1){&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        var scenes=[lights[key].scene1,lights[key].scene2,lights[key].scene3,lights[key].scene4]&lt;br /&gt;
        if (!matchScene){&lt;br /&gt;
            matchScene = scenes.indexOf(lights[key].hex)&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
            matchScene = scenes[matchScene].indexOf(lights[key].hex)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    scene_to_select = [&#039;scene1&#039;,&#039;scene2&#039;,&#039;scene3&#039;,&#039;scene4&#039;][matchScene]&lt;br /&gt;
    switch (scene_to_select){&lt;br /&gt;
        case &#039;&#039;: &lt;br /&gt;
            break;&lt;br /&gt;
        case undefined: &lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            document.getElementById(scene_to_select+&#039;_header&#039;).color=&#039;success&#039;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== light_functions_js ====&lt;br /&gt;
The light functions work together to make sure the UI reflects FHEM consistently and vice versa.  The principle of how it works is described in this diagram for state changes:&lt;br /&gt;
&lt;br /&gt;
If the master power button is clicked in the UI, the change is propagated to the detail tab for all lights.   The change is also sent directly to FHEM and updated in the light() objec&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These light functions are used to set the individual light settings when the master power and dimmer slider are changed:&lt;br /&gt;
&lt;br /&gt;
* powerChange - changes all &amp;quot;on&amp;quot; lights to &amp;quot;off&amp;quot; and vice versa&lt;br /&gt;
* dimmerChange - changes the values of ALL light channels in proportion to the change to the dimmer&lt;br /&gt;
&lt;br /&gt;
These functions are used to reflect changes to the individual lights in the master power and slider&lt;br /&gt;
&lt;br /&gt;
* setPowerColor - sets the power color to be &amp;quot;off&amp;quot; if all lights are off, or &amp;quot;on&amp;quot; if any on&lt;br /&gt;
* setDimmerLevel - set the slider position to the max of all light channels for all &amp;quot;on&amp;quot; lights or the max of all lights channels for all lights if they are all off.&lt;br /&gt;
&lt;br /&gt;
=== Kiosk CSS ===&lt;br /&gt;
&lt;br /&gt;
== Improvements required ==&lt;br /&gt;
&lt;br /&gt;
# The Heating Thermostat scales well for square displays, but other aspect ratios are not handled well.&lt;br /&gt;
# There is code duplication between this implementation and the FTUI3 App.  This should be eliminated.&lt;br /&gt;
# The hard-coded timeouts should be &amp;lt;META&amp;gt; driven.&lt;br /&gt;
# Control of further devices, using the same principles should be possible.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:State_changes.png&amp;diff=39041</id>
		<title>Datei:State changes.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:State_changes.png&amp;diff=39041"/>
		<updated>2024-01-23T18:05:57Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kiosk UI Sate changes data flow&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39014</id>
		<title>Benutzer:Rstooks21/Kiosk UI</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39014"/>
		<updated>2024-01-22T23:36:20Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Typo in html&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle en}}&lt;br /&gt;
&lt;br /&gt;
==Kiosk UI==&lt;br /&gt;
This is an approach to the dynamic creation of a Kiosk UI that can be used by anyone visiting the home. There will be multiple, small, possibly wall mounted Kiosk screens - one in each physical room. The Kiosk UI controls only the devices that are located in the same [FHEM] Room.&lt;br /&gt;
&lt;br /&gt;
== The Objective ==&lt;br /&gt;
To use the new FHEM Tablet UI (FTUI3) to create a tabbed design that allows the most important device controls to be shown by default and at the same time allow other devices to be controlled in as few clicks/screen touches as possible.  The rooms selected for testing had some or all of:&lt;br /&gt;
&lt;br /&gt;
* Lighting (Shelly RGDW LED Controller and Shelly DImmer 2, defined to FHEM as Shelly devices)&lt;br /&gt;
* Audio (Logitech Media Players defined to FHEM as SBPLAYER devices)&lt;br /&gt;
* Heating (EQ3 MAX! WallMountedThermostat and RadiatorThermostat, defined as MAX devices)&lt;br /&gt;
&lt;br /&gt;
With a simple call, the Kiosk screen displays the default screen, with tabs for other device control screens.  The default screens are implemented in the following order:&lt;br /&gt;
&lt;br /&gt;
* Lighting first&lt;br /&gt;
* If no Lighting, then Audio&lt;br /&gt;
* If no Lighting or Audio, then Heating&lt;br /&gt;
&lt;br /&gt;
This web address is intended to be used in a browser in Kiosk mode&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
http[s]://fhem-location:8083/fhem/kiosk/kiosk.html?room=Kitchen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The Panels ==&lt;br /&gt;
All the panels time out to the primary panel after 60 seconds of non use.&lt;br /&gt;
&lt;br /&gt;
=== The Lighting Panels ===&lt;br /&gt;
&lt;br /&gt;
==== Main Panel ====&lt;br /&gt;
The main Lighting Panel is a simple On/Off switch and dimmer slider.  The single control will control all of the lights in the room equally&lt;br /&gt;
&lt;br /&gt;
* turning them all off or on&lt;br /&gt;
* dimming all lights in proportion&lt;br /&gt;
&lt;br /&gt;
[[Datei:KioskLightingMainPanel.png|alternativtext=An on/off switch and a slider used for dimming the lights|ohne|mini|An FTUI3 Main Lighting control panel ]]&lt;br /&gt;
Note the four tabs.   The Lighting tab has further settings that can be adjusted.&lt;br /&gt;
&lt;br /&gt;
==== Settings/Scene Selector Panel ====&lt;br /&gt;
Selecting the &amp;quot;Settings&amp;quot; tab allows the user to select a scene - a combination of light settings that have been previously saved (by the user in the Kiosk UI)&lt;br /&gt;
[[Datei:KioskLightingScenes.png|alternativtext=A grid of four buttons in a panel display|ohne|mini|FTUI Grids and Buttons used to save and recall scenes]]&lt;br /&gt;
The light settings for all lights in the room are recalled by a short press to any of the buttons. &lt;br /&gt;
&lt;br /&gt;
The current light settings for all lights in the room are stored as a scene by pressing and holding the relevant button. This causes the scene to be saved and the button to glow with the average light colour and intensity for all lights that are on.&lt;br /&gt;
&lt;br /&gt;
==== The Details Panel ====&lt;br /&gt;
From the Scene Selector Panel it is possible to control every light in the room individually.   The Details Panels are flexed to display up to three lights each with as many Panels as needed:&lt;br /&gt;
[[Datei:KioskLightingDetail.png|alternativtext=An on/off switch and four sliders in Red, Green, Blue and White|ohne|mini|FTUI3 Switch and Sliders for RGBW control of a single led strip]]&lt;br /&gt;
&lt;br /&gt;
=== The Audio Panel ===&lt;br /&gt;
The Audio Panel uses an IFRAME to embed the Logitech Media Server web interface into the panel.   Only the player in the room can be controlled - the other players cannot be unless they are synchronised.   This is a bit of a cheat as the facilities in FHEM to control the player and display album art are not used.  But why reinvent?&lt;br /&gt;
[[Datei:KioskAudioPlayer.png|alternativtext=A media player display with a second tab for heating control|ohne|mini|The IFRAME containing the web interface at the top (90% height) and the two tabs at the bottom.  One for the Music and a second for the Heating.]]&lt;br /&gt;
&lt;br /&gt;
=== The Heating Panel ===&lt;br /&gt;
The Heating Panel prioritises control of the room&#039;s temperature using a Wall Mounted Thermostat, if one is available.  If not, it is assumed that one of the Radiator Thermostats can be used and that all, if more than one, are linked together.&lt;br /&gt;
&lt;br /&gt;
The heating screen just allows the setting of the temperature in the same room.  None of the other room&#039;s temperatures may be set, nor can any &amp;quot;scenes&amp;quot; be set.&lt;br /&gt;
[[Datei:KioskHeatingThermostat.png|alternativtext=A dial with a gradient to show actual temperature and a needle to show desired temperature|ohne|mini|An FTUI3 Compound Thermostat showing Actual and Desired Temperature]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
The FTUI3 code, available from GITHUB should be downloaded and added to FHEM following the instructions here:   https://github.com/knowthelist/ftui&lt;br /&gt;
&lt;br /&gt;
Create an HTTPSRV address for the kioskUI:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
define kioskUI HTTPSRV kiosk /opt/fhem/www/ftui Kiosk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
&lt;br /&gt;
=== Kiosk HTML ===&lt;br /&gt;
The HTML is divided into three sections&lt;br /&gt;
&lt;br /&gt;
==== The First Section ====&lt;br /&gt;
... in the &amp;lt;HEAD&amp;gt; reads in the FTUI3 code and META tags that configure it.   This implementation choses not to use Toast messages&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot;&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-height, height=device-width, user-scalable = no, initial-scale = 1, maximum-scale = 1, minimum-scale = 1&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;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://&amp;lt;your-url&amp;gt;:8083/fhem/&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;refresh_interval&amp;quot; content=&amp;quot;60&amp;quot;&amp;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;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The Second Section ====&lt;br /&gt;
... also in the &amp;lt;HEAD&amp;gt; reads in the Kiosk support files &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;!-- SET UP THE KIOSK --&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;KIOSK&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_class.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/init.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_functions.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;./kiosk/kiosk_styles.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;!-- &amp;lt;link href=&amp;quot;./kiosk/kiosk-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt; --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The Third Section ====&lt;br /&gt;
... defines the body.&lt;br /&gt;
&lt;br /&gt;
This contains outlines of the Panels, ready for customisation by the JavaScript.&lt;br /&gt;
&lt;br /&gt;
The body has a kiosk_onLoad() function - this is part of the Kiosk customisation and is called to customise the predefined panels once the outlines have been loaded&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;45&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;body&amp;quot; onload=&amp;quot;kiosk_onLoad()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The first tab defined is a clock.  This should only ever be shown if there are no controllable devices in the room.  It&#039;s included so that this is obvious (for instance if something has gone wrong with the Kiosk definition).  There are also three clickable tabs in this panel.  These serve two purposes:&lt;br /&gt;
&lt;br /&gt;
# To allow the configuration to be manually checked when something has gone wrong.&lt;br /&gt;
# To allow the kiosk_onLoad() function to select the first panel for display using the FTUI App by triggering the onclick event.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;46&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Clock&amp;quot;&amp;gt;    &lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;height:100vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot; color=&amp;quot;primary&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;ee&amp;quot; size=&amp;quot;6&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;DD&amp;quot; size=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-clock format=&amp;quot;hh:mm&amp;quot; size=&amp;quot;9&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-tab-row&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;ftui-grid-tile col=&amp;quot;1&amp;quot; width=&amp;quot;0.25&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Clock&amp;quot; id=&amp;quot;click-clock&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;clock&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt; &lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;2&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Player-Main&amp;quot; id=&amp;quot;click-player&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;music&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile col=&amp;quot;3&amp;quot;  width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Heating-Main&amp;quot;  id=&amp;quot;click-heating&amp;quot; &amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;fire&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile  col=&amp;quot;4&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Lighting-Main&amp;quot;  id=&amp;quot;click-lighting&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;lightbulb&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
  &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The next section defines the main panels for the Lighting, &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;81&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Main&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;25%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&amp;quot;LightingPower&amp;quot;&lt;br /&gt;
                       value=&amp;quot;off&amp;quot; fill=&amp;quot;outline&amp;quot; shape=&amp;quot;circle&amp;quot; states=&amp;quot;on,off&amp;quot; color=&amp;quot;gray&amp;quot;&lt;br /&gt;
                       @value-change=&#039;powerChange($event)&#039;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;power-off&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;75%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-slider  id=&amp;quot;LightingDimmer&amp;quot;&lt;br /&gt;
                        value=&amp;quot;1&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot;&lt;br /&gt;
                        color=&amp;quot;white&amp;quot;&lt;br /&gt;
                        @value-change=&amp;quot;dimmerChange($event)&amp;quot;&lt;br /&gt;
                        &amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-slider&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Scenes&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;cog&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Settings&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Heating &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;108&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Heating-Main&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row id=&amp;quot;Heating-Tabs&amp;quot;class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;and Audio&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;112&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Player-Main&amp;quot; &amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;iframe  id=&amp;quot;Player-kiosk&amp;quot; src=&amp;quot;&amp;quot; style=&amp;quot;height:90vh;width:100vw;overflow:hidden;padding:0px;border:none;margin:0px&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Player-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Scenes Tab and Success popup are also defined.  At line 150, the tab selector for the first Lighting Detail Panel is also created:&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;119&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Scenes&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;gap:5em;&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-grid margin=&amp;quot;5&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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene1_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 1&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene1&#039; style=&#039;box-shadow:0px 0px 10px 5px #B26464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot; @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;Press to Select&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;1&amp;quot; col=&amp;quot;2&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene2_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 2&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene2&#039; style=&#039;box-shadow:0px 0px 10px 5px #FF6464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;1&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene3_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 3&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene3&#039; style=&#039;box-shadow:0px 0px 10px 5px #64C9C9;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene4_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 4&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene4&#039; style=&#039;box-shadow:0px 0px 10px 5px #1F6F3F;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;3&amp;quot; col=&amp;quot;1&amp;quot; width=&#039;2&#039; height=&#039;0.1&#039;&amp;gt;&lt;br /&gt;
          Long hold to set&lt;br /&gt;
        &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Scenes-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Main&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;reply&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Go Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Settings-Detail0-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Setting-Detail0&amp;quot;  timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;tasks&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Details&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;ftui-label id=&amp;quot;scene-success-click&amp;quot; @click=&#039;scene_success.open()&#039; style=&amp;quot;display:none&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_fail&amp;quot; timeout=&amp;quot;15&amp;quot; color=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Failed To Save&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Failed to Save Scene&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_success&amp;quot; timeout=&amp;quot;5&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Saved&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Scene Saved&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kiosk JavaScript ===&lt;br /&gt;
&lt;br /&gt;
=== Kiosk CSS ===&lt;br /&gt;
&lt;br /&gt;
== Improvements required ==&lt;br /&gt;
&lt;br /&gt;
# The Heating Thermostat scales well for square displays, but other aspect ratios are not handled well.&lt;br /&gt;
# There is code duplication between this implementation and the FTUI3 App.  This should be eliminated.&lt;br /&gt;
# The hard-coded timeouts should be &amp;lt;META&amp;gt; driven.&lt;br /&gt;
# Control of further devices, using the same principles should be possible.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39013</id>
		<title>Benutzer:Rstooks21/Kiosk UI</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39013"/>
		<updated>2024-01-22T23:31:41Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Part way through the first draft&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle en}}&lt;br /&gt;
&lt;br /&gt;
==Kiosk UI==&lt;br /&gt;
This is an approach to the dynamic creation of a Kiosk UI that can be used by anyone visiting the home. There will be multiple, small, possibly wall mounted Kiosk screens - one in each physical room. The Kiosk UI controls only the devices that are located in the same [FHEM] Room.&lt;br /&gt;
&lt;br /&gt;
== The Objective ==&lt;br /&gt;
To use the new FHEM Tablet UI (FTUI3) to create a tabbed design that allows the most important device controls to be shown by default and at the same time allow other devices to be controlled in as few clicks/screen touches as possible.  The rooms selected for testing had some or all of:&lt;br /&gt;
&lt;br /&gt;
* Lighting (Shelly RGDW LED Controller and Shelly DImmer 2, defined to FHEM as Shelly devices)&lt;br /&gt;
* Audio (Logitech Media Players defined to FHEM as SBPLAYER devices)&lt;br /&gt;
* Heating (EQ3 MAX! WallMountedThermostat and RadiatorThermostat, defined as MAX devices)&lt;br /&gt;
&lt;br /&gt;
With a simple call, the Kiosk screen displays the default screen, with tabs for other device control screens.  The default screens are implemented in the following order:&lt;br /&gt;
&lt;br /&gt;
* Lighting first&lt;br /&gt;
* If no Lighting, then Audio&lt;br /&gt;
* If no Lighting or Audio, then Heating&lt;br /&gt;
&lt;br /&gt;
This web address is intended to be used in a browser in Kiosk mode&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
http[s]://fhem-location:8083/fhem/kiosk/kiosk.html?room=Kitchen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The Panels ==&lt;br /&gt;
All the panels time out to the primary panel after 60 seconds of non use.&lt;br /&gt;
&lt;br /&gt;
=== The Lighting Panels ===&lt;br /&gt;
&lt;br /&gt;
==== Main Panel ====&lt;br /&gt;
The main Lighting Panel is a simple On/Off switch and dimmer slider.  The single control will control all of the lights in the room equally&lt;br /&gt;
&lt;br /&gt;
* turning them all off or on&lt;br /&gt;
* dimming all lights in proportion&lt;br /&gt;
&lt;br /&gt;
[[Datei:KioskLightingMainPanel.png|alternativtext=An on/off switch and a slider used for dimming the lights|ohne|mini|An FTUI3 Main Lighting control panel ]]&lt;br /&gt;
Note the four tabs.   The Lighting tab has further settings that can be adjusted.&lt;br /&gt;
&lt;br /&gt;
==== Settings/Scene Selector Panel ====&lt;br /&gt;
Selecting the &amp;quot;Settings&amp;quot; tab allows the user to select a scene - a combination of light settings that have been previously saved (by the user in the Kiosk UI)&lt;br /&gt;
[[Datei:KioskLightingScenes.png|alternativtext=A grid of four buttons in a panel display|ohne|mini|FTUI Grids and Buttons used to save and recall scenes]]&lt;br /&gt;
The light settings for all lights in the room are recalled by a short press to any of the buttons. &lt;br /&gt;
&lt;br /&gt;
The current light settings for all lights in the room are stored as a scene by pressing and holding the relevant button. This causes the scene to be saved and the button to glow with the average light colour and intensity for all lights that are on.&lt;br /&gt;
&lt;br /&gt;
==== The Details Panel ====&lt;br /&gt;
From the Scene Selector Panel it is possible to control every light in the room individually.   The Details Panels are flexed to display up to three lights each with as many Panels as needed:&lt;br /&gt;
[[Datei:KioskLightingDetail.png|alternativtext=An on/off switch and four sliders in Red, Green, Blue and White|ohne|mini|FTUI3 Switch and Sliders for RGBW control of a single led strip]]&lt;br /&gt;
&lt;br /&gt;
=== The Audio Panel ===&lt;br /&gt;
The Audio Panel uses an IFRAME to embed the Logitech Media Server web interface into the panel.   Only the player in the room can be controlled - the other players cannot be unless they are synchronised.   This is a bit of a cheat as the facilities in FHEM to control the player and display album art are not used.  But why reinvent?&lt;br /&gt;
[[Datei:KioskAudioPlayer.png|alternativtext=A media player display with a second tab for heating control|ohne|mini|The IFRAME containing the web interface at the top (90% height) and the two tabs at the bottom.  One for the Music and a second for the Heating.]]&lt;br /&gt;
&lt;br /&gt;
=== The Heating Panel ===&lt;br /&gt;
The Heating Panel prioritises control of the room&#039;s temperature using a Wall Mounted Thermostat, if one is available.  If not, it is assumed that one of the Radiator Thermostats can be used and that all, if more than one, are linked together.&lt;br /&gt;
&lt;br /&gt;
The heating screen just allows the setting of the temperature in the same room.  None of the other room&#039;s temperatures may be set, nor can any &amp;quot;scenes&amp;quot; be set.&lt;br /&gt;
[[Datei:KioskHeatingThermostat.png|alternativtext=A dial with a gradient to show actual temperature and a needle to show desired temperature|ohne|mini|An FTUI3 Compound Thermostat showing Actual and Desired Temperature]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
The FTUI3 code, available from GITHUB should be downloaded and added to FHEM following the instructions here:   https://github.com/knowthelist/ftui&lt;br /&gt;
&lt;br /&gt;
Create an HTTPSRV address for the kioskUI:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
define kioskUI HTTPSRV kiosk /opt/fhem/www/ftui Kiosk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
&lt;br /&gt;
=== Kiosk HTML ===&lt;br /&gt;
The HTML is divided into three sections&lt;br /&gt;
&lt;br /&gt;
==== The First Section ====&lt;br /&gt;
... in the &amp;lt;HEAD&amp;gt; reads in the FTUI3 code and META tags that configure it.   This implementation choses not to use Toast messages&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot;&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-height, height=device-width, user-scalable = no, initial-scale = 1, maximum-scale = 1, minimum-scale = 1&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;
  &amp;lt;meta name=&amp;quot;toast&amp;quot; content=&amp;quot;0&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;meta name=&amp;quot;fhemweb_url&amp;quot; content=&amp;quot;http://&amp;lt;your-url&amp;gt;:8083/fhem/&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;meta name=&amp;quot;refresh_interval&amp;quot; content=&amp;quot;60&amp;quot;&amp;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;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The Second Section ====&lt;br /&gt;
... also in the &amp;lt;HEAD&amp;gt; reads in the Kiosk support files &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;35&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;!-- SET UP THE KIOSK --&amp;gt;&lt;br /&gt;
  &amp;lt;title&amp;gt;KIOSK&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_class.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/init.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;./kiosk/light_functions.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;link href=&amp;quot;./kiosk/kiosk_styles.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;!-- &amp;lt;link href=&amp;quot;./kiosk/kiosk-theme.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt; --&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The Third Section ====&lt;br /&gt;
... defines the body.&lt;br /&gt;
&lt;br /&gt;
This contains outlines of the Panels, ready for customisation by the JavaScript.&lt;br /&gt;
&lt;br /&gt;
The body has a kiosk_onLoad() function - this is part of the Kiosk customisation and is called to customise the predefined panels once the outlines have been loaded&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;45&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;&#039;body&amp;quot; onload=&amp;quot;kiosk_onLoad()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The first tab defined is a clock.  This should only ever be shown if there are no controllable devices in the room.  It&#039;s included so that this is obvious (for instance if something has gone wrong with the Kiosk definition).  There are also three clickable tabs in this panel.  These serve two purposes:&lt;br /&gt;
&lt;br /&gt;
# To allow the configuration to be manually checked when something has gone wrong.&lt;br /&gt;
# To allow the kiosk_onLoad() function to select the first panel for display using the FTUI App by triggering the onclick event.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;46&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Clock&amp;quot;&amp;gt;    &lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;height:100vh&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile row=&amp;quot;1&amp;quot; col=&amp;quot;1&amp;quot; shape=&amp;quot;round&amp;quot; color=&amp;quot;primary&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;ee&amp;quot; size=&amp;quot;6&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-clock format=&amp;quot;DD&amp;quot; size=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-clock format=&amp;quot;hh:mm&amp;quot; size=&amp;quot;9&amp;quot;&amp;gt;&amp;lt;/ftui-clock&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row class=&amp;quot;kiosk-tab-row&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;ftui-grid-tile col=&amp;quot;1&amp;quot; width=&amp;quot;0.25&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Clock&amp;quot; id=&amp;quot;click-clock&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;clock&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt; &lt;br /&gt;
    &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-grid-tile col=&amp;quot;2&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Player-Main&amp;quot; id=&amp;quot;click-player&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;music&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-label&amp;gt;Music&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile col=&amp;quot;3&amp;quot;  width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Heating-Main&amp;quot;  id=&amp;quot;click-heating&amp;quot; &amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;fire&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-grid-tile  col=&amp;quot;4&amp;quot; width=&amp;quot;0.25&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-tab view=&amp;quot;Lighting-Main&amp;quot;  id=&amp;quot;click-lighting&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-icon name=&amp;quot;lightbulb&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
  &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The next section defines the main panels for the Lighting, &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;81&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Main&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;25%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&amp;quot;LightingPower&amp;quot;&lt;br /&gt;
                       value=&amp;quot;off&amp;quot; fill=&amp;quot;outline&amp;quot; shape=&amp;quot;circle&amp;quot; states=&amp;quot;on,off&amp;quot; color=&amp;quot;gray&amp;quot;&lt;br /&gt;
                       @value-change=&#039;powerChange($event)&#039;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-icon name=&amp;quot;power-off&amp;quot;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-button&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column width=&amp;quot;75%&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-slider  id=&amp;quot;LightingDimmer&amp;quot;&lt;br /&gt;
                        value=&amp;quot;1&amp;quot; min=&amp;quot;1&amp;quot; max=&amp;quot;100&amp;quot;&lt;br /&gt;
                        color=&amp;quot;white&amp;quot;&lt;br /&gt;
                        @value-change=&amp;quot;dimmerChange($event)&amp;quot;&lt;br /&gt;
                        &amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-slider&amp;gt;&lt;br /&gt;
        &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Scenes&amp;quot; timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;cog&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Settings&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Heating &amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;108&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Heating-Main&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;ftui-row id=&amp;quot;Heating-Tabs&amp;quot;class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;and Audio&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;112&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Player-Main&amp;quot; &amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;iframe  id=&amp;quot;Player-kiosk&amp;quot; src=&amp;quot;&amp;quot; style=&amp;quot;height:90vh;width:100vw;overflow:hidden;padding:0px;border:none;margin:0px&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Player-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
&amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Scenes Tab and Success popup are also defined.  At line 150, the tab selector for the first Lighting Detail Panel is also created:&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot; line=&amp;quot;1&amp;quot; start=&amp;quot;119&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ftui-tab-view id=&amp;quot;Lighting-Scenes&amp;quot;&amp;gt;    &lt;br /&gt;
      &amp;lt;ftui-row class=&amp;quot;kiosk-panel-row&amp;quot; style=&amp;quot;gap:5em;&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-grid margin=&amp;quot;5&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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene1_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 1&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene1&#039; style=&#039;box-shadow:0px 0px 10px 5px #B26464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot; @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;Press to Select&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;1&amp;quot; col=&amp;quot;2&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot; &amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header class=&#039;scene_header&#039; id=&#039;scene2_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 2&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene2&#039; style=&#039;box-shadow:0px 0px 10px 5px #FF6464;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;1&amp;quot; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene3_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 3&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene3&#039; style=&#039;box-shadow:0px 0px 10px 5px #64C9C9;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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; width=&#039;1&#039; height=&#039;1&#039; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-grid-header  class=&#039;scene_header&#039; id=&#039;scene4_header&#039; color=&amp;quot;medium&amp;quot;&amp;gt;Scene 4&amp;lt;/ftui-grid-header&amp;gt;&lt;br /&gt;
          &amp;lt;ftui-button id=&#039;scene4&#039; style=&#039;box-shadow:0px 0px 10px 5px #1F6F3F;border-radius:var(--grid-tile-border-radius);text-align:center;&#039; fill=&amp;quot;solid&amp;quot; color=&amp;quot;medium&amp;quot;  @hold=&amp;quot;save_scene(this)&amp;quot; @click=&amp;quot;show_scene(this)&amp;quot; class=&amp;quot;kiosk-scene-button&amp;quot;&amp;gt;&amp;lt;p&amp;gt;Press to Select&amp;lt;/p&amp;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;3&amp;quot; col=&amp;quot;1&amp;quot; width=&#039;2&#039; height=&#039;0.1&#039;&amp;gt;&lt;br /&gt;
          Long hold to set&lt;br /&gt;
        &amp;lt;/ftui-grid-tile&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-grid&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-row id=&amp;quot;Lighting-Scenes-Tabs&amp;quot; class=&amp;quot;kiosk-tab-row&amp;quot; &amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Scenes-Back-Tab&amp;quot; class=&amp;quot;kiosk-tab-active-back&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Main&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;reply&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Go Back&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
        &amp;lt;ftui-column id=&amp;quot;Lighting-Settings-Detail0-Tab&amp;quot; class=&amp;quot;kiosk-tab-inactive&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;ftui-tab class=&#039;kiosk-button&#039; view=&amp;quot;Lighting-Setting-Detail0&amp;quot;  timeout=&amp;quot;60&amp;quot;&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-icon name=&#039;tasks&#039;&amp;gt;&amp;lt;/ftui-icon&amp;gt;&lt;br /&gt;
              &amp;lt;ftui-label&amp;gt;Details&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
            &amp;lt;/ftui-tab&amp;gt;&lt;br /&gt;
          &amp;lt;/ftui-column&amp;gt;&lt;br /&gt;
      &amp;lt;/ftui-row&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-tab-view&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;ftui-label id=&amp;quot;scene-success-click&amp;quot; @click=&#039;scene_success.open()&#039; style=&amp;quot;display:none&amp;quot;&amp;gt;&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_fail&amp;quot; timeout=&amp;quot;15&amp;quot; color=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Failed To Save&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Failed to Save Scene&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
    &amp;lt;ftui-popup id=&amp;quot;scene_success&amp;quot; timeout=&amp;quot;5&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;header&amp;gt;Saved&amp;lt;/header&amp;gt;&lt;br /&gt;
      &amp;lt;ftui-label&amp;gt;Scene Saved&amp;lt;/ftui-label&amp;gt;&lt;br /&gt;
    &amp;lt;/ftui-popup&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kiosk JavaScript ===&lt;br /&gt;
&lt;br /&gt;
=== Kiosk CSS ===&lt;br /&gt;
&lt;br /&gt;
== Improvements required ==&lt;br /&gt;
&lt;br /&gt;
# The Heating Thermostat scales well for square displays, but other aspect ratios are not handled well.&lt;br /&gt;
# There is code duplication between this implementation and the FTUI3 App.  This should be eliminated.&lt;br /&gt;
# The hard-coded timeouts should be &amp;lt;META&amp;gt; driven.&lt;br /&gt;
# Control of further devices, using the same principles should be possible.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:KioskLightingDetail.png&amp;diff=39012</id>
		<title>Datei:KioskLightingDetail.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:KioskLightingDetail.png&amp;diff=39012"/>
		<updated>2024-01-22T22:43:30Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;FTUI3 On/Off plus four sliders -one for each of the RGBW channels&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:KioskLightingScenes.png&amp;diff=39011</id>
		<title>Datei:KioskLightingScenes.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:KioskLightingScenes.png&amp;diff=39011"/>
		<updated>2024-01-22T22:35:35Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;FTU3I Tab, Grid and Button components presenting the options to set or recall lighting scenes&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:KioskLightingMainPanel.png&amp;diff=39010</id>
		<title>Datei:KioskLightingMainPanel.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:KioskLightingMainPanel.png&amp;diff=39010"/>
		<updated>2024-01-22T22:30:49Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;An FTUI3 On/Off Button, a Slider and four Tab Controls&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:KioskAudioPlayer.png&amp;diff=39009</id>
		<title>Datei:KioskAudioPlayer.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:KioskAudioPlayer.png&amp;diff=39009"/>
		<updated>2024-01-22T22:24:42Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the Logitech Media Server web interface for the &amp;quot;Bedroom&amp;quot;&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:KioskHeatingThermostat.png&amp;diff=39008</id>
		<title>Datei:KioskHeatingThermostat.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:KioskHeatingThermostat.png&amp;diff=39008"/>
		<updated>2024-01-22T22:18:20Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A compound FTUI Heating Thermostat Control&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39007</id>
		<title>Benutzer:Rstooks21/Kiosk UI</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21/Kiosk_UI&amp;diff=39007"/>
		<updated>2024-01-22T21:53:42Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Started the Kiosk UI Page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Kiosk UI ===&lt;br /&gt;
The following page describes how to create a dynamic UI that allows the user to use a small, maybe wall-mounted, touch screen device to control only the devices in the same room.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:Rstooks21&amp;diff=39006</id>
		<title>Benutzer Diskussion:Rstooks21</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:Rstooks21&amp;diff=39006"/>
		<updated>2024-01-22T18:22:50Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Neuer Abschnitt /* Dynamic Kiosk for FTUI3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Willkommen! ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;6&amp;quot; style=&amp;quot;line-height: 20px; background: #E0E0E0; border: 2px solid #1874CD;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; style=&amp;quot;background:#1874CD;&amp;quot; |&amp;lt;big&amp;gt;&amp;lt;span style=&amp;quot;color: #FAFAFA&amp;quot;&amp;gt;&#039;&#039;&#039;Hallo Rstooks21,&#039;&#039;&#039; willkommen im FHEM Wiki!&amp;lt;/span&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | Danke für dein Interesse an unserem Projekt, ich freue mich schon auf deine weiteren Beiträge. Die folgenden Seiten sollten dir die ersten Schritte erleichtern, bitte nimm dir daher etwas Zeit, sie zu lesen.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;FHEM-spezifische Informationen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Systemübersicht]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;FHEM Systemübersicht&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[FHEMWiki:Über FHEMWiki]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Informationen über dieses Wiki&#039;&#039;&lt;br /&gt;
&amp;lt;!-- Abschnitt auf Kommentar gesetzt&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Todo|FHEM-spezifische Anleitungen und Regeln.}}&lt;br /&gt;
&lt;br /&gt;
---- &lt;br /&gt;
 Ende von &#039;Abschnitt auf Kommentar gesetzt&#039; --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | &lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;Generelle Informationen über (Media)Wikis&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:Crystal Clear app kedit.svg|rechts|30px|link=Hilfe:Bearbeiten]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Hilfe:Bearbeiten]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Zugang zu allen wichtigen Informationen.&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:X-office-presentation.svg|rechts|30px|link=Wikipedia:Tutorial]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &amp;lt;!-- &#039;&#039;&#039;[[Wikipedia:Tutorial]]&#039;&#039;&#039;--&amp;gt;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Wikipedia:Tutorial Wikipedia:Tutorial]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Schritt-für-Schritt-Anleitung für Einsteiger.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Applications-system.svg|rechts|30px|link=Wikipedia:Grundprinzipien]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Grundprinzipien]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Grundprinzipien Wikipedia:Grundprinzipien]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Die grundlegende Philosophie unseres Projekts.&#039;&#039;&lt;br /&gt;
| [[Datei:MentorenProgrammLogo-7.svg|rechts|60px|link=Wikipedia:Mentorenprogramm]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Mentorenprogramm]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Mentorenprogramm Wikipedia:Mentorenprogramm]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Persönliche Einführung in die Beteiligung bei Wikipedia.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
Bitte beachte, &amp;lt;!--[[Wikipedia:Was Wikipedia nicht ist|was Wikipedia nicht ist]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Was_Wikipedia_nicht_ist was Wikipedia nicht ist], und &amp;quot;unterschreibe&amp;quot; deine Diskussionsbeiträge durch Eingabe von &amp;lt;code&amp;gt;--&amp;lt;nowiki&amp;gt;~~~~&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; oder durch Drücken der Schaltfläche [[Datei:button_sig.png|Signaturknopf|20px|link=Hilfe:Signatur]] über dem Bearbeitungsfeld. Artikel werden jedoch nicht unterschrieben, und wofür die Zusammenfassungszeile da ist, erfährst du unter &amp;lt;!--[[wikipedia:Hilfe:Zusammenfassung und Quellen|Hilfe:Zusammenfassung und Quellen]]--&amp;gt;[http://de.wikipedia.org/wiki/Hilfe:Zusammenfassung_und_Quellen Zusammenfassung und Quellen]. &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Datei:Nuvola apps ksirc.png|25px|link=Benutzer Diskussion:Ph1959de]] &amp;amp;nbsp;&amp;amp;nbsp; &#039;&#039;&#039;Hast du Fragen an mich?&#039;&#039;&#039; Schreib mir auf [[Benutzer Diskussion:Ph1959de|&amp;lt;u&amp;gt;meiner&amp;lt;/u&amp;gt; Diskussionsseite]]! Viele Grüße, [[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 11:07, 19. Jan. 2024 (CET)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Dynamic Kiosk for FTUI3 ==&lt;br /&gt;
&lt;br /&gt;
This is an approach to the dynamic creation of a Kiosk UI that can be used by anyone visiting the home.  There will be multiple, small, Kiosk screens - one in each room.  The Kiosk UI controls only the resources that are located in the same room.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21&amp;diff=39005</id>
		<title>Benutzer:Rstooks21</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21&amp;diff=39005"/>
		<updated>2024-01-22T17:51:28Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: Typo correction&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hello&lt;br /&gt;
&lt;br /&gt;
I&#039;ve been using FHEM for 6 or 7 years now - mainly to control my heating.  I have just started (2024) to expand into controlling other things in my home - audio and lighting.&lt;br /&gt;
&lt;br /&gt;
My FHEM System runs on a Raspberry PI 3B with a CUL &lt;br /&gt;
&lt;br /&gt;
==== Heating ====&lt;br /&gt;
I have 22 EQ3 MAX! Radiator and 8 Wall Thermostats as well as the MAX! Boiler Controller.   These are spread around 15 rooms in my house&lt;br /&gt;
&lt;br /&gt;
==== Audio ====&lt;br /&gt;
My Audio is a Logitech Media Server running in Docker on a Synology NAS together with 4 Raspberry pi tinycoreplayer around the house to provide the music.  These are all defined to FHEM.&lt;br /&gt;
&lt;br /&gt;
==== Lighting ====&lt;br /&gt;
The lighting is all manual at the moment, but I plan to buy a number of Shelly Dimmer 2 and Shelly rgbw controllers&lt;br /&gt;
&lt;br /&gt;
==== UI ====&lt;br /&gt;
The UI is based around the Tablet UI and currently just has the Thermostats&lt;br /&gt;
[[Datei:FTUI.png|zentriert|mini|This is the front end of my system.  It shows the temperatures for each room, the battery status across all devices and switches to control overrides to the heating]]&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21&amp;diff=39004</id>
		<title>Benutzer:Rstooks21</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer:Rstooks21&amp;diff=39004"/>
		<updated>2024-01-22T17:50:21Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: New page to introduce myself and my FHEM system&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hello&lt;br /&gt;
&lt;br /&gt;
I&#039;ve been using FHEM for 6 or 7 years now - mainly to control my heating.  I have just started (2024) to expand into controlling other things in my home - audio and lighting.&lt;br /&gt;
&lt;br /&gt;
My FHEM System runs on a Raspberry PI 3B with a CUL &lt;br /&gt;
&lt;br /&gt;
==== Heating ====&lt;br /&gt;
I have 22 EQ3 MAX! Radiator and 8 Wall Thermostats as well as the MAX! Boiler Controller.   These are spread around 15 rooms in my house&lt;br /&gt;
&lt;br /&gt;
==== Audio ====&lt;br /&gt;
My Audio is a Logitech Media Server running in Docker on a Synology NAS together with 4 Raspberry pi tinycoreplayer around the house to provide the music.  These are all defined to FHEM.&lt;br /&gt;
&lt;br /&gt;
==== Lighting ====&lt;br /&gt;
The lighting is all manual at the moment, but I plan to buy a number of Shelly Dimmer 2 and Shelly rgbw controllers&lt;br /&gt;
&lt;br /&gt;
==== UI ====&lt;br /&gt;
The UI is based around the Tablet UI and currently just has the Thermostats&lt;br /&gt;
[[Datei:FTUI.png|zentriert|mini|This is the front end of my system.  It shows the temperatures for each room, the battery status across all devices and a switches to control overrides to the heating]]&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:FTUI.png&amp;diff=39003</id>
		<title>Datei:FTUI.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:FTUI.png&amp;diff=39003"/>
		<updated>2024-01-22T17:41:28Z</updated>

		<summary type="html">&lt;p&gt;Rstooks21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the main control screen for the system.  It shows the desired and actual temperature for each room, together with a summary of the battery status for each device.&lt;br /&gt;
&lt;br /&gt;
There are several modes that can be selected to set the heating system to do something else - either until it is switched off, or as an override until the next planned temperature change.&lt;/div&gt;</summary>
		<author><name>Rstooks21</name></author>
	</entry>
</feed>