Venus OS D‐Bus S2 Interface - victronenergy/venus GitHub Wiki

The D-Bus S2 Interface

The S2 Interface enables standardized, flexible energy communication between the energy optimization algorithms (Customer Energy Manager — CEM) and controllable energy devices (Resource Managers — RMs) within the Victron ecosystem.

Its purpose is to provide a common language for energy flexibility, following the S2 Standard — a European semantic standard (EN 50491-12-2), for describing and controlling flexible energy resources such as EV chargers, heat pumps, and smart plugs.

Victron uses S2 internally on Venus OS as the communication layer between the optimizer and connected devices. Rather than implementing S2 over IP (as commonly done elsewhere), Venus OS carries S2 messages via D-Bus, which already provides local service discovery and communication on GX devices.

Purpose and Concept

The S2 model defines two main roles:

  • Customer Energy Manager (CEM) – the component that decides how energy should be distributed, optimized, or shifted in time (e.g., the energy optimizer).
  • Resource Manager (RM) – the component representing a controllable physical device such as an EV charger, heater, inverter, or switchable socket. The RM exposes the device’s flexibility and accepts control instructions from the CEM.

Each physical device is already represented on Venus OS as a D-Bus service, see dbus. If that device supports S2 control, it adds a range of well-known D-Bus paths:

S2/0/Rm                        <- mandatory, the D-Bus S2 interface (com.victronenergy.S2)
S2/0/Active                    <- mandatory, read-only flag, shows if any S2 control type except NO_CONTROL was activated
S2/0/Priority                  <- mandatory, set to invalid on default, controlled by Venus OS, persisted in localsettings by the service itself

S2/0/RmSettings/OffHysteresis  <- optional, in seconds, for internal use of the RM
S2/0/RmSettings/OnHysteresis   <- optional, in seconds, for internal use of the RM
S2/0/RmSettings/PowerSetting   <- optional, in Watt, for internal use of the RM

The CEM continuously monitors D-Bus for new services and automatically discovers S2-enabled devices by checking for this path.

This D-Bus based discovery avoids the need for any explicit network discovery protocol — a natural fit for the GX environment.

Stateful Connections over D-Bus

While D-Bus makes discovery easy, it does not provide a stateful connection between components. The com.victronenergy.S2 D-Bus Interface therefore introduces a set of D-Bus Methods and Signals, which enable to implmement a small state layer.

This layer maintains persistent session awareness between the CEM and each RM through explicit connect/disconnect calls and keep-alive signals.

Connection Flow

  1. Discovery – CEM detects a new D-Bus service that includes /S2/0/Rm and checks for the S2 D-Bus interface via the Discover method
  2. Connect – CEM calls the Connect method on /S2/0/Rm, passing its unique cem_id and a keep_alive_interval (in seconds).
  3. Subscription – CEM subscribes to the Message and Disconnect signals of the RM.
  4. KeepAlive – CEM periodically calls KeepAlive to ensure the connection remains valid.
  5. Messaging – Both sides exchange S2-formatted message strings using the Message method and signal.

When the CEM stops sending KeepAlive messages, or a Disconnect is sent from the CEM or RM for another reason, the session is considered closed and needs to be re-established – including the S2 messaging state on top of the S2 D-Bus interface.

D-Bus Interface Definition

Interface com.victronenergy.S2

Methods

(Bool success) Discover()

Allows to check whether the device supports S2. As of now, the method always returns true.

(Bool success) Connect(String cem_id, Int32 keep_alive_interval)

Attempts to establish a session for cem_id.

  • Succeeds (true) only if no CEM is currently connected.
  • On success, the RM records cem_id, the keep_alive_interval (seconds), and starts a keep-alive monitor with a leeway
  • If another CEM is already connected, returns false (no takeover).

(Void) Disconnect(String cem_id)

Requests to end the session for cem_id.

  • If cem_id matches the active CEM, the session is torn down.
  • If cem_id does not match, the RM emits a Disconnect signal with reason "Not connected" to the provided (wrong) cem_id and does nothing else.

(Void) Message(String cem_id, String message)

Sends an S2 message from the CEM to the RM.

  • If cem_id matches the active CEM, the message is processed.
  • If cem_id does not match, the RM emits a Disconnect signal with reason "Not connected" to that cem_id and ignores the message.

(Bool success) KeepAlive(String cem_id)

Heartbeat from the CEM.

  • If cem_id matches, updates last-seen timestamp and returns true.
  • If cem_id does not match, emits Disconnect ("Not connected") to that cem_id and returns false.
  • The RM’s monitor checks liveness every keep_alive_interval seconds; if overdue, it emits Disconnect with reason "KeepAlive missed" and ends the session.

Signals

Message(String cem_id, String message)

Emitted by the RM to deliver an S2 message to the connected CEM.

Disconnect(String cem_id, String reason)

Emitted by the RM when a session is terminated or an invalid client interacts.

Implementation Notes

  • Each RM (device driver) implements the S2 D-Bus methods and emits signals
  • Each CEM maintains a registry of active RMs and their connection states
  • S2 message payloads are plain strings containing serialised S2 JSON messages, cf. docs.s2standard.org
  • The KeepAlive mechanism is crucial for maintaining the logical link, since D-Bus does not preserve connection state
  • Disconnect reasons are free-form strings

Beyond the D-Bus S2 Interface and further details about S2 messaging

From the S2 documentation:

S2 was designed as a semantic protocol, which can have multiple, mutually compatible, implementing protocols. Different devices might prefer a different form of communication. For example, you can have a version of S2 which uses bluetooth, and another version which uses KNX. These versions differ in the transport protocol they use, but typically also in the way they encode data. Since both protocols are based upon the same S2 specification, one S2 implementing protocol can easily be translated into the other version using a simple software adapter.

Source: S2 Documentation, chapter "Communication Layer: Introduction"

In this sense, the D-Bus S2 Interface described above is (only) the "transport protocol", which enables to exchange S2 messages in a stateful/session-based manner between CEM and RM. For data encoding, we decided on JSON string encoded messages, as it works great over D-Bus and allows to reuse large parts of the S2 protocol definition for WebSockets s2-ws-json as well as its reference implementation in Python s2-python.

For the data models and the S2 messaging state, we refer to the official S2 documentation: https://docs.s2standard.org/

During implementation, some additional assumptions and definitions regarding the interaction between CEM and RM had to be made, which are not clearly stated in the official documentation. Therefore, the following documents how we solved them:

How to deal with unresponsiveness or unexpected behaviour

  • The CEM uses several mechanisms to detect an RM becoming unresponsive or derailing from active instructions. In such situations the CEM may disconnect the RM via the D-Bus Disconnect method and retry re-connecting periodically.
  • It is the duty of the RM to return its controlled consumer into an energy neutral state or whatever is the desired situation after an (unexpected) disconnect sent by the CEM. The same rule applies if the RM detects absence of the periodic KeepAlive sent by the CEM.
  • An RM is considered unresponsive if, for example but not limited to, a successful state-transition is not reported within 30 seconds after instructions were first send. The CEM will repeat the desired instructions for a maximum of six times within that period.
  • It is the RM's responsibility at all times to ensure the connectivity and operational status of the controlled device. Should connectivity issues or operating conditions (e.g., overheating) prevent operation or compliance with the CEM's instructions, the RM ignores the instructions and changes its ControlType to "NO_CONTROL" until normal operation can be resumed.