-
Notifications
You must be signed in to change notification settings - Fork 30
Add individual circuit control switches for EcoFlow SHP2 #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- 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.
| class CircuitState(IntFieldValue): | ||
| """Circuit state enum (0=OFF, 1=ON)""" | ||
| OFF = 0 | ||
| ON = 1 |
There was a problem hiding this comment.
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
| 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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
| 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 | |
| ) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
custom_components/ef_ble/switch.py
Outdated
| 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", | ||
| ), |
There was a problem hiding this comment.
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 = Noneand 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
| 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) | |
| ], |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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>
|
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. |
|
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. |
|
Please take over, have at it, I had it working but now its failing and I cant get to work again. |
|
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? Also @rabits It looks like I don't have permission to resolve threads either, lol. |
|
@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. |
|
@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. |
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.