From cc1edeb5c08bd683e33d26744d9bc90a14a9f12f Mon Sep 17 00:00:00 2001 From: Mark Gascoyne Date: Fri, 19 Jun 2026 08:16:32 +0100 Subject: [PATCH 1/3] feat(lattice): Fox read-only producer (device map + sensor inventory) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FoxAPI.lattice_fragment publishes its cloud devices (real deviceSN identity + soc/power sensor inventory) on a cloud access path. Read-only — no control. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/predbat/fox.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/predbat/fox.py b/apps/predbat/fox.py index 57878aaa8..ac427034a 100644 --- a/apps/predbat/fox.py +++ b/apps/predbat/fox.py @@ -1618,6 +1618,14 @@ async def publish_data(self): self.dashboard_item(entity_id, state=state, attributes=attributes, app="fox") + def lattice_fragment(self): + """Read-only Lattice fragment: FoxESS cloud devices (topology + sensor inventory).""" + from lattice import device_fragment + + serials = [d.get("deviceSN") for d in (getattr(self, "device_list", None) or []) if isinstance(d, dict) and d.get("deviceSN")] + devices = [{"serial": str(s), "device_type": "fox", "sensors": [{"capability": "soc", "unit": "%"}, {"capability": "battery_power", "unit": "W"}]} for s in serials] + return device_fragment(devices, provider="fox-cloud", name="FoxESS Cloud", transport="https", preference=1, locality="cloud") + async def write_setting_from_event(self, entity_id, value, is_number=False): """ Handle write events From 4478734f7638368b2130ff7757f13c5a5499f757 Mon Sep 17 00:00:00 2001 From: Mark Gascoyne Date: Fri, 19 Jun 2026 08:16:34 +0100 Subject: [PATCH 2/3] feat(lattice): Solax read-only producer (device map + sensor inventory) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SolaxAPI.lattice_fragment publishes its cloud plants (topology + soc sensor inventory) on a cloud access path. Read-only — no control. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/predbat/solax.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/predbat/solax.py b/apps/predbat/solax.py index fd349467f..074dc81b5 100644 --- a/apps/predbat/solax.py +++ b/apps/predbat/solax.py @@ -453,6 +453,13 @@ async def switch_event(self, entity_id, service): if "_battery_schedule_" in entity_id: await self.write_battery_schedule_event(entity_id, service) + def lattice_fragment(self): + """Read-only Lattice fragment: Solax cloud plants (topology + sensor inventory).""" + from lattice import device_fragment + + devices = [{"serial": str(p), "device_type": "solax", "sensors": [{"capability": "soc", "unit": "%"}]} for p in getattr(self, "plant_list", []) or []] + return device_fragment(devices, provider="solax-cloud", name="Solax Cloud", transport="https", preference=1, locality="cloud") + async def write_battery_schedule_event(self, entity_id, value): """ Write a battery schedule based on an event From 3851e515c01ae80b45acd807c0f8f03f96faa048 Mon Sep 17 00:00:00 2001 From: Mark Gascoyne Date: Fri, 19 Jun 2026 08:16:35 +0100 Subject: [PATCH 3/3] feat(lattice): Solis read-only producer (device map + sensor inventory) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SolisAPI.lattice_fragment publishes its cloud inverters (topology + soc/power sensor inventory) on a cloud access path. Read-only — no control. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/predbat/solis.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/predbat/solis.py b/apps/predbat/solis.py index c792490b2..69a117b3d 100644 --- a/apps/predbat/solis.py +++ b/apps/predbat/solis.py @@ -578,6 +578,13 @@ async def decode_time_windows_v2(self, inverter_sn): self.log("Solis API: Decoded time windows v2 for {}: {}".format(inverter_sn, result)) # Debug log return result + def lattice_fragment(self): + """Read-only Lattice fragment: Solis cloud inverters (topology + sensor inventory).""" + from lattice import device_fragment + + devices = [{"serial": str(s), "device_type": "solis", "sensors": [{"capability": "soc", "unit": "%"}, {"capability": "battery_power", "unit": "W"}]} for s in getattr(self, "inverter_sn", []) or []] + return device_fragment(devices, provider="solis-cloud", name="Solis Cloud", transport="https", preference=1, locality="cloud") + async def reset_charge_windows_if_needed(self, inverter_sn): """ Predbat only uses 1 slot so disable the others to avoid conflicts.