Browse Source

ErsterLokalerCommit

hmetzner 7 years ago
parent
commit
e0975e49bc
100 changed files with 25494 additions and 1 deletions
  1. 12 1
      fhem/core/fhem.cfg
  2. BIN
      fhem/core/www/hausautomatisierung-com/images/haus_automatisierung.png
  3. BIN
      fhem/core/www/hausautomatisierung-com/images/haus_automatisierung_orgi.png
  4. BIN
      fhem/core/www/hausautomatisierung-com/images/my_haus_automatisierung.png
  5. 21 0
      fhem/core/www/tablet_dev/css/fhem-blue-ui.css
  6. 51 0
      fhem/core/www/tablet_dev/css/fhem-darkblue-ui.css
  7. 21 0
      fhem/core/www/tablet_dev/css/fhem-green-ui.css
  8. 88 0
      fhem/core/www/tablet_dev/css/fhem-mobil-ui.css
  9. 16 0
      fhem/core/www/tablet_dev/css/fhem-tablet-ui-custom_1280.css
  10. 158 0
      fhem/core/www/tablet_dev/css/fhem-tablet-ui-wdtimer.css
  11. 3467 0
      fhem/core/www/tablet_dev/css/fhem-tablet-ui.css
  12. 1 0
      fhem/core/www/tablet_dev/css/fhem-tablet-ui.min.css
  13. 95 0
      fhem/core/www/tablet_dev/css/ftui-bright-mint-ui.css
  14. 165 0
      fhem/core/www/tablet_dev/css/ftui_chart.css
  15. 67 0
      fhem/core/www/tablet_dev/css/ftui_checklist.css
  16. 63 0
      fhem/core/www/tablet_dev/css/ftui_colorwheel.css
  17. 36 0
      fhem/core/www/tablet_dev/css/ftui_controlbutton.css
  18. 39 0
      fhem/core/www/tablet_dev/css/ftui_controller.css
  19. 182 0
      fhem/core/www/tablet_dev/css/ftui_departure.css
  20. 59 0
      fhem/core/www/tablet_dev/css/ftui_medialist.css
  21. 25 0
      fhem/core/www/tablet_dev/css/ftui_range.css
  22. 46 0
      fhem/core/www/tablet_dev/css/ftui_slideout.css
  23. 44 0
      fhem/core/www/tablet_dev/css/ftui_slider.css
  24. 79 0
      fhem/core/www/tablet_dev/css/ftui_spinner.css
  25. 239 0
      fhem/core/www/tablet_dev/css/ftui_weekprofile.css
  26. 95 0
      fhem/core/www/tablet_dev/demo_ftui.html
  27. 29 0
      fhem/core/www/tablet_dev/fonts/FTUI-icons.svg
  28. BIN
      fhem/core/www/tablet_dev/fonts/FTUI-icons.ttf
  29. BIN
      fhem/core/www/tablet_dev/fonts/FTUI-icons.woff
  30. BIN
      fhem/core/www/tablet_dev/fonts/FontAwesome.otf
  31. BIN
      fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.eot
  32. 1 0
      fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.ijmap
  33. 2373 0
      fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.svg
  34. BIN
      fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.ttf
  35. BIN
      fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.woff
  36. BIN
      fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.woff2
  37. 203 0
      fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont-License.txt
  38. BIN
      fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont.eot
  39. 631 0
      fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont.svg
  40. BIN
      fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont.ttf
  41. BIN
      fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont.woff
  42. BIN
      fhem/core/www/tablet_dev/fonts/fhemSVG.eot
  43. 964 0
      fhem/core/www/tablet_dev/fonts/fhemSVG.svg
  44. BIN
      fhem/core/www/tablet_dev/fonts/fhemSVG.ttf
  45. BIN
      fhem/core/www/tablet_dev/fonts/fhemSVG.woff
  46. 3 0
      fhem/core/www/tablet_dev/fonts/fontawesome-License.txt
  47. BIN
      fhem/core/www/tablet_dev/fonts/fontawesome-webfont.eot
  48. 2671 0
      fhem/core/www/tablet_dev/fonts/fontawesome-webfont.svg
  49. BIN
      fhem/core/www/tablet_dev/fonts/fontawesome-webfont.ttf
  50. BIN
      fhem/core/www/tablet_dev/fonts/fontawesome-webfont.woff
  51. BIN
      fhem/core/www/tablet_dev/fonts/fontawesome-webfont.woff2
  52. 5 0
      fhem/core/www/tablet_dev/fonts/meteocons-License.txt
  53. BIN
      fhem/core/www/tablet_dev/fonts/meteocons-webfont.eot
  54. 81 0
      fhem/core/www/tablet_dev/fonts/meteocons-webfont.svg
  55. BIN
      fhem/core/www/tablet_dev/fonts/meteocons-webfont.ttf
  56. BIN
      fhem/core/www/tablet_dev/fonts/meteocons-webfont.woff
  57. BIN
      fhem/core/www/tablet_dev/fonts/openautomation.eot
  58. 448 0
      fhem/core/www/tablet_dev/fonts/openautomation.svg
  59. BIN
      fhem/core/www/tablet_dev/fonts/openautomation.ttf
  60. BIN
      fhem/core/www/tablet_dev/fonts/openautomation.woff
  61. BIN
      fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.eot
  62. 257 0
      fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.svg
  63. BIN
      fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.ttf
  64. BIN
      fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.woff
  65. BIN
      fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.woff2
  66. 98 0
      fhem/core/www/tablet_dev/ftui_snippet_tester.html
  67. 3126 0
      fhem/core/www/tablet_dev/icons_table.html
  68. BIN
      fhem/core/www/tablet_dev/images/blackboard_1280x800.jpg
  69. BIN
      fhem/core/www/tablet_dev/images/myfhemfavico_1024x1024.png
  70. BIN
      fhem/core/www/tablet_dev/images/myfhemfavico_120x120.png
  71. BIN
      fhem/core/www/tablet_dev/images/myfhemfavico_144x144.png
  72. BIN
      fhem/core/www/tablet_dev/images/myfhemfavico_152x152.png
  73. BIN
      fhem/core/www/tablet_dev/images/myfhemfavico_167x167.png
  74. BIN
      fhem/core/www/tablet_dev/images/myfhemfavico_180x180.png
  75. BIN
      fhem/core/www/tablet_dev/images/myfhemfavico_192x192.png
  76. BIN
      fhem/core/www/tablet_dev/images/myfhemfavico_512x512.png
  77. BIN
      fhem/core/www/tablet_dev/images/vista-wallpaper1920x1080.jpg
  78. 298 0
      fhem/core/www/tablet_dev/index-example.html
  79. 298 0
      fhem/core/www/tablet_dev/index.html
  80. 65 0
      fhem/core/www/tablet_dev/index_empty.html
  81. 217 0
      fhem/core/www/tablet_dev/index_layout.html
  82. 91 0
      fhem/core/www/tablet_dev/index_state.html
  83. 2204 0
      fhem/core/www/tablet_dev/js/fhem-tablet-ui.js
  84. 2 0
      fhem/core/www/tablet_dev/js/fhem-tablet-ui.min.js
  85. 740 0
      fhem/core/www/tablet_dev/js/highcharts-meteogram.js
  86. 190 0
      fhem/core/www/tablet_dev/js/widget_agenda.js
  87. 54 0
      fhem/core/www/tablet_dev/js/widget_button.js
  88. 238 0
      fhem/core/www/tablet_dev/js/widget_calview.js
  89. 3833 0
      fhem/core/www/tablet_dev/js/widget_chart.js
  90. 111 0
      fhem/core/www/tablet_dev/js/widget_checkbox.js
  91. 115 0
      fhem/core/www/tablet_dev/js/widget_checklist.js
  92. 88 0
      fhem/core/www/tablet_dev/js/widget_circlemenu.js
  93. 62 0
      fhem/core/www/tablet_dev/js/widget_classchanger.js
  94. 152 0
      fhem/core/www/tablet_dev/js/widget_clock.js
  95. 110 0
      fhem/core/www/tablet_dev/js/widget_colorwheel.js
  96. 156 0
      fhem/core/www/tablet_dev/js/widget_controlbutton.js
  97. 251 0
      fhem/core/www/tablet_dev/js/widget_controller.js
  98. 71 0
      fhem/core/www/tablet_dev/js/widget_datetimepicker.js
  99. 189 0
      fhem/core/www/tablet_dev/js/widget_departure.js
  100. 0 0
      fhem/core/www/tablet_dev/js/widget_dimmer.js

+ 12 - 1
fhem/core/fhem.cfg

@@ -28,6 +28,7 @@ attr WEB editConfig 1
 attr WEB group Web-Access
 attr WEB hiddenroom Everything
 attr WEB icon it_i-net
+attr WEB longpoll websocket
 attr WEB menuEntries DOIFtools,/fhem?detail=DOIFtools,haus-automatisierung.com,https://haus-automatisierung.com/
 attr WEB plotWeekStartDay 1
 attr WEB room System
@@ -66,7 +67,17 @@ attr DBLogging icon system_backup
 attr DBLogging room System
 define Mosquitto MQTT mqtt:1883
 attr Mosquitto room Interfaces
-define TabletUi HTTPSRV ftui/ ./www/tablet/ Tablet-UI
+define TabletUiEval HTTPSRV ftuidev/ ./www/tablet_dev TabletUI-Development
+attr TabletUiEval alias Tablet Eval
+attr TabletUiEval group Web-Access
+attr TabletUiEval icon smartphone
+attr TabletUiEval room System
+define TabletUiLatest HTTPSRV ftui/ ./www/tablet TabletUI-Newest
+attr TabletUiLatest alias Tablet Newest
+attr TabletUiLatest group Web-Access
+attr TabletUiLatest icon smartphone
+attr TabletUiLatest room System
+define TabletUi HTTPSRV ftuiiu/ ./www/tablet_inuse TabletUI
 attr TabletUi alias Tablet UI
 attr TabletUi group Web-Access
 attr TabletUi icon smartphone

BIN
fhem/core/www/hausautomatisierung-com/images/haus_automatisierung.png


BIN
fhem/core/www/hausautomatisierung-com/images/haus_automatisierung_orgi.png


BIN
fhem/core/www/hausautomatisierung-com/images/my_haus_automatisierung.png


+ 21 - 0
fhem/core/www/tablet_dev/css/fhem-blue-ui.css

@@ -0,0 +1,21 @@
+/* Slider colors */
+.range-handle {
+  background-color: #bcbcbc !important;
+}
+.range-bar {
+  background-color: #404040;
+}
+
+polyline {
+    stroke: #337ab7 !important;
+}
+
+.pagetab.on{background-color:#337ab7;}
+.dimmer.on{background-color:#337ab7;}
+.symbol.on{color:#337ab7;background-color:#337ab7;}
+.switch.on{background-color:#337ab7;}
+.checkbox.on{background-color:#337ab7;}
+.push.on{color:#337ab7;background-color:#337ab7;}
+.homestatus{color:#337ab7;}
+.slider{color:#337ab7;}
+.volume.hdcolor{color:#337ab7;}

+ 51 - 0
fhem/core/www/tablet_dev/css/fhem-darkblue-ui.css

@@ -0,0 +1,51 @@
+.gridster > ul > li, .card, section {
+    background-color:#223343;
+}
+
+.gridster li header, .card > header {
+    background: #3a4f5c;
+    color: #aaa;
+    border-bottom: 1px solid #222;
+}
+
+/* Slider colors */
+.range-handle {
+  background-color: #bcbcbc !important;
+}
+.range-bar {
+  background-color: #404040;
+}
+
+polyline {
+    stroke: #337ab7 !important;
+}
+
+option {background-color: #ffffff;}
+.pagetab.on{color:#5D6E80;background-color:#0088CC;}
+.pagetab.off{color:#5D6E80;background-color:#aaa;}
+.pagebutton.on{color:#223343;background-color:#4C5D71;}
+.pagebutton.off{color:#4C5D71;background-color:transparent;}
+.dimmer.on{color:#eee;background-color:#0088CC;}
+.dimmer.off{color:#eee;background-color:#4C5D71;}
+.symbol.on{color:#59BFFD;background-color:#59BFFD;}
+.symbol.off{color:#4C5D71;background-color:#aaa;}
+.switch.on{color:#eee;background-color:#0088CC;}
+.switch.off{color:#eee;background-color:#4C5D71;}
+.push.on{color:#eee;background-color:#3DA5D8;}
+.push.off{color:#eee;background-color:#4C5D71;}
+.thermostat.tkcolor{color:#4C5D71;}
+.thermostat.fgcolor{color:#4C5D71;}
+.thermostat.input{color:#aaa;}
+.thermostat.mincolor{color:#0088CC;}
+.thermostat.maxcolor{color:#f71525;}
+.homestatus{color:#0088CC;background-color:#aaa;}
+.homestatus.tkcolor{color:#4C5D71;}
+.homestatus.mincolor{color:#eee;}
+.homestatus.maxcolor{color:#4C5D71;}
+.slider{color:#0088CC;background-color:#999;}
+.volume.hdcolor{color:#0088CC;}
+.volume.fgcolor{color:#555;}
+.checkbox.on {color:#fff;background-color:#0088CC;}
+.checkbox.off {color:#fff;background-color:#999;}
+.playstream.on {color:#fff;background-color:#0088CC;}
+.playstream.off {color:#fff;background-color:#999;}

+ 21 - 0
fhem/core/www/tablet_dev/css/fhem-green-ui.css

@@ -0,0 +1,21 @@
+/* Slider colors */
+.range-handle {
+  background-color: #bcbcbc !important;
+}
+.range-bar {
+  background-color: #404040;
+}
+
+polyline {
+    stroke: #32a054 !important;
+}
+
+.pagetab.on{background-color:#32a054;}
+.dimmer.on{background-color:#32a054;}
+.symbol.on{color:#32a054;background-color:#32a054;}
+.switch.on{background-color:#32a054;}
+.checkbox.on{background-color:#32a054;}
+.push.on{color:#32a054;background-color:#32a054;}
+.homestatus{color:#32a054;}
+.slider{color:#32a054;}
+.volume.hdcolor{color:#32a054;}

+ 88 - 0
fhem/core/www/tablet_dev/css/fhem-mobil-ui.css

@@ -0,0 +1,88 @@
+body {
+    background-color: #eeeeee;
+    color: #222222;
+}
+
+nav {
+    background-color: #1D1F20;
+    background-image: linear-gradient(145deg, #1D1F20,#404348);
+    color: #ffffff;
+}
+
+.gridster > ul > li, .card, section {
+    background-color:#fff;
+}
+
+.gridster li header, .card > header {
+    background: #ffffff;
+    color: #222;
+    border-bottom: 1px solid #222;
+}
+
+ul, ol {
+    list-style: none;
+}
+
+.uline{
+    border-bottom: 1px solid #888;
+}
+
+/* Slider colors */
+.range-handle {
+  background-color: #fff !important;
+}
+.range-bar {
+  background-color: #aaa;
+}
+
+select {
+    color: #444;
+}
+
+svg > text {
+    fill: #444;
+}
+
+polyline {
+    stroke: #0088CC !important;
+}
+
+.dialog  {
+    color: #eeeeee;
+}
+
+i#warn {
+    color: #FFFFFF;
+}
+i#warn-back {
+    color: #DB1D1D;
+}
+
+.main-color              { color:#0088CC;}
+
+.bg-gray,.btn-gray               { background-color:#999999;}
+.bg-lightgray,.btn-lightgray     { background-color:#EDEDED;}
+
+option {background-color: #ffffff;}
+.pagetab.on{color:#eee;background-color:#0088CC;}
+.pagetab.off{color:#eee;background-color:#aaa;}
+.dimmer.on{color:#eee;background-color:#0088CC;}
+.dimmer.off{color:#eee;background-color:#aaa;}
+.symbol.on{color:#0088CC;background-color:#0088CC;}
+.symbol.off{color:#222;background-color:#222;}
+.switch.on{color:#eee;background-color:#0088CC;}
+.switch.off{color:#eee;background-color:#aaa;}
+.push.on{color:#0088CC;background-color:#0088CC;}
+.push.off{color:#888888;background-color:#888888;}
+.thermostat.tkcolor{color:#888;}
+.thermostat.fgcolor{color:#555;}
+.thermostat.input{color:#444;}
+.homestatus.fgcolor{color:#0088CC;}
+.homestatus.tkcolor{color:#aaa;}
+.slider{color:#0088CC;background-color:#999;}
+.volume.hdcolor{color:#0088CC;}
+.volume.fgcolor{color:#555;}
+.checkbox.on {color:#fff;background-color:#0088CC;}
+.checkbox.off {color:#fff;background-color:#999;}
+.playstream.on {color:#fff;background-color:#0088CC;}
+.playstream.off {color:#fff;background-color:#999;}

+ 16 - 0
fhem/core/www/tablet_dev/css/fhem-tablet-ui-custom_1280.css

@@ -0,0 +1,16 @@
+@font-face { font-family: 'DKCrayonCrumble';
+             src: url('../myfonts/crayoncrumble.ttf') format('truetype'); }
+body {
+    background:#000000 url(../images/blackboard_1280x800.jpg) 0 0 no-repeat;
+    font-family: DKCrayonCrumble, "Helvetica Neue", "Helvetica", "Open Sans", "Arial", sans-serif;
+    -webkit-font-smoothing: antialiased;
+    font-size: small;
+ }
+
+.semitransparent {
+    background: rgba(0, 0, 0, 0.4) !important;
+}
+
+.fulltransparent {
+    background: rgba(0, 0, 0, 0) !important;
+}

+ 158 - 0
fhem/core/www/tablet_dev/css/fhem-tablet-ui-wdtimer.css

@@ -0,0 +1,158 @@
+/* wdtimer_widget default CSS */
+.wdtimer { z-index: 1000; }
+.wdtimer_dialog { height: 150px; overflow: auto; width: 200px; text-align: center; }
+.wdtimer_profile{ padding-top: 0; padding-bottom: 5px; text-align: left; height: 80px;}
+.wdtimer_profile.error { background-color: #cd3700 !important; }
+.wdtimer_profilerow {
+    float: left;
+    width: calc(100% - 20% - 10px);
+    height: 100%;
+    margin-left: 10px;
+}
+.wdtimer_profileup, .wdtimer_profiledown {
+    font-size: 15px;
+    height:35px;
+    width:35px;     
+    display: block;
+    margin-top: 5px;    
+}
+.wdtimer_button {
+    padding: 0.5em;
+    margin-left: 0.3em;
+    text-decoration: none;
+    border: none;
+    font-weight: normal;
+    color: inherit;
+}
+.wdtimer_buttonblock { float: right; height: 100%; width: 20%; }
+.wdtimer_deleteprofile {
+    font-size: 15px;
+    height:35px;
+    width:35px;    
+    position: relative; 
+    top: 25px;
+}
+.wdtimer_delprofile { float:left; height: 100%; }
+.wdtimer_profileweekdays { padding-left: 4px; padding-top: 10px; }
+.wdtimer_profiletime { width: auto !important; }
+.wdtimer_profilecmd{ width: auto !important; }
+.wdtimer_text{ 
+    border: none;
+    font-size: small;
+    padding: 6px 6px 6px 6px !important;
+    width: 35px !important;    
+    height: auto;
+}
+.wdtimer_time{ 
+    border: none;
+    font-size: small;
+    padding: 6px 6px 6px 6px !important;
+    width: 3em !important;    
+    height: auto;
+}
+.wdtimer_cmd {
+    width: auto;    
+    border: none;
+    padding: 6px 6px 6px 6px !important;
+}
+.wdtimer_cmd.iconic {
+	font-size: 18px !important;
+	padding: 2px 0px 5px 2px !important;
+	word-spacing: -1.675em;
+	letter-spacing: 0.3em;
+}
+.wdtimer_checkbox {
+	position: relative;
+    display: inherit;       
+    border: none;
+    margin-left: -4px;        
+    height:35px;
+    line-height: 1.6em;
+    width:35px;
+    top: -0.35em;
+}
+.wdtimer_checkbox label {
+	cursor: pointer;
+	position: absolute;
+	left: 7px;
+	top: 7px;
+}
+.wdtimer_checkbox label:after {
+	opacity: 0;
+	content: '';
+	position: absolute;
+	top: -7px;
+	left: -7px;
+	width: 35px;
+	height: 35px;    
+}
+.wdtimer_checkbox input[type=checkbox]:checked + label:after { opacity: 0.5; }
+.wdtimer_header { text-align: center; padding-bottom: 5px; padding-top: 5px; font-size: 1.4em; }
+.wdtimer_header_icon { position: absolute; left: 8px; font-size: 1.25em !important; }
+.wdtimer_footer { padding-bottom: 5px; padding-top: 5px; height: 30px; }
+.wdtimer_footer .ui-dialog-buttonset{ float: right;}
+.wdtimer_active { float:left; display: inline-block; margin: 6px; }
+.wdtimer_shader {
+    position:absolute;
+    background-color:#000000;
+    opacity:0.5;
+    height:100%;
+    width:100%;
+    top:0px;
+    left:0px;    
+}
+.wdtimer_shader.wdtimer_profilelist { background-color:#ff1a1a; }
+
+/* ---------------------------------------------------- */
+/* wdtimer_widget Style "round" */
+.wdtimer_button.round { border-radius: 10px; }        
+.wdtimer_header.round { border-top-left-radius: 10px; border-top-right-radius: 10px; }
+.wdtimer_footer.round { border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; }
+.wdtimer_checkbox.begin.round { border-bottom-left-radius: 10px; border-top-left-radius: 10px; }  
+.wdtimer_checkbox.end.round { border-bottom-right-radius: 10px; border-top-right-radius: 10px; }   
+.wdtimer_checkbox.round input[type=checkbox]:checked + label.begin::after { border-bottom-left-radius: 10px; border-top-left-radius: 10px; }    
+.wdtimer_checkbox.round input[type=checkbox]:checked + label.end::after { border-bottom-right-radius: 10px; border-top-right-radius: 10px; } 
+.wdtimer_time.round { border-radius: 10px; }
+.wdtimer_cmd.round { border-radius: 10px; }
+.wdtimer_text.round { border-radius: 10px; }
+/* ---------------------------------------------------- */
+/* wdtimer_widget Style "square" */
+.wdtimer_cmd.square { border-radius: unset; }
+/* ---------------------------------------------------- */
+/* wdtimer_widget Theam "dark" */
+.wdtimer_button.dark:hover, .wdtimer_time.dark:hover, .wdtimer_cmd.dark:hover, .wdtimer_checkbox.dark:hover { background-color: #007fff; }
+.wdtimer_button.dark { color: #d1eeee; background-color: #393939; }
+.wdtimer_deleteprofile {color: #ff0000!important;}
+.wdtimer_time.dark { background-color: #393939; color: #d1eeee; }
+.wdtimer_text.dark { background-color: #393939; color: #d1eeee; }
+.wdtimer_datetimepicker.dark { background-color: #393939 !important; color: #d1eeee !important; }
+.wdtimer_cmd.dark { background-color: #393939; color: #d1eeee; }
+.wdtimer_checkbox.dark { background-color: #393939; color: #d1eeee; }
+.wdtimer_checkbox.dark label:after { background-color: #cc5500; }
+.wdtimer_dialog.dark{ background-color: #2a2a2a; }
+.wdtimer_header.dark { background-color: #262626; color: #d1eeee; }
+.wdtimer_footer.dark { background-color: #262626; }
+.dark .wdtimer_profile:last-child > .wdtimer_buttonblock > .wdtimer_profilemove > .wdtimer_profiledown { color: #262626;}
+.dark .wdtimer_profile:first-child > .wdtimer_buttonblock > .wdtimer_profilemove > .wdtimer_profileup { color: #262626; }
+.dark .wdtimer_profile:nth-child(odd) {background: #404040;}
+/* ---------------------------------------------------- */
+/* wdtimer_widget Theam "light" */
+.wdtimer_button.light:hover, .wdtimer_time.light:hover, .wdtimer_cmd.light:hover, .wdtimer_checkbox.light:hover { background-color: #007fff; }
+.wdtimer_button.light { color: #000000; background-color: #e3c7c7; border: 1px solid #e0e0e0; }
+.wdtimer_deleteprofile {color: #ff0000!important;}
+.wdtimer_time.light { background-color: #e3c7c7; color: #000000;  border: 1px solid #e0e0e0; }
+.wdtimer_text.light { background-color: #e3c7c7; color: #000000;  border: 1px solid #e0e0e0; }
+.wdtimer_datetimepicker.light { background-color: #e3c7c7 !important; color: #000000 !important; border: 1px solid #e0e0e0; }
+.wdtimer_cmd.light { background-color: #e3c7c7; color: #000000; border: 1px solid #e0e0e0; }
+.wdtimer_checkbox.light { background-color: #e3c7c7; color: #000000;  border: 1px solid #e0e0e0; }
+.wdtimer_checkbox.light label:after { background-color: #cc5500; }
+
+.wdtimer_dialog.light{ background-color: #ffffff; }
+.wdtimer_header.light { background-color: #ffffff; color: #000000; border: 1px solid #e0e0e0; }
+.wdtimer_footer.light { background-color: #ffffff; border: 1px solid #e0e0e0; }
+.light .wdtimer_profile:last-child > .wdtimer_buttonblock > .wdtimer_profilemove > .wdtimer_profiledown { color: #b3b3b3;}
+.light .wdtimer_profile:first-child > .wdtimer_buttonblock > .wdtimer_profilemove > .wdtimer_profileup { color: #b3b3b3; }
+.light .wdtimer_profile:nth-child(odd) {background: #e6e6e6;}
+/* ---------------------------------------------------- */
+/* wdtimer_widget Theam "custom" */
+/* ---------------------------------------------------- */

File diff suppressed because it is too large
+ 3467 - 0
fhem/core/www/tablet_dev/css/fhem-tablet-ui.css


File diff suppressed because it is too large
+ 1 - 0
fhem/core/www/tablet_dev/css/fhem-tablet-ui.min.css


+ 95 - 0
fhem/core/www/tablet_dev/css/ftui-bright-mint-ui.css

@@ -0,0 +1,95 @@
+body {
+    background-color: #fff;
+    color: #222222;
+}
+
+nav,
+:not([data-type="circlemenu"]) > .menu {
+  background: #5bc995;
+    color: #ffffff;
+}
+.menu .menu-trigger {
+  background: #5bc995;
+}
+
+.gridster > ul > li, .card {
+    background-color:#eee;
+    text-align: center;
+}
+
+.gridster li header, .card > header {
+    background: #5bc995;
+    color: #222;
+}
+
+[data-type="symbol"].big, [data-type="multistatebutton"].big,
+[data-type="switch"].big, [data-type="button"].big,
+[data-type="push"].big, [data-type="pagebutton"].big {
+    font-size: 200% !important;
+}
+
+section{
+    background-color: #ffffff;
+}
+
+.uline{
+    border-bottom: 1px solid #888;
+}
+
+/* Slider colors */
+.range-handle {
+  background-color: #fff !important;
+}
+.range-bar {
+  background-color: #aaa;
+}
+
+select {
+    color: #444;
+}
+
+svg > text {
+    fill: #444;
+}
+
+polyline {
+    stroke: #0088CC !important;
+}
+
+.dialog  {
+    color: #eeeeee;
+}
+
+i#warn {
+    color: #FFFFFF;
+}
+i#warn-back {
+    color: #DB1D1D;
+}
+
+.bg-gray,.btn-gray               { background-color:#999999;}
+.bg-lightgray,.btn-lightgray     { background-color:#EDEDED;}
+
+option {background-color: #ffffff;}
+
+/* pseudo classes for widget init function */
+.pagetab.on{color:#eee;background-color:#0088CC;}
+.pagetab.off{color:#eee;background-color:#aaa;}
+.dimmer.on{color:#eee;background-color:#0088CC;}
+.dimmer.off{color:#eee;background-color:#aaa;}
+.symbol.on{color:#0088CC;background-color:#0088CC;}
+.symbol.off{color:#222;background-color:#222;}
+.switch.on{color:#eee;background-color:#0088CC;}
+.switch.off{color:#eee;background-color:#aaa;}
+.push.on{color:#0088CC;background-color:#0088CC;}
+.push.off{color:#888;background-color:#888;}
+.thermostat.tkcolor{color:#888;}
+.thermostat.fgcolor{color:#555;}
+.thermostat.input{color:#444;}
+.homestatus.fgcolor{color:#0088CC;}
+.homestatus.tkcolor{color:#aaa;}
+.checkbox.on {color:#fff;background-color:#5bc995;}
+.checkbox.off {color:#fff;background-color:#999;}
+.slider{color:#0088CC;background-color:#999;}
+.volume.hdcolor{color:#0088CC;}
+.volume.fgcolor{color:#555;}

+ 165 - 0
fhem/core/www/tablet_dev/css/ftui_chart.css

@@ -0,0 +1,165 @@
+/* import needed for availability of FHEM plot stlyes in the HTML files */
+@import "../../pgm2/svg_style.css";
+
+/* start of styles for chart widget */
+
+/* Definition of the chart background area */
+.chart-background {
+	fill:url(#gr_bgftui);
+	fill-opacity: 1;
+}
+/* Definition of the axes generally*/
+.text.axes {
+	fill: #bbb;
+}
+.big .text.axes {
+	font-size: 15px;
+}
+.small .text.axes {
+	font-size: 10px;
+}
+.normal .text.axes {
+	font-size: 12px;
+}
+/* Definition of specialities for x-axis */
+.xaxis {
+	stroke-width: 1px;
+}
+/* Definition of specialities for y-axis */
+.yaxis {
+	stroke-width: 1px;
+}
+/* Definition of the gridlines generally */
+.gridlines {
+	stroke: #bbb;
+}
+/* Definition of specialities for x-axis */
+.xticks {
+	stroke-width: 1px;
+	stroke-dasharray:1px,2px;
+}
+/* Definition of specialities for y-axis */
+.yticks {
+	stroke-width: 1px;
+}
+/* Definition for crosshair cursor */
+.crosshair {
+	stroke: #bababa;
+	background: #fff;
+	stroke-width: 1px;
+	fill: #bababa;
+	font-size: 9px;
+}
+/* Definition of size etc. for the buttons */
+.buttons {
+	font-size: 24px;
+	fill: #555;
+}
+/* Definition for activated texts ('legend' and 'cursor') */
+.caption.active {
+	fill: #AA6900
+}
+/* Definition for caption texts ('legend' and 'cursor') */
+.caption {
+	font-weight: bold;
+	fill-opacity: 1;
+}
+.normal .caption {
+	font-size: 12px;
+}
+.small .caption {
+	font-size: 10px;
+}
+.big .caption {
+	font-size: 15px;
+}
+/* Definition for legend window */
+.legend {
+	stroke: #baabba;
+	stroke-width: 0;
+	fill: #aaa;
+	fill-opacity: 0.7;
+}
+.normal .legend {
+	font-size: 12px;
+}
+.small .legend {
+	font-size: 10px;
+}
+.big .legend {
+	font-size: 15px;
+}
+.text.legend {
+	fill-opacity: 1;
+}
+
+/* Definition for styles to be used for graphs (as alternative to the standard FHEM plot styles */
+.ftui.l0		{ stroke:#DDA400; stroke-width:2px; fill:none; }
+.ftui.l1		{ stroke:#BBBBBB; stroke-width:2px; fill:none; }
+.ftui.l2		{ stroke:#CC0000; stroke-width:2px; fill:none; }
+.ftui.l3		{ stroke:#CCCC00; stroke-width:2px; fill:none; }
+.ftui.l4		{ stroke:#33CC33; stroke-width:2px; fill:none; }
+.ftui.l5		{ stroke:#33CCCC; stroke-width:2px; fill:none; }
+.ftui.l6		{ stroke:#3333CC; stroke-width:2px; fill:none; }
+
+/* Normal lines */
+.ftui.l0sym		{ stroke:#DDA400; stroke-width:12px; fill:none; }
+.ftui.l1sym		{ stroke:#BBBBBB; stroke-width:12px; fill:none; }
+.ftui.l2sym		{ stroke:#CC0000; stroke-width:12px; fill:none; }
+.ftui.l3sym		{ stroke:#CCCC00; stroke-width:12px; fill:none; }
+.ftui.l4sym		{ stroke:#33CC33; stroke-width:12px; fill:none; }
+.ftui.l5sym		{ stroke:#33CCCC; stroke-width:12px; fill:none; }
+.ftui.l6sym		{ stroke:#3333CC; stroke-width:12px; fill:none; }
+
+/* icon lines */
+.ftui.l0icon	{ stroke:#DDA400; stroke-width:32px; fill:none; }
+.ftui.l1icon	{ stroke:#BBBBBB; stroke-width:32px; fill:none; }
+.ftui.l2icon	{ stroke:#CC0000; stroke-width:32px; fill:none; }
+.ftui.l3icon	{ stroke:#CCCC00; stroke-width:32px; fill:none; }
+.ftui.l4icon	{ stroke:#33CC33; stroke-width:32px; fill:none; }
+.ftui.l5icon	{ stroke:#33CCCC; stroke-width:32px; fill:none; }
+.ftui.l6icon	{ stroke:#3333CC; stroke-width:32px; fill:none; }
+
+/* Dashed lines */
+.ftui.l0dash	{ stroke:#DDA400; stroke-width:2px; fill:none; stroke-dasharray:5px,3px; }
+.ftui.l1dash	{ stroke:#BBBBBB; stroke-width:2px; fill:none; stroke-dasharray:5px,3px; }
+.ftui.l2dash	{ stroke:#CC0000; stroke-width:2px; fill:none; stroke-dasharray:5px,3px; }
+.ftui.l3dash	{ stroke:#CCCC00; stroke-width:2px; fill:none; stroke-dasharray:5px,3px; }
+.ftui.l4dash	{ stroke:#33CC33; stroke-width:2px; fill:none; stroke-dasharray:5px,3px; }
+.ftui.l5dash	{ stroke:#33CCCC; stroke-width:2px; fill:none; stroke-dasharray:5px,3px; }
+.ftui.l6dash	{ stroke:#3333CC; stroke-width:2px; fill:none; stroke-dasharray:5px,3px; }
+
+/* Dotted lines */
+.ftui.l0dot		{ stroke:#DDA400; stroke-width:2px; fill:none; stroke-dasharray:2px,2px; }
+.ftui.l1dot		{ stroke:#BBBBBB; stroke-width:2px; fill:none; stroke-dasharray:2px,2px; }
+.ftui.l2dot		{ stroke:#CC0000; stroke-width:2px; fill:none; stroke-dasharray:2px,2px; }
+.ftui.l3dot		{ stroke:#CCCC00; stroke-width:2px; fill:none; stroke-dasharray:2px,2px; }
+.ftui.l4dot		{ stroke:#33CC33; stroke-width:2px; fill:none; stroke-dasharray:2px,2px; }
+.ftui.l5dot		{ stroke:#33CCCC; stroke-width:2px; fill:none; stroke-dasharray:2px,2px; }
+.ftui.l6dot		{ stroke:#3333CC; stroke-width:2px; fill:none; stroke-dasharray:2px,2px; }
+
+/* Filled graphs */
+.ftui.l0fill		{ stroke:#DDA400; fill:url(#gr_ftui0); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l0fill_gyr	{ stroke:red; fill:url(#gr0_gyr); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l0fill_stripe	{ stroke:red; fill:url(#gr0_stripe); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l1fill		{ stroke:#BBBBBB; fill:url(#gr_ftui1); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l1fill_stripe	{ stroke:green; fill:url(#gr1_stripe); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l2fill		{ stroke:#CC0000; fill:url(#gr_ftui2); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l3fill		{ stroke:#CCCC00; fill:url(#gr_ftui3); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l4fill		{ stroke:#33CC33; fill:url(#gr_ftui4); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l5fill		{ stroke:#33CCCC; fill:url(#gr_ftui5); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l6fill		{ stroke:#3333CC; fill:url(#gr_ftui6); stroke-width:2px; fill-opacity:0.8 }
+.ftui.l6fill		{ stroke:#3333CC; fill:url(#gr_ftui6); stroke-width:2px; fill-opacity:0.8 }
+
+/* additional settings for the text (mainly for legend window) */
+text.ftui.l0, text.ftui.l0sym, text.ftui.l0dot, text.ftui.l0dash, text.ftui.l0fill { stroke:none; fill:#DDA400; } 
+text.ftui.l0fill_stripe, text.ftui.l0fill_gyr { stroke:none; fill:red; } 
+text.ftui.l1, text.ftui.l1sym, text.ftui.l1dot, text.ftui.l1dash, text.ftui.l1fill, text.ftui.l1fill_stripe { stroke:none; fill:#BBBBBB; } 
+text.ftui.l1fill_stripe { stroke:none; fill:green; } 
+text.ftui.l2, text.ftui.l2sym, text.ftui.l2dot, text.ftui.l2dash, text.ftui.l2fill { stroke:none; fill:#CC0000; }
+text.ftui.l3, text.ftui.l3sym, text.ftui.l3dot, text.ftui.l3dash, text.ftui.l3fill { stroke:none; fill:#CCCC00; }
+text.ftui.l4, text.ftui.l4sym, text.ftui.l4dot, text.ftui.l4dash, text.ftui.l4fill { stroke:none; fill:#33CC33; }
+text.ftui.l5, text.ftui.l5sym, text.ftui.l5dot, text.ftui.l5dash, text.ftui.l5fill { stroke:none; fill:#33CCCC; }
+text.ftui.l6, text.ftui.l6sym, text.ftui.l6dot, text.ftui.l6dash, text.ftui.l6fill { stroke:none; fill:#3333CC; }
+
+/* end of styles for chart widget */

+ 67 - 0
fhem/core/www/tablet_dev/css/ftui_checklist.css

@@ -0,0 +1,67 @@
+.check-list{
+    position: relative;
+    border-radius: 10px;
+    overflow: auto;
+    margin: 3px;
+    cursor: default !important;
+}
+.check-list .check{
+    position: relative;
+    display: block !important;
+    border-bottom: 1px solid #454545;
+    margin-right: 2%;
+    margin-left: 2%;
+    cursor: pointer;
+}
+.check-list .check:hover{
+    background-color: #333;
+}
+.check-list .check-image{
+    width: 15%;
+    position: relative;
+    display: inline-block;
+    text-align: left;
+}
+.check-list .check-text{
+    width: 85%;
+    position: relative;
+    display: inline-block;
+
+}
+.check-list .cover {
+    width: 46px;
+    height: 46px;
+}
+
+.check-list .test {
+    font-weight: normal;
+    position: relative;
+    left: 0px;
+    top: 0px;
+    text-align: left;
+}
+
+.check-list .duration {
+    position: relative;
+    right: 20px;
+    top: -10px;
+    text-align: right;
+}
+.check-list {
+    line-height: 2em;
+}
+.check-list .check-item {
+    margin: 2px;
+}
+.check-list .check-item.active {
+    background-color: #3a3a3a;
+}
+.check-list .check-item.active .icon:before {
+    content: "\f00c";
+}
+
+.check-list .check-item .icon:before {
+
+}
+
+

+ 63 - 0
fhem/core/www/tablet_dev/css/ftui_colorwheel.css

@@ -0,0 +1,63 @@
+.roundIndicator .colorIndicator{
+    position: relative;
+    height:50px;
+    width: 50px;
+    border-radius: 50%;
+    left:10%;
+}
+.roundIndicator.large .colorIndicator{
+    position: relative;
+    height:70px;
+    width: 70px;
+    border-radius: 50%;
+    left:10%;
+}
+.barIndicator  .colorIndicator{
+    position: relative;
+    width: 50px;
+    left: 5%;
+    height: 10px;
+    margin-top: 10px;
+}
+.lineIndicator  .colorIndicator{
+    position: relative;
+    width: 10px;
+    left: 10%;
+    height: 50px;
+    margin-top: 10px;
+}
+.noneIndicator .colorIndicator,.colorIndicator{
+    width: 0;
+    height: 0;
+}
+.colorArea {
+    margin: 5px;
+}
+.colorWheel {
+    position: relative;
+    vertical-align: middle;
+    display: inline-block;
+}
+.big .colorWheel {
+    top:-10px;
+}
+.farbtastic-mask,.farbtastic-overlay {
+    left:0;
+}
+
+.roundIndicator  .colorWheel{
+    top:-15px;
+}
+.barIndicator  .colorWheel{
+    top:-10px;
+}
+.lineIndicator  .colorWheel{
+    top:-50px;
+}
+.noneIndicator  .colorWheel{
+    top:5px;
+    left:20%;
+}
+
+
+

+ 36 - 0
fhem/core/www/tablet_dev/css/ftui_controlbutton.css

@@ -0,0 +1,36 @@
+[data-type="controlbutton"] {
+    width: 4em;
+    height: 4em;
+}
+
+.controlbutton-wrapper {
+    width: 100%;
+    height: 100%;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.controlbutton-wrapper input {
+    display: none;
+}
+
+.controlbutton-wrapper .controlbutton-area {
+    position: relative;
+    margin-left: auto;
+    margin-right: auto;
+    border-radius: 12px;
+    background-color: #404040;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+}
+
+.controlbutton-wrapper .controlbutton-icon {
+    width: 100%;
+    height: 50%;
+    line-height: 0.1em !important;
+    position: absolute;
+    font-size: 2.1em;
+    bottom: 0;
+    left: 0;
+}

+ 39 - 0
fhem/core/www/tablet_dev/css/ftui_controller.css

@@ -0,0 +1,39 @@
+.controller-wrapper {
+    width: 100%;
+    height: 100%;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.controller-area {
+    position: relative;
+    margin-left: auto;
+    margin-right: auto;
+    display: table;
+    border-radius: 12px;
+    background-color: #404040;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+}
+
+.controller-range {
+    position: absolute;
+
+    width: 100%;
+    left: 0;
+}
+
+.controller-range-full {
+    border-top-left-radius: 12px;
+    border-top-right-radius: 12px;
+}
+
+.controller-icon {
+    width: 100%;
+    height: 2em;
+    line-height: 2em !important;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+}

+ 182 - 0
fhem/core/www/tablet_dev/css/ftui_departure.css

@@ -0,0 +1,182 @@
+[data-type="departure"] {
+
+}
+
+.departure{
+    position: relative;
+    border-radius: 10px;
+    margin: 3px;
+    cursor: default !important;
+}
+.departure .icon {
+    position: absolute;
+    top: 6px;
+    left: 6px;
+    width: 28px;
+    padding-top: 0px;
+    color: #3F862A;
+    background-color: 'transparent';
+    border-width: 4px;
+    border-radius: 50%;
+    border-color: #3F862A;
+    border-style: solid;
+    height: 30px;
+    font-size: 16px;
+    font-weight: bold;
+}
+.departure.big .icon {
+    top: 8px;
+    left: 16px;
+    width: 45px;
+    height: 45px;
+    font-size: 28px;
+    border-width: 5px;
+}
+.departure  .icon.fa{
+    padding-bottom: 0px;
+    border-width: 0px;
+    font-size: 20px;
+    top: 13px;
+}
+.departure .time{
+    color: #222;
+    position: absolute;
+    right: 10px;
+    bottom: 5px;
+}
+.departure .refresh{
+    color: #222;
+    position: absolute;
+    left: 10px;
+    bottom: 5px;
+    cursor: pointer;
+}
+.departure .station{
+    color: #222;
+    position: absolute;
+    left: 33px;
+    top: 10px;
+    width: 80%;
+    text-align: center;
+    font-size: 18px;
+    font-weight: bold;
+}
+.departure.big .station{
+    font-size: 31px;
+ }
+.departure .header{
+    color: #222;
+    margin-top: 20px;
+}
+.departure.big .header{
+    font-size: 23px;
+    margin-top: 24px;
+}
+.departure .header .destination{
+    width: 52%;
+}
+.departure .header .minutes{
+    width: 20%;
+}
+.departure .minutes{
+    display: inline-block;
+    width: 17%;
+    text-align: end;
+}
+.departure .connection{
+}
+.departure .connection.even{
+    background: rgba(0, 0, 0, 0.5);
+}
+.departure  .line{
+    display: inline-block;
+    width: 15%;
+    text-align: end;
+    padding-right: 3px;
+}
+.departure .destination{
+    display: inline-block;
+    width: 64%;
+    text-align: initial;
+    padding-left: 3px;
+}
+.departure .minutes{
+    display: inline-block;
+    width: 17%;
+    text-align: end;
+}
+.departure .listText {
+    position: absolute;
+    top: 54px;
+    left: 8px;
+    width: 92%;
+    height: calc(100% - 77px);
+    background-color: #484848;
+    padding-bottom: 3px;
+    padding-top: 3px;
+    overflow: hidden;
+}
+.departure.big .listText {
+    top: 17%;
+    left: 4%;
+    height: 74%
+}
+
+.departure.large .listText {
+    top: 19% !important;
+    left: 4% !important;
+    height: 72% !important;
+}
+
+.departure.large .header{
+    font-size: 23px !important;
+    margin-top: 24px !important;
+}
+
+.departure.large .station{
+    font-size: 31px !important;
+ }
+
+.departure.large .icon {
+    top: 16px !important;
+    left: 16px !important;
+    width: 45px !important;
+    height: 45px !important;
+    font-size: 32px !important;
+}
+
+.DVB .departure{
+    color: #EFC822 !important;
+    background-color: #CCAE20 !important;
+}
+.DVB .departure .icon {
+    color: #3F862A !important;
+}
+.DVB .departure .listText {
+    background-color: #222;
+    color: #EFC822;
+}
+
+.VVO .departure {
+    background-color: #1765BF !important;
+}
+.VVO .departure .icon {
+    color: #3F862A !important;
+    background-color: #CCAE20 !important;
+}
+.VVO .departure .listText {
+    background-color: #2A2A5C;
+}
+
+.DB .departure {
+    background-color: #FF0100 !important;
+}
+.DB .departure .icon {
+    color: #000000 !important;
+    background-color: transparent !important;
+}
+.DB .departure .listText {
+    background-color: #FFFFFF !important;
+        color: #000000 !important;
+}
+

+ 59 - 0
fhem/core/www/tablet_dev/css/ftui_medialist.css

@@ -0,0 +1,59 @@
+.media-list{
+    position: relative;
+    border-radius: 10px;
+    overflow: auto;
+    margin: 3px;
+    cursor: default !important;
+}
+.media-list .media{
+    position: relative;
+    display: block !important;
+    border-bottom: 1px solid #454545;
+    margin-right: 2%;
+    margin-left: 2%;
+    cursor: pointer;
+}
+.media-list .media:hover{
+    background-color: #333;
+}
+.media-list .media-image{
+    width: 15%;
+    position: relative;
+    display: inline-block;
+    text-align: left;
+}
+.media-list .media-text{
+    width: 85%;
+    position: relative;
+    display: inline-block;
+
+}
+.media-list .cover {
+    width: 46px;
+    height: 46px;
+}
+.media-list .title {
+    font-weight: bold;
+    font-size: larger;
+    position: relative;
+    left: 0px;
+    top: 10px;
+    text-align: left;
+}
+.media-list .artist {
+    font-weight: normal;
+    position: relative;
+    left: 0px;
+    top: 10px;
+    text-align: left;
+}
+
+.media-list .duration {
+    position: relative;
+    right: 20px;
+    top: -10px;
+    text-align: right;
+}
+.media-list .media.current .media-text *{
+    color: #aa6900;
+}

+ 25 - 0
fhem/core/www/tablet_dev/css/ftui_range.css

@@ -0,0 +1,25 @@
+.levelArea {
+    position: relative;
+    margin-left:auto;
+    margin-right:auto;
+    display:table;
+    border-radius: 15px;
+    background-color: #404040;
+}
+.levelRange {
+    position: absolute;
+    border-radius: 15px;
+    left: 0;
+}
+.labelMax,.labelMin,.labelLimitMin,.labelLimitMax{
+    position: absolute;
+    left: -34px;
+    text-align: right;
+    width: 30px;
+    color: #888;
+}
+.labelMin{ bottom:-6px; }
+.labelMax{ top:-9.4px; }
+
+
+

+ 46 - 0
fhem/core/www/tablet_dev/css/ftui_slideout.css

@@ -0,0 +1,46 @@
+  .slideout-menu {
+    position: fixed;
+    left: 0;
+    top: 0;
+    bottom: 0;
+    right: 0;
+    z-index: 0;
+    width: 256px;
+    overflow-y: auto;
+    -webkit-overflow-scrolling: touch;
+    display: none;
+  }
+
+  .slideout-panel {
+
+  }
+
+  .slideout-open,
+  .slideout-open body,
+  .slideout-open .slideout-panel {
+    overflow: hidden;
+  }
+
+  .slideout-open .slideout-menu {
+    display: block;
+  }
+
+  .fixed.open-left {
+    -webkit-transform: translate3d(256px, 0px, 0px);
+    -moz-transform: translate3d(256px, 0px, 0px);
+    -o-transform: translate3d(256px, 0px, 0px);
+    -ms-transform: translate3d(256px, 0px, 0px);
+    transform: translate3d(256px, 0px, 0px);
+  }
+
+.fixed.open-right {
+  -webkit-transform: translate3d(-256px, 0px, 0px);
+  -moz-transform: translate3d(-256px, 0px, 0px);
+  -o-transform: translate3d(-256px, 0px, 0px);
+  -ms-transform: translate3d(-256px, 0px, 0px);
+  transform: translate3d(-256px, 0px, 0px);
+}
+
+[data-type="slideout"] > .icon{
+
+}

+ 44 - 0
fhem/core/www/tablet_dev/css/ftui_slider.css

@@ -0,0 +1,44 @@
+.range-container.slider_vertical.narrow,
+.range-container.level_vertical.narrow {
+    margin: 10px 0px 0px -5px !important;
+}
+
+.range-container.slider_horizontal.narrow,
+.range-container.level_horizontal.narrow {
+    padding-bottom: 5px;
+}
+
+.range-min,
+.range-max {
+    visibility: hidden;
+    width: 0px !important;
+    margin-right: 40px;
+}
+
+.slider_vertical .slidertext,
+.level_vertical .slidertext {
+    margin-left: -18px;
+    margin-top: 10px;
+    width: 40px;
+    text-align: center;
+}
+
+.level_vertical .slider-wrapper,
+.slider_vertical .slider-wrapper {
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+.big > .vertical .range-bar {
+    width: 1em;
+}
+
+.bigger > .vertical .range-bar {
+    width: 25px;
+}
+
+.large > .vertical .range-bar {
+    width: 50px;
+}

+ 79 - 0
fhem/core/www/tablet_dev/css/ftui_spinner.css

@@ -0,0 +1,79 @@
+.spinner{
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    position: relative;
+    border-radius: 30px;
+    margin: 3px;
+    cursor: default !important;
+}
+.spinner > .lefticon {
+    display: inline-block;
+    vertical-align: middle;
+    font-size: 40px;
+    cursor: pointer;
+}
+.spinner > .righticon {
+    display: inline-block;
+    vertical-align: middle;
+    font-size: 40px;
+    cursor: pointer;
+}
+.spinner.plain > .lefticon {
+    font-size: 30px;
+    font-weight: bold
+}
+.spinner.plain > .righticon {
+    font-size: 30px;
+    font-weight: bold
+}
+.spinner > .lefticon.fa,.spinner > .righticon.fa {
+    padding-bottom: 0px;
+    font-size: 20px;
+}
+
+.spinner > .levelArea {
+    position: relative;
+    margin-left:12px;
+    margin-right:12px;
+    display: inline-block;
+    border-radius: 5px;
+    background-color: #313131;
+    height: 7px;
+}
+.spinner.plain > .levelArea {
+    margin-left:2px;
+    margin-right:2px;
+    visibility: hidden;
+}
+
+.spinner .levelRange {
+    position: absolute;
+    border-radius: 5px;
+    height: 7px;
+}
+.spinner.valueonly > .levelArea {
+    visibility: hidden;
+}
+.spinner.value > .spinnerText {
+    position: absolute;
+    top: -24%;
+    left: 25%;
+}
+.spinner.valueonly > .spinnerText,
+.spinner.plain > .spinnerText{
+    position: absolute;
+    top: 0.1em;
+    left: 23%;
+    font-size: 30px;
+    font-weight: bold;
+    font-family: robotothin,sans-serif;
+}
+
+.spinner.plain > .spinnerText {
+    font-size: 18px;
+    top: 0.5em;
+}

+ 239 - 0
fhem/core/www/tablet_dev/css/ftui_weekprofile.css

@@ -0,0 +1,239 @@
+/* weekprofile_widget default CSS */
+.weekprofile {
+    z-index: 1000;
+	position: fixed;
+}
+.weekprofile_dialog {
+    height: 150px;
+    overflow: auto;
+    width: auto;
+}
+
+.weekprofile_profile{
+/* Formatierungstest		*/	
+	padding-left: 10px;
+/* Formatierungstest		*/
+    padding: 5px;
+	border-style: solid;
+	border-radius: 10px;
+	border-width: 2px;
+	border-color: grey;
+}
+
+.weekprofile_profileweekdays{
+/* Formatierungstest		*/	
+	padding-left: 6px;
+/* Formatierungstest		*/
+	
+}
+.weekprofile_profile.error {
+    background-color: #cd3700 !important; 
+}
+
+.weekprofile_lines{
+/* Formatierungstest		*/
+	margin: 0 auto;
+
+	text-align: left;
+/* Formatierungstest		*/
+/*    padding-left: 46px;	*/
+}
+
+.weekprofile_button {
+    padding: 0.5em;
+    margin-left: 0.3em;
+    text-decoration: none;
+    border: none;
+    font-weight: normal;
+    color: inherit;
+}
+.weekprofile_deleteprofile {
+    font-size: 15px;
+    height:35px;
+    width:35px;    
+}
+
+.weekprofile_delprofile {
+	/* Formatierungstest		*/
+	/*padding-left: 44px; */
+	/* Formatierungstest		*/
+}
+
+.weekprofile_addline {
+    font-size: 15px;
+    height:35px;
+    width:35px;    
+	
+}.weekprofile_addlinediv {
+	padding-left: 3px;
+    padding-right: 4px; 
+	vertical-align: middle;
+}
+
+.weekprofile_deleteline {
+    font-size: 15px;
+    height:35px;
+    width:35px;    
+	
+}.weekprofile_deletelinediv {
+    vertical-align: middle;	
+}
+
+.weekprofile_profiletime {
+    width: auto !important; 
+    margin: 4px 2.5px !important;
+    vertical-align: middle;
+    text-align: center;
+}
+.weekprofile_profilecmd{
+    width: auto !important; 
+    margin: 4px 2.5px !important;
+    vertical-align: middle;
+    text-align: center;
+	padding-left: 13px;
+}
+.weekprofile_time{ 
+    border: none;
+    font-size: small;
+    width: 50px !important;    
+    height: 35px;  
+	padding: 0px;
+	/* margin-top: 2px; */
+	text-align: center; 
+}
+
+.weekprofile_starttime{ 
+    border: none;
+    font-size: small;
+    width: 50px !important;    
+    height: 35px;    
+	margin-top: 2px;
+	display: table-cell;
+	vertical-align: middle; 
+}
+
+.weekprofile_cmd {
+	/* Formatierungstest		*/
+	text-align: right;
+	padding-right: 1em !important;
+	width: 55px !important;
+	/* Formatierungstest		*/
+    height: 35px;    
+    border: none;
+	/* margin-top: 2px; */
+}
+
+.weekprofile_checkbox {
+	position: relative;
+    display: inherit;       
+    padding: 0em;
+    border: none;
+	top: -5px;
+    margin-left: -4px;        
+    height:35px;
+    line-height: 1.6em;
+    width:35px;
+}
+  
+.weekprofile_checkbox label {
+	cursor: pointer;
+	position: absolute;
+	left: 7px;
+	top: 7px;
+}
+.weekprofile_checkbox label:after {
+	opacity: 0;
+	content: '';
+	position: absolute;
+	top: -7px;
+	left: -7px;
+	width: 35px;
+	height: 35px;    
+}
+.weekprofile_checkbox input[type=radio]:checked + label:after {
+	opacity: 0.5;
+}
+ 
+.weekprofile_header {
+    text-align: center; 
+    width: 100%;
+ 	padding-bottom: 5px;
+	padding-top: 5px;
+    font-size: 1.4em;    
+}
+.weekprofile_header_icon {
+    position: absolute;
+    left: 8px;
+    font-size: 1.25em !important;
+}
+
+.weekprofile_footer {
+    width: 100%;
+    float: right;
+	padding-bottom: 5px;
+	padding-top: 5px;
+}
+.weekprofile_footer .ui-dialog-buttonset{
+    float: inherit;
+}
+
+.weekprofile_active {
+    float:left;
+    display: inline-block;
+    margin: 6px;
+}
+
+.weekprofile_shader {
+    position:absolute;
+    background-color:#000000;
+    opacity:0.5;
+    height:100%;
+    width:100%;
+    top:0px;
+    left:0px;    
+}
+
+/* ---------------------------------------------------- */
+/* weekprofile_widget Style "round" */
+.weekprofile_button.round { border-radius: 10px; }        
+.weekprofile_header.round { border-top-left-radius: 10px; border-top-right-radius: 10px; }
+.weekprofile_footer.round { border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; }
+.weekprofile_checkbox.begin.round { border-bottom-left-radius: 10px; border-top-left-radius: 10px; }  
+.weekprofile_checkbox.end.round { border-bottom-right-radius: 10px; border-top-right-radius: 10px; }   
+.weekprofile_checkbox.round input[type=radio]:checked + label.begin::after { border-bottom-left-radius: 10px; border-top-left-radius: 10px; }    
+.weekprofile_checkbox.round input[type=radio]:checked + label.end::after { border-bottom-right-radius: 10px; border-top-right-radius: 10px; } 
+.weekprofile_time.round { border-radius: 10px; }
+.weekprofile_starttime.round { border-radius: 10px; }
+/* ---------------------------------------------------- */
+/* weekprofile_widget Style "square" */
+.weekprofile_cmd.square { border-radius: unset; }
+/* ---------------------------------------------------- */
+/* weekprofile_widget Theam "dark" */
+.weekprofile_button.dark:hover, .weekprofile_time.dark:hover, .weekprofile_starttime.dark:hover, .weekprofile_cmd.dark:hover, .weekprofile_checkbox.dark:hover { background-color: #007fff; }
+.weekprofile_button.dark { color: #d1eeee; background-color: #393939; }
+.weekprofile_time.dark { background-color: #393939; color: #d1eeee; }
+.weekprofile_starttime.dark { background-color: #393939; color: #d1eeee; }
+.weekprofile_datetimepicker.dark { background-color: #393939 !important; color: #d1eeee !important; }
+.weekprofile_cmd.dark { background-color: #393939; color: #d1eeee; }
+.weekprofile_checkbox.dark { background-color: #393939; color: #d1eeee; }
+.weekprofile_checkbox.dark label:after { background-color: #cc5500; }
+.weekprofile_dialog.dark{ background-color: #2a2a2a; }
+.weekprofile_header.dark { background-color: #262626; color: #d1eeee; }
+.weekprofile_footer.dark { background-color: #262626; }
+/* ---------------------------------------------------- */
+/* weekprofile_widget Theam "light" */
+.weekprofile_button.light:hover, .weekprofile_time.light:hover, .weekprofile_starttime.light:hover, .weekprofile_cmd.light:hover, .weekprofile_checkbox.light:hover { background-color: #007fff; }
+.weekprofile_button.light { color: #000000; background-color: #e3c7c7; border: 1px solid #e0e0e0; }
+.weekprofile_time.light { background-color: #e3c7c7; color: #000000;  border: 1px solid #e0e0e0; }
+.weekprofile_starttime.light { background-color: #e3c7c7; color: #000000;  border: 1px solid #e0e0e0; }
+.weekprofile_datetimepicker.light { background-color: #e3c7c7 !important; color: #000000 !important; border: 1px solid #e0e0e0; }
+.weekprofile_cmd.light { background-color: #e3c7c7; color: #000000; border: 1px solid #e0e0e0; }
+.weekprofile_checkbox.light { background-color: #e3c7c7; color: #000000;  border: 1px solid #e0e0e0; }
+.weekprofile_checkbox.light label:after { background-color: #cc5500; }
+
+.weekprofile_dialog.light{ background-color: #ffffff; }
+.weekprofile_header.light { background-color: #ffffff; color: #000000; border: 1px solid #e0e0e0; }
+.weekprofile_footer.light { background-color: #ffffff; border: 1px solid #e0e0e0; }
+/* ---------------------------------------------------- */
+/* weekprofile_widget Theam "custom" */
+/* ---------------------------------------------------- */

+ 95 - 0
fhem/core/www/tablet_dev/demo_ftui.html

@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <!--
+     /* FHEM tablet ui */
+     /*
+     * UI builder framework for FHEM
+     *
+     * Version: 2.2.*
+     * URL: https://github.com/knowthelist/fhem-tablet-ui
+     *
+     * Copyright (c) 2015-2016 Mario Stephan <mstephan@shared-files.de>
+     * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+     *
+     * !!!! Evaluation version - run only in a staging enviroment !!!!
+     *
+     * - create a new folder named 'tablet_eval' in /<fhem-path>/www
+     * - copy all files incl. sub folders into /<fhem-path>/www/tablet_eval
+     * - add 'define TABLETUIEVAL HTTPSRV ftui_eval ./www/tablet_eval Tablet-EVAL' in fhem.cfg
+     * - Tadaaa! A new fhem ui in http://<fhem-url>:8083/fhem/tablet_eval/
+     *
+     * Create a Demo Device in FHEM
+     * define ftuitest dummy
+     */
+    -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="widget_base_width" content="74">
+    <meta name="widget_base_height" content="71">
+    <meta name="mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="gridster_disable" content="1">
+    <meta name="longpoll" content="1"> <!-- 1=longpoll;0=shortpoll every 30sec -->
+    <meta name="debug" content="2"> <!-- verbose level 1-6 = output to console;0 = not output -->
+    <meta http-equiv="Cache-Control" content="no-store" />
+
+    <script src="js/fhem-tablet-ui.js" defer></script>
+
+    <title>FTUI Demo</title>
+</head>
+<body>
+<div class="gridster">
+<ul>
+  <li data-row="1" data-col="1" data-sizex="3" data-sizey="4">
+    <header>EXAMPLE1</header>
+
+    <div class="top-space gray">STATE of the device</div>
+    <div data-type="label" data-device="ftuitest" class="big bold thin"></div>
+
+    <div class="top-space gray">STATE of the device colorized</div>
+    <div data-type="label" data-device="ftuitest" data-colors='["red","green"]' data-limits='["on","off"]' class="big bold thin"></div>
+
+    <div class="top-space gray">Timestamp of the reading</div>
+    <div data-type="label" data-device="ftuitest" data-get="state" class="large thin timestamp"></div>
+
+</li>
+<li data-row="5" data-col="1" data-sizex="3" data-sizey="3">
+  <header>EXAMPLE2</header>
+  <!-- place your widget here -->
+</li>
+<li data-row="1" data-col="4" data-sizex="6" data-sizey="4">
+   <header>EXAMPLE3</header>
+   <!-- place your widget here -->
+
+    <div class="row-10 bottomcenter gray">Switch on/off</div>
+    <div data-type="switch" data-device="ftuitest" class="row-20"></div>
+
+    <div class="row-10 bottomcenter gray">Switch on/off blue square</div>
+    <div data-type="switch" data-device="ftuitest" data-background-icon="fa-square" class="row-20 blue"></div>
+
+    <div class="row-10 bottomcenter gray">Botton Group</div>
+    <div class="row-20">
+    <div data-type="switch" data-device="ftuitest" data-background-icon="fa-square-o" data-icon="" data-get-on="0|off" data-get-off="!on" data-set-on="0" class="inline small green"><span class="small gray top">0</span></div>
+    <div data-type="switch" data-device="ftuitest" data-background-icon="fa-square-o" data-icon="" data-get-on="[123][0-9]" data-get-off="!on" data-set-on="25" class="inline small left-narrow-10 green"><span class="small gray top">25</span></div>
+    <div data-type="switch" data-device="ftuitest" data-background-icon="fa-square-o" data-icon="" data-get-on="[456][0-9]" data-get-off="!on" data-set-on="50" class="inline small left-narrow-10 green"><span class="small gray top">50</span></div>
+    <div data-type="switch" data-device="ftuitest" data-background-icon="fa-square-o" data-icon="" data-get-on="[789][0-9]" data-get-off="!on" data-set-on="75" class="inline small left-narrow-10 green"><span class="small gray top">75</span></div>
+    <div data-type="switch" data-device="ftuitest" data-background-icon="fa-square-o" data-icon="" data-get-on="100|on" data-get-off="!on" data-set-on="100" class="inline small left-narrow-10 green"><span class="small gray top">100</span></div>
+</div>
+</li>
+<li data-row="1" data-col="10" data-sizex="3" data-sizey="4">
+    <header>EXAMPLE4</header>
+    <!-- place your widget here -->
+    <div data-type="range" data-device="ftuitest" data-min="0" data-max="100" data-limit-low="25" data-limit-high="75" class="top-space"></div>
+</li>
+<li data-row="5" data-col="10" data-sizex="3" data-sizey="3">
+  <header>EXAMPLE5</header>
+  <!-- place your widget here -->
+</li>
+<li data-row="5" data-col="4" data-sizex="6" data-sizey="3">
+  <header>EXAMPLE6</header>
+  <!-- place your widget here -->
+</li>
+</ul>
+</div>
+</body>
+</html>

File diff suppressed because it is too large
+ 29 - 0
fhem/core/www/tablet_dev/fonts/FTUI-icons.svg


BIN
fhem/core/www/tablet_dev/fonts/FTUI-icons.ttf


BIN
fhem/core/www/tablet_dev/fonts/FTUI-icons.woff


BIN
fhem/core/www/tablet_dev/fonts/FontAwesome.otf


BIN
fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.eot


File diff suppressed because it is too large
+ 1 - 0
fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.ijmap


File diff suppressed because it is too large
+ 2373 - 0
fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.svg


BIN
fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.ttf


BIN
fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.woff


BIN
fhem/core/www/tablet_dev/fonts/MaterialIcons-Regular.woff2


+ 203 - 0
fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont-License.txt

@@ -0,0 +1,203 @@
+Font data copyright Google 2012
+
+                                Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

BIN
fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont.eot


File diff suppressed because it is too large
+ 631 - 0
fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont.svg


BIN
fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont.ttf


BIN
fhem/core/www/tablet_dev/fonts/Roboto-Thin-webfont.woff


BIN
fhem/core/www/tablet_dev/fonts/fhemSVG.eot


File diff suppressed because it is too large
+ 964 - 0
fhem/core/www/tablet_dev/fonts/fhemSVG.svg


BIN
fhem/core/www/tablet_dev/fonts/fhemSVG.ttf


BIN
fhem/core/www/tablet_dev/fonts/fhemSVG.woff


+ 3 - 0
fhem/core/www/tablet_dev/fonts/fontawesome-License.txt

@@ -0,0 +1,3 @@
+Font Awesome by Dave Gandy - http://fontawesome.io
+
+https://fortawesome.github.io/Font-Awesome/license/

BIN
fhem/core/www/tablet_dev/fonts/fontawesome-webfont.eot


File diff suppressed because it is too large
+ 2671 - 0
fhem/core/www/tablet_dev/fonts/fontawesome-webfont.svg


BIN
fhem/core/www/tablet_dev/fonts/fontawesome-webfont.ttf


BIN
fhem/core/www/tablet_dev/fonts/fontawesome-webfont.woff


BIN
fhem/core/www/tablet_dev/fonts/fontawesome-webfont.woff2


+ 5 - 0
fhem/core/www/tablet_dev/fonts/meteocons-License.txt

@@ -0,0 +1,5 @@
+   Copyright (C) 2012 by Alessio Atzeni
+
+   Author:    Alessio Atzeni
+   License:   SIL (http://scripts.sil.org/OFL)
+   Homepage:  http://www.alessioatzeni.com

BIN
fhem/core/www/tablet_dev/fonts/meteocons-webfont.eot


File diff suppressed because it is too large
+ 81 - 0
fhem/core/www/tablet_dev/fonts/meteocons-webfont.svg


BIN
fhem/core/www/tablet_dev/fonts/meteocons-webfont.ttf


BIN
fhem/core/www/tablet_dev/fonts/meteocons-webfont.woff


BIN
fhem/core/www/tablet_dev/fonts/openautomation.eot


File diff suppressed because it is too large
+ 448 - 0
fhem/core/www/tablet_dev/fonts/openautomation.svg


BIN
fhem/core/www/tablet_dev/fonts/openautomation.ttf


BIN
fhem/core/www/tablet_dev/fonts/openautomation.woff


BIN
fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.eot


File diff suppressed because it is too large
+ 257 - 0
fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.svg


BIN
fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.ttf


BIN
fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.woff


BIN
fhem/core/www/tablet_dev/fonts/weathericons-regular-webfont.woff2


+ 98 - 0
fhem/core/www/tablet_dev/ftui_snippet_tester.html

@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <!--
+     /* FHEM tablet ui */
+     /*
+     * UI builder framework for FHEM
+     *
+     * Version: 2.2.*
+     * URL: https://github.com/knowthelist/fhem-tablet-ui
+     *
+     * Copyright (c) 2015-2016 Mario Stephan <mstephan@shared-files.de>
+     * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+     *
+     * - create a new folder named 'tablet' in /<fhem-path>/www
+     * - copy all files incl. sub folders into /<fhem-path>/www/tablet
+     * - add 'define TABLETUI HTTPSRV ftui ./www/tablet Tablet' in fhem.cfg
+     * - Tadaaa! A new fhem ui in http://<fhem-url>:8083/fhem/tablet/
+     */
+    -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="widget_base_width" content="74">
+    <meta name="widget_base_height" content="71">
+    <meta name="mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="gridster_disable" content="1">
+    <meta name="longpoll" content="1"> <!-- 1=longpoll;0=shortpoll every 30sec -->
+    <meta name="debug" content="4"> <!-- verbose level 1-6 = output to console;0 = not output -->
+
+    <link rel="stylesheet" href="lib/jquery.gridster.min.css" />
+    <link rel="stylesheet" href="css/fhem-tablet-ui.css" />
+    <link rel="stylesheet" href="lib/font-awesome.min.css" />
+    <link rel="stylesheet" href="lib/jquery.toast.min.css" />
+
+    <script src="../pgm2/jquery.min.js"></script>
+    <script src="lib/jquery.toast.min.js"></script>
+    <script src="lib/jquery.gridster.min.js"></script>
+    <script src="js/fhem-tablet-ui.js" defer></script>
+
+    <title>FHEM-Tablet-UI</title>
+
+<style>
+
+    .result-container {
+        position: relative;
+    }
+    .result-container > header{
+        position: absolute;
+        top: 0px;
+    }
+
+</style>
+<script>
+    $(document).ready(function(){
+
+        var old = console.log;
+        var logger = document.getElementById('log');
+        console.log = function (message) {
+            if (typeof message == 'object') {
+                logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(message) : message) + '<br />';
+            } else {
+                logger.innerHTML += message + '<br />';
+            }
+            $(logger).scrollTop($(logger).last()[0].scrollHeight);
+        }
+
+        $("button").click(function(){
+            $("#result").html($("#snippet").val());
+            ftui.initWidgets("#result");
+        });
+    });
+</script>
+
+</head>
+<body>
+    <div class="gridster">
+    <ul>
+
+    <li data-row="1" data-col="1" data-sizex="6" data-sizey="4">
+       <header>SNIPPET</header>
+       <textarea id="snippet" name="Text1" cols="45" rows="15" class="top-space-2x"></textarea>
+       <button id="submit" type="submit" name="Submit" value="Init" class="left-space">Init</button>
+    </li>
+
+    <li data-row="1" data-col="7" data-sizex="6" data-sizey="4" class="result-container">
+        <header>RESULT</header>
+        <div id="result" class="squareborder center"></div>
+    </li>
+
+    <li data-row="5" data-col="1" data-sizex="12" data-sizey="3">
+      <header>LOG</header>
+      <div id="log" class="events"></div>
+    </li>
+
+    </ul>
+    </div>
+</body>
+</html>

File diff suppressed because it is too large
+ 3126 - 0
fhem/core/www/tablet_dev/icons_table.html


BIN
fhem/core/www/tablet_dev/images/blackboard_1280x800.jpg


BIN
fhem/core/www/tablet_dev/images/myfhemfavico_1024x1024.png


BIN
fhem/core/www/tablet_dev/images/myfhemfavico_120x120.png


BIN
fhem/core/www/tablet_dev/images/myfhemfavico_144x144.png


BIN
fhem/core/www/tablet_dev/images/myfhemfavico_152x152.png


BIN
fhem/core/www/tablet_dev/images/myfhemfavico_167x167.png


BIN
fhem/core/www/tablet_dev/images/myfhemfavico_180x180.png


BIN
fhem/core/www/tablet_dev/images/myfhemfavico_192x192.png


BIN
fhem/core/www/tablet_dev/images/myfhemfavico_512x512.png


BIN
fhem/core/www/tablet_dev/images/vista-wallpaper1920x1080.jpg


+ 298 - 0
fhem/core/www/tablet_dev/index-example.html

@@ -0,0 +1,298 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <!--
+     /* FHEM tablet ui */
+     /*
+     * UI builder framework for FHEM
+     *
+     * Version: 2.5.*
+     * URL: https://github.com/knowthelist/fhem-tablet-ui
+     *
+     * Copyright (c) 2015-2017 Mario Stephan <mstephan@shared-files.de>
+     * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+     *
+     * - create a new folder named 'tablet' in /<fhem-path>/www
+     * - copy all files incl. sub folders into /<fhem-path>/www/tablet
+     * - add 'define TABLETUI HTTPSRV ftui ./www/tablet Tablet' in fhem.cfg
+     * - Tadaaa! A new fhem ui in http://<fhem-url>:8083/fhem/tablet/
+     */
+    -->
+    <link rel="icon" href="favicon.ico" type="image/x-icon" />
+
+    <!-- define your personal style here, it wont be overwritten  -->
+    <!-- link rel="stylesheet" href="css/fhem-green-ui.css" / -->
+    <!-- link rel="stylesheet" href="css/fhem-tablet-ui-user.css" / -->
+
+    <script src="js/fhem-tablet-ui.js" defer></script>
+
+
+    <!-- Remove this line to enable for usage with WebViewControl
+    <script defer>var wvcDevices = {'12345': 'Tablet'}; var wvcUserCssFile="webviewcontrol.css"</script>
+    <script src="../pgm2/cordova-2.3.0.js" defer></script>
+    <script src="../pgm2/webviewcontrol.js" defer></script>
+    <!-- End for WebViewControl -->
+
+    <title>FHEM-Tablet-UI</title>
+</head>
+
+<body>
+
+    <div class="gridster">
+        <ul>
+            <li data-row="1" data-col="1" data-sizey="1" data-sizex="1">
+                <header>TREND</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="symbol" data-device="dummy1" data-icons='["fa-arrow-up","fa-arrow-right","fa-arrow-down"]' data-on-colors='["#32a054","#6666cc","#ad3333"]' data-get-on='["Wert1","Wert2","Wert3"]' class=""></div>
+                            <div data-type="label" class="small narrow darker">Status</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="2" data-col="1" data-sizex="1" data-sizey="4">
+                <header>GARTEN</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="switch" data-device="GartenLicht" class=""></div>
+                            <div data-type="label" class="">Licht</div>
+                            <div data-type="symbol" data-device="Eingangstuer" class="big"></div>
+                            <div data-type="label" class="">T&#252;r</div>
+                            <div data-type="label" data-device="OutTemp" data-part="2" data-fix="1" data-limits='[-73,10,23]' data-colors='["#6699FF","#AA6900","#FF0000"]' data-unit="%B0C%0A" class="bigger thin top-space-2x"></div>
+                            <div data-type="label" class="">Temperatur</div>
+                            <div data-type="label" class=" darker top-space">Heute</div>
+                            <div data-type="weather" data-device="AgroWeather" data-get="fc0_weatherDay" class=" big"></div>
+                            <div data-type="label" data-device="AgroWeather" data-get="fc0_weatherDay" class="narrow"></div>
+                            <div data-type="label" data-device="AgroWeather" data-get="fc0_tempMax" data-unit="%B0C%0A" class="large"></div>
+                            <div data-type="label" class="darker top-space">Morgen</div>
+                            <div data-type="weather" data-device="AgroWeather" data-get="fc1_weatherDay" class="big"></div>
+                            <div data-type="label" data-device="AgroWeather" data-get="fc1_weatherDay" class="narrow"></div>
+                            <div data-type="label" data-device="AgroWeather" data-get="fc1_tempMax" data-unit="%B0C%0A" class="large"></div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="1" data-col="2" data-sizey="1" data-sizex="2">
+                <header>BAD</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="BadHeizung_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div class="big" data-type="symbol" data-device="BadFenster"></div>
+                            <div data-type="label" class="top-narrow  darker small">Fenster</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="1" data-col="3" data-sizey="1" data-sizex="2">
+                <header>SCHLAFZIMMER</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="SchlafzimmerHeizung_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div class="big" data-type="symbol" data-device="SchlafzimmerFenster"></div>
+                            <div data-type="label" class="top-narrow darker small">Fenster</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="1" data-col="4" data-sizey="1" data-sizex="2">
+                <header>KINDERZIMMER</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="KinderzimmerHeizung_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div class="big" data-type="symbol" data-device="KinderzimmerFenster"></div>
+                            <div class="top-narrow  darker small">Fenster</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="1" data-col="8" data-sizey="1" data-sizex="1">
+                <header>GALERIE</header>
+                <div data-type="switch" data-device="GalerieLicht" class="cell"></div>
+                <div data-type="label" class="cell">Licht</div>
+            </li>
+            <li data-row="2" data-col="2" data-sizey="2" data-sizex="2">
+                <header>KUECHE</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="KuecheHeizung_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div data-type="switch" data-device="HerdLicht_Sw"></div>
+                            <div data-type="label">HerdLicht</div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="KuecheHeizung2_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div class="big" data-type="symbol" data-device="KuechenFenster"></div>
+                            <div class="top-narrow  darker small">Fenster</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="4" data-col="2" data-sizey="2" data-sizex="2">
+                <header>HOMESTATUS</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div data-type="homestatus" data-device='HomeStatus' data-version='' class="cell"></div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="2" data-col="4" data-sizey="2" data-sizex="4">
+                <header>WOHNZIMMER</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="WohnzimmerHeizung_Clima" data-valve="ValvePosition" data-get="desired-temp" data-temp="measured-temp"></div>
+                        <div class="cell">
+                            <div data-type="label" data-device="THSensorWZ" data-get="temperature" data-limits='[-73,19,23]' data-colors='["#6699FF","#aa6900","#bb6242"]' data-unit="%B0C%0A" class="bigger thin"></div>
+                            <div>Temperatur</div>
+                        </div>
+                        <div class="cell">
+                            <div data-type="label" data-device="THSensorWZ" data-fix="0" data-part="4" data-limits='[0,40,60]' data-colors='["#bb6242","#aa6900","#bb6242"]' data-unit="%" class="bigger thin"></div>
+                            <div>Luftfeuchte</div>
+                        </div>
+                        <div class="cell">
+                            <div data-type="switch" data-device="PowerAV_Sw" class=""></div>
+                            <div>SchrankLicht</div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="WohnzimmerHeizung2_Clima" data-valve="ValvePosition" data-get="desired-temp" data-temp="measured-temp"></div>
+                        <div class="cell" data-type="popup" data-width="450px">
+                            <div data-type="simplechart" data-device="THSensorWZ" data-logdevice="FileLog_THSensorWZ" data-columnspec="4:temperature" data-minvalue="15" data-maxvalue="25" data-height="60" data-width="90" class="noticks">
+                            </div>
+                            <div class="dialog">
+                                <header>TEMPERATURE</header>
+                                <div data-type="simplechart" data-device="THSensorWZ" data-logdevice="FileLog_THSensorWZ" data-columnspec="4:temperature" data-minvalue="15" data-maxvalue="25" data-yticks="2" data-height="250">
+                                </div>
+                            </div>
+                        </div>
+                        <div class="cell" data-type="popup" data-width="450px">
+                            <div data-type="simplechart" data-device="THSensorWZ" data-logdevice="FileLog_THSensorWZ" data-columnspec="4:humidity" data-minvalue="0" data-maxvalue="100" data-height="60" data-width="90" class="noticks">
+                            </div>
+                            <div class="dialog">
+                                <header>HUMIDITY</header>
+                                <div data-type="simplechart" data-device="THSensorWZ" data-logdevice="FileLog_THSensorWZ" data-columnspec="4:humidity" data-minvalue="0" data-maxvalue="100" data-yticks="20" data-height="250">
+                                </div>
+                            </div>
+                        </div>
+                        <div class="cell">
+                            <div data-type="dimmer" data-device="HUEDevice1" data-get-on="!off" data-get-off="off" data-set="pct"></div>
+                            <div>Philips</div>
+                        </div>
+
+                    </div>
+                </div>
+            </li>
+            <li data-row="4" data-col="4" data-sizey="2" data-sizex="4">
+                <header>MULTIMEDIA</header>
+                <div class="sheet">
+                    <div class="row-60">
+                        <div class="cell">
+                            <!--left up -->
+                            <div class="sheet">
+                                <div class="row">
+
+                                    <div class="cell-25">Badradio</div>
+                                    <div class="cell-25x" data-type="switch" data-device="BadRadio" data-icon="fa-music"></div>
+                                    <div class="cell-50" data-type="select" data-device="AvReceiverZ2" data-items='["Airplay","Webradio","BD/DVD","PHONO"]' data-get="input" data-set="input"></div>
+                                </div>
+                                <div class="row">
+                                    <div class="cell-25">Radio</div>
+                                    <div class="cell-25" data-type="switch" data-device="RadioAtTablet" data-icon="fa-music"></div>
+                                    <div class="cell-50" data-type="select" data-device="AvReceiver" data-list="inputs" data-get="input" data-set="input"></div>
+                                </div>
+                            </div>
+                            <!-- end left cell -->
+                        </div>
+                        <div class="cell">
+                            <div class="large top-space" data-type="volume" data-device='AvReceiver' data-get='volume' data-set='volume'></div>
+                        </div>
+                    </div>
+                    <div class="row-40">
+                        <div class="cell">
+                            <!--left down -->
+                            <div class="sheet">
+                                <div class="row">
+                                    <div class="cell" data-type="push" data-device="WebRadio" data-icon="fa-step-backward" data-set-on="Prev"></div>
+                                    <div class="cell" data-type="label" data-device="WebRadio">radio</div>
+                                    <div class="cell" data-type="push" data-device="WebRadio" data-icon="fa-step-forward" data-set-on="Next"></div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="cell">
+                            <!--right down -->
+                            <div class="sheet">
+                                <div class="row">
+                                    <div class="cell">
+                                        <div data-type="switch" data-device="Fernsehen" data-icon="fa-film"></div>
+                                        <div>TV</div>
+                                    </div>
+                                    <div class="cell">
+                                        <div data-type="circlemenu">
+                                            <ul>
+                                                <li>
+                                                    <div data-type="push" data-icon="fa-wrench"></div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level -6" data-icon="">-6</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level -2" data-icon="">-2</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level 0" data-icon="">0</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level +3" data-icon="">2</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level +9" data-icon="">9</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level +C" data-icon="">12</div>
+                                                </li>
+                                            </ul>
+                                        </div>
+                                        <div>Woofer</div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="2" data-col="8" data-sizey="4" data-sizex="1">
+                <header>LIGHTS</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="slider" data-device='Testschalter' data-min="10" data-max="90"></div>
+                            <div>Light1</div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="volume" data-device="HUEDevice1" data-min="0" data-max="65353" data-get="hue" data-set="hue" class="hue-tick mini"></div>
+                            <div>Color</div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="dimmer" data-device="HUEDevice1" data-get-on="!off" data-get-off="off" data-set="pct" class=""></div>
+                            <div>Hell</div>
+                        </div>
+                    </div>
+                </div>
+
+            </li>
+        </ul>
+    </div>
+</body>
+
+</html>

+ 298 - 0
fhem/core/www/tablet_dev/index.html

@@ -0,0 +1,298 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <!--
+     /* FHEM tablet ui */
+     /*
+     * UI builder framework for FHEM
+     *
+     * Version: 2.5.*
+     * URL: https://github.com/knowthelist/fhem-tablet-ui
+     *
+     * Copyright (c) 2015-2017 Mario Stephan <mstephan@shared-files.de>
+     * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+     *
+     * - create a new folder named 'tablet' in /<fhem-path>/www
+     * - copy all files incl. sub folders into /<fhem-path>/www/tablet
+     * - add 'define TABLETUI HTTPSRV ftui ./www/tablet Tablet' in fhem.cfg
+     * - Tadaaa! A new fhem ui in http://<fhem-url>:8083/fhem/tablet/
+     */
+    -->
+    <link rel="icon" href="favicon.ico" type="image/x-icon" />
+
+    <!-- define your personal style here, it wont be overwritten  -->
+    <!-- link rel="stylesheet" href="css/fhem-green-ui.css" / -->
+    <!-- link rel="stylesheet" href="css/fhem-tablet-ui-user.css" / -->
+
+    <script src="js/fhem-tablet-ui.js" defer></script>
+
+
+    <!-- Remove this line to enable for usage with WebViewControl
+    <script defer>var wvcDevices = {'12345': 'Tablet'}; var wvcUserCssFile="webviewcontrol.css"</script>
+    <script src="../pgm2/cordova-2.3.0.js" defer></script>
+    <script src="../pgm2/webviewcontrol.js" defer></script>
+    <!-- End for WebViewControl -->
+
+    <title>FHEM-Tablet-UI</title>
+</head>
+
+<body>
+
+    <div class="gridster">
+        <ul>
+            <li data-row="1" data-col="1" data-sizey="1" data-sizex="1">
+                <header>TREND</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="symbol" data-device="dummy1" data-icons='["fa-arrow-up","fa-arrow-right","fa-arrow-down"]' data-on-colors='["#32a054","#6666cc","#ad3333"]' data-get-on='["Wert1","Wert2","Wert3"]' class=""></div>
+                            <div data-type="label" class="small narrow darker">Status</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="2" data-col="1" data-sizex="1" data-sizey="4">
+                <header>GARTEN</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="switch" data-device="GartenLicht" class=""></div>
+                            <div data-type="label" class="">Licht</div>
+                            <div data-type="symbol" data-device="Eingangstuer" class="big"></div>
+                            <div data-type="label" class="">T&#252;r</div>
+                            <div data-type="label" data-device="OutTemp" data-part="2" data-fix="1" data-limits='[-73,10,23]' data-colors='["#6699FF","#AA6900","#FF0000"]' data-unit="%B0C%0A" class="bigger thin top-space-2x"></div>
+                            <div data-type="label" class="">Temperatur</div>
+                            <div data-type="label" class=" darker top-space">Heute</div>
+                            <div data-type="weather" data-device="AgroWeather" data-get="fc0_weatherDay" class=" big"></div>
+                            <div data-type="label" data-device="AgroWeather" data-get="fc0_weatherDay" class="narrow"></div>
+                            <div data-type="label" data-device="AgroWeather" data-get="fc0_tempMax" data-unit="%B0C%0A" class="large"></div>
+                            <div data-type="label" class="darker top-space">Morgen</div>
+                            <div data-type="weather" data-device="AgroWeather" data-get="fc1_weatherDay" class="big"></div>
+                            <div data-type="label" data-device="AgroWeather" data-get="fc1_weatherDay" class="narrow"></div>
+                            <div data-type="label" data-device="AgroWeather" data-get="fc1_tempMax" data-unit="%B0C%0A" class="large"></div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="1" data-col="2" data-sizey="1" data-sizex="2">
+                <header>BAD</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="BadHeizung_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div class="big" data-type="symbol" data-device="BadFenster"></div>
+                            <div data-type="label" class="top-narrow  darker small">Fenster</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="1" data-col="3" data-sizey="1" data-sizex="2">
+                <header>SCHLAFZIMMER</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="SchlafzimmerHeizung_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div class="big" data-type="symbol" data-device="SchlafzimmerFenster"></div>
+                            <div data-type="label" class="top-narrow darker small">Fenster</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="1" data-col="4" data-sizey="1" data-sizex="2">
+                <header>KINDERZIMMER</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="KinderzimmerHeizung_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div class="big" data-type="symbol" data-device="KinderzimmerFenster"></div>
+                            <div class="top-narrow  darker small">Fenster</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="1" data-col="8" data-sizey="1" data-sizex="1">
+                <header>GALERIE</header>
+                <div data-type="switch" data-device="GalerieLicht" class="cell"></div>
+                <div data-type="label" class="cell">Licht</div>
+            </li>
+            <li data-row="2" data-col="2" data-sizey="2" data-sizex="2">
+                <header>KUECHE</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="KuecheHeizung_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div data-type="switch" data-device="HerdLicht_Sw"></div>
+                            <div data-type="label">HerdLicht</div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="KuecheHeizung2_Clima" data-valve="ValvePosition"></div>
+                        <div class="cell">
+                            <div class="big" data-type="symbol" data-device="KuechenFenster"></div>
+                            <div class="top-narrow  darker small">Fenster</div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="4" data-col="2" data-sizey="2" data-sizex="2">
+                <header>HOMESTATUS</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div data-type="homestatus" data-device='HomeStatus' data-version='' class="cell"></div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="2" data-col="4" data-sizey="2" data-sizex="4">
+                <header>WOHNZIMMER</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="WohnzimmerHeizung_Clima" data-valve="ValvePosition" data-get="desired-temp" data-temp="measured-temp"></div>
+                        <div class="cell">
+                            <div data-type="label" data-device="THSensorWZ" data-get="temperature" data-limits='[-73,19,23]' data-colors='["#6699FF","#aa6900","#bb6242"]' data-unit="%B0C%0A" class="bigger thin"></div>
+                            <div>Temperatur</div>
+                        </div>
+                        <div class="cell">
+                            <div data-type="label" data-device="THSensorWZ" data-fix="0" data-part="4" data-limits='[0,40,60]' data-colors='["#bb6242","#aa6900","#bb6242"]' data-unit="%" class="bigger thin"></div>
+                            <div>Luftfeuchte</div>
+                        </div>
+                        <div class="cell">
+                            <div data-type="switch" data-device="PowerAV_Sw" class=""></div>
+                            <div>SchrankLicht</div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="cell" data-type="thermostat" data-device="WohnzimmerHeizung2_Clima" data-valve="ValvePosition" data-get="desired-temp" data-temp="measured-temp"></div>
+                        <div class="cell" data-type="popup" data-width="450px">
+                            <div data-type="simplechart" data-device="THSensorWZ" data-logdevice="FileLog_THSensorWZ" data-columnspec="4:temperature" data-minvalue="15" data-maxvalue="25" data-height="60" data-width="90" class="noticks">
+                            </div>
+                            <div class="dialog">
+                                <header>TEMPERATURE</header>
+                                <div data-type="simplechart" data-device="THSensorWZ" data-logdevice="FileLog_THSensorWZ" data-columnspec="4:temperature" data-minvalue="15" data-maxvalue="25" data-yticks="2" data-height="250">
+                                </div>
+                            </div>
+                        </div>
+                        <div class="cell" data-type="popup" data-width="450px">
+                            <div data-type="simplechart" data-device="THSensorWZ" data-logdevice="FileLog_THSensorWZ" data-columnspec="4:humidity" data-minvalue="0" data-maxvalue="100" data-height="60" data-width="90" class="noticks">
+                            </div>
+                            <div class="dialog">
+                                <header>HUMIDITY</header>
+                                <div data-type="simplechart" data-device="THSensorWZ" data-logdevice="FileLog_THSensorWZ" data-columnspec="4:humidity" data-minvalue="0" data-maxvalue="100" data-yticks="20" data-height="250">
+                                </div>
+                            </div>
+                        </div>
+                        <div class="cell">
+                            <div data-type="dimmer" data-device="HUEDevice1" data-get-on="!off" data-get-off="off" data-set="pct"></div>
+                            <div>Philips</div>
+                        </div>
+
+                    </div>
+                </div>
+            </li>
+            <li data-row="4" data-col="4" data-sizey="2" data-sizex="4">
+                <header>MULTIMEDIA</header>
+                <div class="sheet">
+                    <div class="row-60">
+                        <div class="cell">
+                            <!--left up -->
+                            <div class="sheet">
+                                <div class="row">
+
+                                    <div class="cell-25">Badradio</div>
+                                    <div class="cell-25x" data-type="switch" data-device="BadRadio" data-icon="fa-music"></div>
+                                    <div class="cell-50" data-type="select" data-device="AvReceiverZ2" data-items='["Airplay","Webradio","BD/DVD","PHONO"]' data-get="input" data-set="input"></div>
+                                </div>
+                                <div class="row">
+                                    <div class="cell-25">Radio</div>
+                                    <div class="cell-25" data-type="switch" data-device="RadioAtTablet" data-icon="fa-music"></div>
+                                    <div class="cell-50" data-type="select" data-device="AvReceiver" data-list="inputs" data-get="input" data-set="input"></div>
+                                </div>
+                            </div>
+                            <!-- end left cell -->
+                        </div>
+                        <div class="cell">
+                            <div class="large top-space" data-type="volume" data-device='AvReceiver' data-get='volume' data-set='volume'></div>
+                        </div>
+                    </div>
+                    <div class="row-40">
+                        <div class="cell">
+                            <!--left down -->
+                            <div class="sheet">
+                                <div class="row">
+                                    <div class="cell" data-type="push" data-device="WebRadio" data-icon="fa-step-backward" data-set-on="Prev"></div>
+                                    <div class="cell" data-type="label" data-device="WebRadio">radio</div>
+                                    <div class="cell" data-type="push" data-device="WebRadio" data-icon="fa-step-forward" data-set-on="Next"></div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="cell">
+                            <!--right down -->
+                            <div class="sheet">
+                                <div class="row">
+                                    <div class="cell">
+                                        <div data-type="switch" data-device="Fernsehen" data-icon="fa-film"></div>
+                                        <div>TV</div>
+                                    </div>
+                                    <div class="cell">
+                                        <div data-type="circlemenu">
+                                            <ul>
+                                                <li>
+                                                    <div data-type="push" data-icon="fa-wrench"></div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level -6" data-icon="">-6</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level -2" data-icon="">-2</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level 0" data-icon="">0</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level +3" data-icon="">2</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level +9" data-icon="">9</div>
+                                                </li>
+                                                <li>
+                                                    <div data-type="push" data-device="AvReceiver" data-set="remoteControl subwoofer-temporary-level +C" data-icon="">12</div>
+                                                </li>
+                                            </ul>
+                                        </div>
+                                        <div>Woofer</div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </li>
+            <li data-row="2" data-col="8" data-sizey="4" data-sizex="1">
+                <header>LIGHTS</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="slider" data-device='Testschalter' data-min="10" data-max="90"></div>
+                            <div>Light1</div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="volume" data-device="HUEDevice1" data-min="0" data-max="65353" data-get="hue" data-set="hue" class="hue-tick mini"></div>
+                            <div>Color</div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="cell">
+                            <div data-type="dimmer" data-device="HUEDevice1" data-get-on="!off" data-get-off="off" data-set="pct" class=""></div>
+                            <div>Hell</div>
+                        </div>
+                    </div>
+                </div>
+
+            </li>
+        </ul>
+    </div>
+</body>
+
+</html>

+ 65 - 0
fhem/core/www/tablet_dev/index_empty.html

@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <!--
+     /* FHEM tablet ui */
+     /*
+     * UI builder framework for FHEM
+     *
+     * Version: 2.5.*
+     * URL: https://github.com/knowthelist/fhem-tablet-ui
+     *
+     * Copyright (c) 2015-2017 Mario Stephan <mstephan@shared-files.de>
+     * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+     *
+     * - create a new folder named 'tablet' in /<fhem-path>/www
+     * - copy all files incl. sub folders into /<fhem-path>/www/tablet
+     * - add 'define TABLETUI HTTPSRV ftui ./www/tablet Tablet' in fhem.cfg
+     * - Tadaaa! A new fhem ui in http://<fhem-url>:8083/fhem/tablet/
+     */
+    -->
+    <link rel="icon" href="favicon.ico" type="image/x-icon" />
+
+
+    <!-- define your personal style here, it wont be overwritten  -->
+    <!-- link rel="stylesheet" href="css/fhem-green-ui.css" / -->
+    <!-- link rel="stylesheet" href="css/fhem-tablet-ui-user.css" / -->
+
+    <script src="js/fhem-tablet-ui.js" defer></script>
+
+    <title>FHEM-Tablet-UI</title>
+</head>
+
+<body>
+    <div class="gridster">
+        <ul>
+            <li data-row="1" data-col="1" data-sizey="4" data-sizex="3">
+                <header>EXAMPLE1</header>
+                <!-- place your widget here -->
+            </li>
+            <li data-row="1" data-col="4" data-sizey="4" data-sizex="6">
+                <header>EXAMPLE2</header>
+                <!-- place your widget here -->
+            </li>
+            <li data-row="1" data-col="10" data-sizey="4" data-sizex="3">
+                <header>EXAMPLE3</header>
+                <!-- place your widget here -->
+            </li>
+            <li data-row="5" data-col="1" data-sizey="3" data-sizex="3">
+                <header>EXAMPLE4</header>
+                <!-- place your widget here -->
+            </li>
+            <li data-row="5" data-col="4" data-sizey="3" data-sizex="6">
+                <header>EXAMPLE5</header>
+                <!-- place your widget here -->
+            </li>
+            <li data-row="5" data-col="10" data-sizey="3" data-sizex="3">
+                <header>EXAMPLE6</header>
+                <!-- place your widget here -->
+            </li>
+        </ul>
+    </div>
+</body>
+
+</html>

+ 217 - 0
fhem/core/www/tablet_dev/index_layout.html

@@ -0,0 +1,217 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <!--
+     /* FHEM tablet ui */
+     /*
+     * UI builder framework for FHEM
+     *
+     * Version: 2.5.*
+     * URL: https://github.com/knowthelist/fhem-tablet-ui
+     *
+     * Copyright (c) 2015-2017 Mario Stephan <mstephan@shared-files.de>
+     * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+     *
+     * - create a new folder named 'tablet' in /<fhem-path>/www
+     * - copy all files incl. sub folders into /<fhem-path>/www/tablet
+     * - add 'define TABLETUI HTTPSRV ftui ./www/tablet Tablet' in fhem.cfg
+     * - Tadaaa! A new fhem ui in http://<fhem-url>:8083/fhem/tablet/
+     */
+    -->
+    <link rel="icon" href="favicon.ico" type="image/x-icon" />
+
+
+    <!-- define your personal style here, it wont be overwritten  -->
+    <!-- link rel="stylesheet" href="css/fhem-green-ui.css" / -->
+    <!-- link rel="stylesheet" href="css/fhem-tablet-ui-user.css" / -->
+
+    <script src="js/fhem-tablet-ui.js" defer></script>
+
+    <title>FHEM-Tablet-UI</title>
+</head>
+
+<body>
+    <div class="gridster">
+        <ul>
+            <!-- vbox hbox -->
+            <li data-row="1" data-col="1" data-sizey="2" data-sizex="2">
+                <header>vbox > hbox</header>
+                <div class="vbox">
+                    <div class="hbox">
+                        <div class="red" data-type="switch" data-device="dummy1"></div>
+                        <div class="green" data-type="switch" data-device="dummy2"></div>
+                    </div>
+                    <div class="hbox">
+                        <div class="blue" data-type="switch" data-device="dummy3"></div>
+                        <div class="orange" data-type="switch" data-device="dummy4"></div>
+                    </div>
+                </div>
+            </li>
+            
+            <!-- Sheet > Row > Cell -->
+            <li data-row="1" data-col="3" data-sizey="2" data-sizex="2">
+                <header>Sheet > Row > Cell</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell" data-type="symbol" data-device="dummy1"></div>
+                        <div class="cell" data-type="symbol" data-device="dummy2"></div>
+                    </div>
+                    <div class="row">
+                        <div class="cell" data-type="symbol" data-device="dummy3"></div>
+                        <div class="cell" data-type="symbol" data-device="dummy4"></div>
+                    </div>
+                </div>
+            </li>
+            
+            <!-- row > col-->
+            <li data-row="1" data-col="5" data-sizey="2" data-sizex="2">
+                <header>row > col</header>
+                <div class="row">
+                    <div class="col" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col" data-type="symbol" data-device="dummy2"></div>
+                </div>
+                <div class="row">
+                    <div class="col" data-type="symbol" data-device="dummy3"></div>
+                    <div class="col" data-type="symbol" data-device="dummy4"></div>
+                </div>
+            </li>
+            
+            <!-- -->
+            <li data-row="1" data-col="7" data-sizey="2" data-sizex="2">
+                <header>???</header>
+                <div class="row">
+                    <div class="col" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col" data-type="symbol" data-device="dummy2"></div>
+                </div>
+                <div class="row">
+                    <div class="col" data-type="symbol" data-device="dummy3"></div>
+                    <div class="col" data-type="symbol" data-device="dummy4"></div>
+                </div>
+            </li>
+            
+            <!-- vbox hbox -->
+            <li data-row="3" data-col="1" data-sizey="2" data-sizex="2">
+                <header>vbox > hbox items-space-*</header>
+                <div class="vbox">
+                    <div class="hbox items-space-between">
+                        <div class="" data-type="symbol" data-device="dummy1"></div>
+                        <div class="" data-type="symbol" data-device="dummy2"></div>
+                    </div>
+                    <div class="hbox items-space-around">
+                        <div class="" data-type="symbol" data-device="dummy3"></div>
+                        <div class="" data-type="symbol" data-device="dummy4"></div>
+                    </div>
+                </div>
+            </li>
+            
+            <!-- Sheet > Row > Cell -->
+            <li data-row="3" data-col="3" data-sizey="2" data-sizex="2">
+                <header>Sheet > Row > Cell *-Align</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell left-align" data-type="symbol" data-device="dummy1"></div>
+                        <div class="cell right-align" data-type="symbol" data-device="dummy2"></div>
+                    </div>
+                    <div class="row">
+                        <div class="cell top-align" data-type="symbol" data-device="dummy3"></div>
+                        <div class="cell bottom-align" data-type="symbol" data-device="dummy4"></div>
+                    </div>
+                </div>
+            </li>
+            
+            <!-- row > col-->
+            <li data-row="3" data-col="5" data-sizey="2" data-sizex="2">
+                <header>row > col-*-* </header>
+                <div class="row">
+                    <div class="col-1-3" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col-2-3" data-type="symbol" data-device="dummy2"></div>
+                </div>
+                <div class="row">
+                    <div class="col-4-5" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col-1-5" data-type="symbol" data-device="dummy2"></div>
+                </div>
+                <div class="row">
+                    <div class="col-1-2" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col-1-2" data-type="symbol" data-device="dummy2"></div>
+                </div>
+            </li>
+                            
+            <!-- -->
+            <li data-row="3" data-col="7" data-sizey="2" data-sizex="2">
+                <header>???</header>
+                <div class="row">
+                    <div class="col" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col" data-type="symbol" data-device="dummy2"></div>
+                </div>
+                <div class="row">
+                    <div class="col" data-type="symbol" data-device="dummy3"></div>
+                    <div class="col" data-type="symbol" data-device="dummy4"></div>
+                </div>
+            </li>
+            
+            <!-- vbox hbox -->
+            <li data-row="5" data-col="1" data-sizey="2" data-sizex="2">
+                <header>vbox > hbox items-space-*</header>
+                <div class="vbox">
+                    <div class="hbox items-space-between">
+                        <div class="" data-type="symbol" data-device="dummy1"></div>
+                        <div class="" data-type="symbol" data-device="dummy2"></div>
+                    </div>
+                    <div class="hbox items-space-around">
+                        <div class="" data-type="symbol" data-device="dummy3"></div>
+                        <div class="" data-type="symbol" data-device="dummy4"></div>
+                    </div>
+                </div>
+            </li>
+            
+            <!-- Sheet > Row > Cell -->
+            <li data-row="5" data-col="3" data-sizey="2" data-sizex="2">
+                <header>Sheet > Row > Cell *-Align</header>
+                <div class="sheet">
+                    <div class="row">
+                        <div class="cell left-align" data-type="symbol" data-device="dummy1"></div>
+                        <div class="cell right-align" data-type="symbol" data-device="dummy2"></div>
+                    </div>
+                    <div class="row">
+                        <div class="cell top-align" data-type="symbol" data-device="dummy3"></div>
+                        <div class="cell bottom-align" data-type="symbol" data-device="dummy4"></div>
+                    </div>
+                </div>
+            </li>
+            
+            <!-- row > col-->
+            <li data-row="5" data-col="5" data-sizey="2" data-sizex="2">
+                <header>row > col-*-* </header>
+                <div class="row">
+                    <div class="col-1-3" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col-2-3" data-type="symbol" data-device="dummy2"></div>
+                </div>
+                <div class="row">
+                    <div class="col-4-5" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col-1-5" data-type="symbol" data-device="dummy2"></div>
+                </div>
+                <div class="row">
+                    <div class="col-1-2" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col-1-2" data-type="symbol" data-device="dummy2"></div>
+                </div>
+            </li>
+            
+                        
+            <!-- -->
+            <li data-row="5" data-col="7" data-sizey="2" data-sizex="2">
+                <header>???</header>
+                <div class="row">
+                    <div class="col" data-type="symbol" data-device="dummy1"></div>
+                    <div class="col" data-type="symbol" data-device="dummy2"></div>
+                </div>
+                <div class="row">
+                    <div class="col" data-type="symbol" data-device="dummy3"></div>
+                    <div class="col" data-type="symbol" data-device="dummy4"></div>
+                </div>
+            </li>
+        </ul>
+    </div>
+</body>
+
+</html>

+ 91 - 0
fhem/core/www/tablet_dev/index_state.html

@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <!--
+     /* FHEM tablet ui */
+     /*
+     * UI builder framework for FHEM
+     *
+     * Version: 2.6.*
+     * URL: https://github.com/knowthelist/fhem-tablet-ui
+     *
+     * Copyright (c) 2015-2017 Mario Stephan <mstephan@shared-files.de>
+     * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+     *
+    -->
+    <link rel="icon" href="favicon.ico" type="image/x-icon" />
+
+    <meta name="widget_base_width" content="175">
+
+    <meta name="debug" content="2">
+
+    <script src="js/fhem-tablet-ui.js" defer></script>
+
+    <title>FHEM-Tablet-UI</title>
+</head>
+
+<body>
+    <div class="gridster">
+        <ul>
+            <li data-row="1" data-col="1" data-sizey="6" data-sizex="2">
+                <header>STATE</header>
+
+                <div class="container">
+                    <div class="row">
+                        <div class="col-40 tall thin line-normal right-align bottom-align right-space">FTUI</div>
+                        <div class="col-60 left-align bottom-align" data-bind="ftui.version"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Language:</div>
+                        <div class="col-60 left-align" data-bind="ftui.config.lang"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Debug Level:</div>
+                        <div class="col-60 left-align" data-bind="ftui.config.debuglevel"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Last full Update:</div>
+                        <div class="col-60 left-align" data-bind="ftui.poll.lastShortpollTimestamp.hhmmss()"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Last Event:</div>
+                        <div class="col-60 left-align" data-bind="ftui.poll.lastEventTimestamp.hhmmss()"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Longpoll Type:</div>
+                        <div class="col-60 left-align" data-bind="ftui.config.longPollType"></div>
+                    </div>
+
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Base Dir:</div>
+                        <div class="col-60 left-align" data-bind="ftui.config.basedir"></div>
+                    </div>
+
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">FHEM Dir:</div>
+                        <div class="col-60 left-align" data-bind="ftui.config.fhemDir"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Last Device:</div>
+                        <div class="col-60 left-align" data-bind="ftui.poll.lastDevice"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Last Reading:</div>
+                        <div class="col-60 left-align" data-bind="ftui.poll.lastReading"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">Last Value:</div>
+                        <div class="col-60 left-align" data-bind="ftui.poll.lastValue"></div>
+                    </div>
+                    <div class="row-200">
+                        <div class="col-40 right-align right-space mint">FHEM longpoll:</div>
+                        <div class="col-60 left-align" data-bind="ftui.deviceStates['WEB'].longpoll.val"></div>
+                    </div>
+                </div>
+            </li>
+        </ul>
+    </div>
+</body>
+
+</html>

File diff suppressed because it is too large
+ 2204 - 0
fhem/core/www/tablet_dev/js/fhem-tablet-ui.js


File diff suppressed because it is too large
+ 2 - 0
fhem/core/www/tablet_dev/js/fhem-tablet-ui.min.js


+ 740 - 0
fhem/core/www/tablet_dev/js/highcharts-meteogram.js

@@ -0,0 +1,740 @@
+/**
+ * This is a complex demo of how to set up a Highcharts chart, coupled to a
+ * dynamic source and extended by drawing image sprites, wind arrow paths
+ * and a second grid on top of the chart. The purpose of the demo is to inpire
+ * developers to go beyond the basic chart types and show how the library can
+ * be extended programmatically. This is what the demo does:
+ *
+ * - Loads weather forecast from www.yr.no in form of an XML service. The XML
+ *   is translated on the Higcharts website into JSONP for the sake of the demo
+ *   being shown on both our website and JSFiddle.
+ * - When the data arrives async, a Meteogram instance is created. We have
+ *   created the Meteogram prototype to provide an organized structure of the different
+ *   methods and subroutines associated with the demo.
+ * - The parseYrData method parses the data from www.yr.no into several parallel arrays. These
+ *   arrays are used directly as the data option for temperature, precipitation
+ *   and air pressure. As the temperature data gives only full degrees, we apply
+ *   some smoothing on the graph, but keep the original data in the tooltip.
+ * - After this, the options structure is build, and the chart generated with the
+ *   parsed data.
+ * - In the callback (on chart load), we weather icons on top of the temperature series.
+ *   The icons are sprites from a single PNG image, placed inside a clipped 30x30
+ *   SVG <g> element. VML interprets this as HTML images inside a clipped div.
+ * - Lastly, the wind arrows are built and added below the plot area, and a grid is
+ *   drawn around them. The wind arrows are basically drawn north-south, then rotated
+ *   as per the wind direction.
+ */
+
+function Meteogram(xml, container) {
+    // Parallel arrays for the chart data, these are populated as the XML/JSON file
+    // is loaded
+    this.symbols = [];
+    this.symbolNames = [];
+    this.precipitations = [];
+    this.windDirections = [];
+    this.windDirectionNames = [];
+    this.windSpeeds = [];
+    this.windSpeedNames = [];
+    this.temperatures = [];
+    this.pressures = [];
+
+    // Initialize
+    this.xml = xml;
+    this.container = container;
+
+    // Run
+    this.parseYrData();
+}
+
+Meteogram.prototype.getHeight = function () {
+    return 310;
+};
+Meteogram.prototype.getWidth = function () {
+    return 800;
+};
+
+/**
+ * Return weather symbol sprites as laid out at http://om.yr.no/forklaring/symbol/
+ */
+Meteogram.prototype.getSymbolSprites = function (symbolSize) {
+    return {
+        '01d': {
+            x: 0,
+            y: 0
+        },
+        '01n': {
+            x: symbolSize,
+            y: 0
+        },
+        '16': {
+            x: 2 * symbolSize,
+            y: 0
+        },
+        '02d': {
+            x: 0,
+            y: symbolSize
+        },
+        '02n': {
+            x: symbolSize,
+            y: symbolSize
+        },
+        '03d': {
+            x: 0,
+            y: 2 * symbolSize
+        },
+        '03n': {
+            x: symbolSize,
+            y: 2 * symbolSize
+        },
+        '17': {
+            x: 2 * symbolSize,
+            y: 2 * symbolSize
+        },
+        '04': {
+            x: 0,
+            y: 3 * symbolSize
+        },
+        '05d': {
+            x: 0,
+            y: 4 * symbolSize
+        },
+        '05n': {
+            x: symbolSize,
+            y: 4 * symbolSize
+        },
+        '18': {
+            x: 2 * symbolSize,
+            y: 4 * symbolSize
+        },
+        '06d': {
+            x: 0,
+            y: 5 * symbolSize
+        },
+        '06n': {
+            x: symbolSize,
+            y: 5 * symbolSize
+        },
+        '07d': {
+            x: 0,
+            y: 6 * symbolSize
+        },
+        '07n': {
+            x: symbolSize,
+            y: 6 * symbolSize
+        },
+        '08d': {
+            x: 0,
+            y: 7 * symbolSize
+        },
+        '08n': {
+            x: symbolSize,
+            y: 7 * symbolSize
+        },
+        '19': {
+            x: 2 * symbolSize,
+            y: 7 * symbolSize
+        },
+        '09': {
+            x: 0,
+            y: 8 * symbolSize
+        },
+        '10': {
+            x: 0,
+            y: 9 * symbolSize
+        },
+        '11': {
+            x: 0,
+            y: 10 * symbolSize
+        },
+        '12': {
+            x: 0,
+            y: 11 * symbolSize
+        },
+        '13': {
+            x: 0,
+            y: 12 * symbolSize
+        },
+        '14': {
+            x: 0,
+            y: 13 * symbolSize
+        },
+        '15': {
+            x: 0,
+            y: 14 * symbolSize
+        },
+        '20d': {
+            x: 0,
+            y: 15 * symbolSize
+        },
+        '20n': {
+            x: symbolSize,
+            y: 15 * symbolSize
+        },
+        '20m': {
+            x: 2 * symbolSize,
+            y: 15 * symbolSize
+        },
+        '21d': {
+            x: 0,
+            y: 16 * symbolSize
+        },
+        '21n': {
+            x: symbolSize,
+            y: 16 * symbolSize
+        },
+        '21m': {
+            x: 2 * symbolSize,
+            y: 16 * symbolSize
+        },
+        '22': {
+            x: 0,
+            y: 17 * symbolSize
+        },
+        '23': {
+            x: 0,
+            y: 18 * symbolSize
+        }
+    };
+};
+
+
+/**
+ * Function to smooth the temperature line. The original data provides only whole degrees,
+ * which makes the line graph look jagged. So we apply a running mean on it, but preserve
+ * the unaltered value in the tooltip.
+ */
+Meteogram.prototype.smoothLine = function (data) {
+    var i = data.length,
+        sum,
+        value;
+
+    while (i--) {
+        data[i].value = value = data[i].y; // preserve value for tooltip
+
+        // Set the smoothed value to the average of the closest points, but don't allow
+        // it to differ more than 0.5 degrees from the given value
+        sum = (data[i - 1] || data[i]).y + value + (data[i + 1] || data[i]).y;
+        data[i].y = Math.max(value - 0.5, Math.min(sum / 3, value + 0.5));
+    }
+};
+
+/**
+ * Callback function that is called from Highcharts on hovering each point and returns
+ * HTML for the tooltip.
+ */
+Meteogram.prototype.tooltipFormatter = function (tooltip) {
+
+    // Create the header with reference to the time interval
+    var index = tooltip.points[0].point.index,
+        ret = '<small>' + Highcharts.dateFormat('%A, %b %e, %H:%M', tooltip.x) + '-' +
+            Highcharts.dateFormat('%H:%M', tooltip.points[0].point.to) + '</small><br>';
+
+    // Symbol text
+    ret += '<b>' + this.symbolNames[index] + '</b>';
+
+    ret += '<table>';
+
+    // Add all series
+    Highcharts.each(tooltip.points, function (point) {
+        var series = point.series;
+        ret += '<tr><td><span style="color:' + series.color + '">\u25CF</span> ' + series.name +
+            ': </td><td style="white-space:nowrap">' + Highcharts.pick(point.point.value, point.y) +
+            series.options.tooltip.valueSuffix + '</td></tr>';
+    });
+
+    // Add wind
+    ret += '<tr><td style="vertical-align: top">\u25CF Wind</td><td style="white-space:nowrap">' + this.windDirectionNames[index] +
+        '<br>' + this.windSpeedNames[index] + ' (' +
+        Highcharts.numberFormat(this.windSpeeds[index], 1) + ' m/s)</td></tr>';
+
+    // Close
+    ret += '</table>';
+
+
+    return ret;
+};
+
+/**
+ * Draw the weather symbols on top of the temperature series. The symbols are sprites of a single
+ * file, defined in the getSymbolSprites function above.
+ */
+Meteogram.prototype.drawWeatherSymbols = function (chart) {
+    var meteogram = this,
+        symbolSprites = this.getSymbolSprites(30);
+
+    $.each(chart.series[0].data, function (i, point) {
+        var sprite,
+            group;
+
+        if (meteogram.resolution > 36e5 || i % 2 === 0) {
+
+            sprite = symbolSprites[meteogram.symbols[i]];
+            if (sprite) {
+
+                // Create a group element that is positioned and clipped at 30 pixels width and height
+                group = chart.renderer.g()
+                    .attr({
+                        translateX: point.plotX + chart.plotLeft - 15,
+                        translateY: point.plotY + chart.plotTop - 30,
+                        zIndex: 5
+                    })
+                    .clip(chart.renderer.clipRect(0, 0, 30, 30))
+                    .add();
+
+                // Position the image inside it at the sprite position
+                chart.renderer.image(
+                    '/fhem/tablet/lib/highcharts/graphics/meteogram-symbols-30px.png',
+                    -sprite.x,
+                    -sprite.y,
+                    90,
+                    570
+                )
+                    .add(group);
+            }
+        }
+    });
+};
+
+/**
+ * Create wind speed symbols for the Beaufort wind scale. The symbols are rotated
+ * around the zero centerpoint.
+ */
+Meteogram.prototype.windArrow = function (name) {
+    var level,
+        path;
+
+    // The stem and the arrow head
+    path = [
+        'M', 0, 7, // base of arrow
+        'L', -1.5, 7,
+        0, 10,
+        1.5, 7,
+        0, 7,
+        0, -10 // top
+    ];
+
+    level = $.inArray(name, ['Calm', 'Light air', 'Light breeze', 'Gentle breeze', 'Moderate breeze',
+        'Fresh breeze', 'Strong breeze', 'Near gale', 'Gale', 'Strong gale', 'Storm',
+        'Violent storm', 'Hurricane']);
+
+    if (level === 0) {
+        path = [];
+    }
+
+    if (level === 2) {
+        path.push('M', 0, -8, 'L', 4, -8); // short line
+    } else if (level >= 3) {
+        path.push(0, -10, 7, -10); // long line
+    }
+
+    if (level === 4) {
+        path.push('M', 0, -7, 'L', 4, -7);
+    } else if (level >= 5) {
+        path.push('M', 0, -7, 'L', 7, -7);
+    }
+
+    if (level === 5) {
+        path.push('M', 0, -4, 'L', 4, -4);
+    } else if (level >= 6) {
+        path.push('M', 0, -4, 'L', 7, -4);
+    }
+
+    if (level === 7) {
+        path.push('M', 0, -1, 'L', 4, -1);
+    } else if (level >= 8) {
+        path.push('M', 0, -1, 'L', 7, -1);
+    }
+
+    return path;
+};
+
+/**
+ * Draw the wind arrows. Each arrow path is generated by the windArrow function above.
+ */
+Meteogram.prototype.drawWindArrows = function (chart) {
+    var meteogram = this;
+
+    $.each(chart.series[0].data, function (i, point) {
+        var sprite, arrow, x, y;
+
+        if (meteogram.resolution > 36e5 || i % 2 === 0) {
+
+            // Draw the wind arrows
+            x = point.plotX + chart.plotLeft + 7;
+//            y = 255;
+	    y = meteogram.getHeight() - 55;
+            if (meteogram.windSpeedNames[i] === 'Calm') {
+                arrow = chart.renderer.circle(x, y, 10).attr({
+                    fill: 'none'
+                });
+            } else {
+                arrow = chart.renderer.path(
+                    meteogram.windArrow(meteogram.windSpeedNames[i])
+                ).attr({
+                    rotation: parseInt(meteogram.windDirections[i], 10),
+                    translateX: x, // rotation center
+                    translateY: y // rotation center
+                });
+            }
+            arrow.attr({
+                stroke: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black',
+                'stroke-width': 1.5,
+                zIndex: 5
+            })
+            .add();
+
+        }
+    });
+};
+
+/**
+ * Draw blocks around wind arrows, below the plot area
+ */
+Meteogram.prototype.drawBlocksForWindArrows = function (chart) {
+    var xAxis = chart.xAxis[0],
+        x,
+        pos,
+        max,
+        isLong,
+        isLast,
+        i;
+
+    for (pos = xAxis.min, max = xAxis.max, i = 0; pos <= max + 36e5; pos += 36e5, i += 1) {
+
+        // Get the X position
+        isLast = pos === max + 36e5;
+        x = Math.round(xAxis.toPixels(pos)) + (isLast ? 0.5 : -0.5);
+
+        // Draw the vertical dividers and ticks
+        if (this.resolution > 36e5) {
+            isLong = pos % this.resolution === 0;
+        } else {
+            isLong = i % 2 === 0;
+        }
+        chart.renderer.path(['M', x, chart.plotTop + chart.plotHeight + (isLong ? 0 : 28),
+            'L', x, chart.plotTop + chart.plotHeight + 32, 'Z'])
+            .attr({
+                'stroke': chart.options.chart.plotBorderColor,
+                'stroke-width': 1
+            })
+            .add();
+    }
+};
+
+/**
+ * Get the title based on the XML data
+ */
+Meteogram.prototype.getTitle = function () {
+    return this.xml.location.name + ', ' + this.xml.location.country;
+};
+
+/**
+ * Build and return the Highcharts options structure
+ */
+Meteogram.prototype.getChartOptions = function () {
+    var meteogram = this;
+
+    return {
+        chart: {
+            renderTo: this.container,
+            backgroundColor:'transparent',
+            marginBottom: 70,
+            marginRight: 40,
+            marginTop: 50,
+            plotBorderWidth: 1,
+//            width: 800,
+//            height: 310
+            width: this.getWidth(),
+            height: this.getHeight()
+        },
+
+        title: {
+            text: this.getTitle(),
+            align: 'left'
+        },
+
+        credits: {
+            text: '',
+            href: '#'
+        },
+
+        tooltip: {
+            shared: true,
+            useHTML: true,
+            formatter: function () {
+                return meteogram.tooltipFormatter(this);
+            }
+        },
+
+        xAxis: [{ // Bottom X axis
+            type: 'datetime',
+            tickInterval: 2 * 36e5, // two hours
+            minorTickInterval: 36e5, // one hour
+            tickLength: 0,
+            gridLineWidth: 1,
+            gridLineColor: (Highcharts.theme && Highcharts.theme.background2) || '#F0F0F0',
+            startOnTick: false,
+            endOnTick: false,
+            minPadding: 0,
+            maxPadding: 0,
+            offset: 30,
+            showLastLabel: true,
+            labels: {
+                format: '{value:%H}'
+            }
+        }, { // Top X axis
+            linkedTo: 0,
+            type: 'datetime',
+            tickInterval: 24 * 3600 * 1000,
+            labels: {
+                format: '{value:<span style="font-size: 12px; font-weight: bold">%a</span> %b %e}',
+                align: 'left',
+                x: 3,
+                y: -5
+            },
+            opposite: true,
+            tickLength: 20,
+            gridLineWidth: 1
+        }],
+
+        yAxis: [{ // temperature axis
+            title: {
+                text: null
+            },
+            labels: {
+                format: '{value}°',
+                style: {
+                    fontSize: '10px'
+                },
+                y: -2,
+                x: -3
+            },
+            plotLines: [{ // zero plane
+                value: 0,
+                color: '#BBBBBB',
+                width: 1,
+                zIndex: 2
+            }],
+            // Custom positioner to provide even temperature ticks from top down
+            tickPositioner: function () {
+                var max = Math.ceil(this.max) + 1,
+                    pos = max - 12, // start
+                    ret;
+
+                if (pos < this.min) {
+                    ret = [];
+                    while (pos <= max) {
+                        ret.push(pos += 1);
+                    }
+                } // else return undefined and go auto
+
+                return ret;
+
+            },
+            maxPadding: 0.3,
+            tickInterval: 1,
+            gridLineColor: (Highcharts.theme && Highcharts.theme.background2) || '#F0F0F0'
+
+        }, { // precipitation axis
+            title: {
+                text: null
+            },
+            labels: {
+                enabled: false
+            },
+            gridLineWidth: 0,
+            tickLength: 0
+
+        }, { // Air pressure
+            allowDecimals: false,
+            title: { // Title on top of axis
+                text: 'hPa',
+                offset: 0,
+                align: 'high',
+                rotation: 0,
+                style: {
+                    fontSize: '10px',
+                    color: Highcharts.getOptions().colors[1]
+                },
+                textAlign: 'left',
+                x: 3,
+                y: -5
+            },
+            labels: {
+                style: {
+                    fontSize: '10px',
+                    color: Highcharts.getOptions().colors[1]
+                },
+                y: -2,
+                x: 3
+            },
+            gridLineWidth: 0,
+            opposite: true,
+            showLastLabel: false
+        }],
+
+        legend: {
+            enabled: false
+        },
+
+        plotOptions: {
+            series: {
+                pointPlacement: 'between'
+            }
+        },
+
+        series: [{
+            name: 'Temperature',
+            data: this.temperatures,
+            type: 'spline',
+            marker: {
+                enabled: false,
+                states: {
+                    hover: {
+                        enabled: true
+                    }
+                }
+            },
+            tooltip: {
+                valueSuffix: '°C'
+            },
+            zIndex: 1,
+            color: Highcharts.getOptions().colors[0] || '#3d9dc7',
+            negativeColor: '#48AFE8'
+        }, {
+            name: 'Precipitation',
+            data: this.precipitations,
+            type: 'column',
+            color: Highcharts.getOptions().colors[2] || '#68CFE8',
+            yAxis: 1,
+            groupPadding: 0,
+            pointPadding: 0,
+            borderWidth: 0,
+            shadow: false,
+            dataLabels: {
+                enabled: true,
+                formatter: function () {
+                    if (this.y > 0) {
+                        return this.y;
+                    }
+                },
+                style: {
+                    fontSize: '8px'
+                }
+            },
+            tooltip: {
+                valueSuffix: 'mm'
+            }
+        }, {
+            name: 'Air pressure',
+            color: Highcharts.getOptions().colors[1],
+            data: this.pressures,
+            marker: {
+                enabled: false
+            },
+            shadow: false,
+            tooltip: {
+                valueSuffix: ' hPa'
+            },
+            dashStyle: 'shortdot',
+            yAxis: 2
+        }]
+    }
+};
+
+/**
+ * Post-process the chart from the callback function, the second argument to Highcharts.Chart.
+ */
+Meteogram.prototype.onChartLoad = function (chart) {
+
+    this.drawWeatherSymbols(chart);
+    this.drawWindArrows(chart);
+    this.drawBlocksForWindArrows(chart);
+
+};
+
+/**
+ * Create the chart. This function is called async when the data file is loaded and parsed.
+ */
+Meteogram.prototype.createChart = function () {
+    var meteogram = this;
+    this.chart = new Highcharts.Chart(this.getChartOptions(), function (chart) {
+        meteogram.onChartLoad(chart);
+    });
+};
+
+/**
+ * Handle the data. This part of the code is not Highcharts specific, but deals with yr.no's
+ * specific data format
+ */
+Meteogram.prototype.parseYrData = function () {
+
+    var meteogram = this,
+        xml = this.xml,
+        pointStart;
+
+    if (!xml || !xml.forecast) {
+        $('#loading').html('<i class="fa fa-frown-o"></i> Failed loading data, please try again later');
+        return;
+    }
+
+    // The returned xml variable is a JavaScript representation of the provided XML,
+    // generated on the server by running PHP simple_load_xml and converting it to
+    // JavaScript by json_encode.
+    $.each(xml.forecast.tabular.time, function (i, time) {
+        // Get the times - only Safari can't parse ISO8601 so we need to do some replacements
+        var from = time['@attributes'].from + ' UTC',
+            to = time['@attributes'].to + ' UTC';
+
+        from = from.replace(/-/g, '/').replace('T', ' ');
+        from = Date.parse(from);
+        to = to.replace(/-/g, '/').replace('T', ' ');
+        to = Date.parse(to);
+
+        if (to > pointStart + 4 * 24 * 36e5) {
+            return;
+        }
+
+        // If it is more than an hour between points, show all symbols
+        if (i === 0) {
+            meteogram.resolution = to - from;
+        }
+
+        // Populate the parallel arrays
+        meteogram.symbols.push(time.symbol['@attributes']['var'].match(/[0-9]{2}[dnm]?/)[0]);
+        meteogram.symbolNames.push(time.symbol['@attributes'].name);
+
+        meteogram.temperatures.push({
+            x: from,
+            y: parseInt(time.temperature['@attributes'].value),
+            // custom options used in the tooltip formatter
+            to: to,
+            index: i
+        });
+
+        meteogram.precipitations.push({
+            x: from,
+            y: parseFloat(time.precipitation['@attributes'].value)
+        });
+        meteogram.windDirections.push(parseFloat(time.windDirection['@attributes'].deg));
+        meteogram.windDirectionNames.push(time.windDirection['@attributes'].name);
+        meteogram.windSpeeds.push(parseFloat(time.windSpeed['@attributes'].mps));
+        meteogram.windSpeedNames.push(time.windSpeed['@attributes'].name);
+
+        meteogram.pressures.push({
+            x: from,
+            y: parseFloat(time.pressure['@attributes'].value)
+        });
+
+        if (i == 0) {
+            pointStart = (from + to) / 2;
+        }
+    });
+
+    // Smooth the line
+    this.smoothLine(this.temperatures);
+
+    // Create the chart when the data is loaded
+    this.createChart();
+};
+// End of the Meteogram protype

+ 190 - 0
fhem/core/www/tablet_dev/js/widget_agenda.js

@@ -0,0 +1,190 @@
+/* FTUI Plugin
+ * Copyright (c) 2016 hypetsch 
+ * https://github.com/knowthelist/fhem-tablet-ui/pull/168
+ */
+
+/* global ftui:true, Modul_widget:true */
+
+"use strict";
+
+var Modul_agenda = function () {
+
+    function init_attr(elem) {
+
+        elem.initData('max'   , 100);
+        elem.initData('c-term', 'c-term');
+
+        me.addReading(elem, 'c-term');
+
+        for( var i = 1; i <= elem.data('max'); ++i) {
+            var num = ("00" + i).slice(-3);
+            var reading;
+
+            reading = 't_'+num+'_summary';
+            elem.initData(reading, reading);
+            me.addReading(elem, reading);
+
+            reading = 't_' + num + '_bdate';
+            elem.initData(reading, reading);
+            me.addReading(elem, reading);
+
+            reading = 't_' + num + '_btime';
+            elem.initData(reading, reading);
+            me.addReading(elem, reading);
+
+            reading = 't_' + num + '_edate';
+            elem.initData(reading, reading);
+            me.addReading(elem, reading);
+
+            reading = 't_' + num + '_etime';
+            elem.initData(reading, reading);
+            me.addReading(elem, reading);
+
+            reading = 't_' + num + '_location';
+            elem.initData(reading, reading);
+            me.addReading(elem, reading);
+
+            reading = 't_' + num + '_source';
+            elem.initData(reading, reading);
+            me.addReading(elem, reading);
+        }
+    }
+
+    function init_ui(elem) {
+    }
+
+    function getDate(elem, datename, timename) {
+        var d = elem.getReading(datename).val.split('.');
+        var t = elem.getReading(timename).val.split(':');
+        return new Date(d[2], d[1] - 1, d[0], t[0], t[1], t[2]);
+    }
+
+    function diffDays(date1, date2) {
+        var diff = new Date(new Date(date2.getYear(), date2.getMonth(), date2.getDate()) -
+                   new Date(date1.getYear(), date1.getMonth(), date1.getDate())
+                );
+        return Math.floor((diff / 1000 / 60 / 60 / 24));
+    }
+
+    function prettyPrintDate(date) {
+        var text = "";
+        switch (diffDays(new Date(), date)) {
+            case 0: text += "Heute" + ", "; break;
+            case 1: text += "Morgen" + ", "; break;
+        }
+
+        text += date.eeee() + ", " + date.getDate() + "." + (date.getMonth() + 1) + "." + date.getFullYear();
+        return text;
+    }
+
+    function readCalendarConfig(elem, source, setting, defaultValue) {
+        try {
+            var cfg = elem.data('config');
+            var val = eval("cfg." + source + "." + setting);
+            if (val && val !== "")
+                return val;
+            else
+                return defaultValue;
+        }
+        catch (e) {
+            console.log(e);
+            return defaultValue;
+        }
+    }
+
+    function update_cb(elem) {
+    }
+
+    function update(dev,par) {
+
+        // update from normal state reading
+        me.elements.filterDeviceReading('c-term', dev, par)
+        .each(function(index) {
+
+            var elem = $(this);
+            var text = '';
+            var count = elem.getReading('c-term').val;
+
+            // render container
+            text += '<div class="container padding" style="overflow:hidden;">';
+
+            if( count === 0 )
+                text += '<div data-type="label">Keine Termine</div>';
+            else if (count > elem.data('max'))
+                count = elem.data('max');
+
+            var currentDate = new Date(2000, 1, 1);
+            for( var i = 1; i <= count; i++ ) {
+                var num = ('00' + i).slice(-3);
+
+                var summary = elem.getReading('t_' + num + '_summary').val;
+                var bdate = me.getDate(elem, 't_' + num + '_bdate', 't_' + num + '_btime');
+                var edate = me.getDate(elem, 't_' + num + '_edate', 't_' + num + '_etime');
+                var source = elem.getReading('t_' + num + '_source').val;
+
+                // check if new day
+                if( diffDays(currentDate, bdate) !== 0 ) {
+                    text += '<div class="center container padding inline top-narrow-10">';
+                    text += '<div class="left-align small darker" style="width:100%; border-bottom:1px solid #393939">';
+                    text += me.prettyPrintDate(bdate);
+                    text += '</div>';
+                    text += '</div>';
+
+                    currentDate = bdate;
+                }
+
+                var icon = me.readCalendarConfig(elem, source, "icon", "");
+                var color = me.readCalendarConfig(elem, source, "color", "");
+                var abbr = me.readCalendarConfig(elem, source, "abbreviation", "");
+
+                if( icon !== "" && icon.substr(0,3) == "fa-" ) {
+                    icon = "fa " + icon;
+                    abbr = "";
+                }
+
+                text += '<div class="center container padding inline top-narrow-10">';
+                text += '<div class="center large white ' + icon + '" style="line-height:30px;background-color:' + color + '; min-height:30px; width:35px;">';
+                text += abbr;
+                text += '</div>';
+                text += '<div class="" style="padding-left:5px; width:100%; min-height:30px; background:rgba(0, 0, 0, .08);">';
+                text += '<div class="left-align bold">' + summary + '</div>';
+                text += '<div class="left-align small darker">';
+
+                var durationDays = diffDays(bdate, edate);
+                if (durationDays == 1 && bdate.getHours() === 0 && bdate.getMinutes() === 0 && edate.getHours() === 0 && edate.getMinutes() === 0)
+                    text += 'Ganzer Tag';
+                else if (durationDays === 0)
+                    text += bdate.hhmm() + ' - ' + edate.hhmm();
+                else
+                    text += bdate.hhmm() + ' - ' + me.prettyPrintDate(edate);
+
+                text += '</div>';
+                text += '</div>';
+                text += '</div>';
+            }
+
+            // close container
+            text += '</div>';
+
+            elem.html( text );
+        });
+    }
+
+    // public
+    // inherit all public members from base class
+    var me = $.extend(new Modul_widget(), {
+        // override or own public members
+        widgetname: 'agenda',
+        init_attr: init_attr,
+        init_ui: init_ui,
+        update: update,
+        update_cb: update_cb,
+        prettyPrintDate: prettyPrintDate,
+        getDate: getDate,
+        readCalendarConfig: readCalendarConfig,
+        diffDays: diffDays
+    });
+    
+    return me;
+};
+

+ 54 - 0
fhem/core/www/tablet_dev/js/widget_button.js

@@ -0,0 +1,54 @@
+/* FTUI Plugin
+ * Copyright (c) 2015-2016 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_famultibutton:true */
+
+"use strict";
+
+function depends_button() {
+    if (typeof Module_famultibutton == 'undefined')
+        return ["famultibutton"];
+}
+
+var Modul_button = function () {
+
+    function init() {
+
+        me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
+        me.elements.each(function (index) {
+            var elem = $(this);
+            elem.initData('off-color', ftui.getStyle('.button.off', 'color') || '#2A2A2A');
+            elem.initData('off-background-color', ftui.getStyle('.button.off', 'background-color') || '#505050');
+            elem.initData('on-color', ftui.getClassColor($(this)) || ftui.getStyle('.button.on', 'color') || '#2A2A2A');
+            elem.initData('on-background-color', ftui.getStyle('.button.on', 'background-color') || '#aa6900');
+            elem.initData('get-warn', -1);
+
+            me.init_attr(elem);
+            elem = me.init_ui($(this));
+
+            if (!elem.data('device')) {
+                elem.setOn();
+            }
+        });
+    }
+
+    function update_cb(elem, state) {
+        if (elem.hasClass('warn') || elem.children().children('#fg').hasClass('warn'))
+            me.showOverlay(elem, ftui.getPart(state, elem.data('get-warn')));
+        else
+            me.showOverlay(elem, "");
+    }
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_famultibutton(), {
+        //override members
+        widgetname: 'button',
+        init: init,
+        update_cb: update_cb,
+    });
+
+    return me;
+};

+ 238 - 0
fhem/core/www/tablet_dev/js/widget_calview.js

@@ -0,0 +1,238 @@
+// Initial Version from Chris1284
+// Modifications for 2.2 klausw	4.7.2016
+// Modifications for 2.4 mario stephan	2.12.2016
+// Modifications for 2.6 chris1284 08.12.2016 - Ursprüngliche Darstellung wieder hergestellt
+// Modifications for 2.8 chris1284 28.12.2016 - widget an aktuelle calview angepasst
+// Modifications / user wishes chris1284 13.09.2017
+// Modifications chris1284 13.09.2017 19:05 - nur noch oneline yes/no , onlinesum/desc/loc entfernt)
+// Modifications chris1284 11.10.2017 - new reading weekdayname 
+// data-get			all|today|tomorrow 
+// data-start		none|notoday|notomorrow		(only for data-get="all" -> dont show Entrys from today or today and tomorrow)
+// data-max			number how much Entries are maximal listed
+// data-color		Text color
+// data-detail		Array of details that should be shown default: ["bdate", "btime", "summary", "location"]
+// data-showempty	show Text for "no Date" default: true
+
+/* global ftui:true, Modul_widget:true */
+
+
+var Modul_calview = function () {
+
+    var readings = [];
+
+    function init() {
+
+        me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
+        me.elements.each(function (index) {
+            var elem = $(this);
+            // Standardwerte fuer Parameter
+            elem.initData('max', 10);
+            elem.initData('get', 'STATE');
+            elem.initData('start', 'all');
+            elem.initData('color', '');
+			elem.initData('class', '');
+            elem.initData('detail', ["bdate", "btime","bdatetimeiso","timeshort","summary", "location","edate","etime","edatetimeiso","source","sourcecolor","age","description","daysleft","daysleftLong","weekdayname"]);
+			elem.initData('detailwidth', []);
+			elem.initData('dateformat', 'long');
+			elem.initData('timeformat', 'long');
+			elem.initData('sourcecolor', 'no');
+            elem.initData('showempty', 'true');
+			elem.initData('swiperstyle', 'no');
+			elem.initData('oneline', 'no');
+			
+            var device = $(this).data('device');
+            console.log("device: " + device + " get: " + $(this).data('get') + " max: " + $(this).data('max'));
+            if ($(this).data('get') == 'today' || $(this).data('get') == 'tomorrow' || $(this).data('get') == 'all') {
+				elem.initData('c-term', 'c-term');
+				elem.initData('c-today', 'c-today');
+				elem.initData('c-tomorrow', 'c-tomorrow');
+				var value = $(this).data('max');
+                var wann = $(this).data('get');
+
+                if (wann == "all") { wann = "term";
+                }
+                me.addReading(elem, 'c-' + wann);
+                console.log("c-" + wann + ": " + elem.getReading('c-' + wann).val);
+                if (wann == "term") {
+                    wann = "t";
+                }
+                for (var i = 1; i <= value; i++) {
+                    num = "00" + i;
+                    num = num.slice(-3);
+                    elem.data('detail').forEach(function (wert) {
+                        elem.initData(wann + '_' + num + '_' + wert, wann + '_' + num + '_' + wert);
+                        me.addReading(elem, wann + '_' + num + '_' + wert);
+                        console.log(wann + '_' + num + '_' + wert + ': ' + elem.getReading(wann + '_' + num + '_' + wert).val);
+                    });
+					//elem.initData(wann + '_' + num + '_sourcecolor', wann + '_' + num + '_sourcecolor');
+                }
+            }
+        });
+    }
+
+    function update(dev, par) {
+        me.elements.filter('div[data-device="' + dev + '"]')
+        .each(function (index) {
+            var elem = $(this);
+			var deviceElements;
+			var mytext = "";
+			var num;
+            var color = elem.data('color');
+            elem.css("color", ftui.getStyle('.' + color, 'color') || color);			
+			elem.getReading('c-term').val;
+            elem.getReading('c-today').val;
+            elem.getReading('c-tomorrow').val;
+			//alert(color);
+            if (elem.data('get') == 'STATE') { 
+				if (ftui.isValid(elem.getReading('get').val)) { mytext = "<div class=\"cell\" data-type=\"label\">" + elem.getReading('get').val + "</div>"; }} 
+			else if (elem.data('get') == 'today' || elem.data('get') == 'tomorrow' || elem.data('get') == 'all') {
+				var beginn = 1;
+				var zeitrahmen = {
+					"today": "Heute ",
+					"tomorrow": "Morgen ",
+					"all": "" };
+            
+                if (elem.data('get') == 'all') { var readingPrefix = "t"; var count = elem.getReading('c-term').val;}
+				else if (elem.data('get') == 'today') { var readingPrefix = "today"; var count = elem.getReading('c-' + elem.data('get')).val;}
+				else if (elem.data('get') == 'tomorrow') { var readingPrefix = "tomorrow"; var count = elem.getReading('c-' + elem.data('get')).val;}
+                if (elem.data('start') == "notoday") { beginn = 1 + parseInt(elem.getReading('c-today').val); } 
+				else if (elem.data('start') == "notomorrow") { beginn = 1 + parseInt(elem.getReading('c-today').val) + parseInt(elem.getReading('c-tomorrow').val); }
+                
+                if (count === 0) {
+                    if (elem.data('showempty') == "true") {
+                        mytext += "<div data-type=\"label\">" + zeitrahmen[readingPrefix] + "keine Termine</div>";
+                    }
+                } else {
+                    if (count > elem.data('max')) {
+                        count = elem.data('max');
+                    }
+					if(elem.data('swiperstyle') == 'no'){
+						for (var i = beginn; i <= count; i++) {
+							num = "00" + i;
+							num = num.slice(-3);
+							var mycount = 0;
+							if (elem.data('dateformat') == 'short'){ datesubstr = 6;}
+							else {datesubstr = 10;}
+							if (elem.data('timeformat') == 'short'){ timesubstr = 5;}
+							else {timesubstr = 8;}
+							var onelinesum = "";
+							if (elem.data('onelinesum') === "yes") { onelinesum = "white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";}
+							var onelinedesc   = "";
+							if (elem.data('onelinedesc') === "yes") { onelinedesc = "white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";}
+							var onelineloc   = "";
+							if (elem.data('onelineloc') === "yes") { onelineloc = "white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";}
+							var onelinestyle   = "";
+							if (elem.data('oneline') === "yes") { onelinestyle = "white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";}
+							if(elem.data('detailwidth').length > 0) {
+								//alert(elem.data('detailwidth').length);
+								mytext += "<div class=\"hbox cell\" style=\"height:auto;\">";
+								elem.data('detail').forEach(function(spalte) {
+									if ( typeof elem.getReading(readingPrefix+'_'+num+'_'+spalte).val != "undefined" ) {
+										if(elem.data('sourcecolor') == 'yes'){color = elem.getReading(readingPrefix+'_'+num+'_sourcecolor').val;}
+										if(spalte == 'age'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, datesubstr) + "</div>";}
+										if(spalte == 'bdate'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, datesubstr) + "</div>";}
+										if(spalte == 'btime'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, timesubstr) + "</div>";}
+										if(spalte == 'bdatetimeiso'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'timeshort'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'summary'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'location'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'edate'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, datesubstr) + "</div>";}
+										if(spalte == 'etime'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, timesubstr) + "</div>";}
+										if(spalte == 'edatetimeiso'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'source'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'description'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'daysleft'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'daysleftLong'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'weekdayname'){mytext += "<div data-type=\"label\" class=\""+elem.data('class')+"\" style=\"color:"+color+";width:"+elem.data('detailwidth')[mycount]+"%;"+onelinestyle+"\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										mycount++;
+									}
+									
+								});
+								mytext += "</div>";
+							} else {
+								var $width = 100 / elem.data('detail').length;					
+								//alert($width);
+								mytext += "<div class=\"hbox cell\">";
+								elem.data('detail').forEach(function(spalte) {
+									if ( typeof elem.getReading(readingPrefix+'_'+num+'_'+spalte).val != "undefined" ) {
+										if(elem.data('sourcecolor') == 'yes'){color = elem.getReading(readingPrefix+'_'+num+'_sourcecolor').val; }
+										if(spalte == 'bdate'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, datesubstr) + "</div>";}
+										if(spalte == 'btime'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, timesubstr) + "</div>";}
+										if(spalte == 'bdatetimeiso'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'timeshort'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'summary'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'location'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'edate'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, datesubstr) + "</div>";}
+										if(spalte == 'etime'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val.substr(0, timesubstr) + "</div>";}
+										if(spalte == 'edatetimeiso'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'source'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'description'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'daysleft'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+										if(spalte == 'daysleftLong'){mytext += "<div data-type=\"label\" class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									}
+								});
+								mytext += "</div>";
+							}
+						}
+					}
+					if(elem.data('swiperstyle') == 'yes'){
+						mytext += "<div class=\"cell\">";
+						mytext += "<div class=\"swiper-container\">";
+						mytext += "<div class=\"swiper-wrapper\">";
+						for (var i = beginn; i <= count; i++) {
+							num = "00" + i;
+							num = num.slice(-3);
+							var $width = 100 / elem.data('detail').length;
+							//alert($width);
+							mytext += "<div class=\"swiper-slide\" data-hash=\"slide"+i+"\";>";
+							mytext += "<div class=\"hbox\">";
+							elem.data('detail').forEach(function(spalte) {
+								if ( typeof elem.getReading(readingPrefix+'_'+num+'_'+spalte).val != "undefined" ) {
+									if(spalte == 'bdate'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'btime'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'bdatetimeiso'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'summary'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'location'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'edate'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'etime'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'edatetimeiso'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'source'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'description'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'daysleft'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+									if(spalte == 'daysleftLong'){mytext += "<div class=\"\" style=\"color:"+color+";width:"+$width+"%;\">" + elem.getReading(readingPrefix+'_'+num+'_'+spalte).val + "</div>";}
+								}
+							});
+							mytext += "</div>";//close hbox
+							mytext += "</div>";//close swiper-slide
+						}
+						mytext += "</div>";//close swiper-wrapper
+						mytext += "<div class=\"swiper-pagination\" style=\"position:static;\"></div>";
+						//mytext += "<div class=\"swiper-button-prev\"></div>";
+						//mytext += "<div class=\"swiper-button-next\"></div>";
+						mytext += "</div>";//close swiper-container
+						mytext += "<script>\
+							var swiper = new Swiper('.swiper-container', {\
+								pagination: '.swiper-pagination',\
+								spaceBetween: 30,\
+								autoplay: 2500,\
+								autoplayDisableOnInteraction: false,\
+								centeredSlides: true,\
+								hashnav: true,\
+							});\
+							</script>";
+						mytext += "</div>";
+                    }
+                }       
+            }
+			elem.html(mytext);
+        });
+    }
+
+    var me = $.extend(new Modul_widget(), {
+        widgetname: 'calview',
+        init: init,
+        update: update,
+    });
+
+    return me;
+};

File diff suppressed because it is too large
+ 3833 - 0
fhem/core/www/tablet_dev/js/widget_chart.js


+ 111 - 0
fhem/core/www/tablet_dev/js/widget_checkbox.js

@@ -0,0 +1,111 @@
+/* FTUI Plugin
+ * Copyright (c) 2015-2016 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_famultibutton:true, Switchery:true */
+
+"use strict";
+
+function depends_checkbox() {
+    var deps = [];
+    if (!$.fn.Switchery) {
+        $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'lib/switchery.min.css" type="text/css" />');
+        deps.push(ftui.config.basedir + "lib/switchery.min.js");
+    }
+    if (typeof Module_famultibutton == 'undefined' || !$.fn.famultibutton) {
+        deps.push('famultibutton');
+    }
+    return deps;
+}
+
+var Modul_checkbox = function () {
+
+    function clicked(elem, isClicked) {
+        var value = isClicked ? elem.data('set-on') : elem.data('set-off');
+        elem.data('value', value);
+        elem.transmitCommand();
+    }
+
+    function init() {
+
+        me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
+        me.elements.each(function (index) {
+            var elem = $(this);
+            elem.data('off-color', elem.data('off-color') || ftui.getStyle('.checkbox.off', 'color') || '#bfbfbf');
+            elem.data('off-background-color', elem.data('off-background-color') || ftui.getStyle('.checkbox.off', 'background-color') || '#505050');
+            elem.data('on-color', elem.data('on-color') || ftui.getStyle('.checkbox.on', 'color') || '#bfbfbf');
+            elem.data('on-background-color', elem.data('on-background-color') || ftui.getClassColor($(this)) || ftui.getStyle('.checkbox.on', 'background-color') || '#aa6900');
+            me.init_attr(elem);
+
+            // base element that becomes a Switchery
+            var input = $('<input/>', {
+                type: 'checkbox',
+                checked: true,
+            }).appendTo(elem);
+
+            // transform the input element into a Switchery
+            var switchery = new Switchery(input[0], {
+                size: elem.hasClass('small') ? 'small' : elem.hasClass('large') ? 'large' : 'default',
+                color: elem.data('on-background-color'),
+                secondaryColor: elem.data('off-background-color'),
+                jackColor: elem.data('on-color'),
+                jackSecondaryColor: elem.data('off-color'),
+            });
+
+            // click handler
+            var switcherButton = elem.find('.switchery');
+            var touchIsAllowed = false;
+            switcherButton.on('click', function (event) {
+                touchIsAllowed = false;
+                me.clicked(elem, input.is(":checked"));
+            });
+
+            // touch handler
+            switcherButton.on('touchend', function (e) {
+                if (touchIsAllowed) {
+                    switchery.setPosition(true);
+                    me.clicked(elem, input.is(":checked"));
+                }
+                touchIsAllowed = true;
+            });
+
+            switcherButton.on('touchmove', function (e) {
+                e.preventDefault();
+            });
+
+            // setState for switchery which lacks of such a function
+            switchery.setState = function (checkedBool) {
+                if ((checkedBool && !switchery.isChecked()) || (!checkedBool && switchery.isChecked())) {
+                    switchery.setPosition(true);
+                    switchery.handleOnchange(true);
+                }
+
+            };
+
+            // store input object for usage in update function of base class
+            elem.data("famultibutton", input);
+
+            // provide On / Off functions like a famultibutton
+            input.setOn = function () {
+                switchery.setState(true);
+            };
+            input.setOff = function () {
+                switchery.setState(false);
+            };
+
+            // init state is off
+            switchery.setState(false);
+        });
+    }
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_famultibutton(), {
+        //override members
+        widgetname: 'checkbox',
+        clicked: clicked,
+        init: init,
+    });
+    return me;
+};

+ 115 - 0
fhem/core/www/tablet_dev/js/widget_checklist.js

@@ -0,0 +1,115 @@
+/* FTUI Plugin
+ * Copyright (c) 2017 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_select:true */
+
+"use strict";
+
+function depends_checklist() {
+
+    if (!$('link[href$="css/ftui_checklist.css"]').length)
+        $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'css/ftui_checklist.css" type="text/css" />');
+
+    if (typeof Modul_select == 'undefined') {
+        return ["select"];
+    }
+}
+
+var Modul_checklist = function () {
+
+    function fillList(elem) {
+        elem.html('');
+        var text = '';
+        var items = elem.data('items') || '';
+        var alias = elem.data('alias') || elem.data('items');
+        for (var i = 0, len = items.length; i < len; i++) {
+
+            text += '<div class="check-item" data-text="' + items[i] + '">';
+            text += '<div class="check-text">';
+            text += '<div class="text">' + (alias && alias[i] || items[i]) + '</div>';
+            text += '</div>';
+            text += '<div class="check-image">';
+            text += '<i class="icon fa"/>';
+            text += '</div>';
+            text += '</div>';
+        }
+
+        elem.append(text).fadeIn();
+    }
+
+
+    function setCurrentItem(elem) {
+        var items = elem.data('items') || '';
+        var list = elem.getReading('get').val;
+        for (var i = 0, len = items.length; i < len; i++) {
+            var idx = ftui.indexOfGeneric(list, items[i]);
+            var select_elem = elem.find('.check-item[data-text="' + items[i] + '"]');
+            if (idx >= 0) {
+                select_elem.addClass('active');
+            } else {
+                select_elem.removeClass('active');
+            }
+        }
+    }
+
+    function sendStatus(elem) {
+
+        var arr = [];
+        elem.find('.check-item.active').each(function (index) {
+            arr.push($(this).data('text'));
+        });
+
+        elem.data('value', arr.join(','));
+        elem.transmitCommand();
+
+    }
+
+    function init_ui(elem) {
+
+        // prepare container element
+        var width = elem.data('width');
+        var widthUnit = ($.isNumeric(width)) ? 'px' : '';
+        var height = elem.data('height');
+        var heightUnit = ($.isNumeric(height)) ? 'px' : '';
+
+        elem.html('')
+            .addClass('check-list')
+            .css({
+                width: width + widthUnit,
+                maxWidth: width + widthUnit,
+                height: height + heightUnit,
+                color: elem.mappedColor('text-color'),
+                backgroundColor: elem.mappedColor('background-color'),
+            });
+
+        elem.on('click', '.check-item', function () {
+
+            var item = $(this);
+            if (item.hasClass('active')) {
+                item.removeClass('active');
+            } else {
+                item.addClass('active');
+            }
+            clearTimeout(elem.delayTimer);
+            elem.delayTimer = setTimeout(function () {
+                elem.delayTimer = 0;
+                sendStatus(elem);
+            }, elem.data('delay'));
+        });
+    }
+
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_select(), {
+        //override members
+        widgetname: 'checklist',
+        init_ui: init_ui,
+        fillList: fillList,
+        setCurrentItem: setCurrentItem
+    });
+
+    return me;
+};

+ 88 - 0
fhem/core/www/tablet_dev/js/widget_circlemenu.js

@@ -0,0 +1,88 @@
+/* FTUI Plugin
+ * Copyright (c) 2015-2017 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_widget:true */
+
+"use strict";
+
+function depends_circlemenu() {
+    if (!$.fn.circleMenu)
+        return [ftui.config.basedir + "lib/jquery.circlemenu.js"];
+}
+
+var Modul_circlemenu = function () {
+
+    function init() {
+
+        me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
+        me.elements.each(function (index) {
+            var elem = $(this);
+            var ulElem = elem.find('ul');
+            ulElem.circleMenu({
+                    item_diameter: elem.data('item-diameter') || '4em',
+                    item_width: elem.data('item-width'),
+                    item_height: elem.data('item-height'),
+                    trigger: 'click',
+                    circle_radius: elem.data('circle-radius') || 70,
+                    direction: elem.data('direction') || 'full',
+                    border: elem.data('border') || 'round',
+                    close_event: (ulElem.hasClass("keepopen") || elem.hasClass("keepopen")) ? '' : 'click',
+                    close: function () {
+                        setTimeout(function () {
+                            ftui.showModal(false);
+                        }, 50);
+                    },
+                    select: function () {
+                        setTimeout(function () {
+                            ftui.showModal(false);
+                        }, 50);
+                    },
+                    open: function () {
+                        var cm = this;
+                        if (cm.options.close_event !== '') {
+                            elem.data('timeoutMenu', setTimeout(function () {
+                                cm.close();
+                                setTimeout(function () {
+                                    ftui.showModal(false);
+                                }, 1000);
+                            }, elem.data('close-after') || Math.max(4000, 1000 * (elem.find('li').length - 1))));
+                        }
+                        if (!elem.hasClass("noshade")) {
+                            ftui.showModal(true);
+                        }
+                    },
+                })
+                .addClass('circlemenu');
+            elem.closest('.gridster>ul>li').css({
+                    overflow: 'visible'
+                });
+
+            ulElem.wrapAll('<div class="circlemenu-wrapper"></div>');
+            elem.find('circlemenu-wrapper').css({
+                    minWidth: elem.data('item-width')
+                })
+        });
+        
+        $('.menu li:not(:first-child)').on('click', function () {
+            var timeoutMenu = $(this).parent().data('timeoutMenu');
+            if (timeoutMenu)
+                clearTimeout(timeoutMenu);
+        });
+
+    }
+
+    function update(dev, par) {}
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_widget(), {
+        //override members
+        widgetname: 'circlemenu',
+        init: init,
+        update: update,
+    });
+
+    return me;
+};

+ 62 - 0
fhem/core/www/tablet_dev/js/widget_classchanger.js

@@ -0,0 +1,62 @@
+/* FTUI Plugin
+ * Copyright (c) 2015-2016 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_widget:true */
+
+"use strict";
+
+function depends_classchanger() {
+    var deps = [];
+    return deps;
+}
+
+var Modul_classchanger = function () {
+
+    function init() {
+
+        me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
+        me.elements.each(function (index) {
+
+            var elem = $(this);
+            elem.initData('get', 'STATE');
+
+            elem.initData('get-on', '(true|1|on|open|ON)');
+            elem.initData('get-off', '!on');
+
+            elem.initData('on-class', 'on');
+            elem.initData('off-class', 'off');
+
+            me.addReading(elem, 'get');
+
+        });
+    }
+
+    function update(dev, par) {
+
+        me.elements.filterDeviceReading('get', dev, par)
+            .each(function (index) {
+                var elem = $(this);
+                var state = elem.getReading('get').val;
+                if (elem.matchingState('get', state) === 'on') {
+                    elem.removeClass(elem.data('off-class'));
+                    elem.addClass(elem.data('on-class'));
+                }
+                if (elem.matchingState('get', state) === 'off') {
+                    elem.removeClass(elem.data('on-class'));
+                    elem.addClass(elem.data('off-class'));
+                }
+            });
+
+    }
+
+    var me = $.extend(new Modul_widget(), {
+
+        widgetname: 'classchanger',
+        init: init,
+        update: update,
+    });
+
+    return me;
+};

+ 152 - 0
fhem/core/www/tablet_dev/js/widget_clock.js

@@ -0,0 +1,152 @@
+/* FTUI Plugin
+ * Copyright (c) 2015-2016 Mario Stephan <mstephan@shared-files.de>
+ * originally created by Thomas Nesges
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_widget:true */
+
+"use strict";
+
+var Modul_clock = function () {
+
+    function init_attr(elem) {
+        elem.initData('format', 'H:i:s');
+        elem.initData('interval', 1000);
+        elem.initData('shortday-length', 3);
+        elem.initData('days', ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]);
+        elem.initData('shortmonth-length', 3);
+        elem.initData('months', ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]);
+
+
+        if (!$.isArray(elem.data('days'))) {
+            if (elem.data('days').match(/englisc?h/)) {
+                elem.data('days', ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);
+            } else {
+                console.log(me.widgetname, 'init_attr', 'ERROR: data-days must be an array');
+            }
+        }
+
+
+        if (!$.isArray(elem.data('months'))) {
+            if (elem.data('months').match(/englisc?h/)) {
+                elem.data('months', ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]);
+            } else {
+                console.log(me.widgetname, 'init_attr', 'ERROR: data-months must be an array');
+            }
+        }
+
+    }
+
+    function init_datearray(elem) {
+        var d = [];
+        var now = new Date();
+        // Y: Jahreszahl, vierstellig
+        // y: Jahreszahl, zweistellig
+        // m: Monatszahl, mit f¸hrender Null
+        // n: Monatszahl, ohne f¸hrende Null
+        // d: Tag des Monats, mit f¸hrender Null
+        // j: Tag des Monats, ohne f¸hrende Null
+        // H: Stunde des Tages, mit f¸hrender Null
+        // G: Stunde im 24-Stunden-Format, ohne f¸hrender Null
+        // i: Minute der Stunde, mit f¸hrender Null
+        // s: Sekunde der Minute, mit f¸hrender Null
+        // u: Millisekunden mit f¸hrender Null
+        // O: Zeitunterschied zur Greenwich time (GMT) in Stunden
+        // U: Sekunden seit Beginn der UNIX-Epoche (January 1 1970 00:00:00 GMT)
+        // w: Wochentagszahl (Sonntag = 0, Samstag = 6)
+        // N: Wochentagszahl nach ISO-8601 (Montag = 1, Sonntag = 7)
+        // l: Wochentag
+        // D: Wochentag gek?rzt
+        // S: Anhang der englischen Aufz?hlung f?r einen Monatstag, zwei Zeichen
+        // F: Monat als ganzes Wort, wie January oder March
+        // M: Monatsname gek?rzt
+        // g: Stunde im 12-Stunden-Format, ohne f?hrende Nullen
+        // h: Stunde im 12-Stunden-Format, mit f?hrenden Nullen
+        // a: am/pm
+        // A: AM/PM
+
+        // TODO:
+        // z: Der Tag des Jahres
+        // W: ISO-8601 Wochennummer des Jahres
+
+        d['Y'] = now.getFullYear();
+        d['n'] = now.getMonth() + 1;
+        d['j'] = now.getDate();
+        d['G'] = now.getHours();
+        d['i'] = now.getMinutes();
+        d['s'] = now.getSeconds();
+        d['w'] = now.getDay();
+        d['u'] = now.getMilliseconds();
+        d['O'] = now.getTimezoneOffset() / 60;
+        d['U'] = Math.floor(now.getTime() / 1000);
+
+        d['y'] = d['Y'] - 2000;
+        d['d'] = d['j'] < 10 ? '0' + d['j'] : d['j'];
+        d['m'] = d['n'] < 10 ? '0' + d['n'] : d['n'];
+        d['H'] = d['G'] < 10 ? '0' + d['G'] : d['G'];
+        d['i'] = d['i'] < 10 ? '0' + d['i'] : d['i'];
+        d['s'] = d['s'] < 10 ? '0' + d['s'] : d['s'];
+        d['u'] = d['u'] < 10 ? '000' + d['u'] : d['u'] < 100 ? '00' + d['u'] : d['u'] < 1000 ? '0' + d['u'] : d['u'];
+        d['N'] = d['w'] === 0 ? 7 : d['w'];
+        d['l'] = elem.data('days')[(Number(d['N']) - 1)];
+        d['D'] = d['l'].substr(0, elem.data('shortday-length'));
+        d['S'] = String(d['j']).match(/[23]?1$/) ? 'st' : String(d['j']).match(/[23]?2$/) ? 'nd' : String(d['j']).match(/[23]?3$/) ? 'rd' : 'th';
+        d['F'] = elem.data('months')[(Number(d['n']) - 1)];
+        d['M'] = d['F'].substr(0, elem.data('shortmonth-length'));
+        d['g'] = d['G'] <= 12 ? d['G'] : d['G'] - 12;
+        d['h'] = d['g'] < 10 ? '0' + d['g'] : d['g'];
+        d['a'] = d['G'] <= 12 ? 'am' : 'pm';
+        d['A'] = d['a'].toUpperCase();
+        // 'W' by mc-hollin http://forum.fhem.de/index.php/topic,34233.msg304630.html#msg304630
+        var onejan = new Date(now.getFullYear(), 0, 1);
+        var kw = Math.ceil((((now - onejan) / 86400000) + onejan.getDay() + 1) / 7);
+        d['W'] = kw < 10 ? '0' + kw : kw;
+
+        return d;
+    }
+
+    function init_datetext(format, d) {
+        // split formatstring into it's letters and replace one after the other
+        var datearr = format.split('');
+        for (var l = 0; l < datearr.length; l++) {
+            for (var key in d) {
+                if (datearr[l] == key) {
+                    datearr[l] = d[key];
+                    // stop replacing after a match
+                    break;
+                }
+            }
+        }
+        return datearr.join('');
+    }
+
+    function init_ui(elem) {
+
+        var tid = setInterval(function () {
+            if (elem && elem.data('days')) {
+
+                var d = init_datearray(elem);
+                var text = init_datetext(elem.data('format'), d);
+                elem.text(text);
+
+            } else {
+                clearInterval(tid);
+            }
+        }, elem.data('interval'));
+    }
+
+    function update(dev, par) {}
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_widget(), {
+        //override members
+        widgetname: 'clock',
+        init_ui: init_ui,
+        init_attr: init_attr,
+        update: update,
+    });
+
+    return me;
+};

+ 110 - 0
fhem/core/www/tablet_dev/js/widget_colorwheel.js

@@ -0,0 +1,110 @@
+/* FTUI Plugin
+ * Copyright (c) 2015-2016 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_widget:true */
+
+"use strict";
+
+function depends_colorwheel() {
+    if (!$.fn.farbtastic) {
+        $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'css/ftui_colorwheel.css" type="text/css" />');
+        return [ftui.config.basedir + "lib/farbtastic.js"];
+    }
+}
+
+var Modul_colorwheel = function () {
+
+    function onChange(elem, color) {
+        elem.find('.colorIndicator').css({
+            backgroundColor: color,
+        });
+    }
+
+    function onRelease(elem, color) {
+        var value = color.replace('#', '');
+        elem.data('value', value);
+        elem.transmitCommand();
+    }
+
+    function init_attr(elem) {
+        elem.initData('get', 'STATE');
+        elem.initData('set', '');
+        elem.initData('cmd', 'set');
+        elem.initData('width', 150);
+        if (elem.hasClass('big')) {
+            elem.data('width', 210);
+        }
+        if (elem.hasClass('large')) {
+            elem.data('width', 150);
+        }
+        if (elem.hasClass('small')) {
+            elem.data('width', 100);
+        }
+        if (elem.hasClass('mini')) {
+            elem.data('width', 52);
+        }
+        me.addReading(elem, 'get');
+    }
+
+    function init_ui(elem) {
+        var colorArea = $('<div/>', {
+            class: 'colorArea',
+        });
+        var colorIndicator = $('<div/>', {
+            class: 'colorIndicator',
+        }).appendTo(colorArea);
+        var colorWheel = $('<div/>', {
+                class: 'colorWheel',
+            })
+            .css({
+                width: elem.data('width'),
+            })
+            .appendTo(colorArea);
+        var farbtastic = $.farbtastic(colorWheel, {
+            width: elem.data('width'),
+            callback: function (color) {
+                onChange(elem, color);
+            },
+            release: function (color) {
+                onRelease(elem, color);
+            },
+        });
+        elem.append(colorArea);
+
+        return elem;
+    }
+
+    function update(dev, par) {
+
+        me.elements.filterDeviceReading('get', dev, par)
+            .each(function (index) {
+                var elem = $(this);
+                var value = elem.getReading('get').val;
+                var color = elem.find('.colorWheel');
+                if (value && color) {
+                    if (elem.data('isInit')) {
+                        $.farbtastic(color).setColor('#' + value);
+                    } else {
+                        setTimeout(function () {
+                            elem.data('isInit', true);
+                            $.farbtastic(color).setColor('#' + value);
+                        }, 2000);
+                    }
+                }
+            });
+    }
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_widget(), {
+        //override members
+        widgetname: 'colorwheel',
+        init_ui: init_ui,
+        init_attr: init_attr,
+        update: update,
+    });
+
+    return me;
+};

+ 156 - 0
fhem/core/www/tablet_dev/js/widget_controlbutton.js

@@ -0,0 +1,156 @@
+/* FTUI Plugin
+ * Copyright (c) 2017 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_famultibutton:true */
+
+"use strict";
+
+function depends_controlbutton() {
+    var deps = [];
+    if (!$('link[href$="css/ftui_controlbutton.css"]').length) {
+        $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'css/ftui_controlbutton.css" type="text/css" />');
+    }
+    if (typeof Module_famultibutton == 'undefined' || !$.fn.famultibutton) {
+        deps.push('famultibutton');
+    }
+    return deps;
+}
+
+var Modul_controlbutton = function () {
+
+    function clicked(elem, isOn) {
+        
+        var value = isOn ? elem.data('set-on') : elem.data('set-off');
+        elem.data('value', value);
+        elem.transmitCommand();
+    }
+    
+    function drawElement(elem, isOn) {
+                
+        var controlbuttonArea = elem.controlbuttonArea || elem.find('.controlbutton-area');
+        var controlbuttonIcon = elem.controlbuttonIcon || elem.find('.controlbutton-icon');
+
+        if (isOn) {
+            controlbuttonArea.css("background-color", elem.mappedColor('on-background-color'));
+            controlbuttonIcon.css("color", elem.data('onColor'));
+            elem.addClass('active');
+            elem.trigger('setOn');
+        } else {
+            controlbuttonArea.css("background-color", elem.data('offBackgroundColor'));
+            controlbuttonIcon.css("color", elem.data('offColor'));
+            elem.removeClass('active');
+            elem.trigger('setOff');
+        }
+        elem.data('checked',isOn);
+    }
+
+
+    function init() {
+
+        me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
+        me.elements.each(function (index) {
+            var elem = $(this);
+
+            elem.initData('icon', 'fa-lightbulb-o');
+            elem.initData('off-color', ftui.getStyle('.controlbutton.off', 'color') || '#fff');
+            elem.initData('off-background-color', ftui.getStyle('.controlbutton.off', 'background-color') ||
+                'transparent');
+            elem.initData('on-color', ftui.getStyle('.controlbutton.on', 'color') || '#000');
+            elem.initData('on-background-color', ftui.getClassColor($(this)) || ftui.getStyle(
+                '.controlbutton.on',
+                'background-color') || '#fff');
+            me.init_attr(elem);
+
+
+            // Wrapper
+            var wrapper = $('<div class="controlbutton-wrapper"></div>')
+                .css({
+                    width: elem.data('width'),
+                    height: elem.data('height')
+                });
+
+
+            // wipeArea
+            elem.controlbuttonArea = $('<div/>', {
+                    class: 'controlbutton-area',
+                })
+                .css({
+                    background: elem.mappedColor('background-color')
+                });
+
+
+            // wipeIcon
+            elem.controlbuttonIcon = $('<i/>', {
+                    class: 'controlbutton-icon fa ' + elem.data('icon')
+                })
+                .css({
+                    color: elem.mappedColor('icon-color'),
+                    background: elem.mappedColor('background-color')
+                })
+                .appendTo(elem.controlbuttonArea);
+
+
+            // Events
+            elem.on(ftui.config.clickEventType, function (e) {
+                e.stopImmediatePropagation();
+
+                elem.touch_pos_y = $(window).scrollTop();
+                elem.touch_pos_x = $(window).scrollLeft();
+                elem.clicked = true;
+            });
+
+
+            elem.on(ftui.config.releaseEventType, function (e) {
+                e.preventDefault();
+                e.stopImmediatePropagation();
+                
+                console.log(Math.abs(elem.touch_pos_y - $(window).scrollTop()) );
+
+                if ( Math.abs(elem.touch_pos_y - $(window).scrollTop()) > 3 ||
+                    Math.abs(elem.touch_pos_x - $(window).scrollLeft()) > 3 ||
+                    !elem.clicked ) return;
+
+                var isOn = !elem.data('checked');
+                elem.data('checked', isOn);
+
+                drawElement(elem, isOn);
+                me.clicked(elem, isOn);
+                elem.clicked = false;
+
+                return false;
+            });
+
+
+            // store input object for usage in update function of base class
+            elem.data("famultibutton", wrapper);
+
+            // provide On / Off functions like a famultibutton
+            wrapper.setOn = function () {
+                drawElement(elem, true);
+            };
+            wrapper.setOff = function () {
+                drawElement(elem, false);
+            };
+
+
+            // init state is off
+            drawElement(elem, false);
+
+            wrapper.append(elem.controlbuttonArea);
+            elem.append(wrapper);
+
+        });
+    }
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_famultibutton(), {
+        //override members
+        widgetname: 'controlbutton',
+        clicked: clicked,
+        init: init,
+    });
+    return me;
+};

+ 251 - 0
fhem/core/www/tablet_dev/js/widget_controller.js

@@ -0,0 +1,251 @@
+/* FTUI Plugin
+ * Copyright (c) 2017 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_widget:true */
+
+
+"use strict";
+
+function depends_controller() {
+    if (!$('link[href$="css/ftui_controller.css"]').length)
+        $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'css/ftui_controller.css" type="text/css" />');
+    return [];
+
+}
+
+var Modul_controller = function () {
+
+    function onChangeEvent(elem, e) {
+
+        if (elem.isDown) {
+        var event = e.originalEvent;
+        var position = event.touches ? event.touches[0].pageY : e.pageY;
+
+        elem.height = elem.controllerArea.height();
+        var diff = elem.height - (position - elem.controllerArea.offset().top);
+
+        if (diff > elem.height) {
+            diff = elem.height;
+        }
+        if (diff < 0) {
+            diff = 0;
+        }
+        //console.log(diff,elem.height,elem.pixel);
+        elem.pixel = diff;
+        elem.percent = diff * 100 / elem.height;
+        
+            drawLevel(elem);
+        }
+    }
+
+    function sendLevel(elem) {
+
+        if (elem.hasClass('lock')) {
+            elem.addClass('fail-shake');
+            setTimeout(function () {
+
+                elem.pixel = elem.oldPixel;
+                elem.percent = elem.oldPercent;
+                elem.removeClass('fail-shake');
+            }, 500);
+            return;
+        }
+
+        var max = parseFloat(($.isNumeric(elem.data('max'))) ? elem.data('max') : elem.getReading('max').val);
+        var min = parseFloat(($.isNumeric(elem.data('min'))) ? elem.data('min') : elem.getReading('min').val);
+        var value = elem.percent * ((max - min) + min) / 100;
+        var fix = parseInt(elem.data('fix'));
+        value = (-1 < fix && fix <= 20) ? Number(value).toFixed(fix) : value;
+
+        elem.data('value', value);
+        elem.oldPixel = elem.pixel;
+        elem.oldPercent = elem.percent;
+        elem.transmitCommand();
+    }
+
+    function drawLevel(elem) {
+
+        var controllerArea = elem.controllerArea || elem.find('.controller-area');
+        elem.height = controllerArea.height();
+        
+        var pixel = ftui.isValid(elem.pixel) ?  elem.pixel : elem.data('percent') * elem.height / 100;
+        var controllerRange = elem.controllerRange || elem.find('.controller-range');
+
+        controllerRange.css({
+            height: pixel + "px",
+            top: elem.height - pixel + "px"
+        });
+    }
+
+    function init_attr(elem) {
+
+        // init base attributes
+        _base.init_attr(elem);
+
+        // init special attributes
+        elem.initData('max', 100);
+        elem.initData('min', 0);
+        elem.initData('step', '1');
+        elem.initData('fix', ftui.precision(elem.data('step')));
+        elem.initData('height', elem.hasClass('fullsize') ? '100%' : '11em');
+        elem.initData('width', elem.hasClass('fullsize') ? '100%' : '4em');
+        elem.initData('color', ftui.getClassColor(elem) || ftui.getStyle('.' + me.widgetname + '.range', 'color') || '#fff');
+        elem.initData('background-color', ftui.getClassColor(elem) || ftui.getStyle('.' + me.widgetname + '.range', 'background-color') ||
+            'rgba(40,40,40,0.5)');
+        elem.initData('icon', 'fa-lightbulb-o');
+        elem.initData('icon-color', ftui.getClassColor(elem) || ftui.getStyle('.' + me.widgetname + '.icon', 'color') ||
+            '#aaa');
+
+
+        // numeric value means fix value, others mean it is a reading
+        if (!$.isNumeric(elem.data('max'))) {
+            me.addReading(elem, 'max');
+        }
+        if (!$.isNumeric(elem.data('min'))) {
+            me.addReading(elem, 'min');
+        }
+
+        return elem;
+    }
+
+    function init_ui(elem) {
+
+        // Wrapper
+        var wrapper = $('<div class="controller-wrapper"></div>')
+            .css({
+                width: elem.data('width'),
+                height: elem.data('height')
+            });
+
+        // wipeArea
+        elem.controllerArea = $('<div/>', {
+                class: 'controller-area',
+            })
+            .css({
+                background: elem.mappedColor('background-color')
+            });
+
+
+        // controllerRange
+        elem.controllerRange = $('<div/>', {
+                class: 'controller-range',
+            })
+            .css({
+                background: elem.mappedColor('color')
+            })
+            .appendTo(elem.controllerArea);
+
+        // wipeIcon
+        elem.controllerIcon = $('<i/>', {
+                class: 'controller-icon fa fa-2x ' + elem.data('icon')
+            })
+            .css({
+                color: elem.mappedColor('icon-color')
+            })
+            .appendTo(elem.controllerArea);
+
+        // Events
+        elem.on(ftui.config.clickEventType, function (e) {
+            e.preventDefault();
+            e.stopImmediatePropagation();
+
+            elem.isDown = true;
+            onChangeEvent(elem, e);
+        });
+
+        elem.on(ftui.config.enterEventType, function (e) {
+            e.preventDefault();
+            e.stopImmediatePropagation();
+
+            if (e.which === 0) {
+                elem.isDown = false;
+            }
+        });
+
+        elem.on(ftui.config.leaveEventType, function (e) {
+            e.preventDefault();
+            e.stopImmediatePropagation();
+
+            onChangeEvent(elem, e);
+            if ((elem.pixel <= 1 || elem.pixel >= elem.height) && elem.isDown) {
+                sendLevel(elem);
+            }
+        });
+
+        elem.on(ftui.config.releaseEventType, function (e) {
+            e.preventDefault();
+            e.stopImmediatePropagation();
+
+            if (elem.isDown) {
+                elem.isDown = false;
+                sendLevel(elem);
+            }
+
+            return false;
+        });
+
+        elem.on(ftui.config.moveEventType, function (e) {
+
+            onChangeEvent(elem, e);
+        });
+
+        // force refresh
+        $(document).on('changedSelection', function () {
+            drawLevel(elem);
+        });
+
+        wrapper.append(elem.controllerArea);
+        elem.append(wrapper);
+
+        return elem;
+    }
+
+    function update(dev, par) {
+
+        // update from normal state reading
+        me.elements.filterDeviceReading('get', dev, par)
+            .each(function (index) {
+                var elem = $(this);
+                var state = elem.getReading('get').val;
+                //console.log(state, dev, par);
+                if (state) {
+                    var part = elem.data('get-value');
+                    var val = ftui.getPart(state, part);
+                    elem.data('value', val);
+                    var max = parseFloat(($.isNumeric(elem.data('max'))) ? elem.data('max') : elem.getReading('max').val);
+                    var min = parseFloat(($.isNumeric(elem.data('min'))) ? elem.data('min') : elem.getReading('min').val);
+                    var value = parseFloat(val);
+                    value = (isNaN(value)) ? min : value;
+                    elem.data('percent', (value - min) / (max - min) * 100);
+                    drawLevel(elem);
+                }
+
+            });
+
+        //extra reading for lock
+        me.update_lock(dev, par);
+
+        //extra reading for hide
+        me.update_hide(dev, par);
+
+        //extra reading for reachable
+        me.update_reachable(dev, par);
+    }
+
+    // public
+    // inherit all public members from base class
+    var base = new Modul_widget();
+    var _base = {};
+    _base.init_attr = base.init_attr;
+    var me = $.extend(base, {
+        //override or own public members
+        widgetname: 'controller',
+        init_attr: init_attr,
+        init_ui: init_ui,
+        update: update,
+    });
+
+    return me;
+};

+ 71 - 0
fhem/core/www/tablet_dev/js/widget_datetimepicker.js

@@ -0,0 +1,71 @@
+/* FTUI Plugin
+ * Copyright (c) 2015-2016 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_label:true, Switchery:true */
+
+"use strict";
+
+function depends_datetimepicker() {
+    var deps = [];
+    if (!$.fn.datetimepicker) {
+        $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'lib/jquery.datetimepicker.css" type="text/css" />');
+        deps.push(ftui.config.basedir + "lib/jquery.datetimepicker.js");
+    }
+    if (typeof Module_label == 'undefined') {
+        deps.push('label');
+    }
+    return deps;
+}
+
+var Modul_datetimepicker = function () {
+
+    function init() {
+
+        me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
+        me.elements.each(function (index) {
+            var elem = $(this);
+            elem.data('cmd', elem.isValidData('cmd') ? elem.data('cmd') : 'set');
+            elem.data('set-value', elem.data('set-value') || '$v');
+            elem.data('unit', elem.isValidData('unit') ? elem.data('unit') : '<span style="font-size: 180%;">&#32;&#9660;</span>');
+            elem.data('format', elem.isValidData('format') ? elem.data('format') : 'Y-m-d H:i');
+            elem.data('theme', elem.isValidData('theme') ? elem.data('theme') : 'dark');
+            elem.data('timepicker', elem.isValidData('timepicker') ? elem.data('timepicker') : 'true');
+            elem.data('datepicker', elem.isValidData('datepicker') ? elem.data('datepicker') : 'true');
+            elem.data('step', elem.isValidData('step') ? elem.data('step') : '60');
+            me.init_attr(elem);
+            var picker = elem.datetimepicker({
+                lang: 'de',
+                theme: elem.data('theme'),
+                format: elem.data('format'),
+                timepicker: elem.data('timepicker'),
+                datepicker: elem.data('datepicker'),
+                step: elem.data('step'),
+                onChangeDateTime: function (dp, $input) {
+                    var val = elem.data('set-value').replace('$v', $input.val().toString());
+                    elem.text(val);
+                    elem.data('value', val);
+                    elem.transmitCommand();
+                },
+            });
+        });
+    }
+
+    function update_cb(elem, val) {
+        elem.datetimepicker({
+            value: val
+        });
+    }
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_label(), {
+        //override members
+        widgetname: 'datetimepicker',
+        init: init,
+        update_cb: update_cb,
+    });
+
+    return me;
+};

+ 189 - 0
fhem/core/www/tablet_dev/js/widget_departure.js

@@ -0,0 +1,189 @@
+/* FTUI Plugin
+ * Copyright (c) 2016 Mario Stephan <mstephan@shared-files.de>
+ * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/* global ftui:true, Modul_widget:true, Switchery:true */
+
+"use strict";
+
+
+var Modul_departure = function () {
+
+    $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'css/ftui_departure.css" type="text/css" />');
+
+    function startTimer(elem) {
+        var interval = elem.data('refresh');
+        if ($.isNumeric(interval) && interval > 0) {
+            setInterval(function () {
+                requestUpdate(elem);
+            }, Number(interval) * 1000);
+        }
+    }
+
+    function requestUpdate(elem) {
+        if (elem.is(':visible') || elem.hasClass('hiddenrefresh')) {
+
+            var cmdl = [elem.data('cmd'), elem.data('device'), elem.data('get')].join(' ');
+            ftui.log(2, 'departure - send request: ' + cmdl);
+            ftui.setFhemStatus(cmdl);
+            if (ftui.config.DEBUG) {
+                ftui.toast(cmdl);
+            }
+        }
+    }
+
+    function init_attr(elem) {
+        elem.initData('get', 'STATE');
+        elem.initData('title', elem.data('get'));
+        elem.initData('cmd', 'get');
+        elem.initData('color', ftui.getClassColor(elem) || ftui.getStyle('.' + me.widgetname, 'color') || '#222');
+        elem.initData('background-color', ftui.getStyle('.' + me.widgetname, 'background-color') || '#C0C0C0');
+        elem.initData('icon-color', ftui.getStyle('.' + me.widgetname, 'icon-color') || '#aa6900');
+        elem.initData('text-color', ftui.getStyle('.' + me.widgetname, 'text-color') || '#ddd');
+        elem.initData('width', '200');
+        elem.initData('height', '250');
+        elem.initData('refresh', elem.data('interval') || '120');
+
+        me.addReading(elem, 'get');
+    }
+
+    function init_ui(elem) {
+
+        var icon = elem.data('icon');
+
+
+        // prepare container element
+        var contElem = $('<div/>', {
+            class: 'departure-wrapper'
+        });
+
+        var innerElem = $('<div/>', {
+                class: 'departure'
+            })
+            .css({
+                width: elem.data('width') + 'px',
+                maxWidth: elem.data('width') + 'px',
+                height: elem.data('height') + 'px',
+                color: elem.mappedColor('text-color'),
+                backgroundColor: elem.mappedColor('background-color'),
+            }).prependTo(contElem);
+
+        // prepare icon
+        var elemIcon = $('<div/>', {
+                class: 'icon',
+            })
+            .css({
+                color: elem.mappedColor('icon-color'),
+            })
+            .prependTo(innerElem);
+        if (icon)
+            elemIcon.addClass('fa ' + icon + ' fa-lg fa-fw');
+        else
+            elemIcon.html('H');
+
+        // prepare station label element
+        $('<div/>', {
+                class: 'station',
+            })
+            .text(elem.data('title'))
+            .appendTo(innerElem);
+
+        // prepare refresh element
+        var elemRefresh = $('<div/>', {
+            class: 'refresh fa fa-refresh fa-fw',
+        }).appendTo(innerElem);
+
+        // prepare timestamp element
+        $('<div/>', {
+            class: 'time',
+        }).appendTo(innerElem);
+
+        // prepare list header
+        var text = '&nbsp;<div class="header">';
+        text += '<div class="line">Linie</div>';
+        text += '<div class="destination">Richtung</div>';
+        text += elem.hasClass('deptime') ? '<div class="minutes">Zeit</div></div>' : '<div class="minutes">in Min</div></div>';
+        elem.append(text);
+
+        // prepare list text element
+        $('<div/>', {
+                class: 'listText',
+            })
+            .fadeOut()
+            .appendTo(innerElem);
+
+        elem.html('').append(contElem);
+
+
+        // event handler
+        elemRefresh.on('click', function (e) {
+            requestUpdate(elem);
+        });
+
+        // init interval timer
+        startTimer(elem);
+
+        // first refresh 
+        setTimeout(function () {
+
+            requestUpdate(elem);
+
+
+            // Refresh slider position after it became visible
+            elem.closest('[data-type="popup"]').on("fadein", function (event) {
+                requestUpdate(elem);
+            });
+
+            $(document).on('changedSelection', function () {
+                requestUpdate(elem);
+            });
+        }, 5000);
+    }
+
+    function update(dev, par) {
+        // update from normal state reading
+        me.elements.filterDeviceReading('get', dev, par)
+            .each(function (index) {
+                var elem = $(this);
+                var list = elem.getReading('get').val;
+                if (ftui.isValid(list)) {
+                    var elemText = elem.find('.listText');
+                    elemText.fadeOut();
+                    var ts = elem.getReading('get').date;
+                    if (ts) {
+                        elem.find('.time').html(ts.toDate().hhmm());
+                    }
+                    var text = '';
+                    var n = 0;
+                    var collection = JSON.parse(list);
+                    for (var idx in collection) {
+                        n++;
+                        var line = collection[idx];
+                        var when = line[2];
+                        if (elem.hasClass('deptime') && !when.match(/:/)) {
+                            when = ts.toDate().addMinutes(when).hhmm();
+                        }
+                        text += (n % 2 === 0 && elem.hasClass('alternate')) ? '<div class="connection even">' : '<div class="connection">';
+                        text += '<div class="line">' + line[0] + '</div>';
+                        text += '<div class="destination">' + line[1] + '</div>';
+                        text += '<div class="minutes">' + when + '</div></div>';
+                    }
+                    elemText.html(text).fadeIn();
+                }
+            });
+
+    }
+
+    // public
+    // inherit members from base class
+    var me = $.extend(new Modul_widget(), {
+        //override members
+        widgetname: 'departure',
+        init_attr: init_attr,
+        init_ui: init_ui,
+        update: update,
+    });
+
+    return me;
+};

+ 0 - 0
fhem/core/www/tablet_dev/js/widget_dimmer.js


Some files were not shown because too many files changed in this diff