Skip to content

Commit ef86b8c

Browse files
committed
Add support for River mini (non-wireless version).
1 parent ff78f43 commit ef86b8c

File tree

5 files changed

+151
-57
lines changed

5 files changed

+151
-57
lines changed

custom_components/ecoflow/binary_sensor.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from . import (DOMAIN, EcoFlowBaseEntity, EcoFlowData, EcoFlowDevice,
1111
EcoFlowEntity, EcoFlowExtraDevice, EcoFlowMainDevice)
12-
from .ecoflow import is_delta, is_river
12+
from .ecoflow import is_delta, is_river, is_river_mini
1313

1414

1515
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback):
@@ -35,6 +35,10 @@ def device_added(device: EcoFlowDevice):
3535
entities.extend([
3636
InputEntity(device, device.inverter, "in_type", "Input"),
3737
])
38+
elif is_river_mini(device.product):
39+
entities.extend([
40+
InputEntity(device, device.inverter, "in_type", "Input"),
41+
])
3842
elif type(device) is EcoFlowExtraDevice:
3943
entities.extend([
4044
ExtraErrorEntity(device, device.bms,

custom_components/ecoflow/ecoflow/receive.py

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from reactivex import Observable, Observer
88

9-
from . import calcCrc8, calcCrc16, is_delta, is_river
9+
from . import calcCrc8, calcCrc16, is_delta, is_river, is_river_mini
1010

1111

1212
class Serial(TypedDict):
@@ -69,6 +69,8 @@ def _to_float(d: bytes) -> float:
6969
def _to_int(d: bytes):
7070
return int.from_bytes(d, "little")
7171

72+
def _to_hex_debug(d: bytes):
73+
return " ".join("0x{:02x}".format(x) for x in d)
7274

7375
def _to_int_ex(div: int = 1):
7476
def f(d: bytes):
@@ -299,8 +301,8 @@ def parse_inverter(d: bytes, product: int):
299301
return parse_inverter_delta(d)
300302
if is_river(product):
301303
return parse_inverter_river(d)
302-
# if is_river_mini(product):
303-
# return parse_pd_river_mini(d)
304+
if is_river_mini(product):
305+
return parse_inverter_river_mini(d)
304306
return {}
305307

306308

@@ -364,6 +366,43 @@ def parse_inverter_river(d: bytes):
364366
("fan_config", 1, _to_int),
365367
])
366368

369+
def parse_inverter_river_mini(d: bytes):
370+
return _parse_dict(d, [
371+
("ac_error", 4, _to_int),
372+
("ac_version", 4, _to_ver_reversed),
373+
("in_type", 1, _to_int),
374+
("in_power", 2, _to_int),
375+
("ac_out_power", 2, _to_int),
376+
("ac_type", 1, _to_int),
377+
("ac_out_voltage", 4, _to_int_ex(div=1000)),
378+
("ac_out_current", 4, _to_int_ex(div=1000)),
379+
("ac_out_freq", 1, _to_int),
380+
("ac_in_voltage", 4, _to_int_ex(div=1000)),
381+
("ac_in_current", 4, _to_int_ex(div=1000)),
382+
("ac_in_freq", 1, _to_int),
383+
("ac_out_temp", 1, _to_int),
384+
("dc_in_voltage", 4, _to_int_ex(div=1000)),
385+
("dc_in_current", 4, _to_int_ex(div=1000)),
386+
("ac_in_temp", 1, _to_int),
387+
("fan_state", 1, _to_int),
388+
("ac_out_state", 1, _to_int),
389+
("ac_out_xboost", 1, _to_int),
390+
("ac_out_voltage_config", 4, _to_int_ex(div=1000)),
391+
("ac_out_freq_config", 1, _to_int),
392+
("ac_in_slow", 1, _to_int),
393+
("battery_main_level", 1, _to_int),
394+
("battery_main_voltage", 4, _to_int_ex(div=1000)),
395+
("battery_current", 4, _to_int),
396+
("battery_main_temp", 1, _to_int),
397+
("_open_bms_idx", 1, _to_int),
398+
("battery_capacity_remain", 4, _to_int),
399+
("battery_capacity_full", 4, _to_int),
400+
("battery_cycles", 4, _to_int),
401+
("battery_level_max", 1, _to_int),
402+
("battery_main_level_f32", 4, _to_float),
403+
("ac_out_timeout", 2, _to_int),
404+
])
405+
367406

368407
def parse_lcd_timeout(d: bytes):
369408
return int.from_bytes(d[1:3], "little")
@@ -412,8 +451,8 @@ def parse_pd(d: bytes, product: int):
412451
return parse_pd_delta(d)
413452
if is_river(product):
414453
return parse_pd_river(d)
415-
# if is_river_mini(product):
416-
# return parse_pd_river_mini(d)
454+
if is_river_mini(product):
455+
return parse_pd_river_mini(d)
417456
return {}
418457

419458

@@ -498,49 +537,47 @@ def parse_pd_river(d: bytes):
498537
])
499538

500539

501-
# def parse_pd_river_mini(d: bytes):
502-
# return _parse_dict(d, [
503-
# ("model", 1, _to_int),
504-
# ("pd_error", 4, _to_int),
505-
# ("pd_version", 4, _to_ver_reversed),
506-
# ("wifi_version", 4, _to_ver_reversed),
507-
# ("wifi_autorecovery", 1,),
508-
# ("soc_sum", 1, _to_int),
509-
# ("watts_out_sum", 2, _to_int),
510-
# ("watts_in_sum", 2, _to_int),
511-
# ("remain_time", 4, _to_int),
512-
# ("beep", 1, _to_int),
513-
# ("dc_out", 1, _to_int),
514-
# ("usb1_watts", 1, _to_int),
515-
# ("usb2_watts", 1, _to_int),
516-
# ("usbqc1_watts", 1, _to_int),
517-
# ("usbqc2_watts", 1, _to_int),
518-
# ("typec1_watts", 1, _to_int),
519-
# ("typec2_watts", 1, _to_int),
520-
# ("typec1_temp", 1, _to_int),
521-
# ("typec2_temp", 1, _to_int),
522-
# ("dc_out_watts", 1, _to_int),
523-
# ("car_out_temp", 1, _to_int),
524-
# ("standby_timeout", 2, _to_int),
525-
# ("lcd_sec", 2, _to_int),
526-
# ("lcd_brightness", 1, _to_int),
527-
# ("chg_power_dc", 4, _to_int),
528-
# ("chg_power_mppt", 4, _to_int),
529-
# ("chg_power_ac", 4, _to_int),
530-
# ("dsg_power_dc", 4, _to_int),
531-
# ("dsg_power_ac", 4, _to_int),
532-
# ("usb_used_time", 4, _to_int),
533-
# ("usbqc_used_time", 4, _to_int),
534-
# ("typec_used_time", 4, _to_int),
535-
# ("dc_out_used_time", 4, _to_int),
536-
# ("ac_out_used_time", 4, _to_int),
537-
# ("dc_in_used_time", 4, _to_int),
538-
# ("mppt_used_time", 4, _to_int),
539-
# (None, 5, None),
540-
# ("sys_chg_flag", 1, _to_int),
541-
# ("wifi_rssi", 1, _to_int),
542-
# ("wifi_watts", 1, _to_int),
543-
# ])
540+
def parse_pd_river_mini(d: bytes):
541+
return _parse_dict(d, [
542+
("model", 1, _to_int),
543+
("pd_error", 4, _to_int),
544+
("pd_version", 4, _to_ver_reversed),
545+
("wifi_version", 4, _to_ver_reversed),
546+
("wifi_autorecovery", 1, _to_int),
547+
("battery_level", 1, _to_int),
548+
("out_power", 2, _to_int),
549+
("in_power", 2, _to_int),
550+
("remain_display", 4, _to_timedelta_min),
551+
("beep", 1, _to_int),
552+
("usb_out1_state", 1, _to_int),
553+
("usb_out1_power", 1, _to_int),
554+
("usb2_watts", 1, _to_int),
555+
("usbqc1_watts", 1, _to_int),
556+
("usbqc2_watts", 1, _to_int),
557+
("typec1_watts", 1, _to_int),
558+
("typec2_watts", 1, _to_int),
559+
("typec1_temp", 1, _to_int),
560+
("typec2_temp", 1, _to_int),
561+
("car_out_state", 1, _to_int),
562+
("car_out_power", 1, _to_int),
563+
("car_out_temp", 1, _to_int),
564+
("standby_timeout", 1, _to_int),
565+
("unknown_1", 1, _to_hex_debug),
566+
("lcd_timeout", 2, _to_int),
567+
("lcd_brightness", 1, _to_int),
568+
("car_in_energy", 4, _to_int),
569+
("mppt_in_energy", 4, _to_int),
570+
("ac_in_energy", 4, _to_int),
571+
("dc_out_energy", 4, _to_int),
572+
("ac_out_energy", 4, _to_int),
573+
("usb_time", 4, _to_timedelta_sec),
574+
("unknown_2", 8, _to_hex_debug),
575+
("car_out_time", 4, _to_timedelta_sec),
576+
("ac_out_time", 4, _to_timedelta_sec),
577+
("car_in_time", 4, _to_timedelta_sec),
578+
("mppt_time", 4, _to_timedelta_sec),
579+
("unknown_3", 30, _to_hex_debug),
580+
])
544581

545582

546583
def parse_serial(d: bytes) -> Serial:

custom_components/ecoflow/select.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99

1010
from . import (DOMAIN, EcoFlowConfigEntity, EcoFlowData, EcoFlowDevice,
1111
EcoFlowEntity, EcoFlowMainDevice)
12-
from .ecoflow import is_delta, is_river, send
12+
from .ecoflow import is_delta, is_river, is_river_mini, send
1313

1414
_AC_OPTIONS = {
1515
"Never": 0,
16+
"30min": 30,
1617
"2hour": 120,
1718
"4hour": 240,
1819
"6hour": 360,
@@ -80,6 +81,11 @@ def device_added(device: EcoFlowDevice):
8081
DcInTypeEntity(device),
8182
LcdTimeoutPollEntity(device, "lcd_timeout", "Screen timeout"),
8283
])
84+
if is_river_mini(device.product):
85+
entities.extend([
86+
LcdTimeoutPushEntity(device, device.pd,
87+
"lcd_timeout", "Screen timeout"),
88+
])
8389
async_add_entities(entities)
8490

8591
entry.async_on_unload(data.device_added.subscribe(device_added).dispose)

custom_components/ecoflow/sensor.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from . import (DOMAIN, EcoFlowData, EcoFlowDevice, EcoFlowEntity,
2020
EcoFlowExtraDevice, EcoFlowMainDevice)
21-
from .ecoflow import is_delta, is_delta_mini, is_delta_pro, is_river
21+
from .ecoflow import is_delta, is_delta_mini, is_delta_pro, is_river, is_river_mini
2222

2323

2424
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback):
@@ -58,11 +58,14 @@ def device_added(device: EcoFlowDevice):
5858
"ac_consumption", "AC output + loss", real=True),
5959
WattsEntity(device, device.inverter, "ac_out_power",
6060
"AC output", real=False),
61-
WattsEntity(device, device.pd, "usb_out1_power",
62-
"USB-A left output"),
63-
WattsEntity(device, device.pd, "usb_out2_power",
64-
"USB-A right output"),
6561
])
62+
if not is_river_mini(device.product):
63+
entities.extend([
64+
WattsEntity(device, device.pd, "usb_out1_power",
65+
"USB-A left output"),
66+
WattsEntity(device, device.pd, "usb_out2_power",
67+
"USB-A right output"),
68+
])
6669
if is_delta(device.product):
6770
entities.extend([
6871
CurrentEntity(device, device.mppt, "dc_in_current",
@@ -154,6 +157,31 @@ def device_added(device: EcoFlowDevice):
154157
WattsEntity(device, device.pd, "typec_out1_power",
155158
"USB-C output"),
156159
])
160+
if is_river_mini(device.product):
161+
entities.extend([
162+
CurrentEntity(device, device.inverter, "dc_in_current",
163+
"DC input current"),
164+
CyclesEntity(device, device.inverter, "battery_cycles",
165+
"Battery cycles"),
166+
LevelEntity(device, device.pd, "battery_level",
167+
"Total battery level"),
168+
TempEntity(device, device.inverter, "ac_in_temp",
169+
"AC input temperature"),
170+
TempEntity(device, device.inverter, "ac_out_temp",
171+
"AC output temperature"),
172+
TempEntity(device, device.inverter, "battery_main_temp",
173+
"Battery temperature"),
174+
TempEntity(device, device.pd, "car_out_temp",
175+
"Car output temperature"),
176+
VoltageEntity(device, device.inverter, "dc_in_voltage",
177+
"Car input voltage"),
178+
WattsEntity(device, device.pd, "usb_out1_power",
179+
"USB-A output"),
180+
WattsEntity(device, device.pd,
181+
"car_out_power", "Car output"),
182+
VoltageEntity(device, device.inverter, "battery_main_voltage",
183+
"Battery voltage"),
184+
])
157185
elif type(device) is EcoFlowExtraDevice:
158186
if is_delta(device.product):
159187
entities.extend([

custom_components/ecoflow/switch.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ def device_added(device: EcoFlowDevice):
2020
entities.extend([
2121
AcEntity(device, device.inverter, "ac_out_state", "AC output"),
2222
BeepEntity(device, device.pd, "beep", "Beep"),
23+
XBoostEntity(device, device.inverter,
24+
"ac_out_xboost", "AC X-Boost"),
2325
])
2426
if is_delta(device.product):
2527
entities.extend([
@@ -40,8 +42,17 @@ def device_added(device: EcoFlowDevice):
4042
])
4143
if not is_river_mini(device.product):
4244
entities.extend([
43-
XBoostEntity(device, device.inverter,
44-
"ac_out_xboost", "AC X-Boost"),
45+
DcEntity(device, device.mppt,
46+
"car_out_state", "DC output"),
47+
FanAutoEntity(device, device.inverter,
48+
"fan_config", "Auto fan speed"),
49+
])
50+
if is_river_mini(device.product):
51+
entities.extend([
52+
DcEntity(device, device.pd,
53+
"car_out_state", "DC output"),
54+
UsbEntity(device, device.pd,
55+
"usb_out1_state", "USB output"),
4556
])
4657
elif type(device) is EcoFlowExtraDevice:
4758
if device.product == 5: # RIVER Max
@@ -143,6 +154,14 @@ async def async_turn_off(self, **kwargs: Any):
143154
async def async_turn_on(self, **kwargs: Any):
144155
self._device.send(send.set_dc_out(self._device.product, True))
145156

157+
class UsbEntity(SimpleEntity):
158+
_attr_device_class = SwitchDeviceClass.OUTLET
159+
160+
async def async_turn_off(self, **kwargs: Any):
161+
self._device.send(send.set_usb(False))
162+
163+
async def async_turn_on(self, **kwargs: Any):
164+
self._device.send(send.set_usb(True))
146165

147166
class FanAutoEntity(SimpleEntity):
148167
_attr_entity_category = EntityCategory.CONFIG

0 commit comments

Comments
 (0)