diff --git a/fhem/CHANGED b/fhem/CHANGED index 4e0d4446c..f16825f94 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -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) diff --git a/fhem/FHEM/AttrTemplate.pm b/fhem/FHEM/AttrTemplate.pm new file mode 100644 index 000000000..7348b2478 --- /dev/null +++ b/fhem/FHEM/AttrTemplate.pm @@ -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 = ) { + 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 + "". + "". + "

Replace
".join("
",@mComm). + ' + '; + + } 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; diff --git a/fhem/FHEM/SetExtensions.pm b/fhem/FHEM/SetExtensions.pm index 840e87f91..0c2c59526 100644 --- a/fhem/FHEM/SetExtensions.pm +++ b/fhem/FHEM/SetExtensions.pm @@ -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"; diff --git a/fhem/FHEM/lib/AttrTemplate/mqtt2.template b/fhem/FHEM/lib/AttrTemplate/mqtt2.template new file mode 100644 index 000000000..1e39da40b --- /dev/null +++ b/fhem/FHEM/lib/AttrTemplate/mqtt2.template @@ -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"} diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 12ab5fdf6..efb1e76a1 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -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 diff --git a/fhem/contrib/fhemupdate.pl b/fhem/contrib/fhemupdate.pl index b8cc120fe..139904f44 100755 --- a/fhem/contrib/fhemupdate.pl +++ b/fhem/contrib/fhemupdate.pl @@ -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", diff --git a/fhem/docs/commandref_frame.html b/fhem/docs/commandref_frame.html index c7decb95b..6e77ffbae 100644 --- a/fhem/docs/commandref_frame.html +++ b/fhem/docs/commandref_frame.html @@ -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
+

+ + + attrTemplate
+ 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. + diff --git a/fhem/docs/commandref_frame_DE.html b/fhem/docs/commandref_frame_DE.html index f908269ce..7aa2afc59 100644 --- a/fhem/docs/commandref_frame_DE.html +++ b/fhem/docs/commandref_frame_DE.html @@ -1293,7 +1293,15 @@ Die folgenden lokalen Attribute werden von mehreren Geräten verwendet: set switch intervals 08:00-12:00 13:00-18:00
- +

+ + + attrTemplate
+ mit diesem Befehl kann man eine Menge an vordefinierten Attributen setzen. + Die Einträge befinden sich in Dateien im FHEM/lib/AttrTemplate + Verzeichnis. Einträge können modul-spezifisch sein, und + möglicherweise erfordern weitere Parameter. + diff --git a/fhem/fhem.pl b/fhem/fhem.pl index 7706d83a6..e4d979277 100755 --- a/fhem/fhem.pl +++ b/fhem/fhem.pl @@ -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; diff --git a/fhem/www/pgm2/fhemweb.js b/fhem/www/pgm2/fhemweb.js index d18ff22e3..220a8c090 100644 --- a/fhem/www/pgm2/fhemweb.js +++ b/fhem/www/pgm2/fhemweb.js @@ -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 = '
'+resp+'
'; + if(!resp.match(/^[\s\S]*<\/html>/ ) ) { + resp = FW_htmlQuote(resp); + if(resp.indexOf("\n") >= 0) + resp = '
'+resp+'
'; + } return FW_okDialog(resp); } if(isDef) {