Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

v1.0 #29

Open
wants to merge 227 commits into
base: main
Choose a base branch
from
Open

v1.0 #29

Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
227 commits
Select commit Hold shift + click to select a range
a7b9db1
Merge pull request #1 from jakekeeys/jkeeys/battery-reg-docs
dewet22 Jan 4, 2022
c1dba2c
0.6.0: the great register renaming
dewet22 Jan 4, 2022
c702675
Bump version: 0.5.0 → 0.6.0
dewet22 Jan 4, 2022
368dd58
fine, if you insist pypi
dewet22 Jan 4, 2022
c79611b
Bump version: 0.6.0 → 0.6.1
dewet22 Jan 4, 2022
94184fb
update changelog for 0.6.1
dewet22 Jan 4, 2022
90729de
try to fix prod release workflow
dewet22 Jan 4, 2022
7a52edc
disable doc publishing until project is public
dewet22 Jan 4, 2022
8ae18ac
Bump version: 0.6.1 → 0.6.2
dewet22 Jan 4, 2022
4f79f39
add allow list at WriteHoldingRegisterRequest validation as another c…
dewet22 Jan 4, 2022
f9e1e28
v0.7.0 - better client with convenience methods instead of having to …
dewet22 Jan 5, 2022
5d36a30
Bump version: 0.6.2 → 0.7.0
dewet22 Jan 5, 2022
c6eae4c
preparing for 0.8.0
dewet22 Jan 9, 2022
4c08845
fix changelog date
dewet22 Jan 9, 2022
287556e
Bump version: 0.7.0 → 0.8.0
dewet22 Jan 9, 2022
3f1e555
mypy and tests are painful :(
dewet22 Jan 9, 2022
c3d9834
small fixes missed in the refactoring
dewet22 Jan 9, 2022
bc15552
tiny doc updates, also update some deps
dewet22 Jan 10, 2022
8d7dc8c
add work so far for massive remodelling
dewet22 Jan 11, 2022
b1b7034
show how the models work in README
dewet22 Jan 11, 2022
435209c
checkpoint - big work on RegisterCache and RegisterGetter done now
dewet22 Jan 11, 2022
9062bf2
add a bit of resilience for wire parsing, start the work on being abl…
dewet22 Jan 11, 2022
1073d24
everything going into 0.9.0
dewet22 Jan 12, 2022
b8267d1
logging cleanups, add devtools to dev env, start Plant DTO
dewet22 Jan 13, 2022
c1c5205
tweak changelog
dewet22 Jan 13, 2022
4449dbc
Bump version: 0.8.0 → 0.9.0
dewet22 Jan 13, 2022
ef4b7f3
squelch incorrect mypy errors
dewet22 Jan 13, 2022
3c6bb06
bugfix: the fault _time registers are not BCD-encoded timestamps but …
dewet22 Jan 13, 2022
9501a5a
also guard for cases when timestamps have 60 minutes
dewet22 Jan 13, 2022
2d90d07
Bump version: 0.9.0 → 0.9.1
dewet22 Jan 13, 2022
9ab49f6
Bump version: 0.9.0 → 0.9.1
dewet22 Jan 13, 2022
1285925
fix floating error nonsense with scaled registers, update README
dewet22 Jan 13, 2022
57288ac
start adding more cli commands
dewet22 Jan 13, 2022
e6e1a42
add some client methods
dewet22 Jan 14, 2022
032caf0
start adding some client methods to the cli
dewet22 Jan 14, 2022
f606d0e
Bump ipython from 7.31.0 to 7.31.1
dependabot[bot] Jan 21, 2022
b086644
Merge pull request #2 from dewet22/dependabot/pip/ipython-7.31.1
dewet22 Jan 22, 2022
25149f4
poetry update
dewet22 Jan 24, 2022
4dbbd7d
Set theme jekyll-theme-minimal
dewet22 Jan 24, 2022
e2d7fb9
re-enable pages
dewet22 Jan 24, 2022
6a55225
CHANGELOG for v0.9.2
dewet22 Jan 24, 2022
1e728c6
Bump version: 0.9.1 → 0.9.2
dewet22 Jan 24, 2022
27c851a
Update Energy Total Registers (#4)
britkat1980 Jan 31, 2022
bdbae76
Bump loguru from 0.5.3 to 0.6.0 (#5)
dependabot[bot] Jan 31, 2022
fe24919
add python 3.8 back
dewet22 Jan 31, 2022
76156e4
version bump deps & precommit, more py38 unfixes
dewet22 Feb 1, 2022
e96b45d
more fixes for py38
dewet22 Feb 1, 2022
e14b3fc
mkdocs fails on py38 with type hints
dewet22 Feb 1, 2022
0425a79
add py37 support back for raspi
dewet22 Feb 1, 2022
29c803e
Changelog for v0.9.3
dewet22 Feb 1, 2022
fdcb45c
Bump version: 0.9.2 → 0.9.3
dewet22 Feb 1, 2022
db9062d
Create codeql-analysis.yml
dewet22 Feb 2, 2022
51d4451
add support for hybrid inverters having SD serial number prefixes
dewet22 Feb 15, 2022
24e49a0
serialize Model to str instead of int, more useful exception message
dewet22 Feb 15, 2022
1659bd9
changelog for v0.9.4
dewet22 Feb 20, 2022
f4f6319
Bump version: 0.9.3 → 0.9.4
dewet22 Feb 20, 2022
04ca0b2
Fix for odd f/w (#7)
britkat1980 Mar 1, 2022
dbb005e
rejig the Plant model
dewet22 Mar 2, 2022
6e0a130
changelog for 0.10.0
dewet22 Mar 2, 2022
794bedf
Bump version: 0.9.4 → 0.10.0
dewet22 Mar 2, 2022
3d45b59
make `Plant` serializable
dewet22 Mar 3, 2022
bae3543
Bump version: 0.10.0 → 0.10.1
dewet22 Mar 3, 2022
7090140
Loads of fixes ahead of creating the async client
dewet22 Apr 5, 2022
2b6cb3c
fix #8
dewet22 Apr 8, 2022
d4878b6
poetry update
dewet22 Apr 8, 2022
fecb7c9
pre-commit autoupdate
dewet22 Apr 8, 2022
28d8f86
poetry update
dewet22 Apr 10, 2022
209d4e4
Bind RegisterCache to an explicit slave address as an additional sani…
dewet22 Apr 16, 2022
65fa48a
More sensible naming for max grid power output parameter (it isn't a …
dewet22 Apr 16, 2022
af15888
Refactor Plant to care about slave addresses, and allow updating from…
dewet22 Apr 16, 2022
e9a34e8
Include a "null" Response – some Transparent responses come back with…
dewet22 Apr 16, 2022
cd8a889
poetry update
dewet22 Apr 16, 2022
ac8582a
Implement RegisterCache updating from WriteHoldingRegisterResponse PDU
dewet22 Apr 16, 2022
e06c5c6
Clean up ModbusClient by not inheriting from ModbusTcpClient – the in…
dewet22 Apr 16, 2022
777d7dc
Bugfix - extract the correct frame (an overfull buffer will include t…
dewet22 Apr 16, 2022
197448c
Rename `p_grid_port_max_output` to `grid_port_max_power_output`
dewet22 Apr 16, 2022
7047d83
Improve Framer contract to pass raw frames to the callback when proce…
dewet22 Apr 16, 2022
8f334a5
Bugfix - WriteHoldingRegisterResponse does not actually have a field …
dewet22 Apr 16, 2022
ec2056e
Fix pre-commit issues
dewet22 Apr 16, 2022
edfb897
fix wrong import
dewet22 Apr 16, 2022
9a1d09f
poetry update
dewet22 Apr 16, 2022
6590071
giving up trying to get mkdocs to work, FIXME in future
dewet22 Apr 16, 2022
ae1f8b9
Remove the TransactionManager since we're not using it anywhere
dewet22 Apr 17, 2022
ed5b0f7
Fix bad test data
dewet22 Apr 17, 2022
28d2066
Small convenience for accessing the buffer length in the framer.
dewet22 Apr 17, 2022
03c532f
Rearrange pre-commit tests to try and make it more one-shot
dewet22 Apr 19, 2022
6b56d5c
Remove a few unused crufty utilities
dewet22 Apr 19, 2022
8dc5cdb
Some insignificant cleanups for PDUs
dewet22 Apr 19, 2022
67237b7
Some insignificant cleanups for PDUs, plus some more cleanups
dewet22 Apr 19, 2022
6d556f4
Add aiofiles, metrology and sentry-sdk deps
dewet22 Apr 19, 2022
4ea13c6
Big refactoring of Registers in general to be more accurate and flexible
dewet22 Apr 19, 2022
cb2929e
Prevent RegisterCaches from updating if the PDU doesn't seem sane
dewet22 Apr 19, 2022
dd2425e
Make Plants dynamically discover number of batteries
dewet22 Apr 19, 2022
5759f63
First checkin of the AsyncIO-based client
dewet22 Apr 19, 2022
24678ca
WIP: add shape testing and the start of message mirror generation to …
dewet22 Apr 20, 2022
5da16fc
squelch the cffi deprecation warning that started showing up after py…
dewet22 Apr 20, 2022
d1dd15a
Refactor PDU modelling
dewet22 Apr 22, 2022
78bda9c
Refactor and simplify Framer
dewet22 Apr 22, 2022
cb24aa7
Fix logging destination for top-level modules
dewet22 Apr 22, 2022
64be624
More robust handling of missing serial numbers (should be fixing #9)
dewet22 Apr 22, 2022
96f1334
Start custom exception tree for richer error handling
dewet22 Apr 22, 2022
1faceb2
Bugfix for 7-byte header
dewet22 Apr 22, 2022
a032c1a
fix pre-commit checks
dewet22 Apr 22, 2022
59f758c
better CI & small fixes
dewet22 Apr 24, 2022
37c710e
async client: more WIP
dewet22 Apr 24, 2022
1672ddf
ignore /debug files
dewet22 Apr 24, 2022
045c6cd
poetry update
dewet22 Apr 26, 2022
fe618c5
Prep work for being able to track expected responses
dewet22 Apr 26, 2022
aeb5d8a
Fix the framer callback API
dewet22 Apr 26, 2022
2deb20b
Implement single command sending & testing response
dewet22 Apr 26, 2022
3d21c72
Lots of cleanups
dewet22 May 3, 2022
0309f24
Try to handle missing data more gracefully
dewet22 May 4, 2022
b74dfb4
Loads more work on the AsyncClient
dewet22 May 5, 2022
edea206
poetry update & turn down some noisy logs
dewet22 May 5, 2022
867ab1f
pre-commit autoupdate
dewet22 May 5, 2022
07d3402
Fixes for py3.7/3.8 issues
dewet22 May 7, 2022
996a0ad
poetry update
dewet22 May 7, 2022
99ae998
pre-commit fixes
dewet22 May 7, 2022
e511a4c
Continue expanding typed exceptions over generic exceptions
dewet22 May 12, 2022
6b16ce7
poetry update
dewet22 May 12, 2022
cf075f9
start refactoring Inverter model to make it better
dewet22 May 12, 2022
323735a
improve robustness while trying to hunt down a malformed null response
dewet22 May 12, 2022
10a2cf6
Fix py3.8 issue
dewet22 May 12, 2022
d82234a
remove metrology since cffi doesn't compile on windows
dewet22 May 12, 2022
f6bbea8
small refactoring
dewet22 May 12, 2022
2159b03
Fix for UTC timestamps and lower granularity on Windows
dewet22 May 12, 2022
66e14a1
poetry update
dewet22 May 15, 2022
bb95ec6
add pendulum
dewet22 May 13, 2022
0321e0d
poetry update
dewet22 May 31, 2022
ce4b490
Refactor and simplify the client
dewet22 May 31, 2022
6e79209
small fixes, do away with the cumbersome Message construct
dewet22 May 31, 2022
66453d4
Improve the framer: let PDUs encode and decode themselves
dewet22 May 31, 2022
5d819e8
Bump certifi from 2022.5.18.1 to 2022.12.7 (#12)
dependabot[bot] Dec 12, 2022
81d80e3
Bump pip from 20.3.4 to 21.1 (#13)
dependabot[bot] Dec 12, 2022
9564508
Bump cryptography from 37.0.2 to 38.0.3 (#14)
dependabot[bot] Dec 13, 2022
24ff6b9
Update codeql-analysis.yml (#15)
dewet22 Dec 15, 2022
2a07eff
Bump cryptography from 38.0.3 to 39.0.1 (#20)
dependabot[bot] Feb 8, 2023
1ca6896
Bump gitpython from 3.1.27 to 3.1.30 (#21)
dependabot[bot] Feb 9, 2023
d7165ab
Update inverter.py (#22)
Dominic-Kua Mar 4, 2023
555d46d
Update battery.py (#16)
holdestmade Mar 4, 2023
a31cbec
Bump sentry-sdk from 1.5.12 to 1.14.0 (#25)
dependabot[bot] Mar 23, 2023
eeff7b5
update deps
dewet22 Apr 3, 2023
2f49776
remove ipython from dev deps to silence dependabot
dewet22 Apr 3, 2023
d919095
fix tests for py3.11
dewet22 Apr 3, 2023
ac1563a
update pre-commit checks to latest, make py3.11 default
dewet22 Apr 3, 2023
c430988
remove py3.7 (eol in 2 months) and re-add ipython dev dep
dewet22 Apr 3, 2023
ac57a55
update test deps
dewet22 Apr 3, 2023
24d66bb
remove version checks for py3.7
dewet22 Apr 3, 2023
3cb764c
fix tests for <py3.11
dewet22 Apr 3, 2023
a4db162
update github actions versions
dewet22 Apr 3, 2023
6f170d6
fix codecov action version
dewet22 Apr 3, 2023
ed3ddf4
try to debug actions versions
dewet22 Apr 3, 2023
b2db1eb
more stupid test fixes
dewet22 Apr 3, 2023
0d49261
fix lint errors
dewet22 Apr 3, 2023
1a48390
try to update setup-python action
dewet22 Apr 3, 2023
4e32d09
update codecov action version
dewet22 Apr 3, 2023
978d592
start refactoring the CLI to show how to use the v1 async interface
dewet22 Apr 3, 2023
5af14c0
more CLI utilities
dewet22 Apr 4, 2023
f4a6fdc
last few CLI commands fixed
dewet22 Apr 4, 2023
c08ab5c
more updated versions, fixing the poetry deps mess, fixing docs gener…
dewet22 Apr 4, 2023
9bd8389
fix py3.8 incompatibility
dewet22 Apr 4, 2023
96254ed
update non-dev workflows
dewet22 Apr 4, 2023
25dbdd8
update non-dev workflows
dewet22 Apr 4, 2023
ac76260
try to cache poetry to speed up actions
dewet22 Apr 4, 2023
4f3a8d3
add missing allowlist
dewet22 Apr 4, 2023
5b82ca4
fix
dewet22 Apr 4, 2023
5e3c5d1
rename Coordinator->Client and fold the network client into it
dewet22 Apr 5, 2023
1ff202a
run black ahead of flake8 to reformat things it complains about earlier
dewet22 Apr 5, 2023
ed00e67
add passive mode for just monitoring traffic, simplify more network c…
dewet22 Apr 5, 2023
68d6668
add holding register to reboot inverter
dewet22 Apr 5, 2023
9713abd
small fixes
dewet22 Apr 5, 2023
0578da0
Bump version: 0.10.1 → 0.99.0
dewet22 Apr 5, 2023
c2c4190
fix
dewet22 Apr 5, 2023
7b0aafd
stupid py3.8 lint
dewet22 Apr 5, 2023
3439404
add ACTIVE_POWER_RATE and INVERTER_REBOOT as writable holding registers
dewet22 Apr 6, 2023
7b5f318
rename some commands to match register names
dewet22 Apr 6, 2023
beef71d
disable debug frames which was causing the client to block when the q…
dewet22 Apr 6, 2023
567a234
lint
dewet22 Apr 6, 2023
2593454
add connected flag for convenience
dewet22 Apr 12, 2023
1b89598
add virtual aggregates for PV power & energy
dewet22 Apr 12, 2023
599573b
poetry update
dewet22 Apr 12, 2023
d5b908d
small cleanups, starting to remove CLI to spin out to separate packag…
dewet22 Apr 12, 2023
4d50ace
deleting old CLI
dewet22 Apr 13, 2023
1fd782e
set py3.9 as minimum, clean up deps, re-enable some pre-commit checks,
dewet22 Apr 13, 2023
e4c7d38
Bump version: 0.99.0 → 1.0.0-dev.0
dewet22 Apr 13, 2023
72d1ca5
fix <py3.11 incompatibility
dewet22 Apr 14, 2023
f5ad797
continuing to dismantle auto-registers
dewet22 Apr 14, 2023
e5b99bb
add battery discharge mode
dewet22 Apr 14, 2023
352c520
add enable_60hz_freq_mode
dewet22 Apr 14, 2023
2daf83f
add battery_calibration_stage
dewet22 Apr 14, 2023
c724529
add modbus_address
dewet22 Apr 14, 2023
9b86c82
add charge_slot_2
dewet22 Apr 14, 2023
2f93143
add modbus_version
dewet22 Apr 14, 2023
54096bf
add system_time
dewet22 Apr 14, 2023
11231f0
add enable_drm_rj45_port
dewet22 Apr 14, 2023
31e4e9b
add 7 more inverter fields
dewet22 Apr 14, 2023
5d4d893
move TimeSlot to model package instead
dewet22 Apr 14, 2023
79bdf1a
small fixes
dewet22 Apr 14, 2023
1354874
add more inverter fields
dewet22 Apr 14, 2023
7bfd521
yet more fields
dewet22 Apr 14, 2023
29938ca
yet more fields
dewet22 Apr 14, 2023
639bb62
nearly at the end of holding registers
dewet22 Apr 14, 2023
bd5b57d
add high holding registers, make register creation dynamic
dewet22 Apr 15, 2023
de4b0a7
start disabling unused register definitions
dewet22 Apr 15, 2023
cc18789
poetry update
dewet22 Apr 15, 2023
571501d
update deps
dewet22 Apr 17, 2023
3434f58
update poetry deps & pre-commit hooks to latest
dewet22 Apr 22, 2023
0d8ec13
remove pymodbus dep :tada:
dewet22 Apr 24, 2023
e8d8e13
poetry update
dewet22 Apr 26, 2023
1fa09fa
poetry update
dewet22 Apr 28, 2023
1dcd1de
pre-commit autoupdate
dewet22 Apr 28, 2023
4ddacf2
poetry update
dewet22 May 1, 2023
7de8c51
poetry update & pre-commit autoupdate
dewet22 May 11, 2023
f234090
refactored registers to be dumber – that functionality needs to be in…
dewet22 May 11, 2023
4fa651f
model improvements
dewet22 May 11, 2023
4326484
poetry update
dewet22 May 12, 2023
b89861c
finished with core refactoring for Inverter, Battery next
dewet22 May 12, 2023
4256480
remove custom JSONEncoder that doesn't work well with iterable regist…
dewet22 May 12, 2023
2976e04
pre-commit fixes
dewet22 May 12, 2023
a93c07d
finished with core refactoring for Battery
dewet22 May 12, 2023
8a0e741
finish Battery refactoring
dewet22 May 12, 2023
6620399
add inverter registers HR(0-59)
dewet22 May 12, 2023
e46795c
add inverter registers HR(60-119)
dewet22 May 12, 2023
91ef4f5
add inverter registers HR(120-179)
dewet22 May 13, 2023
ddafa2a
add inverter registers HR(the_rest)
dewet22 May 13, 2023
2132d87
small fixes
dewet22 May 16, 2023
30a1b1d
poetry update
dewet22 May 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
finished with core refactoring for Inverter, Battery next
  • Loading branch information
dewet22 committed May 12, 2023
commit b89861cbd855373be5267a370ea83be5c1c9fda2
7 changes: 1 addition & 6 deletions givenergy_modbus/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
from enum import IntEnum
from typing import TYPE_CHECKING

from pydantic import BaseModel, create_model

from givenergy_modbus.model.inverter import InverterConfig, InverterRegisterGetter
from pydantic import BaseModel

if TYPE_CHECKING:
from givenergy_modbus.model.register_cache import RegisterCache
Expand Down Expand Up @@ -69,6 +67,3 @@ def from_repr(cls, start: int | str, end: int | str):
# Inverter = inverter.Inverter
# Battery = battery.Battery
# RegisterCache = register_cache.RegisterCache
Inverter = create_model(
'Inverter', __config__=InverterConfig, **InverterRegisterGetter.to_fields(), **{'model': 'Foo'}
) # type: ignore[call-overload]
134 changes: 0 additions & 134 deletions givenergy_modbus/model/device.py

This file was deleted.

48 changes: 27 additions & 21 deletions givenergy_modbus/model/inverter.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
from enum import IntEnum
from enum import IntEnum, Enum

from pydantic import BaseConfig
from pydantic import BaseConfig, create_model

from givenergy_modbus.model.device import DataType as DT
from givenergy_modbus.model.device import DeviceRegisterGetter
from givenergy_modbus.model.device import RegisterDefinition as Def
from givenergy_modbus.model.register import HR, IR, Register, RegisterEncoder
from givenergy_modbus.model.register_cache import RegisterCache, RegisterCacheEncoder
from givenergy_modbus.model.register import HR, IR
from givenergy_modbus.model.register import DataType as DT
from givenergy_modbus.model.register import RegisterDefinition as Def
from givenergy_modbus.model.register import RegisterGetter


class Model(IntEnum):
class Model(Enum):
"""Known models of inverters."""

HYBRID = 2
AC = 3
HYBRID_3PH = 4
AC_3PH = 6
EMS = 5
GATEWAY = 7
ALL_IN_ONE = 8
HYBRID = '2'
AC = '3'
HYBRID_3PH = '4'
AC_3PH = '6'
EMS = '5'
GATEWAY = '7'
ALL_IN_ONE = '8'

@classmethod
def _missing_(cls, key: str):
"""Pick model from the first digit of the device type code."""
return cls(key[0])


class UsbDevice(IntEnum):
Expand Down Expand Up @@ -85,12 +89,13 @@ class InverterStatus(IntEnum):
FLASHING_FIRMWARE = 4


class InverterRegisterGetter(DeviceRegisterGetter):
class InverterRegisterGetter(RegisterGetter):
"""Structured format for all inverter attributes."""

REGISTER_LUT = {
# Holding Registers, block 0-59
'device_type_code': Def(DT.hex, None, HR(0)),
'model': Def(DT.hex, Model, HR(0)),
'module': Def(DT.uint32, (DT.hex, 8), HR(1), HR(2)),
'num_mppt': Def((DT.duint8, 0), None, HR(3)),
'num_phases': Def((DT.duint8, 1), None, HR(3)),
Expand All @@ -103,7 +108,7 @@ class InverterRegisterGetter(DeviceRegisterGetter):
'enable_charge_target': Def(DT.bool, None, HR(20)),
'arm_firmware_version': Def(DT.uint16, None, HR(21)),
'usb_device_inserted': Def(DT.uint16, UsbDevice, HR(22)),
'select_arm_chip': Def(DT.bool, UsbDevice, HR(23)),
'select_arm_chip': Def(DT.bool, None, HR(23)),
# variable_address=rc[HR(24)],
# variable_value=rc[HR(25)],
'grid_port_max_power_output': Def(DT.uint16, None, HR(26)),
Expand Down Expand Up @@ -214,10 +219,6 @@ class InverterConfig(BaseConfig):

orm_mode = True
getter_dict = InverterRegisterGetter
json_encoders = {
RegisterCache: RegisterCacheEncoder.encode,
Register: RegisterEncoder.encode,
}


# class Inverter(GivEnergyBaseModel):
Expand Down Expand Up @@ -568,3 +569,8 @@ class InverterConfig(BaseConfig):
# # def compute_e_pv_day(e_pv1_day: float, e_pv2_day: float, **kwargs) -> float:
# # """Computes the discharge slot 2."""
# # return e_pv1_day + e_pv2_day

Inverter = create_model(
'Inverter', __config__=InverterConfig, **InverterRegisterGetter.to_fields()
) # type: ignore[call-overload]
# , **{'model': 'Foo'}
3 changes: 2 additions & 1 deletion givenergy_modbus/model/plant.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging
from typing import Any

from givenergy_modbus.model import GivEnergyBaseModel, Inverter
from givenergy_modbus.model import GivEnergyBaseModel
from givenergy_modbus.model.battery import Battery
from givenergy_modbus.model.inverter import Inverter
from givenergy_modbus.model.register import HR, IR
from givenergy_modbus.model.register_cache import RegisterCache, RegisterCacheEncoder
from givenergy_modbus.pdu import (
Expand Down
132 changes: 131 additions & 1 deletion givenergy_modbus/model/register.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,135 @@
from dataclasses import dataclass
from json import JSONEncoder
from typing import Any
from typing import Any, Callable, Union

from pydantic.utils import GetterDict

from givenergy_modbus.model import TimeSlot


class DataType:
"""Type of data register represents. Encoding is always big-endian."""

@staticmethod
def uint16(val: int) -> int:
"""Simply return the raw unsigned 16-bit integer register value."""
if val is not None:
return int(val)

@staticmethod
def duint8(val: int, *idx: int) -> int:
"""Split one register into two unsigned 8-bit ints and return the specified index."""
if val is not None:
vals = (val >> 8), (val & 0xFF)
return vals[idx[0]]

@staticmethod
def uint32(high_val: int, low_val: int) -> int:
"""Combine two registers into an unsigned 32-bit int."""
if high_val is not None and low_val is not None:
return (high_val << 16) + low_val

@staticmethod
def timeslot(start_time: int, end_time: int) -> TimeSlot:
"""Interpret register as a time slot."""
if start_time is not None and end_time is not None:
return TimeSlot.from_repr(start_time, end_time)

@staticmethod
def bool(val: int) -> bool:
"""Interpret register as a bool."""
if val is not None:
return bool(val)

@staticmethod
def string(*vals: int) -> str:
"""Represent one or more registers as a concatenated string."""
if vals is not None and None not in vals:
return b''.join((v or 0).to_bytes(2, byteorder='big') for v in vals).decode(encoding='latin1').upper()
return ''

@staticmethod
def hex(val: int, width: int = 4) -> str:
"""Represent a register value as a 4-character hex string."""
if val is not None:
return f'{val:0{width}x}'


@dataclass(init=False)
class RegisterDefinition:
"""Specifies how to convert raw register values into their actual representation."""

pre_conv: Union[Callable, tuple, None]
post_conv: Union[Callable, tuple[Callable, Any], None]
registers: tuple['Register']

def __init__(self, *args, **kwargs):
self.pre_conv = args[0]
self.post_conv = args[1]
self.registers = args[2:] # type: ignore[assignment]

def __hash__(self):
return hash(self.registers)


class RegisterGetter(GetterDict):
"""Specifies how device attributes are derived from raw register values."""

REGISTER_LUT: dict[str, RegisterDefinition]

def get(self, key: str, default: Any = None) -> Any:
"""Return a named register's value, after pre- and post-conversion."""
try:
r = self.REGISTER_LUT[key]
except KeyError:
return default

regs = [self._obj.get(r) for r in r.registers]

if None in regs:
return None

if r.pre_conv:
if isinstance(r.pre_conv, tuple):
args = regs + list(r.pre_conv[1:])
val = r.pre_conv[0](*args)
else:
val = r.pre_conv(*regs)
else:
val = regs

if r.post_conv:
if isinstance(r.post_conv, tuple):
return r.post_conv[0](val, *r.post_conv[1:])
else:
return r.post_conv(val)
return val

@classmethod
def to_fields(cls) -> dict[str, tuple[Any, None]]:
"""Determine a pydantic fields definition for the class."""

def infer_return_type(obj: Any):
if hasattr(obj, '__annotations__') and (ret := obj.__annotations__.get('return', None)):
return ret
return obj # assume it is a class/type already?

def return_type(v: RegisterDefinition):
if v.post_conv:
if isinstance(v.post_conv, tuple):
return infer_return_type(v.post_conv[0])
else:
return infer_return_type(v.post_conv)
elif v.pre_conv:
if isinstance(v.pre_conv, tuple):
return infer_return_type(v.pre_conv[0])
else:
return infer_return_type(v.pre_conv)
return Any

register_fields = {k: (return_type(v), None) for k, v in cls.REGISTER_LUT.items()}

return register_fields


class RegisterEncoder(JSONEncoder):
Expand Down
2 changes: 2 additions & 0 deletions givenergy_modbus/model/register_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,6 @@ def to_datetime(self, y: Register, m: Register, d: Register, h: Register, min: R

def to_timeslot(self, start: Register, end: Register) -> 'TimeSlot':
"""Combine two registers into a time slot."""
from givenergy_modbus.model import TimeSlot

return TimeSlot.from_repr(self[start], self[end])
Loading