Skip to content

Commit 44869d2

Browse files
authored
Fix older firmware in battery 'zero' mode treated as standby due to missing permissions (#587)
* Fix older firmware in battery 'zero' mode treated as standby due to missing permissions * Add extra tests
1 parent 35c9c56 commit 44869d2

File tree

5 files changed

+54
-17
lines changed

5 files changed

+54
-17
lines changed

homewizard_energy/models.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -701,15 +701,17 @@ class Permissions(StrEnum):
701701
"deserialize": lambda x: Batteries.Mode.__members__.get(x.upper(), None)
702702
},
703703
)
704-
permissions: list[Permissions] = field(
705-
default_factory=list,
704+
permissions: list[Permissions] | None = field(
705+
default=None,
706706
metadata={
707707
"deserialize": lambda lst: [
708708
perm
709709
for item in lst
710710
if (perm := Batteries.Permissions.__members__.get(item.upper(), None))
711711
is not None
712712
]
713+
if lst is not None
714+
else None
713715
},
714716
)
715717
power_w: float = field()
@@ -720,21 +722,34 @@ class Permissions(StrEnum):
720722

721723
@classmethod
722724
def __post_deserialize__(cls, obj: Batteries) -> Batteries:
723-
"""Set correct mode based on permissions after deserialization."""
725+
"""Set correct mode based on permissions after deserialization.
726+
If permissions is missing and mode is ZERO, keep mode as ZERO for backwards compatibility.
727+
If permissions is present (even if empty), apply the mapping logic.
728+
"""
724729
# Only adjust if mode is ZERO
725-
if obj.mode == cls.Mode.ZERO:
726-
perms = set(obj.permissions)
727-
if perms == {cls.Permissions.CHARGE_ALLOWED}:
728-
obj.mode = cls.Mode.ZERO_CHARGE_ONLY
729-
elif perms == {cls.Permissions.DISCHARGE_ALLOWED}:
730-
obj.mode = cls.Mode.ZERO_DISCHARGE_ONLY
731-
elif perms == {
732-
cls.Permissions.CHARGE_ALLOWED,
733-
cls.Permissions.DISCHARGE_ALLOWED,
734-
}:
735-
obj.mode = cls.Mode.ZERO
736-
elif perms == set():
737-
obj.mode = cls.Mode.STANDBY
730+
if obj.mode != cls.Mode.ZERO:
731+
return obj
732+
733+
# Detect if 'permissions' was present in the original data
734+
# If using mashumaro, the original dict is not available here, so we infer:
735+
# If permissions is None, treat as 'not provided'. If it's an empty list, treat as 'provided but empty'.
736+
if obj.permissions is None:
737+
# Permissions not provided, keep mode as ZERO (backwards compatibility)
738+
return obj
739+
740+
perms = set(obj.permissions)
741+
if perms == {cls.Permissions.CHARGE_ALLOWED}:
742+
obj.mode = cls.Mode.ZERO_CHARGE_ONLY
743+
elif perms == {cls.Permissions.DISCHARGE_ALLOWED}:
744+
obj.mode = cls.Mode.ZERO_DISCHARGE_ONLY
745+
elif perms == {
746+
cls.Permissions.CHARGE_ALLOWED,
747+
cls.Permissions.DISCHARGE_ALLOWED,
748+
}:
749+
obj.mode = cls.Mode.ZERO
750+
elif perms == set():
751+
obj.mode = cls.Mode.STANDBY
752+
738753
return obj
739754

740755

tests/v2/__snapshots__/test_v2_batteries.ambr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@
66
Batteries(mode=<Mode.ZERO: 'zero'>, permissions=[<Permissions.CHARGE_ALLOWED: 'charge_allowed'>, <Permissions.DISCHARGE_ALLOWED: 'discharge_allowed'>], power_w=-404.0, target_power_w=-400.0, max_consumption_w=1600.0, max_production_w=800.0, battery_count=2)
77
# ---
88
# name: test_batteries[HWE-P1-fixtures0]
9+
Batteries(mode=<Mode.ZERO: 'zero'>, permissions=None, power_w=-404.0, target_power_w=-400.0, max_consumption_w=1600.0, max_production_w=800.0, battery_count=None)
10+
# ---
11+
# name: test_batteries[HWE-P1-fixtures0].1
912
Batteries(mode=<Mode.ZERO: 'zero'>, permissions=[<Permissions.CHARGE_ALLOWED: 'charge_allowed'>, <Permissions.DISCHARGE_ALLOWED: 'discharge_allowed'>], power_w=-404.0, target_power_w=-400.0, max_consumption_w=1600.0, max_production_w=800.0, battery_count=2)
1013
# ---
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"mode": "zero",
3+
"power_w": -404,
4+
"target_power_w": -400,
5+
"max_consumption_w": 1600,
6+
"max_production_w": 800
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"mode": "zero",
3+
"permissions": ["charge_allowed", "discharge_allowed"],
4+
"battery_count": 2,
5+
"power_w": -404,
6+
"target_power_w": -400,
7+
"max_consumption_w": 1600,
8+
"max_production_w": 800
9+
}

tests/v2/test_v2_batteries.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
@pytest.mark.parametrize(
1616
("model", "fixtures"),
1717
[
18-
("HWE-P1", ["batteries"]),
18+
("HWE-P1", ["batteries_2_1_0", "batteries_2_2_0"]),
1919
("HWE-KWH1", ["batteries"]),
2020
("HWE-KWH3", ["batteries"]),
2121
],
@@ -109,6 +109,9 @@ def test_batteries_update_modes_and_permissions(
109109
(Batteries.Mode.ZERO, [], Batteries.Mode.STANDBY),
110110
(Batteries.Mode.STANDBY, [], Batteries.Mode.STANDBY),
111111
(Batteries.Mode.TO_FULL, [], Batteries.Mode.TO_FULL),
112+
(Batteries.Mode.ZERO, None, Batteries.Mode.ZERO),
113+
(Batteries.Mode.STANDBY, None, Batteries.Mode.STANDBY),
114+
(Batteries.Mode.TO_FULL, None, Batteries.Mode.TO_FULL),
112115
],
113116
)
114117
def test_set_mode_based_on_permissions(mode, permissions, expected_mode):

0 commit comments

Comments
 (0)