Datenerfassung ortsnetz-auslastung.de

Begonnen von Hadl, 08 Oktober 2026, 00:14:46

Vorheriges Thema - Nächstes Thema

KölnSolar

Hatte die Speicher noch gar nicht drin. Wer bereits ein readingsProxy angelegt hatte, muss nur die Zeile zum Batterydevice anfügen, das setstate zur battery_capacity ausführen und natürlich die geänderte sub in die myUtils.

Neuanlage wäre dann jetzt so:
define EuerProxydevice readingsProxy \
EuerZaehlerdevice:EuerSpannungswertreadingPhase1:l1_v \
EuerZaehlerdevice:EuerSpannungswertreadingPhase2:l2_v \
EuerZaehlerdevice:EuerSpannungswertreadingPhase3:l3_v \
EuerZaehlerdevice:EuerStromstaerkereadingPhase1:l1_a \
EuerZaehlerdevice:EuerStromstaerkereadingPhase2:l2_a \
EuerZaehlerdevice:EuerStromstaerkereadingPhase3:l3_a \
EuerZaehlerdevice:EuerLeistungsreading:power_w \
EuerZaehlerdevice:EuerFrequenzwertreading:grid_frequency_hz \
EuerForecastdevice:EuerForecastreading:pv_forecast_kwh \
EuerBatterydevice:EuerSocreading:battery_soc_percent
attr EuerProxydevice room Euerroom

setstate EuerProxydevice 2026-10-10 21:49:13 latitude Eurelatitude
setstate EuerProxydevice 2026-10-10 21:49:13 longitude Eurelongitude
setstate EuerProxydevice 2026-10-10 21:49:13 plant_capacity_kwp EurePVAnlagenleistung
setstate EuerProxydevice 2026-10-10 21:49:13 battery_capacity EureSpeicherkapazität
setstate EuerProxydevice 2026-10-10 21:49:13 smartmeter_model EuerSmartmetermodell

und die sub. Ströme jetzt als Absolutwerte.

#######################################################################################################
# Ortsnetzauslastung - support project https://ortsnetz-auslastung.de/
# Subroutine to submit grid, solar forecast and battery values
# prereqisitions:
# proxy-device to collect data from meter, solarforecast and battery device. have a look at the wiki for setup
###############################################################################################################
sub Ortsnetz {
#  use HttpUtils;
  use JSON::PP ();

  my $Proxydevice = "Ortsnetz"; 
##########################################
# copy after this mark only
##########################################

#  my $lat  = AttrNum("global", "latitude", -1);
#  my $lon  = AttrNum("global", "longitude", -1);
  my $forecast = ReadingsVal($Proxydevice, "pv_forecast_kwh",-1);
  my $battery_soc_percent = ReadingsVal($Proxydevice, "battery_soc_percent",-1);

  # --------------------------------------------------

  my %data = (
    observed_at         => strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()),
    latitude            => ReadingsVal($Proxydevice, "latitude", -1),
    longitude           => ReadingsVal($Proxydevice, "longitude", -1),
    l1_v                => ReadingsVal($Proxydevice, "l1_v", -1),
    l2_v                => ReadingsVal($Proxydevice, "l2_v", -1),
    l3_v                => ReadingsVal($Proxydevice, "l3_v", -1),
    l1_a                => abs(ReadingsVal($Proxydevice, "l1_a", -1)),
    l2_a                => abs(ReadingsVal($Proxydevice, "l2_a", -1)),
    l3_a                => abs(ReadingsVal($Proxydevice, "l3_a", -1)),
    power_w             => ReadingsVal($Proxydevice, "power_w", -1),
    grid_frequency_hz   => ReadingsVal($Proxydevice, "grid_frequency_hz", -1),
    smartmeter_model    => ReadingsVal($Proxydevice, "smartmeter_model",""),
    integration_version => ReadingsVal($Proxydevice, "integration_version","FHEM-v0.1"),
    plant_capacity_kwp  => ReadingsVal($Proxydevice, "plant_capacity_kwp",-1),
    battery_capacity    => ReadingsVal($Proxydevice, "battery_capacity",-1),
  );
 
  $data{pv_forecast_kwh}     = $forecast  if ($forecast > 0);
  $data{battery_soc_percent} = $battery_soc_percent if ($battery_soc_percent > 0);

  my $json = encode_json(\%data);

  my $param = {
    url      => "https://www.ortsnetz-auslastung.de/v1/measurements",
    timeout  => 10,
    method   => "POST",
    header   => "Content-Type: application/json",
    data     => $json,
    hash     => $defs{$Proxydevice},
    callback => sub($$$) {
      my ($param, $err, $data) = @_;
      my $hash = $param->{hash};

      if ($err)                     { Log3 $Proxydevice, 2, "ortsnetz-auslastung.de API Fehler: $err"; return;}
      my $response = eval {JSON::PP->new->utf8(0)->decode($data) };
      if ($@)                       { Log3 $Proxydevice, 2, "ortsnetz-auslastung.de API JSON-Decode-Fehler: $@"; return; }
      if (ref($response) ne "HASH") { Log3 $Proxydevice, 2, "ortsnetz-auslastung.de API: JSON-Antwort ist kein Objekt: $data"; return; }

      readingsBeginUpdate($hash);
      readingsBulkUpdate($hash, "accepted",               $response->{accepted})                if defined $response->{accepted};
      readingsBulkUpdate($hash, "created",                $response->{created})                 if defined $response->{created};
      readingsBulkUpdate($hash, "storage_recommendation", $response->{storage_recommendation})  if defined $response->{storage_recommendation};

      if (ref($response->{status}) eq "HASH") {
        for my $key (qw(l1 l2 l3 overall)) {
          readingsBulkUpdate($hash, "status_$key", $response->{status}{$key})                   if defined $response->{status}{$key};
        }
      }
      readingsEndUpdate($hash, 1);
      Log3 $Proxydevice, 4, "ortsnetz-auslastung.de API Antwort: $data";
    }
  };
  Log3 $Proxydevice, 4, "ortsnetz-auslastung.de API Anfrage: POST $param->{url} | Body: $json";
  HttpUtils_NonblockingGet($param);

}

ZitatMeint ihr es ist für den normalen Nutzer wirklich einfacher, wenn er zur Integration eine perl Funktion in myUtils einkopieren muss, einen readingsProxy konfigurieren und dann den passenden at erzeugen muss?
Zum Aktualisieren muss man dann auch immer diese Stellen passend zueinander halten.

Wie man jetzt sieht, müssten in einem at alle Daten für die Ursprungsdevices/Konstanten angepasst werden.
Ich empfinde es auch deutlich angenehmer, den Code in einem Editor zu bearbeiten, anstatt in der FHEM-Oberfläche.
Für die Problematik der Integrität der sub habe ich mir einen kleinen workaround überlegt, der so richtig erst bei einer nächsten Anpassung zum Tragen kommt.  ;)

Grüße Markus
RPi5/3/2 Trixie-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-ecovacs(mqtt2)-zigbee2mqtt

bertl

Zitat von: KölnSolar am 24 Oktober 2026, 09:41:03und die sub. Ströme jetzt als Absolutwerte.

    l1_a                => abs(ReadingsVal($Proxydevice, "l1_a", -1)),
    l2_a                => abs(ReadingsVal($Proxydevice, "l2_a", -1)),
    l3_a                => abs(ReadingsVal($Proxydevice, "l3_a", -1)),

So würde bei einem fehlenden Strom Reading eine 1 anstatt -1 übermittelt werden.
Sollte noch angepasst werden.

Schönes WE, Robert

KölnSolar

Seeehr aufmerksam. Danke Robert.

Ich habe es mir einfach gemacht und Stromwerte von genau -1 würden nicht übertragen. Ich denke vernachlässigbar....

Hier die angepasste sub. Nicht mehr komplett, sondern nur der Part ohne userspezifische Namensgebung:
##########################################
# copy and replace after this mark only
##########################################

#  my $lat  = AttrNum("global", "latitude", -1);
#  my $lon  = AttrNum("global", "longitude", -1);
  my $forecast = ReadingsVal($Proxydevice, "pv_forecast_kwh",-1);
  my $battery_soc_percent = ReadingsVal($Proxydevice, "battery_soc_percent",-1);

  # --------------------------------------------------

  my %data = (
    observed_at         => strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()),
    latitude            => ReadingsVal($Proxydevice, "latitude", -1),
    longitude           => ReadingsVal($Proxydevice, "longitude", -1),
    l1_v                => ReadingsVal($Proxydevice, "l1_v", -1),
    l2_v                => ReadingsVal($Proxydevice, "l2_v", -1),
    l3_v                => ReadingsVal($Proxydevice, "l3_v", -1),
    l1_a                => ReadingsVal($Proxydevice, "l1_a", -1),
    l2_a                => ReadingsVal($Proxydevice, "l2_a", -1),
    l3_a                => ReadingsVal($Proxydevice, "l3_a", -1),
    power_w             => ReadingsVal($Proxydevice, "power_w", -1),
    grid_frequency_hz   => ReadingsVal($Proxydevice, "grid_frequency_hz", -1),
    smartmeter_model    => ReadingsVal($Proxydevice, "smartmeter_model",""),
    integration_version => ReadingsVal($Proxydevice, "integration_version","FHEM-v0.1"),
    plant_capacity_kwp  => ReadingsVal($Proxydevice, "plant_capacity_kwp",-1),
    battery_capacity    => ReadingsVal($Proxydevice, "battery_capacity",-1),
  );
 
  $data{pv_forecast_kwh}     = $forecast  if ($forecast > 0);
  $data{battery_soc_percent} = $battery_soc_percent if ($battery_soc_percent > 0);

  $data{l1_a} = abs($data{l1_a}) if ($data{l1_a} != -1);
  $data{l2_a} = abs($data{l2_a}) if ($data{l2_a} != -1);
  $data{l3_a} = abs($data{l3_a}) if ($data{l3_a} != -1);

  my $json = encode_json(\%data);

  my $param = {
    url      => "https://www.ortsnetz-auslastung.de/v1/measurements",
    timeout  => 10,
    method   => "POST",
    header   => "Content-Type: application/json",
    data     => $json,
    hash     => $defs{$Proxydevice},
    callback => sub($$$) {
      my ($param, $err, $data) = @_;
      my $hash = $param->{hash};

      if ($err)                     { Log3 $Proxydevice, 2, "ortsnetz-auslastung.de API Fehler: $err"; return;}
      my $response = eval {JSON::PP->new->utf8(0)->decode($data) };
      if ($@)                       { Log3 $Proxydevice, 2, "ortsnetz-auslastung.de API JSON-Decode-Fehler: $@"; return; }
      if (ref($response) ne "HASH") { Log3 $Proxydevice, 2, "ortsnetz-auslastung.de API: JSON-Antwort ist kein Objekt: $data"; return; }

      readingsBeginUpdate($hash);
      readingsBulkUpdate($hash, "accepted",               $response->{accepted})                if defined $response->{accepted};
      readingsBulkUpdate($hash, "created",                $response->{created})                 if defined $response->{created};
      readingsBulkUpdate($hash, "storage_recommendation", $response->{storage_recommendation})  if defined $response->{storage_recommendation};

      if (ref($response->{status}) eq "HASH") {
        for my $key (qw(l1 l2 l3 overall)) {
          readingsBulkUpdate($hash, "status_$key", $response->{status}{$key})                   if defined $response->{status}{$key};
        }
      }
      readingsEndUpdate($hash, 1);
      Log3 $Proxydevice, 4, "ortsnetz-auslastung.de API Antwort: $data";
    }
  };
  Log3 $Proxydevice, 4, "ortsnetz-auslastung.de API Anfrage: POST $param->{url} | Body: $json";
  HttpUtils_NonblockingGet($param);

}
RPi5/3/2 Trixie-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-ecovacs(mqtt2)-zigbee2mqtt