Skip to content

Commit 06f9f0b

Browse files
fix: read saving session available_events from configured entity, not rewritten one
The binary_sensor -> event entity rewrite used as a fallback for legacy configs rebound entity_id unconditionally whenever joined_events was empty, so the following available_events read used the rewritten name instead of the configured entity. If the rewritten entity didn't exist (e.g. custom entity names with no '_sessions' substring), available_events came back empty and saving-session auto-join silently stopped working with nothing logged. Fixes #4573
1 parent 3ae6825 commit 06f9f0b

3 files changed

Lines changed: 90 additions & 5 deletions

File tree

apps/predbat/octopus.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2940,11 +2940,17 @@ def fetch_octopus_sessions(self, axle_sessions=None):
29402940
if entity_id:
29412941
state = self.get_arg("octopus_saving_session", False)
29422942
joined_events = self.get_state_wrapper(entity_id=entity_id, attribute="joined_events")
2943-
if not joined_events:
2944-
entity_id = entity_id.replace("binary_sensor.", "event.").replace("_sessions", "_session_events")
2945-
joined_events = self.get_state_wrapper(entity_id=entity_id, attribute="joined_events")
2946-
29472943
available_events = self.get_state_wrapper(entity_id=entity_id, attribute="available_events")
2944+
if not joined_events and not available_events:
2945+
# Legacy binary_sensor entities carry neither attribute - fall back to the
2946+
# newer event entity naming convention, but only adopt it if it actually has data
2947+
fallback_entity_id = entity_id.replace("binary_sensor.", "event.").replace("_sessions", "_session_events")
2948+
fallback_joined_events = self.get_state_wrapper(entity_id=fallback_entity_id, attribute="joined_events")
2949+
fallback_available_events = self.get_state_wrapper(entity_id=fallback_entity_id, attribute="available_events")
2950+
if fallback_joined_events or fallback_available_events:
2951+
entity_id = fallback_entity_id
2952+
joined_events = fallback_joined_events
2953+
available_events = fallback_available_events
29482954

29492955
if available_events and not self.get_arg("octopus_saving_auto_join", True):
29502956
self.log("Octopus: Saving session auto-join is disabled, not joining available events")

apps/predbat/tests/test_saving_session.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,76 @@ def setup_items():
592592
return failed
593593

594594

595+
def test_saving_session_custom_entity_no_rewrite_match(my_predbat):
596+
"""
597+
Test that available_events is read from the configured entity even when its name
598+
does not match the binary_sensor -> event rewrite pattern (no '_sessions' substring),
599+
and no rewritten entity exists at all.
600+
Covers GitHub issue #4573
601+
"""
602+
print("Test saving session with custom entity name that does not match the rewrite pattern (issue #4573)")
603+
ha = my_predbat.ha_interface
604+
failed = False
605+
date_today = datetime.now().strftime("%Y-%m-%d")
606+
tz_offset = int(my_predbat.midnight_utc.tzinfo.utcoffset(my_predbat.midnight_utc).total_seconds() / 3600)
607+
tz_offset = f"{tz_offset:02d}"
608+
609+
# Custom entity name bridging a saving session event. It has no '_sessions' substring
610+
# so the legacy binary_sensor -> event rewrite would point at a non-existent entity if
611+
# it were ever applied. joined_events is empty (nothing joined yet) but available_events
612+
# is populated - this is exactly the state auto-join needs to act on.
613+
session_binary = f"""
614+
state: off
615+
available_events:
616+
- id: 9999
617+
start: '{date_today}T18:00:00+{tz_offset}:00'
618+
end: '{date_today}T19:00:00+{tz_offset}:00'
619+
duration_in_minutes: 60
620+
rewarded_octopoints: null
621+
octopoints_per_kwh: 505
622+
code: EVENT_TEST
623+
joined_events: []
624+
friendly_name: Predbat Octopus Power Down For Predbat
625+
"""
626+
627+
ha.dummy_items.clear()
628+
ha.dummy_items["binary_sensor.predbat_octopus_power_down_for_predbat"] = yaml.safe_load(session_binary)
629+
ha.dummy_items["sensor.octopus_free_session"] = {}
630+
my_predbat.args["octopus_saving_session"] = "binary_sensor.predbat_octopus_power_down_for_predbat"
631+
my_predbat.args["octopus_free_session"] = "sensor.octopus_free_session"
632+
if "octopus_free_url" in my_predbat.args:
633+
del my_predbat.args["octopus_free_url"]
634+
if "octopus_saving_session_join" in my_predbat.args:
635+
del my_predbat.args["octopus_saving_session_join"]
636+
my_predbat.args["octopus_saving_session_octopoints_per_penny"] = 10
637+
# Reset throttle so a join is attempted
638+
my_predbat.octopus_last_joined_try = None
639+
640+
ha.service_store_enable = True
641+
ha.service_store = []
642+
my_predbat.fetch_octopus_sessions()
643+
service_result = ha.get_service_store()
644+
ha.service_store_enable = False
645+
646+
join_calls = [svc for svc in service_result if "join" in svc[0]]
647+
if len(join_calls) != 1:
648+
print(f"ERROR: Expected 1 join call reading available_events from the configured entity, got {len(join_calls)}: {service_result}")
649+
failed = True
650+
elif join_calls[0][1].get("entity_id") != "binary_sensor.predbat_octopus_power_down_for_predbat":
651+
print(f"ERROR: Expected join call to use the configured entity, got {join_calls[0][1]}")
652+
failed = True
653+
else:
654+
print(" PASS: available_events read from the configured entity despite no rewrite match")
655+
656+
if not failed:
657+
print("PASS: Custom entity name (no rewrite match) auto-join test passed")
658+
659+
# Restore default throttle state so we do not leak it to other tests
660+
my_predbat.octopus_last_joined_try = None
661+
662+
return failed
663+
664+
595665
def test_saving_session_default_rate(my_predbat):
596666
"""
597667
Test that saving sessions with no octopoints_per_kwh use the default rate

apps/predbat/unit_test.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,15 @@
6464
from tests.test_solax import run_solax_tests
6565
from tests.test_sigenergy import run_sigenergy_tests
6666
from tests.test_single_debug import run_single_debug
67-
from tests.test_saving_session import test_saving_session, test_saving_session_null_octopoints, test_saving_session_notify_config, test_saving_session_default_rate, test_saving_session_axle_conflict, test_saving_session_auto_join_toggle
67+
from tests.test_saving_session import (
68+
test_saving_session,
69+
test_saving_session_null_octopoints,
70+
test_saving_session_notify_config,
71+
test_saving_session_default_rate,
72+
test_saving_session_axle_conflict,
73+
test_saving_session_auto_join_toggle,
74+
test_saving_session_custom_entity_no_rewrite_match,
75+
)
6876
from tests.test_secrets import run_secrets_tests
6977
from tests.test_ge_cloud import test_ge_cloud
7078
from tests.test_teslemetry import test_teslemetry
@@ -436,6 +444,7 @@ def main():
436444
("saving_session_default_rate", test_saving_session_default_rate, "Saving session default rate injection test", False),
437445
("saving_session_axle_conflict", test_saving_session_axle_conflict, "Saving session Axle conflict avoidance test (issue #4120)", False),
438446
("saving_session_auto_join_toggle", test_saving_session_auto_join_toggle, "Saving session auto-join toggle test (issue #4120)", False),
447+
("saving_session_custom_entity_no_rewrite_match", test_saving_session_custom_entity_no_rewrite_match, "Saving session custom entity no rewrite match test (issue #4573)", False),
439448
("alert_feed", test_alert_feed, "Alert feed tests", False),
440449
("fox_api", run_fox_api_tests, "Fox API tests", False),
441450
("deye_const", run_deye_const_tests, "DEYE constants tests", False),

0 commit comments

Comments
 (0)