2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 04:36:36 +00:00

fhemweb-detailscreen of remotecontrol now displays a preview, added RC_summaryFn incl attr rc_devStateIcon so no weblink is required anymore

git-svn-id: https://svn.fhem.de/fhem/trunk@3502 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
ulimaass 2013-07-25 15:33:26 +00:00
parent a2344e93f5
commit fb23aa765a
2 changed files with 162 additions and 107 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII
- SVN
- feature: 95_remotecontrol does not require a weblink anymore (UliM)
- feature: optional sleep parameter quiet
- feature: additional buttons for use with 95_remotecontrol
- feature: new module 71_LISTENLIVE.pm for use with media players (betateilchen)

View File

@ -43,7 +43,7 @@
# fixed minor html-bug - June 26, 2013
# added css-tags rc_body and rc_button - June 27, 2013
# deleted leading \n at beginning of html-code - June 30, 2013
# fhemweb-detailscreen of remotecontrol now displays a preview, added htmlNoTable for RC_attr2html(), added RC_summaryFn incl attr rc_devStateIcon
package main;
@ -57,11 +57,13 @@ sub RC_Set($@);
sub RC_Get($@);
sub RC_Attr(@);
sub RC_array2attr($@);
sub RC_attr2html($);
sub RC_attr2html($@);
sub RC_layout_delete($);
sub RC_layout_samsung();
sub RC_layout_itunes();
sub RC_detailFn($$$$);
sub RC_webCmdFn($$$);
sub RC_summaryFn($$$$);
#####################################
# Initialize module
@ -69,18 +71,22 @@ sub
remotecontrol_Initialize($)
{
my ($hash) = @_;
$hash->{GetFn} = "RC_Get";
$hash->{SetFn} = "RC_Set";
$hash->{AttrFn} = "RC_Attr";
$hash->{DefFn} = "RC_Define";
$hash->{AttrList}= "rc_iconpath rc_iconprefix loglevel:0,1,2,3,4,5,6 ".
"row00 row01 row02 row03 row04 row05 row06 row07 row08 row09 ".
"row10 row11 row12 row13 row14 row15 row16 row17 row18 row19";
$data{RC_layout}{samsung} = "RC_layout_samsung";
$data{RC_layout}{itunes} = "RC_layout_itunes";
$hash->{GetFn} = "RC_Get";
$hash->{SetFn} = "RC_Set";
$hash->{AttrFn} = "RC_Attr";
$hash->{DefFn} = "RC_Define";
$hash->{AttrList} = "rc_iconpath rc_iconprefix loglevel:0,1,2,3,4,5,6 rc_devStateIcon:0,1 ".
"row00 row01 row02 row03 row04 row05 row06 row07 row08 row09 ".
"row10 row11 row12 row13 row14 row15 row16 row17 row18 row19";
$hash->{FW_detailFn} = "RC_detailFn"; # displays rc preview in fhemweb detail-screen
$hash->{FW_summaryFn} = "RC_summaryFn"; # displays rc instead of status icon in fhemweb room-view
$data{webCmdFn}{remotecontrol} = "RC_webCmdFn"; # displays rc instead of device-commands on the calling device
$data{RC_layout}{samsung} = "RC_layout_samsung";
$data{RC_layout}{itunes} = "RC_layout_itunes";
# $data{RC_layout}{enigma} = "RC_layout_enigma";
# $data{RC_makenotify}{enigma} = "RC_makenotify_enigma";
# $data{RC_layout}{enigma} = "RC_layout_enigma";
# $data{RC_makenotify}{enigma} = "RC_makenotify_enigma";
}
@ -122,60 +128,60 @@ RC_Set($@)
## set layout
if ($cmd eq "layout") {
if ($par eq "delete") {
RC_layout_delete($nam);
$hash->{".htmlCode"} = "";
RC_layout_delete($nam);
$hash->{".htmlCode"} = "";
} else { # layout
my $layoutlist = "";
my @rows;
my @rows;
foreach my $fn (sort keys %{$data{RC_layout}}) {
$layoutlist .= $fn."\n";
next if ($fn ne $par);
$layoutlist .= $fn."\n";
next if ($fn ne $par);
no strict "refs";
@rows = &{$data{RC_layout}{$fn}}();
@rows = &{$data{RC_layout}{$fn}}($fn);
use strict "refs";
}
if ($#rows > 0) {
RC_layout_delete($nam);
RC_array2attr($nam, @rows);
$hash->{".htmlCode"} = "";
} else {
if ($#rows > 0) {
RC_layout_delete($nam);
RC_array2attr($nam, @rows);
$hash->{".htmlCode"} = "";
} else {
return "Missing or invalid parameter \"$par\" for set ... layout. Use one of\n".
"delete\n".$layoutlist;
"delete\n".$layoutlist;
}
}
}
## set makeweblink
} elsif ($cmd eq "makeweblink") {
my $wname = $a[2] ? $a[2] : "weblink_".$nam;
fhem("define $wname weblink htmlCode {fhem(\"get $hash->{NAME} htmlcode\", 1)}");
Log 2, "[remotecontrol] Weblink created: $wname";
return "Weblink created: $wname";
Log 2, "[remotecontrol] Weblink created: $wname";
return "Weblink created: $wname";
## set makenotify
} elsif ($cmd eq "makenotify") {
if ($a[2]) {
if ($a[2]) {
my $ndev = $a[2];
my $fn = $defs{$ndev}{TYPE} ? $defs{$ndev}{TYPE} : undef;
if (defined($fn) && defined($data{RC_makenotify}{$fn})) { #foreign makenotify
no strict "refs";
my $msg = &{$data{RC_makenotify}{$fn}}($nam,$ndev);
use strict "refs";
return $msg;
} else {
my $fn = $defs{$ndev}{TYPE} ? $defs{$ndev}{TYPE} : undef;
if (defined($fn) && defined($data{RC_makenotify}{$fn})) { #foreign makenotify
no strict "refs";
my $msg = &{$data{RC_makenotify}{$fn}}($nam,$ndev);
use strict "refs";
return $msg;
} else {
my $nname="notify_$nam";
fhem("define $nname notify $nam set $ndev ".'$EVENT',1);
Log 2, "[remotecontrol] Notify created: $nname";
return "Notify created: $nname";
}
} else {
return "set $nam makenotify <executingdevice>:\n name of executing device missing.";
}
}
} else {
return "set $nam makenotify <executingdevice>:\n name of executing device missing.";
}
## set ?
} elsif ($cmd eq "?") {
my $ret = "Unknown argument $cmd choose one of makeweblink makenotify state layout:";
foreach my $fn (sort keys %{$data{RC_layout}}) {
$ret .= $fn . ",";
}
$ret =~ s/[:,]$//;
return $ret;
my $ret = "Unknown argument $cmd choose one of makeweblink makenotify state .remotecontrol:remotecontrol layout:";
foreach my $fn (sort keys %{$data{RC_layout}}) {
$ret .= $fn . ",";
}
$ret =~ s/[:,]$//;
return $ret;
## set state <command>
} else {
Log GetLogLevel($nam,4), "[remotecontrol] set $nam $cmd $par";
@ -195,8 +201,8 @@ RC_Get($@)
## get htmlcode
if($arg eq "htmlcode") {
$hash->{".htmlCode"} = RC_attr2html($name) if ($hash->{".htmlCode"} eq "");
return $hash->{".htmlCode"};
$hash->{".htmlCode"} = RC_attr2html($name) if ($hash->{".htmlCode"} eq "");
return $hash->{".htmlCode"};
## get layout
} elsif ($arg eq "layout") {
my $layoutlist = "Available predefined layouts are:\n";
@ -214,46 +220,52 @@ RC_Get($@)
#####################################
# Convert all rowXX-attribute-values into htmlcode
sub
RC_attr2html($) {
my $name = shift;
RC_attr2html($@) {
my ($name,$htmlNoTable) = @_;
my $iconpath = AttrVal("$name","rc_iconpath","icons/remotecontrol");
my $iconprefix = AttrVal("$name","rc_iconprefix","");
my $rc_html;
my $row;
$rc_html = "<div class=\"remotecontrol\">";
# $rc_html = "<div class=\"remotecontrol\" id=\"$name\">"; # provokes update by longpoll
$rc_html.= '<table class="rc_body">';
$rc_html.= '<table class="rc_body">' if (!$htmlNoTable);
foreach my $rownr (0..19) {
$rownr = sprintf("%2.2d",$rownr);
$row = AttrVal("$name","row$rownr",undef);
next if (!$row);
$rc_html .= "<tr>\n";
$row = AttrVal("$name","row$rownr",undef);
next if (!$row);
$rc_html .= "<tr>\n" if (!$htmlNoTable);
my @btn = split (",",$row);
foreach my $btnnr (0..$#btn) {
$rc_html .= '<td class="rc_button">';
foreach my $btnnr (0..$#btn) {
$rc_html .= '<td class="rc_button">';# if (!$htmlNoTable);
if ($btn[$btnnr] ne "") {
my $cmd;
my $img;
if ($btn[$btnnr] =~ /(.*?):(.*)/) { # button has format <command>:<image>
if ($btn[$btnnr] =~ /(.*?):(.*)/) { # button has format <command>:<image>
$cmd = $1;
$img = $2;
} else { # button has format <command> or is empty
$cmd = $btn[$btnnr];
$img = $btn[$btnnr];
}
$img = "<img src=\"$FW_ME/$iconpath/$iconprefix$img\">";
if ($cmd || $cmd eq "0") {
$cmd = "cmd.$name=set $name $cmd";
$rc_html .= "<a onClick=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$cmd')\">$img</a>";
if ($img =~ m/\.svg/) { # convert svg-images
$img = FW_makeImage($img, $cmd, "rc-button");
} else {
$rc_html .= $img;
$img = "<img src=\"$FW_ME/$iconpath/$iconprefix$img\">";
}
if ($cmd || $cmd eq "0") {
$cmd = "cmd.$name=set $name $cmd";
$rc_html .= "<a onClick=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$cmd')\">$img</a>";
} else {
$rc_html .= $img;
}
}
$rc_html .= "</td>\n";
$rc_html .= "</td>";# if (!$htmlNoTable);
$rc_html .= "\n";
}
$rc_html .= "</tr>\n";
$rc_html .= "</tr>\n" if (!$htmlNoTable);
}
$rc_html .= "</table></div>";
$rc_html .= "</table>" if (!$htmlNoTable);
$rc_html .= "</div>";
return $rc_html;
}
@ -280,15 +292,52 @@ RC_array2attr($@)
foreach my $rownr (0..21) {
next if (!$row[$rownr]);
$rownr = sprintf("%2.2d",$rownr);
if ($row[$rownr] =~ m/^attr (.*?)\s(.*)/) {
$ret = fhem("attr $name $1 $2");
} else {
$ret = fhem("attr $name row$rownr $row[$rownr]") if ($row[$rownr]);
}
if ($row[$rownr] =~ m/^attr (.*?)\s(.*)/) {
$ret = fhem("attr $name $1 $2");
} else {
$ret = fhem("attr $name row$rownr $row[$rownr]") if ($row[$rownr]);
}
}
}
##################
#remotecontrol-specific fhemweb detail-screen
sub
RC_detailFn($$$$) {
my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
my $hash = $defs{$d};
$hash->{".htmlCode"} = RC_attr2html($d) if ($hash->{".htmlCode"} eq "");
return $hash->{".htmlCode"};
}
##################
#remotecontrol-specific webCmdFn to be used
# calling module needs to provide ".remotecontrol:remotecontrol" in its return to 'set <device> ?'
sub
RC_webCmdFn($$$) {
my ($FW_wname, $d, $FW_room, $cmd, $values) = @_;
return undef if($values !~ m/remotecontrol/);
my @args = split("[ \t]+", $cmd);
return RC_attr2html($args[1],1) if ($args[1]);
return undef;
}
##################
#remotecontrol-specific summaryFn to be used
# displays the remote on the remote-device itself in FHEMWEB room-overview
sub
RC_summaryFn($$$$) {
my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
my $hash = $defs{$d};
my $name = $hash->{NAME};
return undef if (AttrVal($name,"rc_devStateIcon",1) != 1);
return RC_attr2html($name);
}
#####################################
# Default-layout for samsung
sub
@ -354,23 +403,22 @@ RC_layout_itunes() {
<code>define &lt;rc-name&gt; remotecontrol</code><br><br>
Typical steps to implement a remotecontrol:<br>
<table>
<tr><td><code>define rc1 remotecontrol</code></td><td><code># defines a "blank" remotecontrol</code></td></tr>
<tr><td><code>define rc1 remotecontrol</code></td><td><code># defines a "blank" remotecontrol</code></td></tr>
<tr><td><code>get rc1 layout</code></td><td><code># displays all available predefined layouts</code></td></tr>
<tr><td><code>set rc1 layout samsung</code></td><td><code># assigns keys for a SamsungTV</code></td></tr>
<tr><td><code>set rc1 makeweblink</code></td><td><code># creates weblink_rc1 which displays the remotecontrol in fhemweb or floorplan</code></td></tr>
<tr><td><code>set rc1 makenotify myTV</code></td><td><code># creates notify_rc1 which forwards every buttonclick to myTV for execution</code></td></tr>
<tr><td colspan="2"><b>Note:</b> keys can be changed at any time, it is not necessary to redefine the weblink</td></tr>
<tr><td><code>attr rc1 row15 VOLUP,VOLDOWN</code></td></tr>
</table>
</table>
</ul>
<a name="remotecontrolset"></a><br>
<b>Set</b>
<ul>
<li><code>set &lt;rc-name&gt; layout [delete|&lt;layoutname&gt;]</code><br>
<code>layout delete</code> deletes all rowXX-attributes<br>
<code>layout delete</code> deletes all rowXX-attributes<br>
<code>layout &lt;layoutname&gt;</code> assigns a predefined layout to rowXX-attributes</li>
<li><code>set &lt;rc-name&gt; makeweblink [&lt;name&gt;]</code><br>
<li><code>set &lt;rc-name&gt; makeweblink [&lt;name&gt;]</code><br>
creates a weblink to display the graphical remotecontrol. Default-name is weblink_&lt;rc-name&gt; .</li>
<li><code>set &lt;rc-name&gt; makenotify &lt;executingDevice&gt;</code><br>
creates a notify to trigger &lt;executingDevice&gt; every time a button has been pressed. name is notify_&lt;rc-name&gt; .</li>
@ -388,37 +436,40 @@ RC_layout_itunes() {
<b>Attributes</b>
<ul>
<li><a href="#loglevel">loglevel</a></li>
<li><a name="rc_iconpath">rc_iconpath</a><br>
path for icons, default is "icons"</li>
<li><a name="rc_iconpath">rc_iconpath</a><br>
path for icons, default is "icons" . The attribute-value will be used for all icon-files except .svg .</li>
<li><a name="rc_iconprefix">rc_iconprefix</a><br>
prefix for icon-files, default is "" . </li>
<li>Note: Icon-names (button-image-file-names) will be composed as <code>fhem/&lt;rc_iconpath&gt;/&lt;rc_iconprefix&gt;&lt;command|image&gt;</code></li>
prefix for icon-files, default is "" . The attribute-value will be used for all icon-files except .svg .</li>
<li>Note: Icon-names (button-image-file-names) will be composed as <code>fhem/&lt;rc_iconpath&gt;/&lt;rc_iconprefix&gt;&lt;command|image&gt;</code><br>
For .svg -icons, the access sequence is according to the FHEMWEB-attribute iconPath, default is openautomation:fhemSVG:default .</li>
<li><a name="rc_devStateIcon">rc_devStateIcon</a><br>
In FHEMWEB-room-overview, displays the button-layout on the rc-device itself. Default is 1, set to 0 is the remotecontrol-device should not display its buttons in FHEMWEB roomview.</li>
<br>
<li><a href="#rowXX">rowXX</a><br>
<code>attr &lt;rc-name&gt; rowXX &lt;command&gt;[:&lt;image&gt;][,&lt;command&gt;[:&lt;image&gt;]][,...]</code><br>
<code>attr &lt;rc-name&gt; rowXX &lt;command&gt;[:&lt;image&gt;][,&lt;command&gt;[:&lt;image&gt;]][,...]</code><br>
Comma-separated list of buttons/images per row. Any number of buttons can be placed in one row. For each button, use</li>
<ul>
<ul>
<li><code>&lt;command&gt;</code> is the command that will trigger the event after a buttonclick. Case sensitive.</li>
<li><code>&lt;image&gt;</code> is the filename of the image</li><br>
<li>Per button for the remotecontrol, use</li>
<li>Per button for the remotecontrol, use</li>
<li><code>&lt;command&gt;</code> where an icon with the name <rc_iconprefix>&lt;command&gt; is displayed<br>
Example:<br>
<code>attr rc1 rc_iconprefix black_btn_ # used for ALL icons on remotecontrol rc1</code><br>
<code>attr rc1 row00 VOLUP </code><br>
icon is <code>black_btn_VOLUP</code>, a buttonclick creates the event <code>VOLUP</code>
</li>
icon is <code>black_btn_VOLUP</code>, a buttonclick creates the event <code>VOLUP</code>
</li>
or
<li><code>&lt;command&gt;:&lt;image&gt;</code> where an icon with the name <code>&lt;rc_iconprefix&gt;&lt;image&gt;</code> is displayed<br>
Example: <br>
<code>row00=LOUDER:VOLUP</code><br>
icon is <code>black_btn_VOLUP</code>, a buttonclick creates the event <code>LOUDER</code>
<br>
<code>row00=LOUDER:VOLUP</code><br>
icon is <code>black_btn_VOLUP</code>, a buttonclick creates the event <code>LOUDER</code>
<br>
Examples:<br>
<code>attr rc1 row00 1,2,3,TV,HDMI</code><br>
<code>attr rc2 row00 play:PLAY,pause:PAUSE,louder:VOLUP,quieter:VOLDOWN</code><br>
</li>
</li>
<li><b>Hint:</b> use :blank for a blank space, use e.g. :blank,:blank,:blank for a blank row</li>
</ul>
</ul>
</ul>
</ul>
@ -438,41 +489,44 @@ RC_layout_itunes() {
<code>define &lt;rc-name&gt; remotecontrol</code><br><br>
Typische Schritte zur Einrichtung:<br>
<table>
<tr><td><code>define rc1 remotecontrol</code></td><td><code># erzeugt eine "leere" remotecontrol</code></td></tr>
<tr><td><code>define rc1 remotecontrol</code></td><td><code># erzeugt eine "leere" remotecontrol</code></td></tr>
<tr><td><code>get rc1 layout</code></td><td><code># zeigt alle vorhandenen vordefinierten layouts an</code></td></tr>
<tr><td><code>set rc1 layout samsung</code></td><td><code># laedt das layout für SamsungTV</code></td></tr>
<tr><td><code>set rc1 makeweblink</code></td><td><code># erzeugt weblink_rc1 der die remotecontrol in fhemweb or floorplan anzeigt</code></td></tr>
<tr><td><code>set rc1 makenotify myTV</code></td><td><code># erzeugt notify_rc1, das jeden Tastendruck an myTV weitergibt</code></td></tr>
<tr><td colspan="2"><b>Hinweis:</b>die Tastenbelegung kann jederzeit geaendert werden, ohne dass der weblink erneut erzeugt werden muss.</td></tr>
<tr><td><code>attr rc1 row15 VOLUP,VOLDOWN</code></td></tr>
</table>
</table>
</ul>
<a name="remotecontrolset"></a><br>
<b>Set</b>
<ul>
<li><code>set &lt;rc-name&gt; layout [delete|&lt;layoutname&gt;]</code><br>
<code>layout delete</code> loescht alle rowXX-Attribute<br>
<code>layout delete</code> loescht alle rowXX-Attribute<br>
<code>layout &lt;layoutname&gt;</code> laedt das vordefinierte layout in die rowXX-Attribute</li>
<li><code>set &lt;rc-name&gt; makeweblink [&lt;name&gt;]</code><br>
<li><code>set &lt;rc-name&gt; makeweblink [&lt;name&gt;]</code><br>
erzeugt einen weblink zur Anzeige der remotecontrol in FHEMWEB oder FLOORPLAN. Default-Name ist weblink_&lt;rc-name&gt; .</li>
<li><code>set rc1 makenotify mySamsungTV</code><br>
erzeugt <code>notify_rc1</code> das jeden Tastendruck an mySamsungTV zur Ausfuehrung weitergibt</li>
erzeugt <code>notify_rc1</code> das jeden Tastendruck an mySamsungTV zur Ausfuehrung weitergibt</li>
</ul>
<a name="remotecontrolattr"></a><br>
<b>Attribute</b>
<ul>
<li><a href="#loglevel">loglevel</a></li>
<li><a name="rc_iconpath">rc_iconpath</a><br>
Pfad für icons, default ist "icons"</li>
<li><a name="rc_iconpath">rc_iconpath</a><br>
Pfad für icons, default ist "icons" . Der Attribut-Wert wird für alle icon-Dateien verwendet ausser .svg .</li>
<li><a name="rc_iconprefix">rc_iconprefix</a><br>
Prefix für icon-Dateien, default ist "" . </li>
<li>Note: Icon-Namen (Tasten-Bild-Datei-Namen) werden zusammengesetzt als fhem/&lt;rc_iconpath&gt;/&lt;rc_iconprefix&gt;&lt;command|image&gt;</li>
Prefix für icon-Dateien, default ist "" . Der Attribut-Wert wird für alle icon-Dateien verwendet ausser .svg .</li>
<li>Note: Icon-Namen (Tasten-Bild-Datei-Namen) werden zusammengesetzt als fhem/&lt;rc_iconpath&gt;/&lt;rc_iconprefix&gt;&lt;command|image&gt;<br>
Fuer .svg -icons ist die Zugriffsfolge gemaess dem FHEMWEB-Attribut iconPath, default ist openautomation:fhemSVG:default .
</li>
<li><a name="rc_devStateIcon">rc_devStateIcon</a><br>
Zeigt das button-layout auf dem remotecontrol-device selbst in der FHEMWEB-Raumansicht an. Default ist 1, durch setzen auf 0 erscheint in der FHEMWEB-Raumansciht nicht das layout, sondern nur der Status "Initialized".</li>
<br>
<li><a href="#rowXX">rowXX</a><br>
<code>attr &lt;rc-name&gt; rowXX &lt;command&gt;[:&lt;image&gt;]</code><br>
<code>attr &lt;rc-name&gt; rowXX &lt;command&gt;[:&lt;image&gt;]</code><br>
Komma-separarierte Liste von Tasten/Icons je Tastaturzeile. Eine Tastaturzeile kann beliebig viele Tasten enthalten.</li><br>
<li>&lt;command&gt; ist der event, der bei Tastendruck ausgelöst wird. Gross/Kleinschreibung beachten.</li>
<li>&lt;image&gt; ist der Dateiname des als Taste angezeigten icons</li>
@ -481,18 +535,18 @@ RC_layout_itunes() {
Beispiel:<br>
<code>attr rc1 rc_iconprefix black_btn_ # gilt für alle Tasten/icons</code><br>
<code>attr rc1 row00 VOLUP</code><br>
-> icon ist <code>black_btn_VOLUP</code>, ein Tastendruck erzeugt den event <code>VOLUP</code>
</li><br>
-> icon ist <code>black_btn_VOLUP</code>, ein Tastendruck erzeugt den event <code>VOLUP</code>
</li><br>
oder
<li>&lt;command&gt;:&lt;image&gt; wobei als Taste/icon &lt;rc_iconprefix&gt;&lt;image&gt; angezeigt wird.<br>
Beispiel:<br>
<code>attr rc1 row00 LOUDER:VOLUP</code><br>
icon ist black_btn_VOLUP, ein Tastendruck erzeugt den event LOUDER<br>
<code>attr rc1 row00 LOUDER:VOLUP</code><br>
icon ist black_btn_VOLUP, ein Tastendruck erzeugt den event LOUDER<br>
Beispiele:
<code>attr rc1 row00 1,2,3,TV,HDMI</code><br>
<code>attr rc2 row00 play:PLAY,pause:PAUSE,louder:VOLUP,quieter:VOLDOWN</code><br>
</li>
<li><b>Hinweis:</b> verwenden Sie :blank für eine 'leere Taste', oder z.B. :blank,:blank,:blank für eine Abstands-Leerzeile.</li>
</li>
<li><b>Hinweis:</b> verwenden Sie :blank für eine 'leere Taste', oder z.B. :blank,:blank,:blank für eine Abstands-Leerzeile.</li>
</ul>
</ul>