Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions pv_inverter/deye_hybrid_3p_lv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ packages:
pv: !include packages/deye_hybrid_3p/pv.yaml
tou: !include packages/deye_hybrid_3p/tou.yaml
time_sync: !include packages/deye_hybrid_3p/time_sync.yaml
remote: !include packages/deye_hybrid_3p/remote.yaml

script:
- id: set_safe_modbus_registers
Expand Down
177 changes: 177 additions & 0 deletions pv_inverter/packages/deye_hybrid_3p/remote.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
select:
- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: remote_mode
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
name: "Remote mode"
address: 1100
value_type: U_WORD
optionsmap:
"Disable": 0
"Enable remote mode 1": 1
"Enable remote mode 2": 2
"Enable remote mode 3": 3

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: inverter_output_power_control_mode

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Mode switches use stale setpoints

When a client switches to per-phase control while changing its phase setpoints, or a Modbus write fails partway through those changes, inverter_output_power_control_mode can enable per-phase control before the separate phase setpoint writes complete, so the inverter applies previously stored phase values in the meantime; a dropped write leaves the new mode using a mixed set of values. Update the setpoints before switching modes, or provide a grouped update that switches modes last.

Apply all setpoints first and switch the active control mode last, ideally through one coordinated update.

Also at pv_inverter/packages/deye_hybrid_3p/remote.yaml:117-164.

Prompt for AI agents
In `pv_inverter/packages/deye_hybrid_3p/remote.yaml` at line 18:

**Mode switches use stale setpoints**

When a client switches to per-phase control while changing its phase setpoints, or a Modbus write fails partway through those changes, `inverter_output_power_control_mode` can enable per-phase control before the separate phase setpoint writes complete, so the inverter applies previously stored phase values in the meantime; a dropped write leaves the new mode using a mixed set of values. Update the setpoints before switching modes, or provide a grouped update that switches modes last.

Apply all setpoints first and switch the active control mode last, ideally through one coordinated update.

Also at `pv_inverter/packages/deye_hybrid_3p/remote.yaml:117-164`.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, switching the control mode should be done such that the setpoints are written first and the mode change last. I'm not sure that's the job of this package. The question of a modbus write fail is interesting though, is there a way to block several register updates such that they all succeed or fail together?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not with the normal number and select entities. In the current file, use_write_multiple: true makes each individual entity use Modbus function 16, but it does not combine writes made to separate entities into one transaction. ESPHome documents this as selecting the multiple-register function for that component’s write, not as providing transaction or rollback semantics. (ESPHome documentation)

A custom ESPHome lambda/script can send one function-16 command containing the contiguous registers. This repository already uses that approach in time_sync.yaml. For this case, the safest sequence would be:

  1. Send registers 1105–1115 together.
  2. Only after that succeeds, write register 1104 to change the active control mode.

That prevents a failed setpoint write from activating the new mode, although it still cannot guarantee rollback if the inverter applies the write and the response is lost. Modbus defines request/reply transactions and function 16, but does not provide a general multi-request commit/rollback mechanism; whether a single function-16 write is internally atomic is device-specific. (Modbus Application Protocol Specification)

For EMHASS, I would expose a single coordinated action/script for changing the mode and its setpoints, rather than relying on clients to update the independent entities in the correct order.

name: "Inverter output power control mode"
address: 1104
value_type: U_WORD
optionsmap:
"Enable AC side control mode": 0
"Enable battery side control mode": 1
"Enable grid side control mode": 2

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: battery_side_control
name: "Battery side control"
address: 1105
value_type: U_WORD
optionsmap:
"Power": 2
"SOC": 3
"Voltage+Current": 4
"Power+SOC": 5

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: ac_power_control_mode
name: "AC power control mode"
address: 1106
value_type: U_WORD
optionsmap:
"3 phase together": 0
"Each phase independent": 1

number:
- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: remote_mode_watchdog_time
name: "Remote mode watchdog time"
min_value: 10
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
step: 1
address: 1101
value_type: U_WORD

Comment thread
sourcery-ai[bot] marked this conversation as resolved.
- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: battery_constant_voltage_mode
name: "Battery constant voltage mode"
min_value: 0
max_value: 1000

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Unsupported battery targets are accepted

When a caller sets the remote battery voltage above the applicable battery limit, the 0–1000 V range permits writing unsupported targets to register 1107; 3P LV controls are bounded to 38–61 V and HV controls to 710 V, so the inverter cannot apply an out-of-range remote setpoint reliably.

Constrain the accepted maximum to the applicable LV or HV battery-voltage limit.

Also at pv_inverter/deye_hybrid_3p_lv.yaml:40.

Prompt for AI agents
In `pv_inverter/packages/deye_hybrid_3p/remote.yaml` at line 68:

**Unsupported battery targets are accepted**

When a caller sets the remote battery voltage above the applicable battery limit, the 0–1000 V range permits writing unsupported targets to register 1107; 3P LV controls are bounded to 38–61 V and HV controls to 710 V, so the inverter cannot apply an out-of-range remote setpoint reliably.

Constrain the accepted maximum to the applicable LV or HV battery-voltage limit.

Also at `pv_inverter/deye_hybrid_3p_lv.yaml:40`.

step: 0.1
unit_of_measurement: "V"
multiply: 10
address: 1107
value_type: U_WORD

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: battery_constant_current_mode
name: "Battery constant current mode"
min_value: 0
max_value: 200
step: 0.1
unit_of_measurement: "A"
multiply: 10
address: 1108
value_type: U_WORD

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: battery_constant_power_mode
name: "Battery constant power mode"
min_value: -120
max_value: 120
step: 0.1
unit_of_measurement: "%"
multiply: 10
address: 1109
value_type: S_WORD

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: battery_constant_soc_mode
name: "Battery constant SOC mode"
min_value: 0
max_value: 100
step: 0.1
unit_of_measurement: "%"
multiply: 10
address: 1110
value_type: U_WORD

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: inv_output_power_ABC
name: "Inverter output power phase ABC"
min_value: -120
max_value: 120
step: 0.1
unit_of_measurement: "%"
multiply: 10
address: 1111
value_type: S_WORD

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: inv_output_power_A
name: "Inverter output power phase A"
min_value: -120
max_value: 120
step: 0.1
unit_of_measurement: "%"
multiply: 10
address: 1112
value_type: S_WORD

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: inv_output_power_B
name: "Inverter output power phase B"
min_value: -120
max_value: 120
step: 0.1
unit_of_measurement: "%"
multiply: 10
address: 1113
value_type: S_WORD

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: inv_output_power_C
name: "Inverter output power phase C"
min_value: -120
max_value: 120
step: 0.1
unit_of_measurement: "%"
multiply: 10
address: 1114
value_type: S_WORD

- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: ${modbus_controller_id}
id: remote_mode_max_solar_power
name: "Remote Mode max solar power"
min_value: 0
max_value: 200
step: 0.1
unit_of_measurement: "%"
multiply: 10
address: 1115
value_type: U_WORD