Skip to content

Conversation

@crogers2287
Copy link

  • Add circuit state properties (circuit_1 - circuit_12) to track on/off status
  • Add enable methods (enable_circuit_1 - enable_circuit_12) for Home Assistant integration
  • Add 12 circuit switch entities with proper outlet device class and icons
  • Enhance switch state handling for protobuf enum values (0=OFF, 1=ON)
  • Uses existing set_circuit_power() method for reliable BLE communication

This enables individual control of all 12 SHP2 circuits through Home Assistant switches, supporting automation and manual control while maintaining compatibility with the EcoFlow app.

image

- Add circuit state properties (circuit_1 - circuit_12) to track on/off status
- Add enable methods (enable_circuit_1 - enable_circuit_12) for Home Assistant integration
- Add 12 circuit switch entities with proper outlet device class and icons
- Enhance switch state handling for protobuf enum values (0=OFF, 1=ON)
- Uses existing set_circuit_power() method for reliable BLE communication

This enables individual control of all 12 SHP2 circuits through Home Assistant
switches, supporting automation and manual control while maintaining compatibility
with the EcoFlow app.
Comment on lines 22 to 25
class CircuitState(IntFieldValue):
"""Circuit state enum (0=OFF, 1=ON)"""
OFF = 0
ON = 1
Copy link
Collaborator

@GnoX GnoX Jul 21, 2025

Choose a reason for hiding this comment

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

I see there are 4 possible values of this enum so it would crash if it got values other than these 2. Add a UNKNOWN enum value here and use CircuitState.from_value in transforms below

Suggested change
class CircuitState(IntFieldValue):
"""Circuit state enum (0=OFF, 1=ON)"""
OFF = 0
ON = 1
class CircuitState(IntFieldValue):
"""Circuit state enum (0=OFF, 1=ON)"""
UNKNOWN = -1
OFF = 0
ON = 1

Copy link
Owner

Choose a reason for hiding this comment

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

@crogers2287 only topic starter can resolve conversations. Please allow @GnoX to make sure the changes are resolving the concern.

circuit_current_12 = CircuitCurrentField(11)

# Circuit state properties (on/off control)
circuit_1 = pb_field(pb_push_set.load_incre_info.hall1_incre_info.ch1_sta.load_sta, CircuitState)
Copy link
Collaborator

@GnoX GnoX Jul 21, 2025

Choose a reason for hiding this comment

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

For switches, these should be booleans, handling enums in switch code is not ideal, handle it right here

Suggested change
circuit_1 = pb_field(pb_push_set.load_incre_info.hall1_incre_info.ch1_sta.load_sta, CircuitState)
circuit_1 = pb_field(
pb_push_set.load_incre_info.hall1_incre_info.ch1_sta.load_sta,
lambda v: CircuitState.from_value(v) is CircuitState.ON
)

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see you only applied my suggestion for the first line, but you have to apply the same change for all other fields.

Copy link
Owner

Choose a reason for hiding this comment

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

@crogers2287 only topic starter can resolve conversations. Please allow @GnoX to make sure the changes are resolving the concern.

Comment on lines 48 to 119
SwitchEntityDescription(
key="circuit_1",
name="Circuit 1",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_2",
name="Circuit 2",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_3",
name="Circuit 3",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_4",
name="Circuit 4",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_5",
name="Circuit 5",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_6",
name="Circuit 6",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_7",
name="Circuit 7",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_8",
name="Circuit 8",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_9",
name="Circuit 9",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_10",
name="Circuit 10",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_11",
name="Circuit 11",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_12",
name="Circuit 12",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
Copy link
Collaborator

@GnoX GnoX Jul 21, 2025

Choose a reason for hiding this comment

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

What I would do here is add a custom switch description instead of creating method for each circuit, something like this:

class EcoflowSwitchEntityDescription[Device: DeviceBase](SwitchEntityDescription):
    enable: Callable[[Device, bool], Awaitable[None]] | None = None

and modify EcoflowSwitchEntity as

class EcoflowSwitchEntity(EcoflowEntity, SwitchEntity):
    def __init__(
        self, device: DeviceBase, entity_description: SwitchEntityDescription
    ) -> None:
        super().__init__(device)

        self._attr_unique_id = f"{device.name}_{entity_description.key}"
        self._prop_name = entity_description.key
        self.entity_description = entity_description
        self._on_off_state = False

        if (
            isinstance(entity_description, EcoflowSwitchEntityDescription)
            and entity_description.enable is not None
        ):
            self._update_state_func = partial(entity_description.enable, device)
        else:
            self._update_state_func = getattr(self._device, f"enable_{self._prop_name}")

        if entity_description.translation_key is None:
            self._attr_translation_key = self.entity_description.key

    async def async_turn_on(self, **kwargs: Any) -> None:
        await self._update_state_func(True)

    async def async_turn_off(self, **kwargs: Any) -> None:
        await self._update_state_func(False)

This will then allow you to remove all enable_* methods from device and set switch descriptions like this

Suggested change
SwitchEntityDescription(
key="circuit_1",
name="Circuit 1",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_2",
name="Circuit 2",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_3",
name="Circuit 3",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_4",
name="Circuit 4",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_5",
name="Circuit 5",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_6",
name="Circuit 6",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_7",
name="Circuit 7",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_8",
name="Circuit 8",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_9",
name="Circuit 9",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_10",
name="Circuit 10",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_11",
name="Circuit 11",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
SwitchEntityDescription(
key="circuit_12",
name="Circuit 12",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
),
*[
EcoflowSwitchEntityDescription[shp2.Device](
key="circuit_{i}",
name=f"Circuit {i:02}",
device_class=SwitchDeviceClass.OUTLET,
icon="mdi:power-socket-us",
enable=lambda device, enabled: device.set_circuit_power(i, enabled),
) for i in range(1, shp2.Device.NUM_OF_CIRCUITS + 1)
],

Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't forget to also apply changes I mentioned above, otherwise this won't work.

Copy link
Owner

Choose a reason for hiding this comment

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

@crogers2287 only topic starter can resolve conversations. Please allow @GnoX to make sure the changes are resolving the concern.

crogers2287 and others added 3 commits July 21, 2025 11:44
Co-authored-by: GnoX <x.jozef.mlaka@gmail.com>
Co-authored-by: GnoX <x.jozef.mlaka@gmail.com>
Co-authored-by: GnoX <x.jozef.mlaka@gmail.com>
@rabits
Copy link
Owner

rabits commented Jul 24, 2025

Oh my! That's a nice change, thank you @crogers2287 for implementation and @GnoX for review) I see from comments you already changed the names to use "0" prefix for the circuits to show them in proper order in UI, so I see only black formatter issues and I will try to test it asap.

@GnoX
Copy link
Collaborator

GnoX commented Jul 24, 2025

I don't seem to be able to unresolve threads - author only applied my suggestions without any changes, but not yet integrated my other remarks.

@crogers2287 If you want, I can take over this.

@crogers2287
Copy link
Author

Please take over, have at it, I had it working but now its failing and I cant get to work again.

@GnoX
Copy link
Collaborator

GnoX commented Jul 25, 2025

I don't actually have permissions to take over, @rabits are you able to change source branch of this PR to GnoX:feature/shp2-circuit-control?
Or @crogers2287 would you be able to cherry pick this commit GnoX@53d7bb6?

Also @rabits It looks like I don't have permission to resolve threads either, lol.

@rabits
Copy link
Owner

rabits commented Jul 25, 2025

@GnoX I don't think it's actually possible to change someone's else repo branch on github... Probably you will need to create a new PR that will include @crogers2287 changes and your ones from your own branch.

@GnoX
Copy link
Collaborator

GnoX commented Jul 25, 2025

@rabits It is possible if you add my repo as a remote, pull branch into local repo and push it into this one, but I guess that's needlessly complicated. I'll create a new PR and we can close this one.

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.

3 participants