dewet22 / givenergy-modbus Goto Github PK
View Code? Open in Web Editor NEWA python library to access GivEnergy inverters via Modbus TCP on a local network, with no dependency on the GivEnergy Cloud.
License: Other
A python library to access GivEnergy inverters via Modbus TCP on a local network, with no dependency on the GivEnergy Cloud.
License: Other
Feature request:
When used with a Gen2 Inverter : Setting battery_soc_reserve to 100 stops any discharge. Can this be removed from set_mode_storage ? Currently calling set_mode_storage for either Timed Export/Disharge on gen2 effectively disables the battery from discharge.
def set_mode_storage()
........
ret.extend(set_shallow_charge(100)) # r110=100
I dont think that the portal or app set this value when Timed Discharge or Timed Export is selected. Is it needed for Gen1 firmware ?
Describe what you were trying to get done.
Run this simple python script in PyCharm IDE to grab the battery and inverter info. Would just expect a clean exit
import json
import logging
import datetime
import time
from givenergy_modbus.client import GivEnergyClient
from givenergy_modbus.model.inverter import Model
from givenergy_modbus.model.battery import Battery
from givenergy_modbus.model.plant import Plant
from os.path import exists
import os
client = GivEnergyClient(host='192.168.0.70')
p = Plant(number_batteries=1)
client.refresh_plant(p, full_refresh=True)
GEInv = p.inverter
GEBat = p.batteries
Tell us what happened, what went wrong, and what you expected to happen.
Sometimes it works and sometimes it doesn't, it's completely random.
When it works it exits cleanly (and if I add the code to print out an inverter or battery setting, it prints the correct value)
When it fails I see this
Did not receive expected response type: ReadHoldingRegistersResponse != ReadInputRegistersResponse
Returned base register (0) does not match that from request (60).
Traceback (most recent call last):
File "/Users/richard/PycharmProjects/pythonProject3/venv/giv.py", line 17, in
GEInv = p.inverter
^^^^^^^^^^
File "/Users/richard/PycharmProjects/pythonProject3/venv/lib/python3.11/site-packages/givenergy_modbus/model/plant.py", line 32, in inverter
return Inverter.from_orm(self.inverter_rc)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "pydantic/main.py", line 578, in pydantic.main.BaseModel.from_orm
File "pydantic/main.py", line 1056, in pydantic.main.validate_model
File "/Users/richard/PycharmProjects/pythonProject3/venv/lib/python3.11/site-packages/givenergy_modbus/model/register_getter.py", line 69, in get
return getattr(self._obj, key, default)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/richard/PycharmProjects/pythonProject3/venv/lib/python3.11/site-packages/givenergy_modbus/model/register_cache.py", line 26, in getattr
val = self[register]
~~~~^^^^^^^^^^
KeyError: HR:120
The ability to query the inverters and batteries directly in this way is very interesting to us, so I'd love to get this working and stable if I can
Wondering if it could be poor comms quality as our 3 inverters have wifi dongles for connectivity.
I was trying to query my Giv-AC 3.0 but the code fails
This is my test code
from givenergy_modbus.client import GivEnergyClient
from givenergy_modbus.model.plant import Plant
client = GivEnergyClient(host="192.168.99.99")
p = Plant(number_batteries=1)
client.refresh_plant(p, full_refresh=True)
print(p.inverter.json())
and it fails with the following error
Traceback (most recent call last):
File "/home/martin/test/./test.py", line 8, in <module>
print(p.inverter.json())
File "/home/martin/test/.env/lib/python3.10/site-packages/givenergy_modbus/model/plant.py", line 32, in inverter
return Inverter.from_orm(self.inverter_rc)
File "pydantic/main.py", line 562, in pydantic.main.BaseModel.from_orm
File "pydantic/main.py", line 1001, in pydantic.main.validate_model
File "/home/martin/test/.env/lib/python3.10/site-packages/givenergy_modbus/model/register_getter.py", line 61, in get
return self.get(f'{key}_start'), self.get(f'{key}_end')
File "/home/martin/test/.env/lib/python3.10/site-packages/givenergy_modbus/model/register_getter.py", line 69, in get
return getattr(self._obj, key, default)
File "/home/martin/test/.env/lib/python3.10/site-packages/givenergy_modbus/model/register_cache.py", line 27, in __getattr__
return register.convert(val)
File "/home/martin/test/.env/lib/python3.10/site-packages/givenergy_modbus/model/register.py", line 155, in convert
return self.type.convert(val, self.scaling.value)
File "/home/martin/test/.env/lib/python3.10/site-packages/givenergy_modbus/model/register.py", line 49, in convert
return time(hour=int(f'{value:04}'[:2]), minute=int(f'{value:04}'[2:]) % 60)
ValueError: hour must be in 0..23
After some debug it seems discharge_slot_1
end time is 2400. As a work around i added the following to givenergy_modbus/model/register.py when handling time
if value >= 2400:
value = 0
This fixed the issues and i can now query the inverter. All the other values look correct.
I'm using givenergy-local integration in Home Assistant, which uses this library. I also use home-assistant-solaredge-modbus integration to monitor my Solaredge inverter.
The Solaredge integration (and the -multi one as well) uses pymodbus >= 3.1.1. Is there any chance to upgrade this project to use a more modern version of pymodbus (currently ^2.5.3
)?
Thanks.
Unable to connect to the All In One on GivTCP, or directly from Windows
givenergy-modbus -h 192.168.0.8 dump-registers -b 2
To assist Mark Cracknall
Result from AIO attached, compared to the AC3 on the same network
I'm trying to get the latest version to work, tried to CLI but it seems not to work with the API, see below.
treforsouthwell@Trefors-MacBook-Pro-2 givenergy-modbus % python3 givenergy_modbus/cli.py -h 192.168.0.141 dump-registers
Traceback (most recent call last):
File "/Users/treforsouthwell/Library/CloudStorage/OneDrive-Personal/Code/givenergy/givenergy-modbus/givenergy_modbus/cli.py", line 184, in
main(obj={}) # pragma: no cover
File "/Users/treforsouthwell/homebrew/lib/python3.10/site-packages/click/core.py", line 1130, in call
return self.main(*args, **kwargs)
File "/Users/treforsouthwell/homebrew/lib/python3.10/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/Users/treforsouthwell/homebrew/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/treforsouthwell/homebrew/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/Users/treforsouthwell/homebrew/lib/python3.10/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/Users/treforsouthwell/homebrew/lib/python3.10/site-packages/click/decorators.py", line 26, in new_func
return f(get_current_context(), *args, **kwargs)
File "/Users/treforsouthwell/Library/CloudStorage/OneDrive-Personal/Code/givenergy/givenergy-modbus/givenergy_modbus/cli.py", line 71, in dump_registers
ctx.obj['CLIENT'].refresh_plant(plant=plant, full_refresh=True)
AttributeError: 'Coordinator' object has no attribute 'refresh_plant'. Did you mean: 'refresh_count'?
When trying to retrieve information about my inverter I get the error:
Traceback (most recent call last):
File "/config/custom_components/givenergy_local/config_flow.py", line 48, in async_step_user
serial_no = await read_inverter_serial(self.hass, user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/givenergy_local/config_flow.py", line 27, in read_inverter_serial
serial_no: str = plant.inverter.inverter_serial_number
^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/givenergy_modbus/model/plant.py", line 32, in inverter
return Inverter.from_orm(self.inverter_rc)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "pydantic/main.py", line 577, in pydantic.main.BaseModel.from_orm
File "pydantic/main.py", line 1102, in pydantic.main.validate_model
File "/usr/local/lib/python3.11/site-packages/givenergy_modbus/model/inverter.py", line 212, in compute_model
values['inverter_model'] = Model.from_serial_number(values['inverter_serial_number'])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/givenergy_modbus/model/inverter.py", line 39, in from_serial_number
raise UnknownModelError(f"Cannot determine model number from serial number {serial_number}")
givenergy_modbus.model.inverter.UnknownModelError: Cannot determine model number from serial number FA2308****
All information from the battery is able to be received correctly without error.
Thank you in advance.
The README has lots of good examples of how to set registers, but would it be possible to add an example showing how to query.
This code is in use in another givenergy integration, which is broken for us due to the missing serial number prefix. Apparently the other integration requires a release on this in order to use the fix which my partner submitted (01224c7) which has been merged.
Its been over a year since a new version was released. Could one be done?
Thanks.
Dewet,
Based on your great work I built https://github.com/barry-scott/prometheus-givenergy that uses the current version pymodbus to implement a prometheus exporter for GivEnergy.
I did this because I found that updating givenergy-modbus code to the latest version of dependencies was difficult. In part because I am not familiar with some of the libraries you are using and in part because pymodbus V3 API is not compatible with pymodbus V2 API.
I have credited you in the readme for prometheus-givenergy.
Thanks for releasing this project and the documentation in framer.py that has been very helpful.
Barry
[email protected]
Describe what you were trying to get done.
Tell us what happened, what went wrong, and what you expected to happen.
import datetime
import json
from givenergy_modbus.client import GivEnergyClient
from givenergy_modbus.model.inverter import Model
from givenergy_modbus.model.plant import Plant
client = GivEnergyClient(host="192.168.1.xx")
p = Plant(number_batteries=1)
client.refresh_plant(p, full_refresh=True)
data = json.loads(p.inverter.json())
print(data)
####What happened
givenergy_modbus.model.inverter.UnknownModelError:
Cannot determine model number from serial number EA230XXXX
'EA': cls.Gen2 is missing from the inverter list can it be added please.
I am using the library as part of a Home Assistant integration. I've been working with someone who has a 5.0 Gen 2 inverter, and is unable to get my integration working.
The root cause appears to be in the serial number to model decoding function.
The following script has been used to debug the issue:
https://github.com/cdpuk/givenergy-local/blob/master/scripts/debug.py
We can successfully read the registers and print details with plant.inverter_rc
, however decoding the values fails as the registers that would normally hold the serial number seem to hold other data.
Traceback (most recent call last):
File "U:\debug.py", line 68, in <module>
main()
File "U:\debug.py", line 64, in main
debugger.display_decoded_data()
File "U:\debug.py", line 45, in display_decoded_data
print(self.plant.inverter.json(indent=2))
File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\givenergy_modbus\model\plant.py", line 32, in inverter
return Inverter.from_orm(self.inverter_rc)
File "pydantic\main.py", line 562, in pydantic.main.BaseModel.from_orm
File "pydantic\main.py", line 1048, in pydantic.main.validate_model
File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\givenergy_modbus\model\inverter.py", line 212, in compute_model
values['inverter_model'] = Model.from_serial_number(values['inverter_serial_number'])
File "C:\Users\xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\givenergy_modbus\model\inverter.py", line 39, in from_serial_number
raise UnknownModelError(f"Cannot determine model number from serial number {serial_number}")
givenergy_modbus.model.inverter.UnknownModelError: Cannot determine model number from serial number D 1 1 0 2
Patching the library such that it returns an "Unknown" model when decoding fails gets everything working. All other decoded values appear correctly. I was unable to identify another set of registers that hold the serial number in the expected format.
I have the complete output from this script, including all register values, if required.
The "Unknown" approach certainly seems most straightforward, but I appreciate there may be other reasons to raise
instead.
It's worth noting that the log messages contain the correct serial number (from the underlying modbus library?). Perhaps this would be a more reliable source for the serial number property.
givenergy_modbus INFO Decoded response 4/ReadInputRegistersResponse({check: 0xe55a, inverter_serial_number: EDnnnnnnnn, base_register: 0x0000, register_count: 0x003c, data_adapter_serial_number: WGnnnnnnnn, padding: 0x008a, slave_address: 0x0032})
Describe what you were trying to get done.
Fork or download the code
Tell us what happened, what went wrong, and what you expected to happen.
The downloaded or forked code is not the same as that installed by pip install.
This is probably my ignorance of github but the code here does not seem complete and is structured differently to the pip install givenergy-modbus
code
E.g. the main class is GivEnergyClient but I can't find it anywhere in the code...
This is a great project and I have a couple of minor mods I would like to contribute.
Thanks
Mike
The dump-registers command fails to run.
The refresh_plant
member function seems to have been removed in 1180ce1.
python3 cli.py --host "192.168.1.188" dump-registers --batteries 2
Traceback (most recent call last):
File "cli.py", line 184, in <module>
main(obj={}) # pragma: no cover
(...)
File "cli.py", line 71, in dump_registers
ctx.obj['CLIENT'].refresh_plant(plant=plant, full_refresh=True)
AttributeError: 'Coordinator' object has no attribute 'refresh_plant'
Could you provide an example of how to use the new client structure? The original client.py file has gone, and I'd like to bring the async capability into GivTCP.
Trying to use the HA GivEnergy local package from HACS
(https://github.com/cdpuk/givenergy-local)
Input Inverter IP in the Config Wizard, which failed with the following in the logs
Failed to validate inverter configuration
Traceback (most recent call last):
File "/config/custom_components/givenergy_local/config_flow.py", line 48, in async_step_user
serial_no = await read_inverter_serial(self.hass, user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/givenergy_local/config_flow.py", line 27, in read_inverter_serial
serial_no: str = plant.inverter.inverter_serial_number
^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/givenergy_modbus/model/plant.py", line 32, in inverter
return Inverter.from_orm(self.inverter_rc)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pydantic/main.py", line 577, in from_orm
values, fields_set, validation_error = validate_model(cls, obj)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pydantic/main.py", line 1102, in validate_model
values = validator(cls_, values)
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/givenergy_modbus/model/inverter.py", line 212, in compute_model
values['inverter_model'] = Model.from_serial_number(values['inverter_serial_number'])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/givenergy_modbus/model/inverter.py", line 39, in from_serial_number
raise UnknownModelError(f"Cannot determine model number from serial number {serial_number}")
givenergy_modbus.model.inverter.UnknownModelError: Cannot determine model number from serial number FA
Could v1.0 allow individual setting of start and end times for timeslots rather than just both together.
This is useful for automations and manual control in HA through GivTCP.
It would prevent some race conditions when trying to set a single start or end slot.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.