Skip to main content

Asyncio Python client for E3/DC's RSCP (Remote Storage Control Protocol)

Project description

rscp_lib

An asyncio Python client library for E3/DC's RSCP (Remote Storage Control Protocol) — the TCP protocol used to communicate with E3/DC home-storage and energy-management devices (default port 5033).

The library handles the full protocol stack:

  • TCP framing (0xDCE3 magic, timestamp, length)
  • AES/Rijndael-256 CBC encryption with stateful IV chaining
  • Authentication (user/password)
  • Typed tag/value (TLV) (de)serialization, including nested containers
  • A small path/filter query language for extracting values from responses

Requirements

pip install py3rijndael

Installation

The package is not yet published to PyPI. Install directly from the repository:

pip install git+https://github.com/<your-user>/e3dc_rscp_lib.git

or clone and add the project root to your PYTHONPATH.

Quick start

import asyncio

from rscp_lib.RscpConnection import RscpConnection
from rscp_lib.RscpEncryption import RscpEncryption
from rscp_lib.RscpFrame import RscpFrame
from rscp_lib.RscpValue import RscpValue


async def main():
    cipher = RscpEncryption("YOUR_RSCP_PASSPHRASE")
    conn = RscpConnection(
        host="192.168.1.50",
        port=5033,
        ciphersuite=cipher,
        username="user@example.com",
        password="portal-password",
    )

    await conn.connect()
    if not await conn.authorize():
        raise RuntimeError("authentication failed")

    # Request the current PV power
    request = RscpValue().withTagName("TAG_EMS_REQ_POWER_PV", None)
    await conn.send(RscpFrame().packFrame(request))

    data = await conn.receive()
    frame = RscpFrame()
    frame.unpack(data)

    for value in frame.getRscpValues():
        print(value.toString())

    conn.disconnect()


asyncio.run(main())

Building requests

Simple values use withTagName:

RscpValue().withTagName("TAG_EMS_REQ_POWER_BAT", None)

Nested containers can be built declaratively via construct_rscp_value:

req = RscpValue.construct_rscp_value(
    "TAG_RSCP_REQ_AUTHENTICATION",
    [
        ["TAG_RSCP_AUTHENTICATION_USER", "user@example.com"],
        ["TAG_RSCP_AUTHENTICATION_PASSWORD", "secret"],
    ],
)

Reading responses

For navigating deeply nested container responses, use the path helper:

values = frame.getRscpValues()

# Direct child
soc = RscpValue.get_tag_by_path(values, "TAG_EMS_BAT_SOC")

# Nested path
v = RscpValue.get_tag_by_path(values, "TAG_PVI_DATA/TAG_PVI_DC_POWER")

# Filter a container by a child tag's value, then descend
string0 = RscpValue.get_tag_by_path(
    values,
    "TAG_PVI_DATA(TAG_PVI_INDEX==0)/TAG_PVI_DC_POWER",
)

Architecture

The stack is built from four composable layers:

Layer Module Responsibility
Tag/Value (TLV) RscpValue.py Typed tag-value encoding with nested containers
Frame RscpFrame.py Wire frame header, timestamp, and length
Encryption RscpEncryption.py Rijndael-256 CBC with rolling IV
Connection RscpConnection.py Async TCP socket, authentication, send/receive

RscpTags.py contains the full tag dictionary (tag name → tag code + declared type), used for both packing outgoing values and decoding incoming ones.

Protocol notes

A few things worth knowing when extending the library:

  • Encryption IVs start as 0xff × 32 and are updated to the last ciphertext block after every operation. A new connection must call RscpEncryption.reset() (RscpConnection.connect() does this automatically).
  • TAG_RSCP_AUTHENTICATION responses on failure come back as Int32 instead of the declared UChar8 — handled as a special case in RscpValue.unpack.
  • Error tags use type id 0xFF; a 1-byte payload is an Error8, a 4-byte payload an Error32. Check RscpValue.isError after unpacking.
  • Ciphertext whose length is not a multiple of the block size cannot be decrypted — decrypt() returns None and a warning is logged. The caller is expected to read more bytes and retry.
  • Outgoing frames set nanoseconds to 0; only int(time.time()) is transmitted.

Status

This is an independent implementation of the RSCP protocol based on publicly available documentation and is not affiliated with or endorsed by E3/DC GmbH. The tag dictionary is fairly complete, but not every tag combination has been exercised against real hardware. Contributions and bug reports are welcome.

License

The code is distributed under MIT license. See LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rscp_lib-1.0.0.tar.gz (52.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rscp_lib-1.0.0-py3-none-any.whl (46.2 kB view details)

Uploaded Python 3

File details

Details for the file rscp_lib-1.0.0.tar.gz.

File metadata

  • Download URL: rscp_lib-1.0.0.tar.gz
  • Upload date:
  • Size: 52.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rscp_lib-1.0.0.tar.gz
Algorithm Hash digest
SHA256 66d0d6a399e56c7192326de3ec6c8d790f6bab6d94e00cc615f07b71c822a939
MD5 631fe71b76cd829f5b2d6f77345859c4
BLAKE2b-256 0b5053ef1402899c05756b435e3eda18c2c5f96c56d0a49a962da44efe6664db

See more details on using hashes here.

Provenance

The following attestation bundles were made for rscp_lib-1.0.0.tar.gz:

Publisher: publish.yml on tobias-terhaar/rscp-lib

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rscp_lib-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: rscp_lib-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 46.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rscp_lib-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 901a5698e69efbfe84c590c8a320ce1fb107363a1a6fce1e6f4060526ba4387c
MD5 cc6e7693d9bf563c155cfc862f7a7270
BLAKE2b-256 dbc5bbe99a1e2dca150f9eebc21f58154855dbef52115500a7c5fa8e2dd58b40

See more details on using hashes here.

Provenance

The following attestation bundles were made for rscp_lib-1.0.0-py3-none-any.whl:

Publisher: publish.yml on tobias-terhaar/rscp-lib

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page