diff --git a/tesla_fleet_api/router/energysite.py b/tesla_fleet_api/router/energysite.py index 57b5317..436c9b6 100644 --- a/tesla_fleet_api/router/energysite.py +++ b/tesla_fleet_api/router/energysite.py @@ -33,12 +33,17 @@ def merge_local_into_cloud( ownership test — a fixed owned-key set lets a caller overlay local readings without clobbering cloud values, and lets a local outage fall back to the cloud value instead of an unavailable one. + + A key overlays only when it is owned, present in ``local``, and + ``local[key] is not None``; ``None`` means "not served this tick" and the + cloud value is kept, while any other falsy value (``0``, ``False``, ``""``) + still overlays. """ merged = dict(cloud) if local is None: return merged for key in owned_keys: - if key in local: + if key in local and local[key] is not None: merged[key] = local[key] return merged diff --git a/tests/test_energysite_merge_contract.py b/tests/test_energysite_merge_contract.py index f90d4f3..f80a0fc 100644 --- a/tests/test_energysite_merge_contract.py +++ b/tests/test_energysite_merge_contract.py @@ -40,6 +40,19 @@ def test_falsy_but_present_local_value_is_overlaid(self) -> None: result = merge_local_into_cloud(cloud, local, LOCAL_LIVE_STATUS_KEYS) self.assertEqual(result["grid_power"], 0) + def test_falsy_bool_local_value_is_overlaid(self) -> None: + cloud = {"island_status": "on_grid"} + local = {"island_status": False} + result = merge_local_into_cloud(cloud, local, LOCAL_LIVE_STATUS_KEYS) + self.assertEqual(result["island_status"], False) + + def test_owned_key_present_with_none_keeps_cloud_value(self) -> None: + cloud = {"solar_power": 100, "grid_power": 50} + local = {"solar_power": None, "grid_power": 75} + result = merge_local_into_cloud(cloud, local, LOCAL_LIVE_STATUS_KEYS) + self.assertEqual(result["solar_power"], 100) + self.assertEqual(result["grid_power"], 75) + def test_local_keys_outside_owned_set_are_ignored(self) -> None: cloud = {"solar_power": 100} local = {"solar_power": 200, "some_unowned_field": "surprise"}