From 211c4e09ea7513802f35c5f2c1fabdb23cb4abf6 Mon Sep 17 00:00:00 2001
From: rudolfkoenig <>
Date: Sat, 7 Dec 2013 11:31:38 +0000
Subject: [PATCH] fhem.pl/devspec: removed range, added :=FILTER=, the =
 operator is more general

git-svn-id: https://svn.fhem.de/fhem/trunk@4333 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
 fhem/CHANGED                       |  1 +
 fhem/docs/commandref_frame.html    | 29 ++++++------
 fhem/docs/commandref_frame_DE.html | 69 +++++++++++++++--------------
 fhem/fhem.pl                       | 71 ++++++++++++------------------
 4 files changed, 78 insertions(+), 92 deletions(-)

diff --git a/fhem/CHANGED b/fhem/CHANGED
index 292241967..3c207a007 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,6 +1,7 @@
 # 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.
 - SVN
+  - feature: devspec: removed range, added :FILTER and more general search
   - feature: HUEBridge,HUEDevice: support for groups added
   - feature: YAMAHA_AVR: new argument "toggle" for mute command
   - feature: FB_CALLMONITOR: replace &amp; to & at reverse search
diff --git a/fhem/docs/commandref_frame.html b/fhem/docs/commandref_frame.html
index e2239f1dd..796b4b341 100644
--- a/fhem/docs/commandref_frame.html
+++ b/fhem/docs/commandref_frame.html
@@ -279,34 +279,33 @@ A line ending with \ will be concatenated with the next one, so long lines
   <ul>
   <li>a single device name. This is the most common case.</li>
   <li>a list of devices, separated by comma (,)</li>
-  <li>a range of devices, separated by dash (-)</li>
-  <li>a regular expression, if the the spec contains on e of the following
-      characters: ^*[]$</li>
-  <li>an attribute of the device, followed by an equal sign (=) and then a
-      regexp for this attribute. As attribute you can specify either attributes
-      set with the attr command or one of the "internal" attributes DEF, STATE
-      and TYPE.</li>
+  <li>a regular expression</li>
+  <li>a NAME=VALUE pair, where NAME can be an Internal value like TYPE, a
+        Reading-Name or an attribute. VALUE is a regexp. To negate the
+        comparison, use NAME!=VALUE</li>
+  <li>if the spec is followed by the expression :FILTER=NAME=VALUE, then the
+      values found in the first round are filtered by the second expression.
   </ul>
-  Example:
+  Examples:
   <ul>
     <code>set lamp1 on</code><br>
     <code>set lamp1,lamp2,lamp3 on</code><br>
-    <code>set lamp[1-3] on</code><br>
     <code>set lamp.* on</code><br>
-    <code>set lamp1-lamp3 on</code><br>
-    <code>set lamp1-lamp3,lamp3 on</code><br>
     <code>set room=kitchen off</code><br>
+    <code>set room=kitchen:FILTER=STATE=on off</code><br>
+    <code>set room=kitchen:FILTER=STATE!=off off</code><br>
     <code>list disabled=</code><br>
-    <code>list TYPE=FS20</code><br>
+    <code>list TYPE=FS20 STATE</code><br>
   </ul>
   Notes:
   <ul>
-  <li>first the spec is separated by komma, then the range or the regular
-      expression operations are executed.</li>
+  <li>the spec may not contain space characters.</n>
   <li>if there is a device which exactly corresponds to the spec, then
       no special processing is done.</li>
+  <li>first the spec is separated by komma, then the regular expression and
+      filter operations are executed.</li>
   <li>the returned list can contain the same device more than once, so
-      "set lamp1-lamp3,lamp3 on" switches lamp3 twice.</li>
+      "set lamp3,lamp3 on" switches lamp3 twice.</li>
   <li>for more complex structuring demands see the <a href="#structure">
       structure</a> device.
   </ul>
diff --git a/fhem/docs/commandref_frame_DE.html b/fhem/docs/commandref_frame_DE.html
index d61c4aa33..4b202cc53 100644
--- a/fhem/docs/commandref_frame_DE.html
+++ b/fhem/docs/commandref_frame_DE.html
@@ -265,56 +265,59 @@ Zeilen erstreckende Befehle, indem man keine \ am Zeilenende eingeben muss.</p>
 <a name="devspec"></a>
 <h3>Ger&auml;te-Spezifikation (devspec)</h3>
 <ul>
-  Befehle wie
+  Die Befehle
     <a href="#attr">attr</a>,
     <a href="#set">set</a>,
     <a href="#get">get</a>, usw.
+    <a href="#attr">attr</a>,
+    <a href="#deleteattr">deleteattr</a>,
+    <a href="#displayattr">displayattr</a>,
+    <a href="#delete">delete</a>,
+    <a href="#get">get</a>,
+    <a href="#list">list</a>,
+    <a href="#set">set</a>,
+    <a href="#setreading">setreading</a>,
+    <a href="#setstate">setstate</a>,
+    <a href="#trigger">trigger</a>
   k&ouml;nnen eine komplexere Ger&auml;tespezifikation als Argumente enthalten,
   die auch eine Anzahl von Ger&auml;ten betreffen kann. Eine
-  Ger&auml;tespezifikation (Kurzfassung) kann z.B. so aussehen:
+  Ger&auml;tespezifikation kann folgendes sein:
   <ul>
-  <li>ein einzelner Ger&auml;tename. Dies ist der 
-  meist vorkommende Fall.</li>
-  <li>
-  eine Liste von Ger&auml;tenamen, durch Kommata (,) getrennt</li>
-  <li>
-  ein Bereich, durch ein Minuszeichen getrennt (-)</li>
-  <li>ein regul&auml;rer Ausdruck der eines der 
-  folgenden Zeichen enth&auml;lt: ^*[]$</li>
-  <li>
-  ein Ger&auml;teattribut, gefolgt von einem Gleichheitszeichen (=) und einem 
-  regul&auml;ren Ausdruck f&uuml;r dieses Attribut.
-  Als Attribut k&ouml;nnen Sie entweder Attribute die mittels "attr"-Befehl oder 
-  eines der "internen" Attribute wie DEF, STATE oder TYPE angeben.</li>
+  <li>ein einzelner Ger&auml;tename. Dies ist der Normalfall</li>
+  <li>eine durch Komma(,) getrennte Liste von Ger&auml;tenamen</li>
+  <li>ein regul&auml;rer Ausdruck</li>
+  <li>ein NAME=WERT Ausdruck, wo NAME ein "Internal" Wert wie TYPE ist, ein
+      Reading-Name oder ein Attribut. WERT ist ein regul&auml;rer Ausdruck.
+      Um die Bedingung zu negieren, sollte NAME!=WERT verwendet werden.
+      </li>
+  <li>Falls die Spezifikation von :FILTER=NAME=WERT gefolgt wird,
+      dann wird die zuvor gefundene Liste durch diesen neuen Ausdruck
+      gefiltert.
   </ul>
   Beispiele:
   <ul>
     <code>set lamp1 on</code><br>
     <code>set lamp1,lamp2,lamp3 on</code><br>
-    <code>set lamp[1-3] on</code><br>
     <code>set lamp.* on</code><br>
-    <code>set lamp1-lamp3 on</code><br>
-    <code>set lamp1-lamp3,lamp3 on</code><br>
     <code>set room=kitchen off</code><br>
+    <code>set room=kitchen:FILTER=STATE=on off</code><br>
+    <code>set room=kitchen:FILTER=STATE!=off off</code><br>
     <code>list disabled=</code><br>
-    <code>list TYPE=FS20</code><br>
+    <code>list TYPE=FS20 STATE</code><br>
   </ul>
   Bemerkungen:
   <ul>
-  <li>zuerst wird die durch Kommata getrennte 
-  Spezifikation abgearbeitet, dann folgen die Bereichsspezifikationen und die 
-  regul&auml;ren Ausdr&uuml;cke</li>
-  <li>
-  wenn f&uuml;r ein Ger&auml;t eine Spezifikation exakt zutrifft, werden keine weiteren 
-  Vergleiche vorgenommen.</li>
-  <li>
-  die Befehlszeile kann die selbe Ger&auml;tebezeichnung mehrfach enthalten z.B.: &quot;set 
-  lamp1-lamp3, lamp3 on&quot;.
-  Lamp3 wird hier zwei Mal eingeschalten.</li>
-  <li>um Strukturen mit komplexeren Anforderungen zu realisieren lesen Sie bitte 
-  den Abschnitt zu <a href="#structure">
-      structure</a>.
-  </li></ul>
+  <li>die Spezifikation kann keine Leerzeichen enthalten.</li>
+  <li>falls ein Ger&auml;tename exakt dem Spezifikation entspricht, dann werden
+      keine regul&auml;re Ausdr&uuml;cke oder Filter ausgewertet.
+  <li>zuerst wird die durch Komma getrennte Spezifikation abgearbeitet, dann
+      folgen die regul&auml;ren Ausdr&uuml;cke und die Filter</li>
+  <li>die Befehlszeile kann die selbe Ger&auml;tebezeichnung mehrfach enthalten
+      z.B.: &quot;set lamp3,lamp3 on&quot;. Lamp3 wird hier zwei Mal
+      eingeschalten.</li>
+  <li>um Strukturen mit komplexeren Anforderungen zu realisieren lesen Sie
+      bitte den Abschnitt zu <a href="#structure"> structure</a>.
+  </ul>
 </ul>
 
 <a name="help"></a>
diff --git a/fhem/fhem.pl b/fhem/fhem.pl
index 77950f34a..da0f7142c 100755
--- a/fhem/fhem.pl
+++ b/fhem/fhem.pl
@@ -862,8 +862,6 @@ AnalyzeCommand($$)
 sub
 devspec2array($)
 {
-  my %knownattr = ( "DEF"=>1, "STATE"=>1, "TYPE"=>1 );
-
   my ($name) = @_;
 
   return "" if(!defined($name));
@@ -872,59 +870,44 @@ devspec2array($)
     return "FHEM2FHEM_FAKE_$name" if($defs{$name}{FAKEDEVICE});
     return $name;
   }
-  # FAKE is set by FHEM2FHEM LOG
 
-  my ($isattr, @ret);
+  my @ret;
+  foreach my $l (split(",", $name)) {   # List of elements
+    my @names = sort keys %defs;
+    my @res;
+    foreach my $dName (split(":FILTER=", $name)) {
+      my ($n,$op,$re) = ("NAME","=",$dName);
+      ($n,$op,$re) = ($1,$2,$3) if($dName =~ m/^([^!]*)(=|!=)(.*)$/);
 
-  foreach my $l (split(",", $name)) {   # List
-
-    if($l =~ m/(.*)=(.*)/) {
-      my ($lattr,$re) = ($1, $2);
-      if($knownattr{$lattr}) {
-        eval {                          # a bad regexp may shut down fhem.pl
-          foreach my $l (sort keys %defs) {
-              push @ret, $l
-                if($defs{$l}{$lattr} && (!$re || $defs{$l}{$lattr}=~m/^$re$/));
+      @res=();
+      foreach my $d (@names) {
+        next if($attr{$d} && $attr{$d}{ignore});
+        my $hash = $defs{$d};
+        my $val = $hash->{$n};
+        if(!defined($val)) {
+          my $r = $hash->{READINGS};
+          $val = $r->{$n}{VAL} if($r && $r->{$n});
+        }
+        if(!defined($val)) {
+          $val = $attr{$d}{$n} if($attr{$d});
+        }
+        next if(!defined($val));
+        eval { # a bad regexp is deadly
+          if(($op eq  "=" && $val =~ m/^$re$/) ||
+             ($op eq "!=" && $val !~ m/^$re$/)) {
+            push @res, $d 
           }
         };
         if($@) {
           Log 1, "devspec2array $name: $@";
           return $name;
         }
-      } else {
-        foreach my $l (sort keys %attr) {
-          push @ret, $l
-            if($attr{$l}{$lattr} && (!$re || $attr{$l}{$lattr} =~ m/$re/));
-        }
       }
-      $isattr = 1;
-      next;
+      @names = @res;
     }
-
-    my $regok;
-    eval {                              # a bad regexp may shut down fhem.pl
-      if($l =~ m/[*\[\]^\$]/) {         # Regexp
-        push @ret, grep($_ =~ m/^$l$/, sort keys %defs);
-        $regok = 1;
-      }
-    };
-    if($@) {
-      Log 1, "devspec2array $name: $@";
-      return $name;
-    }
-    next if($regok);
-
-    if($l =~ m/-/) {                    # Range
-      my ($lower, $upper) = split("-", $l, 2);
-      push @ret, grep($_ ge $lower && $_ le $upper, sort keys %defs);
-      next;
-    }
-    push @ret, $l;
+    push @ret,@res;
   }
-
-  return $name if(!@ret && !$isattr);             # No match, return the input
-  @ret = grep { !$attr{$_} || !$attr{$_}{ignore} } @ret
-        if($name !~ m/^ignore=/);
+  return $name if(!@ret);
   return @ret;
 }