diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index 166069241..c836b07e6 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -160,6 +160,8 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "1.51.3" => "22.04.2025 change battery text to 'load if above feed-in limit' ". + "transform set setupStringAzimuth, setupStringDeclination to attr setupStringAzimuth, setupStringDeclination ", "1.51.2" => "21.04.2025 Attributes obsolet: graphicHeaderShow replaced by graphicSelect, Value 'none' of consumerControl->showLegend deleted ", "1.51.1" => "20.04.2025 consumer: interruptable, swoncond, swoffcond, spignorecond can be perl code enclosed by {..} ". "check key is valid in plantControl, aiControl, flowGraphicControl, consumerControl, setupMeterDev ". @@ -571,14 +573,6 @@ my @fs = qw( ftui_forecast.css ftui_smaportalspg.css widget_smaportalspg.js ); - # Anlagenkonfiguration: maßgebliche Readings -my @rconfigs = qw( pvCorrectionFactor_Auto - setupStringAzimuth - setupStringDeclination - batteryTrigger - powerTrigger - energyH4Trigger - ); # Grafik Selektionsoptionen my @gsopt = qw ( both both_noHead @@ -597,7 +591,13 @@ my @gsopt = qw ( both forecast_noCons forecast_noHead_noCons none - ); + ); + # Anlagenkonfiguration: maßgebliche Readings +my @rconfigs = qw( pvCorrectionFactor_Auto + batteryTrigger + powerTrigger + energyH4Trigger + ); # Anlagenkonfiguration: maßgebliche Attribute my @aconfigs = qw( aiControl consumerControl @@ -620,7 +620,7 @@ my @aconfigs = qw( aiControl graphicLayoutType graphicSelect graphicShowDiff graphicShowNight graphicShowWeather graphicSpaceSize graphicWeatherColor graphicWeatherColorNight plantControl - setupMeterDev setupInverterStrings setupRadiationAPI setupStringPeak + setupMeterDev setupInverterStrings setupRadiationAPI setupStringPeak setupStringAzimuth setupStringDeclination setupWeatherDev1 setupWeatherDev2 setupWeatherDev3 setupRoofTops ); @@ -690,8 +690,6 @@ my %hset = ( # Ha pvCorrectionFactor_Auto => { fn => \&_setpvCorrectionFactorAuto }, reset => { fn => \&_setreset }, roofIdentPair => { fn => \&_setroofIdentPair }, - setupStringDeclination => { fn => \&_setstringDeclination }, - setupStringAzimuth => { fn => \&_setstringAzimuth }, operatingMemory => { fn => \&_setoperatingMemory }, vrmCredentials => { fn => \&_setVictronCredentials }, aiDecTree => { fn => \&_setaiDecTree }, @@ -735,6 +733,8 @@ my %hattr = ( # H setupInverterStrings => { fn => \&_attrInverterStrings }, setupRadiationAPI => { fn => \&_attrRadiationAPI }, setupStringPeak => { fn => \&_attrStringPeak }, + setupStringAzimuth => { fn => \&_attrstringAzimuth }, + setupStringDeclination => { fn => \&_attrstringDeclination }, setupRoofTops => { fn => \&_attrRoofTops }, flowGraphicControl => { fn => \&_attrflowGraphicControl }, aiControl => { fn => \&_attraiControl }, @@ -823,10 +823,10 @@ my %hqtxt = ( # H DE => qq{Bitte geben sie alle von Ihnen verwendeten Stringnamen mit "attr LINK setupInverterStrings" an} }, mps => { EN => qq{Please enter the DC peak power of each string with "attr LINK setupStringPeak"}, DE => qq{Bitte geben sie die DC Spitzenleistung von jedem String mit "attr LINK setupStringPeak" an} }, - mdr => { EN => qq{Please specify the module direction with "set LINK setupStringAzimuth"}, - DE => qq{Bitte geben sie die Modulausrichtung mit "set LINK setupStringAzimuth" an} }, - mta => { EN => qq{Please specify the module tilt angle with "set LINK setupStringDeclination"}, - DE => qq{Bitte geben sie den Modulneigungswinkel mit "set LINK setupStringDeclination" an} }, + mdr => { EN => qq{Please specify the module direction with "attr LINK setupStringAzimuth"}, + DE => qq{Bitte geben sie die Modulausrichtung mit "attr LINK setupStringAzimuth" an} }, + mta => { EN => qq{Please specify the module tilt angle with "attr LINK setupStringDeclination"}, + DE => qq{Bitte geben sie den Modulneigungswinkel mit "attr LINK setupStringDeclination" an} }, rip => { EN => qq{Please specify at least one combination Rooftop-ID/SolCast-API with "set LINK roofIdentPair"}, DE => qq{Bitte geben Sie mindestens eine Kombination Rooftop-ID/SolCast-API mit "set LINK roofIdentPair" an} }, mrt => { EN => qq{Please set the assignment String / Rooftop identification with "attr LINK setupRoofTops"}, @@ -1042,8 +1042,8 @@ my %htitles = ( DE => qq{SoC am Ende der Stunde} }, bcharrel => { EN => qq{Charging release (activate release for charging the battery if necessary)}, DE => qq{Ladefreigabe (evtl. Freigabe zum Laden der Batterie aktivieren)} }, - bncharel => { EN => qq{no Charging release (possibly deactivate release for charging the battery)}, - DE => qq{keine Ladefreigabe (evtl. Freigabe zum Laden der Batterie deaktivieren)} }, + bncharel => { EN => qq{load if above feed-in limit (possibly deactivate release for charging the battery)}, + DE => qq{laden wenn über Einspeiselimit (evtl. Freigabe zum Laden der Batterie deaktivieren)} }, conrec => { EN => qq{Current time is within the consumption planning}, DE => qq{Aktuelle Zeit liegt innerhalb der Verbrauchsplanung} }, conrecba => { EN => qq{Current time is within the consumption planning, Priority charging Battery is active}, @@ -1606,6 +1606,8 @@ sub Initialize { "setupWeatherDev3 ". "setupRoofTops ". "setupRadiationAPI ". + "setupStringAzimuth ". + "setupStringDeclination ". "setupStringPeak ". $beamcont. $beamcol. @@ -1752,8 +1754,6 @@ sub Set { "powerTrigger:textField-long ". "pvCorrectionFactor_Auto:noLearning,on_simple".($ipai ? ',on_simple_ai,' : ',')."on_complex".($ipai ? ',on_complex_ai,on_complex_api_ai,' : ',')."off ". "reset:$resets ". - "setupStringAzimuth ". - "setupStringDeclination ". $cf." " ; @@ -1940,11 +1940,14 @@ sub _setattrKeyVal { ## no critic "not used" return if(!$init_done); my $valid = { - flowGraphicControl => '', - aiControl => '', - consumerControl => '', - plantControl => '', - setupMeterDev => '', + flowGraphicControl => '', + aiControl => '', + consumerControl => '', + plantControl => '', + setupMeterDev => '', + setupStringAzimuth => '', + setupStringDeclination => '', + setupStringPeak => '', }; for my $cn (1..MAXCONSUMER) { @@ -2166,78 +2169,6 @@ sub _setTrigger { ## no critic "not used" return; } -################################################################ -# Setter setupStringDeclination -################################################################ -sub _setstringDeclination { ## no critic "not used" - my $paref = shift; - my $name = $paref->{name}; - my $arg = $paref->{arg} // return qq{no tilt angle was provided}; - - my ($a,$h) = parseParams ($arg); - - if (!keys %$h) { - return qq{The specified inclination angle has an incorrect format}; - } - - while (my ($key, $value) = each %$h) { - if ($value !~ /^(?:[0-9]{1,2})$/x || $value > 90) { - return qq{The inclination angle of "$key" is incorrect}; - } - } - - my $hash = $defs{$name}; - - readingsSingleUpdate ($hash, 'setupStringDeclination', $arg, 1); - writeCacheToFile ($hash, 'plantconfig', $plantcfg.$name); # Anlagenkonfiguration File schreiben - - return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett - - my $ret = _createStringConfig ($hash); - return $ret if($ret); - -return; -} - -################################################################ -# Setter setupStringAzimuth -# -# Angabe entweder als Azimut-Bezeichner oder direkte -# Azimut Angabe -180 ...0...180 -# -################################################################ -sub _setstringAzimuth { ## no critic "not used" - my $paref = shift; - my $name = $paref->{name}; - my $arg = $paref->{arg} // return qq{no module direction was provided}; - - my $dirs = "N|NE|E|SE|S|SW|W|NW"; # mögliche Azimut-Bezeichner wenn keine direkte Azimut Angabe - - my ($a,$h) = parseParams ($arg); - - if (!keys %$h) { - return qq{The provided module direction has wrong format}; - } - - while (my ($key, $value) = each %$h) { - if ($value !~ /^(?:$dirs)$/x && ($value !~ /^(?:-?[0-9]{1,3})$/x || $value < -180 || $value > 180)) { - return qq{The module direction of "$key" is wrong: $value}; - } - } - - my $hash = $defs{$name}; - - readingsSingleUpdate ($hash, 'setupStringAzimuth', $arg, 1); - writeCacheToFile ($hash, 'plantconfig', $plantcfg.$name); # Anlagenkonfiguration File schreiben - - return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett - - my $ret = _createStringConfig ($hash); - return $ret if($ret); - -return; -} - ################################################################ # Setter / (verborgener) Getter plantConfiguration ################################################################ @@ -4939,7 +4870,7 @@ sub ___createOpenMeteoURL { if ($requestmode eq 'GHIREFILL' && $string eq 'Dummy') {$tilt = 0; $az = 0;} # Dummy Settings if ($tilt eq '' || $az eq '') { - $err = qq{ERROR OpenMeteo API Call - the reading 'setupStringAzimuth' and/or 'setupStringDeclination' is not set}; + $err = qq{ERROR OpenMeteo API Call - the attribute 'setupStringAzimuth' and/or 'setupStringDeclination' is not set}; Log3 ($name, 1, "$name - $err"); return; } @@ -6995,6 +6926,107 @@ sub _attrStringPeak { ## no critic "not used" return; } +################################################################ +# Attr setupStringAzimuth +# +# Angabe entweder als Azimut-Bezeichner oder direkte +# Azimut Angabe -180 ...0...180 +# +################################################################ +sub _attrstringAzimuth { ## no critic "not used" + my $paref = shift; + my $name = $paref->{name}; + my $aVal = $paref->{aVal}; + + return if(!$init_done); + + my $dirs = "N|NE|E|SE|S|SW|W|NW"; # mögliche Azimut-Bezeichner wenn keine direkte Azimut Angabe + my $hash = $defs{$name}; + + if ($paref->{cmd} eq 'set') { + my ($a,$h) = parseParams ($aVal); + + if (!keys %$h) { + return qq{The provided module direction has wrong format}; + } + + while (my ($key, $value) = each %$h) { + if ($value !~ /^(?:$dirs)$/x && ($value !~ /^(?:-?[0-9]{1,3})$/x || $value < -180 || $value > 180)) { + return qq{The module direction of "$key" is wrong: $value}; + } + } + + return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett + + my @istrings = split ",", AttrVal ($name, 'setupInverterStrings', ''); # Stringbezeichner + + if (!@istrings) { + return qq{Define all used strings with command "attr $name setupInverterStrings" first.}; + } + + while (my ($strg, $pp) = each %$h) { + if (!grep /^$strg$/, @istrings) { + return qq{The stringname '$strg' is not defined as valid string name in attribute 'setupInverterStrings'}; + } + } + + $data{$name}{current}{allStringsFullfilled} = 0; # Stringkonfiguration neu prüfen lassen + } + + InternalTimer (gettimeofday() + 0.5, 'FHEM::SolarForecast::centralTask', [$name, 0], 0); + InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::writeCacheToFile', [$name, 'plantconfig', $plantcfg.$name], 0); # Anlagenkonfiguration File schreiben + +return; +} + +################################################################ +# Attr setupStringDeclination +################################################################ +sub _attrstringDeclination { ## no critic "not used" + my $paref = shift; + my $name = $paref->{name}; + my $aVal = $paref->{aVal}; + + return if(!$init_done); + + my $hash = $defs{$name}; + + if ($paref->{cmd} eq 'set') { + my ($a,$h) = parseParams ($aVal); + + if (!keys %$h) { + return qq{The specified inclination angle has an incorrect format}; + } + + while (my ($key, $value) = each %$h) { + if ($value !~ /^(?:[0-9]{1,2})$/x || $value > 90) { + return qq{The inclination angle of "$key" is incorrect}; + } + } + + return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett + + my @istrings = split ",", AttrVal ($name, 'setupInverterStrings', ''); # Stringbezeichner + + if (!@istrings) { + return qq{Define all used strings with command "attr $name setupInverterStrings" first.}; + } + + while (my ($strg, $pp) = each %$h) { + if (!grep /^$strg$/, @istrings) { + return qq{The stringname '$strg' is not defined as valid string name in attribute 'setupInverterStrings'}; + } + } + + $data{$name}{current}{allStringsFullfilled} = 0; # Stringkonfiguration neu prüfen lassen + } + + InternalTimer (gettimeofday() + 0.5, 'FHEM::SolarForecast::centralTask', [$name, 0], 0); + InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::writeCacheToFile', [$name, 'plantconfig', $plantcfg.$name], 0); # Anlagenkonfiguration File schreiben + +return; +} + ################################################################ # Attr setupRoofTops ################################################################ @@ -7170,9 +7202,9 @@ sub _attrWeatherDev { ## no critic "not used" my @istrings = split ",", AttrVal ($name, 'setupInverterStrings', ''); # Stringbezeichner - if ((!ReadingsVal ($name, 'setupStringAzimuth', '') || !ReadingsVal ($name, 'setupStringDeclination', '')) && + if ((!AttrVal ($name, 'setupStringAzimuth', '') || !AttrVal ($name, 'setupStringDeclination', '')) && !grep /KI-based/, @istrings) { - return qq{Execute 'set $name setupStringAzimuth' and/or 'set $name setupStringDeclination' first.}; + return qq{Execute 'attr $name setupStringAzimuth' and/or 'attr $name setupStringDeclination' first.}; } } @@ -7221,11 +7253,11 @@ sub _attrRadiationAPI { ## no critic "not used" my ($set, $lat, $lon, $elev) = locCoordinates(); return qq{set attributes 'latitude' and 'longitude' in global device first} if(!$set); - my $tilt = ReadingsVal ($name, 'setupStringDeclination', ''); # Modul Neigungswinkel für jeden Stringbezeichner - return qq{Please complete command "set $name setupStringDeclination".} if(!$tilt); + my $tilt = AttrVal ($name, 'setupStringDeclination', ''); # Modul Neigungswinkel für jeden Stringbezeichner + return qq{Please complete command "attr $name setupStringDeclination".} if(!$tilt); - my $dir = ReadingsVal ($name, 'setupStringAzimuth', ''); # Modul Ausrichtung für jeden Stringbezeichner - return qq{Please complete command "set $name setupStringAzimuth".} if(!$dir); + my $dir = AttrVal ($name, 'setupStringAzimuth', ''); # Modul Ausrichtung für jeden Stringbezeichner + return qq{Please complete command "attr $name setupStringAzimuth".} if(!$dir); } $data{$name}{current}{allStringsFullfilled} = 0; # Stringkonfiguration neu prüfen lassen @@ -8405,6 +8437,18 @@ sub centralTask { # ::CommandDeleteAttr (undef, "$name affectBatteryPreferredCharge"); #} + my $ssd = ReadingsVal ($name, 'setupStringDeclination', ''); # 22.04.2025 + if ($ssd) { + CommandAttr (undef, "$name setupStringDeclination $ssd"); + readingsDelete ($hash, "setupStringDeclination"); + } + + my $ssa = ReadingsVal ($name, 'setupStringAzimuth', ''); # 22.04.2025 + if ($ssa) { + CommandAttr (undef, "$name setupStringAzimuth $ssa"); + readingsDelete ($hash, "setupStringAzimuth"); + } + if (CurrentVal ($hash, 'consumerCollected', 0)) { for my $c (1..MAXCONSUMER) { # 19.04.2025 $c = sprintf "%02d", $c; @@ -8577,8 +8621,8 @@ sub _createStringConfig { ## no critic "not used" } if (!grep /^KI-based$/, @istrings) { - my $tilt = ReadingsVal ($name, 'setupStringDeclination', ''); # Modul Neigungswinkel für jeden Stringbezeichner - return qq{Please complete command "set $name setupStringDeclination"} if(!$tilt); + my $tilt = AttrVal ($name, 'setupStringDeclination', ''); # Modul Neigungswinkel für jeden Stringbezeichner + return qq{Please complete command "attr $name setupStringDeclination"} if(!$tilt); my ($at,$ht) = parseParams ($tilt); @@ -8591,11 +8635,11 @@ sub _createStringConfig { ## no critic "not used" } } - my $dir = ReadingsVal ($name, 'setupStringAzimuth', ''); # Modul Ausrichtung für jeden Stringbezeichner - return qq{Please complete command "set $name setupStringAzimuth"} if(!$dir); + my $dir = AttrVal ($name, 'setupStringAzimuth', ''); # Modul Ausrichtung für jeden Stringbezeichner + return qq{Please complete command "attr $name setupStringAzimuth"} if(!$dir); my ($ad,$hd) = parseParams ($dir); - my $iwrong = qq{Please check the input of set "setupStringAzimuth". It seems to be wrong.}; + my $iwrong = qq{Please check the input of attr "setupStringAzimuth". It seems to be wrong.}; while (my ($key, $value) = each %$hd) { if (grep /^$key$/, @istrings) { @@ -14370,23 +14414,22 @@ sub _checkSetupNotComplete { my $name = $hash->{NAME}; my $type = $hash->{TYPE}; - my $strings = AttrVal ($name, 'setupInverterStrings', undef); # String Konfig - my $wedev = AttrVal ($name, 'setupWeatherDev1', undef); # Device Vorhersage Wetterdaten (Bewölkung etc.) - my $radev = AttrVal ($name, 'setupRadiationAPI', undef); # Device Strahlungsdaten Vorhersage - my $indev = AttrVal ($name, 'setupInverterDev01', undef); # Inverter Device - my $medev = AttrVal ($name, 'setupMeterDev', undef); # Meter Device - my $peaks = AttrVal ($name, 'setupStringPeak', undef); # String Peak - my $maz = ReadingsVal ($name, 'setupStringAzimuth', undef); # Modulausrichtung Konfig (Azimut) - my $mdec = ReadingsVal ($name, 'setupStringDeclination', undef); # Modul Neigungswinkel Konfig - my $mrt = AttrVal ($name, 'setupRoofTops', undef); # RoofTop Konfiguration (SolCast API) + my $strings = AttrVal ($name, 'setupInverterStrings', undef); # String Konfig + my $wedev = AttrVal ($name, 'setupWeatherDev1', undef); # Device Vorhersage Wetterdaten (Bewölkung etc.) + my $radev = AttrVal ($name, 'setupRadiationAPI', undef); # Device Strahlungsdaten Vorhersage + my $indev = AttrVal ($name, 'setupInverterDev01', undef); # Inverter Device + my $medev = AttrVal ($name, 'setupMeterDev', undef); # Meter Device + my $peaks = AttrVal ($name, 'setupStringPeak', undef); # String Peak + my $maz = AttrVal ($name, 'setupStringAzimuth', undef); # Modulausrichtung Konfig (Azimut) + my $mdec = AttrVal ($name, 'setupStringDeclination', undef); # Modul Neigungswinkel Konfig + my $mrt = AttrVal ($name, 'setupRoofTops', undef); # RoofTop Konfiguration (SolCast API) my $vrmcr = StatusAPIVal ($hash, '?VRM', '?API', 'credentials', ''); # Victron VRM Credentials gesetzt my ($coset, $lat, $lon) = locCoordinates(); # Koordinaten im global device my $rip; - $rip = 1 if(exists $data{$name}{statusapi}{'?IdPair'}); # es existiert mindestens ein Paar RoofTop-ID / API-Key - - my $pv0 = NexthoursVal ($hash, 'NextHour00', 'pvfc', undef); # der erste PV ForeCast Wert + $rip = 1 if(exists $data{$name}{statusapi}{'?IdPair'}); # es existiert mindestens ein Paar RoofTop-ID / API-Key + my $pv0 = NexthoursVal ($hash, 'NextHour00', 'pvfc', undef); # der erste PV ForeCast Wert my $link = qq{$name}; my $height = AttrNum ($name, 'graphicBeamHeightLevel1', BHEIGHTLEVEL); @@ -23710,58 +23753,6 @@ to ensure that the system configuration is correct.
- -
- - -
-
+ + +
+ + +
  • setupStringPeak <Stringname1>=<Peak> [<Stringname2>=<Peak> <Stringname3>=<Peak> ...]

    @@ -26249,58 +26292,6 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
    -
      - -
    • setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...]

      - - Ausrichtung <dir> der Solarmodule im String "StringnameX". Der Stringname ist ein Schlüsselwert des - Attributs setupInverterStrings.
      - Die Richtungsangabe <dir> kann als Azimut Kennung oder als Azimut Wert angegeben werden:

      - -
        - - - - - - - - - - - -
        Kennung Azimut
        N -180 Nordausrichtung
        NE -135 Nord-Ost Ausrichtung
        E -90 Ostausrichtung
        SE -45 Süd-Ost Ausrichtung
        S 0 Südausrichtung
        SW 45 Süd-West Ausrichtung
        W 90 Westausrichtung
        NW 135 Nord-West Ausrichtung
        -
      -
      - - Azimut Werte sind Ganzzahlen im Bereich von -180 bis 180. Obwohl die genannten Kennungen verwendet werden können, - wird empfohlen den genauen Azimut Wert im Attribut anzugeben. Dadurch können beliebige Zwischenwerte wie 83, 48 etc. - angeben werden. -

      - -
        - Beispiel:
        - set <name> setupStringAzimuth Ostdach=-85 Südgarage=S S3=132
        -
      -
    • -
    -
    - -
      - -
    • setupStringDeclination <Stringname1>=<Winkel> [<Stringname2>=<Winkel> <Stringname3>=<Winkel> ...]

      - - Neigungswinkel der Solarmodule. Der Stringname ist ein Schlüsselwert des Attributs setupInverterStrings.
      - Als Neigungswinkel können Ganzzahlen zwischen 0 und 90 angegeben werden. (0 = waagerecht, 90 = senkrecht).

      - -
        - Beispiel:
        - set <name> setupStringDeclination Ostdach=40 Südgarage=60 S3=30
        -
      -
    • -
    -
    -
    • operatingMemory backup | save | recover-<Datei>

      @@ -28089,12 +28080,12 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. Einheit die jeweilige Einheit (W,Wh,kW,kWh) - icon Icon und/oder (nur) Farbe zur Darstellung der Batterie in der Balkengrafik (optional) + icon Icon und/oder (nur) Farbe der Batterie in der Balkengrafik entsprechend des Status (optional). Die Farbe kann als Bezeichner (z.B. blue) oder HEX-Wert (z.B. #d9d9d9) angegeben werden. <empfohlen> - die Aufladung ist empfohlen aber inaktiv (kein Aufladen oder Entladen) <aufladen> - wird verwendet wenn die Batterie aktuell aufgeladen wird <entladen> - wird verwendet wenn die Batterie aktuell entladen wird - <unterlassen> - wird verwendet wenn die Aufladung nicht empfohlen ist + <unterlassen> - nur bei Überschreitung des Einspeiselimits aufladen show Steuerung der Anzeige der Batterie in der Balkengrafik (optional) 0 - keine Anzeige des Gerätes (default) @@ -28432,6 +28423,58 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.

  • + + +
    + + +
  • setupStringPeak <Stringname1>=<Peak> [<Stringname2>=<Peak> <Stringname3>=<Peak> ...]