95_FLOORPLAN.pm 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333
  1. ################################################################################
  2. # 95 FLOORPLAN
  3. # $Id: 95_FLOORPLAN.pm 13735 2017-03-19 12:43:53Z UliM $
  4. # Feedback: http://groups.google.com/group/fhem-users
  5. # Define Custom Floorplans
  6. # Released : 26.02.2012
  7. # Version : 2.1
  8. # Revisions:
  9. # 0001: Released to testers
  10. # 0002: use local FP_select and FP_submit after clash with FHEMWEB update
  11. # 0003: FP_arrange_default repaired
  12. # 0004: WebApp-enabled links in floorplanlist, fixed message 'use of uninitialized value' (FW_pO - $FP_name)
  13. # 0005: Change arrange-mode: When selected, display device-name instead of selection
  14. # 0006: kicked out various routines previously copied from FHEMWEB - now using FW_*-versions thanks to addtl. global variables $FW_RET, $FW_wname, $FW_subdir, %FW_pos
  15. # 0007: Added fp_default
  16. # 0008: Changed name of background-picture from <floorplan-name> to fp_<floorplan-name> to avoid display of picture in device-list at fhem-menu 'Everything'
  17. # -> general release
  18. # 0009: updated selection of add-device-list: suppress CUL$ only (instead of CUL.*)
  19. # 0010: Added Style3, fp_stylesheetPrefix, fp_noMenu (Mar 13, 2012)
  20. # 0011: Added Style4, code beautification, css review, minor $text2-fix (SVN 1342)
  21. # 0012: Added startscreen-text when no floorplans defined, fixed startscreen-stylesheet, added div for bg-img, added arrangeByMouse (1368)
  22. # 0013: implemented redirectCmd, fixed minor </td></tr>-error in html-output, fp_arrange for single web-devices, fp_arrange detail (Mar 23, 2012)
  23. # 0014: deleted $data{FWEXT}{$fhem_url}{STYLESHEET} , added attr-values for FHEMWEB-detail-screen, adapted FHT-representation to FHT.pm updates (Apr 19, 2012)
  24. # 0015: implemented Tobias' icon subfolder solution, fp_arrange detail always (fp_arrange detail deprecated, fp_arrange 1 shows all detail),
  25. # changed backimg-size to 99% to avoid scrollbars , adopted slider & new FHT representation (May 1, 2012)
  26. # 0016: Minor repair of html-output, allowed devices with dot in name (May 2, 2012)
  27. # 0017: updating for changes in fhemweb: css-path, bgimg-path, deactivating rereadicons (July 30, 2012)
  28. # 0018: Changes by Boris (icon-paths, fp_stylesheetPrefix -> stylesheet
  29. # 0019: added fp_backgroundimg (October 15, 2012)
  30. # 0020: moved creation of userattr to define, added slider and timepicker and setList, added style5 icon+commands, added style 6 readingstimestamp,
  31. # added style-descriptions in fp-arrange (October 22, 2012)
  32. # 0021: fixed http-header, unsetting FF-autocomplete, added attribute fp_setbutton (fixes by Matthias) (November 23, 2012)
  33. # 0022: longpoll by Matthias Gehre (November 27, 2012)
  34. # 0023: longpoll updates readings also - by Matthias Gehre; FW_longpoll is now a global variable (January 21, 2013)
  35. # 0024: fix for readings longpoll, added js-extension from Dirk (February 16, 2013)
  36. # 0025: Added fp_viewport-attribute from Jens (March 03, 2013)
  37. # 0026: Adapted to FHEMWEB-changes re webCmdFn - fp_setbutton not functional (May 23, 2013)
  38. # 0027: Added FP_detailFn(), added delete-button in arrange-menu, fixed link for pdf-docu, minor code cleanup, added get config (July 08, 2013)
  39. # 0028: Implemented informid for longpoll, usage of @FW_fhemwebjs (July 19, 2013)
  40. # 0029: Fixed floorplan-specific icons and eliminated FHT-text "desired-temp" - both due to changes in fhemweb (Sep 29, 2013)
  41. # 0030: Style4 (S300TH) now works with longpoll without loosing its formatting (Dec 24, 2013)
  42. # 0031: Text "desiredTemperature" will also be eliminated - for MAX devices (Dec 25, 2013)
  43. # 0032: Ensure URL always contains floorplan-name (redirect if !htmlarg[0]) as basis for fp-specific icon-folder (Jan 06, 2014)
  44. # 0033: Updated loglevel -> verbose, added fp_roomIcons (Feb 2, 2014)
  45. # 0034: iOS fullscreen app - navigating to other floorplan doesn't open safari anymore (Feb 15, 2014)
  46. # 0035: added allowedCommands-Attribute based on FHEMWEB (Feb 20, 2014)
  47. # 0036: added style "commands only", changed html-method "get" to "$FW_formmethod" (June 19, 2014)
  48. # 0037: updated to match fhemweb.js developments (Rudi, Jan 17, 2015)
  49. # 0038: added arrange by drag&drop provided by Markus (mluckey), added longpollfilter (nesges),
  50. # added processing of global userattr fp_<name> and their value per device for rename, copy, delete (Jan 31, 2015)
  51. # 0039: added style 8 for commands-popup provided by André (justme68) (Feb 17, 2015)
  52. # 0040: fixed "no commands for IT devices", drag&drop won't switch device-status anymore (June 15, 2015)
  53. # 0041: fixed eventmap, excess "delete"-messages during rereadconfig (Sept 24, 2015)
  54. # 0042: fixed log-errors, for commandref changed link to german installation guide in fhemwiki (Nov 21, 2015)
  55. # 0043: fixed erroneous replacement of own devicename by actual devicename upon drag&drop (May 15,2016)
  56. # 0044: added csrf-token (March 2017)
  57. #
  58. ################################################################
  59. #
  60. # Copyright notice
  61. #
  62. # (c) 2012-2017 Copyright: Ulrich Maass
  63. #
  64. # This file is part of fhem.
  65. #
  66. # Fhem is free software: you can redistribute it and/or modify
  67. # it under the terms of the GNU General Public License as published by
  68. # the Free Software Foundation, either version 2 of the License, or
  69. # (at your option) any later version.
  70. #
  71. # Fhem is distributed in the hope that it will be useful,
  72. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  73. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  74. # GNU General Public License for more details.
  75. #
  76. # You should have received a copy of the GNU General Public License
  77. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  78. #
  79. ################################################################################
  80. # Usage
  81. # define <NAME> FLOORPLAN
  82. #
  83. # Step-by-Step HowTo - mind all is case sensitive:
  84. # Step 1:
  85. # define <name> FLOORPLAN
  86. # Example: define Groundfloor FLOORPLAN
  87. #
  88. # Step 2:
  89. # store picture fp_<name>.png in your imagepath. This will be used as background-picture.
  90. # Example: fhem/www/images/default/Groundfloor.png
  91. #
  92. # Step 3:
  93. # Activate 'Arrange-Mode' to have user-friendly fields to move items:
  94. # attr <floorplanname> fp_arrange 1
  95. # Example: attr Groundfloor fp_arrange 1
  96. # Delete this attribute when you're done with setup
  97. # Arrange-Mode displays a popup to add and edit how devices will be shown. Arrange devices using drag&drop.
  98. # When adding objects to your floorplan, they will thereby get assigned
  99. # attr <device> fp_<name> <top>,<left>,<style>,<text>
  100. # displays <device> on floorplan <name> at position top:<top>,left:<left> with style <style> and description <text>
  101. # Example: attr lamp fp_Groundfloor 100,100,1,TableLamp #displays lamp at position 100px,100px
  102. #
  103. # Repeat step 3 to add further devices. Delete attr fp_arrange when all devices are arranged on your screen. Enjoy.
  104. #
  105. # Check the colorful pdf-docu in http://sourceforge.net/p/fhem/code/HEAD/tree/trunk/fhem/docs/fhem-floorplan-installation-guide_de.pdf
  106. #
  107. ################################################################################
  108. package main;
  109. use strict;
  110. use warnings;
  111. use vars qw(%data);
  112. use vars qw($FW_longpoll);
  113. use vars qw(@FW_fhemwebjs); # List of fhemweb*js scripts to load - from FHEMWEB
  114. #########################
  115. # Forward declaration
  116. sub FLOORPLAN_Initialize($); # Initialize
  117. sub FP_define(); # define <name> FLOORPLAN
  118. sub FP_undefine($$); # delete <name>
  119. sub FP_rename($$); # rename <old> <new>
  120. sub FP_copy($$); # copy <old> <new>
  121. sub FP_copy_rename_delete($$$); # exec for copy, rename, delete
  122. sub FP_Get($@); # get-command
  123. sub FP_CGI(); # analyze URL
  124. sub FP_digestCgi($); # digest CGI
  125. sub FP_htmlHeader($); # html page - header
  126. sub FP_menu(); # html page - menu left - floorplan-list
  127. sub FP_menuArrange(); # html page - menu bottom - arrange-mode
  128. sub FP_showstart(); # html page - startscreen
  129. sub FP_show(); # produce floorplan
  130. sub FP_input(@); # prepare selection list for forms
  131. sub FP_detailFn($$$$); # floorplan-specific detail-screen in fhemweb
  132. sub FP_getConfig($); # display floorplan configuration
  133. sub FP_pOfill($@); # print line filled up with hash-signs
  134. #########################
  135. # Global variables
  136. # $ret_html; # from FHEMWEB: Returned data (html)
  137. my $FP_name; # current floorplan-name
  138. my $FP_arrange; # arrange-mode
  139. my $FP_arrange_selected; # device selected to be arranged
  140. my $FP_arrange_default; # device selected in previous round
  141. my %FP_webArgs = (); # sections of analyzed URL
  142. my $FP_fwdetail; # set when floorplan is called from fhemweb-detailscreen
  143. my $FP_viewport; # Define width for touchpad device
  144. # $FW_ME # from FHEMWEB: fhem URL
  145. # $FW_CSRF # from FHEMWEB: CSRF Token or empty
  146. # $FW_tp # from FHEMWEB: is touchpad
  147. # $FW_ss # from FHEMWEB: is smallscreen
  148. # $FW_longpoll; # from FHEMWEB: longpoll
  149. # $FW_wname; # from FHEMWEB: name of web-instance
  150. # %FW_pos=(); # from FHEMWEB: scroll position
  151. # $FW_subdir # from FHEMWEB: path of floorplan-subdir - enables reusability of FHEMWEB-routines for sub-URLS like floorplan
  152. # $FW_cname # from FHEMWEB: Current connection name
  153. my $FW_encoding="UTF-8"; # like in FHEMWEB: encoding hardcoded
  154. my $FW_plotmode=""; # like in FHEMWEB: SVG
  155. my $FW_plotsize; # like in FHEMWEB: like in fhemweb dependent on regular/smallscreen/touchpad
  156. my %FW_zoom; # copied from FHEMWEB - using local version to avoid global variable
  157. my @FW_zoom; # copied from FHEMWEB - using local version to avoid global variable
  158. my @styles = ("0 (Icon only)","1 (Name+Icon)","2 (Name+Icon+Commands)","3 (Device-Reading)","4 (S300TH-specific)","5 (Icon+Commands)",
  159. "6 (Reading+Timestamp)","7 (Commands only)","8 (Icon+Commands popup)");
  160. #-------------------------------------------------------------------------------
  161. ##################
  162. # method 'initialize'
  163. sub
  164. FLOORPLAN_Initialize($)
  165. {
  166. my ($hash) = @_;
  167. $hash->{DefFn} = "FP_define";
  168. $hash->{UndefFn} = "FP_undefine";
  169. $hash->{RenameFn} = "FP_rename";
  170. $hash->{CopyFn} = "FP_copy";
  171. $hash->{GetFn} = "FP_Get";
  172. $hash->{FW_detailFn} = "FP_detailFn"; #floorplan-specific detail-screen
  173. $hash->{AttrList} = "refresh fp_arrange:1,detail,WEB,0 commandfield:1,0 fp_default:1,0 ".
  174. "stylesheet fp_noMenu:1,0 fp_backgroundimg fp_setbutton:1,0 fp_viewport ".
  175. "CssFiles JavaScripts fp_roomIcons ";
  176. # CGI
  177. my $name = "floorplan";
  178. my $fhem_url = "/" . $name ;
  179. $data{FWEXT}{$fhem_url}{FUNC} = "FP_CGI";
  180. $data{FWEXT}{$fhem_url}{LINK} = $name;
  181. $data{FWEXT}{$fhem_url}{NAME} = "Floorplans";
  182. $modules{_internal_}{AttrList} .= " VIEW_CSS"; # Global-Config for CSS
  183. my $n = 0;
  184. @FW_zoom = ("qday", "day","week","month","year"); #copied from FHEMWEB - using local version to avoid global variable
  185. %FW_zoom = map { $_, $n++ } @FW_zoom; #copied from FHEMWEB - using local version to avoid global variable
  186. return undef;
  187. }
  188. #-------------------------------------------------------------------------------
  189. ##################
  190. # method 'define'
  191. sub
  192. FP_define(){
  193. my ($hash, $def) = @_;
  194. $hash->{STATE} = 'Defined';
  195. my $name = $hash->{NAME};
  196. if (AttrVal("global","userattr","") !~ m/fp_$name/) {
  197. addToAttrList("fp_$name"); # create userattr fp_<name> if it doesn't exist yet
  198. Log3 $name, 3, "Floorplan - added global userattr fp_$name";
  199. }
  200. return undef;
  201. }
  202. #-------------------------------------------------------------------------------
  203. ##################
  204. # method 'undefine'
  205. sub
  206. FP_undefine($$) {
  207. my ($hash,$arg) = @_;
  208. FP_copy_rename_delete('delete',$hash->{NAME},undef) if ($reread_active != 1); # do not execute during rereadcfg
  209. return undef;
  210. }
  211. #-------------------------------------------------------------------------------
  212. ##################
  213. # FLOORPLAN rename
  214. sub
  215. FP_rename($$) {
  216. my ($new,$old) = @_;
  217. FP_copy_rename_delete('rename',$old,$new);
  218. return undef;
  219. }
  220. #-------------------------------------------------------------------------------
  221. ##################
  222. # FLOORPLAN copy
  223. sub
  224. FP_copy($$) {
  225. my ($old,$new) = @_;
  226. FP_copy_rename_delete('copy',$old,$new);
  227. return undef;
  228. }
  229. #-------------------------------------------------------------------------------
  230. ##################
  231. # FLOORPLAN copy_rename_delete
  232. sub
  233. FP_copy_rename_delete($$$) {
  234. my ($cmd,$old,$new) = @_;
  235. my $aold = "fp_$old";
  236. my $anew = $new ? "fp_$new" : '';
  237. Log3 $old, 3, "Floorplan - $cmd $old - processing...";
  238. if ( ($cmd =~ m/(copy|rename)/) && (AttrVal("global","userattr","") !~ m/$anew/) ) {
  239. addToAttrList("$anew"); # create userattr fp_<name> if it doesn't exist yet for $new
  240. Log3 $old, 3, "Floorplan - added global userattr $anew";
  241. }
  242. foreach my $d (sort keys %defs) {
  243. next if (!$attr{$d}{"$aold"} && !$attr{$d}{"$anew"});
  244. if ($cmd =~ m/(copy|rename)/) {
  245. AnalyzeCommand(undef, "attr $d $anew ".AttrVal($d,$aold,""),AttrVal($FW_wname,"allowedCommands",undef));
  246. Log3 $new, 3, "Floorplan - added attr $d $anew ".AttrVal($d,$anew,"");
  247. }
  248. if ($cmd =~ m/(rename|delete)/) {
  249. Log3 $old, 3, "Floorplan - deleted attr $d $aold ".AttrVal($d,$aold,"");
  250. AnalyzeCommand(undef, "deleteattr $d $aold",AttrVal($FW_wname,"allowedCommands",undef));
  251. }
  252. }
  253. if ($cmd =~ m/(rename|delete)/) {
  254. my $ua = $attr{global}{userattr};
  255. $ua =~ s/ $aold//;
  256. AnalyzeCommand(undef, "attr global userattr $ua",AttrVal($FW_wname,"allowedCommands",undef));
  257. Log3 $old, 3, "Floorplan - deleted global userattr $aold"; # delete the global userattr if $old gets deleted
  258. }
  259. Log3 $old, 3, "Floorplan - $cmd $old - complete.";
  260. return undef;
  261. }
  262. #-------------------------------------------------------------------------------
  263. ##################
  264. # FLOORPLAN get
  265. sub
  266. FP_Get($@) {
  267. my ($hash, @a) = @_;
  268. my $arg = (defined($a[1]) ? $a[1] : ""); #command
  269. my $name = $hash->{NAME};
  270. return "use command: get <dev> getConfig" if ($#a != 1 || $arg ne "config");
  271. return FP_getConfig( $name ) if ($arg eq "config");
  272. }
  273. #-------------------------------------------------------------------------------
  274. ##################
  275. # FP MAIN: Answer URL call
  276. sub
  277. FP_CGI(){
  278. my ($htmlarg) = @_; #URL
  279. ## reset parameters
  280. $FP_name = undef;
  281. my ($p,$v) = ("",""); #parameter and value of analyzed URL
  282. $FW_RET = ""; # blank out any html-code written so far by fhemweb
  283. $FW_subdir = "";
  284. $FW_longpoll = AttrVal($FW_wname, "longpoll", undef); # longpoll
  285. $FW_plotmode = AttrVal($FW_wname, "plotmode", "SVG");
  286. $FW_plotsize = AttrVal($FW_wname, "plotsize", $FW_ss ? "480,160" :
  287. $FW_tp ? "640,160" : "800,160");
  288. $htmlarg =~ s/^\///; # eliminate leading /
  289. ## derive floorplan-name
  290. my @params = split(/\//,$htmlarg); # split URL by /
  291. if ($params[2]) { # URL with CGI-parameters has addtl / -> use $FP_name
  292. $FP_name = $params[1];
  293. $params[1] = $params[2];
  294. }
  295. my @htmlpart = split("\\?", $params[1]) if ($params[1]); # split URL by ? -> htmlpart[0] = FP_name, htmlpart[1] = commandstring
  296. if (!$htmlpart[1] && $htmlpart[0] && $htmlpart[0] !~ "\\?") { # in case of 'post' URL does not contain ?
  297. $htmlpart[0] =~ /([a-z0-9.:_]+)&(.*)/i;
  298. $htmlpart[1] = $2 if ($2);
  299. $htmlpart[1] =~ s/\\+/&/g if ($htmlpart[1]);
  300. $htmlpart[0] = $1 if ($1);
  301. }
  302. $FP_name = $htmlpart[0] if (!$FP_name);
  303. ### set global parameters, check floorplan-name
  304. if ($FP_name) { # floorplan-name is part of URL
  305. if(!defined($defs{$FP_name}) && $FP_name ne "floorplanstartpage"){
  306. $FW_RET = "ERROR: Floorplan $FP_name not defined \n"; # check for typo in URL
  307. return ("text/plain; charset=$FW_encoding", $FW_RET);
  308. }
  309. $FP_arrange = AttrVal($FP_name, "fp_arrange", 0); # set arrange mode
  310. $FP_viewport = AttrVal($FP_name, "fp_viewport", "width=768") if ($FP_name); # viewport definition
  311. $FW_subdir = "/floorplan/$FP_name";
  312. } else { # no floorplan-name in URL....
  313. $FP_arrange_default = undef;
  314. $FP_arrange_selected = undef;
  315. my $dev = undef;
  316. my $tmpname = undef;
  317. my $cnt = 0;
  318. foreach my $fp (keys %defs) {
  319. next if ($defs{$fp}{TYPE} ne "FLOORPLAN");
  320. if (AttrVal($fp, "fp_default", undef)) { # use floorplan with attr fp_default
  321. $FP_name = $fp;
  322. last;
  323. } else {
  324. $tmpname=$fp;
  325. $cnt++;
  326. }
  327. }
  328. $FP_name = $tmpname if (!$FP_name && $cnt==1); # otherwise, if only one floorplan, use that one
  329. $FP_name = "floorplanstartpage" if (!$FP_name); # otherwise go to startpage
  330. $FW_subdir = "/floorplan/$FP_name";
  331. $FP_arrange = AttrVal($FP_name, "fp_arrange", 0);
  332. }
  333. ### process cgi
  334. my $commands = FP_digestCgi($htmlpart[1]) if $htmlpart[1]; # analyze URL-commands
  335. my $FP_ret = AnalyzeCommand(undef, $commands,
  336. AttrVal($FW_wname,"allowedCommands",undef)) ; # Execute commands
  337. Log3 "FLOORPLAN", 1, "FLOORPLAN: regex-error. commands: $commands, FP_name: $FP_name, FP_ret: $FP_ret" if($FP_ret && ($FP_ret =~ m/regex/ )); #errormessage
  338. ### redirect URL - either back to fhemweb-detailscreen, or for redirectCmds to suppress repeated execution of commands upon browser refresh
  339. my $me = $defs{$FW_cname}; # from FHEMWEB: Current connection name
  340. my $tgt = undef;
  341. if( !$htmlpart[0] || (AttrVal($FW_wname, "redirectCmds", 1) && $me && $commands && !$FP_ret)) {
  342. if($FP_name) {
  343. $tgt = "/floorplan/$FP_name"
  344. } else {
  345. $FW_RET = 'ERROR: floorplan-name could not be derived from URL, fp_default or single floorplanname.';
  346. return ("text/plain; charset=$FW_encoding", $FW_RET);
  347. }
  348. }
  349. $tgt = "?detail=$FP_fwdetail" if ($FP_fwdetail); #return to fhemweb-detail-screen if coming from there
  350. if ($tgt) {
  351. my $tgt = $FW_ME.$tgt;
  352. my $c = $me->{CD};
  353. print $c "HTTP/1.1 302 Found\r\n",
  354. "Content-Length: 0\r\n",
  355. "Location: $tgt\r\n",
  356. "\r\n";
  357. return;
  358. }
  359. ### output html-pages
  360. if($FP_name eq "floorplanstartpage") {
  361. FP_showStart(); # show Startscreen if zero or more than one floorplan, and none with fp_default assigned
  362. } else {
  363. FP_show(); # show floorplan
  364. }
  365. # finish HTML & leave back to FHEMWEB
  366. FW_pO "</html>\n";
  367. $FW_subdir = "";
  368. return ("text/html; charset=$FW_encoding", $FW_RET); # $FW_RET composed by FW_pO, FP_pH etc
  369. }
  370. #-------------------------------------------------------------------------------
  371. ###########################
  372. # Digest CGI parameters - portion after '?' in URL
  373. sub
  374. FP_digestCgi($) {
  375. my ($arg) = @_;
  376. my (%arg, %val, %dev, %deva, %attr, %top, %left, %style, %text);
  377. my ($cmd, $c) = ("","","");
  378. %FW_pos = ();
  379. %FP_webArgs = ();
  380. $FP_fwdetail = undef;
  381. $arg =~ s,^[?/],,;
  382. foreach my $pv (split("&", $arg)) { # per each URL-section devided by &
  383. next if($pv eq ""); # happens when post forgot to set FW_ME
  384. $pv =~ s/\+/ /g;
  385. $pv =~ s/%([\dA-F][\dA-F])/chr(hex($1))/ige;
  386. my ($p,$v) = split("=",$pv, 2); # $p = parameter, $v = value
  387. $v =~ s/[\r]\n/\\\n/g if($v && $p && $p ne "data"); # Multiline: escape the NL for fhem
  388. $FP_webArgs{$p} = $v;
  389. if($p eq "arr.dev") { $v =~ m,^([\.\w]*)\s\(,; $v = $1 if ($1); $FP_arrange_selected = $v; $FP_arrange_default = $v; }
  390. if($p eq "add.dev") { $v =~ m,^([\.\w]*)\s\(,; $v = $1 if ($1); $cmd = "attr $v fp_$FP_name 50,200,1"; }
  391. if($p eq "cmd") { $cmd = $v; }
  392. if($p =~ m/^cmd\.(.*)$/) { $cmd = $v; $c = $1; }
  393. if($p =~ m/^detl\.(.*)$/) { $FP_fwdetail = $1; }
  394. if($p =~ m/^dev\.(.*)$/) { $dev{$1} = $v; }
  395. if($p =~ m/^arg\.(.*)$/) { $arg{$1} = $v; }
  396. if($p =~ m/^val\.(.*)$/) { $val{$1} = $v; }
  397. if($p =~ m/^deva\.(.*)$/) { $deva{$1} = $v; $FP_arrange_selected = undef; }
  398. if($p =~ m/^attr\.(.*)$/) { $attr{$1} = $v; }
  399. if($p =~ m/^top\.(.*)$/) { $top{$1} = $v; }
  400. if($p =~ m/^left\.(.*)$/) { $left{$1} = $v; }
  401. if($p =~ m/^style\.(.*)$/) { $style{$1} = int(substr($v,0,2)); }
  402. if($p =~ m/^text\.(.*)$/) { $text{$1} = $v; }
  403. if($p eq "pos") { %FW_pos = split(/[=;]/, $v); }
  404. }
  405. my $dele = ($cmd =~ m/^deleteattr/);
  406. $cmd.=" $dev{$c}" if(defined($dev{$c})); # FHT device
  407. $cmd.=" $arg{$c}" if(defined($arg{$c})&&
  408. ($arg{$c} ne "state" || $cmd !~ m/^set/)); # FHT argument (e.g. desired-temp)
  409. $cmd.=" $val{$c}" if(defined($val{$c})); # FHT value
  410. $cmd.=" $deva{$c}" if(defined($deva{$c})); # arrange device
  411. $cmd.=" $attr{$c}" if(defined($attr{$c})); # arrange attr
  412. $cmd.=" $top{$c}" if(defined($top{$c}) && !$dele); # arrange top
  413. $cmd.=",$left{$c}" if(defined($left{$c}) && !$dele); # arrange left
  414. $cmd.=",$style{$c}" if(defined($style{$c})&& !$dele); # arrange style
  415. $cmd.=",$text{$c}" if(defined($text{$c}) && !$dele); # arrange text
  416. return $cmd;
  417. }
  418. #-------------------------------------------------------------------------------
  419. ##################
  420. # Page header, set webapp & css
  421. sub
  422. FP_htmlHeader($) {
  423. my $title = shift;
  424. $title = "FHEM floorplan" if (!$title);
  425. ### Page start
  426. $FW_RET = "";
  427. $FW_RET .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'."\n";
  428. $FW_RET .= '<html xmlns="http://www.w3.org/1999/xhtml">'."\n";
  429. FW_pO "<head root=\"$FW_ME\">\n<title>$title</title>";
  430. FW_pO "<meta charset=\"$FW_encoding\">"; # Forum 28666
  431. # Enable WebApp
  432. if($FW_tp || $FW_ss) {
  433. FW_pO "<link rel=\"apple-touch-icon-precomposed\" href=\"" . FW_IconURL("fhemicon") . "\"/>";
  434. FW_pO "<meta name=\"apple-mobile-web-app-capable\" content=\"yes\"/>";
  435. if($FW_ss) {
  436. FW_pO "<meta name=\"viewport\" content=\"width=320\"/>";
  437. } elsif($FW_tp) {
  438. FW_pO "<meta name=\"viewport\" content=\"".$FP_viewport."\"/>";
  439. }
  440. }
  441. # refresh-value
  442. my $rf = AttrVal($FW_wname, "refresh", "");
  443. FW_pO "<meta http-equiv=\"refresh\" content=\"$rf\">" if($rf); # use refresh-value from Web-Instance
  444. ########################
  445. # CSS
  446. my $cssTemplate = "<link href=\"$FW_ME/%s\" rel=\"stylesheet\"/>";
  447. #FW_pO sprintf($cssTemplate, "pgm2/style.css");
  448. FW_pO sprintf($cssTemplate, "pgm2/jquery-ui.min.css");
  449. map { FW_pO sprintf($cssTemplate, $_); }
  450. split(" ", AttrVal($FP_name, "CssFiles", ""));
  451. my $defaultcss = AttrVal($FW_wname, "stylesheetPrefix", "") . "floorplanstyle.css";
  452. my $css= AttrVal($FP_name, "stylesheet", $defaultcss);
  453. FW_pO "<link href=\"$FW_ME/css/$css\" rel=\"stylesheet\"/>";
  454. ########################
  455. # JavaScripts
  456. my $jsTemplate = '<script type="text/javascript" src="%s"></script>';
  457. FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/jquery.min.js");
  458. FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/jquery-ui.min.js");
  459. # FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/floorplan_click.js"); #enlarge clickable area per widget
  460. if ($FP_arrange && ($FP_arrange eq "1" || ($FP_arrange eq $FW_wname) || $FP_arrange eq "detail")) {
  461. FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/floorplan_drag.js") ; #arrange-mode drag&drop
  462. }
  463. #######################
  464. # Other JavaScripts + their Attributes
  465. map { FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/$_") } @FW_fhemwebjs;
  466. $jsTemplate = '<script attr=\'%s\' type="text/javascript" src="%s"></script>'."\n";
  467. map {
  468. my $n = $_; $n =~ s+.*/++; $n =~ s/.js$//; $n =~ s/fhem_//; $n .= "Param";
  469. FW_pO sprintf($jsTemplate, AttrVal($FP_name, $n, ""), "$FW_ME/$_");
  470. } split(" ", AttrVal($FP_name, "JavaScripts", ""));
  471. ########################
  472. # FW Extensions
  473. if(defined($data{FWEXT})) {
  474. foreach my $k (sort keys %{$data{FWEXT}}) {
  475. my $h = $data{FWEXT}{$k};
  476. next if($h !~ m/HASH/ || !$h->{SCRIPT} || $h->{SCRIPT} =~ m+pgm2/jquery+);
  477. my $script = $h->{SCRIPT};
  478. $script = ($script =~ m,^/,) ? "$FW_ME$script" : "$FW_ME/pgm2/$script";
  479. FW_pO sprintf($jsTemplate, "", $script) if ($script);
  480. }
  481. }
  482. my $csrf= ($FW_CSRF ? "fwcsrf='$defs{$FW_wname}{CSRFTOKEN}'" : "");
  483. my $gen = 'generated="'.(time()-1).'"';
  484. my $lp = 'longpoll="'.AttrVal($FW_wname,"longpoll",1).'"';
  485. my $lpf = 'longpollfilter="fp_'.$FP_name.'=.%2B"'; #longpollfilter - only devices present on this floorplan shall be notified
  486. $lpf =~ s/:/%3/g;
  487. FW_pO "</head>\n<body name=\"$title\" $gen $lp $lpf $csrf>";
  488. }
  489. #-------------------------------------------------------------------------------
  490. ##################
  491. # show startscreen
  492. sub
  493. FP_showStart() {
  494. FP_htmlHeader("Floorplans"); # incl. body-tag
  495. FW_pO "<div id=\"logo\"></div>";
  496. FP_menu();
  497. FW_pO "<div class=\"screen\" id=\"hdr\">";
  498. FW_pO "<form method=\"$FW_formmethod\" action=\"" . $FW_ME . "\">";
  499. FW_pO "<table WIDTH=\"100%\"><tr>";
  500. FW_pO "<td><input type=\"text\" name=\"cmd\" size=\"30\"/></td>"; #input-field
  501. FW_pO "</tr></table>";
  502. FW_pO "</form></div>";
  503. # no floorplans defined? -> show message
  504. my $count=0;
  505. foreach my $f (sort keys %defs) {
  506. next if ($defs{$f}{TYPE} ne "FLOORPLAN");
  507. $count++;
  508. }
  509. if ($count == 0) {
  510. FW_pO '<div id="startcontent">';
  511. FW_pO "<br><br><br><br>No floorplans have been defined yet. For definition, enter<br>";
  512. FW_pO "<ul><code>define &lt;name&gt; FLOORPLAN</code></ul>";
  513. FW_pO "Also check the <a href=\"$FW_ME/docs/commandref.html#FLOORPLAN\">commandref</a><br>";
  514. FW_pO "</div>";
  515. }
  516. FW_pO "</body>";
  517. }
  518. #-------------------------------------------------------------------------------
  519. ##################
  520. # show floorplan
  521. sub
  522. FP_show(){
  523. ### Page start
  524. FP_htmlHeader("$FP_name"); ## incl. body-tag
  525. my $onload = $FW_longpoll ? "onload=\"FW_delayedStart()\"" : "";
  526. FW_pO "<div id=\"backimg\" style=\"width: 99%; height: 99%;\">";
  527. FW_pO FW_makeImage(AttrVal($FP_name, "fp_backgroundimg", "fp_$FP_name"));
  528. FW_pO "</div>\n";
  529. ## menus
  530. FP_menu();
  531. FP_menuArrange() if ($FP_arrange && ($FP_arrange eq "1" || ($FP_arrange eq $FW_wname) || $FP_arrange eq "detail")); #shows the arrange-menu
  532. ## start floorplan
  533. FW_pO "<div class=\"screen\" id=\"floorplan\">";
  534. FW_pO "<div id=\"logo\"></div>";
  535. ## commandfield in floorplan
  536. if (AttrVal("$FP_name", "commandfield", undef)) {
  537. FW_pO "<div id=\"hdr\">\n";
  538. FW_pO " <form>";
  539. FW_pO " <input type=\"text\" name=\"cmd\" size=\"30\"/>\n"; #fhem-commandfield
  540. FW_pO " </form>";
  541. FW_pO "</div>\n";
  542. }
  543. ## let's go
  544. foreach my $d (sort keys %defs) { # loop all devices
  545. my $type = $defs{$d}{TYPE};
  546. my $attr = AttrVal("$d","fp_$FP_name", undef);
  547. next if(!$attr || $type eq "weblink"); # skip if device-attribute not set for current floorplan-name
  548. my ($top, $left, $style, $text, $text2) = split(/,/ , $attr);
  549. # $top = position in px, top
  550. # $left = position in px, left
  551. # $style = style (0=icon only, 1=name+icon, 2=name+icon+commands, 3=device-Reading + name from $text2, 4=S300TH, 5=icon+commands, 6 device-Reading+timestamp, 7 command only, 8 icon+commands popup)
  552. # $text = alternativeCaption
  553. # $text2 = special for style3+6: $text = ReadingID, $text2=alternativeCaption
  554. $top = 0 if (!$top);
  555. $left = 0 if (!$left);
  556. $style = 0 if (!$style);
  557. # start device-specific table
  558. my $t1 = $text ? $text : InternalVal($d,'NAME',' ');
  559. my $t2 = $text2 ? $text2 : " ";
  560. # wrapper-div needed for floorplan_drag.js and for positioning
  561. FW_pO "\n<div fp_style=\"$style\" fp_text=\"$t1\" fp_text2=\"$t2\" fp_name=\"$FP_name\" class=\"fp_device_div\" style=\"position:absolute; top:".$top."px; left:".$left."px;\" id=\"div-$d\">";
  562. FW_pO "<form method=\"$FW_formmethod\" action=\"$FW_ME/floorplan/$FP_name/$d\" autocomplete=\"off\">";
  563. FW_pO " <table class=\"$type fp_$FP_name\" id=\"table-$d\" align=\"center\">"; # Main table per device
  564. my ($allSets, $cmdlist, $txt) = FW_devState($d, '');
  565. $allSets = FW_widgetOverride($d, $allSets);
  566. $txt = ReadingsVal($d, $text, "Undefined Reading $d-<b>$text</b>") if ($style == 3 || $style == 6); # Style3+6 = DeviceReading given in $text
  567. my $cols = ($cmdlist ? (split(":", $cmdlist)) : 0); # Need command-count for colspan of devicename+state
  568. ########################
  569. # Device-name per device
  570. if ($style gt 0 && $style ne 5 && $style ne 7) {
  571. FW_pO " <tr class=\"devicename fp_$FP_name\" id=\"$d-devicename\">"; # For css: class=devicename, id=<devicename>-devicename
  572. my $devName = "";
  573. if ($style == 3 || $style == 6) {
  574. $devName = $text2 ? $text2 : ""; # Style 3 = Reading - use last part of comma-separated description
  575. } else {
  576. $devName = ($text ? $text : AttrVal($d, "alias", $d));
  577. }
  578. if ($style == 4 && $txt =~ /T: ([\-0-9\.]+)[ ]+H: ([\-0-9\.]+).*/) { # S300TH-specific
  579. $txt = "<span class='fp_tempvalue' display=inline><span informId=$d-temperature>".$1."</span>&deg;C</span><BR><span class='fp_humvalue'><span informId=$d-humidity>".$2."</span>%</span>";
  580. }
  581. FW_pO "<td colspan=\"$cols\">";
  582. FW_pO "$devName" ;
  583. FW_pO "</td></tr>";
  584. }
  585. ########################
  586. # Device-state per device
  587. if ($style != 7) {
  588. if ($style == 3 || $style == 6) {
  589. FW_pO "<tr class=\"devicereading fp_$FP_name\" id=\"$d"."-$text\">"; # For css: class=devicereading, id=<devicename>-<reading>
  590. } else {
  591. FW_pO "<tr class=\"devicestate fp_$FP_name\" id=\"$d\">"; # For css: class=devicestate, id=<devicename>
  592. }
  593. $txt =~ s/measured-temp: ([\.\d]*) \(Celsius\)/$1/; # format FHT-temperature
  594. ### use device-specific icons according to userattr fp_image or fp_<floorplan>.image
  595. my $fp_image = AttrVal("$d", "fp_image", undef); # floorplan-independent icon
  596. my $fp_fpimage = AttrVal("$d","fp_$FP_name".".image", undef); # floorplan-dependent icon
  597. if ($fp_image) {
  598. my $state = ReadingsVal($d, "state", undef);
  599. $fp_image =~ s/\{state\}/$state/; # replace {state} by actual device-status
  600. $txt =~ s/\<img\ src\=\"(.*)\"/\<img\ src\=\"\/fhem\/icons\/$fp_image\"/; # replace icon-link in html
  601. $txt =~ s/\<img\ (.*) src\=\"(.*)\"/\<img\ $1 src\=\"\/fhem\/images\/default\/$fp_image\"/; # replace icon-link in html (new)
  602. }
  603. if ($fp_fpimage) {
  604. my $state = ReadingsVal($d, "state", undef);
  605. $fp_fpimage =~ s/\{state\}/$state/; # replace {state} by actual device-status
  606. $txt =~ s/\<img\ src\=\"(.*)\"/\<img\ src\=\"\/fhem\/icons\/$fp_fpimage\"/; # replace icon-link in html
  607. $txt =~ s/\<img\ (.*) src\=\"(.*)\"/\<img\ $1 src\=\"\/fhem\/images\/default\/$fp_fpimage\"/; # replace icon-link in html (new)
  608. }
  609. if ($style == 3 || $style == 6) {
  610. FW_pO "<td><div informId=\"$d-$text\">$txt</div>"; # reading
  611. } elsif ($style == 4) {
  612. FW_pO "<td>$txt"; # state style4
  613. } elsif ($cmdlist && $style == 8) {
  614. # first collect the popup data
  615. } else {
  616. FW_pO "<td informId=\"$d\" colspan=\"$cols\">$txt"; # state
  617. }
  618. FW_pO "</td></tr>";
  619. }
  620. if ($style == 6) { # add ReadingsTimeStamp for style 6
  621. $txt="";
  622. FW_pO "<tr class=\"devicetimestamp fp_$FP_name\" id=\"$d-devicetimestamp\">"; # For css: class=devicetimestamp, id=<devicename>-devicetimestamp
  623. $txt = ReadingsTimestamp($d, $text, "Undefined Reading $d-<b>$text</b>"); # Style3+6 = DeviceReading given in $text
  624. FW_pO "<td><div colspan=\"$cols\" informId=\"$d-$text-ts\">$txt</div>";
  625. FW_pO "</td></tr>";
  626. }
  627. ########################
  628. # Commands per device
  629. if($cmdlist && ( $style == 2 || $style == 5 || $style == 7 || $style == 8) ) {
  630. my @a = split("[: ]", AttrVal($d, "cmdIcon", "")); #new
  631. Log 1, "ERROR: bad cmdIcon definition for $d" if(@a % 2); #new
  632. my %cmdIcon = @a; #new
  633. my $firstIdx = 0;
  634. FW_pO " <tr class=\"devicecommands\" id=\"$d-devicecommands\">";
  635. my $oldMe = $FW_ME;
  636. my $h = "";
  637. foreach my $cmd (split(":", $cmdlist)) {
  638. # Special handling (slider, dropdown, timepicker, ...)
  639. my $htmlTxt;
  640. my @c = split(' ', $cmd);
  641. if(int(@c) && $allSets && $allSets =~ m/\b$c[0]:([^ ]*)/) {
  642. my $values = $1;
  643. $FW_ME = "$FW_ME/floorplan/$FP_name";
  644. foreach my $fn (sort keys %{$data{webCmdFn}}) {
  645. my $FW_room = ''; ##needed to be able to reuse code from FHEMWEB
  646. no strict "refs";
  647. if ($data{webCmdFn}{$fn}) {
  648. $htmlTxt = &{$data{webCmdFn}{$fn}}($FW_ME,
  649. $d, $FW_room, $cmd, $values);
  650. }
  651. $FW_ME = $oldMe if (defined($htmlTxt) && $htmlTxt eq '');
  652. use strict "refs";
  653. last if(defined($htmlTxt));
  654. }
  655. }
  656. if( $style == 8 ) {
  657. if( $htmlTxt ) {
  658. $h .= "<p>$htmlTxt</p>";
  659. } elsif($cmd) {
  660. my $link = "cmd.$d=set $d $cmd";
  661. $h .= "<p><a href='$FW_ME$FW_subdir?$link$FW_CSRF'>$cmd</a></p>";
  662. }
  663. } else {
  664. if (defined($htmlTxt) && $htmlTxt ne '') {
  665. $htmlTxt =~ s/>desired-temp/>/; #for FHT
  666. $htmlTxt =~ s/>desiredTemperature/>/; #for MAX!
  667. FW_pO $htmlTxt;
  668. } else {
  669. my $nCmd = $cmdIcon{$cmd} ?
  670. FW_makeImage($cmdIcon{$cmd},$cmd,"webCmd") : $cmd;
  671. FW_pH "cmd.$d=set $d $cmd", $nCmd, 1, "col3";
  672. }
  673. # END # Special handling (slider, dropdown, timepicker, ...)
  674. }
  675. }
  676. $FW_ME = $oldMe;
  677. if( $style == 8 ) {
  678. $h =~ s/'/\\"/g;
  679. if( $txt =~ m/<div([^>]*)><a[^>]*>(.*)<\/a>/ ) {
  680. $txt = "<div $1><a onClick='FW_okDialog(\"$h\",this.children[0]);\$(\"a[href]\").each(function() { FW_replaceLink(this); })'\><div informId=\"$d\">$2</div></a></div>";
  681. } else {
  682. FW_pO "<td><div informId=\"$d-$text\">$txt</div>"; # reading
  683. }
  684. FW_pO "<td><div informId=\"$d-$text\">$txt</div>"; # reading
  685. }
  686. FW_pO "</tr>";
  687. } elsif($type eq "FileLog") {
  688. # $row = FW_dumpFileLog($d, 1, $row);
  689. }
  690. FW_pO "</table></form>";
  691. FW_pO "</div>\n";
  692. } #end loop all decives
  693. ########################
  694. # Finally the weblinks
  695. my $buttons = 1;
  696. my @list = (keys %defs);
  697. foreach my $d (sort @list) {
  698. my $attr = AttrVal("$d","fp_$FP_name", undef);
  699. next if(IsIgnored($d) || !$attr);
  700. my $type = $defs{$d}{TYPE};
  701. next if(!$type);
  702. next if($type ne "weblink");
  703. # set position per weblink
  704. my ($top, $left, $style, $text, $text2) = split(/,/ , AttrVal("$d", "fp_$FP_name", undef));
  705. $text = "" if (!$text);
  706. $text2 = "" if (!$text2);
  707. $left = 0 if (!$left);
  708. $style = 0 if (!$style);
  709. FW_pO "\n<div fp_style=\"$style\" fp_text=\"$text\" fp_text2=\"$text2\" fp_name=\"$FP_name\" class=\"fp_weblink_div style_$style\" style=\"position:absolute; top:".
  710. $top."px; left:".$left."px\" id = \"div-$d\">"; # div to position the weblink
  711. FW_pO "<div class = \"fp_$type fp_$FP_name weblink\" id = \"$d\">"; # div to make it accessible to arrangeByMouse
  712. # print weblink
  713. $buttons = FW_showWeblink($d, $defs{$d}{LINK}, $defs{$d}{WLTYPE}, $buttons);
  714. FW_pO "</div></div>";
  715. }
  716. FW_pO "</div>";
  717. FW_pO "</body>\n";
  718. }
  719. #-------------------------------------------------------------------------------
  720. ##################
  721. # Floorplan menu left
  722. sub
  723. FP_menu() {
  724. return if ($FP_name && AttrVal($FP_name, "fp_noMenu", 0)); # fp_noMenu suppresses menu
  725. FW_pO "<div class=\"floorplan\" id=\"menu\">";
  726. # List FPs
  727. FW_pO "<table class=\"start\" id=\"floorplans\">";
  728. FW_pO "<tr>";
  729. FW_pH "$FW_ME", "FHEM", 1;
  730. FW_pO "</tr>";
  731. foreach my $f (sort keys %defs) {
  732. next if (!$defs{$f}{TYPE} || $defs{$f}{TYPE} ne "FLOORPLAN");
  733. FW_pO "<tr>";
  734. my $icoName = "ico$f";
  735. map { my ($n,$v) = split(":",$_); $icoName=$v if($f =~ m/$n/); }
  736. split(" ", AttrVal($FP_name, "fp_roomIcons", ""));
  737. my $icon = FW_iconName($icoName) ? FW_makeImage($icoName,$icoName,"icon")."&nbsp;" : "";
  738. FW_pH "$FW_ME/floorplan/$f", "$icon$f", 1;
  739. FW_pO "</tr>";
  740. }
  741. FW_pO "</table><br>";
  742. FW_pO "</div>\n";
  743. }
  744. #-------------------------------------------------------------------------------
  745. ##################
  746. # Arrange-menu
  747. sub
  748. FP_menuArrange() {
  749. my %desc=();
  750. ## collect data
  751. $FP_arrange_default = "" if (!$FP_arrange_default);
  752. my @fpl; # devices assigned to floorplan
  753. my @nfpl; # devices not assigned to floorplan
  754. foreach my $d (sort keys %defs) { # loop all devices
  755. my $type = $defs{$d}{TYPE};
  756. $type = '?' if (!$type);
  757. # exclude these types from list of available devices
  758. next if ($type =~ m/^(WEB|CUL$|FHEM2FHEM|FHEMWEB|FileLog|PachLog|PID|SUNRISE.*|FLOORPLAN|holiday|Global|notify|autocreate)/ );
  759. my $disp = $d;
  760. $disp .= ' (' . AttrVal($d,"room","Unsorted").") $type";
  761. my $alias = AttrVal($d, "alias", undef);
  762. $disp .= ' (' . $alias . ')' if ($alias);
  763. $desc{$d} = $disp;
  764. if (AttrVal("$d","fp_$FP_name", undef)) {
  765. push(@fpl, $disp); # all devices on floorplan
  766. } else {
  767. push(@nfpl, $disp); # all devices not on floorplan
  768. }
  769. }
  770. my $d = $FP_arrange_selected;
  771. my $attrd = AttrVal($d, "fp_$FP_name", undef) if ($d);
  772. if ( $FP_arrange_selected && !$attrd) { # arrange-selected, but device is not part of fp now chosen -> reset arrange-selected
  773. $FP_arrange_selected = undef;
  774. }
  775. FW_pO "<div style=\"z-index:999\" class=\"fp_arrange\" id=\"fpmenu\">";
  776. # add device to floorplan
  777. if (!defined($FP_arrange_selected)) {
  778. FW_pO "<form method=\"$FW_formmethod\" action=\"$FW_ME/floorplan/$FP_name\">"; #form1
  779. FW_pO "<div class=\"menu-add\" id=\"fpmenu\">\n" .
  780. ($FP_fwdetail?FP_input("detl.$FP_fwdetail", $FP_fwdetail, "hidden") . "\n" :"") .
  781. FW_select("","add.dev", \@nfpl, "", "menu-add") .
  782. FW_submit("ccc.one", "add");
  783. FW_pO "</div></form>\n"; #form1
  784. }
  785. # select device to be arranged
  786. if (!defined($FP_arrange_selected)) {
  787. my $dv = $FP_arrange_default;
  788. $dv = $desc{$dv} if ($dv);
  789. FW_pO "<form method=\"$FW_formmethod\" action=\"$FW_ME/floorplan/$FP_name\">"; #form2
  790. FW_pO "<div class=\"menu-select\" id=\"fpmenu\">\n" .
  791. ($FP_fwdetail?FP_input("detl.$FP_fwdetail", $FP_fwdetail, "hidden") . "\n" :"") .
  792. FW_select("","arr.dev", \@fpl, $dv, "menu-select") .
  793. FW_submit("ccc.one", "select");
  794. FW_pO "</div></form>"; #form2
  795. }
  796. # fields for top,left,style,text
  797. if ($attrd) {
  798. ### build the form
  799. my $disp = $FP_arrange eq "detail" ? $desc{$d} : $d;
  800. FW_pO "<form method=\"$FW_formmethod\" action=\"$FW_ME/floorplan/$FP_name\">"; #form3
  801. my ($top, $left, $style, $text, $text2) = split(",", $attrd);
  802. $text .= ','.$text2 if ($text2); # re-append Description after reading-ID for style3
  803. $style = $styles[$style];
  804. FW_pO "<div class=\"menu-arrange\" id=\"fpmenu\">\n" .
  805. ($FP_fwdetail?FP_input("detl.$FP_fwdetail", $FP_fwdetail, "hidden") . "\n" :"") .
  806. FP_input("deva.$d", $d, "hidden") . "\n" .
  807. FP_input("dscr.$d", $disp, "text", "Selected device", 45, "", "disabled") . "\n<br>\n" .
  808. FP_input("attr.$d", "fp_$FP_name", "hidden") . "\n" .
  809. FP_input("top.$d", $top ? $top : 10, "text", "Top", 4, 4, 'onkeydown="increment(event, this)" class="'.$d.'" id="fp_ar_input_top"') . "\n" .
  810. FP_input("left.$d", $left ? $left : 10, "text", "Left", 4, 4, 'onkeydown="increment(event, this)" class="'.$d.'" id="fp_ar_input_left"' ) . "\n" .
  811. FW_select("","style.$d", \@styles, $style ? $style : 0, "menu-arrange") . "\n" .
  812. FP_input("text.$d", $text ? $text : "", "text", "Description", 15) . "\n" .
  813. FW_submit("cmd.$d", "attr") . "\n" .
  814. FW_submit("cmd.$d", "deleteattr");
  815. FW_pO "</div></form>"; # form3
  816. }
  817. FW_pO "</div>";
  818. }
  819. #-------------------------------------------------------------------------------
  820. ##################
  821. # input-fields for html-forms
  822. sub
  823. FP_input(@)
  824. {
  825. my ($n, $v, $type, $title, $size, $maxlength, $addition) = @_;
  826. $title = $title ? " title=\"$title\"" : "";
  827. $size = $size ? " size=\"$size\"" : "";
  828. $maxlength = $maxlength ? " maxlength=\"$maxlength\"" : "";
  829. $addition = "" if (!defined($addition));
  830. return "<input type=\"$type\"$title$size$maxlength $addition name=\"$n\" value=\"$v\"/>\n";
  831. }
  832. #-------------------------------------------------------------------------------
  833. ##################
  834. #floorplan-specific fhemweb detail-screen
  835. sub
  836. FP_detailFn($$$$) {
  837. my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
  838. my $hash = $defs{$d};
  839. my $link = $hash->{LINK};
  840. my $wltype = $hash->{WLTYPE};
  841. ## Arrange-menu at top of fhemweb-detail-screen
  842. FW_pO '<table class="block wide"><tr><td>';
  843. $FP_name = $d;
  844. $FP_fwdetail = $d;
  845. FP_menuArrange();
  846. $FP_fwdetail = undef;
  847. FW_pO '</td></tr></table>';
  848. ## list of assigned devices
  849. my $row = 0;
  850. FW_pO "<br>Devices assigned to floorplan \"$d\"<br>";
  851. FW_pO '<table class="block wide">';
  852. FW_pO '<thead><tr><th><b>Device</b></th><th><b>X</b></th><th><b>Y</b></th><th><b>Style</b></th><th><b>Text</b></th></tr></thead>';
  853. foreach my $fpd (sort keys %defs) {
  854. my $val = AttrVal($fpd,"fp_$d",undef);
  855. next if (!$val);
  856. my ($x,$y,$style,$txt,$txt2) = split(",",$val);
  857. $txt = "" if (!$txt);
  858. $txt2 = "" if (!$txt2);
  859. $row++;
  860. my $ret = '<tr class = "';
  861. $ret .= ($row/2==int($row/2))?"even":"odd";
  862. $ret .= '">';
  863. $ret .= "<td><div class=\"dname\"><a href=\"$FW_ME?detail=$fpd\">$fpd</a></div></td>";
  864. $ret .= "<td><div class=\"dval\">$x</div></td>";
  865. $ret .= "<td><div class=\"dval\">$y</div></td>";
  866. $ret .= "<td><div class=\"dval\">";
  867. $ret .= $styles[$style] if (defined($style)&& defined($styles[$style]));
  868. $ret .= "</div></td>";
  869. $ret .= "<td><div class=\"dval\">$txt".($txt2?",$txt2":"")."</div></td>";
  870. $ret .= "</tr>";
  871. FW_pO $ret;
  872. }
  873. FW_pO '</table><br>';
  874. ## Arrange-mode on/off
  875. FW_pO "Arrange-mode<br>";
  876. FW_pO '<table class="block wide">';
  877. my $armon = "<div class=\"dval\"><a href=\"$FW_ME?cmd.$d=attr $d fp_arrange 1&detail=$d$FW_CSRF\"><div class=\"col2\">on</div></a></div>"; #mod20170319
  878. my $armoff= "<div class=\"dval\"><a href=\"$FW_ME?cmd.$d=attr $d fp_arrange 0&detail=$d$FW_CSRF\"><div class=\"col2\">off</div></a></div>"; #mod20170319
  879. FW_pO "<tr><td><div class=\"dname\">fp_arrange</div></td><td>$armon</td><td>$armoff</td></tr>";
  880. FW_pO '</table><br>';
  881. return;
  882. }
  883. #-------------------------------------------------------------------------------
  884. ##################
  885. # FLOORPLAN getConfig - can be copied into an include-file
  886. sub
  887. FP_getConfig($) {
  888. my $dev = shift;
  889. my $html="";
  890. if (!defined($defs{$dev})) {
  891. return "get: Device $dev not defined.";
  892. }
  893. my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = localtime;
  894. $year += 1900;
  895. $html .= FP_pOfill(80, ("Config for FLOORPLAN $dev","$mday.$month.$year $hour:$min","get $dev config"));
  896. $html .= FP_pOfill(80, "Definition and attributes for $dev");
  897. $html .= "define $dev FLOORPLAN\n";
  898. ## attributes of floorplan-device
  899. foreach my $a (sort keys %{$attr{$dev}}) {
  900. my $val = AttrVal($dev,$a,undef);
  901. next if (!$val);
  902. $html .= "attr $dev $a $val\n";
  903. }
  904. $html .= "\n\n";
  905. $html .= FP_pOfill(80,"Attributes for devices assigned to $dev");
  906. ## attributes of assigned devices
  907. foreach my $d (sort keys %defs) {
  908. my $val = AttrVal($d,"fp_$dev",undef);
  909. next if (!$val);
  910. $html .= "attr $d fp_$dev $val\n";
  911. }
  912. $html .= "\n\n".FP_pOfill(80, "End of config for FLOORPLAN $dev");
  913. return $html;
  914. }
  915. #-------------------------------------------------------------------------------
  916. ##################
  917. # FLOORPLAN FP_pOfill - FW_pO with filling up with #
  918. sub
  919. FP_pOfill($@) {
  920. my ($digits,@lines,) = @_;
  921. my $ret = "#" x $digits . "\n";
  922. $ret .= ("#"." " x ($digits-2))."#\n";
  923. foreach my $line (@lines) {
  924. $ret .= "# ".$line;
  925. my $len = length($line);
  926. $ret .= (" " x ($digits-$len-3))."#\n" if ( $digits-$len-3 > 0);
  927. }
  928. $ret .= ("#"." " x ($digits-2))."#\n";
  929. $ret .= "#" x $digits . "\n\n";
  930. return $ret;
  931. }
  932. 1;
  933. =pod
  934. +=item helper
  935. =item summary Alternative FHEM Frontend with background pictures
  936. =item summary_DE Alternatives FHEM Frontend mit Hintergrundbildern
  937. =begin html
  938. <a name="FLOORPLAN"></a>
  939. <h3>FLOORPLAN</h3>
  940. <ul>
  941. Implements an additional entry "Floorplans" to your fhem menu, leading to a userinterface without fhem-menu, rooms or devicelists.
  942. Devices can be displayed at a defined coordinate on the screen, usually with a clickable icon allowing to switch
  943. the device on or off by clicking on it. A background-picture can be used - use e.g. a floorplan of your house, or any picture.
  944. Use floorplanstyle.css to adapt the representation.<br>
  945. Step-by-step setup guides are available in
  946. <a href="http://sourceforge.net/p/fhem/code/HEAD/tree/trunk/fhem/docs/fhem-floorplan-installation-guide.pdf?format=raw">english</a> and
  947. <a href="http://www.fhemwiki.de/wiki/Floorplan_Installations-Leitfaden" target="_blank">german</a>. <br>
  948. <br>
  949. <a name="FLOORPLANdefine"></a>
  950. <b>Define</b>
  951. <ul>
  952. <code>define &lt;name&gt; FLOORPLAN </code>
  953. <br><br>
  954. <b>Hint:</b> Store fp_&lt;name&gt;.png in your image folder (www/images/default , www/pgm2 or FHEM) to use it as background picture.<br><br>
  955. Example:
  956. <ul>
  957. <code>
  958. define Groundfloor FLOORPLAN<br>
  959. fp_Groundfloor.png
  960. </code><br>
  961. </ul>
  962. </ul>
  963. <br>
  964. <a name="FLOORPLANset"></a>
  965. <b>Set </b>
  966. <ul>
  967. <li>N/A</li>
  968. </ul>
  969. <br>
  970. <a name="FLOORPLANget"></a>
  971. <b>Get</b>
  972. <ul>
  973. <code>get &lt;name&gt; config</code>
  974. <br>
  975. Displays the configuration of the floorplan <name> with all attributes. Can be used in an include-file.
  976. </ul>
  977. <br>
  978. <a name="FLOORPLANattr"></a>
  979. <b>Attributes</b>
  980. <ul>
  981. <li><a name="fp_fpname">userattr fp_&lt;name&gt; &lt;top&gt;,&lt;left&gt;[,&lt;style&gt;[,&lt;description&gt;]]</a><br><br>
  982. A <a href="#userattr">userattr</a> fp_&lt;name&gt; will be created automatically if it does not exist yet.<br>
  983. <ul>
  984. <li>top = screen-position, pixels from top of screen</li>
  985. <li>left = screen-position, pixels from left of screen</li>
  986. <li>style =
  987. <ul>
  988. <li>0 icon/state only</li>
  989. <li>1 devicename and icon/state</li>
  990. <li>2 devicename, icon/state and commands</li>
  991. <li>3 device-reading and optional description</li>
  992. <li>4 S300TH-specific, displays temperature above humidity</li>
  993. <li>5 icon/state and commands</li>
  994. <li>6 device-reading, reading-timestamp and optional description</li>
  995. <li>7 commands only</li>
  996. <li>8 commands popup</li>
  997. </ul>
  998. </li>
  999. <li>description will be displayed instead of the original devicename</li>
  1000. </ul></li><br>
  1001. Examples:<br>
  1002. <ul>
  1003. <table>
  1004. <tr><td><code>attr lamp1 fp_Groundfloor 100,100</code></td><td><code>#display lamp1 with icon only at screenposition 100,100</code></td></tr>
  1005. <tr><td><code>attr lamp2 fp_Groundfloor 100,140,1,Art-Deco</code></td><td><code>#display lamp2 with description 'Art-Deco-Light' at 100,140</code></td></tr>
  1006. <tr><td><code>attr lamp2 fp_FirstFloor 130,100,1</code></td><td><code>#display the same device at different positions on other floorplans</code></td></tr>
  1007. <tr><td><code>attr myFHT fp_Groundfloor 300,20,10,Temperature</code></td><td><code>#display given Text + FHT-temperature</code></td></tr>
  1008. </table>
  1009. </ul>
  1010. <b>Hint:</b> no blanks between parameters<br><br>
  1011. <li><a name="fp_arrange">fp_arrange</a><br>
  1012. Activates the "arrange mode" which shows an additional menu on the screen to choose style and description.<br>
  1013. When the arrange-mode is activated, devices can be placed freely on the screen by drag&amp;drop.
  1014. allowing to place devices easily on the screen.<br>
  1015. Example:
  1016. <ul>
  1017. <code>attr Groundfloor fp_arrange 1</code><br>
  1018. <code>attr Groundfloor fp_arrange WEB #activates arrange mode for frontend-device WEB only</code><br><br>
  1019. </ul>
  1020. </li>
  1021. <li><a name="stylesheet">stylesheet</a><br>
  1022. Explicitely sets your personal stylesheet for the floorplan. This overrides the standard stylesheet.
  1023. The standard stylesheet for floorplans is <code>floorplanstyle.css</code>. If the <a href="#stylesheetPrefix">stylesheetPrefix</a> is set for the corresponding FHEMWEB instance, this same
  1024. <code>stylesheetPrefix</code> is also prepended to the stylesheet for floorplans.<br>
  1025. All stylesheets must be stored in the stylesheet subfolder of the fhem filesystem hierarchy. Store your personal
  1026. stylesheet along with <code>floorplanstyle.css</code> in the same folder.<br>
  1027. Example:
  1028. <ul>
  1029. <code>attr Groundfloor stylesheet myfloorplanstyle.css</code><br><br>
  1030. </ul>
  1031. </li>
  1032. <li><a name="fp_default">fp_default</a><br>
  1033. The floorplan startscreen is skipped if this attribute is assigned to one of the floorplans in your installation.
  1034. </li>
  1035. Example:
  1036. <ul>
  1037. <code>attr Groundfloor fp_default 1</code><br><br>
  1038. </ul>
  1039. <li><a name="fp_noMenu">fp_noMenu</a><br>
  1040. Suppresses the menu which usually shows the links to all your floorplans.
  1041. </li>
  1042. Example:
  1043. <ul>
  1044. <code>attr Groundfloor fp_noMenu 1</code><br><br>
  1045. </ul>
  1046. <li><a name="commandfield">commandfield</a><br>
  1047. Adds a fhem-commandfield to the floorplan screen.
  1048. </li>
  1049. Example:
  1050. <ul>
  1051. <code>attr Groundfloor commandfield 1</code><br><br>
  1052. </ul>
  1053. <li><a name="fp_backgroundimg">fp_backgroundimg</a><br>
  1054. Allows to choose a background-picture independent of the floorplan-name.
  1055. </li>
  1056. Example:
  1057. <ul>
  1058. <code>attr Groundfloor fp_backgroundimg foobar.png</code><br><br>
  1059. </ul>
  1060. <li><a name="fp_viewport">fp_viewport</a><br>
  1061. Allows usage of a user-defined viewport-value for touchpad.<br>
  1062. Default-viewport-value is "width=768".
  1063. </li>
  1064. <a name="fp_roomIcons"></a>
  1065. <li>fp_roomIcons<br>
  1066. Space separated list of floorplan:icon pairs, to assign icons
  1067. to the floorplan-menu, just like the functionality for rooms
  1068. in FHEMWEB. Example:<br>
  1069. attr Grundriss fp_roomIcons Grundriss:control_building_empty Media:audio_eq
  1070. </li>
  1071. <li><a name="fp_inherited">Inherited from FHEMWEB</a><br>
  1072. The following attributes are inherited from the underlying <a href="#FHEMWEB">FHEMWEB</a> instance:<br>
  1073. <ul>
  1074. <a href="#smallscreen">smallscreen</a><br>
  1075. <a href="#touchpad">touchpad</a><br>
  1076. <a href="#refresh">refresh</a><br>
  1077. <a href="#plotmode">plotmode</a><br>
  1078. <a href="#plotsize">plotsize</a><br>
  1079. <a href="#webname">webname</a><br>
  1080. <a href="#redirectCmds">redirectCmds</a><br>
  1081. <a href="#longpoll">longpoll</a><br>
  1082. <a href="#allowedCommands">allowedCommands</a><br>
  1083. </ul>
  1084. </li><br>
  1085. </ul>
  1086. <br>
  1087. </ul>
  1088. =end html
  1089. =begin html_DE
  1090. <a name="FLOORPLAN"></a>
  1091. <h3>FLOORPLAN</h3>
  1092. <ul>
  1093. Fügt dem fhem-Menü einen zusätzlichen Menüpunkt "Floorplans" hinzu, der zu einer Anzeige ohne fhem-Menü, Räume oder device-Listen führt.
  1094. Geräte können an einer festlegbaren Koordinate auf dem Bildschirm angezeigt werden, üblicherweise mit einem anklickbaren icon, das das Ein- oder Aus-Schalten
  1095. des Geräts durch klicken erlaubt. Ein Hintergrundbild kann verwendet werden - z.B. ein Grundriss oder jegliches andere Bild.
  1096. Mit floorplanstyle.css kann die Formatierung angepasst werden.<br>
  1097. Eine Schritt-für-Schritt-Anleitung zur Einrichtung ist verfügbar in
  1098. <a href="http://sourceforge.net/p/fhem/code/HEAD/tree/trunk/fhem/docs/fhem-floorplan-installation-guide.pdf?format=raw">Englisch</a> und
  1099. <a href="http://www.fhemwiki.de/wiki/Floorplan_Installations-Leitfaden" target="_blank">Deutsch</a>. <br>
  1100. <br>
  1101. <a name="FLOORPLANdefine"></a>
  1102. <b>Define</b>
  1103. <ul>
  1104. <code>define &lt;name&gt; FLOORPLAN </code>
  1105. <br><br>
  1106. <b>Hinweis:</b> Speichern Sie Ihr Hintergrundbild mit dem Dateinamen fp_&lt;name&gt;.png in Ihrem icon_ordner (www/images/default , www/pgm2 or FHEM) .<br><br>
  1107. Beispiel:
  1108. <ul>
  1109. <code>
  1110. define Grundriss FLOORPLAN<br>
  1111. fp_Grundriss.png
  1112. </code><br>
  1113. </ul>
  1114. </ul>
  1115. <br>
  1116. <a name="FLOORPLANset"></a>
  1117. <b>Set</b>
  1118. <ul>
  1119. <li>N/A</li>
  1120. </ul>
  1121. <br>
  1122. <a name="FLOORPLANget"></a>
  1123. <b>Get</b>
  1124. <ul>
  1125. <code>get &lt;name&gt; config</code>
  1126. <br>
  1127. Zeigt die Konfiguration des FLOORPLAN <name> incl. allen Attributen an. Kann fuer ein include-file verwendet werden.<br>
  1128. </ul>
  1129. <br>
  1130. <a name="FLOORPLANattr"></a>
  1131. <b>Attribute</b>
  1132. <ul>
  1133. <li><a name="fp_fpname">userattr fp_&lt;name&gt; &lt;top&gt;,&lt;left&gt;[,&lt;style&gt;[,&lt;description&gt;]]</a><br><br>
  1134. A <a href="#userattr">userattr</a> fp_&lt;name&gt; wird automatisch angelegt, sofern es noch nicht existiert.<br>
  1135. <ul>
  1136. <li>top = Bildschirmposition, pixel vom oberen Bildschirmrand</li>
  1137. <li>left = Bildschirmposition, pixel vom linken Bildschirmrand</li>
  1138. <li>style =
  1139. <ul>
  1140. <li>0 nur icon/Status</li>
  1141. <li>1 Gerätename und icon/Status</li>
  1142. <li>2 Gerätename, icon/Status und Kommandos</li>
  1143. <li>3 Geräte-reading und optionale Beschreibung</li>
  1144. <li>4 S300TH-spezifisch, zeigt Temperatur und Luftfeuchtigkeit an</li>
  1145. <li>5 icon/Status und Kommandos (ohne Gerätename)</li>
  1146. <li>6 Geräte-reading, Zeitstempel und optionale Beschreibung</li>
  1147. <li>7 nur Kommandos</li>
  1148. <li>8 popup für kommandos</li>
  1149. </ul>
  1150. </li>
  1151. <li>Eine ggf. angegebene Bschreibung wird anstelle des original-Gerätenamens angezeigt.</li>
  1152. </ul></li><br>
  1153. Beispiele:<br>
  1154. <ul>
  1155. <table>
  1156. <tr><td><code>attr lamp1 fp_Erdgeschoss 100,100</code></td><td><code>#display lamp1 with icon only at screenposition 100,100</code></td></tr>
  1157. <tr><td><code>attr lamp2 fp_Erdgeschoss 100,140,1,Art-Deco</code></td><td><code>#display lamp2 with description 'Art-Deco-Light' at 100,140</code></td></tr>
  1158. <tr><td><code>attr lamp2 fp_ErsteEtage 130,100,1</code></td><td><code>#display the same device at different positions on other floorplans</code></td></tr>
  1159. <tr><td><code>attr myFHT fp_Erdgeschoss 300,20,10,Temperature</code></td><td><code>#display given Text + FHT-temperature</code></td></tr>
  1160. </table>
  1161. </ul>
  1162. <b>Hinweis:</b> Die Parameter müssen ohne Leerstellen aneinandergereiht werden.<br><br>
  1163. <li><a name="fp_arrange">fp_arrange</a><br>
  1164. Aktiviert den "arrange-Modus" der ein zusätzliches Menü anzeigt,
  1165. mit dem Geräte auf dem Bildschirm angeordnet werden können. Bei aktiviertem arrange-mode können alle devices per drag&amp;drop platziert werden.<br>
  1166. Beispiel:
  1167. <ul>
  1168. <code>attr Erdgeschoss fp_arrange 1</code><br>
  1169. <code>attr Erdgeschoss fp_arrange WEB #Aktiviert den arrange-Modus nur für die Webinstanz WEB</code><br><br>
  1170. </ul>
  1171. </li>
  1172. <li><a name="stylesheet">stylesheet</a><br>
  1173. Ermöglicht die Verwendung eines eigenen css-stylesheet für Ihren floorplan. Dieses Attribut hat Vorrang vor dem Standard-stylesheet.
  1174. Das Standard-stylesheet für floorplans ist <code>floorplanstyle.css</code>. Falls <a href="#stylesheetPrefix">stylesheetPrefix</a> in der korrespondierenden FHEMWEB-Instanz gesetzt ist, wird dieser
  1175. <code>stylesheetPrefix</code> auch dem stylesheet für floorplans vorangestellt (prepend).<br>
  1176. Alle stylesheets werden im stylesheet-Ordner des fhem-Dateisystems abgelegt. Legen Sie dort
  1177. Ihr eigenes stylesheet neben <code>floorplanstyle.css</code> in demselben Ordner ab.<br>
  1178. Beispiel:
  1179. <ul>
  1180. <code>attr Erdgeschoss stylesheet myfloorplanstyle.css</code><br><br>
  1181. </ul>
  1182. </li>
  1183. <li><a name="fp_default">fp_default</a><br>
  1184. Der floorplan-Startbildschirm wird übersprungen wenn dieses Attribut einem der von Ihnen definierten floorplans zugeordnet ist.
  1185. </li>
  1186. Beispiel:
  1187. <ul>
  1188. <code>attr Erdgeschoss fp_default 1</code><br><br>
  1189. </ul>
  1190. <li><a name="fp_noMenu">fp_noMenu</a><br>
  1191. Blendet das floorplans-Menü aus, das normalerweise am linken Bildschirmrand angezeigt wird.
  1192. </li>
  1193. Beispiel:
  1194. <ul>
  1195. <code>attr Erdgeschoss fp_noMenu 1</code><br><br>
  1196. </ul>
  1197. <li><a name="commandfield">commandfield</a><br>
  1198. Fügt Ihrem floorplan ein fhem-Kommandofeld hinzu.
  1199. </li>
  1200. Beispiel:
  1201. <ul>
  1202. <code>attr Erdgeschoss commandfield 1</code><br><br>
  1203. </ul>
  1204. <li><a name="fp_backgroundimg">fp_backgroundimg</a><br>
  1205. Gestattet die Bennung eine Hintergundbilds unabhängig vom floorplan-Namen.<br>
  1206. <b>Hinweis:</b> Das Attribut kann mittels notify geändert werden, um z.B. unterschiedliche Hintergundbidlder am Tag oder in der Nacht anzuzeigen.<br>
  1207. Beispiel:
  1208. <ul>
  1209. <code>attr Erdgeschoss fp_backgroundimg foobar.png</code><br><br>
  1210. </ul>
  1211. </li>
  1212. <li><a name="fp_viewport">fp_viewport</a><br>
  1213. Gestattet die Verwendung eines abweichenden viewport-Wertes für die touchpad-Ausgabe.<br>
  1214. Die Default-viewport-Angbe ist "width=768".
  1215. </li>
  1216. <a name="fp_roomIcons"></a>
  1217. <li>fp_roomIcons<br>
  1218. Mit Leerstellen getrennte Liste von floorplan:icon -Paaren, um
  1219. einem Eintrag des floorplan-Menues icons zuzuordnen, genau wie
  1220. die entsprechende Funktionalitaet in FHEMWEB. Beispiel:<br>
  1221. attr Grundriss fp_roomIcons Grundriss:control_building_empty Media:audio_eq
  1222. </li>
  1223. <li><a name="fp_inherited">Vererbt von FHEMWEB</a><br>
  1224. Die folgenden Attribute werden von der zugrundliegenden <a href="#FHEMWEB">FHEMWEB</a>-Instanz vererbt:<br>
  1225. <ul>
  1226. <a href="#smallscreen">smallscreen</a><br>
  1227. <a href="#touchpad">touchpad</a><br>
  1228. <a href="#refresh">refresh</a><br>
  1229. <a href="#plotmode">plotmode</a><br>
  1230. <a href="#plotsize">plotsize</a><br>
  1231. <a href="#webname">webname</a><br>
  1232. <a href="#redirectCmds">redirectCmds</a><br>
  1233. <a href="#longpoll">longpoll</a><br>
  1234. <a href="#allowedCommands">allowedCommands</a><br>
  1235. </ul>
  1236. </li><br>
  1237. </ul>
  1238. <br>
  1239. </ul>
  1240. =end html_DE
  1241. =cut