|
From: <pf...@us...> - 2012-05-05 19:33:25
|
Revision: 786
http://openautomation.svn.sourceforge.net/openautomation/?rev=786&view=rev
Author: pfry
Date: 2012-05-05 19:33:18 +0000 (Sat, 05 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Szenencontroller.pl
wiregate/plugin/generic/conf.d/Szenencontroller.conf
Added Paths:
-----------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Added: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl (rev 0)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-05 19:33:18 UTC (rev 786)
@@ -0,0 +1,434 @@
+#!/usr/bin/perl -w
+##################
+# Logikprozessor #
+##################
+# Wiregate-Plugin
+# (c) 2012 Fry under the GNU Public License
+
+#$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
+
+my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+
+# eibgaconf fixen falls nicht komplett indiziert
+if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
+{
+ for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
+ {
+ $eibgaconf{$ga}{ga}=$ga;
+ my $name=$eibgaconf{$ga}{name};
+ next unless defined $name;
+ $eibgaconf{$name}=$eibgaconf{$ga};
+
+ next unless $name=~/^\s*(\S+)/;
+ my $short=$1;
+ $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
+
+ $eibgaconf{$ga}{short}=$short;
+ $eibgaconf{$short}=$eibgaconf{$ga};
+ }
+}
+
+# Tools und vorbesetzte Variablen fue die Logiken
+sub limit { my ($lo,$x,$hi)=@_; return $x<$lo?$lo:($x>$hi?$hi:$x); }
+my $day_of_week=`/bin/date +"%a"`;
+my $weekend=($day_of_week=~/Sa|So/);
+my $time_of_day=`/bin/date +"%X"`;
+my $hour_of_day=substr($time_of_day,0,2);
+my $day=($hour_of_day>7 && $hour_of_day<23);
+my $night=!$day;
+
+# Konfigurationsfile einlesen
+my %devices=(0=>'Wiregate');
+my %logic=();
+my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
+open FILE, "<$conf" || return "no config found";
+my @lines = <FILE>;
+close FILE;
+eval("@lines");
+return "config error: $@" if $@;
+
+# Aufrufgrund ermitteln
+my $event=undef;
+if (!$plugin_initflag)
+{ $event='restart'; } # Restart des daemons / Reboot
+elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
+{ $event='modified'; } # Plugin modifiziert
+elsif (%msg) { $event='bus'; } # Bustraffic
+elsif ($fh) { $event='socket'; } # Netzwerktraffic
+else { $event='cycle'; } # Zyklus
+
+# Konfigfile seit dem letzten Mal geaendert?
+my $config_modified = (24*60*60*(-M $conf)-time()) > $plugin_info{$plugname.'_configtime'};
+
+# Plugin-Code
+my $retval='';
+
+if($event=~/restart|modified/ || $config_modified)
+{
+ $plugin_info{$plugname.'_configtime'}=(24*60*60*(-M $conf)-time());
+
+ # alle Variablen loeschen und neu initialisieren, alle GAs abonnieren
+ for my $k (grep /^$plugname\_/, keys %plugin_info)
+ {
+ delete $plugin_info{$k};
+ }
+
+ my $count=0;
+ my $err=0;
+
+ for my $t (keys %logic)
+ {
+ # Eintrag pruefen
+ if(defined $logic{$t}{receive} && ref $logic{$t}{receive} && ref $logic{$t}{receive} ne 'ARRAY')
+ {
+ plugin_log($plugname, "Config err: \$logic{$t}{receive} ist weder Skalar noch ARRAY-Referenz ([...]).");
+ next;
+ }
+
+ if(defined $logic{$t}{translate} && ref $logic{$t}{translate} && ref $logic{$t}{translate} ne 'CODE')
+ {
+ plugin_log($plugname, "Config err: \$logic{$t}{translate} ist weder Skalar noch CODE-Referenz (sub {...}).");
+ next;
+ }
+
+ if(defined $logic{$t}{state} && ref $logic{$t}{state} && ref $logic{$t}{state} ne 'HASH')
+ {
+ plugin_log($plugname, "Config err: \$logic{$t}{state} ist weder Skalar noch HASH-Referenz ({...}).");
+ next;
+ }
+
+ # transmit-Adresse abonnieren
+ my $transmit=groupaddress($logic{$t}{transmit});
+ $plugin_subscribe{$transmit}{$plugname}=1;
+
+ $count++;
+
+ # alle receive-Adressen abonnieren (eine oder mehrere)
+ my $receive=groupaddress($logic{$t}{receive});
+
+ next unless $receive;
+
+ unless(ref $receive)
+ {
+ $plugin_subscribe{$receive}{$plugname}=1;
+ }
+ else
+ {
+ for my $rec (@{$receive})
+ {
+ $plugin_subscribe{$rec}{$plugname}=1;
+ }
+ }
+ }
+
+ $plugin_info{$plugname.'_cycle'}=0;
+ $retval.=$count." initialisiert";
+}
+
+if($event=~/bus/)
+{
+ # Bustraffic bedienen - nur Schreibzugriffe der iButtons interessieren
+ return unless $msg{apci}=~/A_GroupValue_(Write|Read)/;
+
+ my $ga=$msg{dst};
+ my $in=$msg{value};
+ my $keep_subscription=0; # falls am Ende immer noch Null, die GA stornieren
+
+ # welche translate-Logik ist aufgerufen?
+ for my $t (keys %logic)
+ {
+ my $transmit=groupaddress($logic{$t}{transmit});
+ my $transmit_ga = ($ga eq $transmit);
+
+ my $receive=groupaddress($logic{$t}{receive});
+ my $receive_ga=0;
+
+ if(defined $receive)
+ {
+ unless(ref $logic{$t}{receive})
+ {
+ $receive_ga=1 if $ga eq $receive;
+ }
+ else
+ {
+ $receive_ga=1 if grep /^$ga$/, @{$receive};
+ }
+ }
+
+ next unless $receive_ga || $transmit_ga; # diese Logik nicht anwendbar
+
+ $keep_subscription=1;
+
+ # Sonderfall: Read- und Write-Telegramme auf der Transmit-Adresse?
+ if($transmit_ga)
+ {
+ # Ein Read-Request auf einer Transmit-GA wird mit dem letzten Ergebnis beantwortet
+ # Read-Requests auf die receive-Adressen werden gar nicht beantwortet
+ if($msg{apci} eq "A_GroupValue_Read")
+ {
+ my $result=$plugin_info{$plugname.'_'.$t.'_result'};
+ if(defined $result)
+ {
+ knx_write($ga, $result);
+ }
+ next;
+ }
+ elsif(!$receive_ga) # Receive geht vor!
+ {
+ if(defined $in) # Write-Telegramm: das waren moeglicherweise wir selbst, also nicht antworten
+ {
+ $plugin_info{$plugname.'_'.$t.'_result'}=$in; # einfach Input ablegen
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
+ }
+ next;
+ }
+ }
+
+ # Wir wissen ab hier: Es liegt ein Write-Telegramm auf einer der receive-Adressen vor
+
+ # Nebenbei berechnen wir noch zwei Flags, die Zirkelkommunikation verhindern sollen
+ # (Logik antwortet auf sich selbst in einer Endlosschleife)
+
+ # kommt transmit-GA unter den receive-GAs vor?
+ # Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
+ my $possible_circle=$transmit_ga;
+
+ # war Wiregate der Sender des Telegramms?
+ my $sender_is_wiregate=int($msg{src})==0;
+
+ # Es folgt die eigentliche Logik-Engine - als erstes definiere das Input-Array fuer die Logik
+ my $input=$in; # Skalarer Fall (der haeufigste)
+
+ # Array-Fall: bereite Input-Array fuer Logik vor
+ if(ref $receive)
+ {
+ $input=();
+ for my $rec (@{$receive})
+ {
+ my $rec=groupaddress($rec);
+
+ $possible_circle=1 if $transmit eq $rec;
+
+ if($ga eq $rec)
+ {
+ push @{$input}, $in;
+ }
+ else
+ {
+ push @{$input}, knx_read($rec, 300);
+ }
+ }
+ }
+
+ # ab hier liegt $input komplett vor, und nun muss die Logik ausgewertet
+ # und das Resultat auf der Transmit-GA uebertragen
+ my $result=undef;
+
+ unless(ref $logic{$t}{translate})
+ {
+ # Trivialer Fall: translate enthaelt einen fixen Rueckgabewert
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result=$logic{$t}{translate};
+ }
+ elsif(!ref $logic{$t}{state})
+ {
+ # Einfacher aber haeufiger Fall: skalarer $state
+ # $state mit Ergebnis des letzten Aufrufs vorbesetzen
+ my $state=$plugin_info{$plugname.'_'.$t.'_result'};
+
+ # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
+ $result=$logic{$t}{translate}($state,$input);
+
+ # Ergebnis des letzten Aufrufs zurueckschreiben
+ if(defined $result)
+ {
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result;
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
+ }
+ }
+ else
+ {
+ # Komplexer Fall: $state-Hash aus %logic initialisieren
+ my $state=$logic{$t}{state};
+ my @vars=keys %{$state};
+ push @vars, 'result';
+
+ # Nun die dynamischen Variablen aus plugin_info hinzufuegen
+ for my $v (@vars)
+ {
+ $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v} if defined $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
+
+ # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
+ $result=$state->{result}=$logic{$t}{translate}($state,$input);
+
+ # Alle dynamischen Variablen wieder nach plugin_info schreiben
+ # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird,
+ # erlauben wir nur Eintraege mit defined-Werten
+ for my $v (@vars)
+ {
+ if(defined $state->{$v})
+ {
+ $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
+ }
+ else
+ {
+ # wenn die Logik den Wert undef in eine state-Variable schreibt,
+ # wird beim naechsten Aufruf wieder der Startwert aus %logic genommen,
+ delete $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
+ }
+ }
+
+ # In bestimmten Sonderfaellen nichts schicken
+ next unless defined $result; # Resultat undef => nichts senden
+ next if $logic{$t}{transmit_only_on_request};
+ next if $possible_circle && $sender_is_wiregate && $in eq $result;
+
+ # Falls delay spezifiziert, wird ein "Wecker" gestellt, um in einem spaeteren Aufruf den Wert zu senden
+ if($logic{$t}{delay})
+ {
+ $plugin_info{$plugname.'__'.$t.'_timer'}=time()+$logic{$t}{delay};
+ set_timer();
+ }
+ else
+ {
+ knx_write($transmit, $result);
+ }
+ }
+
+ unless($keep_subscription)
+ {
+ delete $plugin_subscribe{$ga}{$plugname};
+ }
+}
+
+# Evtl. faellige Timer finden
+for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
+{
+ next if time()<$plugin_info{$timer}; # weiter falls noch nicht faellig
+
+ # Timer loeschen
+ delete $plugin_info{$timer};
+
+ # Relevanten Eintrag von %logic ermitteln
+ $timer=~/$plugname\__(.*)_timer/;
+ my $t=$1;
+
+ # Transmit-GA
+ my $transmit=groupaddress($logic{$t}{transmit});
+
+ # zu sendendes Resultat
+ my $result=$plugin_info{$plugname.'_'.$t.'_result'};
+ next unless defined $result;
+
+ knx_write($transmit, $result);
+
+ # Timer fuer nachste Aktion setzen
+ set_timer();
+}
+
+return unless $retval;
+return $retval;
+
+
+# Zeit bis zum naechsten Aufruf dieses Plugins berechnen
+
+sub set_timer
+{
+ # optimalen Wert fuer Aufrufzyklus finden, um beim naechsten Aufruf was zu senden
+ my $nexttimer=undef;
+ for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
+ {
+ $nexttimer=$plugin_info{$timer} if !defined $nexttimer || $plugin_info{$timer}<$nexttimer;
+ }
+
+ unless(defined $nexttimer)
+ {
+ $plugin_info{$plugname."_cycle"}=0;
+ }
+ else
+ {
+ my $cycle=$nexttimer-time();
+ $cycle=1 if $cycle<1;
+ $plugin_info{$plugname."_cycle"}=$cycle;
+ }
+}
+
+# Umgang mit GA-Kurznamen und -Adressen
+
+sub groupaddress
+{
+ my $short=shift;
+
+ return unless defined $short;
+
+ if(ref $short)
+ {
+ my $ga=[];
+ for my $sh (@{$short})
+ {
+ if($sh!~/^[0-9\/]+$/ && defined $eibgaconf{$sh}{ga})
+ {
+ push @{$ga}, $eibgaconf{$sh}{ga};
+ }
+ else
+ {
+ push @{$ga}, $sh;
+ }
+ }
+ return $ga;
+ }
+ else
+ {
+ my $ga=$short;
+
+ if($short!~/^[0-9\/]+$/ && defined $eibgaconf{$short}{ga})
+ {
+ $ga=$eibgaconf{$short}{ga};
+ }
+
+ return $ga;
+ }
+}
+
+sub shortname
+{
+ my $gas=shift;
+
+ return unless defined $gas;
+ return $gas unless $use_short_names;
+
+ if(ref $gas)
+ {
+ my $sh=[];
+ for my $ga (@{$gas})
+ {
+ if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
+ {
+ push @{$sh}, $eibgaconf{$ga}{short};
+ }
+ else
+ {
+ push @{$sh}, $ga;
+ }
+ }
+ return $sh;
+ }
+ else
+ {
+ my $sh=$gas;
+
+ if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
+ {
+ $sh=$eibgaconf{$gas}{short};
+ }
+
+ return $sh;
+ }
+}
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-05 15:56:45 UTC (rev 785)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-05 19:33:18 UTC (rev 786)
@@ -5,9 +5,9 @@
# Wiregate-Plugin
# (c) 2012 Fry under the GNU Public License
-# $plugin_info{$plugname.'_cycle'}=0; return "deaktiviert";
+#$plugin_info{$plugname.'_cycle'}=0; return "deaktiviert";
-my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
# eibgaconf fixen falls nicht komplett indiziert
if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
@@ -41,17 +41,24 @@
else { $event='cycle'; } # Zyklus
# Konfigurationsfile einlesen
-my $conf=$plugname; $conf=~s/\.pl$/.conf/;
-$conf="/etc/wiregate/plugin/generic/conf.d/$conf";
my %scene=();
+my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
my $err=read_from_config();
return $err if $err;
+# Konfigfile seit dem letzten Mal geaendert?
+my $config_modified = ($scene{storage} ne 'configfile' && (24*60*60*(-M $conf)-time()) > $plugin_info{$plugname.'_configtime'});
+
# Dynamisch definierte Szenen aus plugin_info einlesen
recall_from_plugin_info();
-if($event=~/restart|modified/)
+# Plugin-Code
+my $retval='';
+
+if($event=~/restart|modified/ || $config_modified)
{
+ $plugin_info{$plugname.'_configtime'}=(24*60*60*(-M $conf)-time());
+
# Cleanup aller Szenenvariablen in %plugin_info
for my $k (grep /^$plugname\_/, keys %plugin_info)
{
@@ -64,7 +71,7 @@
for my $sc (sort keys %scene)
{
- next if $sc eq 'storage';
+ next if $sc=~/^(storage|debug)$/;
my $store=$scene{$sc}{store};
my $recall=$scene{$sc}{recall};
@@ -85,12 +92,9 @@
$plugin_info{$plugname.'__SceneLookup'}=$scene_lookup;
$plugin_info{$plugname.'_cycle'}=0;
- return $count." initialisiert";
+ $retval.=$count." initialisiert";
}
-
-my $retval='';
-
-if($event=~/bus/)
+elsif($event=~/bus/)
{
# nur auf Write-Telegramme reagieren
return if $msg{apci} ne 'A_GroupValue_Write';
@@ -127,11 +131,15 @@
}
# Szenencode
- my $z="$sc\__$n";
+ my $z="$sc\#$n";
if($cmd eq 'S') # Szene speichern
{
- $retval.="Szene $z speichern: ";
+ $retval.="Szene $z speichern: " if $scene{debug};
+
+ my $confirm_store=$scene{$sc}{confirm_store};
+ $confirm_store=$eibgaconf{$confirm_store}{ga} if $confirm_store!~/^[0-9\/]+$/ && defined $eibgaconf{$confirm_store};
+ knx_write($confirm_store,1); # Translator feuert spaeter dann eine Null hinterher, um die LED auszuschalten
delete $scene{$z};
@@ -139,15 +147,18 @@
{
my $wga=$scene{$sc}{gas}{$ga}; # auf diese GA muss spaeter geschrieben werden
$wga=$eibgaconf{$wga}{short} if $wga=~/^[0-9\/]+$/ && $use_short_names && defined $eibgaconf{$wga}{short};
+
$ga=$eibgaconf{$ga}{ga} if $ga!~/^[0-9\/]+$/ && defined $eibgaconf{$ga};
- $scene{$z}{$wga}=knx_read($ga,300);
+ $scene{$z}{$wga}=knx_read($ga,300); # KOSTET ZEIT FALLS GERAETE NICHT ANTWORTEN!
+
if(defined $scene{$z}{$wga})
{
- $retval.=$wga.'->'.$scene{$z}{$wga}.' ';
+ $retval.=$wga.'->'.$scene{$z}{$wga}.' ' if $scene{debug};
}
else
{
+ $retval.=$wga.'? ' if $scene{debug};
delete $scene{$z}{$wga};
}
}
@@ -163,14 +174,12 @@
}
else # Szene abrufen
{
- $retval.="Szene $z abrufen: ";
+ $retval.="Szene $z abrufen: " if $scene{debug};
for my $v (keys %{$scene{$z}})
{
- my $ga=$v;
- $ga=$eibgaconf{$ga}{ga} if $ga!~/^[0-9\/]+$/ && defined $eibgaconf{$ga};
- knx_write($ga,$scene{$z}{$v});
- $retval.=$ga.'->'.$scene{$z}{$v}.' ';
+ knx_write(groupaddress($v),$scene{$z}{$v});
+ $retval.=$v.'->'.$scene{$z}{$v}.' ' if $scene{debug};
}
}
}
@@ -226,9 +235,81 @@
{
for my $k (grep /^$plugname\__/, keys %plugin_info)
{
- next unless($k=~/^$plugname\__([A-Z0-9 ]+__[0-9]+)__(.*)$/i);
+ next unless($k=~/^$plugname\__(.*\#.*)__(.*)$/);
my ($z,$v)=($1,$2);
$scene{$z}{$v}=$plugin_info{$k};
}
}
+# Umgang mit GA-Kurznamen und -Adressen
+sub groupaddress
+{
+ my $short=shift;
+
+ return unless defined $short;
+
+ if(ref $short)
+ {
+ my $ga=[];
+ for my $sh (@{$short})
+ {
+ if($sh!~/^[0-9\/]+$/ && defined $eibgaconf{$sh}{ga})
+ {
+ push @{$ga}, $eibgaconf{$sh}{ga};
+ }
+ else
+ {
+ push @{$ga}, $sh;
+ }
+ }
+ return $ga;
+ }
+ else
+ {
+ my $ga=$short;
+
+ if($short!~/^[0-9\/]+$/ && defined $eibgaconf{$short}{ga})
+ {
+ $ga=$eibgaconf{$short}{ga};
+ }
+
+ return $ga;
+ }
+}
+
+sub shortname
+{
+ my $gas=shift;
+
+ return unless defined $gas;
+ return $gas unless $use_short_names;
+
+ if(ref $gas)
+ {
+ my $sh=[];
+ for my $ga (@{$gas})
+ {
+ if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
+ {
+ push @{$sh}, $eibgaconf{$ga}{short};
+ }
+ else
+ {
+ push @{$sh}, $ga;
+ }
+ }
+ return $sh;
+ }
+ else
+ {
+ my $sh=$gas;
+
+ if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
+ {
+ $sh=$eibgaconf{$gas}{short};
+ }
+
+ return $sh;
+ }
+}
+
Added: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf (rev 0)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-05 19:33:18 UTC (rev 786)
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+#
+# Logikprozessor.pl - Konfiguration
+#
+
+%logic=(
+ # 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
+ mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { my (undef,$input)=@_; 2*$input; }, },
+ # das "undef" steht da einfach, weil uns der letzte Ergebniswert nicht interessiert
+
+ # 2. Die Werte auf der ersten GA werden aufsummiert, das Ergebis auf der anderen GA gesendet.
+ # Damit kann man bspw aus einem relativen Dimmwert einen absoluten Dimmwert machen.
+ sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { my ($state,$input)=@_; $state+$input; }, },
+ # $state enthaelt das jeweils letzte Ergebnis.
+
+ # 3. Wie oben, aber das Ergebnis limitiert auf den Bereich 0-100
+ lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { my ($state,$input)=@_; limit(0,$state+$input,100); }, },
+
+ # 4. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an
+ # die transmit-Adresse (hier gleich) geschickt. Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der
+ # translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
+ # Rueckgabewert spezifiziert.
+ stair => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, },
+ # Damit im Fall transmit != receive der Translator nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, wird nur dann gesendet,
+ # wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
+
+ # 5. Memory-Funktion. Wenn ein Write-Telegramm auf die Transmit-Adresse kommt, speichert der Logikprozessor den Wert ab.
+ # Das Flag "transmit_on_request" bewirkt, dass nichts gesendet wird, jedoch wird eine Leseanfrage auf der transmit-GA immer
+ # mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Damit laesst sich eine Speicherfunktion realisieren.
+ # Hier speichert Translator also den Input ab und sendet den errechneten (=gespeicherten) Wert NUR AUF ANFRAGE.
+ memory => { transmit=>'1/2/9', transmit_on_request=>1 },
+ # Eine receive-Adresse oder translate-Logik werden hier gar nicht gebraucht.
+
+ # 6. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
+ # wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
+ und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { my (undef,$input)=@_; $input->[0] && $input->[1]; }, },
+
+ # 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Translators aus mehreren Werten.
+ # Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
+ complex => { receive=>'9/5/205',
+ transmit=>'9/5/206',
+ delay=>30,
+ state => {val1=>0, val2=>0},
+ translate => sub { my ($state,$input)=@_;
+ $state->{val2}=$state->{val1}; $state->{val1}=$state->{result};
+ $state->{val2}+$state->{val1}+$input; },
+ },
+ # Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
+
+ # 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Translator.pl $use_short_names=1
+ # und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
+ D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
+ translate => sub { my ($state,$input)=@_; limit(0,$state+20*$input,100); }, },
+ # ist doch leserlicher, oder? Hier wird ein relativer Dimmwert durch Skalierung und Summierung
+ # in einen absoluten Wert umgewandelt
+ );
+
+
+
Modified: wiregate/plugin/generic/conf.d/Szenencontroller.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Szenencontroller.conf 2012-05-05 15:56:45 UTC (rev 785)
+++ wiregate/plugin/generic/conf.d/Szenencontroller.conf 2012-05-05 19:33:18 UTC (rev 786)
@@ -5,6 +5,9 @@
%scene=(
+ # Debugflag fuer Kontrollmeldungen auf /var/log/wiregate_plugin.log auf 1 setzen
+ debug=>1,
+
# Storage auf 'configfile' setzen, wenn die Szenen hier im Konfigfile gespeichert werden sollen.
# Sie ueberleben in diesem Fall sogar das Loeschen des plugin_info und gelten somit als "fest verdrahtet".
# Ansonsten 'plugin_info' setzen, dann werden die Szenen im %plugin_info gespeichert.
@@ -48,10 +51,10 @@
#
# 4. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Translator.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
- Schlafzimmer2 => {store=>'ZS_SZ', recall=>'ZA_SZ', gas=>{'LI_SZ'=>'LI_SZ', 'JX_SZ'=>'JW_SZ', 'JQ_SZ'=>'JP_SZ'}},
+ Schlafzimmer2 => {store=>'ZS_SZ', recall=>'ZA_SZ', confirm_store=>'ZC_SZ', gas=>{'LI_SZ'=>'LI_SZ', 'JX_SZ'=>'JW_SZ', 'JQ_SZ'=>'JP_SZ'}},
# ist doch leserlicher, oder? SZ=Schlafzimmer, ZA=Szene abrufen, ZS=Szene speichern, LI=Licht,
# JX=Jalousiewinkel abfragen, JW=Jalousiewinkel einstellen, JQ=Jalousieposition abfragen, JP=Jalousie positionieren
-
+ # confirm_store ist uebrigens eine GA (Trigger), die gesendet wird, um ein Szenenabspeichern zu bestaetigen
);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-06 19:15:59
|
Revision: 793
http://openautomation.svn.sourceforge.net/openautomation/?rev=793&view=rev
Author: pfry
Date: 2012-05-06 19:15:53 +0000 (Sun, 06 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-06 12:19:51 UTC (rev 792)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-06 19:15:53 UTC (rev 793)
@@ -328,11 +328,11 @@
next unless defined $result;
knx_write($transmit, $result);
-
- # Timer fuer nachste Aktion setzen
- set_timer();
}
+# Timer fuer nachste Aktion setzen
+set_timer();
+
return unless $retval;
return $retval;
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-06 12:19:51 UTC (rev 792)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-06 19:15:53 UTC (rev 793)
@@ -5,16 +5,16 @@
%logic=(
# 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
- mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { my (undef,$input)=@_; 2*$input; }, },
+ mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { 2*$input; }, },
# das "undef" steht da einfach, weil uns der letzte Ergebniswert nicht interessiert
# 2. Die Werte auf der ersten GA werden aufsummiert, das Ergebis auf der anderen GA gesendet.
# Damit kann man bspw aus einem relativen Dimmwert einen absoluten Dimmwert machen.
- sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { my ($state,$input)=@_; $state+$input; }, },
+ sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { $state+$input; }, },
# $state enthaelt das jeweils letzte Ergebnis.
# 3. Wie oben, aber das Ergebnis limitiert auf den Bereich 0-100
- lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { my ($state,$input)=@_; limit(0,$state+$input,100); }, },
+ lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { limit(0,$state+$input,100); }, },
# 4. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an
# die transmit-Adresse (hier gleich) geschickt. Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der
@@ -33,7 +33,7 @@
# 6. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
# wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
- und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { my (undef,$input)=@_; $input->[0] && $input->[1]; }, },
+ und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { $input->[0] && $input->[1]; }, },
# 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten.
# Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
@@ -41,8 +41,7 @@
transmit=>'9/5/206',
delay=>30,
state => {val1=>0, val2=>0},
- translate => sub { my ($state,$input)=@_;
- $state->{val2}=$state->{val1}; $state->{val1}=$state->{result};
+ translate => sub { $state->{val2}=$state->{val1}; $state->{val1}=$state->{result};
$state->{val2}+$state->{val1}+$input; },
},
# Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
@@ -50,7 +49,7 @@
# 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
- translate => sub { my ($state,$input)=@_; limit(0,$state+20*$input,100); }, },
+ translate => sub { limit(0,$state+20*$input,100); }, },
# ist doch leserlicher, oder? Hier wird ein relativer Dimmwert durch Skalierung und Summierung
# in einen absoluten Wert umgewandelt
);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-07 20:47:02
|
Revision: 795
http://openautomation.svn.sourceforge.net/openautomation/?rev=795&view=rev
Author: pfry
Date: 2012-05-07 20:46:56 +0000 (Mon, 07 May 2012)
Log Message:
-----------
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-07 18:51:21 UTC (rev 794)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-07 20:46:56 UTC (rev 795)
@@ -30,21 +30,32 @@
# Tools und vorbesetzte Variablen fue die Logiken
sub limit { my ($lo,$x,$hi)=@_; return $x<$lo?$lo:($x>$hi?$hi:$x); }
-my $day_of_week=`/bin/date +"%a"`;
-my $weekend=($day_of_week=~/Sa|So/);
-my $time_of_day=`/bin/date +"%X"`;
-my $hour_of_day=substr($time_of_day,0,2);
-my $day=($hour_of_day>7 && $hour_of_day<23);
+my $date=`/bin/date +"%W,%a,%u,%m,%d,%Y,%H,%M,%X"`;
+plugin_log($plugname, "Datum/Uhrzeit konnte nicht lesbar.") unless $date=~/^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/;
+my $calendar_week=$1;
+my $day_of_week=$2;
+my $day_of_week_no=$3;
+my $month=int($4);
+my $day_of_month=int($5);
+my $year=int($6);
+my $hour=int($7);
+my $minute=int($8);
+my $time_of_day=$9; # '08:30:43'
+my $weekend=($day_of_week_no>=6);
+my $weekday=!$weekend;
+my $day=($hour>7 && $hour<23);
my $night=!$day;
+my $systemtime=time();
# Konfigurationsfile einlesen
-my %devices=(0=>'Wiregate');
my %logic=();
my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
open FILE, "<$conf" || return "no config found";
-my @lines = <FILE>;
+$/=undef;
+my $lines = <FILE>;
+$lines =~ s/((?:translate|\'translate\'|\"translate\")\s*=>\s*sub\s*\{)/$1 my \(\$state,\$input\)=\@\_;/sg;
close FILE;
-eval("@lines");
+eval($lines);
return "config error: $@" if $@;
# Aufrufgrund ermitteln
@@ -97,13 +108,26 @@
next;
}
+ if(defined $logic{$t}{timer} && defined $logic{$t}{delay})
+ {
+ plugin_log($plugname, "Config err: \$logic{$t}: delay und timer festgelegt, ignoriere delay");
+ }
+
# transmit-Adresse abonnieren
my $transmit=groupaddress($logic{$t}{transmit});
$plugin_subscribe{$transmit}{$plugname}=1;
$count++;
- # alle receive-Adressen abonnieren (eine oder mehrere)
+ # Timer-Logiken reagieren nicht auf Bustraffic auf den receive-Adressen
+ # fuer Timer-Logiken: ersten Call berechnen
+ if($logic{$t}{timer})
+ {
+ set_next_call($t);
+ next;
+ }
+
+ # fuer Nicht-Timer-Logiken: alle receive-Adressen abonnieren (eine oder mehrere)
my $receive=groupaddress($logic{$t}{receive});
next unless $receive;
@@ -121,7 +145,6 @@
}
}
- $plugin_info{$plugname.'_cycle'}=0;
$retval.=$count." initialisiert";
}
@@ -143,7 +166,7 @@
my $receive=groupaddress($logic{$t}{receive});
my $receive_ga=0;
- if(defined $receive)
+ if(defined $receive && !$logic{$t}{timer})
{
unless(ref $logic{$t}{receive})
{
@@ -173,7 +196,7 @@
}
next;
}
- elsif(!$receive_ga) # Receive geht vor!
+ elsif(!$receive_ga) # Receive geht vor - bei Timer-Logiken ist receive_ga immer 0
{
if(defined $in) # Write-Telegramm: das waren moeglicherweise wir selbst, also nicht antworten
{
@@ -192,109 +215,30 @@
# Nebenbei berechnen wir noch zwei Flags, die Zirkelkommunikation verhindern sollen
# (Logik antwortet auf sich selbst in einer Endlosschleife)
- # kommt transmit-GA unter den receive-GAs vor?
- # Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
- my $possible_circle=$transmit_ga;
-
# war Wiregate der Sender des Telegramms?
my $sender_is_wiregate=int($msg{src})==0;
- # Es folgt die eigentliche Logik-Engine - als erstes definiere das Input-Array fuer die Logik
- my $input=$in; # Skalarer Fall (der haeufigste)
+ # Aufruf der Logik-Engine
+ my $result=execute_logic($t, $receive, $ga, $in);
- # Array-Fall: bereite Input-Array fuer Logik vor
- if(ref $receive)
+ # In bestimmten Sonderfaellen nichts schicken
+ next unless defined $result; # Resultat undef => nichts senden
+ next if $logic{$t}{transmit_only_on_request};
+
+ # Zirkelaufruf ausschliessen
+ if($sender_is_wiregate && $in eq $result)
{
- $input=();
- for my $rec (@{$receive})
- {
- my $rec=groupaddress($rec);
-
- $possible_circle=1 if $transmit eq $rec;
-
- if($ga eq $rec)
- {
- push @{$input}, $in;
- }
- else
- {
- push @{$input}, knx_read($rec, 300);
- }
- }
+ # kommt transmit-GA unter den receive-GAs vor?
+ # Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
+ next if $transmit_ga;
+ next if !ref $receive && ($transmit eq $receive);
+ next if ref $receive && grep /^$transmit$/, @{$receive};
}
- # ab hier liegt $input komplett vor, und nun muss die Logik ausgewertet
- # und das Resultat auf der Transmit-GA uebertragen
- my $result=undef;
-
- unless(ref $logic{$t}{translate})
- {
- # Trivialer Fall: translate enthaelt einen fixen Rueckgabewert
- $plugin_info{$plugname.'_'.$t.'_result'}=$result=$logic{$t}{translate};
- }
- elsif(!ref $logic{$t}{state})
- {
- # Einfacher aber haeufiger Fall: skalarer $state
- # $state mit Ergebnis des letzten Aufrufs vorbesetzen
- my $state=$plugin_info{$plugname.'_'.$t.'_result'};
-
- # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
- $result=$logic{$t}{translate}($state,$input);
-
- # Ergebnis des letzten Aufrufs zurueckschreiben
- if(defined $result)
- {
- $plugin_info{$plugname.'_'.$t.'_result'}=$result;
- }
- else
- {
- delete $plugin_info{$plugname.'_'.$t.'_result'};
- }
- }
- else
- {
- # Komplexer Fall: $state-Hash aus %logic initialisieren
- my $state=$logic{$t}{state};
- my @vars=keys %{$state};
- push @vars, 'result';
-
- # Nun die dynamischen Variablen aus plugin_info hinzufuegen
- for my $v (@vars)
- {
- $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v} if defined $plugin_info{$plugname.'_'.$t.'_'.$v};
- }
-
- # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
- $result=$state->{result}=$logic{$t}{translate}($state,$input);
-
- # Alle dynamischen Variablen wieder nach plugin_info schreiben
- # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird,
- # erlauben wir nur Eintraege mit defined-Werten
- for my $v (@vars)
- {
- if(defined $state->{$v})
- {
- $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
- }
- else
- {
- # wenn die Logik den Wert undef in eine state-Variable schreibt,
- # wird beim naechsten Aufruf wieder der Startwert aus %logic genommen,
- delete $plugin_info{$plugname.'_'.$t.'_'.$v};
- }
- }
- }
-
- # In bestimmten Sonderfaellen nichts schicken
- next unless defined $result; # Resultat undef => nichts senden
- next if $logic{$t}{transmit_only_on_request};
- next if $possible_circle && $sender_is_wiregate && $in eq $result;
-
# Falls delay spezifiziert, wird ein "Wecker" gestellt, um in einem spaeteren Aufruf den Wert zu senden
if($logic{$t}{delay})
{
- $plugin_info{$plugname.'__'.$t.'_timer'}=time()+$logic{$t}{delay};
- set_timer();
+ $plugin_info{$plugname.'__'.$t.'_timer'}=$systemtime+$logic{$t}{delay};
}
else
{
@@ -308,58 +252,301 @@
}
}
-# Evtl. faellige Timer finden
+# Evtl. faellige Timer finden, gleichzeitig Timer fuer nachste Aktion setzen
+my $nexttimer=undef;
for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
{
- next if time()<$plugin_info{$timer}; # weiter falls noch nicht faellig
-
- # Timer loeschen
- delete $plugin_info{$timer};
-
- # Relevanten Eintrag von %logic ermitteln
- $timer=~/$plugname\__(.*)_timer/;
- my $t=$1;
-
- # Transmit-GA
- my $transmit=groupaddress($logic{$t}{transmit});
-
- # zu sendendes Resultat
- my $result=$plugin_info{$plugname.'_'.$t.'_result'};
- next unless defined $result;
-
- knx_write($transmit, $result);
+ if(time()>=$plugin_info{$timer}) # Timer faellig? -> dann ausfuehren bzw. Resultat senden
+ {
+ # Relevanten Eintrag von %logic ermitteln
+ $timer=~/$plugname\__(.*)_timer/;
+ my $t=$1;
+
+ # Timer loeschen bzw. neu setzen
+ set_next_call($t);
+
+ # zu sendendes Resultat = zuletzt berechnetes Ergebnis der Logik
+ my $result=$plugin_info{$plugname.'_'.$t.'_result'};
+
+ # ...es sei denn, es ist eine timer-Logik. Die muss jetzt ausgefuehrt werden
+ if($logic{$t}{timer})
+ {
+ # Aufruf der Logik-Engine
+ $result=execute_logic($t, groupaddress($logic{$t}{receive}), undef, undef);
+ }
+
+ # Transmit-GA
+ my $transmit=groupaddress($logic{$t}{transmit});
+
+ next unless defined $result;
+ next if $logic{$t}{transmit_only_on_request};
+
+ knx_write($transmit, $result);
+ }
+ else # noch nicht faelliger Timer
+ {
+ $nexttimer=$plugin_info{$timer} if !defined $nexttimer || $plugin_info{$timer}<$nexttimer;
+ }
}
-# Timer fuer nachste Aktion setzen
-set_timer();
+# Cycle auf naechsten Aufruf setzen
+unless(defined $nexttimer)
+{
+ $plugin_info{$plugname."_cycle"}=0; # kein Aufruf noetig
+}
+else
+{
+ my $cycle=$nexttimer-time();
+ $cycle=1 if $cycle<1;
+ $plugin_info{$plugname."_cycle"}=$cycle;
+}
return unless $retval;
return $retval;
-# Zeit bis zum naechsten Aufruf dieses Plugins berechnen
+# Fuer Logiken mit timer-Klausel: Zeit des naechsten Aufrufs bestimmen
-sub set_timer
+sub next_day
{
- # optimalen Wert fuer Aufrufzyklus finden, um beim naechsten Aufruf was zu senden
- my $nexttimer=undef;
- for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
+ my $d=shift;
+
+ my $leapyear = ($d->{year} % 4)==0 && ($d->{year} % 100!=0 || $d->{year} % 400==0);
+ my @days_in_month=(0,31,28+$leapyear,31,30,31,30,31,31,30,31,30,31);
+
+ $d->{day_of_week} = ($d->{day_of_week} % 7)+1;
+ $d->{calendar_week} += $d->{day_of_week}==1;
+ $d->{day_of_month} = ($d->{day_of_month} % $days_in_month[$d->{month}])+1;
+ $d->{month} += $d->{day_of_month}==1;
+ $d->{month}=1 if $d->{month}==13;
+ $d->{year} += ($d->{day_of_month}==1 && $d->{month}==1);
+
+ return $d;
+}
+
+sub schedule_matches_day
+{
+ my ($schedule,$day)=@_;
+ # Beide Argumente sind Hash-Referenzen, wobei schedule jeweils auf Listen zeigt, aber $day alle Kategorien enthaelt
+ # zB $day={year=>2012,month=>4,day_of_month=>13,calendar_week=>16,day_of_week=>4,...};
+
+ my $match=1;
+
+ for my $c (keys %{$schedule})
{
- $nexttimer=$plugin_info{$timer} if !defined $nexttimer || $plugin_info{$timer}<$nexttimer;
+ next if $c eq 'time'; # es geht nur um Tage
+ unless(grep /^$day->{$c}$/, @{$schedule->{$c}})
+ {
+ $match=0;
+ last;
+ }
}
- unless(defined $nexttimer)
+ return $match;
+}
+
+sub set_next_call
+{
+ my $t=shift; # der relevante Eintrag in %logic
+ my $nextcall=undef;
+ my $days_until_nextcall=0;
+
+ # $logic{$t}{timer} ist eine Liste oder ein einzelner Eintrag
+ # jeder solche Eintrag ist ein Hash im Format
+ # {day_of_month=>[(1..7)],day_of_week=>'Mo',time=>['08:30','09:20']}
+ # das gerade genannte Beispiel bedeutet "jeden Monat jeweils der erster Montag, 8:30 oder 9:20"
+ # verwendbare Klauseln sind year, month, day_of_month, calendar_week, day_of_week und time
+ # Pflichtfeld ist lediglich time, die anderen duerfen auch entfallen.
+ # Jeder Wert darf ein Einzelwert oder eine Liste sein.
+ my $schedule=$logic{$t}{timer};
+ my $today={year=>$year,month=>$month,day_of_month=>$day_of_month,calendar_week=>$calendar_week,day_of_week=>$day_of_week_no};
+ my $time_of_day=`/bin/date +"%X"`;
+
+ # Schedule-Form standardisieren (alle Eintraege in Listenform setzen und Wochentage durch Zahlen ersetzen)
+ # dabei gleich schauen, ob HEUTE noch ein Termin ansteht
+ $schedule=[$schedule] if ref $schedule eq 'HASH';
+ my %weekday=(Mo=>1,Di=>2,Mi=>3,Do=>4,Fr=>5,Sa=>6,So=>7);
+
+ for my $s (@{$schedule})
{
- $plugin_info{$plugname."_cycle"}=0;
+ unless(ref $s eq 'HASH')
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t' ist kein Hash oder Liste von Hashes");
+ next;
+ }
+ unless(defined $s->{time})
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t' enthaelt mindestens einen Eintrag ohne Zeitangabe (time=>...)");
+ next;
+ }
+
+ for my $k (keys %{$s})
+ {
+ unless($k=~/^(year|month|calendar_week|day_of_month|day_of_week|time)$/)
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter Eintrag '$k'; erlaubt sind year, month, calendar_week, day_of_month, day_of_week, und Pflichteintrag ist time");
+ next;
+ }
+
+ unless(!ref $s->{$k} || ref $s->{$k} eq 'ARRAY')
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t': '$k' muss auf Skalar oder Array ($k=>[...]) verweisen");
+ next;
+ }
+
+ $s->{$k}=[$s->{$k}] unless ref $s->{$k} eq 'ARRAY'; # alle Kategorien in Listenform
+ map $_=$weekday{$_}, @{$s->{$k}} if $k eq 'day_of_week'; # Wochentage in Zahlenform
+ @{$s->{$k}}=sort @{$s->{$k}}; # alle Listen sortieren
+ }
+
+ # Steht heute aus diesem Schedule noch ein Termin an?
+ next unless schedule_matches_day($s,$today) && $s->{time}[-1] gt $time_of_day;
+
+ # Heute steht tatsaechlich noch ein Termin an! Welcher ist der naechste?
+ # Rueckwaerts durch die Liste $s->{time} suchen
+ my $nc=undef;
+ for(my $i=$#{$s->{time}}; $i>=0 && $s->{time}[$i] gt $time_of_day; $i--) { $nc=$s->{time}[$i]; }
+ $nextcall=$nc unless defined $nextcall && $nextcall lt $nc;
}
+
+ # Wenn $nextcall hier bereits definiert, enthaelt es die naechste Aufrufzeit des Timers im Format "08:30"
+ my $schedules_done=0;
+
+ # falls nextcall noch nicht definiert, geht es jetzt um den naechsten Tag mit Termin
+ until($schedules_done || defined $nextcall)
+ {
+ $schedules_done=1;
+ $days_until_nextcall++;
+ $today=next_day($today);
+
+ for my $s (@{$schedule})
+ {
+ $schedules_done=0 if !defined $s->{year} || $s->{year}[-1]>=$today->{year};
+ next unless schedule_matches_day($s,$today);
+
+ # an diesem Tag gibt es einen Termin! Wann ist der erste?
+ $nextcall=$s->{time}[0] unless defined $nextcall && $nextcall lt $s->{time}[0];
+ }
+ }
+
+ if(defined $nextcall)
+ {
+ plugin_log($plugname, "Naechster Aufruf der Logik '$t' um $nextcall".($days_until_nextcall?" in $days_until_nextcall Tagen.":"."));
+
+ # Zeitdelta zu jetzt berechnen
+ my $seconds=3600*(substr($nextcall,0,2)-substr($time_of_day,0,2))
+ +60*(substr($nextcall,3,2)-substr($time_of_day,3,2))-substr($time_of_day,6,2);
+ $nextcall=$systemtime+$seconds+3600*24*$days_until_nextcall;
+ $plugin_info{$plugname.'__'.$t.'_timer'}=$nextcall;
+ }
else
{
- my $cycle=$nexttimer-time();
- $cycle=1 if $cycle<1;
- $plugin_info{$plugname."_cycle"}=$cycle;
+ plugin_log($plugname, "Logik '$t' wird nicht mehr aufgerufen (alle in time=>... festgelegten Termine sind verstrichen).");
+
+ delete $plugin_info{$plugname.'__'.$t.'_timer'};
}
}
+
+# Es folgt die eigentliche Logik-Engine
+sub execute_logic
+{
+ my ($t, $receive, $ga, $in)=@_; # Logikindex $t, Bustelegramm erhalten auf $ga mit Inhalt $in
+
+ # als erstes definiere das Input-Array fuer die Logik
+ my $input=$in;
+
+ # Array-Fall: bereite Input-Array fuer Logik vor
+ if(!ref $receive)
+ {
+ # wenn ga gesetzt, steht der Input-Wert in $in
+ # wenn receive undefiniert, gibt es keine receive-GA
+ $in=$input=knx_read($receive, 300) if !$ga && $receive;
+ }
+ else
+ {
+ $input=();
+ for my $rec (@{$receive})
+ {
+ my $rec=groupaddress($rec);
+
+ if($ga eq $rec)
+ {
+ push @{$input}, $in;
+ }
+ else
+ {
+ push @{$input}, knx_read($rec, 300);
+ }
+ }
+ }
+
+ # ab hier liegt $input komplett vor, und nun muss die Logik ausgewertet
+ # und das Resultat auf der Transmit-GA uebertragen
+ my $result=undef;
+
+ unless(ref $logic{$t}{translate})
+ {
+ # Trivialer Fall: translate enthaelt einen fixen Rueckgabewert
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result=$logic{$t}{translate};
+ }
+ elsif(!ref $logic{$t}{state})
+ {
+ # Einfacher aber haeufiger Fall: skalarer $state
+ # $state mit Ergebnis des letzten Aufrufs vorbesetzen
+ my $state=$plugin_info{$plugname.'_'.$t.'_result'};
+
+ # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
+ $result=$logic{$t}{translate}($state,$input);
+
+ # Ergebnis des letzten Aufrufs zurueckschreiben
+ if(defined $result)
+ {
+ $plugin_info{$plugname.'_'.$t.'_result'}=$result;
+ }
+ else
+ {
+ delete $plugin_info{$plugname.'_'.$t.'_result'};
+ }
+ }
+ else
+ {
+ # Komplexer Fall: $state-Hash aus %logic initialisieren
+ my $state=$logic{$t}{state};
+ my @vars=keys %{$state};
+ push @vars, 'result';
+
+ # Nun die dynamischen Variablen aus plugin_info hinzufuegen
+ for my $v (@vars)
+ {
+ $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v} if defined $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
+
+ # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
+ $result=$state->{result}=$logic{$t}{translate}($state,$input);
+
+ # Alle dynamischen Variablen wieder nach plugin_info schreiben
+ # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird,
+ # erlauben wir nur Eintraege mit defined-Werten
+ for my $v (@vars)
+ {
+ if(defined $state->{$v})
+ {
+ $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
+ }
+ else
+ {
+ # wenn die Logik den Wert undef in eine state-Variable schreibt,
+ # wird beim naechsten Aufruf wieder der Startwert aus %logic genommen,
+ delete $plugin_info{$plugname.'_'.$t.'_'.$v};
+ }
+ }
+ }
+
+ return $result;
+}
+
+
# Umgang mit GA-Kurznamen und -Adressen
sub groupaddress
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-07 18:51:21 UTC (rev 794)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-07 20:46:56 UTC (rev 795)
@@ -21,21 +21,43 @@
# translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
# Rueckgabewert spezifiziert.
stair => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, },
- # Damit im Fall transmit != receive der Logikprozessor nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, wird nur dann gesendet,
- # wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
+ # Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der translate-Routine.
+ # Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante
+ # als Rueckgabewert spezifiziert.
- # 5. Memory-Funktion. Wenn ein Write-Telegramm auf die Transmit-Adresse kommt, speichert der Logikprozessor den Wert ab.
- # Das Flag "transmit_on_request" bewirkt, dass nichts gesendet wird, jedoch wird eine Leseanfrage auf der transmit-GA immer
- # mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Damit laesst sich eine Speicherfunktion realisieren.
- # Hier speichert Logikprozessor also den Input ab und sendet den errechneten (=gespeicherten) Wert NUR AUF ANFRAGE.
- memory => { transmit=>'1/2/9', transmit_only_on_request=>1 },
- # Eine receive-Adresse oder translate-Logik werden hier gar nicht gebraucht.
+ # Weitere Bemerkungen:
+ # * translate darf nur entweder eine Konstante oder ausf\xFChrbarer Code (sub {...}) sein.
+ # * Damit im Fall transmit==receive der Translator nicht auf sein eigenes Schreibtelegramm immer wieder antwortet,
+ # wird nur dann gesendet, wenn Ergebnis!=Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
+
+ # Damit im Fall transmit != receive der Logikprozessor nicht auf sein eigenes Schreibtelegramm immer wieder antwortet,
+ # wird nur dann gesendet, wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
- # 6. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
+ # 5. Hier eine Logik, die den Input bei Eintreffen mit 2 multipliziert, das Resultat aber nur speichert und erst
+ # spaeter auf ein explizites Lesetelegramm hin auf der transmit-Adresse sendet.
+ req => { receive=>'1/2/9', transmit=>'1/2/9', transmit_on_request=>1, translate => sub { 2*$input; }, },
+
+ # 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
+ dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 5; }, },
+ # Die Variablen $day_of_week (Mo...So), $day_of_month (01-31), $month (01-12), $year (2012),
+ # $weekend (0 oder 1), $weekday (= nicht $weekend), $time_of_day ("08:34:02"),
+ # $hour ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt)
+ # sind f\xFCr diese Logiken vorbesetzt (bitte nicht darauf schreiben, koennte unverhergesehene Auswirkungen
+ # auf andere Logiken haben).
+
+ # 7. Memory-Funktion. Fuer KNX-Ger\xE4te, die kein Auslesen ihres Statuswerts zulassen (z.B. MDT DaliControl
+ # bei Einzel-EVG-Ansteuerung). Sehr einfach:
+ memory => { transmit=>'1/2/9' },
+ # Hier wird folgende Eigenschaft der Logik ausgenutzt: Wenn ein Write-Telegramm auf die Transmit-Adresse kommt,
+ # speichert der Logikprozessor den Wert immer automatisch ab. Eine Leseanfrage auf der transmit-GA hingegen wird
+ # immer mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Eine receive-Adresse oder translate-Logik
+ # werden hier gar nicht gebraucht.
+
+ # 8. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
# wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { $input->[0] && $input->[1]; }, },
- # 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten.
+ # 9. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten.
# Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
complex => { receive=>'9/5/205',
transmit=>'9/5/206',
@@ -46,7 +68,19 @@
},
# Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
- # 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
+ # 10. Eine Timer-Funktion. Hier eine einfache Zeitschaltuhr, die immer um 8 Uhr und um 10:00 am jeweils zweiten
+ # Dienstag jedes Monats eine 1 auf Transmit sendet
+ wecker => { transmit=>'10/1/15', timer=>{ time=>['08:00','10:00'], day_of_month=>[(8..14)], day_of_week=>'Di' }, translate => 1 },
+ # Logiken mit timer-Klausel weichen in mehreren Punkten von den bisherigen Logiken ab:
+ # * sie ignorieren die delay-Klausel und senden sofort, aber transmit_only_on_request funktioniert
+ # * jeglicher Bustraffic auf receive-Adressen wird ignoriert. (Diese werden aber beim Timer-Aufruf abgefragt,
+ # um das input-Array vorzubesetzen).
+ # Als timer-Eintrag geht entweder ein einzelnes Hash timer=>{...} wie oben oder eine Liste solcher Eintraege
+ # time=>[{...},{...},{...},...]. Jeder Eintrag MUSS eine Spezifikation time=>'XX:XX' enthalten (auch das darf wieder
+ # eine Liste sein) und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
+ # day_of_week, calendar_week enthalten.
+
+ # 11. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
translate => sub { limit(0,$state+20*$input,100); }, },
@@ -54,5 +88,3 @@
# in einen absoluten Wert umgewandelt
);
-
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-08 04:36:10
|
Revision: 797
http://openautomation.svn.sourceforge.net/openautomation/?rev=797&view=rev
Author: pfry
Date: 2012-05-08 04:36:03 +0000 (Tue, 08 May 2012)
Log Message:
-----------
Added Paths:
-----------
wiregate/plugin/generic/Heizungsregler.pl
wiregate/plugin/generic/conf.d/Heizungsregler.conf
Removed Paths:
-------------
wiregate/plugin/generic/Translator.pl
wiregate/plugin/generic/conf.d/Translator.conf
Added: wiregate/plugin/generic/Heizungsregler.pl
===================================================================
--- wiregate/plugin/generic/Heizungsregler.pl (rev 0)
+++ wiregate/plugin/generic/Heizungsregler.pl 2012-05-08 04:36:03 UTC (rev 797)
@@ -0,0 +1,764 @@
+##################
+# Heizungsregler #
+##################
+# Wiregate-Plugin
+# (c) 2012 Fry under the GNU Public License
+
+# $plugin_info{$plugname.'_cycle'}=0; return "deaktiviert";
+
+use POSIX qw(floor);
+use Math::Round qw(nearest);
+
+my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+
+# eibgaconf fixen falls nicht komplett indiziert
+if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
+{
+ for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
+ {
+ $eibgaconf{$ga}{ga}=$ga;
+ my $name=$eibgaconf{$ga}{name};
+ next unless defined $name;
+ $eibgaconf{$name}=$eibgaconf{$ga};
+
+ next unless $name=~/^\s*(\S+)/;
+ my $short=$1;
+ $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
+
+ $eibgaconf{$ga}{short}=$short;
+ $eibgaconf{$short}=$eibgaconf{$ga};
+ }
+}
+
+# Aufrufgrund ermitteln
+my $event=undef;
+if (!$plugin_initflag)
+{ $event='restart'; } # Restart des daemons / Reboot
+#elsif ((stat('/etc/wiregate/plugin/generic/' . $plugname))[9] > time()-2)
+# ab PL30:
+elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
+{ $event='modified'; } # Plugin modifiziert
+elsif (%msg) { $event='bus'; } # Bustraffic
+#elsif ($fh) { $event='socket'; } # Netzwerktraffic
+else { $event='cycle'; } # Zyklus
+
+# Konfigurationsfile einlesen
+my $conf=$plugname; $conf=~s/\.pl$/.conf/;
+$conf="/etc/wiregate/plugin/generic/conf.d/$conf";
+my %house=();
+my $err=read_from_house_config();
+return $err if $err;
+
+# Initialisierung
+my %dyn=();
+my $retval='';
+my $t=time();
+
+if($event=~/restart|modified/)
+{
+ # Cleanup aller Variablen
+ for my $k (grep /^$plugname\_/, keys %plugin_info)
+ {
+ delete $plugin_info{$k};
+ }
+
+ # Alle Controller-GAs abonnieren, Reglerstati initialisieren
+ for my $r (grep ref($house{$_}), keys %house)
+ {
+ $plugin_subscribe{$house{$r}{control}}{$plugname}=1;
+ RESET($r);
+ }
+
+ $plugin_info{$plugname.'_cycle'}=$house{cycle};
+
+ store_to_plugin_info(\%dyn);
+
+ $event='cycle';
+}
+
+%dyn=recall_from_plugin_info();
+
+# Zyklischer Aufruf - Regelung
+if($event=~/cycle/)
+{
+ my $Vreq=undef;
+ my $anynews=0;
+ for my $r (grep ref($house{$_}), keys %house)
+ {
+ if($dyn{$r}{mode} eq 'ON')
+ {
+ # PID-Regler
+ my ($T,$T0,$U,$Vr)=PID($r);
+ $retval.=sprintf "$r\->%.1f(%.1f)%d%% ", $T?$T:"T?", $T0, 100*$U;
+ $anynews=1;
+ $Vreq=$Vr if defined $Vr && (!defined $Vreq || $Vr>$Vreq);
+ }
+ elsif($dyn{$r}{mode} eq 'OPTIMIZE')
+ {
+ # Optimierung der PID-Parameter durch Ermittlung der Sprungantwort
+ $retval.="$r\->".OPTIMIZE($r);
+ $anynews=1;
+ $Vreq=$house{inflow_max} if defined $house{inflow_max};
+ }
+ elsif($dyn{$r}{mode} eq 'OFF')
+ {
+ $retval.="$r\->OFF ";
+ }
+ }
+
+ if(defined $Vreq)
+ {
+ $Vreq=$house{inflow_max} if defined $house{inflow_max} && $Vreq>$house{inflow_max};
+ knx_write($house{inflow_control},$Vreq,9.001) if defined $house{inflow_control};
+ $retval.=sprintf "Vreq=%d", $Vreq;
+ $anynews=1;
+ }
+
+ $retval=~s/\s*$//; # Space am Ende entfernen
+
+ return unless $anynews;
+}
+elsif($event=~/bus/)
+{
+ return if $msg{apci} eq 'A_GroupValue_Response';
+
+ # Aufruf durch GA - neue Wunschtemperatur
+ my $ga=$msg{dst};
+
+ # erstmal den betroffenen Raum finden
+ my @rms=(grep ref($house{$_}) && $house{$_}{control} eq $ga, keys %house);
+ my $r=shift @rms;
+
+ # $r ist undef falls obige Schleife fertig durchlaufen wurde
+ if(defined $r)
+ {
+ my $T0=0;
+ $T0 = $msg{value} if defined $msg{value};
+ my $mode=$dyn{$r}{mode};
+
+ # Jemand moechte einen Sollwert wissen
+ if($msg{apci} eq 'A_GroupValue_Read')
+ {
+ $T0=$dyn{$r}{T0};
+ $T0=$dyn{$r}{T0old} if $dyn{mode} eq 'OPTIMIZE';
+ knx_write($ga,$T0,9.001);
+ return;
+ }
+
+ # spezielle Temperaturwerte sind 0=>OFF und -1=>OPTIMIZE
+ if($T0==0)
+ {
+ RESET($r);
+ writeactuators($r,0);
+ $dyn{$r}{mode}='OFF';
+ $retval.="$r\->OFF";
+ }
+ elsif($T0==-1)
+ {
+ return if $dyn{$r}{mode} eq 'OPTIMIZE'; # Entprellen
+
+ # Initialisierung der Optimierungsfunktion
+ $dyn{$r}{mode}='OPTIMIZE';
+ $dyn{$r}{T0old}=$dyn{$r}{T0};
+ writeactuators($r,0);
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+
+ $retval.=sprintf "$r\->OPT", $T;
+ }
+ else # neue Wunschtemperatur
+ {
+ return if $dyn{$r}{T0} == $T0; # Entprellen
+
+ RESET($r) if $mode eq 'OPTIMIZE'; # Optimierung unterbrochen
+ $dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
+ $dyn{$r}{T0}=$T0;
+ my ($T,$T0,$U,$Vr)=PID($r);
+ $retval.=sprintf "$r\->%.1f(%.1f)%d%%", $T, $T0, 100*$U;
+ }
+ }
+ else
+ {
+ # GA-Abonnement loeschen
+ delete $plugin_subscribe{$ga}{$plugname};
+ }
+}
+
+# Speichere Statusvariablen aller Regler
+store_to_plugin_info(\%dyn);
+
+return $retval eq '' ? undef : $retval;
+
+
+########## Datenpersistenz - Speichern und Einlesen ###############
+
+sub store_to_plugin_info
+{
+ my $dyn=shift;
+
+ # Alle Laufzeitvariablen im Hash %{$dyn}
+ # in das (flache) Hash plugin_info schreiben
+ for my $r (keys %{$dyn})
+ {
+ for my $k (grep /^$plugname\_$r\_(temps|times|Uvals)_/, keys %plugin_info)
+ {
+ delete $plugin_info{$k};
+ }
+
+ for my $v (keys %{$dyn->{$r}})
+ {
+ next if $v=~/^(temps|times|Uvals)$/; # Arrays
+ $plugin_info{$plugname.'_'.$r.'_'.$v}=$dyn->{$r}{$v};
+ }
+
+ for my $array (qw(temps times Uvals))
+ {
+ my $arr=$dyn->{$r}{$array};
+ next unless $arr;
+ for my $i (0..$#{$arr})
+ {
+ $plugin_info{$plugname.'_'.$r.'_'.$array.'_'.$i}=$arr->[$i];
+ }
+ }
+ }
+}
+
+sub recall_from_plugin_info
+{
+ my %dyn=();
+
+ for my $k (grep /^$plugname\_/, keys %plugin_info)
+ {
+ next unless $k=~/^$plugname\_([^_]+)\_(\S+)$/;
+ my $r=$1; my $v=$2;
+
+ unless($v=~/^(temps|times|Uvals)_([0-9]+)$/)
+ {
+ $dyn{$r}{$v}=$plugin_info{$k};
+ }
+ else
+ {
+ my $array=$1; my $i=$2;
+ $dyn{$r}{$array}[$i]=$plugin_info{$k};
+ }
+ }
+
+ return %dyn;
+}
+
+sub read_from_house_config
+{
+ open CONFIG, "<$conf" || return "no config found";
+ my @lines = <CONFIG>;
+ close CONFIG;
+ eval("@lines");
+ return "config error" if $@;
+}
+
+sub store_to_house_config
+{
+ my $r=shift; # der betreffende Raum im Haus
+
+ open CONFIG, ">>$conf";
+ print CONFIG "\$house{$r}{pid}={";
+ for my $k (sort keys %{$house{$r}{pid}})
+ {
+ print CONFIG sprintf "$k=>%.2f, ", $house{$r}{pid}{$k} unless $k eq 'date';
+ print CONFIG "$k=>'$house{$r}{pid}{date}'," if $k eq 'date';
+ }
+ print CONFIG "};\n";
+ close CONFIG;
+}
+
+########## Kommunikation mit Sensoren und Aktoren ###############
+
+sub readsensors
+{
+ my $r=shift; # interessierender Raum
+ my @substructures=();
+
+ push @substructures, values %{$house{$r}->{circ}} if defined $house{$r}->{circ};
+ push @substructures, $house{$r};
+
+ my %T=();
+ my %R=();
+
+ for my $type (qw(sensor inflow floor outflow window))
+ {
+ my $dpt=$type eq 'window' ? 1 : 9;
+
+ # Alle Sensoren eines Typs im gesamten Raum einlesen
+ for my $ss (@substructures)
+ {
+ if(defined $ss->{$type})
+ {
+ my $sensorlist=(ref $ss->{$type})?$ss->{$type}:[$ss->{$type}];
+ for my $s (@{$sensorlist})
+ {
+ unless(defined $T{$type}{$s})
+ {
+ $T{$type}{$s}=knx_read($s,$house{cycle},$dpt);
+ delete $T{$type}{$s} unless defined $T{$type}{$s};
+ }
+ }
+ }
+ }
+
+ # Ueber alle Sensoren mitteln, dabei wird jeder Sensor genau einmal
+ # beruecksichtigt, auch wenn er in der Konfiguration mehrfach steht
+ my $n=0;
+ for my $k (keys %{$T{$type}})
+ {
+ if(defined $T{$type}{$k})
+ {
+ if($type eq 'window')
+ {
+ $R{$type}=1 if int($T{$type}{$k})==1;
+ }
+ else
+ {
+ $R{$type}+=$T{$type}{$k};
+ $n++;
+ }
+ }
+ }
+ $R{$type}/=$n if defined $R{$type} && $type ne 'window';
+ }
+
+ # Falls Fensterkontakte nicht lesbar -> Fenster als geschlossen annehmen
+ $R{window}=0 unless defined $R{window};
+
+ # outflow (Ruecklauf) und floor (Estrich) nehmen wir als gleich an,
+ # falls nicht beide Werte vorhanden sind. Das sollte immer noch besser
+ # sein als der globale Hauswert, um danach den Spread zu berechnen.
+ unless(defined $R{outflow} && defined $R{floor})
+ {
+ $R{outflow}=$R{floor} if defined $R{floor};
+ $R{floor}=$R{outflow} if defined $R{outflow};
+ }
+
+ # Falls Vor- oder Ruecklauf nicht fuer den Raum definiert,
+ # nehmen wir die Hauswerte - falls diese verfuegbar sind
+ for my $type (qw(inflow outflow))
+ {
+ if(!defined $R{$type} && defined $house{$type})
+ {
+ $R{$type} = knx_read($house{$type},$house{cycle},9);
+ delete $R{$type} unless $R{$type};
+ }
+ }
+
+ # Jetzt Spread (Spreizung) berechnen, falls alle Daten verfuegbar
+ if(defined $R{inflow} && defined $R{outflow})
+ {
+ $R{spread}=$R{inflow}-$R{outflow};
+ }
+
+ # und wenn alle Stricke reissen, bleibt der vorkonfigurierte Wert
+ $R{spread}=$house{spread} unless defined $R{spread};
+
+ return @R{qw(sensor inflow floor outflow spread window)};
+}
+
+sub writeactuators
+{
+ my $r=shift; # Raum mit Substruktur
+ my $U=shift; # Ventileinstellung
+
+# plugin_log($plugname, "Trying to write $r, $U");
+
+ my @substructures=values %{$house{$r}->{circ}} if defined $house{$r}->{circ};
+ push @substructures, $house{$r};
+
+ for my $ss (@substructures)
+ {
+ if(defined $ss->{actuator})
+ {
+ unless(ref $ss->{actuator})
+ {
+ knx_write($ss->{actuator},100*$U,5.001); # DPT NOCH UNKLAR ########
+ }
+ else
+ {
+ for my $s (@{$ss->{actuator}})
+ {
+ knx_write($s,100*$U,5.001);
+ }
+ }
+ }
+ }
+# plugin_log($plugname, "Done trying to write $r, $U");
+}
+
+########## PID-Regler #####################
+
+sub RESET
+{
+ my $r=shift; # zu regelnder Raum im Haus
+
+ $dyn{$r} = {
+ mode=>'OFF', T0=>20, Told=>0, told=>$t, IS=>0, DF=>0,
+ temps=>[], times=>[], Uvals=>[], U=>0
+ };
+}
+
+sub PID
+{
+ my $r=shift; # zu regelnder Raum im Haus
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+
+ # Ohne Temperaturmessung und Spread keine Regelung -> aufgeben
+ my ($mode,$T0,$Told,$told,$IS,$DF,$temps,$times,$Uvals,$U)
+ = @{$dyn{$r}}{qw(mode T0 Told told IS DF temps times Uvals U)};
+
+ return ($T,$T0,$U,0) unless $T && $spread;
+
+ # Regelparameter einlesen
+ my ($Tv,$Tn,$lim,$prop,$refspread)=(5,30,1,1,10); # Defaults
+
+ ($Tv,$Tn,$lim,$prop,$refspread)
+ =@{$house{$r}{pid}}{qw(Tv Tn lim prop refspread)}
+ if defined $house{$r}{pid};
+
+ $Tv*=60; $Tn*=60; # in Sekunden umrechnen
+
+ # Anzahl Datenpunkte fuer Steigungsberechnung
+ my $S1=12; $S1=$house{mindata} if defined $house{mindata};
+
+ # Anzahl Datenpunkte fuer Ermittlung neuer Vorhaltetemperatur
+ my $S2=$S1;
+
+ push @{$temps},$T; shift @{$temps} while @{$temps}>$S1;
+ push @{$times},$t; shift @{$times} while @{$times}>$S1;
+
+ if($window)
+ {
+ $U=0; # Heizung aus falls Fenster offen
+ push @{$Uvals}, $U; shift @{$Uvals} while @{$Uvals}>$S2;
+
+ $dyn{$r} = {
+ mode=>$mode, T0=>$T0, Told=>$T, told=>$t, IS=>$IS, DF=>$DF,
+ temps=>$temps, times=>$times, Uvals=>$Uvals, U=>$U
+ };
+
+ writeactuators($r,$U);
+ return ($T,$T0,$U,0);
+ }
+
+ # Skalierung fuer aktuellen Spread
+ my $coeff = $refspread/($spread*$prop);
+
+ # Proportionalteil (P)
+ my $P = $T0 - $T;
+
+ # Integralteil (I)
+ $IS += $P * ($t - $told) / $Tn;
+
+ # kein negativer I-Anteil bei reiner Heizung (nur fuer Klimaanlage erforderlich)
+ $IS=0 if $IS<0;
+
+ # Begrenzung des I-Anteils zur Vermeidung von Ueberschwingern ("wind-up")
+ $IS=+$lim/$coeff if $IS>+$lim/$coeff;
+
+ # Differentialteil (D) - gemittelt wegen moeglichem Sensorrauschen
+ $S1=scalar(@{$times});
+ if($S1>=2)
+ {
+ my ($SX,$SX2,$SY,$SXY)=(0,0,0,0);
+ for my $i (0..$S1-1)
+ {
+ my $time=$times->[$i]-$times->[0];
+ $SX+=$time;
+ $SX2+=$time*$time;
+ $SY+=$temps->[$i];
+ $SXY+=$time*$temps->[$i];
+ }
+ $DF = - $Tv * ($S1*$SXY - $SX*$SY)/($S1*$SX2 - $SX*$SX);
+ }
+# Fuer den Fall S1==2 fuehrt die obige Regression zum gleichen Ergebnis wie:
+# $DF = - $Tv * ($T - $Told) / ($t - $told);
+
+ # und alles zusammen, skaliert mit der aktuellen Spreizung
+ $U = ($P + $IS + $DF) * $coeff;
+
+ # Stellwert begrenzen auf 0-1
+ $U=1 if $U>1;
+ $U=0 if $U<0;
+ push @{$Uvals}, $U; shift @{$Uvals} while @{$Uvals}>$S2;
+
+ # Wunsch-Vorlauftemperatur ermitteln
+ my $Vr=$V;
+ if(defined $Vr)
+ {
+ $Vr=$T0+3 if $Vr<$T0+3;
+ my $Uavg=0; $Uavg+=$_ foreach (@{$Uvals}); $Uavg/=scalar(@{$Uvals});
+
+ $Vr+=1 if $Uavg>0.9;
+ $Vr-=1 if $Uavg<0.6 && $V>$T0+6 && $spread>6;
+ $Vr-=1 if $Uavg<0.75 && $V>$T0+5 && $spread>5;
+ $Vr-=1 if $Uavg<0.7 && $V>$T0+4 && $spread>4;
+ $Vr-=1 if $Uavg<0.6 && $V>$T0+3 && $spread>3;
+ }
+
+ # Variablen zurueckschreiben
+ $dyn{$r} = {
+ mode=>$mode, T0=>$T0, Told=>$T, told=>$t, IS=>$IS, DF=>$DF,
+ temps=>$temps, times=>$times, Uvals=>$Uvals, U=>$U
+ };
+
+ # Ventil einstellen
+ writeactuators($r,$U);
+
+ # Ist, Soll, Stellwert, Spread, Wunsch-Vorlauftemp.
+ return ($T,$T0,$U,$Vr);
+}
+
+########## Optimierungsroutine #####################
+
+sub OPTIMIZE
+{
+ my $r=shift;
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+
+ # Ohne Temperaturmessung und Spread keine Regelung -> aufgeben
+ return "(OPT) " unless defined $T && defined $spread;
+
+ # Praktische Abkuerzungen fuer Statusvariablen
+ my ($mode,$phase,$T0old) = @{$dyn{$r}}{qw(mode phase T0old)};
+
+ # Falls Fenster offen -> Abbruch, Heizung aus und Regler resetten
+ if($window)
+ {
+ if($phase ne 'COOL')
+ {
+ RESET($r);
+ $dyn{$r}{mode}='ON';
+ $dyn{$r}{T0}=$T0old;
+ return "FAILED:WINDOW ";
+ }
+ else
+ {
+ # Tn, Tv, prop und refspread wurden am Ende der HEAT-Periode bereits berechnet
+ # Wir nutzen die "cooling"-Periode sowieso nicht.
+ # Also Parameter ins Konfig-File schreiben.
+ my ($Tn, $Tv, $prop, $refspread) = @{$dyn{$r}}{qw(Tn Tv prop refspread)};
+ my $date=`/bin/date +"%F %X"`; chomp $date;
+ my $lim=0.5;
+ $house{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
+ store_to_house_config($r);
+ }
+ }
+
+ # Warte bis Therme voll aufgeheizt
+ # das Aufheizen der Therme geschieht in der Hauptschleife
+ $phase='WAIT' unless defined $phase;
+
+ if($phase eq 'WAIT')
+ {
+ if(defined $V && defined $house{inflow_max} && $V<$house{inflow_max}-3)
+ {
+ writeactuators($r,0); # noch nicht heizen
+ return "WAIT(V=$V) ";
+ }
+
+ # Falls Heizung noch nicht voll an, jetzt starten
+ writeactuators($r,1); # maximal heizen
+
+ # Temperaturaufzeichnung beginnen
+ $dyn{$r} = {
+ mode=>$mode, phase=>'HEAT',
+ T0old=>$T0old, told=>0, optstart=>$t,
+ maxpos=>0, maxslope=>0,
+ sumspread=>$spread, temps=>[0], times=>[$T]
+ };
+
+ return sprintf("%.1f(HEAT)%.1f ",$T,$spread);
+ }
+
+ my ($optstart, $sumspread, $told, $temps, $times)
+ = @{$dyn{$r}}{qw(optstart sumspread told temps times)};
+
+ my $tp=$t-$optstart;
+
+ # falls aus irgendeinem Grund zu frueh aufgerufen, tu nichts
+ return sprintf("%.1f(", $T).'SKP'.sprintf(")%.1f ",$spread)
+ if $tp-$told<$house{cycle}/2;
+
+ # Temperaturkurve aufzeichnen
+ push @{$times}, $tp;
+ push @{$temps}, $T;
+ $sumspread+=$spread;
+
+ # Anzahl Datenpunkte fuer Steigungsberechnung. Hier verdoppelt, weil wir
+ # mehr Praezision brauchen.
+ my $S1=25; $S1=2*$house{mindata} if defined $house{mindata};
+
+ if(scalar(@{$temps})<=$S1)
+ {
+ $dyn{$r} = {
+ mode=>$mode, phase=>$phase,
+ T0old=>$T0old, told=>$tp, optstart=>$optstart,
+ maxpos=>0, maxslope=>0,
+ sumspread=>$sumspread, temps=>$temps, times=>$times
+ };
+
+ return sprintf("%.1f(", $T).'OPT'.sprintf(")%.1f ",$spread);
+ }
+
+ # Steigung der Temperaturkurve durch Regression bestimmen
+ my ($SX,$SY,$SY2,$SXY)=(0,0,0,0);
+
+ for my $i (-$S1..-1)
+ {
+ $SX+=$temps->[$i];
+ $SY+=$times->[$i];
+ $SY2+=$times->[$i]*$times->[$i];
+ $SXY+=$times->[$i]*$temps->[$i];
+ }
+
+ my $slope = ($S1*$SXY - $SX*$SY)/($S1*$SY2 - $SY*$SY);
+
+ if($phase eq 'HEAT')
+ {
+ my ($maxpos, $maxslope) = @{$dyn{$r}}{qw(maxpos maxslope)};
+
+ if($slope<=0 || $maxslope<=0 || $slope>=0.7*$maxslope)
+ {
+ my $retval='';
+
+ if($slope>$maxslope)
+ {
+ $maxslope = $slope;
+ $maxpos = nearest(1,$#{$temps}-$S1/2);
+ $retval=sprintf "%.2fKph",$slope*60*60;
+ }
+ elsif($slope>0)
+ {
+ $retval=sprintf "%.2fKph=%d%%", $slope*60*60, 100*$slope/$maxslope;
+ }
+ else
+ {
+ $retval=sprintf "%.2fKph",$slope*60*60;
+ }
+
+ # Statusvariablen zurueckschreiben
+ $dyn{$r} = {
+ mode=>$mode, phase=>'HEAT',
+ T0old=>$T0old, told=>$tp, optstart=>$optstart,
+ maxpos=>$maxpos, maxslope=>$maxslope,
+ sumspread=>$sumspread, temps=>$temps, times=>$times
+ };
+
+ return sprintf("%.1f(", $T).$retval.sprintf(")%.1f ",$spread);
+ }
+
+ # Erwaermung deutlich verlangsamt -> Optimierung berechnen
+ # Abschaetzung des finalen Plateauniveaus durch Annahme
+ # exponentieller Thermalisierung
+
+ # Position maximaler Steigung
+ my $pos1 = nearest(1,$maxpos-$S1/2);
+ my $t1 = $times->[$maxpos];
+
+ # Endpunkt
+ my $t3 = $times->[nearest(1,-1-$S1/2)];
+
+ # Punkt in der Mitte zwischen max. Steigung und Endpunkt
+ my $pos2 = undef;
+ for my $p ($maxpos..$#{$times})
+ {
+ if($times->[$p]>=($t1+$t3)/2) { $pos2=$p; last; }
+ }
+ unless(defined $pos2)
+ {
+ RESET($r);
+ $dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
+ $dyn{$r}{T0}=$T0old;
+ return "FAILED:POS2";
+ }
+ $pos2 = nearest(1,$pos2-$S1/2);
+
+ # Temperaturen an den Punkten t=0, maxtime, (maxtime+t)/2, t
+ # gemittelt ueber S1 Werte
+ my ($X0,$X1,$X2,$X3)=(0,0,0,0);
+ for my $i (0..($S1-1))
+ {
+ $X0+=$temps->[$i];
+ $X1+=$temps->[$i+$pos1];
+ $X2+=$temps->[$i+$pos2];
+ $X3+=$temps->[-$i-1];
+ }
+ $X0/=$S1; $X1/=$S1; $X2/=$S1; $X3/=$S1;
+
+ # Berechnung des Plateauwertes bei exponentieller Thermalisierung
+ my $Xplateau=($X1*$X3 - $X2*$X2)/($X1 - 2*$X2 + $X3);
+
+ # Analyse der Sprungantwort
+ my $refspread = $sumspread/scalar(@{$times});
+ my $DX = $Xplateau - $X0;
+ my $Ks = $DX/$refspread;
+ my $Tu = $t1 - 2*($tp-$told) - ($X1-$X0)/$maxslope;
+ my $Tg = $DX/$maxslope;
+
+ # Optimierung der PID-Parameter nach Chien/Hrones/Reswick
+ # (siehe zB Wikipedia). Wir nehmen aber etwas andere Koeffizienten,
+ # das fuehrt zu ruhigerem Regelverhalten...
+
+ # Proportionalbereich prop=1/Kp, kleineres prop ist aggressiver
+ my $prop = $maxslope*$Tu/(0.3*$refspread);
+
+ # Nachstellzeit des Integralteils, kleiner ist aggressiver
+ my $Tn = $Tg/60;
+
+ # Vorhaltezeit des Differentialteils, groesser ist aggressiver
+ my $Tv = $Tu/60;
+
+ # alle drei Parameter muessen positiv sein, sonst Fehler
+ unless($prop>=0 && $Tn>=0 && $Tv>=0)
+ {
+ RESET($r);
+ $dyn{$r}{mode}='ON';
+ $dyn{$r}{T0}=$T0old;
+ $dyn{$r}{Told}=$T;
+
+ return "FAILED:NEG";
+ }
+
+ # Statusvariablen zurueckschreiben
+ $dyn{$r} = {
+ mode=>$mode, phase=>'COOL',
+ T0old=>$T0old, told=>$tp, optstart=>$optstart,
+ Tn=>$Tn, Tv=>$Tv, prop=>$prop, refspread=>$refspread, tcool=>$t3,
+ sumspread=>$sumspread, temps=>$temps, times=>$times
+ };
+
+ # Abkuehlung einleiten
+ writeactuators($r, 0);
+
+ return sprintf("%.1f(COOL) ",$T);
+ }
+
+ if($phase eq 'COOL' && $slope>0)
+ {
+ return sprintf("%.1f(%.2fKph) ",$T,$slope*60*60);
+ }
+
+ # Abspeichern der optimierten Parameter im Konfigurationsfile
+ # aus der Laenge der "cooling"-Periode bis zum Maximum koennte man noch was berechnen,
+ # aber wir setzen $lim hier als Konstante
+ my ($Tn, $Tv, $prop, $refspread, $tcool)
+ = @{$dyn{$r}}{qw(Tn Tv prop refspread tcool)};
+ my $date=`/bin/date +"%F %X"`; chomp $date;
+ my $lim=0.5;
+ $house{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
+ store_to_house_config($r);
+
+ # Regelung starten
+ RESET($r);
+ $dyn{$r}{mode}='ON';
+ $dyn{$r}{T0}=$T0old;
+ $dyn{$r}{Told}=$T;
+
+ # Info an den User
+ return sprintf "t=%dh:%02dmin Tv=%.1fmin Tn=%dmin lim=%.1f prop=%.1f spread=%.1f ", $tp/3600,($tp/60)%60,$Tv,$Tn,$lim,$prop,$refspread;
+}
+
Deleted: wiregate/plugin/generic/Translator.pl
===================================================================
--- wiregate/plugin/generic/Translator.pl 2012-05-07 21:12:24 UTC (rev 796)
+++ wiregate/plugin/generic/Translator.pl 2012-05-08 04:36:03 UTC (rev 797)
@@ -1,218 +0,0 @@
-#!/usr/bin/perl -w
-##############
-# Translator #
-##############
-# Wiregate-Plugin
-# (c) 2012 Fry under the GNU Public License
-
-#$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
-
-my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
-
-# eibgaconf fixen falls nicht komplett indiziert
-if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
-{
- for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
- {
- $eibgaconf{$ga}{ga}=$ga;
- my $name=$eibgaconf{$ga}{name};
- next unless defined $name;
- $eibgaconf{$name}=$eibgaconf{$ga};
-
- next unless $name=~/^\s*(\S+)/;
- my $short=$1;
- $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
-
- $eibgaconf{$ga}{short}=$short;
- $eibgaconf{$short}=$eibgaconf{$ga};
- }
-}
-
-sub limit { my ($lo,$x,$hi)=@_; return $x<$lo?$lo:($x>$hi?$hi:$x); }
-
-# Konfigurationsfile einlesen
-my %trans=();
-my $conf=$plugname; $conf=~s/\.pl$/.conf/;
-open FILE, "</etc/wiregate/plugin/generic/conf.d/$conf" || return "no config found";
-my @lines = <FILE>;
-close FILE;
-eval("@lines");
-return "config error: $@" if $@;
-
-# Aufrufgrund ermitteln
-my $event=undef;
-if (!$plugin_initflag)
-{ $event='restart'; } # Restart des daemons / Reboot
-elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
-{ $event='modified'; } # Plugin modifiziert
-elsif (%msg) { $event='bus'; } # Bustraffic
-elsif ($fh) { $event='socket'; } # Netzwerktraffic
-else { $event='cycle'; } # Zyklus
-
-# Plugin-Code
-if($event=~/restart|modified/)
-{
- # alle Variablen loeschen und neu initialisieren, alle GAs abonnieren
- for my $k (grep /^$plugname\_/, keys %plugin_info)
- {
- delete $plugin_info{$k};
- }
-
- my $count=0;
- my $rxtx_lookup='';
-
- for my $t (sort keys %trans)
- {
- my $receive=$trans{$t}{receive};
- my $transmit=$trans{$t}{transmit};
-
- $receive=$eibgaconf{$receive}{ga} if $receive!~/^[0-9\/]+$/ && defined $eibgaconf{$receive};
- $transmit=$eibgaconf{$transmit}{ga} if $transmit!~/^[0-9\/]+$/ && defined $eibgaconf{$transmit};
-
- next unless defined $receive && defined $transmit;
-
- $rxtx_lookup.="Rx($receive)=>'$t', Tx($transmit)=>'$t', ";
-
- $plugin_subscribe{$receive}{$plugname}=1;
- $plugin_subscribe{$transmit}{$plugname}=1;
-
- $count++;
-
- delete $plugin_info{$plugname.'_'.$t.'_result'};
-
- if(ref $trans{$t}{state})
- {
- for my $v (keys %{$trans{$t}{state}})
- {
- $plugin_info{$plugname.'_'.$t.'_'.$v}=$trans{$t}{state}{$v};
- }
- }
- elsif(defined $trans{$t}{state})
- {
- $plugin_info{$plugname.'_'.$t.'_result'}=$trans{$t}{state};
- }
- }
-
- $plugin_info{$plugname.'__RxTxLookup'}=$rxtx_lookup;
- $plugin_info{$plugname.'_cycle'}=0;
-
- return $count." initialisiert";
-}
-
-# Bustraffic bedienen - nur Schreibzugriffe der iButtons interessieren
-if($event=~/bus/)
-{
- return if $msg{apci} eq "A_GroupValue_Response";
-
- my $ga=$msg{dst};
-
- unless($plugin_info{$plugname.'__RxTxLookup'}=~/(Tx|Rx)\($ga\)=>\'(.+?)\',/)
- {
- delete $plugin_subscribe{$ga}{$plugname}; # unbekannte GA
- return;
- }
-
- my $cmd=$1; chop $cmd;
- my $t=$2;
-
- if($msg{apci} eq "A_GroupValue_Read")
- {
- # Ein Read-Request auf einer Transmit-GA wird mit dem letzten Ergebnis beantwortet
- if($cmd eq 'T')
- {
- my $transmit=$ga;
- $transmit=$eibgaconf{$ga}{short} if $use_short_names;
- my $result=$plugin_info{$plugname.'_'.$t.'_result'};
- knx_write($ga, $result);
- return "Memory: $result ($transmit)";
- }
- return;
- }
- elsif($msg{apci} eq "A_GroupValue_Write")
- {
- my $input=$msg{value};
-
- # Write-Telegramm auf unserer Transmit-Adresse?
- # Vorsicht! - das koennten wir selbst gewesen sein, also nicht antworten!
- if($cmd eq 'T')
- {
- if(defined $input)
- {
- $plugin_info{$plugname.'_'.$t.'_result'}=$input; # einfach Input ablegen
- }
- else
- {
- delete $plugin_info{$plugname.'_'.$t.'_result'};
- }
- return;
- }
-
- my $result=undef;
-
- # Ein Write-Request auf einer Receive-GA wird uebersetzt und das Resultat auf der Transmit-GA uebertragen
- if(ref $trans{$t}{state})
- {
- # Komplexer state-Hash: Basis sind die Werte im Configfile
- my $state=$trans{$t}{state};
-
- # Nun die dynamischen Variablen aus plugin_info hinzufuegen
- for my $k (keys %plugin_info)
- {
- next unless $k=~/^$plugname\_$t\_(.*)$/;
- my $v=$1;
- $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v};
- }
-
- # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
- $result=$trans{$t}{translate}($state,$input);
-
- # Alle dynamischen Variablen wieder nach plugin_info schreiben
- # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird,
- # erlauben wir nur vorhandene Eintraege
- for my $v (keys %{$state})
- {
- if(defined $state->{$v})
- {
- $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
- }
- else
- {
- delete $plugin_info{$plugname.'_'.$t.'_'.$v};
- }
- }
- }
- else # Einfacher Fall - skalare state-Variable
- {
- # Einfache state-Skalar: Ergebnis des letzten Aufrufs
- my $state=$plugin_info{$plugname.'_'.$t.'_result'};
-
- # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
- $result=$trans{$t}{translate}($state,$input);
- }
-
- # Ergebnis des letzten Aufrufs zurueckschreiben
- if(defined $result)
- {
- $plugin_info{$plugname.'_'.$t.'_result'}=$result;
- }
- else
- {
- delete $plugin_info{$plugname.'_'.$t.'_result'};
- }
-
- # Uebersetzung auf Bus schreiben, ausser im Sonderfall receive==transmit, dann nur auf Anfrage senden
- my $receive=$trans{$t}{receive};
- my $transmit=$trans{$t}{transmit};
-
- $receive=$eibgaconf{$receive}{ga} if $receive!~/^[0-9\/]+$/ && defined $eibgaconf{$receive};
- $transmit=$eibgaconf{$transmit}{ga} if $transmit!~/^[0-9\/]+$/ && defined $eibgaconf{$transmit};
-
- unless($transmit eq $receive)
- {
- knx_write($transmit, $result);
- return "$input ($receive) -> $result ($transmit)";
- }
- }
-}
-
-return;
Added: wiregate/plugin/generic/conf.d/Heizungsregler.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Heizungsregler.conf (rev 0)
+++ wiregate/plugin/generic/conf.d/Heizungsregler.conf 2012-05-08 04:36:03 UTC (rev 797)
@@ -0,0 +1,114 @@
+#!/usr/bin/perl
+#
+# Konfiguration Heizungsregler ##########################################
+#
+# Raeume, Messfuehler und Heizkreislaeufe definieren
+# Jeder Raum hat einen eigenen Regler. Die Bezeichnungen der Raeume sind
+# beliebig, sie duerfen aber keinen Underscore ("_") enthalten.
+#
+# Ueber mehrere Sensoren eines Raumes inklusive aller seiner Heizkreislaeufe
+# wird gemittelt (egal wie oft eine GA in der Struktur auftaucht, sie wird
+# nur einfach gewichtet).
+
+# Falls kein Sensor im Raum definiert ist oder antwortet, wird der Haussensor
+# genommen.
+#
+# Falls inflow und outflow weder im Raum noch zentral im Haus definiert
+# oder verfuegbar sind, wird der global definierte fixe Spread angenommen.
+# (Spread = Vorlauf- minus Ruecklauftemperatur = inflow-outflow). Fehlt auch
+# dieser, so faellt die Regelung aus, sobald der Spread mit den vorhandenen
+# Werten nicht ermittelt werden kann.
+#
+# Eine komplette, minimale Konfiguration fuer zwei Zimmer sieht daher aus:
+#
+#%house=(
+# cycle=>60,
+# spread=>20,
+# Zimmer1=>{control=>'1/2/4', sensor=>'1/2/3'},
+# Zimmer2=>{control=>'1/2/14', sensor=>'1/2/13'},
+# );
+#
+# Hierbei gibt es genau eine Wunschtemperatur und einen Messfuehler pro Raum.
+# Der Spread ist dabei fix mit 20K angenommen.
+
+%house=(
+
+# Taktung des Reglers, Anzahl Sekunden zwischen Regleraufrufen
+# Es macht durchaus Sinn, den Regler haeufiger laufen zu lassen als die
+# Sensoren ausgelesen werden. Das sollte zu einem ruhigeren Regler fuehren.
+ cycle => 60,
+
+# Anzahl der Datenpunkte fuer Trendberechnung
+ mindata => 15,
+
+# Falls die Heizung ueber eine einstellbare Vorlauftemperatur verfuegt,
+# so kann hier eine GA fuer die Wunsch-Vorlauftemperatur festgelegt werden,
+# sowie eine maximale Vorlauftemperatur. Vorsicht! Bitte nur benutzen, wenn
+# die tatsaechliche Vorlauftemperatur auch gemessen werden kann!
+
+# inflow_control => '1/2/3', # GA zum Setzen der Soll-Vorlauftemperatur
+ inflow_max => 40, # maximale Soll-Vorlauftemperatur
+
+# Der Spread, oder die Spreizung, ist die Differenz zwischen Vo...
[truncated message content] |
|
From: <pf...@us...> - 2012-05-10 10:15:36
|
Revision: 805
http://openautomation.svn.sourceforge.net/openautomation/?rev=805&view=rev
Author: pfry
Date: 2012-05-10 10:15:25 +0000 (Thu, 10 May 2012)
Log Message:
-----------
eibd_backend_address konfigurierbar gemacht, zur Vermeidung von Zirkellogiken
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 08:54:24 UTC (rev 804)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 10:15:25 UTC (rev 805)
@@ -48,6 +48,7 @@
my $systemtime=time();
# Konfigurationsfile einlesen
+my $eibd_backend_address='1.1.254';
my %logic=();
my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname";
$conf.='.conf' unless $conf=~s/\.pl$/.conf/;
@@ -90,11 +91,11 @@
for my $t (keys %logic)
{
+ next if $t eq 'debug';
+
# Debuggingflag gesetzt
my $debug = $logic{debug} || $logic{$t}{debug};
- next if $t eq 'debug';
-
# Eintrag pruefen
if(defined $logic{$t}{receive} && ref $logic{$t}{receive} && ref $logic{$t}{receive} ne 'ARRAY')
{
@@ -170,6 +171,8 @@
# welche translate-Logik ist aufgerufen?
for my $t (keys %logic)
{
+ next if $t eq 'debug';
+
my $transmit=groupaddress($logic{$t}{transmit});
my $transmit_ga = ($ga eq $transmit);
@@ -230,7 +233,7 @@
# (Logik antwortet auf sich selbst in einer Endlosschleife)
# war Wiregate der Sender des Telegramms?
- my $sender_is_wiregate=int($msg{src})==0;
+ my $sender_is_wiregate = $msg{src} eq $eibd_backend_address;
# Aufruf der Logik-Engine
my $result=execute_logic($t, $receive, $ga, $in);
@@ -263,12 +266,13 @@
if($logic{$t}{delay})
{
$plugin_info{$plugname.'__'.$t.'_timer'}=$systemtime+$logic{$t}{delay};
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s");
+ plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s")
+ if $debug;
}
else
{
knx_write($transmit, $result);
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet") if $debug;
+ plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet") if $debug;
}
}
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-10 08:54:24 UTC (rev 804)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-10 10:15:25 UTC (rev 805)
@@ -3,6 +3,8 @@
# Logikprozessor.pl - Konfiguration
#
+$eibd_backend_address='1.1.254'; # eigene Adresse zur Vermeidung von Zirkellogiken
+
%logic=(
# 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { 2*$input; }, },
@@ -38,7 +40,7 @@
req => { receive=>'1/2/9', transmit=>'1/2/9', transmit_on_request=>1, translate => sub { 2*$input; }, },
# 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
- dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 5; }, },
+ dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 3; }, },
# Die Variablen $day_of_week (Mo...So), $day_of_month (01-31), $month (01-12), $year (2012),
# $weekend (0 oder 1), $weekday (= nicht $weekend), $time_of_day ("08:34:02"),
# $hour ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt)
@@ -88,3 +90,73 @@
# in einen absoluten Wert umgewandelt
);
+
+# In meinem (Fry) speziellen GA-Schema kann ich mir die obige Einzelkonfiguration sparen, weil aus meinem
+# GA-Namensschema mit sprechenden Kuerzeln alle Zuweisungen folgen; das Konfigurationshash wird in folgender
+# Schleife automatisch erzeugt.
+%logic=(
+ wecker => { debug=>1, transmit=>'LI_SZ_Decke_1', timer=>{ time=>'22:23', day_of_month=>[(7..14)], day_of_week=>'Mi' }, translate => 1 },
+ wecker2 => { debug=>1, transmit=>'LI_SZ_Decke_1', timer=>{ time=>'22:24', day_of_month=>[(7..14)], day_of_week=>'Mi' }, translate => 0 },
+ lichtaus => { debug=>1, receive=>'LD_SZ_Decke_1', transmit=>'LD_SZ_Decke_1', translate => 0 },
+# temperatur => { receive=>['KR_E_Fermax_Oeffner','TA_A2'], transmit=>'WA_Temperatur',
+# translate => sub { return if $input->[0]; $input->[1]; },
+# },
+);
+
+# Memory-Funktion f\xFCr alle Aktoren, die kein KO fuer die Abfrage des eingestellten Wertes anbieten
+for my $rec (grep /^(LD|JP|JW)_\S*$/, keys %eibgaconf)
+{
+ $logic{$rec.'_mem'}={ transmit=>$rec, transmit_only_on_request=>1 };
+}
+
+# Wenn unter 3% gedimmt wird -> ausschalten
+for my $rec (grep /^LD_\S*$/, keys %eibgaconf)
+{
+ my $trm=$rec; $trm=~s/^LD/LI/;
+ $logic{$rec.'_off'}={ receive=>$rec, transmit=>$trm, translate => sub { $input<3 ? 0 : undef; } };
+}
+
+# Szenenbestaetigung: LEDs der Taster blinken nach Szenenspeicherung, diese 1s spaeter wieder ausschalten
+for my $rec (grep /^ZC_\S*$/, keys %eibgaconf)
+{
+ $logic{$rec.'_led_off'}={ debug=>1, receive=>$rec, transmit=>$rec, delay=>1, translate=>0 };
+}
+
+# nachts gedaempftes Licht. Auf einen Einschaltbefehl bei Nacht direkt einen Dimmwert hinterherschicken
+for my $rec (grep /^LI_\S*$/, keys %eibgaconf)
+{
+ my $trm=$rec; $trm=~s/^LI/LK/;
+ $logic{$rec.'_dim'}={ receive=>$rec, transmit=>$trm,
+ translate => sub { return undef unless $input;
+ return 3 if $night;
+ return 50; }
+ };
+}
+
+# Umwandlung relativer Dimmwerte in absolute Dimmwerte
+#for my $rec (grep /^LR_\S*$/, keys %eibgaconf)
+#{
+# next unless $eibgaconf{$rec}{name}=~/Dali/;
+# $rec=$eibgaconf{$rec}{short};
+# my $trm=$rec; $trm=~s/^LR/LK/;
+#
+# $logic{$rec}={
+# receive=>$rec, transmit=>$trm,
+# translate => sub { my ($state,$input)=@_; limit(0,(1+0.2*$input)*$state+5*$input,100); }
+# };
+#}
+
+# Umwandlung von Schaltbefehlen in absolute Dimmwerte
+#for my $rec (grep /^LI_\S*$/, keys %eibgaconf)
+#{
+# next unless $eibgaconf{$rec}{name}=~/Dali/;
+# $rec=$eibgaconf{$rec}{short};
+# my $trm=$rec; $trm=~s/^LI/LK/;
+#
+# $logic{$rec}={
+# receive=>$rec, transmit=>$trm,
+# translate => sub { my ($state,$input)=@_; 100*(1-$input); }
+# };
+#}
+
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-10 20:01:17
|
Revision: 807
http://openautomation.svn.sourceforge.net/openautomation/?rev=807&view=rev
Author: pfry
Date: 2012-05-10 20:01:11 +0000 (Thu, 10 May 2012)
Log Message:
-----------
Nochmal Zirkelschluesse.
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 16:20:49 UTC (rev 806)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-10 20:01:11 UTC (rev 807)
@@ -239,14 +239,7 @@
my $result=execute_logic($t, $receive, $ga, $in);
# Zirkelaufruf ausschliessen
- if($sender_is_wiregate && $in == $result)
- {
- # kommt transmit-GA unter den receive-GAs vor?
- # Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
- next if $transmit_ga;
- next if !ref $receive && ($transmit eq $receive);
- next if ref $receive && grep /^$transmit$/, @{$receive};
- }
+ next if $sender_is_wiregate && $transmit_ga && $in == $result;
# In bestimmten Sonderfaellen nichts schicken
unless(defined $result) # Resultat undef => nichts senden
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-10 16:20:49 UTC (rev 806)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-10 20:01:11 UTC (rev 807)
@@ -93,72 +93,4 @@
);
-# In meinem (Fry) speziellen GA-Schema kann ich mir die obige Einzelkonfiguration sparen, weil aus meinem
-# GA-Namensschema mit sprechenden Kuerzeln alle Zuweisungen folgen; das Konfigurationshash wird in folgender
-# Schleife automatisch erzeugt.
-%logic=(
- wecker => { debug=>1, transmit=>'LI_SZ_Decke_1', timer=>{ time=>'22:23', day_of_month=>[(7..14)], day_of_week=>'Mi' }, translate => 1 },
- wecker2 => { debug=>1, transmit=>'LI_SZ_Decke_1', timer=>{ time=>'22:24', day_of_month=>[(7..14)], day_of_week=>'Mi' }, translate => 0 },
- lichtaus => { debug=>1, receive=>'LD_SZ_Decke_1', transmit=>'LD_SZ_Decke_1', translate => 0 },
-# temperatur => { receive=>['KR_E_Fermax_Oeffner','TA_A2'], transmit=>'WA_Temperatur',
-# translate => sub { return if $input->[0]; $input->[1]; },
-# },
-);
-# Memory-Funktion f\xFCr alle Aktoren, die kein KO fuer die Abfrage des eingestellten Wertes anbieten
-for my $rec (grep /^(LD|JP|JW)_\S*$/, keys %eibgaconf)
-{
- $logic{$rec.'_mem'}={ transmit=>$rec, transmit_only_on_request=>1 };
-}
-
-# Wenn unter 3% gedimmt wird -> ausschalten
-for my $rec (grep /^LD_\S*$/, keys %eibgaconf)
-{
- my $trm=$rec; $trm=~s/^LD/LI/;
- $logic{$rec.'_off'}={ receive=>$rec, transmit=>$trm, translate => sub { $input<3 ? 0 : undef; } };
-}
-
-# Szenenbestaetigung: LEDs der Taster blinken nach Szenenspeicherung, diese 1s spaeter wieder ausschalten
-for my $rec (grep /^ZC_\S*$/, keys %eibgaconf)
-{
- $logic{$rec.'_led_off'}={ debug=>1, receive=>$rec, transmit=>$rec, delay=>1, translate=>0 };
-}
-
-# nachts gedaempftes Licht. Auf einen Einschaltbefehl bei Nacht direkt einen Dimmwert hinterherschicken
-for my $rec (grep /^LI_\S*$/, keys %eibgaconf)
-{
- my $trm=$rec; $trm=~s/^LI/LK/;
- $logic{$rec.'_dim'}={ receive=>$rec, transmit=>$trm,
- translate => sub { return undef unless $input;
- return 3 if $night;
- return 50; }
- };
-}
-
-# Umwandlung relativer Dimmwerte in absolute Dimmwerte
-#for my $rec (grep /^LR_\S*$/, keys %eibgaconf)
-#{
-# next unless $eibgaconf{$rec}{name}=~/Dali/;
-# $rec=$eibgaconf{$rec}{short};
-# my $trm=$rec; $trm=~s/^LR/LK/;
-#
-# $logic{$rec}={
-# receive=>$rec, transmit=>$trm,
-# translate => sub { my ($state,$input)=@_; limit(0,(1+0.2*$input)*$state+5*$input,100); }
-# };
-#}
-
-# Umwandlung von Schaltbefehlen in absolute Dimmwerte
-#for my $rec (grep /^LI_\S*$/, keys %eibgaconf)
-#{
-# next unless $eibgaconf{$rec}{name}=~/Dali/;
-# $rec=$eibgaconf{$rec}{short};
-# my $trm=$rec; $trm=~s/^LI/LK/;
-#
-# $logic{$rec}={
-# receive=>$rec, transmit=>$trm,
-# translate => sub { my ($state,$input)=@_; 100*(1-$input); }
-# };
-#}
-
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-05-11 18:16:13
|
Revision: 808
http://openautomation.svn.sourceforge.net/openautomation/?rev=808&view=rev
Author: makki1
Date: 2012-05-11 18:16:06 +0000 (Fri, 11 May 2012)
Log Message:
-----------
Sunny Webbox/SMA
Modified Paths:
--------------
wiregate/plugin/generic/SunnyWeb.pl
Added Paths:
-----------
wiregate/plugin/generic/Plugin-subscribe-debug.pl
wiregate/plugin/generic/SunnyWeb-CSV.pl
Added: wiregate/plugin/generic/Plugin-subscribe-debug.pl
===================================================================
--- wiregate/plugin/generic/Plugin-subscribe-debug.pl (rev 0)
+++ wiregate/plugin/generic/Plugin-subscribe-debug.pl 2012-05-11 18:16:06 UTC (rev 808)
@@ -0,0 +1,19 @@
+# Einfaches Plugin zum debuggen aller aktiven Plugin-subscriptions
+# just zur Doku
+
+$plugin_info{$plugname.'_cycle'} = 3600;
+my $ret;
+
+for my $k ( sort keys %plugin_subscribe ) {
+ for my $p ( keys %{$plugin_subscribe{ $k }} ) {
+ $ret .= "Plugin $p subscribed to $k\n";
+ }
+}
+
+for my $k ( sort keys %plugin_socket_subscribe ) {
+ $ret .= "Plugin $plugin_socket_subscribe{$k} subscribed to socket $k\n";
+}
+
+#return; # no debug out
+return $ret;
+
Added: wiregate/plugin/generic/SunnyWeb-CSV.pl
===================================================================
--- wiregate/plugin/generic/SunnyWeb-CSV.pl (rev 0)
+++ wiregate/plugin/generic/SunnyWeb-CSV.pl 2012-05-11 18:16:06 UTC (rev 808)
@@ -0,0 +1,109 @@
+# Plugin zur Abfrage einer Sunny Webbox-2 (BT) via FTP/CSV
+# V 1.1 2012-05-03
+# Aufbau moeglichst so, dass man unterhalb der Definitionen nichts aendern muss!
+# Der Abruf des CSV ist recht langsam, daher erledigt das ein Shell-script/Crontab:
+# Das CSV wird nicht analysiert sondern nur fixe Positionen angesprungen,
+# da unterschiedliche WR unterschiedlich viele Werte liefern
+
+##################
+### DEFINITION ###
+##################
+$plugin_info{$plugname.'_cycle'} = 300; # Eigenen Aufruf-Zyklus setzen
+
+my @Werte = (
+{ Name => "G_Metering.TotWhOut", GA => "10/1/1", DPT => "13", Column => 1, Type => "COUNTER"},
+{ Name => "G_Operation.GriSwCnt", GA => "10/1/2", DPT => "13", Column => 2, Type => "COUNTER"},
+{ Name => "G_GridMs.TotW", GA => "10/1/3", DPT => "14", Column => 5, Type => "GAUGE"},
+
+{ Name => "210_Metering.TotWhOut", GA => "10/1/11", DPT => "13", Column => 22, Type => "COUNTER"},
+{ Name => "210_Operation.GriSwCnt", GA => "10/1/12", DPT => "13", Column => 23, Type => "COUNTER"},
+{ Name => "210_Metering.TotOpTms", GA => "10/1/13", DPT => "13", Column => 24, Type => "COUNTER"},
+{ Name => "210_Metering.TotFeedTms",GA => "10/1/14", DPT => "13", Column => 25, Type => "COUNTER"},
+{ Name => "210_GridMs.TotW", GA => "10/1/15", DPT => "14", Column => 26, Type => "GAUGE"},
+{ Name => "210_GridMs.Hz", GA => "10/1/16", DPT => "14", Column => 27, Type => "GAUGE"},
+{ Name => "210_Isolation.FltA", GA => "10/1/17", DPT => "9", Column => 28, Type => "GAUGE"},
+{ Name => "210_Isolation.LeakRis", GA => "10/1/18", DPT => "14", Column => 29, Type => "GAUGE"},
+{ Name => "210_DcMs.Vol_A", GA => "10/1/19", DPT => "14", Column => 30, Type => "GAUGE"},
+{ Name => "210_DcMs.Amp_A", GA => "10/1/21", DPT => "14", Column => 31, Type => "GAUGE"},
+{ Name => "210_GridMs.PhV.phsA", GA => "10/1/23", DPT => "14", Column => 32, Type => "GAUGE"},
+{ Name => "210_GridMs.A.phsA", GA => "10/1/26", DPT => "14", Column => 33, Type => "GAUGE"},
+{ Name => "210_DcMs.Watt_A", GA => "10/1/29", DPT => "13", Column => 34, Type => "GAUGE"},
+
+{ Name => "211_Metering.TotWhOut", GA => "10/1/41", DPT => "13", Column => 39, Type => "COUNTER"},
+{ Name => "211_Operation.GriSwCnt", GA => "10/1/42", DPT => "13", Column => 40, Type => "COUNTER"},
+{ Name => "211_Metering.TotOpTms", GA => "10/1/43", DPT => "13", Column => 41, Type => "COUNTER"},
+{ Name => "211_Metering.TotFeedTms",GA => "10/1/44", DPT => "13", Column => 42, Type => "COUNTER"},
+{ Name => "211_GridMs.TotW", GA => "10/1/45", DPT => "14", Column => 43, Type => "GAUGE"},
+{ Name => "211_GridMs.Hz", GA => "10/1/46", DPT => "14", Column => 44, Type => "GAUGE"},
+{ Name => "211_Isolation.FltA", GA => "10/1/47", DPT => "9", Column => 45, Type => "GAUGE"},
+{ Name => "211_Isolation.LeakRis", GA => "10/1/48", DPT => "14", Column => 46, Type => "GAUGE"},
+{ Name => "211_DcMs.Vol_A", GA => "10/1/49", DPT => "14", Column => 47, Type => "GAUGE"},
+{ Name => "211_DcMs.Vol_B", GA => "10/1/10", DPT => "14", Column => 48, Type => "GAUGE"},
+{ Name => "211_DcMs.Amp_A", GA => "10/1/51", DPT => "14", Column => 49, Type => "GAUGE"},
+{ Name => "211_DcMs.Amp_B", GA => "10/1/52", DPT => "14", Column => 50, Type => "GAUGE"},
+{ Name => "211_GridMs.PhV.phsA", GA => "10/1/53", DPT => "14", Column => 51, Type => "GAUGE"},
+{ Name => "211_GridMs.PhV.phsB", GA => "10/1/54", DPT => "14", Column => 52, Type => "GAUGE"},
+{ Name => "211_GridMs.PhV.phsC", GA => "10/1/55", DPT => "14", Column => 53, Type => "GAUGE"},
+{ Name => "211_GridMs.A.phsA", GA => "10/1/56", DPT => "14", Column => 54, Type => "GAUGE"},
+{ Name => "211_GridMs.A.phsB", GA => "10/1/57", DPT => "14", Column => 55, Type => "GAUGE"},
+{ Name => "211_GridMs.A.phsC", GA => "10/1/58", DPT => "14", Column => 56, Type => "GAUGE"},
+{ Name => "211_DcMs.Watt_A", GA => "10/1/59", DPT => "13", Column => 57, Type => "GAUGE"},
+{ Name => "211_DcMs.Watt_B", GA => "10/1/60", DPT => "13", Column => 58, Type => "GAUGE"}
+);
+
+#Daten von Referenzanlage/CSV:
+#0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;21;22;23;24;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39;40;41;42;43;44;45;46;47;48;49;50;51;52;53;54;55;56;57;58;59;60;61;62
+#<SN>
+#;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;WebBox-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;SB 3000TL-20;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10;STP 12000TL-10
+#<SN>
+#;Metering.TotWhOut;Operation.GriSwCnt;Metering.TotOpTms;Metering.TotFeedTms;GridMs.TotW;GridMs.Hz;Isolation.FltA;Isolation.LeakRis;DcMs.Vol[A];DcMs.Vol[B];DcMs.Amp[A];DcMs.Amp[B];GridMs.PhV.phsA;GridMs.PhV.phsB;GridMs.PhV.phsC;GridMs.A.phsA;GridMs.A.phsB;GridMs.A.phsC;DcMs.Watt[A];DcMs.Watt[B];Operation.Health;Metering.TotWhOut;Operation.GriSwCnt;Metering.TotOpTms;Metering.TotFeedTms;GridMs.TotW;GridMs.Hz;Isolation.FltA;Isolation.LeakRis;DcMs.Vol[A];DcMs.Amp[A];GridMs.PhV.phsA;GridMs.A.phsA;DcMs.Watt[A];Operation.Health;Operation.Evt.Prio;Operation.Evt.Msg;Operation.Evt.Dsc;Metering.TotWhOut;Operation.GriSwCnt;Metering.TotOpTms;Metering.TotFeedTms;GridMs.TotW;GridMs.Hz;Isolation.FltA;Isolation.LeakRis;DcMs.Vol[A];DcMs.Vol[B];DcMs.Amp[A];DcMs.Amp[B];GridMs.PhV.phsA;GridMs.PhV.phsB;GridMs.PhV.phsC;GridMs.A.phsA;GridMs.A.phsB;GridMs.A.phsC;DcMs.Watt[A];DcMs.Watt[B];Operation.Health;Operation.Evt.Prio;Operation.Evt.Msg;Operation.Evt.Dsc
+#;Counter;Counter;Counter;Counter;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Status;Counter;Counter;Counter;Counter;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Status;Status;Status;Status;Counter;Counter;Counter;Counter;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Analog;Status;Status;Status;Status
+#dd.MM.yyyy HH:mm;kWh;;h;h;W;Hz;mA;kOhm;V;V;A;A;V;V;V;A;A;A;W;W;;kWh;;h;h;W;Hz;mA;kOhm;V;A;V;A;W;;;;;kWh;;h;h;W;Hz;mA;kOhm;V;V;mA;mA;V;V;V;A;A;A;W;W;;;;
+#03.05.2012 14:40;3804,60;407,00;3137,63;2838,07;4594,00;49,96;9,00;3000,00;291,54;527,14;5,54;2,71;232,54;231,42;233,42;4,76;5,11;5,12;3305,00;1434,00;Ok;881,10;221,00;1515,96;1357,44;1038,00;49,96;6,00;3000,00;203,98;5,21;234,31;4,42;1066,00;Ok;NonePrio;None;None;2923,50;186,00;1621,67;1480,63;3556,00;49,96;12,00;3000,00;379,11;527,14;5874,00;2712,00;230,78;231,42;233,42;5,09;5,11;5,12;2239,00;1434,00;Ok;NonePrio;None;None
+#root@wiregateXXX:~# cat /usr/local/bin/sunny-getcsv.sh
+# #!/bin/sh
+#FILE="CSV/$(date +%Y/%m/%Y-%m-%d).csv"
+#USER="Installer"
+#PASS="1234"
+#IP="192.168.0.110"
+#curl "ftp://$USER:$PASS@$IP/$FILE" | tail -n 1 > /tmp/sunny.csv
+#curl "http://$IP/culture/login?Language=LangDE&Userlevels=$USER&password=$PASS" > /tmp/sunny.xml
+
+#chmod a+x /usr/local/bin/sunny-getcsv.sh
+#crontab -l
+# m h dom mon dow command
+#*/6 * * * * /usr/local/bin/sunny-getcsv.sh >/dev/null 2>&1
+
+# 13.010 DPT_ActiveEnergy [-2 147 483 648 ... 2 147 483 647] Wh - 4 byte signed
+# 14.056 DPT_Value_Power W - 4 byte float
+
+#######################
+### ENDE DEFINITION ###
+#######################
+
+# Hauptverarbeitung
+open (CSV, '/tmp/sunny.csv');
+my @line;
+while (<CSV>) {
+ chomp;
+ next unless $_; # protect empty lines
+ @line = split /;/, $_;
+}
+close (CSV);
+
+foreach ( @Werte ) {
+ $line[$_->{Column}] =~ s/\.//g;
+ $line[$_->{Column}] =~ s/,/\./;
+ knx_write($_->{GA}, $line[$_->{Column}], $_->{DPT});
+ $line[$_->{Column}] *= 3600 if ($_->{Type} eq "COUNTER");
+ update_rrd($_->{Name},"",$line[$_->{Column}], $_->{Type});
+}
+
+
+@line=();
+undef @line;
+@Werte=();
+undef @Werte;
+
+return "Updated";
+
Modified: wiregate/plugin/generic/SunnyWeb.pl
===================================================================
--- wiregate/plugin/generic/SunnyWeb.pl 2012-05-10 20:01:11 UTC (rev 807)
+++ wiregate/plugin/generic/SunnyWeb.pl 2012-05-11 18:16:06 UTC (rev 808)
@@ -1,57 +1,120 @@
-# Plugin zur Abfrage einer Sunny Webbox-2 (BT)
-# V 1.0
+# Plugin zur Abfrage einer Sunny Webbox-2 (BT) via HTTP/XML
+# es existiert noch ein zweites, erweiteretes Plugin SunnyWeb-CSV!
+# -> Gesamt-Anlagenstatus/Ertrag sofern mehrere Wechselrichter
+# angeschlossen sind!
+# V 1.1 2012-05-03
# Aufbau moeglichst so, dass man unterhalb der Definitionen nichts aendern muss!
##################
### DEFINITION ###
##################
-my $ip = "192.168.1.110";
+my $ip = "192.168.0.110";
my $user = "Installer";
-my $pass = "1111";
+my $pass = "1234";
my $url = "/culture/login?Language=LangDE&Userlevels=".$user."&password=".$pass;
-# 13.010 DPT_ActiveEnergy [-2 147 483 648 ... 2 147 483 647] Wh
+# 13.010 DPT_ActiveEnergy [-2 147 483 648 ... 2 147 483 647] Wh - 4 byte signed
my $name = "PV-Anlage"; # Name fuer die RRD-Archive
-my $ga_ertrag = "13/3/1"; # GA fuer Gesamtertrag in Wh DPT13
-my $ga_tagesertrag = "13/3/2"; # GA fuer Tagesertrag in Wh DPT13
-my $ga_error = "13/3/0"; # GA erhaelt eine 1 wenn Anlagenzustand nicht Ok
-
+my $ga_ertrag = "10/0/1"; # GA fuer Gesamtertrag in Wh DPT13
+my $ga_tagesertrag = "10/0/2"; # GA fuer Tagesertrag in Wh DPT13
+# 14.056 DPT_Value_Power W - 4 byte float
+my $ga_leistung = "10/0/3"; # GA fuer akt. Leistung in W DPT14
+my $ga_error = "10/0/4"; # GA erhaelt eine 1 wenn Anlagenzustand nicht Ok
$plugin_info{$plugname.'_cycle'} = 300; # Eigenen Aufruf-Zyklus setzen
+my $debug = 0; # Debug-Ausgaben aktivieren?
+
#######################
### ENDE DEFINITION ###
#######################
# Hauptverarbeitung
-use LWP::Simple;
+use LWP::Simple; # comment wenn lokales XML
use XML::Simple;
-my $xml = get("http://".$ip.$url);
+my $xml = get("http://".$ip.$url); # comment wenn lokales XML
return "Fehler beim auslesen!" unless $xml;
+if ($debug) {
+ open (XMLFILE, '>/tmp/sunny1.xml');
+ print XMLFILE $xml;
+ close (XMLFILE);
+}
+
+# Die Webbox ist ziemlich lahm, ca. 2.3s im Schnitt; geschickter waere es, das XML per wget/curl mit der crontab zu holen siehe SunnyWeb-CSV.
my $data = XMLin($xml)->{Content}->{Element}->{Tab}->{'hp.PlantOverview'}->{Element};
+#my $data = XMLin('/tmp/sunny.xml')->{Content}->{Element}->{Tab}->{'hp.PlantOverview'}->{Element};
my $separator = $data->{'decimalSeparator'};
my $dataP = $data->{Element}[0]->{Items}->{XmlItem}->{'Solar-Wechselrichter'}->{Items}->{XmlItem};
my $zustand = $dataP->{Zustand}{'Value'};
+
my $gesErtrag = $dataP->{Gesamtertrag}{'Value'};
-my $tagErtrag = $dataP->{Tagesertrag}{'Value'};
-my $gesFactor = $dataP->{Gesamtertrag}{'factor'};
-$gesErtrag =~ s/$separator/\./;
+my $gesFactor = $dataP->{Gesamtertrag}{'factor'} || 1;
+#my $dseparator = $data->{'decimalSeparator'};
+#my $gseparator = $data->{'groupSeparator'};
+# as lang=DE we assume x.xxx,yy
+# a little haky-cranky as perl writes UTF8 numbers otherwise
+$gesErtrag =~ s/\.//g;
+$gesErtrag =~ s/,/\./;
$gesErtrag *= $gesFactor;
+my $tagErtrag = $dataP->{Tagesertrag}{'Value'};
+my $tagFactor = $dataP->{Tagesertrag}{'factor'} || 1;
+$tagErtrag =~ s/\.//;
+$tagErtrag =~ s/,/\./;
+$tagErtrag *= $tagFactor;
+
+my $aktLeistung = $dataP->{Leistung}->{Items}->{XmlItem}{'Summe'}{'Value'};
+my $leistFactor = $dataP->{Leistung}->{Items}->{XmlItem}{'Summe'}{'factor'} || 1;
+$aktLeistung =~ s/\.//;
+$aktLeistung =~ s/\,/\./;
+$aktLeistung *= $leistFactor;
+
knx_write($ga_ertrag,$gesErtrag,13);
knx_write($ga_tagesertrag,$tagErtrag,13);
+knx_write($ga_leistung,$aktLeistung,14);
if ($zustand !~ /Ok/) {
# Etwas mit dem Problem machen
- knx_write($ga_error,1,1)
+ knx_write($ga_error,1,1);
+} else {
+ knx_write($ga_error,0,1);
}
# Werte loggen
update_rrd($name,"_Leistung",$gesErtrag*3600,"COUNTER");
update_rrd($name,"_Tag",$tagErtrag);
+update_rrd($name,"_aktLeistung",$aktLeistung);
-#DEBUG: return "Zustand $zustand Tag: $tagErtrag Wh Gesamt $gesErtrag Wh";
+#my $dataD = XMLin($xml)->{DeviceTree}->{Devices}->{Device}->{Devices}->{Device}; # only first, a little hacky
+#my $dataD = XMLin('/tmp/sunny.xml')->{DeviceTree}->{Devices}->{Device}->{Devices}->{Device}; # only first, a little hacky
+
+#my @wrs; # Array der einzelnen Wechselrichter
+#for my $key (sort keys %$dataD) {
+# print "$dataD->{$key}->{deviceType} <= $key => $dataD->{$key}->{Value}\n";
+# push (@wrs,$dataD->{$key}->{Value}); # SN in array ablegen
+#}
+
+#foreach (@wrs) {
+ # hier koennte man nun noch die einzelnen WR-Details holen
+#}
+
+# avoid memleaks?
+$xml = (); # comment wenn lokales XML
+$data = ();
+$dataP = ();
+#$dataD = ();
+undef $xml; # comment wenn lokales XML
+undef $data;
+undef $dataP;
+#undef $dataD;
+
+
+# finally wollen wir Tausender im log, wilde RE
+$gesErtrag =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1./g;
+$tagErtrag =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1./g;
+$aktLeistung =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1./g;
+return "Zustand $zustand Leistung: $aktLeistung W Tag: $tagErtrag Wh Gesamt $gesErtrag Wh\n" if $debug;
return;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-11 22:17:07
|
Revision: 811
http://openautomation.svn.sourceforge.net/openautomation/?rev=811&view=rev
Author: pfry
Date: 2012-05-11 22:17:00 +0000 (Fri, 11 May 2012)
Log Message:
-----------
Edits betr. Zeitansage und cool=>....-Klausel
Modified Paths:
--------------
wiregate/plugin/generic/Ansagen.pl
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Ansagen.pl
===================================================================
--- wiregate/plugin/generic/Ansagen.pl 2012-05-11 19:12:49 UTC (rev 810)
+++ wiregate/plugin/generic/Ansagen.pl 2012-05-11 22:17:00 UTC (rev 811)
@@ -7,6 +7,7 @@
# $plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
use POSIX qw(floor);
+use feature "switch"; # aktiviert given...when-Struktur von Perl 5.10
# Defaultkonfiguration
my $speechdir='/var/lib/mpd/music/Ansagen';
@@ -52,9 +53,10 @@
delete $plugin_info{$k};
}
- for my $ga (keys %eibgaconf)
+ # Rueckwaertskompatible Behandlung von eibgaconf
+ for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
{
- my $name=$eibgaconf{$ga}{'name'};
+ my $name=$eibgaconf{$ga}{name};
next unless defined $name;
for my $pat (keys %channels)
@@ -79,14 +81,14 @@
return join ' ', map $_.'->'.$gas{$_}, keys %gas;
}
-elsif($event=~/bus/ && $msg{'apci'} eq 'A_GroupValue_Write')
+elsif($event=~/bus/ && $msg{apci} eq 'A_GroupValue_Write')
{
- my $ga=$msg{'dst'};
- my $val=$msg{'value'};
- my $dpt=$eibgaconf{$ga}{'DPTSubId'};
+ my $ga=$msg{dst};
+ my $val=$msg{value};
+ my $dpt=$eibgaconf{$ga}{DPTSubId};
$dpt=1.017 unless defined $dpt; # = Trigger, bedeutet Textansage ohne Daten
- my $name=$eibgaconf{$ga}{'name'};
+ my $name=$eibgaconf{$ga}{name};
my $channel=$channels{default};
my $pattern=$name;
@@ -122,9 +124,11 @@
{ push(@statement, 'Zahlen/'.($val?'hoch':'runter').'.wav'); }
when(1.009) # Auf/Zu
{ push(@statement, 'Zahlen/'.($val?'auf':'zu').'.wav'); }
+ when(2.007) # Auf/Ab/Stop
+ { push(@statement, 'Zahlen/'.($val==1?'auf':($val==-1?'ab':'stop')).'.wav'); }
when([5.010,7.001,12.001]) # Ordinalzahl
{ push(@statement, number(\@speech, $val, -1)); }
- when([6.010,8.001,13.001]) # Kardinalzahl
+ when([3.007,6.010,8.001,13.001]) # Kardinalzahl
{ push(@statement, number(\@speech, $val)); }
when([5.001,6.001]) # Prozent
{
@@ -176,13 +180,13 @@
push(@statement, "Wochentage/$1.wav");
push(@statement, number(\@speech, $2));
push(@statement, "Zeiten/Uhr.wav");
- push(@statement, number(\@speech, $3));
+ push(@statement, number(\@speech, $3)) if $3;
}
- elsif($val=~/^([0-9][0-9])\:([0-9][0-9])}\:([0-9][0-9])/)
+ elsif($val=~/^([0-9][0-9])\:([0-9][0-9])/)
{
- push(@statement, number(\@speech, $2));
+ push(@statement, number(\@speech, $1));
push(@statement, "Zeiten/Uhr.wav");
- push(@statement, number(\@speech, $3)) if $3;
+ push(@statement, number(\@speech, $2)) if $2;
}
else
{
@@ -224,9 +228,9 @@
# Konstruiere die abzuspielenden File(s) aus dem GA-Kuerzel
# erster Versuch: eine Datei passt komplett auf das Muster im Kuerzel
my $pat1=$pattern;
- $pat1=~s/[_\s]+/.*?/g; # allgemeine Fassung
-# $pat1=~s/\s+.*$//; # meine spezielle GA-Struktur
-# $pat1=~s/_+/.*?/g; # meine spezielle GA-Struktur
+# $pat1=~s/[_\s]+/.*?/g; # allgemeine Fassung
+ $pat1=~s/\s+.*$//; # meine spezielle GA-Struktur
+ $pat1=~s/_+/.*?/g; # meine spezielle GA-Struktur
$pat1='.*'.$pat1.'.*\.wav$';
my @hits=();
@@ -236,8 +240,8 @@
unless(@hits)
{
$pattern='_'.$pattern;
- $pattern=~s/\s+/_/g; # allgemeine Fassung
-# $pattern=~s/\s+.*$//; # meine spezielle GA-Struktur
+# $pattern=~s/\s+/_/g; # allgemeine Fassung
+ $pattern=~s/\s+.*$//; # meine spezielle GA-Struktur
# zweiter Versuch: aus Kuerzeln die Bausteine zusammenbauen
while($pattern=~s/^_([^_]+)//)
@@ -441,11 +445,11 @@
system "$mpc update"; # Aktualisierung verfuegbarer Soundclips
# wird momentan noch was gespielt?
- my $playing = grep /playing/, `$mpc`;
-
- system "$mpc clear" unless $playing; # aufraeumen
+ system "$mpc clear" unless `$mpc`=~/playing/s; # leeren falls abgespielt
system "$mpc add \"".(join "\" \"", @_)."\"";
- system "$mpc play" unless $playing; # nur dann noetig
+# plugin_log($plugname, "$mpc add \"".(join "\" \"", @_)."\"");
+# plugin_log($plugname, "$mpc play") unless `$mpc`=~/playing/s;
+ system "$mpc play" unless `$mpc`=~/playing/s; # starten falls noch nicht aktiv
# $playing=`$mpc; $mpc outputs`;
# $playing=~s/\s+/ /sg;
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-11 19:12:49 UTC (rev 810)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-11 22:17:00 UTC (rev 811)
@@ -8,6 +8,7 @@
#$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+my $stime=time(); # Stoppuhr zum Nachverfolgen der Reaktionsgeschwindigkeit der Logikengine
# eibgaconf fixen falls nicht komplett indiziert
if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
@@ -80,7 +81,7 @@
{
$plugin_info{$plugname.'_configtime'}=(24*60*60*(-M $conf)-time());
- # alle Variablen loeschen und neu initialisieren, alle GAs abonnieren
+ # alle Variablen loeschen
for my $k (grep /^$plugname\_/, keys %plugin_info)
{
delete $plugin_info{$k};
@@ -92,6 +93,7 @@
for my $t (keys %logic)
{
next if $t eq 'debug';
+ $t=~s/^_//g;
# Debuggingflag gesetzt
my $debug = $logic{debug} || $logic{$t}{debug};
@@ -172,6 +174,7 @@
for my $t (keys %logic)
{
next if $t eq 'debug';
+ $t=~s/^_//g;
my $transmit=groupaddress($logic{$t}{transmit});
my $transmit_ga = ($ga eq $transmit);
@@ -208,7 +211,9 @@
my $result=$plugin_info{$plugname.'_'.$t.'_result'};
if(defined $result)
{
- plugin_log($plugname, "$ga:Lesetelegramm -> \$logic{$t}{transmit}(memory) -> $ga:$result gesendet") if $debug;
+ plugin_log($plugname, "$ga:Lesetelegramm -> \$logic{$t}{transmit}(memory) -> $ga:$result gesendet ("
+ .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $stime=time();
knx_write($ga, $result);
}
next;
@@ -232,26 +237,34 @@
# Nebenbei berechnen wir noch zwei Flags, die Zirkelkommunikation verhindern sollen
# (Logik antwortet auf sich selbst in einer Endlosschleife)
- # war Wiregate der Sender des Telegramms?
- my $sender_is_wiregate = $msg{src} eq $eibd_backend_address;
+ # Cool-Periode definiert und noch nicht abgelaufen?
+ if(defined $plugin_info{$plugname.'__'.$t.'_cool'} && $plugin_info{$plugname.'__'.$t.'_cool'}>time())
+ {
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Cool)") if $debug;
+ next;
+ }
# Aufruf der Logik-Engine
my $result=execute_logic($t, $receive, $ga, $in);
- # Zirkelaufruf ausschliessen
+ # war Wiregate der Sender des Telegramms?
+ # Zirkelaufruf mit wiederholt gleichen Ergebnissen ausschliessen
+ my $sender_is_wiregate = $msg{src} eq $eibd_backend_address;
next if $sender_is_wiregate && $transmit_ga && $in == $result;
# In bestimmten Sonderfaellen nichts schicken
unless(defined $result) # Resultat undef => nichts senden
{
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden") if $debug;
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden (".sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $stime=time();
next;
}
if($logic{$t}{transmit_only_on_request})
{
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gespeichert")
+ plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gespeichert (".sprintf("%0.1f",time()-$stime)."s)")
if $debug;
+ $stime=time();
next;
}
@@ -259,13 +272,20 @@
if($logic{$t}{delay})
{
$plugin_info{$plugname.'__'.$t.'_timer'}=$systemtime+$logic{$t}{delay};
- plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s")
- if $debug;
+ $plugin_info{$plugname.'__'.$t.'_cool'}=time()+$logic{$t}{delay}+$logic{$t}{cool} if defined $logic{$t}{cool};
+ plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s ("
+ .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $stime=time();
}
else
{
knx_write($transmit, $result);
- plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet") if $debug;
+ plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet ("
+ .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $stime=time();
+
+ # Cool-Periode starten
+ $plugin_info{$plugname.'__'.$t.'_cool'}=time()+$logic{$t}{cool} if defined $logic{$t}{cool};
}
}
@@ -298,7 +318,9 @@
# zu sendendes Resultat = zuletzt berechnetes Ergebnis der Logik
$result=$plugin_info{$plugname.'_'.$t.'_result'};
plugin_log($plugname, "\$logic{$t} -> $transmit:".
- (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (delayed)") if $debug;
+ (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (delayed) ("
+ .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $stime=time();
}
else
{
@@ -306,7 +328,9 @@
# Aufruf der Logik-Engine
$result=execute_logic($t, groupaddress($logic{$t}{receive}), undef, undef);
plugin_log($plugname, "\$logic{$t} -> $transmit:".
- (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (Timer)") if $debug;
+ (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (Timer) ("
+ .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $stime=time();
}
# Timer loeschen bzw. neu setzen
@@ -315,6 +339,9 @@
if(defined $result && !$toor)
{
knx_write($transmit, $result);
+
+ # Cool-Periode starten
+ $plugin_info{$plugname.'__'.$t.'_cool'}=time()+$logic{$t}{cool} if defined $logic{$t}{cool};
}
}
else # noch nicht faelliger Timer
@@ -400,7 +427,7 @@
# Schedule-Form standardisieren (alle Eintraege in Listenform setzen und Wochentage durch Zahlen ersetzen)
# dabei gleich schauen, ob HEUTE noch ein Termin ansteht
$schedule=[$schedule] if ref $schedule eq 'HASH';
- my %weekday=(Mo=>1,Di=>2,Mi=>3,Do=>4,Fr=>5,Sa=>6,So=>7);
+ my %weekday=(Mo=>1,Mo=>1,Mon=>1,Di=>2,Tu=>2,Tue=>2,Mi=>3,We=>3,Wed=>3,Do=>4,Th=>4,Thu=>4,Fr=>5,Fri=>5,Sa=>6,Sat=>6,So=>7,Su=>7,Sun=>7);
for my $s (@{$schedule})
{
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-11 19:12:49 UTC (rev 810)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-11 22:17:00 UTC (rev 811)
@@ -3,13 +3,14 @@
# Logikprozessor.pl - Konfiguration
#
-$eibd_backend_address='1.1.254'; # eigene Adresse zur Vermeidung von Zirkellogiken
+$eibd_backend_address='1.1.254'; # eigene Adresse zur Vermeidung von Zirkellogiken '0.0.0';
%logic=(
# 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { 2*$input; }, },
# das "undef" steht da einfach, weil uns der letzte Ergebniswert nicht interessiert
-
+ # Namen von Logiken (hier "mal2" sind voellig frei, duerfen aber nicht mit einem "_" beginnen.
+
# 2. Die Werte auf der ersten GA werden aufsummiert, das Ergebis auf der anderen GA gesendet.
# Damit kann man bspw aus einem relativen Dimmwert einen absoluten Dimmwert machen.
sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { $state+$input; }, },
@@ -18,7 +19,15 @@
# 3. Wie oben, aber das Ergebnis limitiert auf den Bereich 0-100
lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { limit(0,$state+$input,100); }, },
- # 4. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an
+ # 4. Memory-Funktion. Fuer KNX-Ger\xE4te, die kein Auslesen ihres Statuswerts zulassen (z.B. MDT DaliControl
+ # bei Einzel-EVG-Ansteuerung). Sehr einfach:
+ memory => { transmit=>'1/2/9' },
+ # Hier wird folgende Eigenschaft der Logik ausgenutzt: Wenn ein Write-Telegramm auf die Transmit-Adresse kommt,
+ # speichert der Logikprozessor den Wert immer automatisch ab. Eine Leseanfrage auf der transmit-GA hingegen wird
+ # immer mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Eine receive-Adresse oder translate-Logik
+ # werden hier gar nicht gebraucht.
+
+ # 5. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an
# die transmit-Adresse (hier gleich) geschickt. Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der
# translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
# Rueckgabewert spezifiziert.
@@ -26,35 +35,41 @@
# Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der translate-Routine.
# Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante
# als Rueckgabewert spezifiziert.
-
+
# Weitere Bemerkungen:
# * translate darf nur entweder eine Konstante oder ausf\xFChrbarer Code (sub {...}) sein.
# * Damit im Fall transmit==receive der Translator nicht auf sein eigenes Schreibtelegramm immer wieder antwortet,
- # wird nur dann gesendet, wenn Ergebnis!=Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
+ # wird nur dann gesendet, wenn Ergebnis!=Input oder Sender des empfangenen Telegramms!=$eibd_backend_address (Wiregate).
+ # * Wenn waehrend der delay-Zeit erneut ein Receive-Telegramm empfangen wird, wird die Logik erneut ausgefuehrt und
+ # die delay-Zeit beginnt von vorne (wie bei einem Treppenlicht die Leuchtzeit bei erneutem Druecken verlaengert wird).
- # Damit im Fall transmit != receive der Logikprozessor nicht auf sein eigenes Schreibtelegramm immer wieder antwortet,
- # wird nur dann gesendet, wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
+ # Als weitere Sicherheit vor Zirkellogiken (die ja auch im Zusammenwirken mehrerer Logiken entstehen koennen) gibt es
+ # die Klausel "cool=>...", die eine Logik nur dann zur Ausfuehrung zulaesst, wenn seit der letzten Ausfuehrung
+ # die spezifizierte Zahl von Sekunden verstrichen ist. Im obigen Beispiel also zB
+ stair2 => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, cool=>650 },
+ # So werden Zirkelschluesse komplett unmoeglich.
+ # Hinweise zu cool:
+ # * bei gesetztem "cool=>..." wird die Ausfuehrung der Logik auf receive-Telegramme (nicht aber die Reaktion auf
+ # Schreib/Lesetelegramme wie in Bsp 4) fuer die definierte Zeit verhindert.
+ # * Falls gleichzeitig "delay=>..." spezifiziert ist, so wird die erneute Ausfuehrung der Logik bereits waehrend
+ # der delay-Zeit verhindert bis zum folgenden Schreibvorgang UND danach noch fuer die in cool=>... spezifizierte
+ # Anzahl Sekunden.
+ # * Ein Setzen von cool=>0 und delay=>10 hat den Effekt, dass die Ausfuehrung der Logik sofort nach Senden des
+ # transmit-Telegramms wieder erlaubt ist, aber NICHT waehrend der delay-Zeit (hier 10s).
+
# 5. Hier eine Logik, die den Input bei Eintreffen mit 2 multipliziert, das Resultat aber nur speichert und erst
# spaeter auf ein explizites Lesetelegramm hin auf der transmit-Adresse sendet.
req => { receive=>'1/2/9', transmit=>'1/2/9', transmit_on_request=>1, translate => sub { 2*$input; }, },
# 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
- dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 3; }, },
- # Die Variablen $day_of_week (Mo...So), $day_of_month (01-31), $month (01-12), $year (2012),
+ dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 3; } },
+ # Die Variablen $day_of_week (Mo...So), $calendar_week, $day_of_month (01-31), $month (01-12), $year (2012),
# $weekend (0 oder 1), $weekday (= nicht $weekend), $time_of_day ("08:34:02"),
# $hour ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt)
# sind f\xFCr diese Logiken vorbesetzt (bitte nicht darauf schreiben, koennte unverhergesehene Auswirkungen
# auf andere Logiken haben).
- # 7. Memory-Funktion. Fuer KNX-Ger\xE4te, die kein Auslesen ihres Statuswerts zulassen (z.B. MDT DaliControl
- # bei Einzel-EVG-Ansteuerung). Sehr einfach:
- memory => { transmit=>'1/2/9' },
- # Hier wird folgende Eigenschaft der Logik ausgenutzt: Wenn ein Write-Telegramm auf die Transmit-Adresse kommt,
- # speichert der Logikprozessor den Wert immer automatisch ab. Eine Leseanfrage auf der transmit-GA hingegen wird
- # immer mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Eine receive-Adresse oder translate-Logik
- # werden hier gar nicht gebraucht.
-
# 8. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
# wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { $input->[0] && $input->[1]; }, debug=>1 },
@@ -76,13 +91,14 @@
# Dienstag jedes Monats eine 1 auf Transmit sendet
wecker => { transmit=>'10/1/15', timer=>{ time=>['08:00','10:00'], day_of_month=>[(8..14)], day_of_week=>'Di' }, translate => 1 },
# Logiken mit timer-Klausel weichen in mehreren Punkten von den bisherigen Logiken ab:
- # * sie ignorieren die delay-Klausel und senden sofort, aber transmit_only_on_request funktioniert
+ # * sie ignorieren die delay-Klausel und die cool-Klausel
+ # * transmit_only_on_request funktioniert
# * jeglicher Bustraffic auf receive-Adressen wird ignoriert. (Diese werden aber beim Timer-Aufruf abgefragt,
# um das input-Array vorzubesetzen).
# Als timer-Eintrag geht entweder ein einzelnes Hash timer=>{...} wie oben oder eine Liste solcher Eintraege
# time=>[{...},{...},{...},...]. Jeder Eintrag MUSS eine Spezifikation time=>'XX:XX' enthalten (auch das darf wieder
- # eine Liste sein) und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
- # day_of_week, calendar_week enthalten.
+ # eine Liste sein) und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
+ # calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) enthalten.
# 11. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
@@ -92,5 +108,3 @@
# in einen absoluten Wert umgewandelt
);
-
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-12 08:21:53
|
Revision: 812
http://openautomation.svn.sourceforge.net/openautomation/?rev=812&view=rev
Author: pfry
Date: 2012-05-12 08:21:46 +0000 (Sat, 12 May 2012)
Log Message:
-----------
Timerfunktion um periodische Schaltuhrfunktion erweitert
Alle Plugins loeschen ihr Konfigurationshash vor dem return -> moeglicherweise lindert das die Memory Leaks
Modified Paths:
--------------
wiregate/plugin/generic/Heizungsregler.pl
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/Szenencontroller.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Heizungsregler.pl
===================================================================
--- wiregate/plugin/generic/Heizungsregler.pl 2012-05-11 22:17:00 UTC (rev 811)
+++ wiregate/plugin/generic/Heizungsregler.pl 2012-05-12 08:21:46 UTC (rev 812)
@@ -9,7 +9,7 @@
use POSIX qw(floor);
use Math::Round qw(nearest);
-my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
# eibgaconf fixen falls nicht komplett indiziert
if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
@@ -65,7 +65,7 @@
# Alle Controller-GAs abonnieren, Reglerstati initialisieren
for my $r (grep ref($house{$_}), keys %house)
{
- $plugin_subscribe{$house{$r}{control}}{$plugname}=1;
+ $plugin_subscribe{groupaddress($house{$r}{control})}{$plugname}=1;
RESET($r);
}
@@ -109,18 +109,28 @@
if(defined $Vreq)
{
$Vreq=$house{inflow_max} if defined $house{inflow_max} && $Vreq>$house{inflow_max};
- knx_write($house{inflow_control},$Vreq,9.001) if defined $house{inflow_control};
+ knx_write(groupaddress($house{inflow_control}),$Vreq,9.001) if defined $house{inflow_control};
$retval.=sprintf "Vreq=%d", $Vreq;
$anynews=1;
}
$retval=~s/\s*$//; # Space am Ende entfernen
- return unless $anynews;
+ unless($anynews)
+ {
+ for my $k (keys %house) { delete $house{$k}; } # Hilfe fuer die Garbage Collection
+ for my $k (keys %dyn) { delete $dyn{$k}; } # Hilfe fuer die Garbage Collection
+ return;
+ }
}
elsif($event=~/bus/)
{
- return if $msg{apci} eq 'A_GroupValue_Response';
+ if($msg{apci} eq 'A_GroupValue_Response')
+ {
+ for my $k (keys %house) { delete $house{$k}; } # Hilfe fuer die Garbage Collection
+ for my $k (keys %dyn) { delete $dyn{$k}; } # Hilfe fuer die Garbage Collection
+ return;
+ }
# Aufruf durch GA - neue Wunschtemperatur
my $ga=$msg{dst};
@@ -142,6 +152,8 @@
$T0=$dyn{$r}{T0};
$T0=$dyn{$r}{T0old} if $dyn{mode} eq 'OPTIMIZE';
knx_write($ga,$T0,9.001);
+ for my $k (keys %house) { delete $house{$k}; } # Hilfe fuer die Garbage Collection
+ for my $k (keys %dyn) { delete $dyn{$k}; } # Hilfe fuer die Garbage Collection
return;
}
@@ -155,8 +167,13 @@
}
elsif($T0==-1)
{
- return if $dyn{$r}{mode} eq 'OPTIMIZE'; # Entprellen
-
+ if($dyn{$r}{mode} eq 'OPTIMIZE') # Entprellen
+ {
+ for my $k (keys %house) { delete $house{$k}; } # Hilfe fuer die Garbage Collection
+ for my $k (keys %dyn) { delete $dyn{$k}; } # Hilfe fuer die Garbage Collection
+ return;
+ }
+
# Initialisierung der Optimierungsfunktion
$dyn{$r}{mode}='OPTIMIZE';
$dyn{$r}{T0old}=$dyn{$r}{T0};
@@ -167,8 +184,13 @@
}
else # neue Wunschtemperatur
{
- return if $dyn{$r}{T0} == $T0; # Entprellen
-
+ if($dyn{$r}{T0} == $T0) # Entprellen
+ {
+ for my $k (keys %house) { delete $house{$k}; } # Hilfe fuer die Garbage Collection
+ for my $k (keys %dyn) { delete $dyn{$k}; } # Hilfe fuer die Garbage Collection
+ return;
+ }
+
RESET($r) if $mode eq 'OPTIMIZE'; # Optimierung unterbrochen
$dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
$dyn{$r}{T0}=$T0;
@@ -186,6 +208,8 @@
# Speichere Statusvariablen aller Regler
store_to_plugin_info(\%dyn);
+for my $k (keys %house) { delete $house{$k}; } # Hilfe fuer die Garbage Collection
+for my $k (keys %dyn) { delete $dyn{$k}; } # Hilfe fuer die Garbage Collection
return $retval eq '' ? undef : $retval;
@@ -291,7 +315,9 @@
{
if(defined $ss->{$type})
{
- my $sensorlist=(ref $ss->{$type})?$ss->{$type}:[$ss->{$type}];
+ my $sensorlist=groupaddress($ss->{$type});
+ $sensorlist=[$sensorlist] unless ref $sensorlist eq 'ARRAY';
+
for my $s (@{$sensorlist})
{
unless(defined $T{$type}{$s})
@@ -342,7 +368,7 @@
{
if(!defined $R{$type} && defined $house{$type})
{
- $R{$type} = knx_read($house{$type},$house{cycle},9);
+ $R{$type} = knx_read(groupaddress($house{$type}),$house{cycle},9);
delete $R{$type} unless $R{$type};
}
}
@@ -373,17 +399,12 @@
{
if(defined $ss->{actuator})
{
- unless(ref $ss->{actuator})
+ $ss->{actuator}=[$ss->{actuator}] unless ref $ss->{actuator} eq 'ARRAY';
+
+ for my $s (@{$ss->{actuator}})
{
- knx_write($ss->{actuator},100*$U,5.001); # DPT NOCH UNKLAR ########
+ knx_write(groupaddress($s),100*$U,5.001); # DPT NOCH UNKLAR ########
}
- else
- {
- for my $s (@{$ss->{actuator}})
- {
- knx_write($s,100*$U,5.001);
- }
- }
}
}
# plugin_log($plugname, "Done trying to write $r, $U");
@@ -762,3 +783,76 @@
return sprintf "t=%dh:%02dmin Tv=%.1fmin Tn=%dmin lim=%.1f prop=%.1f spread=%.1f ", $tp/3600,($tp/60)%60,$Tv,$Tn,$lim,$prop,$refspread;
}
+
+# Umgang mit GA-Kurznamen und -Adressen
+
+sub groupaddress
+{
+ my $short=shift;
+
+ return unless defined $short;
+
+ if(ref $short)
+ {
+ my $ga=[];
+ for my $sh (@{$short})
+ {
+ if($sh!~/^[0-9\/]+$/ && defined $eibgaconf{$sh}{ga})
+ {
+ push @{$ga}, $eibgaconf{$sh}{ga};
+ }
+ else
+ {
+ push @{$ga}, $sh;
+ }
+ }
+ return $ga;
+ }
+ else
+ {
+ my $ga=$short;
+
+ if($short!~/^[0-9\/]+$/ && defined $eibgaconf{$short}{ga})
+ {
+ $ga=$eibgaconf{$short}{ga};
+ }
+
+ return $ga;
+ }
+}
+
+sub shortname
+{
+ my $gas=shift;
+
+ return unless defined $gas;
+ return $gas unless $use_short_names;
+
+ if(ref $gas)
+ {
+ my $sh=[];
+ for my $ga (@{$gas})
+ {
+ if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
+ {
+ push @{$sh}, $eibgaconf{$ga}{short};
+ }
+ else
+ {
+ push @{$sh}, $ga;
+ }
+ }
+ return $sh;
+ }
+ else
+ {
+ my $sh=$gas;
+
+ if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
+ {
+ $sh=$eibgaconf{$gas}{short};
+ }
+
+ return $sh;
+ }
+}
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-11 22:17:00 UTC (rev 811)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-12 08:21:46 UTC (rev 812)
@@ -125,7 +125,7 @@
# transmit-Adresse abonnieren
my $transmit=groupaddress($logic{$t}{transmit});
$plugin_subscribe{$transmit}{$plugname}=1;
- plugin_log($plugname, "\$logic{$t}: Transmit-GA $transmit abonniert") if $debug;
+ plugin_log($plugname, "\$logic{$t}: Transmit-GA $transmit nicht in %eibgaconf gefunden") if $debug && !exists $eibgaconf{$transmit};
# Zaehlen und Logeintrag
$count++;
@@ -146,15 +146,15 @@
unless(ref $receive)
{
$plugin_subscribe{$receive}{$plugname}=1;
- plugin_log($plugname, "\$logic{$t}: Receive-GA $receive abonniert") if $debug;
+ plugin_log($plugname, "\$logic{$t}: Receive-GA $receive nicht in %eibgaconf gefunden") if $debug && !exists $eibgaconf{$receive};
}
else
{
for my $rec (@{$receive})
{
$plugin_subscribe{$rec}{$plugname}=1;
+ plugin_log($plugname, "\$logic{$t}: Receive-GA $rec nicht in %eibgaconf gefunden") if $debug && !exists $eibgaconf{$rec};
}
- plugin_log($plugname, "\$logic{$t}: Receive-GAs (".join(",",@{$receive}).") abonniert") if $debug;
}
}
@@ -211,8 +211,7 @@
my $result=$plugin_info{$plugname.'_'.$t.'_result'};
if(defined $result)
{
- plugin_log($plugname, "$ga:Lesetelegramm -> \$logic{$t}{transmit}(memory) -> $ga:$result gesendet ("
- .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $retval.="$ga:Lesetelegramm -> \$logic{$t}{transmit}(memory) -> $ga:$result gesendet. " if $debug;
$stime=time();
knx_write($ga, $result);
}
@@ -240,7 +239,7 @@
# Cool-Periode definiert und noch nicht abgelaufen?
if(defined $plugin_info{$plugname.'__'.$t.'_cool'} && $plugin_info{$plugname.'__'.$t.'_cool'}>time())
{
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Cool)") if $debug;
+ $retval.="$ga:$in -> \$logic{$t}{receive}(Cool) " if $debug;
next;
}
@@ -255,15 +254,14 @@
# In bestimmten Sonderfaellen nichts schicken
unless(defined $result) # Resultat undef => nichts senden
{
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden (".sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $retval.="$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden " if $debug;
$stime=time();
next;
}
if($logic{$t}{transmit_only_on_request})
{
- plugin_log($plugname, "$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gespeichert (".sprintf("%0.1f",time()-$stime)."s)")
- if $debug;
+ $retval.="$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gespeichert " if $debug;
$stime=time();
next;
}
@@ -273,15 +271,13 @@
{
$plugin_info{$plugname.'__'.$t.'_timer'}=$systemtime+$logic{$t}{delay};
$plugin_info{$plugname.'__'.$t.'_cool'}=time()+$logic{$t}{delay}+$logic{$t}{cool} if defined $logic{$t}{cool};
- plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s ("
- .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $retval.="$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s " if $debug;
$stime=time();
}
else
{
knx_write($transmit, $result);
- plugin_log($plugname, "$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet ("
- .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $retval.="$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet " if $debug;
$stime=time();
# Cool-Periode starten
@@ -317,9 +313,8 @@
{
# zu sendendes Resultat = zuletzt berechnetes Ergebnis der Logik
$result=$plugin_info{$plugname.'_'.$t.'_result'};
- plugin_log($plugname, "\$logic{$t} -> $transmit:".
- (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (delayed) ("
- .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $retval.="\$logic{$t} -> $transmit:".
+ (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (delayed) " if $debug;
$stime=time();
}
else
@@ -327,9 +322,8 @@
# ...es sei denn, es ist eine timer-Logik. Die muss jetzt ausgefuehrt werden
# Aufruf der Logik-Engine
$result=execute_logic($t, groupaddress($logic{$t}{receive}), undef, undef);
- plugin_log($plugname, "\$logic{$t} -> $transmit:".
- (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (Timer) ("
- .sprintf("%0.1f",time()-$stime)."s)") if $debug;
+ $retval.="\$logic{$t} -> $transmit:".
+ (defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (Timer) " if $debug;
$stime=time();
}
@@ -357,18 +351,20 @@
}
else
{
- my $cycle=$nexttimer-time();
+ my $cycle=int($nexttimer-time());
$cycle=1 if $cycle<1;
$plugin_info{$plugname."_cycle"}=$cycle;
- plugin_log($plugname, "Cycle (Timer) gestellt auf ".$cycle."s") if $logic{debug};
+ $retval.="Cycle (Timer) gestellt auf ".$cycle."s" if $logic{debug};
}
+# experimentell - wir helfen der Garbage Collection etwas nach...
+for my $k (keys %logic) { delete $logic{$k}; }
return unless $retval;
return $retval;
# Fuer Logiken mit timer-Klausel: Zeit des naechsten Aufrufs bestimmen
-
+# Fuer einen Tag den jeweils naechsten berechnen
sub next_day
{
my $d=shift;
@@ -386,6 +382,7 @@
return $d;
}
+# Passt ein bestimmtes Datum auf das Schema in einer "Schedule"?
sub schedule_matches_day
{
my ($schedule,$day)=@_;
@@ -407,6 +404,8 @@
return $match;
}
+# Fuer eine bestimmte Timer-Logik den naechsten Aufruf berechnen (relativ komplexes Problem wegen der
+# vielen moeglichen Konfigurationen und Konstellationen)
sub set_next_call
{
my ($t,$debug)=@_; # der relevante Eintrag in %logic, und das Debugflag
@@ -431,17 +430,20 @@
for my $s (@{$schedule})
{
+ # Timereintrag pruefen und standardisieren
unless(ref $s eq 'HASH')
{
plugin_log($plugname, "Logiktimer zu Logik '$t' ist kein Hash oder Liste von Hashes");
next;
}
+
unless(defined $s->{time})
{
plugin_log($plugname, "Logiktimer zu Logik '$t' enthaelt mindestens einen Eintrag ohne Zeitangabe (time=>...)");
next;
}
+ # Eintrag pruefen und standardisieren
for my $k (keys %{$s})
{
unless($k=~/^(year|month|calendar_week|day_of_month|day_of_week|time)$/)
@@ -461,6 +463,33 @@
@{$s->{$k}}=sort @{$s->{$k}}; # alle Listen sortieren
}
+ # Expandieren periodischer Zeitangaben, das sind Zeitangaben der Form
+ # time=>'08:00+30min' - ab 08:00 alle 30min
+ # time=>'08:00+5min-09:00' - ab 08:00 alle 5min mit Ende 09:00
+ if(grep /\+/, @{$s->{time}})
+ {
+ my $newtime=[];
+ for my $ts (@{$s->{time}})
+ {
+ unless($ts=~/^(.*?)([0-9][0-9]):([0-9][0-9])\+([1-9][0-9]*)(m|h)(?:\-([0-9][0-9]):([0-9][0-9]))?(.*?)$/)
+ {
+ push @{$newtime}, $ts;
+ }
+ else
+ {
+ my ($head,$t1,$period,$t2h,$t2m,$tail)=($1,$2*60+$3,$4*($5 eq 'h'?60:1),$6,$7,$8);
+ my $t2 = (defined $t2h ? $t2h : 24)*60 + (defined $t2m ? $t2m : 0);
+
+ for(my $tm=$t1; $tm<=$t2; $tm+=$period)
+ {
+ push @{$newtime}, sprintf("$head%02d:%02d$tail",$tm/60,$tm%60);
+ }
+ }
+ }
+ @{$s->{time}} = sort @{$newtime};
+ plugin_log($plugname, "\$logic{$t} Aufrufzeiten: ".join " ", @{$newtime}) if $debug;
+ }
+
# Steht heute aus diesem Schedule noch ein Termin an?
next unless schedule_matches_day($s,$today) && $s->{time}[-1] gt $time_of_day;
@@ -511,11 +540,12 @@
}
}
-
# Es folgt die eigentliche Logik-Engine
+# Im wesentlichen Vorbesetzen von input und state, Aufrufen der Logik, Zurueckschreiben von state
sub execute_logic
{
- my ($t, $receive, $ga, $in)=@_; # Logikindex $t, Bustelegramm erhalten auf $ga mit Inhalt $in
+ my ($t, $receive, $ga, $in)=@_; # Logikindex $t, Bustelegramm erhalten auf $ga mit Inhalt $in
+ # $receive muss die direkten Gruppenadressen enthalten - Decodierung von Kuerzeln wird nicht vorgenommen
# als erstes definiere das Input-Array fuer die Logik
my $input=$in;
@@ -532,8 +562,6 @@
$input=();
for my $rec (@{$receive})
{
- my $rec=groupaddress($rec);
-
if($ga eq $rec)
{
push @{$input}, $in;
@@ -610,7 +638,6 @@
return $result;
}
-
# Umgang mit GA-Kurznamen und -Adressen
sub groupaddress
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-11 22:17:00 UTC (rev 811)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-12 08:21:46 UTC (rev 812)
@@ -97,7 +97,11 @@
elsif($event=~/bus/)
{
# nur auf Write-Telegramme reagieren
- return if $msg{apci} ne 'A_GroupValue_Write';
+ if($msg{apci} ne 'A_GroupValue_Write')
+ {
+ for my $k (keys %scene) { delete $scene{$k}; } # Hilfe fuer die Garbage Collection
+ return;
+ }
# Aufruf durch GA
my $ga=$msg{dst};
@@ -107,6 +111,7 @@
unless($plugin_info{$plugname.'__SceneLookup'}=~/(St|Rc)\($ga\)=>\'(.+?)\',/)
{
delete $plugin_subscribe{$ga}{$plugname}; # unbekannte GA
+ for my $k (keys %scene) { delete $scene{$k}; } # Hilfe fuer die Garbage Collection
return;
}
@@ -116,7 +121,11 @@
if($eibgaconf{$ga}{DPTSubId} eq '1.017')
{
# Szenennummer aus physikalischer Adresse ableiten falls DPTSubId==1.017
- return unless $msg{src}=~/[0-9]+\.[0-9]+\.([0-9]+)/;
+ unless($msg{src}=~/[0-9]+\.[0-9]+\.([0-9]+)/)
+ {
+ for my $k (keys %scene) { delete $scene{$k}; } # Hilfe fuer die Garbage Collection
+ return;
+ }
$n=$1;
}
elsif($scene{$sc}{store} eq $scene{$sc}{recall})
@@ -184,6 +193,7 @@
}
}
+for my $k (keys %scene) { delete $scene{$k}; } # Hilfe fuer die Garbage Collection
return unless $retval;
return $retval;
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-11 22:17:00 UTC (rev 811)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-12 08:21:46 UTC (rev 812)
@@ -87,18 +87,20 @@
},
# Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
- # 10. Eine Timer-Funktion. Hier eine einfache Zeitschaltuhr, die immer um 8 Uhr und um 10:00 am jeweils zweiten
- # Dienstag jedes Monats eine 1 auf Transmit sendet
- wecker => { transmit=>'10/1/15', timer=>{ time=>['08:00','10:00'], day_of_month=>[(8..14)], day_of_week=>'Di' }, translate => 1 },
+ # 10. Eine Timer-Funktion. Hier eine Zeitschaltuhr, die immer am jeweils zweiten Dienstag jedes Monats
+ # um 08:00, um 10:00, zwischen 09:00 und 09:30 alle 2min und zwischen 18:00 und 20:00 jede volle Stunde
+ # eine 1 auf Transmit sendet
+ wecker => { transmit=>'10/1/15', timer=>{ time=>['08:00','10:00','09:00+2m-09:30','18:00+1h-20:00'],
+ day_of_month=>[(8..14)], day_of_week=>'Di' }, translate => 1 },
# Logiken mit timer-Klausel weichen in mehreren Punkten von den bisherigen Logiken ab:
# * sie ignorieren die delay-Klausel und die cool-Klausel
- # * transmit_only_on_request funktioniert
- # * jeglicher Bustraffic auf receive-Adressen wird ignoriert. (Diese werden aber beim Timer-Aufruf abgefragt,
- # um das input-Array vorzubesetzen).
- # Als timer-Eintrag geht entweder ein einzelnes Hash timer=>{...} wie oben oder eine Liste solcher Eintraege
- # time=>[{...},{...},{...},...]. Jeder Eintrag MUSS eine Spezifikation time=>'XX:XX' enthalten (auch das darf wieder
- # eine Liste sein) und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
- # calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) enthalten.
+ # * Die transmit_only_on_request-Klausel funktioniert aber
+ # * jeglicher Bustraffic auf receive-Adressen wird ignoriert.
+ # * Evtl. spezifizierte receive-Adressen werden aber beim Timer-Aufruf abgefragt, um das input-Array vorzubesetzen).
+ # Als timer-Eintrag geht entweder ein einzelner Eintrag timer=>{...} wie oben oder eine Liste solcher Eintraege
+ # time=>[{...},{...},{...},...]. Jeder Eintrag MUSS eine Spezifikation time=>... (Varianten siehe Beispiel) enthalten
+ # und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
+ # calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) enthalten. Alle diese duerfen wieder Listen sein.
# 11. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
# und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-15 06:23:11
|
Revision: 820
http://openautomation.svn.sourceforge.net/openautomation/?rev=820&view=rev
Author: pfry
Date: 2012-05-15 06:23:03 +0000 (Tue, 15 May 2012)
Log Message:
-----------
Garbage Collection bereinigt plugin_info und plugin_subscribe um Eintraege fuer Plugins, die gar nicht existieren. USE AT YOUR OWN RISK!
Modified Paths:
--------------
wiregate/plugin/generic/MissGoogle.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Added Paths:
-----------
wiregate/plugin/generic/Garbage_Collection.pl
Added: wiregate/plugin/generic/Garbage_Collection.pl
===================================================================
--- wiregate/plugin/generic/Garbage_Collection.pl (rev 0)
+++ wiregate/plugin/generic/Garbage_Collection.pl 2012-05-15 06:23:03 UTC (rev 820)
@@ -0,0 +1,38 @@
+######################
+# Garbage Collection #
+######################
+# Wiregate-Plugin
+# (c) 2012 Fry under the GNU Public License
+
+# $plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
+
+my $retval='';
+chdir "/etc/wiregate/plugin/generic";
+
+# Cleanup plugin_subscribe
+my @plugins=<*.pl>;
+my $valid=join "|", map quotemeta, @plugins;
+for my $ga (keys %plugin_subscribe)
+{
+ my @delme=grep !/^($valid)/, keys %{$plugin_subscribe{$ga}};
+ for my $v (@delme)
+ {
+ delete $plugin_subscribe{$ga}{$v};
+ $retval.=$ga.'->'.$v.', ';
+ }
+}
+
+# Cleanup plugin_info
+push @plugins, "conf.d";
+my $valid=join "|", map quotemeta, @plugins;
+my @delme=grep !/^($valid)/, keys %plugin_info;
+for my $v (@delme)
+{
+ delete $plugin_info{$v};
+ $retval.=$v.', ';
+}
+
+$plugin_info{$plugname.'_cycle'}=1000;
+
+return $retval ? ('Geloescht: '.$retval) : undef;
+
Modified: wiregate/plugin/generic/MissGoogle.pl
===================================================================
--- wiregate/plugin/generic/MissGoogle.pl 2012-05-15 00:15:08 UTC (rev 819)
+++ wiregate/plugin/generic/MissGoogle.pl 2012-05-15 06:23:03 UTC (rev 820)
@@ -1,12 +1,15 @@
#!/usr/bin/perl -w
+# Ansagedateien fuer das Wiregate-Plugin Ansagen.pl in /var/lib/mpd/music/Ansagen schreiben
+# Quelle fuer die Dateien ist Google
+
use strict;
system "mkdir", "-p", "/var/lib/mpd/music/Ansagen";
chdir "/var/lib/mpd/music/Ansagen";
my %text=(
- "./Achtung, es wurde ein Einbruch detektiert. Die Polizei wird automatisch benachrichtigt.wav"=>"Achtung, es wurde ein Einbruch detektiert. Die Polizei wird automatisch benachrichtigt",
+ "./Achtung Einbruch.wav"=>"Achtung Einbruch Verlassen Sie sofort das Haus",
"./AufWiedersehen.wav"=>"Auf Wiedersehen",
"./Tschuess.wav"=>"tschuess",
"./Ciao.wav"=>"tschau",
@@ -45,7 +48,8 @@
"./Personen/Lynn.wav"=>"Lynn",
"./Personen/Michael.wav"=>"Michael",
"./Personen/Nils.wav"=>"Nils",
- "./Personen/Rita.wav"=>"Rita",
+ "./Personen/Rita.wav"=>"Ritah",
+ "./Personen/Jule.wav"=>"Jule",
"./Personen/Tanja.wav"=>"Tanja",
"./Personen/Uta.wav"=>"Uta",
"./Personen/Wigbert.wav"=>"Wigbert",
@@ -123,6 +127,8 @@
"./Zahlen/an.wav"=>"an",
"./Zahlen/auf.wav"=>"auf",
"./Zahlen/aus.wav"=>"aus",
+ "./Zahlen/ab.wav"=>"ab",
+ "./Zahlen/stop.wav"=>"stop",
"./Zahlen/c0.wav"=>"Null",
"./Zahlen/c1000.wav"=>"tausend",
"./Zahlen/c100.wav"=>"hundert",
@@ -218,7 +224,21 @@
"./Zeiten/Minuten.wav"=>"Minuten",
"./Zeiten/Sekunden.wav"=>"Sekunden",
"./Zeiten/Stunden.wav"=>"Stunden",
- "./Zeiten/Uhr.wav"=>"Uhr");
+ "./Zeiten/Uhr.wav"=>"Uhr",
+ "./Warnung/keine_Meldung.wav"=>"keine Meldung",
+ "./Warnung/Hinweis.wav"=>"Hinweis",
+ "./Warnung/Vorwarnung.wav"=>"Vorwarnung",
+ "./Warnung/Warnung.wav"=>"Warnung",
+ "./Warnung/Gefahr.wav"=>"Gefahr",
+ "./Warnung/Gefahr_hoch.wav"=>"Gefahr hoch",
+ "./Unwetter/Gewitter.wav"=>"Gewitter",
+ "./Unwetter/Glatteis.wav"=>"Glatteis",
+ "./Unwetter/Regen.wav"=>"Regen",
+ "./Unwetter/Schnee.wav"=>"Schnee",
+ "./Unwetter/Sturm.wav"=>"Sturm",
+ "./Unwetter/Temperatur.wav"=>"Temperatur",
+ "./Unwetter/Strassenglaette.wav"=>"Strassenglaette",
+);
for my $f (keys %text)
{
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-15 00:15:08 UTC (rev 819)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-15 06:23:03 UTC (rev 820)
@@ -28,9 +28,7 @@
# werden hier gar nicht gebraucht.
# 5. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an
- # die transmit-Adresse (hier gleich) geschickt. Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der
- # translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
- # Rueckgabewert spezifiziert.
+ # die transmit-Adresse (hier gleich) geschickt.
stair => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, },
# Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der translate-Routine.
# Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante
@@ -110,3 +108,6 @@
# in einen absoluten Wert umgewandelt
);
+
+
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-17 20:09:04
|
Revision: 824
http://openautomation.svn.sourceforge.net/openautomation/?rev=824&view=rev
Author: pfry
Date: 2012-05-17 20:08:58 +0000 (Thu, 17 May 2012)
Log Message:
-----------
holiday-Spezifikation im Timer ergaenzt
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-15 18:18:24 UTC (rev 823)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-17 20:08:58 UTC (rev 824)
@@ -7,43 +7,44 @@
#$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
-my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
-my $stime=time(); # Stoppuhr zum Nachverfolgen der Reaktionsgeschwindigkeit der Logikengine
-
# eibgaconf fixen falls nicht komplett indiziert
-if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
-{
- for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
- {
- $eibgaconf{$ga}{ga}=$ga;
- my $name=$eibgaconf{$ga}{name};
- next unless defined $name;
- $eibgaconf{$name}=$eibgaconf{$ga};
+# Kann ab Wiregate PL32 entfallen
+#if(!exists $eibgaconf{ZV_Uhrzeit})
+#{
+# for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
+# {
+# $eibgaconf{$ga}{ga}=$ga;
+# my $name=$eibgaconf{$ga}{name};
+# next unless defined $name;
+# $eibgaconf{$name}=$eibgaconf{$ga};
+#
+# next unless $name=~/^\s*(\S+)/;
+# my $short=$1;
+# $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
+#
+# $eibgaconf{$ga}{short}=$short;
+# $eibgaconf{$short}=$eibgaconf{$ga};
+# }
+#}
- next unless $name=~/^\s*(\S+)/;
- my $short=$1;
- $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
-
- $eibgaconf{$ga}{short}=$short;
- $eibgaconf{$short}=$eibgaconf{$ga};
- }
-}
-
# Tools und vorbesetzte Variablen fue die Logiken
sub limit { my ($lo,$x,$hi)=@_; return $x<$lo?$lo:($x>$hi?$hi:$x); }
-my $date=`/bin/date +"%W,%a,%u,%m,%d,%Y,%H,%M,%X"`;
-plugin_log($plugname, "Datum/Uhrzeit konnte nicht lesbar.") unless $date=~/^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/;
+my $date=`/bin/date +"%W,%a,%u,%m,%d,%Y,%j,%H,%M,%X"`;
+plugin_log($plugname, "Datum/Uhrzeit konnte nicht lesbar.") unless $date=~/^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/;
my $calendar_week=$1;
my $day_of_week=$2;
my $day_of_week_no=$3;
my $month=int($4);
my $day_of_month=int($5);
my $year=int($6);
-my $hour=int($7);
-my $minute=int($8);
-my $time_of_day=$9; # '08:30:43'
+my $day_of_year=int($7);
+my $hour=int($8);
+my $minute=int($9);
+my $time_of_day=$10; # '08:30:43'
my $weekend=($day_of_week_no>=6);
my $weekday=!$weekend;
+my $holiday=is_holiday($year,$day_of_year);
+my $workingday=(!$weekend && !$holiday);
my $day=($hour>7 && $hour<23);
my $night=!$day;
my $systemtime=time();
@@ -212,7 +213,6 @@
if(defined $result)
{
$retval.="$ga:Lesetelegramm -> \$logic{$t}{transmit}(memory) -> $ga:$result gesendet. " if $debug;
- $stime=time();
knx_write($ga, $result);
}
next;
@@ -255,14 +255,12 @@
unless(defined $result) # Resultat undef => nichts senden
{
$retval.="$ga:$in -> \$logic{$t}{receive}(Logik) -> nichts zu senden " if $debug;
- $stime=time();
next;
}
if($logic{$t}{transmit_only_on_request})
{
$retval.="$ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gespeichert " if $debug;
- $stime=time();
next;
}
@@ -272,13 +270,11 @@
$plugin_info{$plugname.'__'.$t.'_timer'}=$systemtime+$logic{$t}{delay};
$plugin_info{$plugname.'__'.$t.'_cool'}=time()+$logic{$t}{delay}+$logic{$t}{cool} if defined $logic{$t}{cool};
$retval.="$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result, zu senden in ".$logic{$t}{delay}."s " if $debug;
- $stime=time();
}
else
{
knx_write($transmit, $result);
$retval.="$msg{src} $ga:$in -> \$logic{$t}{receive}(Logik) -> $transmit:$result gesendet " if $debug;
- $stime=time();
# Cool-Periode starten
$plugin_info{$plugname.'__'.$t.'_cool'}=time()+$logic{$t}{cool} if defined $logic{$t}{cool};
@@ -315,7 +311,6 @@
$result=$plugin_info{$plugname.'_'.$t.'_result'};
$retval.="\$logic{$t} -> $transmit:".
(defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (delayed) " if $debug;
- $stime=time();
}
else
{
@@ -324,7 +319,6 @@
$result=execute_logic($t, groupaddress($logic{$t}{receive}), undef, undef);
$retval.="\$logic{$t} -> $transmit:".
(defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (Timer) " if $debug;
- $stime=time();
}
# Timer loeschen bzw. neu setzen
@@ -376,12 +370,57 @@
$d->{calendar_week} += $d->{day_of_week}==1;
$d->{day_of_month} = ($d->{day_of_month} % $days_in_month[$d->{month}])+1;
$d->{month} += $d->{day_of_month}==1;
- $d->{month}=1 if $d->{month}==13;
+ $d->{month} = 1 if $d->{month}==13;
+ $d->{day_of_year} = ($d->{day_of_year} % (365+$leapyear))+1;
$d->{year} += ($d->{day_of_month}==1 && $d->{month}==1);
+
+ add_day_info($d);
return $d;
}
+sub is_holiday
+{
+ my $Y=int(shift);
+ my $doy=int(shift);
+
+ # Schaltjahr?
+ my $leapyear = ($Y % 4)==0 && ($Y % 100!=0 || $Y % 400==0);
+
+ # Osterdatum berechnen (Algorithmus von Ron Mallen, Codefragment von Randy McLeary)
+ my $C = int($Y/100);
+ my $G = $Y%19;
+ my $K = int(($C - 17)/25);
+ my $I = ($C - int($C/4) - int(($C - $K)/3) + 19 * $G + 15)%30;
+ $I = $I - int($I/28) * (1 - int($I/28) * int(29/($I + 1)) * int((21 - $G)/11));
+ my $L = $I - ($Y + int($Y/4) + $I + 2 - $C + int($C/4))%7;
+ my $M = 3 + int(($L + 40)/44);
+ my $D = $L + 28 - 31 * int($M/4);
+ # diesjaehriger Ostersonntag ist $Y-$M-$D
+
+ # julianisches Osterdatum (Tag im Jahr) berechnen $Y-$J
+ my @days_before_month=(0,0,31,59+$leapyear,90+$leapyear,120+$leapyear,151+$leapyear,181+$leapyear,212+$leapyear,243+$leapyear,
+ 273+$leapyear,304+$leapyear,334+$leapyear);
+ my $J = $days_before_month[$M]+$D;
+
+ # Feiertagstabelle als Tageszahl im Jahr (1=1.Januar, 32=1.Februar usw.): 1.1., 1.5., 3.10., 25./26.12.
+ # und die auf Ostern bezogenen Kirchenfeiertage: Karfreitag, Ostern (2x), Christi Himmelfahrt, Pfingsten (2x), Fronleichnam
+ my @holidays=(1,121+$leapyear,276+$leapyear,359+$leapyear,360+$leapyear,$J-2,$J,$J+39,$J+49,$J+50,$J+60);
+ my $is_holiday = scalar(grep { $_==$doy } @holidays);
+
+ return $is_holiday;
+}
+
+sub add_day_info
+{
+ my $day=shift;
+
+ $day->{weekend}=($day->{day_of_week_no}>=6);
+ $day->{weekday}=!$day->{weekend};
+ $day->{holiday}=is_holiday($day->{year},$day->{day_of_year});
+ $day->{workingday}=(!$day->{weekend} && !$day->{holiday});
+}
+
# Passt ein bestimmtes Datum auf das Schema in einer "Schedule"?
sub schedule_matches_day
{
@@ -420,7 +459,10 @@
# Pflichtfeld ist lediglich time, die anderen duerfen auch entfallen.
# Jeder Wert darf ein Einzelwert oder eine Liste sein.
my $schedule=$logic{$t}{timer};
- my $today={year=>$year,month=>$month,day_of_month=>$day_of_month,calendar_week=>$calendar_week,day_of_week=>$day_of_week_no};
+ my $today={year=>$year,day_of_year=>$day_of_year,month=>$month,day_of_month=>$day_of_month,
+ calendar_week=>$calendar_week,day_of_week=>$day_of_week_no};
+ add_day_info($today);
+
my $time_of_day=`/bin/date +"%X"`;
# Schedule-Form standardisieren (alle Eintraege in Listenform setzen und Wochentage durch Zahlen ersetzen)
@@ -446,12 +488,12 @@
# Eintrag pruefen und standardisieren
for my $k (keys %{$s})
{
- unless($k=~/^(year|month|calendar_week|day_of_month|day_of_week|time)$/)
+ unless($k=~/^(year|month|calendar_week|day_of_year|day_of_month|day_of_week|holiday|weekend|weekday|workingday|time)$/)
{
- plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter Eintrag '$k'; erlaubt sind year, month, calendar_week, day_of_month, day_of_week, und Pflichteintrag ist time");
+ plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter Eintrag '$k'; erlaubt sind year, month, calendar_week, day_of_year, day_of_month, day_of_week, weekend, weekday, holiday, workingday, und Pflichteintrag ist time");
next;
}
-
+
unless(!ref $s->{$k} || ref $s->{$k} eq 'ARRAY')
{
plugin_log($plugname, "Logiktimer zu Logik '$t': '$k' muss auf Skalar oder Array ($k=>[...]) verweisen");
@@ -471,23 +513,30 @@
my $newtime=[];
for my $ts (@{$s->{time}})
{
- unless($ts=~/^(.*?)([0-9][0-9]):([0-9][0-9])\+([1-9][0-9]*)(m|h)(?:\-([0-9][0-9]):([0-9][0-9]))?(.*?)$/)
+ unless($ts=~/^(.*?)([0-9][0-9]):([0-9][0-9])\+([1-9][0-9]*)(m|h)(?:\-([0-9][0-9]):([0-9][0-9]))?$/)
{
- push @{$newtime}, $ts;
+ if($ts=~/^(.*?)([0-9][0-9]):([0-9][0-9])$/)
+ {
+ push @{$newtime}, sprintf("%02d:%02d",$2, $3);
+ }
+ else
+ {
+ plugin_log($plugname, "Ignoriere falschen time-Eintrag in \$logic{$t}{timer}: '$ts' (Format ist nicht XX:XX)");
+ next;
+ }
}
else
{
- my ($head,$t1,$period,$t2h,$t2m,$tail)=($1,$2*60+$3,$4*($5 eq 'h'?60:1),$6,$7,$8);
- my $t2 = (defined $t2h ? $t2h : 24)*60 + (defined $t2m ? $t2m : 0);
-
+ my ($head,$t1,$period,$t2)=($1,$2*60+$3,$4*($5 eq 'h' ? 60 : 1),(defined $6 ? ($6*60+$7) : 24*60));
+
for(my $tm=$t1; $tm<=$t2; $tm+=$period)
{
- push @{$newtime}, sprintf("$head%02d:%02d$tail",$tm/60,$tm%60);
+ push @{$newtime}, sprintf("%02d:%02d",$tm/60,$tm%60);
}
}
}
@{$s->{time}} = sort @{$newtime};
- plugin_log($plugname, "\$logic{$t} Aufrufzeiten: ".join " ", @{$newtime}) if $debug;
+# plugin_log($plugname, "\$logic{$t} Aufrufzeiten: ".join " ", @{$newtime});
}
# Steht heute aus diesem Schedule noch ein Termin an?
@@ -522,8 +571,9 @@
if(defined $nextcall)
{
- plugin_log($plugname, "Naechster Aufruf der Logik '$t' um $nextcall".($days_until_nextcall?" in $days_until_nextcall Tagen.":"."))
- if $debug;
+ my $daytext='';
+ $daytext = ($days_until_nextcall==1 ? " morgen" : " in $days_until_nextcall Tagen") if $days_until_nextcall;
+ plugin_log($plugname, "Naechster Aufruf der Timer-Logik '$t'$daytext um $nextcall."); # if $debug;
# Zeitdelta zu jetzt berechnen
my $seconds=3600*(substr($nextcall,0,2)-substr($time_of_day,0,2))
@@ -675,38 +725,3 @@
}
}
-sub shortname
-{
- my $gas=shift;
-
- return unless defined $gas;
- return $gas unless $use_short_names;
-
- if(ref $gas)
- {
- my $sh=[];
- for my $ga (@{$gas})
- {
- if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
- {
- push @{$sh}, $eibgaconf{$ga}{short};
- }
- else
- {
- push @{$sh}, $ga;
- }
- }
- return $sh;
- }
- else
- {
- my $sh=$gas;
-
- if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
- {
- $sh=$eibgaconf{$gas}{short};
- }
-
- return $sh;
- }
-}
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-15 18:18:24 UTC (rev 823)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-17 20:08:58 UTC (rev 824)
@@ -3,7 +3,7 @@
# Logikprozessor.pl - Konfiguration
#
-$eibd_backend_address='1.1.254'; # eigene Adresse zur Vermeidung von Zirkellogiken '0.0.0';
+$eibd_backend_address='0.0.0'; # eigene Adresse zur Vermeidung von Zirkellogiken, ist oft auch '1.1.254'
%logic=(
# 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
@@ -100,14 +100,13 @@
# und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
# calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) enthalten. Alle diese duerfen wieder Listen sein.
- # 11. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
- # und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
+ # 11. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Verwendet man GA-Namen mit eindeutigem Kuerzel
+ # (Kuerzel=erstes Wort des Namens), so kann man ab Wiregate PL32 auch diese Kuerzel verwenden.
+ # Mit meinem (Frys) Namensschema funktioniert bspw. das folgende:
D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
translate => sub { limit(0,$state+20*$input,100); }, },
- # ist doch leserlicher, oder? Hier wird ein relativer Dimmwert durch Skalierung und Summierung
- # in einen absoluten Wert umgewandelt
+ # ist doch leserlicher, oder? Hier wird ein relativer Dimmwert (LR=Licht relativ, SZ=Schlafzimmer) durch Skalierung
+ # und Summierung in einen absoluten Wert umgewandelt
);
-
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-17 20:40:18
|
Revision: 825
http://openautomation.svn.sourceforge.net/openautomation/?rev=825&view=rev
Author: pfry
Date: 2012-05-17 20:40:11 +0000 (Thu, 17 May 2012)
Log Message:
-----------
Bereiche als Datumsspezifikationen in der Timer-Funktion
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-17 20:08:58 UTC (rev 824)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-17 20:40:11 UTC (rev 825)
@@ -502,7 +502,29 @@
$s->{$k}=[$s->{$k}] unless ref $s->{$k} eq 'ARRAY'; # alle Kategorien in Listenform
map $_=$weekday{$_}, @{$s->{$k}} if $k eq 'day_of_week'; # Wochentage in Zahlenform
- @{$s->{$k}}=sort @{$s->{$k}}; # alle Listen sortieren
+
+ # Expandieren von Bereichen, z.B. month=>'3-5'
+ if($k ne 'time' && grep /\-/, @{$s->{$k}})
+ {
+ my $newlist=[];
+ for my $ks (@{$s->{$k}})
+ {
+ if($ks=~/^([0-9]+)\-([0-9]+)$/)
+ {
+ push @{$newlist}, ($1..$2);
+ }
+ else
+ {
+ push @{$newlist}, $ks;
+ }
+ }
+ @{$s->{$k}} = sort @{$newlist};
+# plugin_log($plugname, "\$logic{$t} Aufrufdaten $k: ".join " ", @{$s->{$k}});
+ }
+ else
+ {
+ @{$s->{$k}}=sort @{$s->{$k}}; # alle Listen sortieren
+ }
}
# Expandieren periodischer Zeitangaben, das sind Zeitangaben der Form
@@ -553,7 +575,7 @@
my $schedules_done=0;
# falls nextcall noch nicht definiert, geht es jetzt um den naechsten Tag mit Termin
- until($schedules_done || defined $nextcall)
+ until($schedules_done || defined $nextcall || $days_until_nextcall>5000) # maximal ca. 15 Jahre suchen
{
$schedules_done=1;
$days_until_nextcall++;
@@ -572,7 +594,8 @@
if(defined $nextcall)
{
my $daytext='';
- $daytext = ($days_until_nextcall==1 ? " morgen" : " in $days_until_nextcall Tagen") if $days_until_nextcall;
+ my $datum="$today->{day_of_month}\.$today->{month}\.$today->{year}";
+ $daytext = ($days_until_nextcall==1 ? " morgen" : " in $days_until_nextcall Tagen, am $datum,") if $days_until_nextcall;
plugin_log($plugname, "Naechster Aufruf der Timer-Logik '$t'$daytext um $nextcall."); # if $debug;
# Zeitdelta zu jetzt berechnen
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-17 20:08:58 UTC (rev 824)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-17 20:40:11 UTC (rev 825)
@@ -62,11 +62,13 @@
# 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 3; } },
- # Die Variablen $day_of_week (Mo...So), $calendar_week, $day_of_month (01-31), $month (01-12), $year (2012),
- # $weekend (0 oder 1), $weekday (= nicht $weekend), $time_of_day ("08:34:02"),
+ # Die Variablen $day_of_week (Mo...So), $calendar_week, $day_of_month (01-31), $month (01-12), $day_of_year (1-366),
+ # $year (2012), $weekend (0 oder 1), $weekday (= nicht $weekend), $holiday (0 oder 1),
+ # $workingday (=nicht $weekend und nicht $holiday), $time_of_day ("08:34:02"),
# $hour ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt)
# sind f\xFCr diese Logiken vorbesetzt (bitte nicht darauf schreiben, koennte unverhergesehene Auswirkungen
- # auf andere Logiken haben).
+ # auf andere Logiken haben). $holiday beruecksichtigt dabei Neujahr, 1. Mai,
+ # 3. Oktober, Weihnachten/Ostern/Pfingsten, Christi Himmelfahrt und Fronleichnam als Feiertage.
# 8. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
# wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
@@ -95,10 +97,17 @@
# * Die transmit_only_on_request-Klausel funktioniert aber
# * jeglicher Bustraffic auf receive-Adressen wird ignoriert.
# * Evtl. spezifizierte receive-Adressen werden aber beim Timer-Aufruf abgefragt, um das input-Array vorzubesetzen).
- # Als timer-Eintrag geht entweder ein einzelner Eintrag timer=>{...} wie oben oder eine Liste solcher Eintraege
- # time=>[{...},{...},{...},...]. Jeder Eintrag MUSS eine Spezifikation time=>... (Varianten siehe Beispiel) enthalten
- # und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
- # calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) enthalten. Alle diese duerfen wieder Listen sein.
+ # Weitere Infos zu Timer-Spezifikationen
+ # * Als timer-Eintrag geht entweder ein einzelner Eintrag timer=>{...} wie oben oder eine Liste solcher Eintraege
+ # time=>[{...},{...},{...},...].
+ # * Jeder Eintrag MUSS eine Spezifikation time=>... (Varianten siehe Beispiel oben) enthalten.
+ # * Jeder Eintrag DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_year,
+ # day_of_month, calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) enthalten. Solche Eintr\xE4ge duerfen
+ # Einzelwerte sein (year=>2012), Bereiche (day_of_month=>'8-14') oder auch wieder Listen von Werten/Bereichen.
+ # als auch Listen von sein.
+ # * Jeder Eintrag DARF zusaetzliche binaere (0 oder 1) Einschraenkungen enthalten: weekend, weekday,
+ # holiday und workingday (=weder holiday noch weekend). holiday beruecksichtigt dabei Neujahr, 1. Mai,
+ # 3. Oktober, Weihnachten/Ostern/Pfingsten, Christi Himmelfahrt und Fronleichnam als Feiertage.
# 11. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Verwendet man GA-Namen mit eindeutigem Kuerzel
# (Kuerzel=erstes Wort des Namens), so kann man ab Wiregate PL32 auch diese Kuerzel verwenden.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-18 05:05:39
|
Revision: 828
http://openautomation.svn.sourceforge.net/openautomation/?rev=828&view=rev
Author: pfry
Date: 2012-05-18 05:05:32 +0000 (Fri, 18 May 2012)
Log Message:
-----------
Weitere Parameter "date" f?\195?\188r Timer, Wort "Achtung" f?\195?\188r MissGoogle
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/MissGoogle.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-17 20:56:56 UTC (rev 827)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-18 05:05:32 UTC (rev 828)
@@ -48,6 +48,7 @@
my $day=($hour>7 && $hour<23);
my $night=!$day;
my $systemtime=time();
+my $date=sprintf("%02d/%02d",$month,$day_of_month);
# Konfigurationsfile einlesen
my $eibd_backend_address='1.1.254';
@@ -373,7 +374,7 @@
$d->{month} = 1 if $d->{month}==13;
$d->{day_of_year} = ($d->{day_of_year} % (365+$leapyear))+1;
$d->{year} += ($d->{day_of_month}==1 && $d->{month}==1);
-
+
add_day_info($d);
return $d;
@@ -413,12 +414,13 @@
sub add_day_info
{
- my $day=shift;
+ my $d=shift;
- $day->{weekend}=($day->{day_of_week_no}>=6);
- $day->{weekday}=!$day->{weekend};
- $day->{holiday}=is_holiday($day->{year},$day->{day_of_year});
- $day->{workingday}=(!$day->{weekend} && !$day->{holiday});
+ $d->{weekend}=($d->{day_of_week_no}>=6);
+ $d->{weekday}=!$d->{weekend};
+ $d->{holiday}=is_holiday($d->{year},$d->{day_of_year});
+ $d->{workingday}=(!$d->{weekend} && !$d->{holiday});
+ $d->{date} = sprintf("%02d/%02d",$d->{month},$d->{day_of_month});
}
# Passt ein bestimmtes Datum auf das Schema in einer "Schedule"?
@@ -443,130 +445,172 @@
return $match;
}
-# Fuer eine bestimmte Timer-Logik den naechsten Aufruf berechnen (relativ komplexes Problem wegen der
-# vielen moeglichen Konfigurationen und Konstellationen)
-sub set_next_call
+
+sub standardize_and_expand_single_schedule
{
- my ($t,$debug)=@_; # der relevante Eintrag in %logic, und das Debugflag
- my $nextcall=undef;
- my $days_until_nextcall=0;
-
- # $logic{$t}{timer} ist eine Liste oder ein einzelner Eintrag
- # jeder solche Eintrag ist ein Hash im Format
- # {day_of_month=>[(1..7)],day_of_week=>'Mo',time=>['08:30','09:20']}
- # das gerade genannte Beispiel bedeutet "jeden Monat jeweils der erster Montag, 8:30 oder 9:20"
- # verwendbare Klauseln sind year, month, day_of_month, calendar_week, day_of_week und time
- # Pflichtfeld ist lediglich time, die anderen duerfen auch entfallen.
- # Jeder Wert darf ein Einzelwert oder eine Liste sein.
- my $schedule=$logic{$t}{timer};
- my $today={year=>$year,day_of_year=>$day_of_year,month=>$month,day_of_month=>$day_of_month,
- calendar_week=>$calendar_week,day_of_week=>$day_of_week_no};
- add_day_info($today);
-
- my $time_of_day=`/bin/date +"%X"`;
-
- # Schedule-Form standardisieren (alle Eintraege in Listenform setzen und Wochentage durch Zahlen ersetzen)
- # dabei gleich schauen, ob HEUTE noch ein Termin ansteht
- $schedule=[$schedule] if ref $schedule eq 'HASH';
+ my ($t,$s)=@_;
+ my @days_in_month=(0,31,29,31,30,31,30,31,31,30,31,30,31); # hier ist jedes Jahr ein Schaltjahr
my %weekday=(Mo=>1,Mo=>1,Mon=>1,Di=>2,Tu=>2,Tue=>2,Mi=>3,We=>3,Wed=>3,Do=>4,Th=>4,Thu=>4,Fr=>5,Fri=>5,Sa=>6,Sat=>6,So=>7,Su=>7,Sun=>7);
- for my $s (@{$schedule})
+ # Timereintrag pruefen und standardisieren
+ unless(ref $s eq 'HASH')
{
- # Timereintrag pruefen und standardisieren
- unless(ref $s eq 'HASH')
+ plugin_log($plugname, "Logiktimer zu Logik '$t' ist kein Hash oder Liste von Hashes");
+ next;
+ }
+
+ unless(defined $s->{time})
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t' enthaelt mindestens einen Eintrag ohne Zeitangabe (time=>...)");
+ next;
+ }
+
+ # Eintrag pruefen und standardisieren
+ for my $k (keys %{$s})
+ {
+ unless($k=~/^(year|month|calendar_week|day_of_year|day_of_month|day_of_week|date|holiday|weekend|weekday|workingday|time)$/)
{
- plugin_log($plugname, "Logiktimer zu Logik '$t' ist kein Hash oder Liste von Hashes");
+ plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter Eintrag '$k'; erlaubt sind year, month, calendar_week, day_of_year, day_of_month, day_of_week, date, weekend, weekday, holiday, workingday, und Pflichteintrag ist time");
next;
}
- unless(defined $s->{time})
+ if($k=~/^(holiday|weekend|weekday|workingday)$/ && $s->{$k}!~/^(0|1)$/)
{
- plugin_log($plugname, "Logiktimer zu Logik '$t' enthaelt mindestens einen Eintrag ohne Zeitangabe (time=>...)");
+ plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter Wert '$k\->$s->{$k}': erlaubt sind 0 und 1");
next;
- }
-
- # Eintrag pruefen und standardisieren
- for my $k (keys %{$s})
+ }
+
+ unless(!ref $s->{$k} || ref $s->{$k} eq 'ARRAY')
{
- unless($k=~/^(year|month|calendar_week|day_of_year|day_of_month|day_of_week|holiday|weekend|weekday|workingday|time)$/)
+ plugin_log($plugname, "Logiktimer zu Logik '$t': '$k' muss auf Skalar oder Array ($k=>[...]) verweisen");
+ next;
+ }
+
+ $s->{$k}=[$s->{$k}] unless ref $s->{$k} eq 'ARRAY'; # alle Kategorien in Listenform
+
+ if($k eq 'day_of_week')
+ {
+ for my $wd (sort { length($b) cmp length($a) } keys %weekday)
{
- plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter Eintrag '$k'; erlaubt sind year, month, calendar_week, day_of_year, day_of_month, day_of_week, weekend, weekday, holiday, workingday, und Pflichteintrag ist time");
- next;
+ foreach (@{$s->{$k}}) { s/$wd/$weekday{$wd}/gie } # Wochentage in Zahlenform
}
-
- unless(!ref $s->{$k} || ref $s->{$k} eq 'ARRAY')
+ }
+
+ # Expandieren von Bereichen, z.B. month=>'3-5'
+ if($k!~/^(time|date)$/ && grep /\-/, @{$s->{$k}})
+ {
+ my $newlist=[];
+ for my $ks (@{$s->{$k}})
{
- plugin_log($plugname, "Logiktimer zu Logik '$t': '$k' muss auf Skalar oder Array ($k=>[...]) verweisen");
- next;
- }
-
- $s->{$k}=[$s->{$k}] unless ref $s->{$k} eq 'ARRAY'; # alle Kategorien in Listenform
-
- if($k eq 'day_of_week')
- {
- for my $wd (sort { length($b) cmp length($a) } keys %weekday)
+ if($ks=~/^([0-9]+)\-([0-9]+)$/)
{
- foreach (@{$s->{$k}}) { s/$wd/$weekday{$wd}/gie } # Wochentage in Zahlenform
+ push @{$newlist}, ($1..$2);
}
+ else
+ {
+ push @{$newlist}, $ks;
+ }
}
-
- # Expandieren von Bereichen, z.B. month=>'3-5'
- if($k ne 'time' && grep /\-/, @{$s->{$k}})
+ @{$s->{$k}} = sort @{$newlist};
+# plugin_log($plugname, "\$logic{$t} Aufrufdaten $k: ".join " ", @{$s->{$k}});
+ }
+ elsif($k eq 'date')
+ {
+ my $newlist=[];
+ for my $ks (@{$s->{date}})
{
- my $newlist=[];
- for my $ks (@{$s->{$k}})
+ if($ks=~/^([0-9]+)\/([0-9]+)\-([0-9]+)\/([0-9]+)$/)
{
- if($ks=~/^([0-9]+)\-([0-9]+)$/)
+ my ($m1,$d1,$m2,$d2)=($1,$2,$3,$4);
+ while($m1<$m2 || ($m1==$m2 && $d1<$d2))
{
- push @{$newlist}, ($1..$2);
+ push @{$newlist}, sprintf("%02d\/%02d",$m1,$d1);
+ if($d1==$days_in_month[$m1]) { $m1++; $d1=1; } else { $d1++; }
}
- else
- {
- push @{$newlist}, $ks;
- }
}
- @{$s->{$k}} = sort @{$newlist};
-# plugin_log($plugname, "\$logic{$t} Aufrufdaten $k: ".join " ", @{$s->{$k}});
- }
- else
- {
- @{$s->{$k}}=sort @{$s->{$k}}; # alle Listen sortieren
+ elsif($ks=~s/([0-9]+)\/([0-9]+)/sprintf("%02d\/%02d",$1,$2)/ge)
+ {
+ push @{$newlist}, $ks;
+ }
+ else
+ {
+ plugin_log($plugname, "Logiktimer zu Logik '$t': unerlaubte Datumsangabe date->$ks (erlaubt sind Einzeleintraege wie '02/03' oder Bereich wie '02/28-03/15')");
+ }
}
+ @{$s->{date}} = sort @{$newlist};
+# plugin_log($plugname, "\$logic{$t} Aufrufdaten date: ".join " ", @{$s->{date}});
}
-
- # Expandieren periodischer Zeitangaben, das sind Zeitangaben der Form
- # time=>'08:00+30min' - ab 08:00 alle 30min
- # time=>'08:00+5min-09:00' - ab 08:00 alle 5min mit Ende 09:00
- if(grep /\+/, @{$s->{time}})
+ else
{
- my $newtime=[];
- for my $ts (@{$s->{time}})
+ @{$s->{$k}}=sort @{$s->{$k}}; # alle Listen sortieren
+ }
+ }
+
+ # Expandieren periodischer Zeitangaben, das sind Zeitangaben der Form
+ # time=>'08:00+30min' - ab 08:00 alle 30min
+ # time=>'08:00+5min-09:00' - ab 08:00 alle 5min mit Ende 09:00
+ if(grep /\+/, @{$s->{time}})
+ {
+ my $newtime=[];
+ for my $ts (@{$s->{time}})
+ {
+ unless($ts=~/^(.*?)([0-9][0-9]):([0-9][0-9])\+([1-9][0-9]*)(m|h)(?:\-([0-9][0-9]):([0-9][0-9]))?$/)
{
- unless($ts=~/^(.*?)([0-9][0-9]):([0-9][0-9])\+([1-9][0-9]*)(m|h)(?:\-([0-9][0-9]):([0-9][0-9]))?$/)
+ if($ts=~/^(.*?)([0-9][0-9]):([0-9][0-9])$/)
{
- if($ts=~/^(.*?)([0-9][0-9]):([0-9][0-9])$/)
- {
- push @{$newtime}, sprintf("%02d:%02d",$2, $3);
- }
- else
- {
- plugin_log($plugname, "Ignoriere falschen time-Eintrag in \$logic{$t}{timer}: '$ts' (Format ist nicht XX:XX)");
- next;
- }
+ push @{$newtime}, sprintf("%02d:%02d",$2, $3);
}
else
{
- my ($head,$t1,$period,$t2)=($1,$2*60+$3,$4*($5 eq 'h' ? 60 : 1),(defined $6 ? ($6*60+$7) : 24*60));
-
- for(my $tm=$t1; $tm<=$t2; $tm+=$period)
- {
- push @{$newtime}, sprintf("%02d:%02d",$tm/60,$tm%60);
- }
+ plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter time-Eintrag '$ts' (erlaubt sind Eintraege wie '14:05' oder '07:30+30m-14:30' oder '07:30+5m-08:00')");
+ next;
}
}
- @{$s->{time}} = sort @{$newtime};
+ else
+ {
+ my ($head,$t1,$period,$t2)=($1,$2*60+$3,$4*($5 eq 'h' ? 60 : 1),(defined $6 ? ($6*60+$7) : 24*60));
+
+ for(my $tm=$t1; $tm<=$t2; $tm+=$period)
+ {
+ push @{$newtime}, sprintf("%02d:%02d",$tm/60,$tm%60);
+ }
+ }
+ }
+ @{$s->{time}} = sort @{$newtime};
# plugin_log($plugname, "\$logic{$t} Aufrufzeiten: ".join " ", @{$newtime});
- }
+ }
+}
+
+
+# Fuer eine bestimmte Timer-Logik den naechsten Aufruf berechnen (relativ komplexes Problem wegen der
+# vielen moeglichen Konfigurationen und Konstellationen)
+sub set_next_call
+{
+ my ($t,$debug)=@_; # der relevante Eintrag in %logic, und das Debugflag
+ my $nextcall=undef;
+ my $days_until_nextcall=0;
+
+ # $logic{$t}{timer} ist eine Liste oder ein einzelner Eintrag
+ # jeder solche Eintrag ist ein Hash im Format
+ # {day_of_month=>[(1..7)],day_of_week=>'Mo',time=>['08:30','09:20']}
+ # das gerade genannte Beispiel bedeutet "jeden Monat jeweils der erster Montag, 8:30 oder 9:20"
+ # verwendbare Klauseln sind year, month, day_of_month, calendar_week, day_of_week und time
+ # Pflichtfeld ist lediglich time, die anderen duerfen auch entfallen.
+ # Jeder Wert darf ein Einzelwert oder eine Liste sein.
+ my $schedule=$logic{$t}{timer};
+ my $today={year=>$year,day_of_year=>$day_of_year,month=>$month,day_of_month=>$day_of_month,
+ calendar_week=>$calendar_week,day_of_week=>$day_of_week_no};
+ add_day_info($today);
+
+ my $time_of_day=`/bin/date +"%X"`;
+
+ # Schedule-Form standardisieren (alle Eintraege in Listenform setzen und Wochentage durch Zahlen ersetzen)
+ # dabei gleich schauen, ob HEUTE noch ein Termin ansteht
+ $schedule=[$schedule] if ref $schedule eq 'HASH';
+
+ for my $s (@{$schedule})
+ {
+ standardize_and_expand_single_schedule($t,$s);
# Steht heute aus diesem Schedule noch ein Termin an?
next unless schedule_matches_day($s,$today) && $s->{time}[-1] gt $time_of_day;
Modified: wiregate/plugin/generic/MissGoogle.pl
===================================================================
--- wiregate/plugin/generic/MissGoogle.pl 2012-05-17 20:56:56 UTC (rev 827)
+++ wiregate/plugin/generic/MissGoogle.pl 2012-05-18 05:05:32 UTC (rev 828)
@@ -12,6 +12,7 @@
"./Achtung Einbruch.wav"=>"Achtung Einbruch Verlassen Sie sofort das Haus",
"./AufWiedersehen.wav"=>"Auf Wiedersehen",
"./Tschuess.wav"=>"tschuess",
+ "./Achtung.wav"=>"Achtung",
"./Ciao.wav"=>"tschau",
"./Bitte Schluesselbund ans Schluesselbrett haengen.wav"=>"Bitte Schluesselbund ans Schluesselbrett haengen",
"./Der Trockner ist fertig.wav"=>"Der Trockner ist fertig",
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-17 20:56:56 UTC (rev 827)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-18 05:05:32 UTC (rev 828)
@@ -63,7 +63,7 @@
# 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 3; } },
# Die Variablen $day_of_week (Mo...So), $calendar_week, $day_of_month (01-31), $month (01-12), $day_of_year (1-366),
- # $year (2012), $weekend (0 oder 1), $weekday (= nicht $weekend), $holiday (0 oder 1),
+ # $year (2012), $date ('01/01'-'12/31'), $weekend (0 oder 1), $weekday (= nicht $weekend), $holiday (0 oder 1),
# $workingday (=nicht $weekend und nicht $holiday), $time_of_day ("08:34:02"),
# $hour ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt)
# sind f\xFCr diese Logiken vorbesetzt (bitte nicht darauf schreiben, koennte unverhergesehene Auswirkungen
@@ -91,7 +91,7 @@
# um 08:00, um 10:00, zwischen 09:00 und 09:30 alle 2min und zwischen 18:00 und 20:00 jede volle Stunde
# eine 1 auf Transmit sendet
wecker => { transmit=>'10/1/15', timer=>{ time=>['08:00','10:00','09:00+2m-09:30','18:00+1h-20:00'],
- day_of_month=>[(8..14)], day_of_week=>'Di' }, translate => 1 },
+ day_of_month=>'8..14', day_of_week=>'Di' }, translate => 1 },
# Logiken mit timer-Klausel weichen in mehreren Punkten von den bisherigen Logiken ab:
# * sie ignorieren die delay-Klausel und die cool-Klausel
# * Die transmit_only_on_request-Klausel funktioniert aber
@@ -102,9 +102,9 @@
# time=>[{...},{...},{...},...].
# * Jeder Eintrag MUSS eine Spezifikation time=>... (Varianten siehe Beispiel oben) enthalten.
# * Jeder Eintrag DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_year,
- # day_of_month, calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) enthalten. Solche Eintr\xE4ge duerfen
- # Einzelwerte sein (year=>2012), Bereiche (day_of_month=>'8-14') oder auch wieder Listen von Werten/Bereichen.
- # als auch Listen von sein.
+ # day_of_month, calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) und date (Format 'MM/DD') enthalten.
+ # Solche Eintr\xE4ge duerfen Einzelwerte sein (year=>2012), Bereiche (day_of_month=>'8-14') oder auch wieder Listen
+ # von Werten/Bereichen.
# * Jeder Eintrag DARF zusaetzliche binaere (0 oder 1) Einschraenkungen enthalten: weekend, weekday,
# holiday und workingday (=weder holiday noch weekend). holiday beruecksichtigt dabei Neujahr, 1. Mai,
# 3. Oktober, Weihnachten/Ostern/Pfingsten, Christi Himmelfahrt und Fronleichnam als Feiertage.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-19 21:44:58
|
Revision: 831
http://openautomation.svn.sourceforge.net/openautomation/?rev=831&view=rev
Author: pfry
Date: 2012-05-19 21:44:51 +0000 (Sat, 19 May 2012)
Log Message:
-----------
Auf read-Telegramme wird nun mit response gantwortet
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-18 22:00:58 UTC (rev 830)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-19 21:44:51 UTC (rev 831)
@@ -165,8 +165,7 @@
if($event=~/bus/)
{
- # Bustraffic bedienen - nur Schreibzugriffe der iButtons interessieren
- return unless $msg{apci}=~/A_GroupValue_(Write|Read)/;
+ return if $msg{apci} eq "A_GroupValue_Response";
my $ga=$msg{dst};
my $in=$msg{value};
@@ -214,13 +213,13 @@
if(defined $result)
{
$retval.="$ga:Lesetelegramm -> \$logic{$t}{transmit}(memory) -> $ga:$result gesendet. " if $debug;
- knx_write($ga, $result);
+ knx_write($ga, $result, undef, 0x40); # response, DPT aus eibga.conf
}
next;
}
elsif(!$receive_ga) # Receive geht vor - bei Timer-Logiken ist receive_ga immer 0
{
- if(defined $in) # Write-Telegramm: das waren moeglicherweise wir selbst, also nicht antworten
+ if(defined $in) # Write/Response-Telegramm: das waren moeglicherweise wir selbst, also nicht antworten
{
$plugin_info{$plugname.'_'.$t.'_result'}=$in; # einfach Input ablegen
}
@@ -232,6 +231,7 @@
}
}
+ next unless $msg{apci} eq "A_GroupValue_Write" && $receive_ga;
# Wir wissen ab hier: Es liegt ein Write-Telegramm auf einer der receive-Adressen vor
# Nebenbei berechnen wir noch zwei Flags, die Zirkelkommunikation verhindern sollen
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-18 22:00:58 UTC (rev 830)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-05-19 21:44:51 UTC (rev 831)
@@ -103,8 +103,8 @@
# * Jeder Eintrag MUSS eine Spezifikation time=>... (Varianten siehe Beispiel oben) enthalten.
# * Jeder Eintrag DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_year,
# day_of_month, calendar_week, day_of_week (Mo...So oder Mon...Sun oder 1...7) und date (Format 'MM/DD') enthalten.
- # Solche Eintr\xE4ge duerfen Einzelwerte sein (year=>2012), Bereiche (day_of_month=>'8-14') oder auch wieder Listen
- # von Werten/Bereichen.
+ # Solche Eintr\xE4ge duerfen Einzelwerte sein (year=>2012), Bereiche (day_of_month=>'8-14', date=>'02/12-03/15')
+ # oder auch wieder Listen von Werten/Bereichen.
# * Jeder Eintrag DARF zusaetzliche binaere (0 oder 1) Einschraenkungen enthalten: weekend, weekday,
# holiday und workingday (=weder holiday noch weekend). holiday beruecksichtigt dabei Neujahr, 1. Mai,
# 3. Oktober, Weihnachten/Ostern/Pfingsten, Christi Himmelfahrt und Fronleichnam als Feiertage.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-05-25 07:14:16
|
Revision: 837
http://openautomation.svn.sourceforge.net/openautomation/?rev=837&view=rev
Author: pfry
Date: 2012-05-25 07:14:08 +0000 (Fri, 25 May 2012)
Log Message:
-----------
Szenencontroller: Anpassungen auf PL32 (Vereinfachungen moeglich)
Logikprozessor: Patch u.a. fuer den Fall, dass waehrend einer Timer-Berechnung ein Plugin-Timeout kommt
Ansagen: jetzt mit integriertem Radio!
Modified Paths:
--------------
wiregate/plugin/generic/Ansagen.pl
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/Szenencontroller.pl
wiregate/plugin/generic/conf.d/Ansagen.conf
Modified: wiregate/plugin/generic/Ansagen.pl
===================================================================
--- wiregate/plugin/generic/Ansagen.pl 2012-05-20 21:10:59 UTC (rev 836)
+++ wiregate/plugin/generic/Ansagen.pl 2012-05-25 07:14:08 UTC (rev 837)
@@ -18,6 +18,8 @@
my @additional_subscriptions=();
my %mpdhost=('default'=>'127.0.0.1/6600');
my $mode='mpd';
+my $radioga=undef;
+my %stations=(); # Internet-Radiostationen
# Konfigurationsfile einlesen
my $conf=$plugname; $conf=~s/\.pl$/.conf/;
@@ -61,16 +63,23 @@
for my $pat (keys %channels)
{
- next if $pat eq 'default';
+ next if $pat eq 'default' || $name!~/$pat/;
+ $plugin_subscribe{$ga}{$plugname}=1;
+ $gas{$channels{$pat}}++;
- if($name=~/$pat/)
+ if($name=~/$radioga/)
{
- $plugin_subscribe{$ga}{$plugname}=1;
- $gas{$channels{$pat}}++;
+ speak($channels{$pat},$name,'AUS');
}
}
+
}
+ for my $channel (values %channels)
+ {
+ $plugin_info{$plugname.'_radio_'.$channel}='AUS';
+ }
+
for my $ga (@additional_subscriptions)
{
$plugin_subscribe{$ga}{$plugname}=1;
@@ -101,35 +110,59 @@
}
}
+ # Radiosender bei dpt 16 (Text=Sendername)
+ if($mode eq 'mpd' && $name=~/$radioga/ && $dpt=~/^16/)
+ {
+ $val=~s/\000*$//; # streiche Nullen am Ende
+ return speak($channel, $name, $val);
+ }
+
# Hole alle verfuegbaren Durchsagedateien
my $find=checkexec('find');
my @speech=split /\n/, `$find . -name '*.wav'`;
map s!^\./!!, @speech; # Pfade relativ zum speechdir
return 'no speech files found' unless @speech;
-
+
my @statement=();
+
+ # Praefix bei Gefahrenwarnung (dpt 5.005)
+ push(@statement, words(\@speech, 'Achtung')) if $dpt eq '5.005'; # Gefahrenwarnung
# Textteil (Gruppenadresse ausgesprochen)
- if(defined $pattern)
- {
- push(@statement, words(\@speech, $pattern));
- }
+ push(@statement, words(\@speech, $pattern)) if defined $pattern;
# Informationsteil (Inhalt des Telegramms)
given($dpt)
{
when (1.001) # An/Aus
- { push(@statement, 'Zahlen/'.($val?'an':'aus').'.wav'); }
+ {
+ push(@statement, 'Zahlen/'.($val?'an':'aus').'.wav');
+ }
when (1.008) # Hoch/Runter
- { push(@statement, 'Zahlen/'.($val?'hoch':'runter').'.wav'); }
+ {
+ push(@statement, 'Zahlen/'.($val?'hoch':'runter').'.wav');
+ }
when(1.009) # Auf/Zu
- { push(@statement, 'Zahlen/'.($val?'auf':'zu').'.wav'); }
+ {
+ push(@statement, 'Zahlen/'.($val?'auf':'zu').'.wav');
+ }
when(2.007) # Auf/Ab/Stop
- { push(@statement, 'Zahlen/'.($val==1?'auf':($val==-1?'ab':'stop')).'.wav'); }
+ {
+ push(@statement, 'Zahlen/'.($val==1?'auf':($val==-1?'ab':'stop')).'.wav');
+ }
+ when(5.005) # Gefahrenwarnung
+ {
+ my %warnstufe=(0=>'keine_Meldung', 1=>'Hinweis', 2=>'Vorwarnung', 3=>'Warnung', 4=>'Gefahr', 5=>'Gefahr_hoch');
+ push(@statement, 'Warnung/'.$warnstufe{$val}.'.wav');
+ }
when([5.010,7.001,12.001]) # Ordinalzahl
- { push(@statement, number(\@speech, $val, -1)); }
+ {
+ push(@statement, number(\@speech, $val, -1));
+ }
when([3.007,6.010,8.001,13.001]) # Kardinalzahl
- { push(@statement, number(\@speech, $val)); }
+ {
+ push(@statement, number(\@speech, $val));
+ }
when([5.001,6.001]) # Prozent
{
push(@statement, number(\@speech, $val));
@@ -150,7 +183,7 @@
}
else
{
- return "Unbekanntes Datumsformat $val";
+ return "Unbekanntes Datumsformat '$val'";
}
}
when(7.005) # Zeitdauer
@@ -180,23 +213,30 @@
push(@statement, "Wochentage/$1.wav");
push(@statement, number(\@speech, $2));
push(@statement, "Zeiten/Uhr.wav");
- push(@statement, number(\@speech, $3)) if $3;
+ push(@statement, number(\@speech, $3)) if int($3)>0;
}
elsif($val=~/^([0-9][0-9])\:([0-9][0-9])/)
{
push(@statement, number(\@speech, $1));
push(@statement, "Zeiten/Uhr.wav");
- push(@statement, number(\@speech, $2)) if $2;
+ push(@statement, number(\@speech, $2)) if int($2)>0;
}
else
{
- return "Unbekanntes Uhrzeitformat $msg{value}";
+ return "Unbekanntes Uhrzeitformat '$val'";
}
}
+ when(/^16/) # Freitext
+ {
+ $val=~s/\000*$//; # streiche Nullen am Ende
+ push(@statement, words(\@speech, $val));
+ }
when(1.017) # Trigger, kein Datenzusatz
{}
default # kein Datenzusatz, aber mit Logeintrag
- { return "Datentyp $dpt nicht implementiert"; }
+ {
+ return "Datentyp $dpt nicht implementiert";
+ }
}
# Das komplette Statement in die Ausgabe geben
return speak($channel, $name, @statement);
@@ -404,21 +444,21 @@
my $datetime=`$date +"%F %X"`;
$datetime=~s/\s*$//s;
- if(@_)
+ # Fuer Russound-Ausgabe: Star Trek 'Beep' vorweg weckt Russound (und User) fuer Ansage auf
+ if($channel=~/$beepchannel/ && $name!~/$radioga/)
{
- # Nur fuer Russound-Paging: Star Trek 'Beep' vorweg weckt Russound auf
- if($channel=~/$beepchannel/)
+ my $lastbeep=$plugin_info{$plugname.'_lastbeep_'.$channel};
+
+ # max ein Beep pro Minute
+ if(!defined $lastbeep || time()>$lastbeep+60)
{
- my $lastbeep=$plugin_info{$plugname.'_lastbeep'};
-
- # max ein Beep pro Minute
- if(!defined $lastbeep || time()>$lastbeep+60)
- {
- unshift(@_, $beep);
- $plugin_info{$plugname.'_lastbeep'}=time();
- }
+ unshift(@_, $beep);
+ $plugin_info{$plugname.'_lastbeep_'.$channel}=time();
}
+ }
+ if(@_)
+ {
if($mode eq 'aplay')
{
my $aplay=checkexec('aplay');
@@ -429,12 +469,7 @@
}
elsif($mode eq 'mpd')
{
- push @_, "silence.wav"; # kurze Pause zwischen Ansagen
-
- map s!^/*!$speechdir/!, @_; # alle Eintraege relativ zum speechdir
- map s!^$mpddir/!!, @_; # mpd braucht einen Pfadnamen relativ zum music-Dir
- map s!/+!/!, @_; # zur Sicherheit
-
+ # Host und Port ermitteln
$mpdhost{$channel}=~m!^\s*(.*)\s*/\s*(.*)\s*$!;
my $host=$1; my $port=$2;
@@ -442,19 +477,71 @@
# also so:
my $mpc=checkexec('mpc');
$mpc="export MPD_HOST=$host; export MPD_PORT=$port; $mpc";
- system "$mpc update"; # Aktualisierung verfuegbarer Soundclips
- # wird momentan noch was gespielt?
- system "$mpc clear" unless `$mpc`=~/playing/s; # leeren falls abgespielt
- system "$mpc add \"".(join "\" \"", @_)."\"";
-# plugin_log($plugname, "$mpc add \"".(join "\" \"", @_)."\"");
-# plugin_log($plugname, "$mpc play") unless `$mpc`=~/playing/s;
- system "$mpc play" unless `$mpc`=~/playing/s; # starten falls noch nicht aktiv
+ # Laeuft gerade das Radio oder eine Ansage?
+ my $lfd_radio = $plugin_info{$plugname.'_radio_'.$channel} ne 'AUS';
+ my $lfd_ansage = `$mpc`=~/playing/s && !$lfd_radio;
-# $playing=`$mpc; $mpc outputs`;
-# $playing=~s/\s+/ /sg;
-# plugin_log($plugname, $playing);
- map s!^.*/(.*?)\.wav!$1!, @_;
+ # Sonderfall Internetradio statt Sprachausgabe
+ if($name=~/$radioga/)
+ {
+ my $val=$_[0];
+
+ if($val=~s/V([+-]?[0-9X])$//)
+ {
+ my $vol=$1;
+ $vol = ($vol eq 'X' ? 100: 10*$vol);
+ system "$mpc volume $vol"; # ermoeglicht gleichzeitig Sender u Lautstaerke festzulegen
+ }
+
+ if($val eq 'AUS')
+ {
+ system "$mpc clear";
+ $plugin_info{$plugname.'_radio_'.$channel}='AUS';
+ }
+ elsif($val =~ /^VOLUME\s*([+-]?[0-9]+)/)
+ {
+ system "$mpc volume $1";
+ }
+ elsif(defined $stations{$val})
+ {
+ system "$mpc clear" unless $lfd_ansage; # nur leeren falls abgespielt
+ system "$mpc add \"$stations{$val}\"";
+ plugin_log($plugname, "$mpc add \"$stations{$val}\"");
+ system "$mpc play" unless `$mpc`=~/playing/s; # starten falls noch nicht aktiv
+ $plugin_info{$plugname.'_radio_'.$channel}=$stations{$val};
+ }
+ else
+ {
+ return "Unbekannter Radiosender '$val'";
+ }
+ }
+ else # Regelfall: Sprachausgabe
+ {
+ system "$mpc update"; # Aktualisierung verfuegbarer Soundclips
+
+ push @_, "silence.wav"; # kurze Pause zwischen Ansagen
+
+ map s!^/*!$speechdir/!, @_; # alle Eintraege relativ zum speechdir
+ map s!^$mpddir/!!, @_; # mpd braucht einen Pfadnamen relativ zum music-Dir
+ map s!/+!/!, @_; # zur Sicherheit
+
+ push @_, $plugin_info{$plugname.'_radio_'.$channel} if $lfd_radio; # nach der Ansage wieder zurueck aufs Radio
+
+ # wird momentan noch was gespielt?
+ # dann Playlist leeren, ggf Radio stoppen falls abgespielt
+ system "$mpc crossfade 0"; # nur leeren falls abgespielt
+ system "$mpc clear" unless $lfd_ansage;
+ # ein Fall noch zu klaeren: wenn Radio laeuft und zwei Ansagen kurz hintereinander kommen,
+ # wird die zweite die erste unterbrechen, weil $lfd_ansage hier (inkorrekt) 0 sein wird.
+
+ system "$mpc add \"".(join "\" \"", @_)."\"";
+ plugin_log($plugname, "$mpc add \"".(join "\" \"", @_)."\"");
+ system "$mpc play" unless `$mpc`=~/playing/s; # starten falls noch nicht aktiv
+
+ map s!^.*/(.*?)\.wav!$1!, @_;
+ }
+
$retval.=$channel.':'.(join ' ', @_);
}
else
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-20 21:10:59 UTC (rev 836)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-25 07:14:08 UTC (rev 837)
@@ -320,6 +320,7 @@
$result=execute_logic($t, groupaddress($logic{$t}{receive}), undef, undef);
$retval.="\$logic{$t} -> $transmit:".
(defined $result?$result.($toor?" gespeichert":" gesendet"):"nichts zu senden")." (Timer) " if $debug;
+
}
# Timer loeschen bzw. neu setzen
@@ -339,6 +340,21 @@
}
}
+# Suche Timer-Logiken, bei denen aus irgendeinem Grund der naechste Aufruf noch nicht berechnet wurde,
+# bspw wegen eines Plugin-Timeouts waehrend der Berechnung
+for my $t (keys %logic)
+{
+ next if $t eq 'debug' || !defined $logic{$t}{timer};
+
+ my $timer=$plugin_info{$plugname.'__'.$t.'_timer'};
+
+ next if defined $timer && $timer>time();
+
+ # Debuggingflag gesetzt
+ my $debug = $logic{debug} || $logic{$t}{debug};
+ set_next_call($t, $debug);
+}
+
# Cycle auf naechsten Aufruf setzen
unless(defined $nexttimer)
{
@@ -407,19 +423,18 @@
# Feiertagstabelle als Tageszahl im Jahr (1=1.Januar, 32=1.Februar usw.): 1.1., 1.5., 3.10., 25./26.12.
# und die auf Ostern bezogenen Kirchenfeiertage: Karfreitag, Ostern (2x), Christi Himmelfahrt, Pfingsten (2x), Fronleichnam
my @holidays=(1,121+$leapyear,276+$leapyear,359+$leapyear,360+$leapyear,$J-2,$J,$J+39,$J+49,$J+50,$J+60);
- my $is_holiday = scalar(grep { $_==$doy } @holidays);
-
- return $is_holiday;
+
+ return (grep { $_==$doy } @holidays) ? 1 : 0;
}
sub add_day_info
{
my $d=shift;
- $d->{weekend}=($d->{day_of_week_no}>=6);
- $d->{weekday}=!$d->{weekend};
- $d->{holiday}=is_holiday($d->{year},$d->{day_of_year});
- $d->{workingday}=(!$d->{weekend} && !$d->{holiday});
+ $d->{weekend} = ($d->{day_of_week}>=6) ? 1 : 0;
+ $d->{weekday} = !$d->{weekend};
+ $d->{holiday} = is_holiday($d->{year},$d->{day_of_year});
+ $d->{workingday} = (!$d->{weekend} && !$d->{holiday}) ? 1 : 0;
$d->{date} = sprintf("%02d/%02d",$d->{month},$d->{day_of_month});
}
@@ -474,7 +489,7 @@
next;
}
- if($k=~/^(holiday|weekend|weekday|workingday)$/ && $s->{$k}!~/^(0|1)$/)
+ if($k=~/^(holiday|weekend|weekday|workingday)$/ && !ref $s->{$k} && $s->{$k}!~/^(0|1)$/)
{
plugin_log($plugname, "Logiktimer zu Logik '$t': Unerlaubter Wert '$k\->$s->{$k}': erlaubt sind 0 und 1");
next;
@@ -598,6 +613,11 @@
# Pflichtfeld ist lediglich time, die anderen duerfen auch entfallen.
# Jeder Wert darf ein Einzelwert oder eine Liste sein.
my $schedule=$logic{$t}{timer};
+
+# return unless defined $schedule;
+
+ # Das "Day-Hash" wird dazu verwendet, Tage zu finden, auf die die Timer-Spezifikation zutrifft
+ # Wir fangen dabei mit today, also heute, an.
my $today={year=>$year,day_of_year=>$day_of_year,month=>$month,day_of_month=>$day_of_month,
calendar_week=>$calendar_week,day_of_week=>$day_of_week_no};
add_day_info($today);
@@ -611,7 +631,7 @@
for my $s (@{$schedule})
{
standardize_and_expand_single_schedule($t,$s);
-
+
# Steht heute aus diesem Schedule noch ein Termin an?
next unless schedule_matches_day($s,$today) && $s->{time}[-1] gt $time_of_day;
@@ -658,7 +678,7 @@
else
{
plugin_log($plugname, "Logik '$t' wird nicht mehr aufgerufen (alle in time=>... festgelegten Termine sind verstrichen).")
- if $debug && $logic{$t}{timer};
+ if $logic{$t}{timer}; # if $debug;
delete $plugin_info{$plugname.'__'.$t.'_timer'};
}
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-20 21:10:59 UTC (rev 836)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-25 07:14:08 UTC (rev 837)
@@ -10,24 +10,25 @@
my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
# eibgaconf fixen falls nicht komplett indiziert
-if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
-{
- for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
- {
- $eibgaconf{$ga}{ga}=$ga;
- my $name=$eibgaconf{$ga}{name};
- next unless defined $name;
- $eibgaconf{$name}=$eibgaconf{$ga};
+# entfaellt ab Wiregate PL32
+#if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
+#{
+# for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
+# {
+# $eibgaconf{$ga}{ga}=$ga;
+# my $name=$eibgaconf{$ga}{name};
+# next unless defined $name;
+# $eibgaconf{$name}=$eibgaconf{$ga};
+#
+# next unless $name=~/^\s*(\S+)/;
+# my $short=$1;
+# $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
+#
+# $eibgaconf{$ga}{short}=$short;
+# $eibgaconf{$short}=$eibgaconf{$ga};
+# }
+#}
- next unless $name=~/^\s*(\S+)/;
- my $short=$1;
- $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
-
- $eibgaconf{$ga}{short}=$short;
- $eibgaconf{$short}=$eibgaconf{$ga};
- }
-}
-
# Aufrufgrund ermitteln
my $event=undef;
if (!$plugin_initflag)
@@ -42,7 +43,8 @@
# Konfigurationsfile einlesen
my %scene=();
-my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
+my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname";
+$conf.='.conf' unless $conf=~s/\.pl$/.conf/;
my $err=read_from_config();
return $err if $err;
@@ -110,6 +112,7 @@
# die betreffende Szene finden
unless($plugin_info{$plugname.'__SceneLookup'}=~/(St|Rc)\($ga\)=>\'(.+?)\',/)
{
+ plugin_log($plugname, "Storniere $ga");
delete $plugin_subscribe{$ga}{$plugname}; # unbekannte GA
for my $k (keys %scene) { delete $scene{$k}; } # Hilfe fuer die Garbage Collection
return;
@@ -213,7 +216,7 @@
my $z=shift; # die Szenenbezeichnung
open CONFIG, ">>$conf";
- print CONFIG "\$scene{$z}={";
+ print CONFIG "\$scene{'$z'}={";
for my $v (sort keys %{$scene{$z}})
{
print CONFIG sprintf "'$v'=>%.2f, ", $scene{$z}{$v};
@@ -230,25 +233,41 @@
# Alle Laufzeitvariablen im Hash %{$dyn}
# in das (flache) Hash plugin_info schreiben
- for my $k (grep /^$plugname\__$z/, keys %plugin_info)
- {
- delete $plugin_info{$k};
- }
+
+ my @keylist=keys %{$scene{$z}};
+ map { $_=sprintf("$_=>'%.2f'",$scene{$z}{$_}) } @keylist;
- for my $v (keys %{$scene{$z}})
- {
- $plugin_info{$plugname.'__'.$z.'__'.$v}=$scene{$z}{$v};
- }
+ $plugin_info{$plugname.'__'.$z} = join ',', @keylist;
+# plugin_log($plugname, "stored: ".$plugin_info{$plugname.'__'.$z});
+
+# for my $k (grep /^$plugname\__$z/, keys %plugin_info)
+# {
+# delete $plugin_info{$k};
+# }
+#
+# for my $v (keys %{$scene{$z}})
+# {
+# $plugin_info{$plugname.'__'.$z.'__'.$v}=$scene{$z}{$v};
+# }
}
sub recall_from_plugin_info
{
for my $k (grep /^$plugname\__/, keys %plugin_info)
{
- next unless($k=~/^$plugname\__(.*\#.*)__(.*)$/);
- my ($z,$v)=($1,$2);
- $scene{$z}{$v}=$plugin_info{$k};
+ next unless($k=~/^$plugname\__(.*\#.*)$/);
+ my $z=$1;
+ $scene{$z}={};
+ my $pi=$plugin_info{$k};
+ while($pi=~m/(.*?)=>\'(.*?)\'/g) { $scene{$z}{$1}=$2 }
+# plugin_log($plugname, "retrieved: ".join ',', map $_=sprintf("$_=>'%.2f'",$scene{$z}{$_}), keys %{$scene{$z}});
}
+# for my $k (grep /^$plugname\__/, keys %plugin_info)
+# {
+# next unless($k=~/^$plugname\__(.*\#.*)__(.*)$/);
+# my ($z,$v)=($1,$2);
+# $scene{$z}{$v}=$plugin_info{$k};
+# }
}
# Umgang mit GA-Kurznamen und -Adressen
@@ -287,39 +306,3 @@
}
}
-sub shortname
-{
- my $gas=shift;
-
- return unless defined $gas;
- return $gas unless $use_short_names;
-
- if(ref $gas)
- {
- my $sh=[];
- for my $ga (@{$gas})
- {
- if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
- {
- push @{$sh}, $eibgaconf{$ga}{short};
- }
- else
- {
- push @{$sh}, $ga;
- }
- }
- return $sh;
- }
- else
- {
- my $sh=$gas;
-
- if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
- {
- $sh=$eibgaconf{$gas}{short};
- }
-
- return $sh;
- }
-}
-
Modified: wiregate/plugin/generic/conf.d/Ansagen.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Ansagen.conf 2012-05-20 21:10:59 UTC (rev 836)
+++ wiregate/plugin/generic/conf.d/Ansagen.conf 2012-05-25 07:14:08 UTC (rev 837)
@@ -1,7 +1,6 @@
#!/usr/bin/perl
#
# Ansagen.pl - Konfiguration
-
# Voraussetzungen:
# 1. Im Verzeichnis $speechdir/Zahlen muessen folgende Dateien vorhanden sein:
@@ -78,23 +77,321 @@
# Directories, Kanaele, Gruppenadressen
$speechdir='/var/lib/mpd/music/Ansagen/';
%channels=(
- '^WA_'=>'welcome', # zB "WA_Die Aussentemperatur betraegt"
- '^WD_'=>'paging', # zB "WD_Folgende Fenster sind geoeffnet"
+ '^WW_'=>'welcome',
+ # zB "WW_Die Aussentemperatur betraegt"
+ '^WP_'=>'paging',
+ # zB "WP_Folgende Fenster sind geoeffnet"
+ '^WM_'=>'music',
+ # zB "WM_Folgende Fenster sind geoeffnet"
'default'=>'welcome' # die GAs in additional_subscriptions
);
-$beepchannel='paging';
+$beepchannel='paging|music';
$beep = sprintf "Beep/%02d.wav", 3; # int(rand(32))+1 fuer Zufallsbeep
#@additional_subscriptions=qw(0/7/245 0/7/246 6/2/186);
@additional_subscriptions=();
+
# Ausgabeoptionen
$mode='mpd'; # mpd or aplay
-# die folgenden Eintraege nur fuer mpd benoetigt
+# die folgenden Eintraege werden nur fuer mpd benoetigt
$mpddir='/var/lib/mpd/music';
%mpdhost=(
- 'welcome'=>'127.0.0.1/6600',
- 'paging'=>'127.0.0.1/6601'
+ 'welcome'=>'127.0.0.1/6600',
+ 'paging'=>'127.0.0.1/6601',
+ 'music'=>'127.0.0.1/6602',
);
+# Integriertes Internetradio, funktioniert NUR mit $mode='mpd',
+# GA-Kuerzel nach diesem Muster rufen das Radio auf
+$radioga='^...MusicPlayer';
+# solche GAs muessen Datentyp DPT 16 haben und uebertragen im Datenteil den Sendernamen
+# sowie Kommandos wie 'AUS' oder 'VOLUME 30' oder 'VOLUME +10',
+# es ist auch moeglich, in einem einzigen Aufruf Sender und Lautstaerke festzulegen:
+# zB knx_write($ga, 'Bayern1 V3', 16); setzt Lautstaerke auf 30%, 'bayern1 VX' auf 100%
+
+# Tabelle von Sendernamen, nicht alle sind <=14 Zeichen und damit aufrufbar...
+# die meisten Links sind UNGETESTET!
+%stations = (
+ 'Bayern1' => 'http://gffstream.ic.llnwd.net/stream/gffstream_w10a',
+ 'Bayern2' => 'http://gffstream.ic.llnwd.net/stream/gffstream_w11a',
+ 'Bayern3' => 'http://gffstream.ic.llnwd.net/stream/gffstream_w12a',
+ 'BRKlassik' => 'http://gffstream.ic.llnwd.net/stream/gffstream_w13a',
+ 'B5aktuell' => 'http://gffstream.ic.llnwd.net/stream/gffstream_w14a',
+ 'B5plus' => 'http://gffstream.ic.llnwd.net/stream/gffstream_w15a',
+ 'FFH' => 'http://streams.ffh.de/radioffh/mp3/hqlivestream.mp3',
+ 'FFHTop40' => 'http://streams.ffh.de/ffhchannels/mp3/hqtop40.mp3',
+ 'FFH80er' => 'http://streams.ffh.de/ffhchannels/mp3/hq80er.mp3',
+ 'FFHRock' => 'http://streams.ffh.de/ffhchannels/mp3/hqrock.mp3',
+ 'FFHElektro' => 'http://streams.ffh.de/ffhchannels/mp3/hqelectrobeatz.mp3',
+ 'FFHEurodance' => 'http://streams.ffh.de/ffhchannels/mp3/hqeurodance.mp3',
+ 'FFHDeutsch' => 'http://streams.ffh.de/ffhchannels/mp3/hqdeutsch.mp3',
+ 'FFHSoundtrck' => 'http://streams.ffh.de/ffhchannels/mp3/hqsoundtrack.mp3',
+ 'FFHHits4Kids' => 'http://streams.ffh.de/ffhchannels/mp3/hqhits4kids.mp3',
+ 'FFHJazz' => 'http://streams.ffh.de/ffhchannels/mp3/hqjazz.mp3',
+ 'FFH90er' => 'http://streams.ffh.de/ffhchannels/mp3/hqspezial.mp3',
+ 'FFHLounge' => 'http://streams.ffh.de/ffhchannels/mp3/hqlounge.mp3',
+ 'FFHLovesongs' => 'http://streams.ffh.de/ffhchannels/mp3/hqspezial1.mp3',
+ 'FFHAlternatv' => 'http://streams.ffh.de/ffhchannels/mp3/hqalternative.mp3',
+
+ '1Live Diggi' => 'http://gffstream.ic.llnwd.net/stream/gffstream_einslive_b',
+ '1Live Kunst' => 'http://gffstream.ic.llnwd.net/stream/gffstream_einslive_a',
+ '1Live' => 'http://gffstream.ic.llnwd.net/stream/gffstream_stream_wdr_einslive_a',
+ '2Radio' => 'http://88.198.50.10:8020',
+ '80s' => 'http://64.236.34.67:80/stream/1040',
+ '89.0 RTL' => 'http://62.75.176.45/rtl-high',
+ 'Alpen-Radio' => 'http://cdn1.ice.muc.streamin.de/alpenradio',
+ 'BHJMS-Radio1' => 'http://bhjms-streamserver.mscs.net:8000/',
+ 'BIG FM' => 'http://www.bigfm.de/webradio/bigfm-high.m3u',
+ 'BS Allgenres' => 'http://www.besonic.fm/allgenres',
+ 'BS HipHop' => 'http://besonic.fm:8000/hiphopsoulurban',
+ 'BS JazzBlues' => 'http://besonic.fm:8000/jazzblues',
+ 'BS MixedUP' => 'http://besonic.fm:8000/mixedup',
+ 'BS Pop' => 'http://besonic.fm:8000/Pop',
+ 'BSAlternativ' => 'http://besonic.fm:8000/rockalternative',
+ 'BavOpenRadio' => 'http://gffstream.ic.llnwd.net/stream/gffstream_w9b',
+ 'BlackBeats' => 'http://blackbeats.fm:13000/',
+ 'Blackmusic' => 'http://listen.bmradio.de',
+ 'Blitz-Radio' => 'http://blitz-stream.de:8000/',
+ 'Blu.FM' => 'http://sc1.netstreamer.net:9800/',
+ 'Br4 Zeiglers' => 'http://80.252.104.101:8000/zeiglerswwdp',
+ 'CFNYFM' => 'http://208.80.52.106/CFNYFM',
+ 'CHROMANOV' => 'http://212.112.241.88:8000/',
+ 'Chat-Radio' => 'http://chatradio.myftp.org:8202',
+ 'Citybull' => 'http://citybull.net:11000/',
+ 'ClubNatur.FM' => 'http://server2.digital-webstream.de:7800',
+ 'CrossChannel' => 'http://62.146.11.24/3095crosschannel_dsl.mp3',
+ 'DEFJAY.DE' => 'http://sc1.netstreamer.net:8040/',
+ 'DLF' => 'http://www.dradio.de/streaming/dlf_hq_ogg.m3u',
+ 'DLR Kultur' => 'http://www.dradio.de/streaming/dkultur_hq_ogg.m3u',
+ 'Dancefox' => 'http://s5.pop-stream.de:9000/',
+ 'DeluxeJazz' => 'http://d85195728.i.tis.core005.cdn.streamfarm.net:80/13000deluxe/live/3258radiodeluxe/de_96.mp3',
+ 'DeluxeLounge' => 'http://d852143781.i.sto.core008.cdn.streamfarm.net/13000deluxe/live/3304lounge/de_128.mp3',
+ 'DieNeue107.7' => 'http://cdn1.ice.muc.streamin.de:80/dieneue1077',
+ 'Disco-Beatz' => 'http://85.214.43.232:1598/',
+ 'Discodance' => 'http://discodancefoxradio.eu:25819',
+ 'Discoradio' => 'http://discoradio.fabbricadigitale.it/asx/discoradio.asx',
+ 'Dt Welle' => 'http://metafiles.gl-systemhaus.de/dw/radio_de_64.m3u',
+ 'ELDORADIO' => 'http://sender.eldoradio.de:8000/128',
+ 'ERF Radio' => 'http://d85195728.i.tis.core005.cdn.streamfarm.net:80/3212erf_96_live.mp3',
+ 'Enc Ismaning' => 'http://mp3.webradio.antenne.de:80',
+ 'Enjoy' => 'http://91.121.29.176:9000/',
+ 'EnrgieBerlin' => 'mms://213.200.97.107/energyberlin$livestream.wma',
+ 'EnrgieBremen' => 'mms://213.200.97.107/energybremen$livestream.wma',
+ 'EnrgieHH' => 'mms://213.200.97.107/energyhamburg$livestream.wma',
+ 'EnrgieM' => 'mms://213.200.97.107/energymuenchen$livestream.wma',
+ 'EnrgieN' => 'mms://213.200.97.107/energynuernberg$livestream.wma',
+ 'EnrgieS' => 'mms://213.200.97.107/energystuttgart$livestream.wma',
+ 'EnrgieSachsn' => 'mms://62.26.161.89/energysachsen$livestream.wma',
+ 'FFN' => 'http://rs20.stream24.org:8000/stream',
+ 'Fernsehen' => 'mms://80.190.139.69/11008bmt/live/3338s10rfo/300.wmv?cid=60921&dummy=.wmv',
+ 'Filmradio' => 'http://212.123.103.171:8000/',
+ 'GameSports' => 'http://195.13.63.169:8200/',
+ 'Gamer-FM' => 'http://82.149.224.55:8500/',
+ 'Gong FM' => 'http://213.186.33.86:8321/gongfm.mp3',
+ 'Gong Live' => 'http://radiator.nci.de:8000/live',
+ 'GothicFan' => 'http://85.214.43.100:8000',
+ 'HR Info' => 'http://d851957213.i.tis.core005.cdn.streamfarm.net:80/17000hr/live/3435hrinfo/de_96.mp3',
+ 'HR1' => 'http://d85195728.i.tis.core005.cdn.streamfarm.net:80/17000hr/live/3435hr1/de_96.mp3',
+ 'HR2' => 'http://hr.streamfarm.net/cms/_vm100/radio/live/hr2_cms.asx',
+ 'HR3' => 'http://d85195728.i.tis.core005.cdn.streamfarm.net:80/17000hr/live/3435hr3/de_96.mp3',
+ 'HR4' => 'http://hr.streamfarm.net/cms/_vm100/radio/live/hr4_cms.asx',
+ 'Hit-R.Ant' => 'MMS://213.200.64.231/ANTENNE1$LIVESTREAM2.WMA',
+ 'Hitradio 77' => 'http://80.154.38.77:8000/',
+ 'HotBeats FM' => 'http://break.hotbeats.fm:9999/',
+ 'House-Musik' => 'http://85.214.92.77:8000',
+ 'Impact Punk' => 'http://85.214.37.96:8000/',
+ 'JUMP' => 'mms://wm-ipx-11.stream.tv1.de/22004mdrjump/live/3087mdr_jump_live/de_44.wma',
+ 'Jack FM' => 'http://sc1.streamfox.com:8050',
+ 'KlassikRadio' => 'http://str0.creacast.com:80/klassik1',
+ 'Krautrock' => 'http://krautrock.pop-stream.de:7592/',
+ 'Kronehit' => 'http://kronehit.sil.at:8016',
+ 'LeFunkBoat' => 'http://lefunkboat.com:8000/',
+ 'M94.5' => 'http://stream.lrz-muenchen.de:31337/m945-hq.mp3',
+ 'MAIN.FM' => 'http://87.106.14.18:8000/',
+ 'MDR Figaro' => 'MMS://D621466636.W.IPX.CORE001.CDN.STREAMFARM.NET/22007MDRFIGARO/LIVE/3087MDR_FIGARO_LIVE/DE_44.WMA',
+ 'MDR Info' => 'MMS://D621466636.W.IPX.CORE001.CDN.STREAMFARM.NET/22006MDRINFO/LIVE/3087MDR_INFO_LIVE/DE_44.WMA',
+ 'MDR Klassik' => 'http://62.146.11.19:80/22008mdrklassik/live/3087mdrklassik/klassik_128.mp3',
+ 'MDR1 Thuerin' => 'mms://wm-ipx-11.stream.tv1.de/22003mdr1thueringen/live/3087mdr_thue/de_44.wma',
+ 'METAL-ONLY ' => 'http://80.190.246.22:6666',
+ 'Memoryradio' => 'http://www.memoryradio.de:4000/',
+ 'Metal Refine' => 'http://www.metal-refinement.de:8088',
+ 'MotorFM' => 'http://85.25.145.179:80/motorfm.mp3',
+ 'Muni.Hardest' => 'http://84.19.188.7:8010',
+ 'N-JOY' => 'mms://213.254.239.66/ndr$live$n-joy.wma',
+ 'NDR Info' => 'http://213.200.97.110:8000/ndr/ndrinfo_spezial.mp3',
+ 'NDR Kultur' => 'mms://213.254.239.60/ndr$live$ndrkultur.wma',
+ 'NDR1 90.3' => 'mms://213.254.239.66/ndr$live$ndr903.wma',
+ 'NDR1 NiederS' => 'mms://213.254.239.60/ndr$live$ndr1niedersachsen.wma',
+ 'NDR1 Welle' => 'mms://213.254.239.66/ndr$live$ndr1wellenord.wma',
+ 'NDR2' => 'mms://213.254.239.60/ndr$live$ndr2.wma',
+ 'NE-WS 89.4' => 'mms://213.200.75.248/news894$livestream.wma',
+ 'Neue Welle' => 'http://www.meine-neue-welle.de/dieneuewelle.m3u',
+ 'NightFly' => 'http://pia-server.com:8080/',
+ 'Nightline' => 'http://radio1.nightline-radio.de:8000',
+ 'Nordwest' => 'mms://a346.l2377539860.c23775.g.lm.akamaistream.net/D/346/23775/v0001/reflector:39860',
+ 'OldieStar' => 'http://www.digitalradiostream.de:8000',
+ 'OnkelzRadio' => 'http://80.67.26.220:8000/',
+ 'PELLENZRADIO' => 'http://pellenzradio.info:8020/',
+ 'POP-Radio 1' => 'http://tobit.pop-radio.de:8000',
+ 'POP-Radio 2' => 'http://tobit.pop-radio.de:8030',
+ 'POP-Radio 3' => 'http://tobit.pop-radio.de:8060',
+ 'PRIDE1' => 'http://p1standby.serveftp.org:8100',
+ 'Powerhitz' => 'http://scfire-chi0l-2.stream.aol.com:80/stream/1044',
+ 'Punkrockers' => 'http://punkrockers.kicks-ass.net:8003/',
+ 'R.AktivFM' => 'http://stream.radioaktiv.fm:8000',
+ 'R.Arabella ' => 'http://83.246.121.39:8010/arabella.mp3',
+ 'R.Bielefeld ' => 'http://stream.radiobielefeld.de:8000/live',
+ 'R.Bonn' => 'http://ga-stream01.ga-bonn.de:80/live128',
+ 'R.Brocken' => 'http://62.75.176.44/brocken-high',
+ 'R.Chemnitz' => 'mms://stream.green.ch/RadioChemnitz',
+ 'R.Chiemgau' => 'http://chiemgau.blm.netlantic.net:80/live.mp3',
+ 'R.Cottbus' => 'mms://d85195723.w.tis.core005.cdn.streamfarm.net/21001radio_cottbus/live/3142radio_cottbus/de_48.wma',
+ 'R.Crazy Oper' => 'http://stream2139.init7.net:80',
+ 'R.Dresden' => 'mms://stream.green.ch/RadioDresden',
+ 'R.Electracks' => 'http://peace.str3am.com:6390/',
+ 'R.Erding' => 'mms://stream.hitwelle.de/hitwelle',
+ 'R.Fantasy' => 'http://stream.fantasy.de:8000/radiofantasy.mp3',
+ 'R.Harmony' => 'mms://213.200.64.231/harmony$livestream.wma',
+ 'R.Hellwig' => 'http://s2.global-streaming.net:8100/',
+ 'R.Jumpstyle' => 'http://88.198.13.153:16055',
+ 'R.Kiepenkerl' => 'http://rs1.radiostreamer.com:8810',
+ 'R.Lausitz' => 'mms://stream.green.ch/RadioLausitz',
+ 'R.Leipzig' => 'mms://stream.green.ch/RadioLeipzig',
+ 'R.Melodie' => 'http://62.146.11.24/3107melodie_live.mp3',
+ 'R.Multikulti' => 'mms://stream2.rbb-online.de/wmtencoder/multikulti-live.wma',
+ 'R.Nachteule' => 'ttp://radio-nachteule.de:8010/',
+ 'R.Norderney' => 'http://radiosws.s2.onlinestream.de/listen1.m3u',
+ 'R.Primavera' => 'http://hellfireserver.de:8000/primavera.mp3',
+ 'R.Regenbogen' => 'mms://195.52.221.166/regenbogenweb$livestream.wma',
+ 'R1Live' => 'http://www.r1live.de:8000',
+ 'R2 Offbeat' => 'http://webchannels.sr-online.de:8000/sr2offbeat',
+ 'RBB 1' => 'mms://st...
[truncated message content] |
|
From: <pf...@us...> - 2012-05-27 20:28:57
|
Revision: 840
http://openautomation.svn.sourceforge.net/openautomation/?rev=840&view=rev
Author: pfry
Date: 2012-05-27 20:28:51 +0000 (Sun, 27 May 2012)
Log Message:
-----------
Diverse kleinere Bugfixes
Szenencontroller speichert jetzt die Szenendaten kompakter in plugin_info
Modified Paths:
--------------
wiregate/plugin/generic/Ansagen.pl
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/Szenencontroller.pl
Modified: wiregate/plugin/generic/Ansagen.pl
===================================================================
--- wiregate/plugin/generic/Ansagen.pl 2012-05-27 06:29:07 UTC (rev 839)
+++ wiregate/plugin/generic/Ansagen.pl 2012-05-27 20:28:51 UTC (rev 840)
@@ -31,7 +31,7 @@
# speechdir muss im mpddir liegen!
$speechdir=~s!/$!!;
$mpddir=~s!/$!!;
-return "config error" if $@ || ($mode eq 'mpd' && $speechdir !~ /^$mpddir/);
+return "config error: $@" if $@ || ($mode eq 'mpd' && $speechdir !~ /^$mpddir/);
# Aufrufgrund ermitteln
my $event=undef;
@@ -442,7 +442,7 @@
my $speech=shift;
my $pattern=shift;
- my @hits=sort { length($a) cmp length($b) } grep /$pattern/i, @{$speech};
+ my @hits=sort { length($a) <=> length($b) } grep /$pattern/i, @{$speech};
return @hits ? (shift @hits) : undef;
}
@@ -517,17 +517,42 @@
{
system "$mpc volume $1";
}
- elsif(defined $stations{$val})
+ elsif(grep /$val/i, keys %stations)
{
+ my $station=undef;
+
+ unless(defined $stations{$val})
+ {
+ my @hits=grep /$val/i, sort { length($a) <=> length($b) } keys %stations;
+ $val=shift @hits;
+ }
+ $station=$stations{$val};
+
+ my $wget=checkexec('wget');
+
+ # MusicPal-Links "uebersetzen"
+ if($station=~/freecom\.vtuner\.com/)
+ {
+ $station=~s!freecom\.vtuner\.com!vtuner.com!;
+ $station=~s!setupapp/fc/asp!setupapp/guide/asp!;
+ $station=~s!dynam.*?\.asp!dynampls.asp!;
+ $station=~s!\?ex45v=.*\&id=!\?id=!;
+ }
+
+ # vtuner-Links "uebersetzen"
+ $station = `$wget 2>/dev/null -O - $station` if $station=~/vtuner/;
+
system "$mpc clear" unless $lfd_ansage; # nur leeren falls abgespielt
- system "$mpc add \"$stations{$val}\"";
- plugin_log($plugname, "$mpc add \"$stations{$val}\"");
+ system "$mpc add \"$station\"";
+ plugin_log($plugname, "$mpc add \"$station\"");
system "$mpc play" unless `$mpc`=~/playing/s; # starten falls noch nicht aktiv
- $plugin_info{$plugname.'_radio_'.$channel}=$stations{$val};
+ $plugin_info{$plugname.'_radio_'.$channel}=$station;
+
+ $retval.="$channel:Radiosender '$val'='$station'";
}
else
{
- return "Unbekannter Radiosender '$val'";
+ $retval.="Unbekannter Radiosender '$val'";
}
}
else # Regelfall: Sprachausgabe
@@ -550,13 +575,13 @@
# wird die zweite die erste unterbrechen, weil $lfd_ansage hier (inkorrekt) 0 sein wird.
system "$mpc add \"".(join "\" \"", @_)."\"";
- plugin_log($plugname, "$mpc add \"".(join "\" \"", @_)."\"");
+# plugin_log($plugname, "$mpc add \"".(join "\" \"", @_)."\"");
system "$mpc play" unless `$mpc`=~/playing/s; # starten falls noch nicht aktiv
map s!^.*/(.*?)\.wav!$1!, @_;
+
+ $retval.=$channel.':'.(join ' ', @_);
}
-
- $retval.=$channel.':'.(join ' ', @_);
}
else
{
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-05-27 06:29:07 UTC (rev 839)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-05-27 20:28:51 UTC (rev 840)
@@ -505,7 +505,7 @@
if($k eq 'day_of_week')
{
- for my $wd (sort { length($b) cmp length($a) } keys %weekday)
+ for my $wd (sort { length($b) <=> length($a) } keys %weekday)
{
foreach (@{$s->{$k}}) { s/$wd/$weekday{$wd}/gie } # Wochentage in Zahlenform
}
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-05-27 06:29:07 UTC (rev 839)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-05-27 20:28:51 UTC (rev 840)
@@ -91,7 +91,7 @@
$count++;
}
- $plugin_info{$plugname.'__SceneLookup'}=$scene_lookup;
+ $plugin_info{$plugname.'__Lookup'}=$scene_lookup;
$plugin_info{$plugname.'_cycle'}=0;
$retval.=$count." initialisiert";
@@ -110,7 +110,7 @@
my $n=int($msg{value}); # die Szenennummer
# die betreffende Szene finden
- unless($plugin_info{$plugname.'__SceneLookup'}=~/(St|Rc)\($ga\)=>\'(.+?)\',/)
+ unless($plugin_info{$plugname.'__Lookup'}=~/(St|Rc)\($ga\)=>\'(.+?)\',/)
{
plugin_log($plugname, "Storniere $ga");
delete $plugin_subscribe{$ga}{$plugname}; # unbekannte GA
@@ -230,25 +230,9 @@
sub store_to_plugin_info
{
my $z=shift; # die Szenenbezeichnung
-
- # Alle Laufzeitvariablen im Hash %{$dyn}
- # in das (flache) Hash plugin_info schreiben
-
my @keylist=keys %{$scene{$z}};
- map { $_=sprintf("$_=>'%.2f'",$scene{$z}{$_}) } @keylist;
-
+ map { $_=sprintf("'$_'=>'%.2f'",$scene{$z}{$_}) } @keylist;
$plugin_info{$plugname.'__'.$z} = join ',', @keylist;
-# plugin_log($plugname, "stored: ".$plugin_info{$plugname.'__'.$z});
-
-# for my $k (grep /^$plugname\__$z/, keys %plugin_info)
-# {
-# delete $plugin_info{$k};
-# }
-#
-# for my $v (keys %{$scene{$z}})
-# {
-# $plugin_info{$plugname.'__'.$z.'__'.$v}=$scene{$z}{$v};
-# }
}
sub recall_from_plugin_info
@@ -259,15 +243,8 @@
my $z=$1;
$scene{$z}={};
my $pi=$plugin_info{$k};
- while($pi=~m/(.*?)=>\'(.*?)\'/g) { $scene{$z}{$1}=$2 }
-# plugin_log($plugname, "retrieved: ".join ',', map $_=sprintf("$_=>'%.2f'",$scene{$z}{$_}), keys %{$scene{$z}});
+ while($pi=~m/\'(.*?)\'=>\'(.*?)\'/g) { $scene{$z}{$1}=$2 }
}
-# for my $k (grep /^$plugname\__/, keys %plugin_info)
-# {
-# next unless($k=~/^$plugname\__(.*\#.*)__(.*)$/);
-# my ($z,$v)=($1,$2);
-# $scene{$z}{$v}=$plugin_info{$k};
-# }
}
# Umgang mit GA-Kurznamen und -Adressen
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-10-04 07:59:13
|
Revision: 1027
http://openautomation.svn.sourceforge.net/openautomation/?rev=1027&view=rev
Author: pfry
Date: 2012-10-04 07:59:06 +0000 (Thu, 04 Oct 2012)
Log Message:
-----------
transmit_only_on_request im conf-File korrigiert und kommentiert
Neue Optionen transmit_on_startup (programmiert) und transmit_changes_only (in conf erwaehnt).
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/conf.d/Logikprozessor.conf
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-10-03 17:58:02 UTC (rev 1026)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-10-04 07:59:06 UTC (rev 1027)
@@ -170,6 +170,21 @@
plugin_log($plugname, "\$logic{$t}: Receive-GA $rec nicht in %eibgaconf gefunden") if $debug && !exists $eibgaconf{$rec};
}
}
+
+ if($logic{$t}{transmit_on_startup})
+ {
+ # Berechnung und Senden beim Startup des Logikprozessors
+ # Vorsicht: Logiken mit dieser Bedingung koennen durch knx_read-Requests den Logikprozessor deutlich (!)
+ # ueber die erlaubten 10s Ausfuehrungszeit eines Wiregate-Plugins bringen.
+ # In diesem Fall wird die Initialisierung des Logikprozessors unvollstaendig abgebrochen!
+ # Empfehlung: nur solche Logiken mit transmit_on_startup versehen, deren Read-Requests auf schnell
+ # antwortende externe KNX-Geraete gehen oder sowieso durch den EIB-Cache beantwortet werden,
+ # also bspw. nicht auf andere Plugins warten.
+ my $result=execute_logic($t, $receive, undef, undef);
+ my $ga=groupaddress($logic{$t}{transmit});
+ $retval.="\$logic{$t}{transmit}(Logik) -> $ga:$result " if $debug;
+ knx_write($ga, $result); # DPT aus eibga.conf
+ }
}
$retval.=$count." initialisiert";
@@ -214,7 +229,7 @@
# Debuggingflag gesetzt
my $debug = $logic{debug} || $logic{$t}{debug};
- # Sonderfall: Read- und Write-Telegramme auf der Transmit-Adresse?
+ # Sonderfall: Read- und Write-Telegramme auf der Transmit-Adresse
if($transmit_ga)
{
# Ein Read-Request auf einer Transmit-GA wird mit dem letzten Ergebnis beantwortet
@@ -222,11 +237,21 @@
if($msg{apci} eq "A_GroupValue_Read")
{
my $result=$plugin_info{$plugname.'_'.$t.'_result'};
+
if(defined $result)
{
$retval.="$ga:Lesetelegramm -> \$logic{$t}{transmit}(memory) -> $ga:$result gesendet. " if $debug;
knx_write($ga, $result, undef, 0x40); # response, DPT aus eibga.conf
}
+ else
+ {
+ # falls gespeichertes Ergebnis ungueltig, neuer Berechnungsversuch
+ $result=execute_logic($t, groupaddress($logic{$t}{receive}), undef, undef);
+
+ $retval.="$ga:Lesetelegramm -> \$logic{$t}{transmit}(Logik) -> $ga:$result gesendet. " if $debug;
+ knx_write($ga, $result, undef, 0x40); # response, DPT aus eibga.conf
+ }
+
next;
}
elsif(!$receive_ga) # Receive geht vor - bei Timer-Logiken ist receive_ga immer 0
@@ -716,8 +741,8 @@
my ($t, $receive, $ga, $in)=@_; # Logikindex $t, Bustelegramm erhalten auf $ga mit Inhalt $in
# $receive muss die direkten Gruppenadressen enthalten - Decodierung von Kuerzeln wird nicht vorgenommen
- # Debuggingflag gesetzt
- my $debug = $logic{debug} || $logic{$t}{debug};
+ # Debuggingflag gesetzt
+ my $debug = $logic{debug} || $logic{$t}{debug};
# als erstes definiere das Input-Array fuer die Logik
my $input=$in;
Modified: wiregate/plugin/generic/conf.d/Logikprozessor.conf
===================================================================
--- wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-10-03 17:58:02 UTC (rev 1026)
+++ wiregate/plugin/generic/conf.d/Logikprozessor.conf 2012-10-04 07:59:06 UTC (rev 1027)
@@ -58,8 +58,10 @@
# 5. Hier eine Logik, die den Input bei Eintreffen mit 2 multipliziert, das Resultat aber nur speichert und erst
# spaeter auf ein explizites Lesetelegramm hin auf der transmit-Adresse sendet.
- req => { receive=>'1/2/9', transmit=>'1/2/9', transmit_on_request=>1, translate => sub { 2*$input; }, },
+ req => { receive=>'1/2/9', transmit=>'1/2/9', transmit_only_on_request=>1, translate => sub { 2*$input; }, },
+ # Uebrigens gibt es neben transmit_only_on_request auch transmit_on_startup und transmit_changes_only.
+
# 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 3; } },
# Die Variablen $day_of_week (Mo...So), $calendar_week, $day_of_month (01-31), $month (01-12), $day_of_year (1-366),
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kru...@us...> - 2013-09-16 10:14:58
|
Revision: 1889
http://sourceforge.net/p/openautomation/code/1889
Author: krumboeck
Date: 2013-09-16 10:14:56 +0000 (Mon, 16 Sep 2013)
Log Message:
-----------
Updated RollladenAutomatik.pl
- Added templates
- Added various weather functions
- Added configuration file
- Added room temp funktion
Modified Paths:
--------------
wiregate/plugin/generic/RollladenAutomatik.pl
Added Paths:
-----------
wiregate/plugin/generic/conf.d/RollladenAutomatik.conf_sample
Modified: wiregate/plugin/generic/RollladenAutomatik.pl
===================================================================
--- wiregate/plugin/generic/RollladenAutomatik.pl 2013-09-14 05:12:14 UTC (rev 1888)
+++ wiregate/plugin/generic/RollladenAutomatik.pl 2013-09-16 10:14:56 UTC (rev 1889)
@@ -1,28 +1,34 @@
######################################################################################
-#
# Plugin RollladenAutomatik
-# V0.5 2011-06-10
-# Ben\xF6tigt: libastro-satpass-perl -> 'apt-get install libastro-satpass-perl'
+# V0.6 2013-09-15
+# Autoren: kleinklausi (http://knx-user-forum.de/members/kleinklausi.html)
+# krumboeck (http://knx-user-forum.de/members/krumboeck.html)
+# Benötigt: libastro-satpass-perl -> 'apt-get install libastro-satpass-perl'
#
-# Ein Wiregate Plugin zum automatischen Fahren der Rolll\xE4den. Es berechnet unter Anderem
-# den Stand der Sonne und f\xE4hrt je nach Winkel der Sonne zum Fenster, den Rollladen in
-# eine Beschattungsposition. Folgende Funktionen werden unterst\xFCtzt:
+# Ein Wiregate Plugin zum automatischen Fahren der Rollläden. Es berechnet unter Anderem
+# den Stand der Sonne und fährt je nach Winkel der Sonne zum Fenster, den Rollladen in
+# eine Beschattungsposition. Folgende Funktionen werden unterstützt:
# - Sonnenstand (Azimuth)
# - Anfangs- und Endwinkel (Azimuth) ab dem das Fenster beschienen wird
# - Globale Sperre duch eine Gruppenadresse
# - Sperre eines einzelnen Rollladens durch eine Gruppenadresse
# - Fahren des Rollladen zu (1) oder auf (0) oder Positionsfahren mit Prozentwert
# - Zufahren bei Dunkelheit am Abend und Hell am Morgen
-# - Bugfix f\xFCr Busch-J\xE4ger USB Schnittstelle (muss eingeschaltet werden)
+# - Bugfix für Busch-Jäger USB Schnittstelle (muss eingeschaltet werden)
+# - Vorlagen um die Konfiguration zu vereinfachen (krumboeck)
+# - Rolladenschutz vor hohen Windgeschwindigkeiten (krumboeck)
+# - Zufahren bei sehr niedgrigen Außentemperaturen um die Isolationswirkung zu erhöhen (krumboeck)
+# - Öffnen bei sehr starker Bewölkung um die Helligkeit im Raum zu erhöhen (krumboeck)
+# - Seperate Konfigurationsdatei (krumboeck)
+# - Steuerung aufgrund der Raumtemperatur (krumboeck)
+# - Hysteresewerte für Temperatur, Wind und Bewölkung (krumboeck)
+# - Speichern der Rolladenposition und aktuellen Zuständen (krumboeck)
#
# TODO: Was teilweise integriert ist aber noch nicht komplett ist:
-# - Nur zufahren, wenn es im Raum warm genug ist
-# - Wetterstation einbinden: Helligkeit, Sonnenschein, D\xE4mmerung
# - Bei Fensterdefinition auch Elevation oben bzw. unten angeben
-# - Jalousie Lamellenf\xFChrung
+# - Jalousie Lamellenführung
# - Vorwarnpositionsfahrten?
# - Englisch oder Deutsch?
-# - Au\xDFentemperaur
######################################################################################
@@ -30,149 +36,172 @@
### BEGINN DEFINITION ###
#########################
-# Die Koordinaten des Hauses. Sehr einfach \xFCber http://www.getlatlon.com/ zu ermitteln.
-# Und die H\xF6he \xFCber NN
-my ($lat, $lon, $elev) = (
- 49.02917390736781, # Breitengrad in Grad
- 8.570709228515625, # L\xE4ngengrad in Grad
- 180 / 1000 # H\xF6he \xFCber NN in Kilometer (dewegen geteilt durch 1000)
- );
+use constant HIGHER => 1;
+use constant EQUAL => 0;
+use constant LOWER => -1;
+# Die Koordinaten des Hauses. Sehr einfach über http://www.getlatlon.com/ zu ermitteln.
+# Und die Höhe über NN
+my ($lat, $lon, $elev);
+
# Elevation der Sonne, ab der es abends dunkel ist bzw. morgens hell ist.
-# B\xFCrgerliche D\xE4mmerung ist bei -6 Grad.
+# Bürgerliche Dämmerung ist bei -6 Grad.
my $daemmerung = -3;
-# Gruppenadresse, \xFCber welche die komplette Automatik f\xFCr alle Rolll\xE4den gesperrt werden kann
-my $GASperreAlle = "0/0/125";
+# Gruppenadresse, über welche die komplette Automatik für alle Rollläden gesperrt werden kann
+my $GASperreAlle;
-# Bugfix f\xFCr KNX-Schnittstellen die sich bei zu schneller Telegrammabfolge
-# verschlucken, und denen wir deshalb die Geschwindigkeit der Telegramme drosseln m\xFCssen
-# 0 = nicht anschalten (Telegramme mit voller Geschwindigkeit abfeuern)
-# 1 = anschalten (Telegramme um 20 millisekunden verz\xF6gern)
-# nur f\xFCr "Busch-J\xE4ger 6196 USB REG" ist bekannt das dies ben\xF6tigt wird
+# Bugfix für KNX-Schnittstellen die sich bei zu schneller Telegrammabfolge
+# verschlucken, und denen wir deshalb die Geschwindigkeit der Telegramme drosseln müssen
my $bugfixSlowInterface = 0;
# Ein Array von Hashes, wobei jeder Hash ein Rollladen/Fenster/Raum ist.
my @AlleRolllaeden;
-# Name des Rolladen
-# name => "Speisekammer"
-# Winkel zum Norden, ab dem das Fenster beschienen wird.
-# Echter Osten = 90\xB0, echter S\xFCden = 180\xB0, echter Westen = 270\xB0, echter Norden = 0\xB0
-# winkel1 => 66
-# Winkel zum Norden, bis zu dem das Fenster beschienen wird
-# winkel2 => 186
-# Richtung bei Beschattung: wenn 1 wird DPT3 angenommen und ganz zugefahren.
-# Bei ungleich 1, wird DPT5 angenommen und Position angefahren
-# wertZuBesch => 1
-# Richtung bei keiner Beschattung: wenn 0 wird DPT3 angenommen und ganz aufgefahren.
-# Bei ungleich 0, wird DPT5 angenommen und Position angefahren
-# wertAufBesch => 0
-# Richtung bei Nacht: wenn 1 wird DPT3 angenommen und ganz zugefahren.
-# Bei ungleich 1, wird DPT5 angenommen und Position angefahren
-# wertZuNacht => 1
-# Richtung bei keiner Nacht: wenn 0 wird DPT3 angenommen und ganz aufgefahren.
-# Bei ungleich 0, wird DPT5 angenommen und Position angefahren
-# wertAufNacht => 0
-# Ob der Rollladen in die Automatik f\xFCr Sonnenauf- und untergang einbezogen werden soll
-# sonnenAufUnter => 1
-# Raum-Solltemperatur, wenn keine GA angegeben wurde oder kein Wert vom Bus gelesen wurde
-# raumSollTemp => 22
-# GA der Raum-Solltemperatur
-# GAraumsollTemp => "0/0/127"
-# GA der Raum-Isttemperatur
-# GAraumIstTemp => "0/0/128"
-# GA um Rollladen zu fahren TODO:Sollte man hier mehrere GAs angeben k\xF6nnen?
-# GAfahren => "0/0/126"
-# GA um die Automatik dieses einen Rollladen zu sperren
-# GAsperre=> "0/0/129"
-push @AlleRolllaeden, { name => "Speisekammer", winkel1 => 66, winkel2 => 186, wertZuBesch => 1, wertAufBesch => 0,
- wertZuNacht => 1, wertAufNacht => 0, sonnenAufUnter => 1, raumSollTemp => 22, GAraumSollTemp => "0/0/127",
- GAraumIstTemp => "0/0/128", GAfahren => "2/1/11", GAsperre => "0/0/129" };
-push @AlleRolllaeden, { name => "K\xFCche", winkel1 => 194, winkel2 => 294, wertZuBesch => 80, wertAufBesch => 2,
- wertZuNacht => 100, wertAufNacht => 2, sonnenAufUnter => 1, raumSollTemp => 22, GAraumSollTemp => "0/0/127",
- GAraumIstTemp => "0/0/128", GAfahren => "2/2/3", GAsperre => "0/0/129" };
-push @AlleRolllaeden, { name => "Kind Strasse", winkel1 => 182, winkel2 => 294, wertZuBesch => 86, wertAufBesch => 2,
- wertZuNacht => 100, wertAufNacht => 2, sonnenAufUnter => 0, raumSollTemp => 22, GAraumSollTemp => "0/0/127",
- GAraumIstTemp => "0/0/128", GAfahren => "2/3/13", GAsperre => "0/0/129" };
-push @AlleRolllaeden, { name => "Kind Friedhof", winkel1 => 182, winkel2 => 294, wertZuBesch => 86, wertAufBesch => 2,
- wertZuNacht => 100, wertAufNacht => 2, sonnenAufUnter => 0, raumSollTemp => 22, GAraumSollTemp => "0/0/127",
- GAraumIstTemp => "0/0/128", GAfahren => "2/3/23", GAsperre => "0/0/129" };
-push @AlleRolllaeden, { name => "Schlafen", winkel1 => 359, winkel2 => 359, wertZuBesch => 82, wertAufBesch => 2,
- wertZuNacht => 100, wertAufNacht => 2, sonnenAufUnter => 0, raumSollTemp => 22, GAraumSollTemp => "0/0/127",
- GAraumIstTemp => "0/0/128", GAfahren => "2/3/3", GAsperre => "0/0/129" };
-#######################
-### ENDE DEFINITION ###
-#######################
+# Gruppenadresse für die Windgeschwindigkeit
+my $GAWindSpeed;
+# Gruppenadresse für die Windgrichtung
+my $GAWindDirection;
+# Gruppenadresse für die Bewölkung
+my $GACloudiness;
+
+# Gruppenadresse für die Außentemperatur
+my $GATemperature;
+
+
+#################################
+### Lesen der Konfigurationsdatei
+#################################
+
+my ($user, $pass, $name, $lat, $lon, $alt, $GAtemp);
+my @TimeExclusions;
+
+# Read config file in conf.d
+my $confFile = '/etc/wiregate/plugin/generic/conf.d/'.basename($plugname,'.pl').'.conf';
+if (! -f $confFile) {
+ plugin_log($plugname, " no conf file [$confFile] found.");
+ return "no conf file [$confFile] found.";
+} else {
+ open(CONF, $confFile);
+ my @lines = <CONF>;
+ close($confFile);
+ my $result = eval("@lines");
+ if ($@) {
+ plugin_log($plugname, "conf file [$confFile] returned:");
+ my @parts = split(/\n/, $@);
+ plugin_log($plugname, "--> $_") foreach (@parts);
+ }
+}
+
# Festlegen, dass das Plugin alle 5 Minuten laufen soll
$plugin_info{$plugname.'_cycle'} = 300;
-# Auf die GA der globalen Sperre anmelden
-#TODO: muss man sich \xFCberhaupt auf die GA anmelden. Sollte doch reichen wenn man den letzten Stand liest...
-$plugin_subscribe{$GASperreAlle}{$plugname} = 1;
-# Fals global gesperrt, Plugin-Durchgang beenden
-if (knx_read($GASperreAlle, 0, 1) == 1) {
- return "Global gesperrt";
+my $debug = 0;
+
+#######################
+### ENDE DEFINITION ###
+#######################
+
+if (defined $GASperreAlle) {
+ # Auf die GA der globalen Sperre anmelden
+ # TODO: muss man sich überhaupt auf die GA anmelden. Sollte doch reichen wenn man den letzten Stand liest...
+ $plugin_subscribe{$GASperreAlle}{$plugname} = 1;
+ # Fals global gesperrt, Plugin-Durchgang beenden
+ if (knx_read($GASperreAlle, 0, 1) == 1) {
+ return "Global gesperrt";
+ }
}
-# Sonnenstands-Berechnungen durchf\xFChren
+my $weather = {};
+if (defined $GAWindSpeed) {
+ $weather->{windSpeed} = knx_read($GAWindSpeed, 1800, 9.005);
+}
+if (defined $GAWindDirection) {
+ my $windDirection = knx_read($GAWindDirection, 1800, 5.003);
+ if (defined $windDirection) {
+ $weather->{windFromDirection} = ($windDirection + 180) % 360;
+ }
+}
+if (defined $GACloudiness) {
+ $weather->{cloudiness} = knx_read($GACloudiness, 1800, 5.004);
+}
+if (defined $GATemperature) {
+ $weather->{temperature} = knx_read($GATemperature, 1800, 9.001);
+}
+
+# Sonnenstands-Berechnungen durchführen
my ($azimuth, $elevation) = berechneSonnenstand($lat, $lon, $elev);
+
# Auslesen wo die Sonne beim letzten Durchgang war
my $lastAzimuth = $plugin_info{$plugname.'_lastAzimuth'};
my $lastElevation = $plugin_info{$plugname.'_lastElevation'};
-# Teste ob es Nacht ist, oder gerade Abend- oder Morgend\xE4mmerung ist
-my $testAbendDaemmerung = ($elevation < deg2rad($daemmerung) && $lastElevation > deg2rad($daemmerung)) || 0;
-my $testMorgenDaemmerung = ($elevation > deg2rad($daemmerung) && $lastElevation < deg2rad($daemmerung)) || 0;
-my $testNacht = ($elevation < deg2rad($daemmerung)) || 0;
+my %rolllaeden;
+foreach my $element (@AlleRolllaeden) {
+ $rolllaeden{$element->{name}} = $element;
+}
+
# Los gehts. Jeden Rolladen/Fenster/Raum abarbeiten.
foreach my $element (@AlleRolllaeden) {
- # Falls gesperrt, mit n\xE4chstem Rollladen fortfahren
- if (knx_read($element->{GAsperre}, 0, 1) == 1) {
+
+ if (defined $element->{istVorlage} && $element->{istVorlage}) {
next;
}
- # Die Einfallwinkel in Radians umrechnen
- my $winkel1 = deg2rad($element->{winkel1});
- my $winkel2 = deg2rad($element->{winkel2});
- # Teste ob das Fenster beschienen wird
- my $testAktuellBeschienen = ($azimuth > $winkel1 && $azimuth < $winkel2) || 0;
- my $testVoherBeschienen = ($lastAzimuth > $winkel1 && $lastAzimuth < $winkel2) || 0;
-
- # Fenster war nicht beschienen, ist jetzt beschienen, keine Nacht
- if (!$testVoherBeschienen && $testAktuellBeschienen && !$testNacht) {
- fahreRollladen($element->{wertZuBesch}, $element->{GAfahren});
- plugin_log($plugname,"Name: " . $element->{name} . "; Wegen Sonne zufahren bei: " . round(rad2deg($azimuth)));
+ my $rolladen = berechneRolladenParameter($element, 0);
+
+ # Falls gesperrt, mit nächstem Rollladen fortfahren
+ if (defined $rolladen->{GAsperre}
+ && knx_read($rolladen->{GAsperre}, 0, 1) == 1) {
+ next;
}
- # Fenster ist nicht beschienen, war beschienen, keine Nacht
- if ($testVoherBeschienen && !$testAktuellBeschienen && !$testNacht) {
- fahreRollladen($element->{wertAufBesch}, $element->{GAfahren});
- plugin_log($plugname,"Name: " . $element->{name} . "; Wegen Sonne auffahren bei: " . round(rad2deg($azimuth)));
+
+ my ($position, $bemerkung) = berechneRolladenposition($rolladen, $azimuth, $elevation, $lastAzimuth, $lastElevation, $daemmerung, $weather);
+ my $lastPosition = ladeRolladenParameter($rolladen, "position");
+
+ if (defined $position) {
+ if ($position != $lastPosition) {
+ fahreRollladen($rolladen, $position);
+ plugin_log($plugname,"Name: " . $rolladen->{name} . "; " . $bemerkung);
+ }
+ } elsif ($debug) {
+ plugin_log($plugname,"Name: " . $rolladen->{name} . "; Fahren wird fuer diesen Zyklus ausgesetzt");
}
- # AbendDaemmerung
- if ($testAbendDaemmerung && $element->{sonnenAufUnter}) {
- fahreRollladen($element->{wertZuNacht}, $element->{GAfahren});
- plugin_log($plugname,"Name: " . $element->{name} . "; Wegen Abenddaemmerung zufahren bei: " . round(rad2deg($azimuth)));
- }
- # MorgenDaemmerung
- if ($testMorgenDaemmerung && $element->{sonnenAufUnter}) {
- fahreRollladen($element->{wertAufNacht}, $element->{GAfahren});
- plugin_log($plugname,"Name: " . $element->{name} . "; Wegen Morgendaemmerung auffahren bei: " . round(rad2deg($azimuth)));
- }
+
}
-# F\xFCr die n\xE4chste Iteration den aktuellen Sonnenstand merken
-# TODO: M\xFCsste man sich nicht eigentlich f\xFCr jedes Element den Zustand merken, ob es auf- oder zugefahren wurde???
-# lastAzimuth ging noch als nur der Sonnenstand entscheidend war, ob gefahren wird. Jetzt aber auch lokale Sperre,
-# IstTemperatur, Sonnenschein.
+# Für die nächste Iteration den aktuellen Sonnenstand merken
$plugin_info{$plugname.'_lastAzimuth'} = $azimuth;
$plugin_info{$plugname.'_lastElevation'} = $elevation;
-# Ende
+
return "Grad gegen Norden: " . round(rad2deg($azimuth)) . "; Grad ueber Horizont: " . round(rad2deg($elevation));
+####################################
+# Berechne Parameter eines Rolladen
+####################################
+sub berechneRolladenParameter {
+ my ($rolladen, $counter) = @_;
+ if ($counter > 20) {
+ die $plugname . "Name: " . $rolladen->{name} . "; Endlosschleife bei Templates";
+ }
+ if (defined $rolladen->{vorlage}) {
+ my $template = berechneRolladen($rolllaeden{$rolladen->{vorlage}}, $counter + 1);
+ foreach my $key (keys (%$template)) {
+ if (!defined $rolladen->{$key}) {
+ if ($debug) {
+ plugin_log($plugname,"Name: " . $rolladen->{name} . "; Uebernehme Parameter " . $key . " aus Template " . $template->{name});
+ }
+ $rolladen->{$key} = $template->{$key};
+ }
+ }
+ }
+ return $rolladen;
+}
+
+
####################################################
# Aufruf mit berechneSonnenstand($lat, $lon, $elev);
####################################################
@@ -191,11 +220,168 @@
# Feststellen wo die Sonne gerade ist
my ($azimuth, $elevation, $range) = $loc->azel($sun);
return ($azimuth, $elevation);
+}
+##################################
+# Berechne Position eines Rolladen
+##################################
+sub berechneRolladenposition {
+ my ($element, $azimuth, $elevation, $lastAzimuth, $lastElevation, $daemmerung, $weather) = @_;
+
+ # Die Einfallwinkel in Radians umrechnen
+ my $winkel1 = deg2rad($element->{winkel1});
+ my $winkel2 = deg2rad($element->{winkel2});
+
+ # Teste ob das Fenster beschienen wird
+ my $testAktuellBeschienen = ($azimuth > $winkel1 && $azimuth < $winkel2) || 0;
+ my $testVoherBeschienen = ($lastAzimuth > $winkel1 && $lastAzimuth < $winkel2) || 0;
+
+ # Test ob Nach oder Daemmerung
+ my $testAbendDaemmerung = ($elevation < deg2rad($daemmerung) && $lastElevation > deg2rad($daemmerung)) || 0;
+ my $testMorgenDaemmerung = ($elevation > deg2rad($daemmerung) && $lastElevation < deg2rad($daemmerung)) || 0;
+ my $testNacht = ($elevation < deg2rad($daemmerung)) || 0;
+
+ my ($position, $bemerkung);
+
+ if (defined $element->{maxWindGeschw}) {
+ if (defined $weather->{windSpeed}) {
+ my $compare = vergleicheWert($weather->{windSpeed}, $element->{maxWindGeschw}, $element->{windGeschwHysterese});
+ if ($compare == HIGHER) {
+ speichereRolladenParameter($element, "windProtection", 1);
+ }
+ if ($compare == LOWER) {
+ speichereRolladenParameter($element, "windProtection", 0);
+ }
+ }
+ my $windProtection = ladeRolladenParameter($element, "windProtection");
+ if (defined $windProtection && $windProtection) {
+ $position = $element->{wertAufSchutz} || 0;
+ $bemerkung = "Wegen zu hoher Windgeschwindigkeit auffahren bei " + $weather->{windSpeed} . " km/h";
+ return ($position, $bemerkung);
+ }
+ }
+
+ if ($element->{sonnenAufUnter}) {
+ if ($testNacht) {
+ $position = $element->{wertZuNacht};
+ $bemerkung = "Wegen Abenddaemmerung zufahren bei: " . round(rad2deg($azimuth));
+ return ($position, $bemerkung);
+ } elsif ($testMorgenDaemmerung) {
+ $position = $element->{wertAufNacht};
+ $bemerkung = "Wegen Morgendaemmerung auffahren bei: " . round(rad2deg($azimuth));
+ }
+ }
+
+ # Fenster wird von der Sonne beschienen
+ if (!$testVoherBeschienen && $testAktuellBeschienen) {
+ $position = $element->{wertZuBesch};
+ $bemerkung = "Wegen Sonne zufahren bei: " . round(rad2deg($azimuth));
+ } elsif ($testVoherBeschienen && !$testAktuellBeschienen) {
+ $position = $element->{wertAufBesch};
+ $bemerkung = "Wegen Sonne auffahren bei: " . round(rad2deg($azimuth));
+ }
+
+ my $testTempNiedrig = 0;
+ my $testTempHoch = 0;
+ if (!$testNacht && $testAktuellBeschienen && $element->{tempGesteuert}) {
+ # Solltemperatur für den Raum feststellen
+ my $sollTemp;
+ if (defined $element->{GAraumSollTemp}) {
+ $sollTemp = knx_read($element->{GAraumSollTemp}, 300, 9);
+ }
+ if (!defined $sollTemp) {
+ $sollTemp = $element->{raumSollTemp};
+ }
+
+ # Aktuelle Temperatur für den Raum feststellen
+ my $istTemp = knx_read($element->{GAraumIstTemp}, 300, 9);
+
+ if (defined $sollTemp && defined $istTemp) {
+ my $tempHysterese = $element->{tempHysterese};
+ if (!defined $tempHysterese) {
+ $tempHysterese = 1;
+ }
+ $testTempNiedrig = ($istTemp < ($sollTemp - $tempHysterese));
+ $testTempHoch = ($istTemp > ($sollTemp + $tempHysterese));
+
+ # Fenster ist beschienen, Rolladen ist zu und Temperatur ist zu niedrig
+ if ($testTempNiedrig) {
+ $position = $element->{wertAufBesch};
+ $bemerkung = "Wegen Temperatur auffahren bei: " . $istTemp . ' °C';
+ }
+
+ # Fenster ist beschienen, Rolladen ist offen und Temperatur ist zu hoch
+ if ($testTempHoch) {
+ $position = $element->{wertZuBesch};
+ $bemerkung = "Wegen Temperatur zufahren bei: " . $istTemp . ' °C';
+ }
+ } else {
+ plugin_log($plugname,"Name: " . $element->{name} . "; Temperatur konnte nicht festgestellt werden");
+ return (undef, undef);
+ }
+ }
+
+ if (!$testNacht
+ && $testAktuellBeschienen
+ && defined $element->{maxBewoelkung}) {
+ if (defined $weather->{cloudiness}) {
+ my $compare = vergleicheWert($weather->{cloudiness}, $element->{maxBewoelkung}, $element->{bewoelkungHysterese});
+ if ($compare == HIGHER) {
+ speichereRolladenParameter($element, "overclouded", 1);
+ }
+ if ($compare == LOWER) {
+ speichereRolladenParameter($element, "overclouded", 0);
+ }
+ }
+ my $overclouded = ladeRolladenParameter($element, "overclouded");
+ if (defined $overclouded && $overclouded) {
+ $position = $element->{wertAufBesch};
+ $bemerkung = "Wegen Bewoelkung auffahren bei: " . $weather->{cloudiness} . ' %';
+ }
+ }
+
+ if (defined $element->{minAussenTemp}) {
+ if (defined $weather->{temperature}) {
+ my $compare = vergleicheWert($weather->{temperature}, $element->{minAussenTemp}, $element->{aussenTempHysterese});
+ if ($compare == LOWER) {
+ speichereRolladenParameter($element, "tempProtection", 1);
+ }
+ if ($compare == HIGHER) {
+ speichereRolladenParameter($element, "tempProtection", 0);
+ }
+ }
+ my $tempProtection = ladeRolladenParameter($element, "tempProtection");
+ if (defined $tempProtection && $tempProtection) {
+ $position = $element->{wertZuSchutz} || 1;
+ $bemerkung = "Wegen zu niedriger Temperatur zufahren bei " + $weather->{temperature} . ' °C';
+ }
+ }
+
+ return ($position, $bemerkung);
}
+######################################################################
+# Prüfen ob der aktuelle Wert eine Grenze über bzw. unterschritten hat
+######################################################################
+sub vergleicheWert {
+ my ($currentValue, $reference, $hysterese) = @_;
+ my $current = EQUAL;
+ my $last = EQUAL;
+ if (!defined $hysterese) {
+ $hysterese = 0;
+ }
+ if ($currentValue < ($reference - $hysterese)) {
+ $current = LOWER;
+ }
+ if ($currentValue > ($reference + $hysterese)) {
+ $current = HIGHER;
+ }
+ return $current;
+}
+
+
####################################################
# Aufruf mit fahreRollladen($richtung, $GA);
####################################################
@@ -203,11 +389,13 @@
# Falls $richtung 0 oder 1 ist, wird angenommen, dass der Rollladen
# komplett zu- bzw. aufgefahren werden soll (DPT3).
# Bei $richtung>1 wird angenommen, dass eine Positionsfahrt
- # durchgef\xFChrt werden soll (DPT5).
- # TODO: man muss bei Positionsfahrt f\xFCr den Offen-Zustand mindestens 2% angeben...
- # hm, wenn man die GAs ins Wiregate importiert hat, br\xE4uchte man keinerlei
+ # durchgeführt werden soll (DPT5).
+ # TODO: man muss bei Positionsfahrt für den Offen-Zustand mindestens 2% angeben...
+ # hm, wenn man die GAs ins Wiregate importiert hat, bräuchte man keinerlei
# Unterscheidung mehr! Und man kann auch 0% bzw 1% benutzen
- my ($richtung, $GA) = @_;
+ my ($rolladen, $richtung) = @_;
+ my $GA = $rolladen->{GAfahren};
+
if ($richtung == 0 || $richtung == 1) {
# Auf/Zu fahren
knx_write($GA,$richtung,3);
@@ -216,9 +404,31 @@
# Position anfahren
knx_write($GA,$richtung,5);
}
+
+ # Position speichern
+ speichereRolladenParameter($rolladen, "position", $richtung);
+
# kurze Pause, falls das benutzte Interface das braucht...
if ($bugfixSlowInterface) {
usleep(20000);
}
}
+
+########################################
+# Parameter für einen Rolladen speichern
+########################################
+sub speichereRolladenParameter {
+ my ($rolladen, $parameter, $value) = @_;
+ $plugin_info{$plugname . '_Rolladen_' . $rolladen->{GAfahren} . "_" . $parameter} = $value;
+}
+
+
+####################################
+# Parameter für einen Rolladen laden
+####################################
+sub ladeRolladenParameter {
+ my ($rolladen, $parameter) = @_;
+ return $plugin_info{$plugname . '_Rolladen_' . $rolladen->{GAfahren} . "_" . $parameter};
+}
+
Added: wiregate/plugin/generic/conf.d/RollladenAutomatik.conf_sample
===================================================================
--- wiregate/plugin/generic/conf.d/RollladenAutomatik.conf_sample (rev 0)
+++ wiregate/plugin/generic/conf.d/RollladenAutomatik.conf_sample 2013-09-16 10:14:56 UTC (rev 1889)
@@ -0,0 +1,104 @@
+# Die Koordinaten des Hauses. Sehr einfach über http://www.latlong.net/ zu ermitteln.
+# Und die Höhe über Normalnull (NN)
+$lat = 48.225073; # Breitengrad in Grad
+$lon = 15.643443; # Längengrad in Grad
+$elev = 267 / 1000; # Höhe über NN in Kilometer (dewegen geteilt durch 1000)
+
+# Elevation der Sonne, ab der es abends dunkel ist bzw. morgens hell ist.
+# Bürgerliche Dämmerung ist bei -6 Grad.
+# Defaultwert ist -3
+$daemmerung = -3;
+
+# Gruppenadresse, über welche die komplette Automatik für alle Rollläden gesperrt werden kann
+# $GASperreAlle = "0/1/2";
+
+# Gruppenadresse für die Außentemperatur
+# $GATemperature = "1/2/0";
+
+# Gruppenadresse für die Windgeschwindigkeit
+# $GAWindSpeed = "1/2/3";
+
+# Gruppenadresse für die Windrichtung (hat derzeit noch keine Funktion)
+# $GAWindDirection = "1/2/4";
+
+# Gruppenadresse für die Bewölkung
+# $GACloudiness = "1/2/5";
+
+# Bugfix für KNX-Schnittstellen die sich bei zu schneller Telegrammabfolge
+# verschlucken, und denen wir deshalb die Geschwindigkeit der Telegramme drosseln müssen
+# 0 = nicht anschalten (Telegramme mit voller Geschwindigkeit abfeuern)
+# 1 = anschalten (Telegramme um 20 millisekunden verzögern)
+# nur für "Busch-Jäger 6196 USB REG" ist bekannt das dies benötigt wird
+# $bugfixSlowInterface = 1;
+
+# Ein Array von Hashes, wobei jeder Hash ein Rollladen/Fenster/Raum ist.
+# Name des Rolladen
+# name => "Speisekammer"
+# Definition ist ausschließlich eine Vorlage und wird daher nicht gesteuert
+# istVorlage => 1
+# Name des Rollladen oder der Definition, welche als Vorlage dienen soll (Rekursionen sind möglich)
+# vorlage => "default"
+# Winkel zum Norden, ab dem das Fenster beschienen wird.
+# Echter Osten = 90°, echter Süden = 180°, echter Westen = 270°, echter Norden = 0°
+# winkel1 => 66
+# Winkel zum Norden, bis zu dem das Fenster beschienen wird
+# winkel2 => 186
+# Richtung bei Beschattung: wenn 1 wird DPT3 angenommen und ganz zugefahren.
+# Bei ungleich 1, wird DPT5 angenommen und Position angefahren
+# wertZuBesch => 1
+# Richtung bei keiner Beschattung: wenn 0 wird DPT3 angenommen und ganz aufgefahren.
+# Bei ungleich 0, wird DPT5 angenommen und Position angefahren
+# wertAufBesch => 0
+# Richtung bei Nacht: wenn 1 wird DPT3 angenommen und ganz zugefahren.
+# Bei ungleich 1, wird DPT5 angenommen und Position angefahren
+# wertZuNacht => 1
+# Richtung bei keiner Nacht: wenn 0 wird DPT3 angenommen und ganz aufgefahren.
+# Bei ungleich 0, wird DPT5 angenommen und Position angefahren
+# wertAufNacht => 0
+# Schließen für Schutzfunktion (z.B.: zu niedrige Temp): wenn 1 wird DPT3 angenommen und ganz zugefahren.
+# Bei ungleich 1, wird DPT5 angenommen und Position angefahren
+# wertZuSchutz => 1
+# Öffnen für Schutzfunktion (z.B.: zu hoher Wind): wenn 0 wird DPT3 angenommen und ganz aufgefahren.
+# Bei ungleich 0, wird DPT5 angenommen und Position angefahren
+# wertAufSchutz => 0
+# Ob der Rollladen in die Automatik für Sonnenauf- und untergang einbezogen werden soll
+# sonnenAufUnter => 1
+# Raum-Solltemperatur, wenn keine GA angegeben wurde oder kein Wert vom Bus gelesen wurde
+# raumSollTemp => 22
+# GA der Raum-Solltemperatur
+# GAraumsollTemp => "0/0/127"
+# GA der Raum-Isttemperatur
+# GAraumIstTemp => "0/0/128"
+# GA um Rollladen zu fahren
+# GAfahren => "0/0/126"
+# GA um die Automatik dieses einen Rollladen zu sperren
+# GAsperre=> "0/0/129"
+# Rolladen soll aufgrund der Raumtemperatur gesteuert werden
+# tempGesteuert => 1
+# Hysterese für Regelung aufgrund der Raumtemperatur (in °C)
+# tempHysterese => 0.5
+# Rolladenschutz für zu hohe Windgeschwindigkeiten (max. Geschwindigkeit in km/h)
+# maxWindGeschw => 50
+# Hysterese für Regelung aufgrund der Windgeschwindigkeit (in km/h)
+# windGeschwHysterese => 5
+# Rolladen wird ab einer maximalen Bewölkung (in %) nicht mehr runtergefahren
+# maxBewoelkung => 75
+# Hysterese für Regelung aufgrund der Bewölkung (in %-Punkten)
+# bewoelkungHysterese => 5
+# Rolladen wird runtergefahren wenn die Temperatur unterschritten wird (in °C)
+# minAussenTemp => -15
+# Hysterese für Regelung aufgrund der Außentemperatur (in °C)
+# aussenTempHysterese => 2
+push @AlleRolllaeden, { name => "default", istVorlage => 1, wertZuBesch => 1, wertAufBesch => 0,
+ wertZuNacht => 1, wertAufNacht => 0, sonnenAufUnter => 1, raumSollTemp => 23,
+ tempGesteuert => 1, tempHysterese => 0.5, maxWindGeschw => 80,
+ windGeschwHysterese => 5, maxBewoelkung => 75, bewoelkungHysterese => 5,
+ minAussenTemp => -15, aussenTempHysterese => 2, wertAufSchutz => 0, wertZuSchutz => 1 };
+
+push @AlleRolllaeden, { name => "Wohnzimmer", vorlage => "default", winkel1 => 66, winkel2 => 180,
+ GAraumIstTemp => "2/3/0", GAfahren => "3/1/0" };
+push @AlleRolllaeden, { name => "Küche", vorlage => "default", winkel1 => 152, winkel2 => 252,
+ GAraumIstTemp => "2/3/1", GAfahren => "3/1/2" };
+push @AlleRolllaeden, { name => "Schlafzimmer", vorlage => "default", winkel1 => 93, winkel2 => 186,
+ wertZuNacht => 80, sonnenAufUnter => 0, GAraumIstTemp => "2/3/3", GAfahren => "3/1/3" };
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2014-01-20 20:44:36
|
Revision: 2045
http://sourceforge.net/p/openautomation/code/2045
Author: makki1
Date: 2014-01-20 20:44:33 +0000 (Mon, 20 Jan 2014)
Log Message:
-----------
WireGate Hue-Plugins
Added Paths:
-----------
wiregate/plugin/generic/Hue-Simple.pl
wiregate/plugin/generic/ZLL-Bridge V0_4.pl
Added: wiregate/plugin/generic/Hue-Simple.pl
===================================================================
--- wiregate/plugin/generic/Hue-Simple.pl (rev 0)
+++ wiregate/plugin/generic/Hue-Simple.pl 2014-01-20 20:44:33 UTC (rev 2045)
@@ -0,0 +1,41 @@
+# Philips Hue Leuchten via LAN-GW/Zigbee ansteuern
+# Einfachste Variante - just Demo/testing
+#
+# 2013-04-24 V1.0
+
+### Definitionen
+# Eigenen Aufruf-Zyklus auf 300 Sekunden setzen
+$plugin_info{$plugname.'_cycle'} = 86400; # egal..
+my $ip = "172.17.3.170"; # Gateway-IP FIXME: detect
+my $appname = "HuePL";
+my $key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; # username/API-Key
+my $aktiv_ga = "13/2/100"; # Gruppenadresse zum aktivieren
+# Gruppenadresse zum aktivieren des Farbwechsel-Modus (Gruppe 0 / alle)
+my $color_ga = "13/2/101";
+my $group_ga = "13/2/102";
+### Ende Definitionen
+
+
+#FIXME: Hash, Array, switch o.a.
+
+if ($msg{'apci'} eq "A_GroupValue_Write" and $msg{'dst'} eq $aktiv_ga) {
+ #$plugin_info{$plugname.'_aktiv'} = int($msg{'data'});
+ #knx_write($schalt_ga,int($msg{'data'}),1);
+} elsif ($msg{'apci'} eq "A_GroupValue_Write" and $msg{'dst'} eq $color_ga) {
+ my $value = int($msg{'data'}) ? "colorloop" : "none";
+ my $dummy = `curl -s --request PUT --data '{"effect" : "$value"}' http://$ip/api/$key/groups/0/action`;
+ # http://$ip/api/$key/lights/1/state
+ return "color_ga: $value -> $dummy";
+} elsif ($msg{'apci'} eq "A_GroupValue_Write" and $msg{'dst'} eq $group_ga) {
+ my $value = int($msg{'data'}) ? "true" : "false";
+ my $dummy = `curl -s --request PUT --data '{"on" : $value}' http://$ip/api/$key/groups/0/action`;
+ return "group_ga: $value -> $dummy";
+} else { # zyklischer Aufruf
+ # Plugin an Gruppenadresse "anmelden"
+ $plugin_subscribe{$aktiv_ga}{$plugname} = 1;
+ $plugin_subscribe{$color_ga}{$plugname} = 1;
+ $plugin_subscribe{$group_ga}{$plugname} = 1;
+}
+
+return;
+
Added: wiregate/plugin/generic/ZLL-Bridge V0_4.pl
===================================================================
--- wiregate/plugin/generic/ZLL-Bridge V0_4.pl (rev 0)
+++ wiregate/plugin/generic/ZLL-Bridge V0_4.pl 2014-01-20 20:44:33 UTC (rev 2045)
@@ -0,0 +1,331 @@
+################################ Plugin für die Zigbee Light Link Steuerung ########################
+# Thomas Willi, Version 0.4
+# Ermöglicht die Steuerung von Philips HUE Lampen, etc. über IP Telegramme an ZLL Bridge
+
+# Die benötigten Gruppenadressen müssen im Wiregate eingetragen sein.
+
+### Definitionen -----------------------------------------------------------------------
+
+# Perl Module
+use utf8;
+use HTTP::Request::Common;
+use LWP;
+use Time::HiRes qw( usleep );
+
+# Basis Link zur ZLL Bridge
+# Um den Zugriff auf die Bridge zu bekommen muss ein neuer Benutzer angelegt werden.
+# Anleitung siehe z.B. http://developers.meethue.com/gettingstarted.html
+my $Bridge = 'http://192.168.178.31:8080/api/newdeveloper/lights/';
+
+# Gesamtanzahl aller HUE-Lampen
+my $anzahl=4;
+
+#########################################################################################
+# Gruppenadressen
+# Um Lampen hinzuzufügen Block kopieren und anschließen nur Index ändern
+my %adresse=(
+# Dresden FLS-PP Nr. 1+++++++++++++++++++++++++++++++++++++++++++++++++++++
+# Bridge light: 1
+# Name Gruppenadresse Funktion
+"1_Schalten"=> "1/6/0", #Ein/Aus
+"1_Status_Schalten"=> "1/6/1", #Rückmeldung Ein/Aus
+"1_Helligkeit"=> "1/6/2", #Helligkeit
+"1_Status_Helligkeit"=> "1/6/3", #Rückmeldung Helligkeit
+"1_Rot"=> "1/6/4", #RGB: Rot
+"1_Gruen"=> "1/6/5", #RGB: Grün
+"1_Blau"=> "1/6/6", #RGB: Blau
+"1_Status_Rot"=> "1/6/7", #Rückmeldung RGB: Rot
+"1_Status_Gruen"=> "1/6/8", #Rückmeldung RGB: Grün
+"1_Status_Blau"=> "1/6/9", #Rückmeldung RGB: Blau
+
+# Philips HUE Lampe Nr. 1+++++++++++++++++++++++++++++++++++++++++++++++++++
+# Bridge light: 2
+# Name Gruppenadresse Funktion
+"2_Schalten"=> "1/6/10", #Ein/Aus
+"2_Status_Schalten"=> "1/6/11", #Rückmeldung Ein/Aus
+"2_Helligkeit"=> "1/6/12", #Helligkeit
+"2_Status_Helligkeit"=> "1/6/13", #Rückmeldung Helligkeit
+"2_Rot"=> "1/6/14", #RGB: Rot
+"2_Gruen"=> "1/6/15", #RGB: Grün
+"2_Blau"=> "1/6/16", #RGB: Blau
+"2_Status_Rot"=> "1/6/17", #Rückmeldung RGB: Rot
+"2_Status_Gruen"=> "1/6/18", #Rückmeldung RGB: Grün
+"2_Status_Blau"=> "1/6/19", #Rückmeldung RGB: Blau
+
+# Philips HUE Lampe Nr. 2+++++++++++++++++++++++++++++++++++++++++++++++++++
+# Bridge light: 3
+# Name Gruppenadresse Funktion
+"3_Schalten"=> "1/6/20", #Ein/Aus
+"3_Status_Schalten"=> "1/6/21", #Rückmeldung Ein/Aus
+"3_Helligkeit"=> "1/6/22", #Helligkeit
+"3_Status_Helligkeit"=> "1/6/23", #Rückmeldung Helligkeit
+"3_Rot"=> "1/6/24", #RGB: Rot
+"3_Gruen"=> "1/6/25", #RGB: Grün
+"3_Blau"=> "1/6/26", #RGB: Blau
+"3_Status_Rot"=> "1/6/27", #Rückmeldung RGB: Rot
+"3_Status_Gruen"=> "1/6/28", #Rückmeldung RGB: Grün
+"3_Status_Blau"=> "1/6/29", #Rückmeldung RGB: Blau
+
+# Philips HUE Lampe Nr. 3+++++++++++++++++++++++++++++++++++++++++++++++++++
+# Bridge light: 4
+# Name Gruppenadresse Funktion
+"4_Schalten"=> "1/6/30", #Ein/Aus
+"4_Status_Schalten"=> "1/6/31", #Rückmeldung Ein/Aus
+"4_Helligkeit"=> "1/6/32", #Helligkeit
+"4_Status_Helligkeit"=> "1/6/33", #Rückmeldung Helligkeit
+"4_Rot"=> "1/6/34", #RGB: Rot
+"4_Gruen"=> "1/6/35", #RGB: Grün
+"4_Blau"=> "1/6/36", #RGB: Blau
+"4_Status_Rot"=> "1/6/37", #Rückmeldung RGB: Rot
+"4_Status_Gruen"=> "1/6/38", #Rückmeldung RGB: Grün
+"4_Status_Blau"=> "1/6/39" #Rückmeldung RGB: Blau
+);
+#########################################################################################
+
+#Variablen
+my $Helligkeit;
+my $Status_Helligkeit;
+my $Rot;
+my $Gruen;
+my $Blau;
+my $Status_Rot;
+my $Status_Gruen;
+my $Status_Blau;
+my $hue; # Farbe als hsv Wert
+my $bri; # Helligkeit
+my $sat; # Sättigung
+my $on_off; # on/off
+my $light=1; # Startwert Index für Gruppenadressen (1_xxx)
+
+# Liste Standard Befehle an Bride+++++++++++++++++++++++++++++++++++++++++++++
+my %befehl=(
+# Name Bridge-Kommando
+"ein"=> '{"on":true}',
+"aus"=> '{"on":false}',
+);
+
+### Ende Definitionen -------------------------------------------------------------------
+
+# Eigenen Aufruf-Zyklus setzen
+$plugin_info{$plugname.'_cycle'} = 60; #Aufruf alle 60 Sekunden
+
+# Anmeldung Gruppenadressen
+while (my($key,$ga) = each %adresse){
+ #my $Name_GA=$adresse{$key};
+ my $muster='Status';
+ if ($key !~m/$muster/i){
+ $plugin_subscribe{$adresse{$key}}{$plugname} = 1;
+ } # Ende if
+}
+
+############################### HAUPTPROGRAMM ############################################
+$anzahl=$anzahl+1;
+
+while ($light<$anzahl){
+ my $key_Schalten=$light.'_Schalten';
+ my $key_Status_Schalten=$light.'_Status_Schalten';
+ my $key_Helligkeit=$light.'_Helligkeit';
+ my $key_Status_Helligkeit=$light.'_Status_Helligkeit';
+ my $key_Rot=$light.'_Rot';
+ my $key_Gruen=$light.'_Gruen';
+ my $key_Blau=$light.'_Blau';
+ my $key_Status_Rot=$light.'_Status_Rot';
+ my $key_Status_Gruen=$light.'_Status_Gruen';
+ my $key_Status_Blau=$light.'_Status_Blau';
+
+# Ein/Aus
+ if ($msg{'apci'} eq "A_GroupValue_Write" && $msg{'dst'} eq $adresse{$key_Schalten}) {
+ if ($msg{'value'} eq "0") {
+ my $HTTP=Sende_Befehl($light, $befehl{'aus'});
+ usleep(300);
+ Sende_KNX_Status($light);
+ }
+
+ elsif ($msg{'value'} eq "1") {
+ my $HTTP=Sende_Befehl($light, $befehl{'ein'});
+ usleep(300);
+ Sende_KNX_Status($light);
+ }
+ return 0;
+ } # Ende Ein/Aus
+
+# Dimmen
+ if ($msg{'apci'} eq "A_GroupValue_Write" && $msg{'dst'} eq $adresse{$key_Helligkeit}) {
+ $Helligkeit=decode_dpt5($msg{'data'});
+ if($Helligkeit==0){
+ my $HTTP=Sende_Befehl($light, $befehl{'aus'});
+ usleep(300);
+ Sende_KNX_Status($light);
+ } else {
+ $bri=int($Helligkeit/100*255);
+ my $command='{"on":true,"bri":'.$bri.',"hue":14964,"sat":144}';
+ my $HTTP=Sende_Befehl($light, $command);
+ usleep(300);
+ Sende_KNX_Status($light);
+ return 0;
+ }
+ return 0;
+ } # Ende Dimmen
+
+# RGB Wert senden
+ if ($msg{'apci'} eq "A_GroupValue_Write" && $msg{'dst'} eq $adresse{$key_Rot}) {
+ $Rot=decode_dpt5($msg{'data'});
+ usleep(300);
+ $Gruen=knx_read($adresse{$key_Gruen},100,5);
+ usleep(300);
+ $Blau=knx_read($adresse{$key_Blau},100,5);
+ usleep(300);
+ if(($Rot==0&&$Gruen==0&&$Blau==0)){
+ my $HTTP=Sende_Befehl($light, $befehl{'aus'});
+ usleep(300);
+ knx_write($adresse{$key_Status_Helligkeit},0,5);
+ knx_write($adresse{$key_Status_Schalten},0,1);
+ } else {
+ ($hue, $sat, $bri)=RGBtoHSV($Rot, $Gruen, $Blau);
+ my $command='{"on":true,"bri":'.$bri.',"hue":'.$hue.',"sat":'.$sat.'}';
+ my $HTTP=Sende_Befehl($light, $command);
+ usleep(300);
+ Sende_KNX_Status($light);
+ return 0;
+ }
+ return 0;
+ } # Ende RGB Wert senden
+
+ # Status Update aller Lampen
+ Sende_KNX_Status($light);
+ # Ende Status Update
+
+ $light=$light+1;
+} # Ende while
+
+$light=1;
+
+
+
+############################### ENDE HAUPTPROGRAMM ########################################
+
+# ------------------------------- Unterprogramme -----------------------------------------
+
+###################### RGBtoHSV #####################
+sub RGBtoHSV {
+ my ($r, $g, $b2) = @_;
+ my ($h, $s, $v, $delta, $min);
+ ($r, $g, $b2)= map{$_ *= 1/100 }($r, $g, $b2); #Umrechung RGB Werte in 0-1
+
+ $min = [sort {$a <=> $b} ($r, $g, $b2)] -> [0]; # Minimum
+ $v = [sort {$b <=> $a} ($r, $g, $b2)] -> [0]; # Maximum
+ $delta = $v - $min; # Maximum - Minimum
+
+ if (($v == 0 || $delta == 0)) { $s = 0; $h = 0; } # falls Min = 0 oder Max=Min, dann h=0, s=0
+ else {
+ if ($r == $v) { $h = 60 * ($g - $b2) / $delta; } # Max=R
+ elsif ($g == $v) { $h = 120 + 60 * ($b2 - $r) / $delta; } # Max=G
+ else { $h = 240 + 60 * ($r - $g) / $delta; } # Max=B
+ $h = $h + 360 if ($h < 0); # falls h<0° dann h=h+360°
+ }
+ $s=$delta/$v;
+
+ $h=int($h*65535/360);
+ $s=int($s*255);
+ $v=int($v*255);
+ return ($h,$s,$v);
+}
+########## Ende HSVto RGB ########################
+
+###################### HSVtoRGB ##################
+sub HSVtoRGB {
+ my ($h, $s, $v) = @_;
+ my ($r, $g, $b);
+ my ($f, $i, $h_temp, $p, $q, $t);
+
+ $h=int($h/65535*360); # Umrechnen 0-655355 in 0-360°
+ $s=$s/255; # Umrechnen 0-255 in 0-1
+ $v=$v/255; # Umrechnen 0-255 in 0-1
+
+
+ if ($s == 0) { # falls s=0, dann R=G=B=V
+ $r = $g = $b = $v;
+ } else {
+ if ($h == 360) { $h_temp = 0; } else { $h_temp = $h; } # h=0° identisch h=360°
+ $h_temp /= 60; # Berechne hi=H/60°
+
+ $i = int($h_temp);
+ $f = $h_temp - $i;
+ $p = $v * (1 - $s);
+ $q = $v * (1 - ($s * $f));
+ $t = $v * (1 - ($s * (1 - $f)));
+
+ if ($i == 0) {$r = $v; $g = $t; $b = $p;}
+ if ($i == 1) {$r = $q; $g = $v; $b = $p;}
+ if ($i == 2) {$r = $p; $g = $v; $b = $t;}
+ if ($i == 3) {$r = $p; $g = $q; $b = $v;}
+ if ($i == 4) {$r = $t; $g = $p; $b = $v;}
+ if ($i > 4) {$r = $v; $g = $p; $b = $q;}
+ }
+ $r=int($r*100);
+ $g=int($g*100);
+ $b=int($b*100);
+ return ($r,$g,$b);
+}
+############### Ende HSVtoRGB #####################
+
+######### Sende KNX Status ########################
+sub Sende_KNX_Status {
+ my ($light)=@_;
+ my $key_Status_Schalten=$light.'_Status_Schalten';
+ my $key_Status_Helligkeit=$light.'_Status_Helligkeit';
+ my $key_Status_Rot=$light.'_Status_Rot';
+ my $key_Status_Gruen=$light.'_Status_Gruen';
+ my $key_Status_Blau=$light.'_Status_Blau';
+ ($on_off,$hue,$sat,$bri)=Abfrage_Status($light);
+ $Helligkeit=int($bri/255*100);
+ knx_write($adresse{$key_Status_Schalten},$on_off,1);
+ knx_write($adresse{$key_Status_Helligkeit},$Helligkeit,5);
+ #($Status_Rot, $Status_Gruen, $Status_Blau)=HSVtoRGB($hue, $sat, $bri);
+ #knx_write($adresse{$key_Status_Rot},$Status_Rot,5);
+ #knx_write($adresse{$key_Status_Gruen},$Status_Rot,5);
+ #knx_write($adresse{$key_Status_Blau},$Status_Rot,5);
+ return 0;
+}
+
+###### Ende Sende KNX Status ######################
+
+
+######### Abfrage Routine #########################
+sub Abfrage_Status {
+
+my($LNr)=@_;
+my $Adresse=$Bridge.$LNr;
+my $req=HTTP::Request->new( 'GET', $Adresse );
+ my $lwp=LWP::UserAgent->new;
+ my $response= $lwp->request( $req )->content;
+ my ($response_hue, $response_sat, $response_bri);
+ my ($response_string, $response_on_off);
+ ($response_hue)=$response=~m/"hue":(\d+)/g;
+ ($response_sat)=$response=~m/"sat":(\d+)/g;
+ ($response_bri)=$response=~m/"bri":(\d+)/g;
+ ($response_string)=$response=~m/"on":(\w+)/g;
+ if ($response_string eq 'false'){
+ $response_on_off=0;
+ }
+ else {$response_on_off=1;};
+ return ($response_on_off,$response_hue,$response_sat,$response_bri);
+
+}
+########## Ende Abfrage Routine ####################
+
+
+########## Sende Routine #########################
+sub Sende_Befehl {
+
+my($LNr, $Bef)=@_;
+my $Adresse=$Bridge.$LNr.'/state';
+my $req=HTTP::Request->new( 'PUT', $Adresse );
+ $req->header( 'Content-Type' => 'application/json' );
+ $req->content( $Bef );
+ my $lwp=LWP::UserAgent->new;
+ $lwp->request( $req );
+}
+########## Ende Sende Routine #####################
+
+# ----------------------------- Ende Unterprogramme ---------------------------------------
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2014-12-28 07:35:36
|
Revision: 2297
http://sourceforge.net/p/openautomation/code/2297
Author: pfry
Date: 2014-12-28 07:35:33 +0000 (Sun, 28 Dec 2014)
Log Message:
-----------
Aktualisierung
Modified Paths:
--------------
wiregate/plugin/generic/Heizungsregler.pl
Added Paths:
-----------
wiregate/plugin/generic/conf.d/Heizungsregler.conf2
Modified: wiregate/plugin/generic/Heizungsregler.pl
===================================================================
--- wiregate/plugin/generic/Heizungsregler.pl 2014-12-27 13:11:19 UTC (rev 2296)
+++ wiregate/plugin/generic/Heizungsregler.pl 2014-12-28 07:35:33 UTC (rev 2297)
@@ -4,6 +4,8 @@
# Wiregate-Plugin
# (c) 2012 Fry under the GNU Public License
+# COMPILE_PLUGIN
+
#$plugin_info{$plugname.'_cycle'}=0; return "deaktiviert";
use POSIX qw(floor strftime);
@@ -11,9 +13,16 @@
my $use_short_names=1; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
+sub groupaddress;
+
# Konfigfile seit dem letzten Mal geaendert?
my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname";
$conf.='.conf' unless $conf=~s/\.pl$/.conf/;
+unless(-f $conf)
+{
+ plugin_log($plugname, "Config err: $conf nicht gefunden.");
+ exit;
+}
my $configtime=24*60*60*(-M $conf);
my $config_modified = ($configtime < $plugin_info{$plugname.'_configtime'}-1);
@@ -23,43 +32,39 @@
{ $event='restart'; } # Restart des daemons / Reboot
elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
{ $event='modified'; } # Plugin modifiziert
-elsif (%msg)
-{
- $event='bus';
- unless($plugin_subscribe{$msg{dst}}{$plugname})
- {
- $event='cycle';
- }
- else
- {
- return if !$config_modified && $msg{apci} eq "A_GroupValue_Response";
- }
-} # Bustraffic
-elsif ($fh) { $event='socket'; } # Netzwerktraffic
+elsif (%msg) { $event='bus'; return if !$config_modified && $msg{apci} eq "A_GroupValue_Response"; } # Bustraffic
+#elsif ($fh) { $event='socket'; } # Netzwerktraffic
else { $event='cycle'; } # Zyklus
-# Konfigurationsfile einlesen
-my %house=();
-my $err=read_from_house_config();
-return $err if $err;
-
-# Initialisierung
-my %dyn=recall_from_plugin_info();
+# Rueckgabewert des Plugins
my $retval='';
+my $anynews=0;
my $t=time();
-if($event=~/restart|modified/)
+if($event=~/restart|modified/ || $config_modified || !defined $plugin_cache{$plugname}{house})
{
- # alle Variablen loeschen
+ my %house=();
+
+ # Konfigurationsfile einlesen
+ open CONFIG, "<$conf" || return "no config found";
+ my $lines = join '',<CONFIG>;
+ close CONFIG;
+ eval $lines;
+ return "config error: $@" if $@;
+
+ # Cleanup, wobei persistente dyn-Variablen (zB Solltemperaturen) gerettet werden:
+ my $dyn=recall_from_plugin_info(\%house);
for my $k (grep /^$plugname\_/, keys %plugin_info)
{
next if $k=~/^$plugname\_last/;
delete $plugin_info{$k};
}
- store_to_plugin_info(\%dyn);
+ store_to_plugin_info($dyn,\%house);
- $plugin_info{$plugname.'_configtime'}=$configtime;
-
+ # %house im Cache speichern, so muss nicht jedesmal das Config eingelesen werden
+ $plugin_cache{$plugname}{house}=\%house;
+ $plugin_cache{$plugname}{actuators}={}; # Status Stellventile
+
# Alle Controller-GAs abonnieren
for my $r (grep ref($house{$_}), keys %house)
{
@@ -70,69 +75,110 @@
$plugin_subscribe{groupaddress($house{$r}{reset})}{$plugname}=1 if defined $house{$r}{reset};
}
- $plugin_info{$plugname.'_cycle'}=$house{cycle};
+ $plugin_subscribe{groupaddress($house{control_switch})}{$plugname}=1 if defined $house{control_switch};
+ $plugin_subscribe{groupaddress($house{control_status})}{$plugname}=1 if defined $house{control_status};
+ $plugin_info{$plugname.'_control_status'}=1 unless defined $plugin_info{$plugname.'_control_status'};
- $retval.='initialisiert: ';
+ $plugin_info{$plugname.'_configtime'}=$configtime;
+
+ $retval.='initialisiert: '.(join ',', (grep ref($house{$_}), keys %house)).'; ';
$event='cycle';
}
+my $house=$plugin_cache{$plugname}{house};
+my $dyn=recall_from_plugin_info($house); # hier stehen bspw die persistenten Solltemperaturen
+
+#plugin_log($plugname, "event=$event");
+
# Zyklischer Aufruf - Regelung
if($event=~/cycle/)
{
+ return if $plugin_info{$plugname.'_control_status'}==0;
+
my $Vreq=undef;
- my $anynews=0;
- for my $r (sort grep ref($house{$_}), keys %house)
+ # Ist die Therme ueberhaupt an?
+ my $heat=knx_read($house->{heating},3000);
+ $heat=(defined $heat && $heat!~/^$house->{heating_off}$/i);
+ $retval.="Heating is OFF" unless $heat;
+ plugin_log($plugname, "Heating is OFF") unless $heat;
+
+ for my $r (sort grep ref($house->{$_}), keys %{$house})
{
-# plugin_log($plugname, "\$dyn{$r}{mode}==$dyn{$r}{mode}");
- if($dyn{$r}{mode} eq 'ON')
+ if($heat && $dyn->{$r}{mode} eq 'ON')
{
# PID-Regler
- my ($T,$T0,$U,$Vr)=PID($r);
+ my ($T,$T0,$U,$Vr)=PID($r,$house,$dyn);
$retval.=sprintf "$r\->%.1f(%.1f)%d%% ", $T?$T:"T?", $T0, 100*$U;
$anynews=1;
$Vreq=$Vr if defined $Vr && (!defined $Vreq || $Vr>$Vreq);
}
- elsif($dyn{$r}{mode} eq 'OPTIMIZE')
+ elsif($heat && $dyn->{$r}{mode} eq 'OPTIMIZE')
{
# Optimierung der PID-Parameter durch Ermittlung der Sprungantwort
- $retval.="$r\->".OPTIMIZE($r);
+ $retval.="$r\->".OPTIMIZE($r,$house,$dyn,$conf);
$anynews=1;
- $Vreq=$house{inflow_max} if defined $house{inflow_max};
+ $Vreq=$house->{inflow_max} if defined $house->{inflow_max};
}
- elsif($dyn{$r}{mode} eq 'OFF')
+ elsif(!$heat)
{
+ writeactuators($r,0,$house); # alle ventile AUS
+ }
+ else
+ {
+ writeactuators($r,0,$house); # alle ventile AUS
+ $dyn->{$r}{mode}='OFF';
$retval.="$r\->OFF ";
}
}
- if(defined $Vreq)
+ if($heat && defined $Vreq)
{
- $Vreq=$house{inflow_max} if defined $house{inflow_max} && $Vreq>$house{inflow_max};
- knx_write(groupaddress($house{inflow_control}),$Vreq,9.001) if defined $house{inflow_control};
- $retval.=sprintf "Vreq=%d", $Vreq;
+ $Vreq=$house->{inflow_max} if defined $house->{inflow_max} && $Vreq>$house->{inflow_max};
+# knx_write(groupaddress($house->{inflow_control}),$Vreq,9.001) if defined $house->{inflow_control};
+ $retval.=sprintf "KNX-would-be:Vreq=%d", $Vreq;
$anynews=1;
}
$retval=~s/\s*$//; # Space am Ende entfernen
+
+ $plugin_info{$plugname.'_cycle'}=$house->{cycle};
}
elsif($event=~/bus/)
{
- # Aufruf durch GA - neue Wunschtemperatur
+ # Aufruf durch GA
my $ga=$msg{dst};
+ # Jemand will den Heizungsregler an- oder ausschalten, oder den Schaltstatus erfahren
+ if($ga eq groupaddress($house->{control_switch}))
+ {
+ if($msg{apci} eq 'A_GroupValue_Write')
+ {
+ $plugin_info{$plugname.'_control_status'}=int($msg{value});
+ knx_write(groupaddress($house->{control_status}), $plugin_info{$plugname.'_control_status'});
+ $plugin_info{$plugname.'_cycle'}=1; # sofort zyklisch ausfuehren, um alle Ventile zu stellen
+ }
+ return $retval;
+ }
+ elsif($ga eq groupaddress($house->{control_status}))
+ {
+ knx_write(groupaddress($house->{control_status}), $plugin_info{$plugname.'_control_status'}, undef, 0x40)
+ if $msg{apci} eq 'A_GroupValue_Read';;
+ return $retval;
+ }
+
+ # Telegramm betrifft Wunschtemperatur (setzen oder lesen)
# erstmal den betroffenen Raum finden
- my @controls=(sort grep ref($house{$_}) && groupaddress($house{$_}{control}) eq $ga, keys %house);
- my @optimizes=(sort grep ref($house{$_}) && groupaddress($house{$_}{optimize}) eq $ga, keys %house);
- my @resets=(sort grep ref($house{$_}) && groupaddress($house{$_}{reset}) eq $ga, keys %house);
+ my @controls=(sort grep ref($house->{$_}) && groupaddress($house->{$_}{control}) eq $ga, keys %{$house});
+ my @optimizes=(sort grep ref($house->{$_}) && groupaddress($house->{$_}{optimize}) eq $ga, keys %{$house});
+ my @resets=(sort grep ref($house->{$_}) && groupaddress($house->{$_}{reset}) eq $ga, keys %{$house});
# Unbekannte GA de-abonnieren
unless(@controls || @optimizes || @resets)
{
# GA-Abonnement loeschen
+ plugin_log($plugname, "received $ga -> unsubscribed (".($plugin_subscribe{$ga}{$plugname} ? "was subscribed":"was not subscribed").")");
delete $plugin_subscribe{$ga}{$plugname};
- plugin_log($plugname, "received $ga -> unsubscribed");
-
return;
}
@@ -147,7 +193,7 @@
if(@controls)
{
$r=shift @controls;
- $action='control';
+ $action='control';
}
elsif(@resets)
{
@@ -166,65 +212,77 @@
# Wert des Telegramms, Modus des Reglers abholen
my $T0=0;
$T0 = $msg{value} if $action eq 'control' && defined $msg{value};
- my $mode=$dyn{$r}{mode};
+ my $mode=$dyn->{$r}{mode};
# Jemand moechte einen Sollwert wissen
if($action eq 'control' && $msg{apci} eq 'A_GroupValue_Read')
{
- $T0=$dyn{$r}{T0};
- $T0=$dyn{$r}{T0old} if $dyn{mode} eq 'OPTIMIZE';
+ $T0=$dyn->{$r}{T0};
+ $T0=$dyn->{$r}{T0old} if $dyn->{$r}{mode} eq 'OPTIMIZE';
knx_write($ga,$T0,9.001,0x40);
return;
}
- # spezielle Temperaturwerte sind 0=>OFF und -1=>OPTIMIZE
- if($action eq 'reset' || ($action eq 'control' && $T0==0))
+ # spezielle Temperaturwerte sind 0-15 Grad =>OFF und -1=>OPTIMIZE
+ if($action eq 'reset' || ($action eq 'control' && $T0>=0 && $T0<=15))
{
- RESET($r);
- writeactuators($r,0);
- $dyn{$r}{mode}='OFF';
- $retval.="$r\->OFF";
+ RESET($r,$dyn);
+ writeactuators($r,0,$house);
+ $dyn->{$r}{mode}='OFF';
+ $retval.="$r\->OFF";
+ $anynews=1;
}
elsif($action eq 'optimize' || ($action eq 'control' && $T0==-1))
{
- return if $dyn{$r}{mode} eq 'OPTIMIZE'; # Entprellen
+ return if $dyn->{$r}{mode} eq 'OPTIMIZE'; # Entprellen
# Initialisierung der Optimierungsfunktion
- $dyn{$r}{mode}='OPTIMIZE';
- $dyn{$r}{T0old}=$dyn{$r}{T0};
- writeactuators($r,0);
- my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+ $dyn->{$r}{mode}='OPTIMIZE';
+ $dyn->{$r}{T0old}=$dyn->{$r}{T0};
+ writeactuators($r,0,$house);
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r,$house);
$retval.=sprintf "$r\->OPT", $T;
+ $anynews=1;
}
elsif($action eq 'control') # neue Wunschtemperatur
{
- return if $dyn{$r}{T0} == $T0; # Entprellen
+ return if $dyn->{$r}{T0} == $T0; # Entprellen
- RESET($r) if $mode eq 'OPTIMIZE'; # Optimierung unterbrochen
- $dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
- $dyn{$r}{T0}=$T0;
- my ($T,$T0,$U,$Vr)=PID($r);
+ RESET($r,$dyn) if $mode eq 'OPTIMIZE'; # Optimierung unterbrochen
+ $dyn->{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
+ $dyn->{$r}{T0}=$T0;
+ my ($T,$T0,$U,$Vr)=PID($r,$house,$dyn);
$retval.=sprintf "$r\->%.1f(%.1f)%d%%", $T, $T0, 100*$U;
+ $anynews=1;
}
}
}
# Speichere Statusvariablen aller Regler
-store_to_plugin_info(\%dyn);
+store_to_plugin_info($dyn,$house);
-return $retval eq '' ? 'Heizungsregler: nothing to do... event='.$event : $retval;
+#return $retval eq '' ? 'Heizungsregler: nothing to do... event='.$event : $retval;
+# Heizungsregler fuehrt ein separates Log
+open LOG, ">>/var/log/Heizungsregler.log";
+print LOG strftime("%F %X, ", localtime).$retval."\n";
+close LOG;
+return unless $anynews;
+return if $event=~/cycle/; # nicht den Log vollknallen
+return $retval;
+
+
########## Datenpersistenz - Speichern und Einlesen ###############
sub store_to_plugin_info
{
- my $dyn=shift;
+ my ($dyn,$house)=@_;
# Alle Laufzeitvariablen im Hash %{$dyn}
# in das (flache) Hash plugin_info schreiben
- for my $r (grep ref($house{$_}), keys %house)
+ for my $r (grep ref($house->{$_}), keys %{$house})
{
# Skalare
my @keylist=grep !/^(temps|times|Uvals)$/, keys %{$dyn->{$r}};
@@ -249,53 +307,51 @@
sub recall_from_plugin_info
{
- my %dyn=();
+ my $house=shift;
+ my $dyn={};
- for my $r (grep ref($house{$_}), keys %house)
+ for my $r (grep ref($house->{$_}), keys %{$house})
{
if(defined $plugin_info{$plugname.'_'.$r})
{
my $pi=$plugin_info{$plugname.'_'.$r};
# plugin_log($plugname,$r."> ".$pi) if $r eq 'D2';
- while($pi=~m/\'(.*?)\'=>\'(.*?)\'/g) { $dyn{$r}{$1}=$2; }
+ while($pi=~m/\'(.*?)\'=>\'(.*?)\'/g) { $dyn->{$r}{$1}=$2; }
}
for my $v (qw(temps times Uvals))
{
if(defined $plugin_info{$plugname.'_'.$r.'_'.$v})
{
- @{$dyn{$r}{$v}}=split ',', $plugin_info{$plugname.'_'.$r.'_'.$v};
+ @{$dyn->{$r}{$v}}=split ',', $plugin_info{$plugname.'_'.$r.'_'.$v};
}
else
{
- $dyn{$r}{$v}=[];
+ $dyn->{$r}{$v}=[];
}
}
}
-
- return %dyn;
-}
-sub read_from_house_config
-{
- open CONFIG, "<$conf" || return "no config found";
- my @lines = <CONFIG>;
- close CONFIG;
- eval("@lines");
- return "config error: $@" if $@;
+ return $dyn;
}
sub store_to_house_config
{
- my $r=shift; # der betreffende Raum im Haus
+ my ($r,$house,$conf)=@_; # der betreffende Raum im Haus
open CONFIG, ">>$conf";
- print CONFIG "\$house{$r}{pid}={";
- for my $k (sort keys %{$house{$r}{pid}})
+ print CONFIG "\$house->{$r}{pid}={";
+ for my $k (sort keys %{$house->{$r}{pid}})
{
- print CONFIG sprintf "$k=>%f, ", $house{$r}{pid}{$k} unless $k eq 'date';
- print CONFIG "$k=>'$house{$r}{pid}{date}'," if $k eq 'date';
+ unless($k eq 'date')
+ {
+ print CONFIG sprintf "'$k'=>%f, ", $house->{$r}{pid}{$k};
+ }
+ else
+ {
+ print CONFIG "'$k'=>'$house->{$r}{pid}{date}', " if $k eq 'date';
+ }
}
print CONFIG "};\n";
close CONFIG;
@@ -305,11 +361,11 @@
sub readsensors
{
- my $r=shift; # interessierender Raum
+ my ($r,$house)=@_; # interessierender Raum
my @substructures=();
- push @substructures, values %{$house{$r}->{circ}} if defined $house{$r}->{circ};
- push @substructures, $house{$r};
+ push @substructures, values %{$house->{$r}->{circ}} if defined $house->{$r}->{circ};
+ push @substructures, $house->{$r};
my %T=();
my %R=();
@@ -330,10 +386,13 @@
{
unless(defined $T{$type}{$s})
{
- # wir lesen mit 1000s aus dem Cache, denn die Temp-Sensoren sind sowieso auf 1wire
+ # wir lesen mit 3000s aus dem Cache, denn die Temp-Sensoren sind sowieso auf 1wire
# (und antworten daher nicht innerhalb der Plugin-Laufzeit).
# Achtung: sowohl Fensterkontakte als auch Temp-Sensoren sollten zyklisch senden!
- $T{$type}{$s}=knx_read($s,5*$house{cycle},$dpt,1000);
+ my $stime=time();
+ $T{$type}{$s}=knx_read($s,3000,$dpt);
+ $stime=time()-$stime;
+ plugin_log($plugname, "knx_read $s took $stime s") if $stime>0.8;
delete $T{$type}{$s} unless defined $T{$type}{$s} && $T{$type}{$s}!=85;
}
}
@@ -364,6 +423,9 @@
# Falls Fensterkontakte nicht lesbar -> Fenster als geschlossen annehmen
$R{window}=0 unless defined $R{window};
+ # Kaputten Estrichfuehler, erkennbar an zu tiefer Temperatur, durch Luftsensor ersetzen
+ $R{floor}=$R{sensor} if defined $R{floor} && defined $R{sensor} && $R{floor}<$R{sensor};
+
# outflow (Ruecklauf) und floor (Estrich) nehmen wir als gleich an,
# falls nicht beide Werte vorhanden sind. Das sollte immer noch besser
# sein als der globale Hauswert, um danach den Spread zu berechnen.
@@ -377,9 +439,13 @@
# nehmen wir die Hauswerte - falls diese verfuegbar sind
for my $type (qw(inflow outflow))
{
- if(!defined $R{$type} && defined $house{$type})
+ if(!defined $R{$type} && defined $house->{$type})
{
- $R{$type} = knx_read(groupaddress($house{$type}),5*$house{cycle},9);
+ my $stime=time();
+ $R{$type} = knx_read(groupaddress($house->{$type}),3000,9);
+ $stime=time()-$stime;
+ plugin_log($plugname, "knx_read $house->{$type} took $stime s") if $stime>0.8;
+
delete $R{$type} unless $R{$type};
}
}
@@ -391,21 +457,20 @@
}
# und wenn alle Stricke reissen, bleibt der vorkonfigurierte Wert
- $R{spread}=$house{spread} unless defined $R{spread};
+ $R{spread}=$house->{spread} unless defined $R{spread};
- #plugin_log($plugname, $r.": ".(join " ", map "$_=$R{$_}", qw(sensor inflow floor outflow spread window)));
+# plugin_log($plugname, $r.": ".(join " ", map "$_=$R{$_}", qw(sensor inflow floor outflow spread window)));
return @R{qw(sensor inflow floor outflow spread window)};
}
sub writeactuators
{
- my $r=shift; # Raum mit Substruktur
- my $U=shift; # Ventileinstellung
+ my ($r,$U,$house)=@_; # Raum mit Substruktur
my @substructures=();
- @substructures=values %{$house{$r}->{circ}} if defined $house{$r}->{circ};
- push @substructures, $house{$r};
+ @substructures=values %{$house->{$r}->{circ}} if defined $house->{$r}->{circ};
+ push @substructures, $house->{$r};
for my $ss (@substructures)
{
@@ -415,7 +480,9 @@
for my $s (@{$ss->{actuator}})
{
- knx_write(groupaddress($s),100*$U,5.001); # DPT NOCH UNKLAR ########
+ knx_write(groupaddress($s),100*$U,5.001);
+ update_rrd($s,'',100*$U) if $house->{rrd};
+ $plugin_cache{$plugname}{actuators}{$s}=100*$U;
}
}
}
@@ -425,45 +492,45 @@
sub RESET
{
- my $r=shift; # zu regelnder Raum im Haus
+ my ($r,$dyn)=@_; # zu regelnder Raum im Haus
- $dyn{$r} = {
- mode=>'OFF', T0=>20, Told=>0, told=>$t, IS=>0, DF=>0,
+ $dyn->{$r} = {
+ mode=>'OFF', T0=>15, Told=>0, told=>$t, IS=>0, DF=>0,
temps=>[], times=>[], Uvals=>[], U=>0
};
}
sub PID
{
- my $r=shift; # zu regelnder Raum im Haus
- my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+ my ($r,$house,$dyn)=@_; # zu regelnder Raum im Haus
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r,$house);
# Ohne Temperaturmessung und Spread keine Regelung -> aufgeben
my ($mode,$T0,$Told,$told,$IS,$DF,$temps,$times,$Uvals,$U)
- = @{$dyn{$r}}{qw(mode T0 Told told IS DF temps times Uvals U)};
+ = @{$dyn->{$r}}{qw(mode T0 Told told IS DF temps times Uvals U)};
return ($T,$T0,$U,0) unless $T && $spread;
# Regelparameter einlesen
my ($Tv,$Tn,$lim,$prop,$refspread)=(30,30,1,1,10); # Defaults
- if(defined $house{$r}{pid})
+ if(defined $house->{$r}{pid})
{
- ($Tv,$Tn,$lim,$prop,$refspread)=@{$house{$r}{pid}}{qw(Tv Tn lim prop refspread)};
+ ($Tv,$Tn,$lim,$prop,$refspread)=@{$house->{$r}{pid}}{qw(Tv Tn lim prop refspread)};
}
else
{
- $Tv=$house{Tv} if defined $house{Tv};
- $Tn=$house{Tn} if defined $house{Tn};
- $lim=$house{lim} if defined $house{lim};
- $prop=$house{prop} if defined $house{prop};
- $refspread=$house{refspread} if defined $house{refspread};
+ $Tv=$house->{Tv} if defined $house->{Tv};
+ $Tn=$house->{Tn} if defined $house->{Tn};
+ $lim=$house->{lim} if defined $house->{lim};
+ $prop=$house->{prop} if defined $house->{prop};
+ $refspread=$house->{refspread} if defined $house->{refspread};
}
$Tv*=60; $Tn*=60; # in Sekunden umrechnen
# Anzahl Datenpunkte fuer Steigungsberechnung
- my $S1=12; $S1=$house{mindata} if defined $house{mindata};
+ my $S1=12; $S1=$house->{mindata} if defined $house->{mindata};
# Anzahl Datenpunkte fuer Ermittlung neuer Vorhaltetemperatur
my $S2=$S1;
@@ -476,20 +543,22 @@
$U=0; # Heizung aus falls Fenster offen
push @{$Uvals}, $U; shift @{$Uvals} while @{$Uvals}>$S2;
- $dyn{$r} = {
+ $dyn->{$r} = {
mode=>$mode, T0=>$T0, Told=>$T, told=>$t, IS=>$IS, DF=>$DF,
temps=>$temps, times=>$times, Uvals=>$Uvals, U=>$U
};
- plugin_log($plugname, "$r: WINDOW");
+# plugin_log($plugname, "$r: WINDOW");
- writeactuators($r,$U);
+ writeactuators($r,$U,$house);
return ($T,$T0,$U,0);
}
- # Skalierung fuer aktuellen Spread
- my $coeff = $refspread/($spread*$prop);
-
+ # Skalierung fuer aktuellen Spread, Regelung wird aggressiver wenn Spread<1
+ my $coeff = $spread>1 ? $refspread/($spread*$prop) : 100;
+ $coeff = 100 if $coeff<0;
+ $coeff = 1 if $coeff<1;
+
# Proportionalteil (P)
my $P = $T0 - $T;
@@ -522,7 +591,14 @@
# und alles zusammen, skaliert mit der aktuellen Spreizung
$U = ($P + $IS + $DF) * $coeff;
- plugin_log($plugname, sprintf("$r: %.1f(%.1f) U = P+I+D = %d%+d%+d = %d",$T,$T0,100*$P*$coeff,100*$IS*$coeff,100*$DF*$coeff,100*$U));
+
+ if($r eq 'WZ')
+ {
+ open LOG, ">>/var/log/Heizungsregler.log";
+ print LOG strftime("%F %X, ", localtime).sprintf("$r: %.1f(%.1f) U = P+I+D = %d%%+%d%%+%d%% = %d%%",$T,$T0,100*$P*$coeff,100*$IS*$coeff,100*$DF*$coeff,100*$U)."\n";
+ print LOG strftime("%F %X, ", localtime).sprintf("$r: coeff = %f, refspread = %f, prop = %f, spread = %f",$coeff,$refspread,$prop,$spread)."\n";
+ close LOG;
+ }
# Stellwert begrenzen auf 0-1
$U=1 if $U>1;
@@ -533,24 +609,24 @@
my $Vr=$V;
if(defined $Vr)
{
- $Vr=$T0+3 if $Vr<$T0+3;
+ $Vr=$T0+3 if $Vr<$T0+3; # mindestens Raumwunschtemperatur + 3 Grad
my $Uavg=0; $Uavg+=$_ foreach (@{$Uvals}); $Uavg/=scalar(@{$Uvals});
$Vr+=1 if $Uavg>0.9;
- $Vr-=1 if $Uavg<0.6 && $V>$T0+6 && $spread>6;
- $Vr-=1 if $Uavg<0.75 && $V>$T0+5 && $spread>5;
- $Vr-=1 if $Uavg<0.7 && $V>$T0+4 && $spread>4;
- $Vr-=1 if $Uavg<0.6 && $V>$T0+3 && $spread>3;
+ $Vr-=1 if $Uavg<0.6 && $V>$T0+6 && $spread>6 || $Uavg<0.75 && $V>$T0+5 && $spread>5
+ || $Uavg<0.7 && $V>$T0+4 && $spread>4 || $Uavg<0.6 && $V>$T0+3 && $spread>3;
+
+# plugin_log($plugname, "room=$r, Uavg = $Uavg, T0=$T0, V=$V, spread=$spread, Vr=$Vr");
}
# Variablen zurueckschreiben
- $dyn{$r} = {
+ $dyn->{$r} = {
mode=>$mode, T0=>$T0, Told=>$T, told=>$t, IS=>$IS, DF=>$DF,
temps=>$temps, times=>$times, Uvals=>$Uvals, U=>$U
};
# Ventil einstellen
- writeactuators($r,$U);
+ writeactuators($r,$U,$house);
# Ist, Soll, Stellwert, Spread, Wunsch-Vorlauftemp.
return ($T,$T0,$U,$Vr);
@@ -560,25 +636,25 @@
sub OPTIMIZE
{
- my $r=shift;
- my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
+ my ($r,$house,$dyn,$conf)=@_;
+ my ($T,$V,$E,$R,$spread,$window)=readsensors($r,$house);
# Ohne Temperaturmessung und Spread keine Regelung -> aufgeben
return "(OPT) " unless defined $T && defined $spread;
# Praktische Abkuerzungen fuer Statusvariablen
- my ($mode,$phase,$T0old) = @{$dyn{$r}}{qw(mode phase T0old)};
+ my ($mode,$phase,$T0old) = @{$dyn->{$r}}{qw(mode phase T0old)};
- plugin_log($plugname, "D2: phase=$phase") if $r eq 'D2';
+# plugin_log($plugname, "$r: phase=$phase");
# Falls Fenster offen -> Abbruch, Heizung aus und Regler resetten
if($window)
{
if($phase ne 'COOL')
{
- RESET($r);
- $dyn{$r}{mode}='ON';
- $dyn{$r}{T0}=$T0old;
+ RESET($r,$dyn);
+ $dyn->{$r}{mode}='ON';
+ $dyn->{$r}{T0}=$T0old;
return "FAILED:WINDOW ";
}
else
@@ -586,11 +662,11 @@
# Tn, Tv, prop und refspread wurden am Ende der HEAT-Periode bereits berechnet.
# Wir nutzen die "cooling"-Periode sowieso nicht fuer die Berechnung der Parameter.
# Also Parameter jetzt (Beginn "cooling") schon ins Konfig-File schreiben.
- my ($Tn, $Tv, $prop, $refspread) = @{$dyn{$r}}{qw(Tn Tv prop refspread)};
+ my ($Tn, $Tv, $prop, $refspread) = @{$dyn->{$r}}{qw(Tn Tv prop refspread)};
my $date=strftime("%F %X",localtime);
my $lim=0.5;
- $house{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
- store_to_house_config($r);
+ $house->{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
+ store_to_house_config($r,$house,$conf);
}
}
@@ -600,17 +676,17 @@
if($phase eq 'WAIT')
{
- if(defined $V && defined $house{inflow_max} && $V<$house{inflow_max}-3)
+ if(defined $V && defined $house->{inflow_max} && $V<$house->{inflow_max}-3)
{
- writeactuators($r,0); # noch nicht heizen
+ writeactuators($r,0,$house); # noch nicht heizen
return "WAIT(V=$V) ";
}
# Falls Heizung noch nicht voll an, jetzt starten
- writeactuators($r,1); # maximal heizen
+ writeactuators($r,1,$house); # maximal heizen
# Temperaturaufzeichnung beginnen
- $dyn{$r} = {
+ $dyn->{$r} = {
mode=>$mode, phase=>'HEAT',
T0old=>$T0old, told=>0, optstart=>$t,
maxpos=>0, maxslope=>0,
@@ -620,13 +696,13 @@
return sprintf("%.1f(HEAT)%.1f ",$T,$spread);
}
- my ($optstart, $sumspread, $told, $temps, $times) = @{$dyn{$r}}{qw(optstart sumspread told temps times)};
+ my ($optstart, $sumspread, $told, $temps, $times) = @{$dyn->{$r}}{qw(optstart sumspread told temps times)};
my $tp=$t-$optstart;
# falls aus irgendeinem Grund zu frueh aufgerufen, tu nichts
return sprintf("%.1f(", $T).'SKP'.sprintf(")%.1f ",$spread)
- if $tp-$told<$house{cycle}/2;
+ if $tp-$told<$house->{cycle}/2;
# Temperaturkurve aufzeichnen
push @{$times}, $tp;
@@ -635,11 +711,11 @@
# Anzahl Datenpunkte fuer Steigungsberechnung. Hier verdoppelt, weil wir
# mehr Praezision brauchen.
- my $S1=25; $S1=2*$house{mindata} if defined $house{mindata};
+ my $S1=25; $S1=2*$house->{mindata} if defined $house->{mindata};
if(scalar(@{$temps})<=$S1)
{
- $dyn{$r} = {
+ $dyn->{$r} = {
mode=>$mode, phase=>$phase,
T0old=>$T0old, told=>$tp, optstart=>$optstart,
maxpos=>0, maxslope=>0,
@@ -664,13 +740,13 @@
if($phase eq 'HEAT')
{
- my ($maxpos, $maxslope) = @{$dyn{$r}}{qw(maxpos maxslope)};
+ my ($maxpos, $maxslope) = @{$dyn->{$r}}{qw(maxpos maxslope)};
- plugin_log($plugname, "D2: maxpos=$maxpos maxslope=$maxslope slope=$slope") if $r eq 'D2';
+# plugin_log($plugname, "D2: maxpos=$maxpos maxslope=$maxslope slope=$slope") if $r eq 'D2';
- if($maxslope==0) { $maxslope=0.5; }
+# if($maxslope==0) { $maxslope=0.005; }
- if($slope<=0 || $maxslope<=0 || $slope>=0.6*$maxslope)
+ if($slope<=0 || $maxslope<=0.01 || $slope>=0.6*$maxslope)
{
my $retval='';
@@ -680,7 +756,7 @@
$maxpos = nearest(1,$#{$temps}-$S1/2);
$retval=sprintf "%.2fKph/max=%.2fKph)",$slope,$maxslope;
}
- elsif($slope>0)
+ elsif($slope>0 && $maxslope>0.01)
{
$retval=sprintf "%.2fKph=%d%%", $slope, 100*$slope/$maxslope;
}
@@ -690,7 +766,7 @@
}
# Statusvariablen zurueckschreiben
- $dyn{$r} = {
+ $dyn->{$r} = {
mode=>$mode, phase=>'HEAT',
T0old=>$T0old, told=>$tp, optstart=>$optstart,
maxpos=>$maxpos, maxslope=>$maxslope,
@@ -719,9 +795,9 @@
}
unless(defined $pos2)
{
- RESET($r);
- $dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
- $dyn{$r}{T0}=$T0old;
+ RESET($r,$dyn);
+ $dyn->{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
+ $dyn->{$r}{T0}=$T0old;
return "FAILED:POS2 ";
}
$pos2 = nearest(1,$pos2-$S1/2);
@@ -764,16 +840,16 @@
# alle drei Parameter muessen positiv sein, sonst Fehler
unless($prop>=0 && $Tn>=0 && $Tv>=0)
{
- RESET($r);
- $dyn{$r}{mode}='ON';
- $dyn{$r}{T0}=$T0old;
- $dyn{$r}{Told}=$T;
+ RESET($r,$dyn);
+ $dyn->{$r}{mode}='ON';
+ $dyn->{$r}{T0}=$T0old;
+ $dyn->{$r}{Told}=$T;
return "FAILED:NEG ";
}
# Statusvariablen zurueckschreiben
- $dyn{$r} = {
+ $dyn->{$r} = {
mode=>$mode, phase=>'COOL',
T0old=>$T0old, told=>$tp, optstart=>$optstart,
Tn=>$Tn, Tv=>$Tv, prop=>$prop, refspread=>$refspread, tcool=>$t3,
@@ -781,7 +857,7 @@
};
# Abkuehlung einleiten
- writeactuators($r, 0);
+ writeactuators($r,0,$house);
return sprintf("%.1f(COOL) ",$T);
}
@@ -795,17 +871,17 @@
# aus der Laenge der "cooling"-Periode bis zum Maximum koennte man noch was berechnen,
# aber wir setzen $lim hier als Konstante
my ($Tn, $Tv, $prop, $refspread, $tcool)
- = @{$dyn{$r}}{qw(Tn Tv prop refspread tcool)};
+ = @{$dyn->{$r}}{qw(Tn Tv prop refspread tcool)};
my $date=strftime("%F %X",localtime);
my $lim=0.5;
- $house{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
- store_to_house_config($r);
+ $house->{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
+ store_to_house_config($r,$house,$conf);
# Regelung starten
- RESET($r);
- $dyn{$r}{mode}='ON';
- $dyn{$r}{T0}=$T0old;
- $dyn{$r}{Told}=$T;
+ RESET($r,$dyn);
+ $dyn->{$r}{mode}='ON';
+ $dyn->{$r}{T0}=$T0old;
+ $dyn->{$r}{Told}=$T;
# Info an den User
return sprintf "t=%dh:%02dmin Tv=%.1fmin Tn=%dmin lim=%.1f prop=%.1f spread=%.1f ", $tp/3600,($tp/60)%60,$Tv,$Tn,$lim,$prop,$refspread;
@@ -818,7 +894,7 @@
{
my $short=shift;
- return unless defined $short;
+ return undef unless defined $short;
if(ref $short)
{
@@ -849,38 +925,3 @@
}
}
-sub shortname
-{
- my $gas=shift;
-
- return unless defined $gas;
- return $gas unless $use_short_names;
-
- if(ref $gas)
- {
- my $sh=[];
- for my $ga (@{$gas})
- {
- if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
- {
- push @{$sh}, $eibgaconf{$ga}{short};
- }
- else
- {
- push @{$sh}, $ga;
- }
- }
- return $sh;
- }
- else
- {
- my $sh=$gas;
-
- if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
- {
- $sh=$eibgaconf{$gas}{short};
- }
-
- return $sh;
- }
-}
Added: wiregate/plugin/generic/conf.d/Heizungsregler.conf2
===================================================================
--- wiregate/plugin/generic/conf.d/Heizungsregler.conf2 (rev 0)
+++ wiregate/plugin/generic/conf.d/Heizungsregler.conf2 2014-12-28 07:35:33 UTC (rev 2297)
@@ -0,0 +1,113 @@
+#!/usr/bin/perl
+#
+# Konfiguration Heizungsregler ##########################################
+#
+%house=(
+ cycle => 60,
+
+ # Anzahl der Datenpunkte fuer Trendberechnung
+ mindata => 30,
+
+ inflow_control => 'TS_HE_Zentralvorlauf_Stellwert', # GA zum Setzen der Soll-Vorlauftemperatur
+ inflow_max => 55, # maximale Soll-Vorlauftemperatur
+
+ inflow => 'TO_HE_Zentralvorlauf...
[truncated message content] |
|
From: <mi...@us...> - 2015-01-03 18:44:09
|
Revision: 2306
http://sourceforge.net/p/openautomation/code/2306
Author: mivola
Date: 2015-01-03 18:43:57 +0000 (Sat, 03 Jan 2015)
Log Message:
-----------
add description to "AddOns" for Logikprozessor.pl
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.addRssLogViaCurl.pl
wiregate/plugin/generic/Logikprozessor.addRssLogViaHttpRequest.pl
wiregate/plugin/generic/Logikprozessor.sendNma.pl
Modified: wiregate/plugin/generic/Logikprozessor.addRssLogViaCurl.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.addRssLogViaCurl.pl 2015-01-02 23:39:19 UTC (rev 2305)
+++ wiregate/plugin/generic/Logikprozessor.addRssLogViaCurl.pl 2015-01-03 18:43:57 UTC (rev 2306)
@@ -1,12 +1,22 @@
#!/usr/bin/perl -w
##################
-# Logikprozessor AddOn: create RSS Log via HTTP::Request
+# Logikprozessor AddOn: create an RSS Log entry using curl; this is executed asynchronous to avoid performance problems
+# that might occur using HTTP::Request
##################
#
# COMPILE_PLUGIN
+#
+# benoetigt einen Konfigurationseintrag in Logikprozessor.conf:
+# %settings=(
+# rssLog => {
+# url => 'http://wiregateXYZ/cometvisu/plugins/rsslog/rsslog.php' # URL to rsslog.php which writes the entry to the database
+# }
+# );
+#
+# weitere Erklaerungen: http://knx-user-forum.de/code-schnipsel/19912-neues-plugin-logikprozessor-pl-36.html#post384199
+#
-
-sub addRssLog {
+sub addRssLogViaCurl {
my (%parameters)=@_;
my ($title, $content, $tags, $url);
Modified: wiregate/plugin/generic/Logikprozessor.addRssLogViaHttpRequest.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.addRssLogViaHttpRequest.pl 2015-01-02 23:39:19 UTC (rev 2305)
+++ wiregate/plugin/generic/Logikprozessor.addRssLogViaHttpRequest.pl 2015-01-03 18:43:57 UTC (rev 2306)
@@ -1,9 +1,19 @@
#!/usr/bin/perl -w
##################
-# Logikprozessor AddOn: create RSS Log via HTTP::Request
+# Logikprozessor AddOn: create an RSS Log entry via HTTP::Request
##################
#
# COMPILE_PLUGIN
+#
+# benoetigt einen Konfigurationseintrag in Logikprozessor.conf:
+# %settings=(
+# rssLog => {
+# url => 'http://wiregateXYZ/cometvisu/plugins/rsslog/rsslog.php' # URL to rsslog.php which writes the entry to the database
+# }
+# );
+#
+# weitere Erklaerungen: http://knx-user-forum.de/code-schnipsel/19912-neues-plugin-logikprozessor-pl-36.html#post384199
+#
sub addRssLogViaHttpRequest {
my (%parameters)=@_;
Modified: wiregate/plugin/generic/Logikprozessor.sendNma.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.sendNma.pl 2015-01-02 23:39:19 UTC (rev 2305)
+++ wiregate/plugin/generic/Logikprozessor.sendNma.pl 2015-01-03 18:43:57 UTC (rev 2306)
@@ -4,8 +4,20 @@
##################
#
# COMPILE_PLUGIN
+#
+# benoetigt einen Konfigurationseintrag in Logikprozessor.conf:
+# %settings=(
+# nma => {
+# apikey => "YOUR_API_KEY",
+# application => 'SmartHome Logikprozessor',
+# targetUrl => 'https://URL_TO_YOUR_VISU/cometvisu',
+# url => 'http://www.notifymyandroid.com/publicapi/notify'
+# }
+# );
+#
+# weitere Erklaerungen: http://knx-user-forum.de/code-schnipsel/19912-neues-plugin-logikprozessor-pl-36.html#post384076
+#
-
sub sendNma {
my (%parameters)=@_;
my ($priority, $event, $description, $application, $url, $apikey, $targetUrl);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-06-02 21:02:57
|
Revision: 857
http://openautomation.svn.sourceforge.net/openautomation/?rev=857&view=rev
Author: pfry
Date: 2012-06-02 21:02:50 +0000 (Sat, 02 Jun 2012)
Log Message:
-----------
Anpassung auf allgemeine GA-Struktur
Modified Paths:
--------------
wiregate/plugin/generic/Ansagen.pl
wiregate/plugin/generic/MissGoogle.pl
Modified: wiregate/plugin/generic/Ansagen.pl
===================================================================
--- wiregate/plugin/generic/Ansagen.pl 2012-06-02 19:21:39 UTC (rev 856)
+++ wiregate/plugin/generic/Ansagen.pl 2012-06-02 21:02:50 UTC (rev 857)
@@ -282,9 +282,9 @@
# Konstruiere die abzuspielenden File(s) aus dem GA-Kuerzel
# erster Versuch: eine Datei passt komplett auf das Muster im Kuerzel
my $pat1=$pattern;
-# $pat1=~s/[_\s]+/.*?/g; # allgemeine Fassung
- $pat1=~s/\s+.*$//; # meine spezielle GA-Struktur
- $pat1=~s/_+/.*?/g; # meine spezielle GA-Struktur
+ $pat1=~s/[_\s]+/.*?/g; # allgemeine Fassung
+# $pat1=~s/\s+.*$//; # meine spezielle GA-Struktur
+# $pat1=~s/_+/.*?/g; # meine spezielle GA-Struktur
$pat1='.*'.$pat1.'.*\.wav$';
my @hits=();
@@ -294,8 +294,8 @@
unless(@hits)
{
$pattern='_'.$pattern;
-# $pattern=~s/\s+/_/g; # allgemeine Fassung
- $pattern=~s/\s+.*$//; # meine spezielle GA-Struktur
+ $pattern=~s/\s+/_/g; # allgemeine Fassung
+# $pattern=~s/\s+.*$//; # meine spezielle GA-Struktur
# zweiter Versuch: aus Kuerzeln die Bausteine zusammenbauen
while($pattern=~s/^_([^_]+)//)
Modified: wiregate/plugin/generic/MissGoogle.pl
===================================================================
--- wiregate/plugin/generic/MissGoogle.pl 2012-06-02 19:21:39 UTC (rev 856)
+++ wiregate/plugin/generic/MissGoogle.pl 2012-06-02 21:02:50 UTC (rev 857)
@@ -16,6 +16,9 @@
"./Ciao.wav"=>"tschau",
"./Bitte Schluesselbund ans Schluesselbrett haengen.wav"=>"Bitte Schluesselbund ans Schluesselbrett haengen",
"./Der Trockner ist fertig.wav"=>"Der Trockner ist fertig",
+ "./Bitte Papiermuell rausstellen.wav"=>"Bitte Papiermuell rausstellen",
+ "./Bitte Biomuell rausstellen.wav"=>"Bitte Biomuell rausstellen",
+ "./Bitte Restmuell rausstellen.wav"=>"Bitte Restmuell rausstellen",
"./Die Alarmanlage ist aktiviert.wav"=>"Die Alarmanlage ist aktiviert",
"./Die Alarmanlage ist deaktiviert.wav"=>"Die Alarmanlage ist deaktiviert",
"./Die Alarmanlage wird in fuenf Minuten aktiviert.wav"=>"Die Alarmanlage wird in fuenf Minuten aktiviert",
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pf...@us...> - 2012-06-13 22:07:18
|
Revision: 881
http://openautomation.svn.sourceforge.net/openautomation/?rev=881&view=rev
Author: pfry
Date: 2012-06-13 22:07:11 +0000 (Wed, 13 Jun 2012)
Log Message:
-----------
Bugfix bei der Erkennung eines ver?\195?\164nderten Configfiles
Modified Paths:
--------------
wiregate/plugin/generic/Logikprozessor.pl
wiregate/plugin/generic/Szenencontroller.pl
Modified: wiregate/plugin/generic/Logikprozessor.pl
===================================================================
--- wiregate/plugin/generic/Logikprozessor.pl 2012-06-13 21:58:48 UTC (rev 880)
+++ wiregate/plugin/generic/Logikprozessor.pl 2012-06-13 22:07:11 UTC (rev 881)
@@ -74,7 +74,8 @@
else { $event='cycle'; } # Zyklus
# Konfigfile seit dem letzten Mal geaendert?
-my $config_modified = (24*60*60*(-M $conf)-time()) > $plugin_info{$plugname.'_configtime'};
+my $configtime=24*60*60*(-M $conf);
+my $config_modified = ($configtime < $plugin_info{$plugname.'_configtime'}-1);
# Plugin-Code
my $retval='';
@@ -84,10 +85,11 @@
# alle Variablen loeschen
for my $k (grep /^$plugname\_/, keys %plugin_info)
{
+ next if $k=~/^$plugname\_last/;
delete $plugin_info{$k};
}
- $plugin_info{$plugname.'_configtime'}=(24*60*60*(-M $conf)-time());
+ $plugin_info{$plugname.'_configtime'}=$configtime;
my $count=0;
my $err=0;
@@ -404,7 +406,7 @@
# Schaltjahr?
my $leapyear = ($Y % 4)==0 && ($Y % 100!=0 || $Y % 400==0);
- # Osterdatum berechnen (Algorithmus von Ron Mallen, Codefragment von Randy McLeary)
+ # Osterdatum berechnen (Algorithmus von Ron Mallen, Codefragment von Randy McLeary, beruht auf der Formel von Gauss/Lichtenberg)
my $C = int($Y/100);
my $G = $Y%19;
my $K = int(($C - 17)/25);
Modified: wiregate/plugin/generic/Szenencontroller.pl
===================================================================
--- wiregate/plugin/generic/Szenencontroller.pl 2012-06-13 21:58:48 UTC (rev 880)
+++ wiregate/plugin/generic/Szenencontroller.pl 2012-06-13 22:07:11 UTC (rev 881)
@@ -49,7 +49,8 @@
return $err if $err;
# Konfigfile seit dem letzten Mal geaendert?
-my $config_modified = ($scene{storage} ne 'configfile' && (24*60*60*(-M $conf)-time()) > $plugin_info{$plugname.'_configtime'});
+my $configtime=24*60*60*(-M $conf);
+my $config_modified = ($scene{storage} ne 'configfile' && $configtime < $plugin_info{$plugname.'_configtime'}-1);
# Dynamisch definierte Szenen aus plugin_info einlesen
recall_from_plugin_info();
@@ -59,14 +60,15 @@
if($event=~/restart|modified/ || $config_modified)
{
- $plugin_info{$plugname.'_configtime'}=(24*60*60*(-M $conf)-time());
-
# Cleanup aller Szenenvariablen in %plugin_info
for my $k (grep /^$plugname\_/, keys %plugin_info)
{
+ next if $k=~/^$plugname\_last/;
delete $plugin_info{$k};
}
+ $plugin_info{$plugname.'_configtime'}=$configtime;
+
# Alle Szenen-GAs abonnieren
my $count=0;
my $scene_lookup='';
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ma...@us...> - 2012-06-17 15:08:22
|
Revision: 886
http://openautomation.svn.sourceforge.net/openautomation/?rev=886&view=rev
Author: makki1
Date: 2012-06-17 15:08:14 +0000 (Sun, 17 Jun 2012)
Log Message:
-----------
CUL/CUN adjustments, better EM1000, storable-example for complex datatypes
Modified Paths:
--------------
wiregate/plugin/generic/cul868
Added Paths:
-----------
wiregate/plugin/generic/Plugininfo_StoreComplex.pl
Added: wiregate/plugin/generic/Plugininfo_StoreComplex.pl
===================================================================
--- wiregate/plugin/generic/Plugininfo_StoreComplex.pl (rev 0)
+++ wiregate/plugin/generic/Plugininfo_StoreComplex.pl 2012-06-17 15:08:14 UTC (rev 886)
@@ -0,0 +1,19 @@
+# Beispielplugin um komplexe Datenstrukturen in $plugin_info abzulegen
+# V0.1
+
+use Storable;
+
+$plugin_info{$plugname."_cycle"} = 86400;
+
+my %localhash = ( 'test' => 'value1',
+ 'counter' => $plugin_info{$plugname."_ticks"} );
+
+store \%localhash, $plugin_info{$plugname."_complex"};
+
+return "stored hash in plugin_info";
+
+# zum laden sowas wie:
+#use Storable;
+#my %loadedhash = %{retrieve $plugin_info{'PluginInfo_StoreComplex.pl_complex'}};
+#return $loadedhash{'counter'};
+
Modified: wiregate/plugin/generic/cul868
===================================================================
--- wiregate/plugin/generic/cul868 2012-06-15 21:37:21 UTC (rev 885)
+++ wiregate/plugin/generic/cul868 2012-06-17 15:08:14 UTC (rev 886)
@@ -1,5 +1,5 @@
# Plugin zum verarbeiten aller Funktelegramme von FS20/(K)S300/FHZ/FHT/HMS/EM via CUL
-# Version: 0.71 2010-07-28
+# Version: 0.72 2012-06-17
# - KS300,S300TH-Empfang
# - FS20<->KNX
@@ -98,7 +98,7 @@
# RM100: T3
return "HMS recv: $buf T$type Status $stat P$prf Batt:$bat Code:$HA Val:$values";
}
- elsif($fn eq "K" && $len == 15) # KS300
+ elsif($fn eq "K" && $len >= 15) # KS300
{
my @a = split("", $buf);
my $rain = sprintf("%0.1f", hex("$a[14]$a[11]$a[12]") * 255 / 1000);
@@ -117,7 +117,7 @@
knx_write($ks300_israin_ga,$ir,1);
return "KS300 recv: $val";
}
- elsif($fn eq "K" && $len == 9) # S300TH
+ elsif($fn eq "K" && $len >= 9) # S300TH
{
my @a = split("", $buf);
my $addr = $a[2]+(hex($a[1])&7);
@@ -145,7 +145,19 @@
# 1111: cumulated value
# 2222: last value (Not set for type 2)
# 3333: top value (Not set for type 2)
- return "Debug EM recv: $buf";
+ #FIXME: ist noch nicht 100% ..
+ my @a = split("", $buf);
+ my $type = $a[2];
+ my $addr = $a[3] . $a[4];
+ my $seqno = hex($a[5].$a[6]);
+ my $total_cnt = hex($a[ 9].$a[10].$a[ 7].$a[ 8]) / 100;
+ my $current_cnt = hex($a[13].$a[14].$a[11].$a[12]) / 100;
+ my $peak_cnt = hex($a[17].$a[18].$a[15].$a[16]) / 100;
+ # these are the raw readings from the device
+ my $val = sprintf("CNT: %d CUM: %0.3f kW 5MIN: %0.3f kW TOP: %0.3f kW",
+ $seqno, $total_cnt, $current_cnt, $peak_cnt);
+ # nun sollten wir noch was sinnvolles tun, aufn Bus schreiben oder so
+ return "Debug EM recv: $buf type $type addr $addr $val";
}
elsif($fn eq "?" or $fn eq "") # empty/error-message
{
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <j-...@us...> - 2012-07-06 16:07:11
|
Revision: 912
http://openautomation.svn.sourceforge.net/openautomation/?rev=912&view=rev
Author: j-n-k
Date: 2012-07-06 16:07:04 +0000 (Fri, 06 Jul 2012)
Log Message:
-----------
Erfuellen scheinbar nicht die Qualitaetskriterien
Removed Paths:
-------------
wiregate/plugin/generic/Fensterstatus.pl
wiregate/plugin/generic/Heizung.pl
Deleted: wiregate/plugin/generic/Fensterstatus.pl
===================================================================
--- wiregate/plugin/generic/Fensterstatus.pl 2012-07-06 14:42:12 UTC (rev 911)
+++ wiregate/plugin/generic/Fensterstatus.pl 2012-07-06 16:07:04 UTC (rev 912)
@@ -1,51 +0,0 @@
-# Plugin zum Erfassen des Gesamt-Fenster-Status
-# Version 0.3 / 24.09.2011
-# Copyright: JNK (http://knx-user-forum.de/members/jnk.html)
-# License: GPL (v2)
-#
-
-####################
-###Einstellungen:###
-####################
-
-my @Fenster_GA = ('7/7/7', '7/7/8'); # hier alle Fenster-Einzel-GA
-my $Sammel_GA = '7/7/9'; # hier die Sammel-GA
-my $zustand_geschlossen = 01; # auf richtige polarit\xE4t achten!
-my $zustand_offen = 00;
-
-######################
-##ENDE Einstellungen##
-######################
-
-#
-# Flossen weg, der Rest geht automatisch
-#
-$plugin_info{$plugname.'_cycle'} = 0; # nur bei Telegramm aufrufen
-
-if (($msg{'apci'} eq 'A_GroupValue_Write') && (grep {$_ eq $msg{'dst'};} @Fenster_GA)) {
- # Telegramm auf einer Einzel-GA erhalten
- my $status = $zustand_geschlossen; # mit geschlossen anfangen
- my $old_status = $plugin_info{$plugname.'_oldstatus'};
- foreach my $GA (@Fenster_GA) {
- if (knx_read($GA, 0, 1) == $zustand_offen) { # da war eine 'offen', also Status auf 1 setzen
- $status = $zustand_offen;
- last;
- }
- }
- if ($old_status != $status){ #
- knx_write($Sammel_GA, $status, 1); # Status hat sich geaendert, senden
- $plugin_info{$plugname.'_oldstatus'} = $status; # neuen Status speichern
- return 'Sent Value'.$status;
- }
- return 0; # nichts gesendet, Also
-}
-
-# keine Telegramme, also Init
-
-foreach my $GA (@Fenster_GA) { # an allen Fenster-Einzel-GA anmelden
- $plugin_subscribe{$GA}{$plugname} = 1;
-}
-# bis zum Beweis des Gegenteils sind alle Fenster zu
-$plugin_info{$plugname.'_oldstatus'} = $zustand_geschlossen;
-
-return 'Init';
\ No newline at end of file
Deleted: wiregate/plugin/generic/Heizung.pl
===================================================================
--- wiregate/plugin/generic/Heizung.pl 2012-07-06 14:42:12 UTC (rev 911)
+++ wiregate/plugin/generic/Heizung.pl 2012-07-06 16:07:04 UTC (rev 912)
@@ -1,58 +0,0 @@
-# Plugin zum Erfassen des Gesamt-Fenster-Status
-# Version 0.1 / 18.05.2011
-# Copyright: JNK (http://knx-user-forum.de/members/jnk.html)
-# License: GPL (v2)
-#
-
-####################
-###Einstellungen:###
-####################
-
-my $schritt_ga = '4/1/3'; # Gruppenadresse Sollwert Auf = 1 / Ab = 0
-my $sollwert_ga = '4/2/3'; # Gruppenadresse Sollwert
-my $sollwert = 15;
-my $sollwertmin = 10; # Sollwert Minimum
-my $sollwertmax = 25; # Sollwert Maximum
-
-
-######################
-##ENDE Einstellungen##
-######################
-
-# Eigenen Aufruf-Zyklus auf 1x st\xFCndlich setzen, h\xF6rt ja auf GA
-$plugin_info{$plugname.'_cycle'} = 3600;
-$sollwert = $plugin_info{$plugname.'_sollwert'};
-
-# Plugin an Gruppenadresse "anmelden"
-$plugin_subscribe{$schritt_ga}{$plugname} = 1;
-$plugin_subscribe{$sollwert_ga}{$plugname} = 1;
-
-# 1=auf, 0=ab
-if ($msg{'apci'} eq "A_GroupValue_Write" && $msg{'dst'} eq $schritt_ga && defined $msg{'value'} && $msg{'value'} == "0" ) {
- if($sollwert>$sollwertmin) {
- $sollwert -= 0.5;
- knx_write($sollwert_ga,$sollwert,9);
- $plugin_info{$plugname.'_sollwert'} = $sollwert;
- return 1;
- }
-}
-
-if ($msg{'apci'} eq "A_GroupValue_Write" && $msg{'dst'} eq $schritt_ga && defined $msg{'value'} && $msg{'value'} == "1" ) {
- if($sollwert<$sollwertmax) {
- $sollwert += 0.5;
- knx_write($sollwert_ga,$sollwert,9);
- $plugin_info{$plugname.'_sollwert'} = $sollwert;
- return 1;
- }
-}
-
-#Sollwert vom Bus lesen, wenn von dort gesendet
-if ($msg{'apci'} eq "A_GroupValue_Write" && $msg{'dst'} eq $sollwert_ga ) {
- $msg{'value'} = decode_dpt9($msg{'data'});
- $plugin_info{$plugname.'_sollwert'} = $msg{'value'};
- return 2;
-}
-
-knx_write($sollwert_ga,$sollwert,9);
-
-return 0;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|