Page 1 of 1

Solaredge Modbus another variant

Posted: Friday 11 September 2020 13:02
by boskant
Hi all,

Because my readings where very slow (i am on a rpi 1) i modified some scripts and, just wanted to share these.

I did modify the script SolarEdge.perl (Timo Kokkonen) somewhat so i could run it as a service on the pi.
So my connection to the inverter stays open all the time, so no need to make a connection and compile the script at every run.
This is now working for 2 days as a charm :-) and i get updates every second ( this is adjustable)

The code which follows i didn't clean it, it just a copy of my files.

Code: Select all

#!/usr/bin/perl
#
# sunspec-monitor
#
# Copyright (C) 2017-2018 Timo Kokkonen <[email protected]>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA  02110-1301, USA.
#
# Modified by P.v.d. Linden 10-09-2020
# Created loop so script wil run as a service
# Added option --update=<sec>, -u <sec> this will generate output every xx sec
#

use Data::Dumper;
use Device::Modbus::TCP::Client;
use Getopt::Long;
use strict;
use LWP::Simple;


my $UPDATE_EVERY = 1 ;
my $TIMEOUT = 10;
my $PORT = 502;

my $SUNSPEC_I_COMMON_BLOCK_START = 40000;
my $SUNSPEC_I_MODEL_BLOCK_START = 40069;
my $SUNSPEC_M_COMMON_BLOCK_START = 40121;
my $SUNSPEC_M_MODEL_BLOCK_START = 40188;


#####################################################################
# For Domoticz, please edit below to your own environment           #
#####################################################################
my $domoip ="192.168.0.5"; # Domoticz ipaddress.
my $dport = "8080";   #Domoticz port number.
#my $idxSEstat = 4;   # idx value of SolarEdge Status, Text device
#my $idxActPwrAC = 89; # idx value of SolarEdge Actual Power, Electric (Instant+Counter, Computed)) device   = Power Output (AC)
#my $idxActPwrDC=6;   # idx value of SolarEdge Actual Power, Electric (Instant+Counter, Computed) device    = Power Input (DC)
#my $idxACinv = 7;    # idx value of AC Inverter, Voltage device                                            = Voltage (AC)
#my $idxac_i = 8;     # idx value of AC Inverter, Amp device                                                = Current (AC)
#my $idxAC_f = 9;     # idx value of AC Inverter, Frequency text device                                     = Voltage (AC) (Hz)
#my $idxDCinv = 10;   # idx value of DC Inverter, Voltage device                                            = Voltage (DC)
#my $idxdc_i = 11;    # idx value of DC Inverter, Amp device                                                = Current (DC)
#my $idxEffin = 12;   # idx value of SolarEdge Efficiency, Percentage device                                = Efficiency
my $idxTemp  = 74;    # idx value of SalorEdge Temperature, Temperature device                              = Temperature
my $idxTotPwr = 75;   # idx value of Total LifeTime Production, Custom Sensor                               = Total Production
######################################################################
# Domoticz extra variables  used                                     #
######################################################################
my $version ="1.22";
my $url ="";
my $content="";
my $content="";
my $dwh=0;
my $deff=0;
my $dtemp=0;
my $dac_f=0;


my %I_STATUS = (
    0=>'Unknown',
    1=>'OFF',
    2=>'SLEEPING',
    3=>'STARTING',
    4=>'ON (MPPT)',
    5=>'THROTTLED',
    6=>'SHUTTING DOWN',
    7=>'FAULT',
    8=>'STANDBY'
    );



# get_inverter_common_block - return a hash containing Sunspec
#                             Common Block values
#
sub get_inverter_common_block(*) {
    my ($ctx) = @_;

    my $req = $ctx->read_holding_registers(unit=>1,
                                           address=>$SUNSPEC_I_COMMON_BLOCK_START,
                                           quantity=>69);
    $ctx->send_request($req) || die("send_request(): failed");

    my $adu = $ctx->receive_response();
    die("Read of Common block registers failed (device does not support SunSpec?)")
        unless ($adu->success);


    my $data = pack("n*",@{$adu->values});
    my $cb;
    $cb->{C_SunSpec_ID} = unpack("a4",substr($data,0,4));
    $cb->{C_SunSpec_DID} = unpack("n",substr($data,4,2));
    $cb->{C_SunSpec_Length} = unpack("n",substr($data,6,2));
    $cb->{C_Manufacturer} = unpack("a*",substr($data,8,32));
    $cb->{C_Manufacturer} =~ s/\0+$//;
    $cb->{C_Model} = unpack("a*",substr($data,40,32));
    $cb->{C_Model} =~ s/\0+$//;
    $cb->{C_Version} = unpack("a*",substr($data,88,16));
    $cb->{C_Version} =~ s/\0+$//;
    $cb->{C_SerialNumber} = unpack("a*",substr($data,104,32));
    $cb->{C_SerialNumber} =~ s/\0+$//;
    $cb->{C_DeviceAddress} = unpack("n",substr($data,136,2));


    die("Non SunSpec common block received (not SunSpec compliant device?)")
             unless ($cb->{C_SunSpec_ID} eq "SunS" && $cb->{C_SunSpec_DID} == 1);

    return $cb;
}


# get_inverter_model_block - return a hash containing Sunspec Inverter
#                            Model Block values
#
sub get_inverter_model_block(*) {
    my ($ctx) = @_;

    my $req = $ctx->read_holding_registers(unit=>1,
                                           address=>$SUNSPEC_I_MODEL_BLOCK_START,
                                           quantity=>52);
    $ctx->send_request($req) || die("send_request(): failed");

    my $adu = $ctx->receive_response();
    die("Read of Inverter Model block registers failed (device does not support SunSpec?)")
        unless ($adu->success);


    my $data = pack("n*",@{$adu->values});
    my $mb;
    $mb->{C_SunSpec_DID} = unpack("n",substr($data,0,2));
    $mb->{C_SunSpec_Length} = unpack("n",substr($data,2,2));
    $mb->{I_AC_Current} = unpack("n",substr($data,4,2));
    $mb->{I_AC_CurrentA} = unpack("n",substr($data,6,2));
    $mb->{I_AC_CurrentB} = unpack("n",substr($data,8,2));
    $mb->{I_AC_CurrentC} = unpack("n",substr($data,10,2));
    $mb->{I_AC_Current_SF} = unpack("n!",substr($data,12,2));
    $mb->{I_AC_VoltageAB} = unpack("n",substr($data,14,2));
    $mb->{I_AC_VoltageBC} = unpack("n",substr($data,16,2));
    $mb->{I_AC_VoltageCA} = unpack("n",substr($data,18,2));
    $mb->{I_AC_VoltageAN} = unpack("n",substr($data,20,2));
    $mb->{I_AC_VoltageBN} = unpack("n",substr($data,22,2));
    $mb->{I_AC_VoltageCN} = unpack("n",substr($data,24,2));
    $mb->{I_AC_Voltage_SF} = unpack("n!",substr($data,26,2));
    $mb->{I_AC_Power} = unpack("n!",substr($data,28,2));
    $mb->{I_AC_Power_SF} = unpack("n!",substr($data,30,2));
    $mb->{I_AC_Frequency} = unpack("n",substr($data,32,2));
    $mb->{I_AC_Frequency_SF} = unpack("n!",substr($data,34,2));
    $mb->{I_AC_VA} = unpack("n!",substr($data,36,2));
    $mb->{I_AC_VA_SF} = unpack("n!",substr($data,38,2));
    $mb->{I_AC_VAR} = unpack("n!",substr($data,40,2));
    $mb->{I_AC_VAR_SF} = unpack("n!",substr($data,42,2));
    $mb->{I_AC_PF} = unpack("n!",substr($data,44,2));
    $mb->{I_AC_PF_SF} = unpack("n!",substr($data,46,2));
    $mb->{I_AC_Energy_WH} = unpack("N",substr($data,48,4));
    $mb->{I_AC_Energy_WH_SF} = unpack("n!",substr($data,52,2));
    $mb->{I_DC_Current} = unpack("n",substr($data,54,2));
    $mb->{I_DC_Current_SF} = unpack("n!",substr($data,56,2));
    $mb->{I_DC_Voltage} = unpack("n",substr($data,58,2));
    $mb->{I_DC_Voltage_SF} = unpack("n!",substr($data,60,2));
    $mb->{I_DC_Power} = unpack("n!",substr($data,62,2));
    $mb->{I_DC_Power_SF} = unpack("n!",substr($data,64,2));
    # 40103 unused
    $mb->{I_Temp_Sink} = unpack("n!",substr($data,68,2));
    # 40105-40106 unused
    $mb->{I_Temp_Sink_SF} = unpack("n!",substr($data,74,2));
    $mb->{I_Status} = unpack("n",substr($data,76,2));
    $mb->{I_Status_Vendor} = unpack("n",substr($data,78,2));
    $mb->{I_Event_1} = unpack("N",substr($data,80,4));
    $mb->{I_Event_2} = unpack("N",substr($data,84,4));
    $mb->{I_Event_1_Vendor} = unpack("N",substr($data,88,4));
    $mb->{I_Event_2_Vendor} = unpack("N",substr($data,92,4));
    $mb->{I_Event_3_Vendor} = unpack("N",substr($data,96,4));
    $mb->{I_Event_4_Vendor} = unpack("N",substr($data,100,4));

    die("Non SunSpec Model block received (not SunSpec compliant device?)")
             unless ($mb->{C_SunSpec_Length} == 50);

    return $mb;
}



# Send URL to domoticz to update sensors
# para  idx ,value, verbose
#
sub senddomo(***){
  my ($idx, $value,$verbose) = @_;
  $url = "http://$domoip:".$dport."/json.htm?type=command&param=udevice&idx=$idx&svalue=$value";
  $content = get($url);
  if ($verbose) {
    print "\n";
    print ($url);
        print "\n";
  }
  die "Can't GET $url" if (! defined $content);
}





# get_meter_common_block - return a has containing Sunspec
#                          Meter Common block values
#  meter = 1..3
#
sub get_meter_common_block(**) {
    my ($ctx,$meter) = @_;

    $meter=1 if ($meter < 1);
    $meter=3 if ($meter > 3);
    my $offset = ($meter - 1) * 174;

    my $req = $ctx->read_holding_registers(unit=>1,
                                           address=>$SUNSPEC_M_COMMON_BLOCK_START + $offset,
                                           quantity=>65);
    $ctx->send_request($req) || die("send_request(): failed");

    my $adu = $ctx->receive_response();
    die("Read of Meter $meter block registers failed (device does not support SunSpec?)")
        unless ($adu->success);


    my $data = pack("n*",@{$adu->values});
    my $cb;
    $cb->{C_SunSpec_DID} = unpack("n",substr($data,0,2));
    $cb->{C_SunSpec_Length} = unpack("n",substr($data,2,2));
    $cb->{C_Manufacturer} = unpack("a*",substr($data,4,32));
    $cb->{C_Manufacturer} =~ s/\0+$//;
    $cb->{C_Model} = unpack("a*",substr($data,36,32));
    $cb->{C_Model} =~ s/\0+$//;
    $cb->{C_Option} = unpack("a*",substr($data,68,16));
    $cb->{C_Option} =~ s/\0+$//;
    $cb->{C_Version} = unpack("a*",substr($data,84,16));
    $cb->{C_Version} =~ s/\0+$//;
    $cb->{C_SerialNumber} = unpack("a*",substr($data,100,32));
    $cb->{C_SerialNumber} =~ s/\0+$//;


    die("Non SunSpec Meter Common block received (not SunSpec compliant device?)")
             unless ($cb->{C_SunSpec_Length} == 65);

    return $cb;
}


# get_meter_model_block - return a has containing Sunspec
#                         Meter Model block values
#  meter = 1..3
#
sub get_meter_model_block(**) {
    my ($ctx,$meter) = @_;

    $meter=1 if ($meter < 1);
    $meter=3 if ($meter > 3);
    my $offset = ($meter - 1) * 174;

    my $req = $ctx->read_holding_registers(unit=>1,
                                           address=>$SUNSPEC_M_MODEL_BLOCK_START + $offset,
                                           quantity=>107);
    $ctx->send_request($req) || die("send_request(): failed");

    my $adu = $ctx->receive_response();
    die("Read of Meter $meter block registers failed (device does not support SunSpec?)")
        unless ($adu->success);


    my $data = pack("n*",@{$adu->values});
    my $mb;
    $mb->{C_SunSpec_DID} = unpack("n",substr($data,0,2));
    $mb->{C_SunSpec_Length} = unpack("n",substr($data,2,2));
    $mb->{M_AC_Current} = unpack("n!",substr($data,4,2));
    $mb->{M_AC_Current_A} = unpack("n!",substr($data,6,2));
    $mb->{M_AC_Current_B} = unpack("n!",substr($data,8,2));
    $mb->{M_AC_Current_C} = unpack("n!",substr($data,10,2));
    $mb->{M_AC_Current_SF} = unpack("n!",substr($data,12,2));
    $mb->{M_AC_Voltage_LN} = unpack("n!",substr($data,14,2));
    $mb->{M_AC_Voltage_AN} = unpack("n!",substr($data,16,2));
    $mb->{M_AC_Voltage_BN} = unpack("n!",substr($data,18,2));
    $mb->{M_AC_Voltage_CN} = unpack("n!",substr($data,20,2));
    $mb->{M_AC_Voltage_LL} = unpack("n!",substr($data,22,2));
    $mb->{M_AC_Voltage_AB} = unpack("n!",substr($data,24,2));
    $mb->{M_AC_Voltage_BC} = unpack("n!",substr($data,26,2));
    $mb->{M_AC_Voltage_CA} = unpack("n!",substr($data,28,2));
    $mb->{M_AC_Voltage_SF} = unpack("n!",substr($data,30,2));
    $mb->{M_AC_Freq} = unpack("n!",substr($data,32,2));
    $mb->{M_AC_Freq_SF} = unpack("n!",substr($data,34,2));
    $mb->{M_AC_Power} = unpack("n!",substr($data,36,2));
    $mb->{M_AC_Power_A} = unpack("n!",substr($data,38,2));
    $mb->{M_AC_Power_B} = unpack("n!",substr($data,40,2));
    $mb->{M_AC_Power_C} = unpack("n!",substr($data,42,2));
    $mb->{M_AC_Power_SF} = unpack("n!",substr($data,44,2));
    $mb->{M_AC_VA} = unpack("n!",substr($data,46,2));
    $mb->{M_AC_VA_A} = unpack("n!",substr($data,48,2));
    $mb->{M_AC_VA_B} = unpack("n!",substr($data,50,2));
    $mb->{M_AC_VA_C} = unpack("n!",substr($data,52,2));
    $mb->{M_AC_VA_SF} = unpack("n!",substr($data,54,2));
    $mb->{M_AC_VAR} = unpack("n!",substr($data,56,2));
    $mb->{M_AC_VAR_A} = unpack("n!",substr($data,58,2));
    $mb->{M_AC_VAR_B} = unpack("n!",substr($data,60,2));
    $mb->{M_AC_VAR_C} = unpack("n!",substr($data,62,2));
    $mb->{M_AC_VAR_SF} = unpack("n!",substr($data,64,2));
    $mb->{M_AC_PF} = unpack("n!",substr($data,66,2));
    $mb->{M_AC_PF_A} = unpack("n!",substr($data,68,2));
    $mb->{M_AC_PF_B} = unpack("n!",substr($data,70,2));
    $mb->{M_AC_PF_C} = unpack("n!",substr($data,72,2));
    $mb->{M_AC_PF_SF} = unpack("n!",substr($data,74,2));
    $mb->{M_Exported} = unpack("N",substr($data,76,4));
    $mb->{M_Exported_A} = unpack("N",substr($data,80,4));
    $mb->{M_Exported_B} = unpack("N",substr($data,84,4));
    $mb->{M_Exported_C} = unpack("N",substr($data,88,4));
    $mb->{M_Imported} = unpack("N",substr($data,92,4));
    $mb->{M_Imported_A} = unpack("N",substr($data,96,4));
    $mb->{M_Imported_B} = unpack("N",substr($data,100,4));
    $mb->{M_Imported_C} = unpack("N",substr($data,104,4));
    $mb->{M_Energy_W_SF} = unpack("n!",substr($data,108,2));
    $mb->{M_Exported_VA} = unpack("N",substr($data,110,4));
    $mb->{M_Exported_VA_A} = unpack("N",substr($data,114,4));
    $mb->{M_Exported_VA_B} = unpack("N",substr($data,118,4));
    $mb->{M_Exported_VA_C} = unpack("N",substr($data,122,4));
    $mb->{M_Imported_VA} = unpack("N",substr($data,126,4));
    $mb->{M_Imported_VA_A} = unpack("N",substr($data,130,4));
    $mb->{M_Imported_VA_B} = unpack("N",substr($data,134,4));
    $mb->{M_Imported_VA_C} = unpack("N",substr($data,138,4));
    $mb->{M_Energy_VA_SF} = unpack("n!",substr($data,142,2));
    $mb->{M_Import_VARh_Q1} = unpack("N",substr($data,144,4));
    $mb->{M_Import_VARh_Q1A} = unpack("N",substr($data,148,4));
    $mb->{M_Import_VARh_Q1B} = unpack("N",substr($data,152,4));
    $mb->{M_Import_VARh_Q1C} = unpack("N",substr($data,156,4));
    $mb->{M_Import_VARh_Q2} = unpack("N",substr($data,160,4));
    $mb->{M_Import_VARh_Q2A} = unpack("N",substr($data,164,4));
    $mb->{M_Import_VARh_Q2B} = unpack("N",substr($data,168,4));
    $mb->{M_Import_VARh_Q2C} = unpack("N",substr($data,172,4));
    $mb->{M_Import_VARh_Q3} = unpack("N",substr($data,176,4));
    $mb->{M_Import_VARh_Q3A} = unpack("N",substr($data,180,4));
    $mb->{M_Import_VARh_Q3B} = unpack("N",substr($data,184,4));
    $mb->{M_Import_VARh_Q3C} = unpack("N",substr($data,188,4));
    $mb->{M_Import_VARh_Q4} = unpack("N",substr($data,192,4));
    $mb->{M_Import_VARh_Q4A} = unpack("N",substr($data,196,4));
    $mb->{M_Import_VARh_Q4B} = unpack("N",substr($data,200,4));
    $mb->{M_Import_VARh_Q4C} = unpack("N",substr($data,204,4));
    $mb->{M_Energy_VAR_SF} = unpack("n!",substr($data,208,2));
    $mb->{M_Events} = unpack("N",substr($data,210,4));


    die("Non SunSpec Meter Common block received (not SunSpec compliant device?)")
             unless ($mb->{C_SunSpec_DID} >= 201 && $mb->{C_SunSpec_DID} <= 204);

    return $mb;
}


# scale_value - scale input value using scale factor (SF)
#
sub scale_value(**) {
  my ($val,$sf) = @_;

  return $val * (10 ** $sf);
}


###################################################################################
# main program

my $verbose_mode = 0;
my $debug_mode = 0;
my $numeric_mode = 0;
my $json_mode = 0;
my $meter = 1;
my $port = $PORT;
my $timeout = $TIMEOUT;
my $domoticz = 0;
my $update_every = $UPDATE_EVERY;

GetOptions("verbose|v" => \$verbose_mode,
           "debug|d" => \$debug_mode,
           "port|p=s" => \$port,
           "meter|m=s" => \$meter,
           "numeric|n" => \$numeric_mode,
           "json|j" => \$json_mode,
           "timeout|t=s" => \$timeout,
           "domoticz|z" => \$domoticz,
           "update|u=s" => \$update_every);

my $host = shift;

unless ($host) {
  print STDERR "syntax: $0 [options] <host>\n\n",
    "Options:\n",
    " --port=<port>, -p <port>      Use port (default $PORT)\n",
    " --meter=<meter>, -m <meter>   Query meter (default 1) \n",
    "                               (meter = 1..3  or 0 = no meter)\n",
    " --numeric, -n                 Numeric output mode (time, status)\n",
    " --json, -j                    Output in JSON (instead of CSV) format\n",
    " --timeout=<sec>, -t <sec>     Timeout (default $TIMEOUT)\n",
    " --verbose, -v                 Verbose mode\n",
    " --domoticz, -z                Domoticz mode\n",
    " --update=<sec>, -u <sec>      Update every (default $UPDATE_EVERY)\n",
    " --debug, -d                   Debug mode\n";
        print "Version : $version\n\n";
  exit(1);
}

die("invalid port ($port) specified") unless ($port > 0 && $port < 65536);
die("invalid meter ($meter) specified") unless ($meter >= 0 && $meter <= 3);


my $c = Device::Modbus::TCP::Client->new(host=>$host,port=>$port,timeout=>$timeout);


if ($verbose_mode) {
        print "Update interval = $update_every \n";
}

my $checktime = time() + $update_every;
while(1) {

if ( $checktime <= time() )
    {

        my $tstamp = time();
        my $cb = get_inverter_common_block($c);

        my $fw_ver = $cb->{C_Version};

        $fw_ver =~ s/^0+//;

        if ($verbose_mode) {
        print "INVERTER:\n";
        print "             Model: $cb->{C_Manufacturer} $cb->{C_Model}\n";
        print "  Firmware version: $fw_ver\n";
        print "     Serial Number: $cb->{C_SerialNumber}\n";
        print Dumper($cb) if ($debug_mode);
        print "\n";
        }

        my $mb = get_inverter_model_block($c);

        my $status = $I_STATUS{$mb->{I_Status}};
        my $ac_power = scale_value($mb->{I_AC_Power},$mb->{I_AC_Power_SF});
        my $dc_power = scale_value($mb->{I_DC_Power},$mb->{I_DC_Power_SF});
        my $temp = scale_value($mb->{I_Temp_Sink},$mb->{I_Temp_Sink_SF});
        my $eff = ($dc_power > 0 ? $ac_power/$dc_power*100 : 0);
        my $dc_v = scale_value($mb->{I_DC_Voltage},$mb->{I_DC_Voltage_SF});
        my $ac_v = scale_value($mb->{I_AC_VoltageAB},$mb->{I_AC_Voltage_SF});
        my $ac_i = scale_value($mb->{I_AC_Current},$mb->{I_AC_Current_SF});
        my $dc_i = scale_value($mb->{I_DC_Current},$mb->{I_DC_Current_SF});
        my $ac_f = scale_value($mb->{I_AC_Frequency},$mb->{I_AC_Frequency_SF});
        my $wh = scale_value($mb->{I_AC_Energy_WH},$mb->{I_AC_Energy_WH_SF});

        if ($verbose_mode) {
        print  "            Status: $status\n\n";
        printf(" Power Output (AC): %12.0f W\n",$ac_power);
        printf("  Power Input (DC): %12.0f W\n",$dc_power);
        printf("        Efficiency: %12.2f %\n",$eff);
        printf("  Total Production: %12.3f kWh\n",$wh/1000);
        printf("      Voltage (AC): %12.2f V (%.2f Hz)\n",$ac_v,$ac_f);
        printf("      Current (AC): %12.2f A\n",$ac_i);
        printf("      Voltage (DC): %12.2f V\n",$dc_v);
        printf("      Current (DC): %12.2f A\n",$dc_i);
        printf("       Temperature: %12.2f C (heatsink)\n",$temp);
        print Dumper($mb) if ($debug_mode);
        print "\n";
        }

        # Domoticz update to sensor routine
        #  idx is the domoticz idx sensor value, value, verbose mode
        #
        #
        if ($domoticz){
        if ($verbose_mode) {
                        print ("********************************************\n");
                        print("domo update mode entered  \n",);
        }

        ## SolarEdge SolarEdge Status, Text device
        #senddomo ($idxSEstat,$status,$verbose_mode);

        ## SolarEdge AC Inverter, Voltage device
        #senddomo ($idxACinv ,$ac_v,$verbose_mode);


        ## SolarEdge AC Inverter, Amp device
        #senddomo ($idxac_i ,$ac_i,$verbose_mode);

        ## SolarEdge AC Inverter, Hertz device
        #$dac_f = sprintf("%.2f Hz", $ac_f);
        #senddomo ($idxAC_f ,$dac_f,$verbose_mode);

        ## SolarEdge DC Inverter, Voltage device
        #senddomo ($idxDCinv ,$dc_v,$verbose_mode);

        ## SolarEdge DC Inverter, Amp device
        #senddomo ($idxdc_i ,$dc_i,$verbose_mode);

        ## SolarEdge Actual Power / Total Production, General, Kwh device
        #senddomo ($idxActPwrAC ,$ac_power,$verbose_mode);

        ## SolarEdge Actual Power / Total Production, General, Kwh device
        #senddomo ($idxActPwrDC, $dc_power,$verbose_mode);

        ## SolarEdge Efficiency, Percentage device
        #$deff = sprintf("%.2f", $eff);
        #senddomo ($idxEffin,$deff,$verbose_mode);

        ## SolarEdge Temperature, Temperature device
        $dtemp = sprintf("%.2f", $temp);
        senddomo ($idxTemp  ,$dtemp,$verbose_mode);

        ## SolarEdge Total Production, General, Kwh device + incr. counter
        $dwh = sprintf("%d;%d", $ac_power, $wh );
        #senddomo ($idxTotPwr,$dwh,$verbose_mode);

		$url = "http://$domoip:".$dport."/json.htm?type=command&param=udevice&idx=$idxTotPwr&&nvalue=0&svalue=$dwh";
		$content = get($url);

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

        }



        my ($m_i, $m_e);

        if ($meter > 0) {
        if ($verbose_mode) {
                my $mcb = get_meter_common_block($c,$meter);
                print "METER (#".$meter."):\n";
                print "             Model: $mcb->{C_Manufacturer} $mcb->{C_Model}\n";
                print "            Option: $mcb->{C_Option}\n";
                print "  Firmware version: $mcb->{C_Version}\n";
                print "     Serial Number: $mcb->{C_SerialNumber}\n";
                print Dumper($mcb) if ($debug_mode);
                print "\n";
        }

        my $mmb = get_meter_model_block($c,1);
        $m_e = scale_value($mmb->{M_Exported},$mmb->{M_Energy_W_SF});
        $m_i = scale_value($mmb->{M_Imported},$mmb->{M_Energy_W_SF});
        my $m_c = scale_value($mmb->{M_AC_Current},$mmb->{M_AC_Current_SF});
        my $m_f = scale_value($mmb->{M_AC_Freq},$mmb->{M_AC_Freq_SF});
        my $m_v = scale_value($mmb->{M_AC_Voltage_AB},$mmb->{M_AC_Voltage_SF});
        my $m_p = scale_value($mmb->{M_AC_Power},$mmb->{M_AC_Power_SF});
        my $m_pa = scale_value($mmb->{M_AC_VA},$mmb->{M_AC_VA_SF});
        my $m_pf = scale_value($mmb->{M_AC_PF}/100,$mmb->{M_AC_PF_SF});

        if ($verbose_mode) {
                printf("   Exported Energy: %12.3f kWh\n",$m_e/1000);
                printf("   Imported Energy: %12.3f kWh\n",$m_i/1000);
                printf("        Real Power: %12.0f W\n",$m_p);
                printf("    Apparent Power: %12.0f VA\n",$m_pa);
                printf("      Power Factor: %12.2f\n",$m_pf);
                printf("      Voltage (AC): %12.2f V (%.2f Hz)\n",$m_v,$m_f);
                printf("      Current (AC): %12.2f A\n",$m_c);
                print Dumper($mmb) if ($debug_mode);
                print "\n";
        }
        }



        unless ($verbose_mode) {
                print("timestamp,status,ac_power,dc_power,total_production,ac_voltage,ac_current,dc_voltage,dc_current,te
        mperature,exported_energy,imporoted_energy\n") if ($debug_mode);

                if ($numeric_mode) {
                        $status=$mb->{I_Status};
                } else {
                        my($sec,$min,$hour,$day,$month,$year) = (localtime($tstamp))[0,1,2,3,4,5];
                        $tstamp=sprintf("%04d-%02d-%02d %02d:%02d:%02d",$year+1900,$month+1,$day,$hour,$min,$sec);
                }

                if ($json_mode) {
                        unless ($numeric_mode) {
                                $status="\"$status\"";
                                $tstamp="\"$tstamp\"";
                        }
                        printf("{\n" .
                                "\t\"timestamp\": %s,\n" .
                                "\t\"status\": %s,\n" .
                                "\t\"ac_power\": %d,\n" .
                                "\t\"dc_power\": %d,\n" .
                                "\t\"total_production\": %d,\n" .
                                "\t\"ac_voltage\": %.2f,\n" .
                                "\t\"ac_current\": %.2f,\n" .
                                "\t\"dc_voltage\": %.2f,\n" .
                                "\t\"dc_current\": %.2f,\n" .
                                "\t\"temperature\": %.2f,\n" .
                                "\t\"exported_energy\": %d,\n" .
                                "\t\"imported_energy\": %d\n" .
                                "}\n",
                                $tstamp,$status,$ac_power,$dc_power,$wh,
                                $ac_v,$ac_i,$dc_v,$dc_i,
                                $temp,$m_e,$m_i);
                } else {
                        unless($domoticz){
                        printf("%s,%s,%d,%d,%d,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d\n",
                                $tstamp,$status,$ac_power,$dc_power,$wh,
                                $ac_v,$ac_i,$dc_v,$dc_i,
                                $temp,$m_e,$m_i);
                        }
                }
        }

        $checktime = time() + $update_every;
    }
    # DO SOMETHING ELSE
}

$c->disconnect;

# eof :-)

My script in domotics calculates total current usage with the values of my P1 meter (3-fase)

Code: Select all

-- sourse code is from : https://www.domoticz.com/forum/viewtopic.php?t=16440#p224533
-- Modified bcause of faulty reading of P1 values.

----------------------------------------------------------------------------------------------------------
-- Domoticz IDX and names of the needed devices


----------------------------------------------------------------------------------------------------------
-- Script parameters
----------------------------------------------------------------------------------------------------------
Delivery_L1 = 0 -- in Watt hours
Delivery_L2 = 0 -- in Watt hours
Delivery_L3 = 0	-- in Watt hours

Usage_L1 = 0 -- in Watt hours
Usage_L2 = 0 -- in Watt hours
Usage_L3 = 0 -- in Watt hours

CalculatedDelivered = 0     -- in Watt hours
CalculatedUsage = 0         -- in Watt hours

ac_power = 0 -- in Watt hours

----------------------------------------------------------------------------------------------------------

debug = "NO" 		-- Turn debugging on ("YES") or off ("NO")

----------------------------------------------------------------------------------------------------------
-- CommandArray
----------------------------------------------------------------------------------------------------------
commandArray = {}

local function update(idx, value1, value2)
    local cmd = string.format("%d|0|%.2f;%.2f", idx, value1, value2)
    table.insert (commandArray, { ['UpdateDevice'] = cmd } )
end

local function isempty(s)
  return s == nil or s == ''
end

if (devicechanged["Slimme Meter"]) then
    
        --data from P1(usage1, usage2, return1, return2, cons, cons)
        usage1, usage2, return1, return2, usage, usageDelivered = otherdevices_svalues["Slimme Meter"]:match("([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);([^;]+)")
	    local SE=otherdevices["Zonnepanelen"]           -- Virtual Electricity device (usage, counter) in which the actual Solar Output will be stored 
	    local SV=otherdevices["Stroom Verbruik"]        -- Virtual Electricity device (usage, counter) in which the actual power consumption will be stored
	    local OT=otherdevices["Omvormer Temperatuur"]   -- Virtual Temperature device, in which the inverter temperature will be stored

        local Delivery_L1 = otherdevices["Delivery L1"] -- Delivered power in Fase L1 created by P1 meter
	    local Delivery_L2 = otherdevices["Delivery L2"] -- Delivered power in Fase L2 created by P1 meter
        local Delivery_L3 = otherdevices["Delivery L3"] -- Delivered power in Fase L3 created by P1 meter
        
        local Usage_L1 = otherdevices["Usage L1"]       -- Used power in Fase L1 created by P1 meter
	    local Usage_L2 = otherdevices["Usage L2"]       -- Used power in Fase L2 created by P1 meter
        local Usage_L3 = otherdevices["Usage L3"]       -- Used power in Fase L3 created by P1 meter
        
        -- read values from solar Zonnepanelen
        ac_power,total_production = SE:match("([^;]+);([^;]+)")
        print ("Total Solar Production = "..total_production.."Wh Current Solar Power = "..ac_power.."Wh")

        -- make sure there is no NIL value 
        if isempty(usage1) then usage1=0 end
        if isempty(usage2) then usage2=0 end
        if isempty(return1) then return1=0 end
        if isempty(return2) then return2=0 end
        if isempty(usage) then usage=0 end
        if isempty(usageDelivered) then usageDelivered=0 end
        
        if isempty(Delivery_L1) then Delivery_L1=0 end
        if isempty(Delivery_L2) then Delivery_L2=0 end
        if isempty(Delivery_L3) then Delivery_L3=0 end
        
        if isempty(Usage_L1) then Usage_L1=0 end
        if isempty(Usage_L2) then Usage_L2=0 end
        if isempty(Usage_L3) then Usage_L3=0 end
        
        if isempty(ac_power) then ac_power=0 end
        if isempty(total_production) then total_production=0 end
        

        -- now that we are sure there is no NIL value we can do the calculations
        CalculatedDelivered=Delivery_L1+Delivery_L2+Delivery_L3
        CalculatedUsage=Usage_L1+Usage_L2+Usage_L3

        -- For debugging purposes: Log result
        if (debug=="YES") then
	        print ("SM.usage1 = "..usage1.." SM.usage2 = "..usage2.." SM.return1 = "..return1.." SM.return2 = "..return2.." Current Power Usage = "..usage.." Current Power Delivery = "..usageDelivered)
	        print ("Usage L1 = "..Usage_L1.." Usage L2 = "..Usage_L2.." Usage L3 = "..Usage_L3.." Usage L1+L2+L3 = "..CalculatedUsage)
	        print ("Delivery L1 = "..Delivery_L1.." Delivery L2 = "..Delivery_L2.." Delivery L3 = "..Delivery_L3.." Total L1+L2+L3 = "..CalculatedDelivered)
	        print ("Total Solar Production = "..total_production.." Current Solar Power = "..ac_power)
	    end -- end if : Debug

        -- calculate real energy consumption
        local virtuelemeterstand=total_production+usage1+usage2-return1-return2
        local vermogen=CalculatedUsage-CalculatedDelivered+ac_power
            
        -- update real actual usage sensor
        print("Stroom Verbruik("..vermogen..","..virtuelemeterstand..")")
        update(otherdevices_idx["Stroom Verbruik"],vermogen,virtuelemeterstand)

end --end if : devicechanged

return commandArray

To run the script as a service you have to create a file in the directory /etc/systemd/system
for example : solar.service
Place in this file :

Code: Select all

[Unit]
Description=Solar service
After=domoticz.service
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=pi
ExecStart=/usr/bin/env perl /home/pi/SolarEdge2_service.perl -m=0 192.168.0.3 -z

[Install]
WantedBy=multi-user.target
set your actual username after User=
set the proper path to your script in ExecStart=

That’s it. We can now start the service:
$ systemctl start solar

And automatically get it to start on boot:
$ systemctl enable solar

More on this can be found on : https://medium.com/@benmorel/creating-a ... 1b5c8b91d6

I hope this helps someone or gives some a idea.