2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-12 22:56:34 +00:00

AttrTemplate.pm: first version

git-svn-id: https://svn.fhem.de/fhem/trunk@17769 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2018-11-17 22:57:02 +00:00
parent e9c7b8fd08
commit 183c62c302
10 changed files with 231 additions and 10 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- feature: attrTemplate set command added
- bugfix: 73_AutoShuttersControl: fix logical bugs,
change Antifreeze values
- change: DOIFtools: forced change to li-tags in direct help (Forum #93243)

158
fhem/FHEM/AttrTemplate.pm Normal file
View File

@ -0,0 +1,158 @@
##############################################
# $Id$
package main;
my %templates;
my $initialized;
my %cachedUsage;
sub
AttrTemplate_Initialize()
{
my $me = "AttrTemplate_Initialize";
my $dir = $attr{global}{modpath}."/FHEM/lib/AttrTemplate";
if(!opendir(dh, $dir)) {
Log 1, "$me: cant open $dir: $!";
return;
}
my @files = grep /\.template$/, sort readdir dh;
closedir(dh);
%templates = ();
%cachedUsage = ();
for my $file (@files) {
if(!open(fh,"$dir/$file")) {
Log 1, "$me: cant open $dir/$file: $!";
continue;
}
my ($name, %h);
while(my $line = <fh>) {
chomp($line);
next if($line =~ m/^$/ || $line =~ m/^#/);
if($line =~ m/^name:(.*)/) {
$name = $1;
my (@p,@c);
$templates{$name}{pars} = \@p;
$templates{$name}{cmds} = \@c;
} elsif($line =~ m/^filter:(.*)=(.*)/) {
$templates{$name}{filterName} = $1;
$templates{$name}{filterVal} = $2;
} elsif($line =~ m/^par:(.*)/) {
push(@{$templates{$name}{pars}}, $1);
} else {
push(@{$templates{$name}{cmds}}, $line);
}
}
close(fh);
}
my $nr = (int keys %templates);
$initialized = 1;
Log 2, "AttrTemplates: got $nr entries" if($nr);
}
sub
AttrTemplate_Set($$@)
{
my ($hash, $list, $name, $cmd, @a) = @_;
AttrTemplate_Initialize() if(!$initialized);
if($cmd ne "attrTemplate") {
if(!$cachedUsage{$name}) {
my @list;
for my $k (sort keys %templates) {
my $h = $templates{$k};
if(!$h->{filterName} || $hash->{$h->{filterName}} eq $h->{filterVal}) {
push @list, $k;
}
}
$cachedUsage{$name} = (@list ? "attrTemplate:".join(",",@list) : "");
}
$list .= " " if($list ne "");
return "Unknown argument $cmd, choose one of $list$cachedUsage{$name}";
}
return "Missing template_entry_name parameter for attrTemplate" if(@a < 1);
my $entry = shift(@a);
my $h = $templates{$entry};
return "Unknown template_entry_name $entry" if(!$h);
my (%repl, @mComm, @mList, $missing);
for my $k (@{$h->{pars}}) {
my ($parname, $comment, $perl_code) = split(";",$k,3);
if(@a) {
$repl{$parname} = $a[0];
push(@mList, $parname);
push(@mComm, "$parname: with the $comment");
shift(@a);
next;
}
if($perl_code) {
$perl_code =~ s/DEVICE/$name/g;
my $ret = eval $perl_code;
return "Error checking template regexp: $@" if($@);
if($ret) {
$repl{$parname} = $ret;
next;
}
}
push(@mList, $parname);
push(@mComm, "$parname: with the $comment");
$missing = 1;
}
if($missing) {
if($hash->{CL} && $hash->{CL}{TYPE} eq "FHEMWEB") {
return
"<html>".
"<input size='60' type='text' spellcheck='false' ".
"value='set $name attrTemplate $entry @mList'>".
"<br><br>Replace<br>".join("<br>",@mComm).
'<script>
setTimeout(function(){
// TODO: fix multiple dialog calls
$("#FW_okDialog").parent().find("button").css("display","block");
$("#FW_okDialog").parent().find(".ui-dialog-buttonpane button")
.unbind("click").click(function(){
var val = encodeURIComponent($("#FW_okDialog input").val());
FW_cmd(FW_root+"?cmd="+val+"&XHR=1",
function(){ location.reload() } );
$("#FW_okDialog").remove();
})}, 100);
</script>
</html>';
} else {
return "Usage: set $name attrTemplate $entry @mList\nReplace\n".
join("\n", @mComm);
}
}
my $cmdlist = join("\n",@{$h->{cmds}});
$repl{DEVICE} = $name;
map { $cmdlist =~ s/$_/$repl{$_}/g; } keys %repl;
my $cmd = "";
my @ret;
map {
if($_ =~ m/^(.*)\\$/) {
$cmd .= "$1\n";
} else {
my $r = AnalyzeCommand($hash->{CL}, $cmd.$_);
push(@ret, $r) if($r);
$cmd = "";
}
} split("\n", $cmdlist);
return @ret ? join("\n", @ret) : undef;
}
1;

View File

@ -4,6 +4,7 @@
package main;
use strict;
use warnings;
use AttrTemplate;
sub SetExtensions($$@);
sub SetExtensionsFn($);
@ -44,7 +45,7 @@ SetExtensions($$@)
{
my ($hash, $list, $name, $cmd, @a) = @_;
return "Unknown argument $cmd, choose one of " if(!$list);
return AttrTemplate_Set($hash, $list, $name, $cmd, @a) if(!$list);
my %se_list = (
"on-for-timer" => 1,
@ -74,14 +75,13 @@ SetExtensions($$@)
}
if(!$hasOn || !$hasOff) { # No extension
return "Unknown argument $cmd, choose one of $list";
return AttrTemplate_Set($hash, $list, $name, $cmd, @a);
}
if(!defined($se_list{$cmd})) {
# Add only "new" commands
my @mylist = grep { $list !~ m/\b$_\b/ } keys %se_list;
return "Unknown argument $cmd, choose one of $list " .
join(" ", @mylist);
return AttrTemplate_Set($hash, join(" ", @mylist), $name, $cmd, @a);
}
if($se_list{$cmd} && $se_list{$cmd} != int(@a)) {
return "$cmd requires $se_list{$cmd} parameter";

View File

@ -0,0 +1,40 @@
# Comments start with #. Empty lines are ignored.
# Syntax of one entry: name: line, one optional filter: line, zero or more par: lines, FHEM-Commands
# filter:INTERNAL=VALUE (optional)
# par: name of the parameter; comment; perl_code (optional)
# perl_code returns a value for the parameter, or undef.
# If undef, the user has to specify them (the comment is shown to the user)
name:zigbee2mqtt_bridge
filter:TYPE=MQTT2_DEVICE
par:BRIDGENAME;name of the zigbee2mqtt bridge in the topics
attr DEVICE setList\
permit_join:true,false zigbee2mqtt/BRIDGENAME/config/permit_join $EVTPART1\
remove:textField zigbee2mqtt/BRIDGENAME/config/remove $EVTPART1\
log_level:debug,info,warn,error zigbee2mqtt/BRIDGENAME/config/log_level $EVTPART1\
rename:textField zigbee2mqtt/BRIDGENAME/config/rename {"old":"$EVTPART1","new":"$EVTPART2"}\
network_map:raw,graphviz zigbee2mqtt/BRIDGENAME/networkmap $EVTPART1\
devicelist:noArg zigbee2mqtt/BRIDGENAME/config/devices
name:zigbee2mqtt_bulb
filter:TYPE=MQTT2_DEVICE
par:NAMEINTHEBRIDGE;name of this device in the bridge;{ AttrVal("DEVICE","readingList","") =~ m,zigbee2mqtt/(.*):, ? $1 : undef }
attr DEVICE icon light_control
attr DEVICE webCmd brightness
attr DEVICE setList \
on:noArg zigbee2mqtt/NAMEINTHEBRIDGE/set {"state":"ON"}\
off:noArg zigbee2mqtt/NAMEINTHEBRIDGE/set {"state":"OFF"}\
brightness:colorpicker,BRI,0,15,255 zigbee2mqtt/NAMEINTHEBRIDGE/set {"state":"on","$EVTPART0":"$EVTPART1"}
name:zigbee2mqtt_colorbulb
filter:TYPE=MQTT2_DEVICE
par:NAMEINTHEBRIDGE;name of this device in the bridge;{ AttrVal("DEVICE","readingList","") =~ m,zigbee2mqtt/(.*):, ? $1 : undef }
attr DEVICE icon light_control
attr DEVICE webCmd brightness:color_temp
attr DEVICE setList \
on:noArg zigbee2mqtt/NAMEINTHEBRIDGE/set {"state":"ON"}\
off:noArg zigbee2mqtt/NAMEINTHEBRIDGE/set {"state":"OFF"}\
brightness:colorpicker,BRI,0,15,255 zigbee2mqtt/NAMEINTHEBRIDGE/set {"state":"on","$EVTPART0":"$EVTPART1"}\
color_temp:colorpicker,CT,250,1,454 zigbee2mqtt/NAMEINTHEBRIDGE/set {"$EVTPART0":"$EVTPART1"}

View File

@ -510,6 +510,7 @@ FHEM/98_SmarterCoffee CoolTux Sonstige Systeme
FHEM/99_SUNRISE_EL.pm rudolfkoenig Automatisierung
FHEM/99_Utils.pm rudolfkoenig Automatisierung
FHEM/99_Venetian.pm Christian.Kühnel Automatisierung
FHEM/AttrTemplate.pm rudolfkoenig Automatisierung
FHEM/Blocking.pm rudolfkoenig Automatisierung
FHEM/DevIo.pm rudolfkoenig Sonstiges
FHEM/Color.pm justme1968 Sonstiges

View File

@ -52,6 +52,7 @@ my @filelist2 = (
"FHEM/lib/MP3/.*.pm",
"FHEM/lib/MP3/Tag/.*",
"FHEM/lib/UPnP/.*",
"FHEM/lib/AttrTemplate/.*.template",
"FHEM/holiday/.*.holiday",
"contrib/commandref_join.pl.txt",
"contrib/commandref_modular.pl.txt",

View File

@ -1217,6 +1217,15 @@ The following local attributes are used by a wider range of devices:
set switch intervals 08:00-12:00 13:00-18:00<br>
</code>
</ul>
<br><br>
<a name="attrTemplate"></a>
attrTemplate<br>
with this command a set of predefined attributes may be set at once. The
template files containing the entries are in FHEM/lib/AttrTemplate
directory. Template entries can be module specific, and may require further
parameters to be specified.
</ul>
<!-- set end -->

View File

@ -1293,7 +1293,15 @@ Die folgenden lokalen Attribute werden von mehreren Ger&auml;ten verwendet:
set switch intervals 08:00-12:00 13:00-18:00<br>
</code>
</ul>
</ul>
<br><br>
<a name="attrTemplate"></a>
attrTemplate<br>
mit diesem Befehl kann man eine Menge an vordefinierten Attributen setzen.
Die Eintr&auml;ge befinden sich in Dateien im FHEM/lib/AttrTemplate
Verzeichnis. Eintr&auml;ge k&ouml;nnen modul-spezifisch sein, und
m&ouml;glicherweise erfordern weitere Parameter.
</ul>
<!-- set end -->

View File

@ -2870,7 +2870,9 @@ CommandAttr($$)
$attrVal = "-" if($attrName eq "logfile");
$attrVal = 5 if($attrName eq "verbose");
}
$defs{$sdev}->{CL} = $cl;
$ret = CallFn($sdev, "AttrFn", "set", $sdev, $attrName, $attrVal);
delete($defs{$sdev}->{CL});
if($ret) {
push @rets, $ret;
next;

View File

@ -750,7 +750,7 @@ FW_inlineModify() // Do not generate a new HTML page upon pressing modify
var ifid = (devName+"-"+arg).replace(/([^_a-z0-9])/gi,
function(m){ return "\\"+m });
if($(".dval[informid="+ifid+"]").length == 0) {
if(cmd == "attr") {
if(cmd == "attr" || (cmd == "set" && arg == "attrTemplate")) {
reloadIfOk = true;
} else {
$(this).unbind('click').click();// No element found to replace, reload
@ -762,14 +762,15 @@ FW_inlineModify() // Do not generate a new HTML page upon pressing modify
newDef = $(this).closest("form").find("[name^=val]").val();
cmd = $(this).attr("name")+"="+cmd+" "+devName+" "+arg+" "+newDef;
}
FW_cmd(FW_root+"?"+encodeURIComponent(cmd)+"&XHR=1", function(resp){
if(!resp && reloadIfOk)
location.reload();
if(resp) {
resp = FW_htmlQuote(resp);
if(resp.indexOf("\n") >= 0)
resp = '<pre>'+resp+'</pre>';
if(!resp.match(/^<html>[\s\S]*<\/html>/ ) ) {
resp = FW_htmlQuote(resp);
if(resp.indexOf("\n") >= 0)
resp = '<pre>'+resp+'</pre>';
}
return FW_okDialog(resp);
}
if(isDef) {