dzVents script to collect real/time data from SAJ solar power inverter

Moderator: leecollings

Post Reply
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

dzVents script to collect real/time data from SAJ solar power inverter

Post by waaren »

One of my friends recently started with domoticz and use a SAJ inverter to invert DC from his solar panels to AC.

1 + 1 = 2 ; Have Fun !

Code: Select all

--[[

    dzVents script to collect real/time data from SAJ solar power inverter (https://www.saj-electric.com ) and send this to domoticz devices
    Information layout
        
    After modifying the relevant section, place the script in domoticz/scripts/dzVents/scripts or use the internal event editor
    
    Create virtual hardware and the following devices
    # Name                  Type        Subtype
    - Solar Power Today     RFXMeter    RFXMeter counter
    - Frequency (Hz)        General     Text
    - InverterTemperature   Temp        LaCrosse TX3
    - Total generated       RFXMeter    RFXMeter counter
    - PV1 DC Current        General     Current
    - PV1 DC Voltage        General     Voltage
    - Solar Power           Usage       Electric


    response lay-out
    
    Skip to line 101 and change the URL to match your situation, USERID, PASSWORD and IP-ADDRESS

        #    what               value   divider Value Quantity
        1   System number        1        1
        2   Total generated      786798   100   kWh
        3   runTime              97063    10    hours
        4   Today                39       100   Kwh
        5   Today                43       10    hours
        6   PV1 DC Voltage       1533     10    Volt
        7   PV1 DC Current       20       10    Ampere
        8   PV2 DC Voltage       2181     10    Volt
        9   PV2 DC Current       19       10    Volt
        10  PV3 DC Voltage       65535    N/A
        11  PV3 DC Current       65535    N/A
        12  Grid connected       123      1     Watt
        13  Frequency            5000     100   Hz
        14  Line1 voltage        2333     10    Volt
        15  Line1 current        64       10    Ampere
        16  Line2 voltage        65535    N/A
        17  Line2 current        65535    N/A
        18  Line3 voltage        65535    N/A
        19  Line3 current        65535    N/A
        20  Bus voltage          3619     10    Volt
        21  Temperature          259      10    Celsius
        22  CO2 emission         61762    10    Kg
        23  system number        2        1

        History:
        20200112: Start coding
        20200113: Start testing on life situation (thanks to nlfva1)
        20200119: First public release
 
]]--

local scriptVar = 'getSolarInformation'

return
{
    on = 
    { 
        timer = 
        {
           'every 5 minutes at daytime',
        },
        
        httpResponses = 
        {
            scriptVar,
        },
    },

    logging =   
    {
        level = domoticz.LOG_DEBUG, -- set to LOG_ERROR when tested and OK
        marker = scriptVar,
    },
       
    execute = function(dz, item)
        
        ----
        -- Enter your setting below this line
        ----
        local idTable = -- modify column devicetype / devicename where applicable 
        {
            -- What,                divider,    Quantity,   deviceType/nil, deviceName  
            { "System number",      1,          ""},
            { "Total generated",    100,        "kWh",      "Counter",      "Total generated"}, -- define as Counter
            { "runTime",            10,         "hours"},
            { "Today",              100,        "kWh",      "Counter",      "Solar Power Today"},
            { "Today",              10,         "hours"},
            { "PV1 DC Voltage",     10,         "Volt",     "Voltage",      "PV1 DC Voltage"},
            { "PV1 DC Current",     10,         "Ampere",   "Ampere",       "PV1 DC Current"},
            { "PV2 DC Voltage",     10,         "Volt"},
            { "PV2 DC Current",     10,         "Volt"},
            { "PV3 DC Voltage",     1,          ""},
            { "PV3 DC Current",     1,          ""},
            { "Grid connected",     1,          "Watt",     "Energy",       "Solar Power"},
            { "Frequency",          100,        "Hz",       "Text",         "Frequency (Hz)"},
            { "Line1 voltage",      10,         "Volt"},
            { "Line1 current",      10,         "Ampere"},
            { "Line2 voltage",      1,          ""},
            { "Line2 current",      1,          ""},
            { "Line3 voltage",      1,          ""},
            { "Line3 current",      1,          ""},
            { "Bus voltage",        10,         "Volt"},
            { "Temperature",        10,         "Celsius",  "Temperature",  "InverterTemperature"},
            { "CO2 emission",       10,         "Kg"},
            { "next",               1,          ""},
        }

        local SAJ_IP = '192.168.1.8'
        local SAJ_userID = 'admin'
        local SAJ_password = 'password'

        ----
        --   No changes required below this line
        ----
        
        -- testData = '1,786779,97053,69,43,1533,20,2181,19,65535,65535,123,5000,2333,64,65535,65535,65535,65535,3619,259,61762,2'
        
        local function callSolarInverter()
            local url = 'http://' .. SAJ_userID ..':' .. SAJ_password .. '@' .. SAJ_IP .. '/status/status.php' 
            -- url = 'http://admin:[email protected]/status/status.php' -- example URL
            dz.openURL({ url = url, callback = scriptVar })
        end

        local function processValue(value, index)
            local notApplicable = 65535
            if value == notApplicable then 
                return 'N/A' 
            else
                return value / idTable[index][2] -- divide by divider
            end    
        end

        local function updateDevice(deviceType, name, value)
            dz.log(deviceType ..  ' : ' .. name  .. ' : ' ..   value ,dz.LOG_DEBUG)
            if deviceType == 'Temperature' then
                dz.devices(name).updateTemperature(value)
            elseif deviceType == 'Ampere' then
                 dz.devices(name).updateCurrent(value)
            elseif deviceType == 'Voltage' then
                 dz.devices(name).updateVoltage(value)
            elseif deviceType == 'Counter' then
                 dz.devices(name).updateCounter(value*1000)
            elseif deviceType == 'Energy' then
                 dz.devices(name).updateEnergy(value)
            elseif deviceType == 'kWh' then
                 dz.devices(name).updateElectricity(value,1)
            elseif deviceType == 'Text' then
                 dz.devices(name).updateText(value)
            end
        end

        local function processData(data)
            dTable = dz.utils.stringSplit(data,',')  -- convert data (string) to table
            -- dz.utils.dumpTable(dTable) -- debug
            
            for index, value in ipairs(dTable) do 
                local value = processValue(math.floor(value), index)
                dz.log(idTable[index][1] .. ' : ' .. value .. ' ' .. idTable[index][3] ,dz.LOG_DEBUG)
                
                if idTable[index][4] then
                    updateDevice(idTable[index][4], idTable[index][5], value )
                end
            end
        end    

        -- main    
        if item.isTimer then
            callSolarInverter()
        else    -- triggered by callback
            if item.ok then
                processData(item.data)
                -- processData(testData) -- Test Data
            else
                dz.log('Problem retrieving data from inverter.. ' .. item.statusText,dz.LOG_ERROR)
            end
        end
    end
}
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
JPSke
Posts: 33
Joined: Wednesday 11 November 2020 11:37
Target OS: Raspberry Pi / ODroid
Domoticz version: 2022.1
Location: Sint-Gillis-Waas, Belgium
Contact:

Re: dzVents script to collect real/time data from SAJ solar power inverter

Post by JPSke »

This script is for SAJ Solar inverters which are connected through wifi. Does anyone know how to read values from an ethernet connected inverter? :(
RaspberryDomoticz
Posts: 1
Joined: Saturday 13 February 2021 20:49
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: dzVents script to collect real/time data from SAJ solar power inverter

Post by RaspberryDomoticz »

This script is for SAJ Solar inverters which are connected through wifi.
Is there a script to get these values through the RS485 connector on the inverter?
User avatar
waltervl
Posts: 5397
Joined: Monday 28 January 2019 18:48
Target OS: Linux
Domoticz version: 2024.7
Location: NL
Contact:

Re: dzVents script to collect real/time data from SAJ solar power inverter

Post by waltervl »

@JPSke and @raspberryDomoticz There is now one plugin for SAJ Modbus (ethernet) here (currently in development)
https://domoticz.com/forum/viewtopic.php?f=65&t=37005
Domoticz running on Udoo X86 (on Ubuntu)
Devices/plugins: ZigbeeforDomoticz (with Xiaomi, Ikea, Tuya devices), Nefit Easy, Midea Airco, Omnik Solar, Goodwe Solar
mancide
Posts: 29
Joined: Wednesday 17 May 2017 0:18
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.6
Location: Belgium - Ghent
Contact:

Re: dzVents script to collect real/time data from SAJ solar power inverter

Post by mancide »

Hi,

I have a script that works on my SAJ converter that is connected over ethernet. In my opinion wifi and ethernet should work just the same.
anyway here is my script:

Code: Select all

return {
	on = {
		timer = {
			-- timer triggers.. if one matches with the current time then the script is executed
			'every minute'
		},
	},
    logging =   {   
        --level     =   domoticz.LOG_INFO,
        marker    =   "SAJ"      
    },
	data = { SAJEnergyToday = {initial=0},
	         SAJDate
	       },

	execute = function(domoticz, triggeredItem, info)
	    --print("dzVentsSAJ2Domoticz")

	    --local Time = require('Time')
        --local currentTime = Time()
        --if (currentTime.seconds % 10 == 0)
        --then
            --print(' ')
            --print(string.format("seconds:%d", currentTime.seconds))

    	    local SAJAcPower      = 0
            local bNowUpdated     = false
            local SAJTemp         = 0
            local bTempInverterUpdated = false
            --local SAJEnergyToday  = 0
            local bTodayUpdated   = false
            local SAJEnergyTotal  = 0
            local bTotalUpdated   = false
            local OutsideTemperature = domoticz.devices('carport').temperature
        

            local PanelV1     = 0
            local bPanelV1Updated = false
            local PanelV2     = 0
            local bPanelV2Updated = false
            local MaxVoltage  = 0

            --not used for now
            local PanelI1     = 0
            local bPanelI1Updated = false
            local PanelI2     = 0
            local bPanelI2Updated = false
            
            local ip     = domoticz.variables('SAJ Ip address').value
            local i = 1
            local DateToday = os.date("%d/%m/%Y")
    
        	--Get the XML data from the inverter
            local url = 'curl http://'..ip..'/real_time_data.xml'
            local data = assert(io.popen(url))
            
            --print(string.format("http request from SAJ: %s", data))
        
            --filter the lines we need
            local LineNr = 0
            for line in data:lines() do
                LineNr = LineNr + 1
        
                if( string.find(line, 'temp') ~= nil)
                then
                    --print(string.format("* line %d: %s", LineNr, line))
                    SAJTemp  = string.match(line, "%d+%.%d+")
                    bTempInverterUpdated = true
        
                elseif( string.find(line, 'e%-total') ~= nil)
                then
                    --print(string.format("** line %d: %s", LineNr, line))
                    SAJEnergyTotal  = string.match(line, "%d+%.%d+")
                    bTotalUpdated = true
        
                elseif( string.find(line, 'e%-today') ~= nil)
                then
                    --print(string.format("*** line %d: %s", LineNr, line))
                    --SAJEnergyToday  = string.match(line, "%d+%.%d+")
                    --save today's energy for use after the SAJ is OFF
                    domoticz.data.SAJEnergyToday  = string.match(line, "%d+%.%d+")
                    domoticz.data.SAJDate = DateToday
                    bTodayUpdated = true
        
                elseif( string.find(line, 'p%-ac') ~= nil)
                then
                    --print(string.format("**** line %d: %s", LineNr, line))
                    SAJAcPower  = string.match(line, "%d+")
                    bNowUpdated = true
        
                elseif( string.find(line, 'v%-pv1') ~= nil)
                then
                    --print(string.format("**** line %d: %s", LineNr, line))
                    PanelV1  = string.match(line, "%d+%.%d+")
                    bPanelV1Updated = true
        
                elseif( string.find(line, 'v%-pv2') ~= nil)
                then
                    --print(string.format("**** line %d: %s", LineNr, line))
                    PanelV2  = string.match(line, "%d+%.%d+")
                    bPanelV2Updated = true

                elseif( string.find(line, 'i%-pv11') ~= nil)
                then
                    --print(string.format("**** line %d: %s", LineNr, line))
                    PanelI1  = string.match(line, "%d+%.%d+")
                    bPaneli1Updated = true

                elseif( string.find(line, 'i%-pv21') ~= nil)
                then
                    --print(string.format("**** line %d: %s", LineNr, line))
                    PanelI2  = string.match(line, "%d+%.%d+")
                    bPanelI2Updated = true

                else
                    --print(string.format("line %d: %s", LineNr, line))
                end
            end
            
            data:close()
    
            if( bTempInverterUpdated==true)
            then
                --print(string.format("updating SAJ temp:%4.1f°C",SAJTemp))
                --SAJ Temp sensor id: 3174
                domoticz.devices(3174).update(0,SAJTemp)
            end
            
            if( bNowUpdated==true and bTotalUpdated==true)
            then
                --print(string.format("updating SAJ energy:%d %d",SAJAcPower, SAJEnergyTotal*1000))
                --SAJ Energy sensor id: 3173
                domoticz.devices(3173).update(0, SAJAcPower..";".. SAJEnergyTotal*1000)
            end

            if( bPanelV1Updated==true)
            then
                --print(string.format("updating SAJ Voltage String1:%4.1f",PanelV1))
                --SAJ Voltage String1 sensor id: 3183
                domoticz.devices(3183).update(0,PanelV1)
                if( PanelV1 > PanelV2 )
                then
                    MaxVoltage = PanelV1
                else
                    MaxVoltage = PanelV2
                end
            end
            
            if( bPanelV2Updated==true)
            then
                --print(string.format("updating SAJ Voltage String2:%4.1f",PanelV2))
                --SAJ Voltage String2 sensor id: 3184
                domoticz.devices(3184).update(0,PanelV2)
                if( PanelV1 > PanelV2 )
                then
                    MaxVoltage = PanelV1
                else
                    MaxVoltage = PanelV2
                end
            end

		    --print(string.format("HEM Energy counter: Today:%4.1fWh  Total:%4.1fWh", HEMEnergyToday, -1*domoticz.devices(2546).WhTotal ))
		    
		    --print(string.format("domoticz.data.SAJDate=%s",domoticz.data.SAJDate))
		    --print(string.format("domoticz.data.SAJEnergyToday=%s",domoticz.data.SAJEnergyToday))
		    
		    if( domoticz.data.SAJDate ~= DateToday )
		    then
    	        --print(string.format("domoticz.data.SAJDate          = %s", domoticz.data.SAJDate))
		        domoticz.data.SAJEnergyToday = 0
		        domoticz.data.SAJDate = DateToday
	        end
	        
	        local HEM_Id          = 5361    --HEM id
	        local SolarCounter_Id = 3173    --SAJ energy counter id
	        
	        --print("-----------------------------------------------------------")
	        
	        --these should be equal -> choose which one to use
	        --print(string.format("domoticz.data.SAJEnergyToday*1000        = %4.1fWh", domoticz.data.SAJEnergyToday*1000))
	        --print(string.format("domoticz.devices(SolarCounter_Id).WhToday= %4.1fWh", domoticz.devices(SolarCounter_Id).WhToday))
	        local EnergyGeneratedToday = 0
	        if( domoticz.devices(SolarCounter_Id).usage>0 )
	        then
	            --only report an energy value when the power value > 0
	            EnergyGeneratedToday = domoticz.devices(SolarCounter_Id).WhToday
	        end

	        
	        --these should be equal -> choose which one to use
	        --BUG: if HEMEnergyDayStartValue is not yet set for today we use yesterday's value. Is this a problem???
	        --     cfr. pvoutput value 00:00 = 0Wh, 00:05 = 123145Wh
		    --print(string.format("domoticz.devices(HEM_Id).WhTotal-HEMEnergyDayStartValue = %4.1fWh", domoticz.devices(HEM_Id).WhTotal-domoticz.variables('HEMEnergyDayStartValue').value))
		    --print(string.format("domoticz.devices(HEM_Id).WhToday                        = %4.1fWh", domoticz.devices(HEM_Id).WhToday))
		    --print(string.format("TODO: switch to domoticz.devices(HEM_Id).WhToday                        = %4.1fWh", domoticz.devices(HEM_Id).WhToday))
	        local EnergyConsumedToday  = domoticz.devices(HEM_Id).WhToday
	        

    	    local Time = require('Time')
            local currentTime = Time()

            if (currentTime.minutes % 5 == 0)
            then
        		-- Upload the data to PVoutput every 5 mins

                baseURL="http://pvoutput.org/service/r2/addstatus.jsp?"
    		    API="---fill in your own API key here---"                  --pvOutput API key
    		    PVO_URL= baseURL .. "sid=---fill in your own site id here---&key=" .. API .. "&d=" .. os.date("%Y%m%d") .. "&t=" .. os.date("%H:%M")
    		    GENERATION =""
    		    CONSUMPTION=""
        		
                if( bTempUpdated==true or bNowUpdated==true or bTotalUpdated==true) --only update when we have new values
                then
        		    GENERATION = "&v1=" .. EnergyGeneratedToday .. "&v2=" .. domoticz.devices(SolarCounter_Id).usage .. "&v6=" .. MaxVoltage
    		    end
    		    --use this to force a force an update of the energy generated after the sun is down
    		    --GENERATION = "&v1=" .. EnergyGeneratedToday .. "&v2=" .. domoticz.devices(SolarCounter_Id).usage .. "&v6=" .. MaxVoltage
    		    
		        CONSUMPTION = "&v3=" .. EnergyConsumedToday .. "&v4=" .. domoticz.devices(HEM_Id).usage .. "&v5=" .. OutsideTemperature
		        
    		    PVO_URL = PVO_URL .. GENERATION .. CONSUMPTION
    		    domoticz.openURL(PVO_URL)

    		    --print(string.format("pvoutput.org: %s %s", GENERATION, CONSUMPTION))
                
                --print(string.format("Energy today: consumed: %4.1fWh | generated: %4.1fWh", EnergyConsumedToday, EnergyGeneratedToday))
            end

            --print(string.format("Energy today: consumed: %4.1fWh | generated: %4.1fWh", EnergyConsumedToday, EnergyGeneratedToday))

        --end
	end
}
Raspberry Pi 3B v1.3 - 16GB SDcard - Raspbian Bullseye
Aeotec Z-Stick Gen5 (ZW090) -> Z-Wave JS UI -> mosquitto
RFXCOM RFXtrx433E
Unifi controller -> Domoticz-Unifi-Presence plugin
Nest thermostat
SAJ solar convertor
Smart Gateway Gas/water meter
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests