Script in Modul "umwandeln"

Begonnen von fhainz, 14 Dezember 2013, 12:43:23

Vorheriges Thema - Nächstes Thema

fhainz

Hallo!

Ich hab in den letzten Tagen versucht mein Script zur Stromverbrauchs Berechnung über die Laufzeit in ein Modul umzuwandeln. Hab mich durch diverse Wiki- und Foren Einträge geschlagen aber wenn ich ehrlich bin, bin ich nicht schlauer als vorher.

Das Ding macht bisher folgendes: Wenn ich einen Aktor zB. Steckdose, UP-Licht-Schalter, etc. einschalte wird die Start-Zeit ins Reading des Aktors geschrieben. Beim Ausschalten wird dann der Verbrauch berechnet und ins Reading geschrieben. Das funktioniert mittlerweile.
Der Aufruf des Scripts erfolgt derzeit über ein notify. define n_DeckenfluterVerbrauchR notify wzDeckenfluter { stromverbrauch($NAME, <WATT>);; }

Mein Ziel ist es das man das Modul definiert und es per on/off des Aktors automatisch gestartet und beendet wird. In einem Abstand von x Sekunden soll das Modul wenn der Aktor ein ist starten, die Readings neu berechnen und eintragen.
Dieser Abstands soll sowie die Leistung in Watt per Attribut definierbar sein.
Wenn ich das geschafft hab, kommt noch der Verbrauch von Heute, Monat, etc.

Nun meine Frage: Gibt es irgendwo eine Anleitung wie man ein Modul erstellt die ich übersehen hab? Oder kann mir das vielleicht jemand erklären?

Viele Grüße,
fhainz

Und sry falls ich das falsche Forum erwischt hab. Wusste nicht genau wohin damit ;)

fhainz

#1
Ich hab jetzt einfach versucht aus vorhandenen Modulen mir etwas zusammen zu kopieren.
Wie zu erwarten funktioniert das ganze nicht ;)

Ich schaff es nicht mal das Modul zu definieren, bekomm immer Unknown module currentCalculator . Kann mir jemand sagen was ich alles falsch mache? Hab ich es wenigstens ansatzweise richtig gemacht oder bin ich komplett am falschen weg?

Hier mal das Modul:
package main;

use strict;
use warnings;
use Time::HiRes qw(gettimeofday sleep);

sub currentCalculator_Get($@);
sub currentCalculator_Define($$);
sub currentCalculator_GetStatus($;$);
sub currentCalculator_Undefine($$);


sub
currentCalculator_Initialize($)
{
my ($hash) = @_;

$hash->{GetFn}     = "currentCalculator_Get";
  $hash->{SetFn}     = "currentCalculator_Set";
  $hash->{DefFn}    = "currentCalculator_Define";
  $hash->{UndefFn}   = "currentCalculator_Undefine"; 
  $hash->{ParseFn}  = "currentCalculator_ParseFn";
}

#####################################

sub
currentCalculator_Define($$)
{
my ($hash, $def) = @_;

  my @a = split("[ \t][ \t]*", $def);

  return "Usage: define <name> currentCalculator [aktor] [watt] [invterval]"  if(@a < 4 || @a > 5);

  my $name = $a[0];
  my $device = $a[2]; 
  my $watt = $a[3];
 
  my $interval = 60;
  $interval = $a[4] if($interval < 60);
 
  $hash->{NAME} = $name;
  $hash->{DEVICE} = $device;
  $hash->{STATE} = "Active";
  $hash->{INTERVAL} = $interval;
  $hash->{WATT} = $watt;

  return undef;
}

sub
currentCalculator_Set()
{}

sub currentCalculator_Undefine($$)
{}


sub
currentCalculator_ParseFn($)
{
  my ($hash) = @_;
  my $name = $hash->{NAME};
  my $device = $hash->{DEVICE};

#----- Untoggle Fix-----#
if ( Value($hash->{DEVICE}) eq "toggle" ) {
if ( OldValue($hash->{DEVICE}) eq "off" ) {
fhem("set $device on");
}
else {
fhem("set $device off");
}
}

my $on = "";
$hash->{ON} = $on;

if ( Value($device) eq "on" ) {
$hash->{ON} = int( time() );

speedtest_setReading( $hash->{DEVICE}, "on", $hash->{ON} );
speedtest_setReading( $hash->{DEVICE}, "power", $hash->{WATT} );
}
elsif ( Value($device) eq "off" ) {
currentCalculator_berechnen( $hash->{DEVICE}, $hash->{WATT} );
}
else {
Log3 $hash->{NAME}, 3, "Untoggle Problem";
}
}

#-----     Stromverbrauch berechnen     -----#
sub
currentCalculator_Berechnen($)
{
my ($hash) = @_;

$hash->{ON} = ReadingsVal( $hash->{DEVICE}, "on", "0" );
$hash->{OFF} = int( time() );

$hash->{ONLAST} = $hash->{OFF} - $hash->{ON};

$hash->{ONTOTAL}          = ReadingsVal( $hash->{DEVICE}, "onTotal",          "0" );
$hash->{CONSUMPTIONTOTAL} = ReadingsVal( $hash->{DEVICE}, "consumptionTotal", "" );
$hash->{ONTOTAL}          = int( $hash->{ONTOTAL} + $hash->{ONLAST} );

$hash->{CONSUMPTIONLAST}  = $hash->{ONLAST} * $hash->{WATT} / 1000 / 3600;
$hash->{CONSUMPTIONTOTAL} = $hash->{CONSUMPTIONTOTAL} + $hash->{CONSUMPTIONLAST};

#$consumptionTotal = sprintf "%.2f", $consumptionTotal;

if ( $hash->{ON} > 0 ) {
setReading( $hash->{DEVICE}, "on", 0 );
setReading( $hash->{DEVICE}, "off", $hash->{OFF} );
setReading( $hash->{DEVICE}, "onLast", $hash->{ONLAST} );
setReading( $hash->{DEVICE}, "onTotal", $hash->{ONTOTAL} );
setReading( $hash->{DEVICE}, "power", "0" );
setReading( $hash->{DEVICE}, "consumptionTotal", $hash->{CONSUMPTIONTOTAL} );
}
else {
Log3 $hash->{DEVICE}, 3, "Keine Startzeit ermittelt";
}
}

#-----     setReading     -----#
sub setReading($$$) {
my ($hash, $readingsName, $readingsWert) = @_;

readingsBeginUpdate( $hash );
readingsBulkUpdate( $hash, $readingsName, $readingsWert );
readingsBulkUpdate( $hash, $readingsName, $readingsWert );
readingsEndUpdate( $hash, 1 );
}


Ich hoffe mir kann jemand weiterhelfen.

Grüße

justme1968

ganz ans ende muss ein 1;sonst läd fhem das modul nicht.

für die notifys ist notifyFn zuständig. nicht parseFn.

es taucht noch ein spendetest_... auf. das ist ziemlich sicher nicht beabsichtig.

die mehrfachen readingsBeginUpdate/readingsEndUpdate sind nicht optimal. die würde ich auf einen rutsch schreiben.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

fhainz

Ich hab die Punkte geändert. Nun schmiert FHEM direkt nach dem speichern der fhem.cfg ab.

package main;

use strict;
use warnings;
use Time::HiRes qw(gettimeofday sleep);

sub currentCalculator_Get($@);
sub currentCalculator_Define($$);
sub currentCalculator_GetStatus($;$);
sub currentCalculator_Undefine($$);

sub currentCalculator_Initialize($) {
my ($hash) = @_;

$hash->{GetFn}   = "currentCalculator_Get";
$hash->{SetFn}   = "currentCalculator_Set";
$hash->{DefFn}   = "currentCalculator_Define";
$hash->{UndefFn} = "currentCalculator_Undefine";
$hash->{NotifyFn} = "currentCalculator_NotifyFn";
}

#####################################

sub currentCalculator_Define($$) {
my ( $hash, $def ) = @_;

my @a = split( "[ \t][ \t]*", $def );
return "Usage: define <name> currentCalculator [aktor] [watt] [invterval]"
if ( @a < 4 || @a > 5 );

my $name   = $a[0];
my $device = $a[2];
my $watt   = $a[3];

my $interval = 60;
$interval = $a[4] if ( $interval < 60 );

$hash->{NAME}     = $name;
$hash->{DEVICE}   = $device;
$hash->{STATE}    = "Active";
$hash->{INTERVAL} = $interval;
$hash->{WATT}     = $watt;

return undef;
}

sub currentCalculator_Set() {
}

sub currentCalculator_Undefine($$) { }

sub currentCalculator_NotifyFn($) {
my ($hash) = @_;
my $name   = $hash->{NAME};
my $device = $hash->{DEVICE};

#----- Untoggle Fix-----#
if ( Value( $hash->{DEVICE} ) eq "toggle" ) {
if ( OldValue( $hash->{DEVICE} ) eq "off" ) {
fhem("set $device on");
}
else {
fhem("set $device off");
}
}

my $on = "";
$hash->{ON} = $on;

if ( Value($device) eq "on" ) {
$hash->{ON} = int( time() );

setReading( $hash->{DEVICE}, "on",    $hash->{ON} );
setReading( $hash->{DEVICE}, "power", $hash->{WATT} );
}
elsif ( Value($device) eq "off" ) {
currentCalculator_berechnen( $hash->{DEVICE}, $hash->{WATT} );
}
else {
Log3 $hash->{NAME}, 3, "Untoggle Problem";
}
}

#-----     Stromverbrauch berechnen     -----#
sub currentCalculator_Berechnen($) {
my ($hash) = @_;

$hash->{ON} = ReadingsVal( $hash->{DEVICE}, "on", "0" );
$hash->{OFF} = int( time() );

$hash->{ONLAST} = $hash->{OFF} - $hash->{ON};

$hash->{ONTOTAL} = ReadingsVal( $hash->{DEVICE}, "onTotal", "0" );
$hash->{CONSUMPTIONTOTAL} = ReadingsVal( $hash->{DEVICE}, "consumptionTotal", "" );
$hash->{ONTOTAL} = int( $hash->{ONTOTAL} + $hash->{ONLAST} );

$hash->{CONSUMPTIONLAST} = $hash->{ONLAST} * $hash->{WATT} / 1000 / 3600;
$hash->{CONSUMPTIONTOTAL} = $hash->{CONSUMPTIONTOTAL} + $hash->{CONSUMPTIONLAST};

#$consumptionTotal = sprintf "%.2f", $consumptionTotal;

if ( $hash->{ON} > 0 ) {
setReading( $hash->{DEVICE}, "on",      0 );
setReading( $hash->{DEVICE}, "off",     $hash->{OFF} );
setReading( $hash->{DEVICE}, "onLast",  $hash->{ONLAST} );
setReading( $hash->{DEVICE}, "onTotal", $hash->{ONTOTAL} );
setReading( $hash->{DEVICE}, "power",   "0" );
setReading( $hash->{DEVICE}, "consumptionTotal",
$hash->{CONSUMPTIONTOTAL} );
}
else {
Log3 $hash->{DEVICE}, 3, "Keine Startzeit ermittelt";
}
}

#-----     setReading     -----#
sub setReading($$$) {
my ( $hash, $readingsName, $readingsWert ) = @_;

readingsBeginUpdate($hash);
readingsBulkUpdate( $hash, $readingsName, $readingsWert );
readingsEndUpdate( $hash, 1 );
}

1;


grüße

justme1968

schau ins log was der grund ist.

oder auf der konsole auf der du fhem gestartet hast.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

fhainz

#5
Leider steht nichts im Log das sich auf das Modul bezieht. Nur das neustarten nach dem Absturz.

Edit:
Ich habs nochmal versucht. Dieses mal direkt in der Eingabezeile in fhemweb. Selbes Ergebnis:
2013.12.15 15:12:36.163 3: FS20 set wzDeckenfluter on
2013.12.15 15:12:36.847 1: Perfmon: possible freeze starting at 15:12:34, delay is 2.847
2013.12.15 15:12:45.518 1: Perfmon: possible freeze starting at 15:12:39, delay is 6.518
2013.12.15 15:12:50.803 1: Perfmon: possible freeze starting at 15:12:46, delay is 4.802
2013.12.15 15:13:40.131 1: Including fhem.cfg

Direkt nachdem mich den deckenfluter eingeschaltet hab hab ich in die adresszeile define sv_wzDeckenfluter currentCalculator wzDeckenfluter 9 60 kopiert, bestätigt, abgestürzt.
Die freeze's sind "normal" die hab ich ständig.

John

Hallo fhainz,

vielleicht hilft dir ja mein Betriebsstundenzähler weiter
http://www.fhemwiki.de/wiki/HourCounter

Über UserReadings kannst du beliebige Wert, also auch den Stromverbrauch über die Einschaltzeit berechnen lassen.


John
CubieTruck Docker Node-Red Tasmota Shelly Homematic-IP

fhainz

Hallo John,

den HourCounter hab ich mir schon angesehen. Habs mir auch teilweise zur Vorlage genommen aber leider blick ich nicht ganz durch den code.
Wahrscheinlich würde es mit deinem Modul auch irgendwie klappen, aber das bringt mir in meiner jetzigen Situation nicht. Ich will ja lernen wie man ein Modul schreibt.
Deshalb auch der "aufwand" etwas neu zu programmieren was es vielleicht in irgendeiner form schon gibt.

Grüße

UliM

setz mal
attr global verbose 5
bevor du das define absetzt.
Vll siehst Du dann besser, bis zu welcher Stelle das ganze kommt.
Vorher vll. noch Dein Prog mit
Log 1, cc: Zeile ###";
pflastern, damit Du sihest, was noch aufgerufen wurde und was nicht mehr.

Es könnte an der leeren set-routine liegen, denn fhemweb setzt zum Anzeigen des device ein "set <device> ?" ab, um die verfügbaren Befehle zurückzubekommen.

Wenn Du nur eine Variable belegst, funktioniert = @_ nicht, da musst Du shift nehmen.

Wird denn eine Fehlermeldung erzeugt, wenn Du ein reload Deines Moduls ausführst?

=8-)
RPi4/Raspbian, CUL V3 (ca. 30 HomeMatic-devices), LAN (HarmonyHub, alexa etc.).  Fördermitglied des FHEM e.V.

justme1968

starte vor allem fhem selber von hand auf der kommandozeile so das du ausgaben sehen kannst oder leite stdout und stderr um so das du beides auch im log hast.

my ($hash) = @_;funktioniert auch mit einer variable. shift nur verwenden wenn du @_ auch tastächlich modifizieren willst.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

fhainz

Ok habs num mit verbose 5 versucht.

Im Log steht folgendes:
Zitat2013.12.15 17:47:55.012 4: HTTP FHEMWEB:10.0.0.1:65154 GET /fhem&cmd=define+sv_wzDeckenfluter+currentCalculator+wzDeckenfluter+9+60
2013.12.15 17:47:55.017 5: Cmd: >define sv_wzDeckenfluter currentCalculator wzDeckenfluter 9 60<
2013.12.15 17:47:55.020 5: Loading ./FHEM/60_currentCalculator.pm
2013.12.15 17:47:55.078 5: Triggering global (1 changes)
2013.12.15 17:47:55.090 5: Notify loop for global DEFINED sv_wzDeckenfluter
2013.12.15 17:47:55.190 4: HTTP FHEMWEB:10.0.0.1:65154 GET /fhem?detail=sv_wzDeckenfluter
2013.12.15 17:49:22.531 1: Including fhem.cfg

Im Telnet Fenster hatte ich nur
Zitat2013-12-15 17:47:55.175 Global global DEFINED sv_wzDeckenfluter
Connection closed by foreign host.

Ich hab dann die Zeilen logs eingebaut. Ich komm bis zum return undef; in der sub currentCalculator_Define()
Hab direkt drunter noch ein Log und der wird nicht angezeigt.

Hilft das was?

Grüße

justme1968

schau auf die konsole wenn du fhem von hand startest.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

fhainz

Was meinst du mit von hand starten?

Nachdem abstürzen starte ich das raspi der sudo reboot neu.

justme1968

also wenn fhem abschmiert musst du nicht den ganzen rechner neu starten.

irgendwo gibt es ein start script das fhem startet. das kannst du auch von hand starten. etwas in der art /etc/init.d/fhem startje nach user vermutlich mit sudo. in diesem terminal in dem du das gemacht hast sollte beim absturz etwas zu sehen sein.

du kannst auch die zeile perl fhem.pl fhem.cfg so ändern das stdout und stderr geloggt werden:perl fhem.pl fhem.cfg >>/opt/fhem/log/fhem-stdout 2>>/opt/fhem/log/fhem-stderr

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

fhainz

Zitat von: justme1968 am 15 Dezember 2013, 18:36:59also wenn fhem abschmiert musst du nicht den ganzen rechner neu starten.
Gut zu wissen ;) Das script ist in dem ordner den du beschrieben hast.

Zitat von: justme1968 am 15 Dezember 2013, 18:36:59in diesem terminal in dem du das gemacht hast sollte beim absturz etwas zu sehen sein.
Wann? Wenn ich dann per telnet eingeloggt bin?

Zitat von: justme1968 am 15 Dezember 2013, 18:36:59du kannst auch die zeile perl fhem.pl fhem.cfg so ändern das stdout und stderr geloggt werden:perl fhem.pl fhem.cfg >>/opt/fhem/log/fhem-stdout 2>>/opt/fhem/log/fhem-stderr
Wo soll sich diese Zeile befinden?

Ich glaub ich steh am Schlauch  :(

grüße