marcelblijleven / goodwe

Get inverter data from a Goodwe XS inverter
MIT License
125 stars 52 forks source link

Modbus stop working #91

Open htttito opened 2 months ago

htttito commented 2 months ago

Hi :-)

Can anybody help?! GW25K-ET The Modbus/UDP stops at night (I think) when there is no longer any power available to the panels. When I start the SolarGo App the Modbus starts again to work. Where can I change or which Modbus address is the right one to change this behavior?

Thanks

JWScheijgrond commented 1 month ago

I have the same issue on the GW3000D-NS with the wifi/lan kit connected via wifi DSP FW: 08.8004.05.14 ARM FW 07.53 Communication Module FW 1.3.53

crispe commented 1 month ago

I see this behaviour also on a GW6000ES, it occurs every night at 00:49:30UTC (my local time is UTC+1). Initially I thought it was related to WiFi, so changed it to wired lan, the problem remains.

JWScheijgrond commented 1 month ago

I think I have found the solution. (on another forum, I got the tip for the broadcasting) First I have to run the goodwe.search_inverters() it will execute a broadcast on the network to trigger the modbus/udp

htttito commented 1 month ago

I will test this? But how to run this command - goodwe.search_inverters() ? Can you help me?

JWScheijgrond commented 1 month ago

This is my current script (note: I have a fixed IP Address for the inverter)

import asyncio
import goodwe
import requests
import schedule
import time
from datetime import datetime

# Replace with your PVOutput API key and system ID
API_KEY = 'API KEY'
SYSTEM_ID = 'SYSTEM ID'
ip_address = '192.168.0.192'

async def get_runtime_data():
    inverter = await goodwe.connect(host=ip_address )
    runtime_data = await inverter.read_runtime_data()

    data = {}
    for sensor in inverter.sensors():
        if sensor.id_ in runtime_data:
            data[sensor.id_] = runtime_data[sensor.id_]
            print("ID: %s value: %s" % (sensor.id_, data[sensor.id_]))
    return data

def upload_to_pvoutput(data):
    url = 'https://pvoutput.org/service/r2/addstatus.jsp'
    headers = {
        'X-Pvoutput-Apikey': API_KEY,
        'X-Pvoutput-SystemId': SYSTEM_ID
    }

    # Prepare the data to be uploaded
    payload = {
        'd': datetime.now().strftime('%Y%m%d'),  # Date in YYYYMMDD format
        't': datetime.now().strftime('%H:%M'),   # Time in HH:MM format
#        'v1': data.get('pv_power', 0),           # Power Generation (W)
        'v2': data.get('total_inverter_power', 0),         # Power Consumption (W)
        'v5': data.get('temperature', 0),         # Voltage (V)
        'v6': data.get('vpv1', 0)          # Current (A)
    }

    print(payload)

    response = requests.post(url, headers=headers, data=payload)
    if response.status_code == 200:
        print('Data uploaded successfully')
    else:
        print(f'Failed to upload data: {response.status_code} - {response.text}')

async def search():
    await goodwe.search_inverters()
    print('Finished search')

async def main():
    data = await get_runtime_data()
    upload_to_pvoutput(data)

def run_main():
    try:
        print('Starting run')
        asyncio.run(main())
    except Exception as e:
        print(f"An error occurred in main: {e}")

def run_search():
    try:
        print('Starting search')
        asyncio.run(search())
    except Exception as e:
        print(f"An error occurred in search: {e}")

def job():
    print("I'm working... at " + datetime.now().strftime('%H:%M'))

if __name__ == '__main__':
    print('Start MAIN')
    print('Start first Search')
    run_search()
    print('Start jobs')
    schedule.every(10).minutes.do(run_search)
    schedule.every(1).minutes.do(run_main)
    schedule.every(1).minutes.do(job)

    while 1:
        schedule.run_pending()
        time.sleep(30)
htttito commented 1 month ago

Hi thanks, but there is always the same error.

Traceback (most recent call last): File "/etc/openhab/scripts/goodwetest.py", line 16, in asyncio.run(get_runtime_data()) File "/usr/lib/python3.11/asyncio/runners.py", line 190, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "/etc/openhab/scripts/goodwetest.py", line 8, in get_runtime_data inverter = await goodwe.connect(ip_address) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/goodwe/init.py", line 48, in connect return await discover(host, port, timeout, retries) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/goodwe/init.py", line 111, in discover raise InverterError( goodwe.exceptions.InverterError: Unable to connect to the inverter at host=192.168.0.145, or your inverter is not supported yet. Failures=[RequestFailedException("No valid response received to 'aa55c07f0102000241' request."), RequestFailedException("No valid response received to 'f70388b800213ac1' request.", 1), RequestFailedException("No valid response received to '7f03753100280409' request.", 1), RequestFailedException("No valid response received to 'aa55c07f0102000241' request.", 1)]

JWScheijgrond commented 1 month ago

And if you start the solargo app then this script runs succesfully again? Because if that is the case, then there is some kind of broadcast that the inverter responds to that might not be included in the search. The search method does not produce errors? Or do you get an error 13 because the broadcast is not allowed on linux.

htttito commented 1 month ago

It looks like error 13:

Start MAIN Start first Search Starting search An error occurred in search: [Errno 13] Permission denied Start jobs

JWScheijgrond commented 1 month ago

Ok, I am trying this to workaround that by not broadcasting to all IP addresses but only to the inverter. But I will not know if that works until tomorrow.

def run_search_local():
    try:
        print('Starting search local')
        asyncio.run(search_inverterforAddress())
    except Exception as e:
        print(f"An error occurred in search local: {e}")

async def search_inverterforAddress() -> bytes:
    command = ProtocolCommand("WIFIKIT-214028-READ".encode("utf-8"), lambda r: True)
    try:
        result = await command.execute(UdpInverterProtocol('192.168.0.145', 48899, 2, 1))
        if result is not None:
            return result.response_data()
        else:
            raise Exception("No response received to broadcast request.")
    except Exception as e:
        print(f"An error occurred in discover: {e}")

UPDATE: It appears to work, this morning my logging started automatically without starting the app or anything like that. So the targeted "broadcast" works to activate the inverter.

htttito commented 1 month ago

OK, thank you for your help!

htttito commented 4 weeks ago

Sorry hmmm...where to but this code?!

JWScheijgrond commented 4 weeks ago

So this is what I currently use

import asyncio
import goodwe
import requests
import schedule
import time
from datetime import datetime

# Replace with your PVOutput API key and system ID
API_KEY = 'API KEY'
SYSTEM_ID = 'SYSTEM ID'
ip_address = '192.168.0.145'

async def get_runtime_data():
    inverter = await goodwe.connect(host=ip_address )
    runtime_data = await inverter.read_runtime_data()

    data = {}
    for sensor in inverter.sensors():
        if sensor.id_ in runtime_data:
            data[sensor.id_] = runtime_data[sensor.id_]
            print("ID: %s value: %s" % (sensor.id_, data[sensor.id_]))
    return data

def upload_to_pvoutput(data):
    url = 'https://pvoutput.org/service/r2/addstatus.jsp'
    headers = {
        'X-Pvoutput-Apikey': API_KEY,
        'X-Pvoutput-SystemId': SYSTEM_ID
    }

    # Prepare the data to be uploaded
    payload = {
        'd': datetime.now().strftime('%Y%m%d'),  # Date in YYYYMMDD format
        't': datetime.now().strftime('%H:%M'),   # Time in HH:MM format
#        'v1': data.get('pv_power', 0),           # Power Generation (W)
        'v2': data.get('total_inverter_power', 0),         # Power Consumption (W)
        'v5': data.get('temperature', 0),         # Voltage (V)
        'v6': data.get('vpv1', 0)          # Current (A)
    }

    print(payload)

    response = requests.post(url, headers=headers, data=payload)
    if response.status_code == 200:
        print('Data uploaded successfully')
    else:
        print(f'Failed to upload data: {response.status_code} - {response.text}')

def run_search_local():
    try:
        print('Starting search local')
        asyncio.run(search_inverterforAddress())
    except Exception as e:
        print(f"An error occurred in search local: {e}")

async def search_inverterforAddress() -> bytes:
    command = ProtocolCommand("WIFIKIT-214028-READ".encode("utf-8"), lambda r: True)
    try:
        result = await command.execute(UdpInverterProtocol(ip_address , 48899, 2, 1))
        if result is not None:
            return result.response_data()
        else:
            raise Exception("No response received to broadcast request.")
    except Exception as e:
        print(f"An error occurred in discover: {e}")

async def main():
    data = await get_runtime_data()
    upload_to_pvoutput(data)

def run_main():
    try:
        print('Starting run')
        asyncio.run(main())
    except Exception as e:
        print(f"An error occurred in main: {e}")

def job():
    print("I'm working... at " + datetime.now().strftime('%H:%M'))

if __name__ == '__main__':
    print('Start MAIN')
    print('Start local Search')
    run_search_local()
    print('Start jobs')
    schedule.every(10).minutes.do(run_search_local)
    schedule.every(1).minutes.do(run_main)
    schedule.every(1).minutes.do(job)

    while 1:
        schedule.run_pending()
        time.sleep(30)