Skip to content

Commit

Permalink
Multiple devices to work with multiple BOWs
Browse files Browse the repository at this point in the history
  • Loading branch information
cryptk committed May 15, 2023
1 parent e548c67 commit 85cb49d
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 18 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Allow us to use provided diagnostic data to reproduce issues
custom_components/omnilogic_local/test_diagnostic_data.py

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
27 changes: 21 additions & 6 deletions custom_components/omnilogic_local/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr

from .const import DOMAIN, KEY_COORDINATOR, UNIQUE_ID
from .const import DOMAIN, KEY_COORDINATOR, UNIQUE_ID, KEY_MSP_BACKYARD, KEY_MSP_BOW, BACKYARD_SYSTEM_ID
from .coordinator import OmniLogicCoordinator
from .utils import get_entities_of_omni_type
import logging

PLATFORMS: list[Platform] = [
Platform.BINARY_SENSOR,
Expand All @@ -28,6 +30,7 @@
Platform.WATER_HEATER,
]

_LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up OmniLogic Local from a config entry."""
Expand All @@ -48,20 +51,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await coordinator.async_config_entry_first_refresh()

device_registry = dr.async_get(hass)

#Create a device for the Omni Backyard
backyard = get_entities_of_omni_type(coordinator.data, KEY_MSP_BACKYARD)[BACKYARD_SYSTEM_ID]
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, UNIQUE_ID)},
identifiers={(KEY_MSP_BACKYARD, BACKYARD_SYSTEM_ID)},
manufacturer="Hayward",
# TODO: Figure out how to manage device naming, the API does not return a name
name="omnilogic",
suggested_area="Back Yard",
name=f"{entry.data[CONF_NAME]}_{backyard['metadata']['name']}",
)

# Create a device for each Body of Water
for system_id, bow in get_entities_of_omni_type(coordinator.data, KEY_MSP_BOW).items():
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(KEY_MSP_BOW, system_id)},
manufacturer="Hayward",
suggested_area="Back Yard",
# TODO: Figure out how to manage device naming, the API does not return a name
name=f"{entry.data[CONF_NAME]}_{bow['metadata']['name']}",
)

# Store them for use later
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {
KEY_COORDINATOR: coordinator,
# KEY_DEVICE_REGISTRY: device_registry
# KEY_OMNI_API: omni_api,
}

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
Expand Down
7 changes: 7 additions & 0 deletions custom_components/omnilogic_local/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
KEY_TELEMETRY_BOW = "BodyOfWater"
KEY_TELEMETRY_SYSTEM_ID = "@systemId"

OMNI_BOW_TYPE_POOL = "BOW_POOL"
OMNI_BOW_TYPE_SPA = "BOW_SPA"
OMNI_BOW_TYPES = [
OMNI_BOW_TYPE_POOL,
OMNI_BOW_TYPE_SPA
]

OMNI_DEVICE_TYPES_FILTER = [
"Filter",
]
Expand Down
17 changes: 11 additions & 6 deletions custom_components/omnilogic_local/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
KEY_MSP_SYSTEM_ID,
OMNI_DEVICE_TYPES,
OMNI_TO_HASS_TYPES,
OMNI_BOW_TYPES,
)
from .utils import get_telemetry_by_systemid, one_or_many
import json

# Import diagnostic data to reproduce issues
from .test_diagnostic_data import TEST_DIAGNOSTIC_DATA

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -81,7 +86,7 @@ def build_entity_index(data: dict[str, str]) -> dict[int, dict[str, str]]:

for item in one_or_many(tier):
bow_id = (
int(item[KEY_MSP_SYSTEM_ID]) if item.get("Type") == "BOW_POOL" else None
int(item[KEY_MSP_SYSTEM_ID]) if item.get("Type") in OMNI_BOW_TYPES else None
)
for omni_entity_type, entity_data in item.items():
if omni_entity_type not in OMNI_DEVICE_TYPES:
Expand Down Expand Up @@ -125,11 +130,6 @@ async def _async_update_data(self):
# Note: asyncio.TimeoutError and aiohttp.ClientError are already
# handled by the data update coordinator.
async with async_timeout.timeout(10):
# Grab active context variables to limit data required to be fetched from API
# Note: using context is not required if there is no need or ability to limit
# data retrieved from API.
# listening_idx = set(self.async_contexts())

# Initially we only pulled the msp_config at integration startup as it rarely changes
# Then we learned that heater set points (which can change often enough) are stored
# within the MSP Config, not the telemetry, so now we pull the msp_config on every update
Expand All @@ -143,6 +143,11 @@ async def _async_update_data(self):
await self.omni_api.async_get_telemetry()
)

# The below is used if we have a test_diagnostic_data.py populated with a diagnostic data file to reproduce an issue
# test_data = json.loads(TEST_DIAGNOSTIC_DATA)
# self.msp_config = test_data['data']['msp_config']
# self.telemetry = test_data['data']['telemetry']

omnilogic_data = self.msp_config | self.telemetry

entity_index = build_entity_index(omnilogic_data)
Expand Down
10 changes: 7 additions & 3 deletions custom_components/omnilogic_local/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import BACKYARD_SYSTEM_ID, DOMAIN, MANUFACTURER, UNIQUE_ID
from .const import BACKYARD_SYSTEM_ID, DOMAIN, MANUFACTURER, UNIQUE_ID, KEY_MSP_BOW, KEY_MSP_BACKYARD

if TYPE_CHECKING:
from .coordinator import OmniLogicCoordinator
Expand Down Expand Up @@ -90,10 +90,14 @@ def available(self) -> bool:
@property
def device_info(self) -> DeviceInfo:
"""Return the device info."""
# If we have a BOW ID, then we associate with that BOWs device, if not, we associate with the Backyard
if self.bow_id is not None:
identifiers = {(KEY_MSP_BOW, self.bow_id)}
else:
identifiers = {(KEY_MSP_BACKYARD, BACKYARD_SYSTEM_ID)}
return DeviceInfo(
identifiers={(DOMAIN, UNIQUE_ID)},
identifiers=identifiers,
manufacturer=MANUFACTURER,
# model=self.model,
)

@property
Expand Down
4 changes: 1 addition & 3 deletions custom_components/omnilogic_local/water_heater.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@ def extra_state_attributes(self) -> Mapping[str, Any] | None:
f"{prefix}_enabled": self.get_config(system_id)["Enabled"],
f"{prefix}_system_id": system_id,
f"{prefix}_bow_id": heater_equipment["metadata"]["bow_id"],
f"{prefix}_supports_cooling": self.get_config(system_id)[
"SupportsCooling"
],
f"{prefix}_supports_cooling": self.get_config(system_id).get("SupportsCooling", "no"),
f"{prefix}_state": STATE_ON
if self.get_telemetry(system_id)["@heaterState"] == "1"
else STATE_OFF,
Expand Down

0 comments on commit 85cb49d

Please sign in to comment.