Skip to content

Conversation

sgryphon
Copy link

@sgryphon sgryphon commented Jun 7, 2025

Overview

This PR implements support for communicating with multiple Modbus devices sharing a single connection but with different unit IDs. This is particularly useful for gateway devices that expose multiple logical devices through a single TCP connection (such as some SolarEdge configurations).

Features

  • Unit Collection API: Access models from different unit IDs through the Units collection
  • Scanning Multiple Units: New scan_units([unit_ids]) method to discover models on multiple units
  • Unit-Specific Operations: read_unit() and write_unit() methods for direct register access
  • Consistent Interface: Units expose the same API as regular devices
  • Full Backwards Compatibility: Existing code continues to work without modification

Example Usage

# Create a single TCP connection to a gateway device
d = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='192.168.1.100', ipport=502)
d.connect()

# Scan multiple units for SunSpec models
d.scan_units([1, 2, 3])

# Access models from different units
print(f"Unit 1 manufacturer: {d.Units[1].common[0].Mn.value}")
print(f"Unit 2 power: {d.Units[2].inverter[0].W.value}")
print(f"Unit 3 energy: {d.Units[3].meter[0].TotWhExp.value}")

# Backwards compatible with existing API
print(f"Default Unit manufacturer (same as Unit 1): {d.common[0].Mn.value}")

Implementation Details

  • Added SunSpecModbusClientUnit class that acts as a proxy for a specific unit ID
  • Created SunSpecModbusClientUnitCollection to manage and provide access to units
  • Implemented proper delegation of Modbus commands to the parent device
  • Added comprehensive tests to verify functionality
  • Updated documentation with examples and best practices

Closes

Closes #107

sgryphon added 6 commits June 7, 2025 20:29
Modernizes the Modbus terminology by using "unit ID" throughout the codebase.
This addresses issue sunspec#105 by:

- Renaming parameters and variables to unit_id
- Updating method signatures while maintaining backward compatibility
- Revising documentation and comments to reflect current industry standards
- Ensuring consistent terminology across the entire Modbus implementation

This change improves code readability and aligns with current Modbus
specification terminology while maintaining full backward compatibility.
Updates test cases to align with the modernized unit ID terminology.
Ensures all tests properly exercise the updated API while maintaining
complete test coverage include backward compatibility tests.
Implements the ability to communicate with multiple Modbus devices sharing
the same TCP/RTU connection but with different unit IDs. This addresses
issue sunspec#107 by:

- Adding SunSpecModbusClientUnit class to represent a specific unit ID
- Creating SunSpecModbusClientUnitCollection for managing units
- Adding scan_units method to discover models on specific unit IDs
- Implementing read_unit and write_unit methods for unit-specific operations
- Ensuring proper delegation of Modbus commands to the parent device

This change enables users to interact with multiple logical devices on a
single physical connection while maintaining the same API interface.
Adds comprehensive test cases for the new multiple unit ID functionality.
Verifies proper behavior of unit scanning, reading, and writing across
different unit IDs on the same connection.
Updates documentation to explain the new multiple unit ID functionality.
Includes usage examples, API references, and best practices for working
with multiple logical devices on a single connection.
@dersecure
Copy link
Contributor

There has been a lot of discussion about the use of, and need for, the unit ID with TCP devices. I didn't realize that SolarEdge was using a gateway in this way.

What's the biggest benefit of this functionality over this approach:

devices = []
devices.append(client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='192.168.1.100', ipport=502))
devices.append(client.SunSpecModbusClientDeviceTCP(unit_id=2, ipaddr='192.168.1.100', ipport=502))
devices.append(client.SunSpecModbusClientDeviceTCP(unit_id=3, ipaddr='192.168.1.100', ipport=502))

I suppose in the future case where we may have encrypted connections to equipment, this may reduce the number of TLS handshakes to the gateway?

@sgryphon
Copy link
Author

There has been a lot of discussion about the use of, and need for, the unit ID with TCP devices. I didn't realize that SolarEdge was using a gateway in this way.
What's the biggest benefit of this functionality over this approach:

SolarEdge only supports a single TCP connection and session, see p. 13 of https://knowledge-center.solaredge.com/sites/kc/files/sunspec-implementation-technical-note.pdf

Creating multiple devices would work if you only ever used one connection at a time. i.e. if you used the automatic connect/disconnect.

But it doesn't work if you want to use connect(), then do a bunch of operations across multiple connections. Having child devices of the main connection (i.e. multiple units across one connection) avoids this issue. The unit ID is just a parameter sent in messages, so this works fine.

There is also a mention in the documentation of a connection idle time of 2 mins, which originally I thought meant you had to wait 2 mins before you could make the second connection, because I was having a lot of trouble using HA-sunspec to connect twice to the same IP address (the whole driver for this).

Although I'm not sure now if that is the issue, as the connection to unit 2 just seems flakey (even if it is the only one).

But I've already done the work, so thought I might as well submit the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow multiple unit IDs to be accessed through a single connection
2 participants