1 """Support for Rain Bird Irrigation system LNK WiFi Module."""
3 from __future__
import annotations
9 from pyrainbird.async_client
import AsyncRainbirdClient, AsyncRainbirdController
10 from pyrainbird.exceptions
import RainbirdApiException, RainbirdAuthException
17 EVENT_HOMEASSISTANT_CLOSE,
25 from .const
import CONF_SERIAL_NUMBER
26 from .coordinator
import (
27 RainbirdScheduleUpdateCoordinator,
28 RainbirdUpdateCoordinator,
29 async_create_clientsession,
31 from .types
import RainbirdConfigEntry, RainbirdData
33 _LOGGER = logging.getLogger(__name__)
36 Platform.BINARY_SENSOR,
50 clientsession: aiohttp.ClientSession,
52 """Register cleanup hooks for the clientsession."""
54 async
def _async_close_websession(*_: Any) ->
None:
55 """Close websession."""
56 await clientsession.close()
58 unsub = hass.bus.async_listen_once(
59 EVENT_HOMEASSISTANT_CLOSE, _async_close_websession
61 entry.async_on_unload(unsub)
62 entry.async_on_unload(_async_close_websession)
66 """Set up the config entry for Rain Bird."""
68 hass.data.setdefault(DOMAIN, {})
73 controller = AsyncRainbirdController(
76 entry.data[CONF_HOST],
77 entry.data[CONF_PASSWORD],
83 if mac_address := entry.data.get(CONF_MAC):
89 str(entry.data[CONF_SERIAL_NUMBER]),
96 str(entry.data[CONF_SERIAL_NUMBER]),
100 model_info = await controller.get_model_and_version()
101 except RainbirdAuthException
as err:
102 raise ConfigEntryAuthFailed
from err
103 except RainbirdApiException
as err:
104 raise ConfigEntryNotReady
from err
112 controller=controller,
113 unique_id=entry.unique_id,
114 model_info=model_info,
118 name=f
"{entry.title} Schedule",
119 controller=controller,
122 await data.coordinator.async_config_entry_first_refresh()
124 entry.runtime_data = data
125 await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
131 hass: HomeAssistant, controller: AsyncRainbirdController, entry: ConfigEntry
133 """Update the config entry with a unique id based on the mac address."""
134 _LOGGER.debug(
"Checking for migration of config entry (%s)", entry.unique_id)
135 if not (mac_address := entry.data.get(CONF_MAC)):
137 wifi_params = await controller.get_wifi_params()
138 except RainbirdApiException
as err:
139 _LOGGER.warning(
"Unable to fix missing unique id: %s", err)
142 if (mac_address := wifi_params.mac_address)
is None:
143 _LOGGER.warning(
"Unable to fix missing unique id (mac address was None)")
147 if entry.unique_id == new_unique_id
and CONF_MAC
in entry.data:
148 _LOGGER.debug(
"Config entry already in correct state")
151 entries = hass.config_entries.async_entries(DOMAIN)
152 for existing_entry
in entries:
153 if existing_entry.unique_id == new_unique_id:
155 "Unable to fix missing unique id (already exists); Removing duplicate entry"
157 hass.async_create_background_task(
158 hass.config_entries.async_remove(entry.entry_id),
159 "Remove rainbird config entry",
163 _LOGGER.debug(
"Updating unique id to %s", new_unique_id)
164 hass.config_entries.async_update_entry(
166 unique_id=new_unique_id,
169 CONF_MAC: mac_address,
177 entity_registry: er.EntityRegistry,
178 config_entry_id: str,
182 """Migrate existing entity if current one can't be found and an old one exists."""
183 entity_entries = er.async_entries_for_config_entry(entity_registry, config_entry_id)
184 for entity_entry
in entity_entries:
185 unique_id =
str(entity_entry.unique_id)
186 if unique_id.startswith(mac_address):
188 if (suffix := unique_id.removeprefix(
str(serial_number))) != unique_id:
189 new_unique_id = f
"{mac_address}{suffix}"
190 _LOGGER.debug(
"Updating unique id from %s to %s", unique_id, new_unique_id)
191 entity_registry.async_update_entity(
192 entity_entry.entity_id, new_unique_id=new_unique_id
197 old_entry: dr.DeviceEntry, new_entry: dr.DeviceEntry
199 """Determine which device entry to keep when there are duplicates.
201 As we transitioned to new unique ids, we did not update existing device entries
202 and as a result there are devices with both the old and new unique id format. We
203 have to pick which one to keep, and preferably this can repair things if the
204 user previously renamed devices.
208 if new_entry.disabled_by
is None and (
209 new_entry.area_id
is not None or new_entry.name_by_user
is not None
212 if old_entry.disabled_by
is None and (
213 old_entry.area_id
is not None or old_entry.name_by_user
is not None
216 return new_entry
if new_entry.disabled_by
is None else old_entry
221 device_registry: dr.DeviceRegistry,
222 config_entry_id: str,
226 """Migrate existing device identifiers to the new format.
228 This will rename any device ids that are prefixed with the serial number to be prefixed
229 with the mac address. This also cleans up from a bug that allowed devices to exist
230 in both the old and new format.
232 device_entries = dr.async_entries_for_config_entry(device_registry, config_entry_id)
233 device_entry_map = {}
235 for device_entry
in device_entries:
236 unique_id =
str(next(iter(device_entry.identifiers))[1])
237 device_entry_map[unique_id] = device_entry
238 if (suffix := unique_id.removeprefix(
str(serial_number))) != unique_id:
239 migrations[unique_id] = f
"{mac_address}{suffix}"
241 for unique_id, new_unique_id
in migrations.items():
242 old_entry = device_entry_map[unique_id]
243 if (new_entry := device_entry_map.get(new_unique_id))
is not None:
246 if entry_to_keep == new_entry:
247 _LOGGER.debug(
"Removing device entry %s", unique_id)
248 device_registry.async_remove_device(old_entry.id)
251 _LOGGER.debug(
"Removing device entry %s", new_unique_id)
252 device_registry.async_remove_device(new_entry.id)
254 _LOGGER.debug(
"Updating device id from %s to %s", unique_id, new_unique_id)
255 device_registry.async_update_device(
256 old_entry.id, new_identifiers={(DOMAIN, new_unique_id)}
261 """Unload a config entry."""
262 return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
aiohttp.ClientSession async_create_clientsession()
None _async_register_clientsession_shutdown(HomeAssistant hass, ConfigEntry entry, aiohttp.ClientSession clientsession)
None _async_fix_device_id(HomeAssistant hass, dr.DeviceRegistry device_registry, str config_entry_id, str mac_address, str serial_number)
bool _async_fix_unique_id(HomeAssistant hass, AsyncRainbirdController controller, ConfigEntry entry)
None _async_fix_entity_unique_id(HomeAssistant hass, er.EntityRegistry entity_registry, str config_entry_id, str mac_address, str serial_number)
bool async_setup_entry(HomeAssistant hass, RainbirdConfigEntry entry)
dr.DeviceEntry _async_device_entry_to_keep(dr.DeviceEntry old_entry, dr.DeviceEntry new_entry)
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)