async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Initialize config entry which represents the HEOS controller.""" # For backwards compat if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=DOMAIN) host = entry.data[CONF_HOST] # Setting all_progress_events=False ensures that we only receive a # media position update upon start of playback or when media changes controller = Heos(host, all_progress_events=False) try: await controller.connect(auto_reconnect=True) # Auto reconnect only operates if initial connection was successful. except HeosError as error: await controller.disconnect() _LOGGER.debug("Unable to connect to controller %s: %s", host, error) raise ConfigEntryNotReady from error # Disconnect when shutting down async def disconnect_controller(event): await controller.disconnect() entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, disconnect_controller) ) # Get players and sources try: players = await controller.get_players() favorites = {} if controller.is_signed_in: favorites = await controller.get_favorites() else: _LOGGER.warning( "%s is not logged in to a HEOS account and will be unable to retrieve " "HEOS favorites: Use the 'heos.sign_in' service to sign-in to a HEOS account", host, ) inputs = await controller.get_input_sources() except HeosError as error: await controller.disconnect() _LOGGER.debug("Unable to retrieve players and sources: %s", error) raise ConfigEntryNotReady from error controller_manager = ControllerManager(hass, controller) await controller_manager.connect_listeners() source_manager = SourceManager(favorites, inputs) source_manager.connect_update(hass, controller) hass.data[DOMAIN] = { DATA_CONTROLLER_MANAGER: controller_manager, DATA_SOURCE_MANAGER: source_manager, MEDIA_PLAYER_DOMAIN: players, } services.register(hass, controller) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up System Bridge from a config entry.""" bridge = Bridge( BridgeClient(aiohttp_client.async_get_clientsession(hass)), f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}", entry.data[CONF_API_KEY], ) try: async with async_timeout.timeout(30): await bridge.async_get_information() except BridgeAuthenticationException as exception: raise ConfigEntryAuthFailed( f"Authentication failed for {entry.title} ({entry.data[CONF_HOST]})" ) from exception except BRIDGE_CONNECTION_ERRORS as exception: raise ConfigEntryNotReady( f"Could not connect to {entry.title} ({entry.data[CONF_HOST]})." ) from exception coordinator = SystemBridgeDataUpdateCoordinator(hass, bridge, _LOGGER, entry=entry) await coordinator.async_config_entry_first_refresh() # Wait for initial data try: async with async_timeout.timeout(60): while ( coordinator.bridge.battery is None or coordinator.bridge.cpu is None or coordinator.bridge.filesystem is None or coordinator.bridge.graphics is None or coordinator.bridge.information is None or coordinator.bridge.memory is None or coordinator.bridge.network is None or coordinator.bridge.os is None or coordinator.bridge.processes is None or coordinator.bridge.system is None ): _LOGGER.debug( "Waiting for initial data from %s (%s)", entry.title, entry.data[CONF_HOST], ) await asyncio.sleep(1) except asyncio.TimeoutError as exception: raise ConfigEntryNotReady( f"Timed out waiting for {entry.title} ({entry.data[CONF_HOST]})." ) from exception hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) if hass.services.has_service(DOMAIN, SERVICE_SEND_COMMAND): return True async def handle_send_command(call): """Handle the send_command service call.""" device_registry = dr.async_get(hass) device_id = call.data[CONF_BRIDGE] device_entry = device_registry.async_get(device_id) if device_entry is None: _LOGGER.warning("Missing device: %s", device_id) return command = call.data[CONF_COMMAND] arguments = shlex.split(call.data.get(CONF_ARGUMENTS, "")) entry_id = next( entry.entry_id for entry in hass.config_entries.async_entries(DOMAIN) if entry.entry_id in device_entry.config_entries ) coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry_id] bridge: Bridge = coordinator.bridge _LOGGER.debug( "Command payload: %s", {CONF_COMMAND: command, CONF_ARGUMENTS: arguments, CONF_WAIT: False}, ) try: response: CommandResponse = await bridge.async_send_command( {CONF_COMMAND: command, CONF_ARGUMENTS: arguments, CONF_WAIT: False} ) if response.success: _LOGGER.debug( "Sent command. Response message was: %s", response.message ) else: _LOGGER.warning( "Error sending command. Response message was: %s", response.message ) except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception: _LOGGER.warning("Error sending command. Error was: %s", exception) async def handle_open(call): """Handle the open service call.""" device_registry = dr.async_get(hass) device_id = call.data[CONF_BRIDGE] device_entry = device_registry.async_get(device_id) if device_entry is None: _LOGGER.warning("Missing device: %s", device_id) return path = call.data[CONF_PATH] entry_id = next( entry.entry_id for entry in hass.config_entries.async_entries(DOMAIN) if entry.entry_id in device_entry.config_entries ) coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry_id] bridge: Bridge = coordinator.bridge _LOGGER.debug("Open payload: %s", {CONF_PATH: path}) try: await bridge.async_open({CONF_PATH: path}) _LOGGER.debug("Sent open request") except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception: _LOGGER.warning("Error sending. Error was: %s", exception) hass.services.async_register( DOMAIN, SERVICE_SEND_COMMAND, handle_send_command, schema=SERVICE_SEND_COMMAND_SCHEMA, ) hass.services.async_register( DOMAIN, SERVICE_OPEN, handle_open, schema=SERVICE_OPEN_SCHEMA, ) # Reload entry when its updated. entry.async_on_unload(entry.add_update_listener(async_reload_entry)) return True
async def async_setup_gateway_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: """Set up the Xiaomi Gateway component from a config entry.""" host = entry.data[CONF_HOST] token = entry.data[CONF_TOKEN] name = entry.title gateway_id = entry.unique_id # For backwards compat if entry.unique_id.endswith("-gateway"): hass.config_entries.async_update_entry(entry, unique_id=entry.data["mac"]) entry.async_on_unload(entry.add_update_listener(update_listener)) # Connect to gateway gateway = ConnectXiaomiGateway(hass, entry) try: await gateway.async_connect_gateway(host, token) except AuthException as error: raise ConfigEntryAuthFailed() from error except SetupException as error: raise ConfigEntryNotReady() from error gateway_info = gateway.gateway_info device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, connections={(dr.CONNECTION_NETWORK_MAC, gateway_info.mac_address)}, identifiers={(DOMAIN, gateway_id)}, manufacturer="Xiaomi", name=name, model=gateway_info.model, sw_version=gateway_info.firmware_version, hw_version=gateway_info.hardware_version, ) def update_data(): """Fetch data from the subdevice.""" data = {} for sub_device in gateway.gateway_device.devices.values(): try: sub_device.update() except GatewayException as ex: _LOGGER.error("Got exception while fetching the state: %s", ex) data[sub_device.sid] = {ATTR_AVAILABLE: False} else: data[sub_device.sid] = {ATTR_AVAILABLE: True} return data async def async_update_data(): """Fetch data from the subdevice using async_add_executor_job.""" return await hass.async_add_executor_job(update_data) # Create update coordinator coordinator = DataUpdateCoordinator( hass, _LOGGER, name=name, update_method=async_update_data, # Polling interval. Will only be polled if there are subscribers. update_interval=UPDATE_INTERVAL, ) hass.data[DOMAIN][entry.entry_id] = { CONF_GATEWAY: gateway.gateway_device, KEY_COORDINATOR: coordinator, } for platform in GATEWAY_PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform))
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Sonos from a config entry.""" platform = entity_platform.async_get_current_platform() @callback def async_create_entities(speaker: SonosSpeaker) -> None: """Handle device discovery and create entities.""" _LOGGER.debug("Creating media_player on %s", speaker.zone_name) async_add_entities([SonosMediaPlayerEntity(speaker)]) @service.verify_domain_control(hass, SONOS_DOMAIN) async def async_service_handle(service_call: ServiceCall) -> None: """Handle dispatched services.""" assert platform is not None entities = await platform.async_extract_from_service(service_call) if not entities: return speakers = [] for entity in entities: assert isinstance(entity, SonosMediaPlayerEntity) speakers.append(entity.speaker) if service_call.service == SERVICE_JOIN: master = platform.entities.get(service_call.data[ATTR_MASTER]) if master: await SonosSpeaker.join_multi(hass, master.speaker, speakers ) # type: ignore[arg-type] else: _LOGGER.error( "Invalid master specified for join service: %s", service_call.data[ATTR_MASTER], ) elif service_call.service == SERVICE_UNJOIN: await SonosSpeaker.unjoin_multi(hass, speakers) # type: ignore[arg-type] elif service_call.service == SERVICE_SNAPSHOT: await SonosSpeaker.snapshot_multi( hass, speakers, service_call.data[ATTR_WITH_GROUP] # type: ignore[arg-type] ) elif service_call.service == SERVICE_RESTORE: await SonosSpeaker.restore_multi( hass, speakers, service_call.data[ATTR_WITH_GROUP] # type: ignore[arg-type] ) config_entry.async_on_unload( async_dispatcher_connect(hass, SONOS_CREATE_MEDIA_PLAYER, async_create_entities)) hass.services.async_register( SONOS_DOMAIN, SERVICE_JOIN, async_service_handle, cv.make_entity_service_schema( {vol.Required(ATTR_MASTER): cv.entity_id}), ) hass.services.async_register( SONOS_DOMAIN, SERVICE_UNJOIN, async_service_handle, cv.make_entity_service_schema({}), ) join_unjoin_schema = cv.make_entity_service_schema( {vol.Optional(ATTR_WITH_GROUP, default=True): cv.boolean}) hass.services.async_register(SONOS_DOMAIN, SERVICE_SNAPSHOT, async_service_handle, join_unjoin_schema) hass.services.async_register(SONOS_DOMAIN, SERVICE_RESTORE, async_service_handle, join_unjoin_schema) platform.async_register_entity_service( # type: ignore SERVICE_SET_TIMER, { vol.Required(ATTR_SLEEP_TIME): vol.All( vol.Coerce(int), vol.Range(min=0, max=86399) ) }, "set_sleep_timer", ) platform.async_register_entity_service(SERVICE_CLEAR_TIMER, {}, "clear_sleep_timer") # type: ignore platform.async_register_entity_service( # type: ignore SERVICE_UPDATE_ALARM, { vol.Required(ATTR_ALARM_ID): cv.positive_int, vol.Optional(ATTR_TIME): cv.time, vol.Optional(ATTR_VOLUME): cv.small_float, vol.Optional(ATTR_ENABLED): cv.boolean, vol.Optional(ATTR_INCLUDE_LINKED_ZONES): cv.boolean, }, "set_alarm", ) platform.async_register_entity_service( # type: ignore SERVICE_PLAY_QUEUE, {vol.Optional(ATTR_QUEUE_POSITION): cv.positive_int}, "play_queue", ) platform.async_register_entity_service( # type: ignore SERVICE_REMOVE_FROM_QUEUE, {vol.Optional(ATTR_QUEUE_POSITION): cv.positive_int}, "remove_from_queue", )
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Do setup of vera.""" # Use options entered during initial config flow or provided from configuration.yml if entry.data.get(CONF_LIGHTS) or entry.data.get(CONF_EXCLUDE): hass.config_entries.async_update_entry( entry=entry, data=entry.data, options=new_options( entry.data.get(CONF_LIGHTS, []), entry.data.get(CONF_EXCLUDE, []), ), ) saved_light_ids = entry.options.get(CONF_LIGHTS, []) saved_exclude_ids = entry.options.get(CONF_EXCLUDE, []) base_url = entry.data[CONF_CONTROLLER] light_ids = fix_device_id_list(saved_light_ids) exclude_ids = fix_device_id_list(saved_exclude_ids) # If the ids were corrected. Update the config entry. if light_ids != saved_light_ids or exclude_ids != saved_exclude_ids: hass.config_entries.async_update_entry( entry=entry, options=new_options(light_ids, exclude_ids) ) # Initialize the Vera controller. subscription_registry = SubscriptionRegistry(hass) controller = veraApi.VeraController(base_url, subscription_registry) try: all_devices = await hass.async_add_executor_job(controller.get_devices) all_scenes = await hass.async_add_executor_job(controller.get_scenes) except RequestException as exception: # There was a network related error connecting to the Vera controller. _LOGGER.exception("Error communicating with Vera API") raise ConfigEntryNotReady from exception # Exclude devices unwanted by user. devices = [device for device in all_devices if device.device_id not in exclude_ids] vera_devices: defaultdict[Platform, list[veraApi.VeraDevice]] = defaultdict(list) for device in devices: device_type = map_vera_device(device, light_ids) if device_type is not None: vera_devices[device_type].append(device) vera_scenes = [] for scene in all_scenes: vera_scenes.append(scene) controller_data = ControllerData( controller=controller, devices=vera_devices, scenes=vera_scenes, config_entry=entry, ) set_controller_data(hass, entry, controller_data) # Forward the config data to the necessary platforms. hass.config_entries.async_setup_platforms( entry, platforms=get_configured_platforms(controller_data) ) def stop_subscription(event): """Stop SubscriptionRegistry updates.""" controller.stop() await hass.async_add_executor_job(controller.start) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_subscription) ) entry.async_on_unload(entry.add_update_listener(_async_update_listener)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up SimpliSafe as config entry.""" hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = {} _async_standardize_config_entry(hass, entry) _verify_domain_control = verify_domain_control(hass, DOMAIN) websession = aiohttp_client.async_get_clientsession(hass) try: api = await API.async_from_refresh_token(entry.data[CONF_TOKEN], session=websession) except InvalidCredentialsError as err: raise ConfigEntryAuthFailed from err except SimplipyError as err: LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady from err simplisafe = SimpliSafe(hass, entry, api) try: await simplisafe.async_init() except SimplipyError as err: raise ConfigEntryNotReady from err hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] = simplisafe hass.config_entries.async_setup_platforms(entry, PLATFORMS) @callback def verify_system_exists( coro: Callable[..., Awaitable]) -> Callable[..., Awaitable]: """Log an error if a service call uses an invalid system ID.""" async def decorator(call: ServiceCall) -> None: """Decorate.""" system_id = int(call.data[ATTR_SYSTEM_ID]) if system_id not in simplisafe.systems: LOGGER.error("Unknown system ID in service call: %s", system_id) return await coro(call) return decorator @callback def v3_only(coro: Callable[..., Awaitable]) -> Callable[..., Awaitable]: """Log an error if the decorated coroutine is called with a v2 system.""" async def decorator(call: ServiceCall) -> None: """Decorate.""" system = simplisafe.systems[int(call.data[ATTR_SYSTEM_ID])] if system.version != 3: LOGGER.error("Service only available on V3 systems") return await coro(call) return decorator @verify_system_exists @_verify_domain_control async def clear_notifications(call: ServiceCall) -> None: """Clear all active notifications.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.async_clear_notifications() except SimplipyError as err: LOGGER.error("Error during service call: %s", err) @verify_system_exists @_verify_domain_control async def remove_pin(call: ServiceCall) -> None: """Remove a PIN.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.async_remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE]) except SimplipyError as err: LOGGER.error("Error during service call: %s", err) @verify_system_exists @_verify_domain_control async def set_pin(call: ServiceCall) -> None: """Set a PIN.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.async_set_pin(call.data[ATTR_PIN_LABEL], call.data[ATTR_PIN_VALUE]) except SimplipyError as err: LOGGER.error("Error during service call: %s", err) @verify_system_exists @v3_only @_verify_domain_control async def set_system_properties(call: ServiceCall) -> None: """Set one or more system parameters.""" system = cast(SystemV3, simplisafe.systems[call.data[ATTR_SYSTEM_ID]]) try: await system.async_set_properties({ prop: value for prop, value in call.data.items() if prop != ATTR_SYSTEM_ID }) except SimplipyError as err: LOGGER.error("Error during service call: %s", err) for service, method, schema in ( ("clear_notifications", clear_notifications, None), ("remove_pin", remove_pin, SERVICE_REMOVE_PIN_SCHEMA), ("set_pin", set_pin, SERVICE_SET_PIN_SCHEMA), ( "set_system_properties", set_system_properties, SERVICE_SET_SYSTEM_PROPERTIES_SCHEMA, ), ): async_register_admin_service(hass, DOMAIN, service, method, schema=schema) entry.async_on_unload(entry.add_update_listener(async_reload_entry)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Network UPS Tools (NUT) from a config entry.""" # strip out the stale options CONF_RESOURCES, # maintain the entry in data in case of version rollback if CONF_RESOURCES in entry.options: new_data = { **entry.data, CONF_RESOURCES: entry.options[CONF_RESOURCES] } new_options = { k: v for k, v in entry.options.items() if k != CONF_RESOURCES } hass.config_entries.async_update_entry(entry, data=new_data, options=new_options) config = entry.data host = config[CONF_HOST] port = config[CONF_PORT] alias = config.get(CONF_ALIAS) username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) scan_interval = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) data = PyNUTData(host, port, alias, username, password) async def async_update_data(): """Fetch data from NUT.""" async with async_timeout.timeout(10): await hass.async_add_executor_job(data.update) if not data.status: raise UpdateFailed("Error fetching UPS state") return data.status coordinator = DataUpdateCoordinator( hass, _LOGGER, name="NUT resource status", update_method=async_update_data, update_interval=timedelta(seconds=scan_interval), ) # Fetch initial data so we have data when entities subscribe await coordinator.async_config_entry_first_refresh() status = coordinator.data _LOGGER.debug("NUT Sensors Available: %s", status) entry.async_on_unload(entry.add_update_listener(_async_update_listener)) unique_id = _unique_id_from_status(status) if unique_id is None: unique_id = entry.entry_id hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { COORDINATOR: coordinator, PYNUT_DATA: data, PYNUT_UNIQUE_ID: unique_id, } device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, unique_id)}, name=data.name.title(), manufacturer=data.device_info.get(ATTR_MANUFACTURER), model=data.device_info.get(ATTR_MODEL), sw_version=data.device_info.get(ATTR_SW_VERSION), ) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up an Insteon entry.""" if not devices.modem: try: await async_connect(**entry.data) except ConnectionError as exception: _LOGGER.error("Could not connect to Insteon modem") raise ConfigEntryNotReady from exception entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, close_insteon_connection) ) await devices.async_load( workdir=hass.config.config_dir, id_devices=0, load_modem_aldb=0 ) # If options existed in YAML and have not already been saved to the config entry # add them now if ( not entry.options and entry.source == SOURCE_IMPORT and hass.data.get(DOMAIN) and hass.data[DOMAIN].get(OPTIONS) ): hass.config_entries.async_update_entry( entry=entry, options=hass.data[DOMAIN][OPTIONS], ) for device_override in entry.options.get(CONF_OVERRIDE, []): # Override the device default capabilities for a specific address address = device_override.get("address") if not devices.get(address): cat = device_override[CONF_CAT] subcat = device_override[CONF_SUBCAT] devices.set_id(address, cat, subcat, 0) for device in entry.options.get(CONF_X10, []): housecode = device.get(CONF_HOUSECODE) unitcode = device.get(CONF_UNITCODE) x10_type = "on_off" steps = device.get(CONF_DIM_STEPS, 22) if device.get(CONF_PLATFORM) == "light": x10_type = "dimmable" elif device.get(CONF_PLATFORM) == "binary_sensor": x10_type = "sensor" _LOGGER.debug( "Adding X10 device to Insteon: %s %d %s", housecode, unitcode, x10_type ) device = devices.add_x10_device(housecode, unitcode, x10_type, steps) for platform in INSTEON_PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform) ) for address in devices: device = devices[address] platforms = get_device_platforms(device) if ON_OFF_EVENTS in platforms: add_on_off_event_device(hass, device) create_insteon_device(hass, device, entry.entry_id) _LOGGER.debug("Insteon device count: %s", len(devices)) register_new_device_callback(hass) async_register_services(hass) create_insteon_device(hass, devices.modem, entry.entry_id) api.async_load_api(hass) await api.async_register_insteon_frontend(hass) asyncio.create_task(async_get_device_config(hass, entry)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the WeatherFlow config entries.""" _async_import_options_from_data_if_missing(hass, entry) session = async_create_clientsession(hass) unit_system = (CONF_UNIT_SYSTEM_METRIC if hass.config.units.is_metric else CONF_UNIT_SYSTEM_IMPERIAL) weatherflowapi = WeatherFlowApiClient( entry.data[CONF_STATION_ID], entry.data[CONF_API_TOKEN], units=unit_system, forecast_hours=entry.options.get(CONF_FORECAST_HOURS, DEFAULT_FORECAST_HOURS), homeassistant=True, session=session, ) try: await weatherflowapi.initialize() station_data: StationDescription = weatherflowapi.station_data except WrongStationID: _LOGGER.debug("The Station Id entered is not correct") return False except Invalid as notreadyerror: _LOGGER.error("The data returned from WeatherFlow is invalid") raise ConfigEntryNotReady from notreadyerror except NotAuthorized: _LOGGER.debug( "The Api Token entered is not valid for the supplied Station Id.") return False except BadRequest as notreadyerror: _LOGGER.error("An unknown error occurred when retreiving data") raise ConfigEntryNotReady from notreadyerror if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=station_data.key) async def async_update_data(): """Obtain the latest data from WeatherFlow.""" try: data: ObservationDescription = await weatherflowapi.update_observations( ) return data except (BadRequest, Invalid) as err: raise UpdateFailed(f"Error while retreiving data: {err}") from err async def async_update_forecast(): """Obtain the latest forecast from WeatherFlow.""" try: data: ForecastDescription = await weatherflowapi.update_forecast() return data except (BadRequest, Invalid) as err: raise UpdateFailed( f"Error while retreiving forecast data: {err}") from err unit_descriptions = await weatherflowapi.load_unit_system() coordinator = DataUpdateCoordinator( hass, _LOGGER, name=DOMAIN, update_method=async_update_data, update_interval=timedelta(minutes=entry.options.get( CONF_INTERVAL_OBSERVATION, DEFAULT_OBSERVATION_INTERVAL)), ) await coordinator.async_config_entry_first_refresh() if not coordinator.last_update_success: raise ConfigEntryNotReady forecast_coordinator = DataUpdateCoordinator( hass, _LOGGER, name=DOMAIN, update_method=async_update_forecast, update_interval=timedelta(minutes=entry.options.get( CONF_INTERVAL_FORECAST, DEFAULT_FORECAST_INTERVAL)), ) await forecast_coordinator.async_config_entry_first_refresh() if not forecast_coordinator.last_update_success: raise ConfigEntryNotReady hass.data.setdefault(DOMAIN, {})[entry.entry_id] = WeatherFlowEntryData( coordinator=coordinator, forecast_coordinator=forecast_coordinator, weatherflowapi=weatherflowapi, station_data=station_data, unit_descriptions=unit_descriptions, ) await _async_get_or_create_nvr_device_in_registry(hass, entry, station_data) hass.config_entries.async_setup_platforms(entry, WEATHERFLOW_PLATFORMS) entry.async_on_unload(entry.add_update_listener(_async_options_updated)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.config_entries.async_setup_platforms(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(update_listener)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Bond from a config entry.""" host = entry.data[CONF_HOST] token = entry.data[CONF_ACCESS_TOKEN] config_entry_id = entry.entry_id bond = Bond( host=host, token=token, timeout=ClientTimeout(total=_API_TIMEOUT), session=async_get_clientsession(hass), ) hub = BondHub(bond, host) try: await hub.setup() except ClientResponseError as ex: if ex.status == HTTPStatus.UNAUTHORIZED: _LOGGER.error("Bond token no longer valid: %s", ex) return False raise ConfigEntryNotReady from ex except (ClientError, AsyncIOTimeoutError, OSError) as error: raise ConfigEntryNotReady from error bpup_subs = BPUPSubscriptions() stop_bpup = await start_bpup(host, bpup_subs) @callback def _async_stop_event(*_: Any) -> None: stop_bpup() entry.async_on_unload(_async_stop_event) entry.async_on_unload( hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, _async_stop_event)) hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { HUB: hub, BPUP_SUBS: bpup_subs, } if not entry.unique_id: hass.config_entries.async_update_entry(entry, unique_id=hub.bond_id) assert hub.bond_id is not None hub_name = hub.name or hub.bond_id device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=config_entry_id, identifiers={(DOMAIN, hub.bond_id)}, manufacturer=BRIDGE_MAKE, name=hub_name, model=hub.target, sw_version=hub.fw_ver, hw_version=hub.mcu_ver, suggested_area=hub.location, configuration_url=f"http://{host}", ) _async_remove_old_device_identifiers(config_entry_id, device_registry, hub) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the AVM FRITZ!SmartHome platforms.""" fritz = Fritzhome( host=entry.data[CONF_HOST], user=entry.data[CONF_USERNAME], password=entry.data[CONF_PASSWORD], ) try: await hass.async_add_executor_job(fritz.login) except LoginError as err: raise ConfigEntryAuthFailed from err hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { CONF_CONNECTIONS: fritz, } def _update_fritz_devices() -> dict[str, FritzhomeDevice]: """Update all fritzbox device data.""" try: devices = fritz.get_devices() except requests.exceptions.HTTPError: # If the device rebooted, login again try: fritz.login() except requests.exceptions.HTTPError as ex: raise ConfigEntryAuthFailed from ex devices = fritz.get_devices() data = {} for device in devices: device.update() data[device.ain] = device return data async def async_update_coordinator() -> dict[str, FritzhomeDevice]: """Fetch all device data.""" return await hass.async_add_executor_job(_update_fritz_devices) hass.data[DOMAIN][entry.entry_id][ CONF_COORDINATOR ] = coordinator = DataUpdateCoordinator( hass, LOGGER, name=f"{entry.entry_id}", update_method=async_update_coordinator, update_interval=timedelta(seconds=30), ) await coordinator.async_config_entry_first_refresh() hass.config_entries.async_setup_platforms(entry, PLATFORMS) def logout_fritzbox(event: Event) -> None: """Close connections to this fritzbox.""" fritz.logout() entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, logout_fritzbox) ) return True
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up the deCONZ lights and groups from a config entry.""" gateway = get_gateway_from_config_entry(hass, config_entry) gateway.entities[DOMAIN] = set() @callback def async_add_light( lights: list[Light] | ValuesView[Light] = gateway.api.lights.values(), ) -> None: """Add light from deCONZ.""" entities = [] for light in lights: if ( isinstance(light, Light) and light.type not in POWER_PLUGS and light.unique_id not in gateway.entities[DOMAIN] ): entities.append(DeconzLight(light, gateway)) if entities: async_add_entities(entities) config_entry.async_on_unload( async_dispatcher_connect( hass, gateway.signal_new_light, async_add_light, ) ) @callback def async_add_group( groups: list[Group] | ValuesView[Group] = gateway.api.groups.values(), ) -> None: """Add group from deCONZ.""" if not gateway.option_allow_deconz_groups: return entities = [] for group in groups: if not group.lights: continue known_groups = set(gateway.entities[DOMAIN]) new_group = DeconzGroup(group, gateway) if new_group.unique_id not in known_groups: entities.append(new_group) if entities: async_add_entities(entities) config_entry.async_on_unload( async_dispatcher_connect( hass, gateway.signal_new_group, async_add_group, ) ) async_add_light() async_add_group()
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Sense from a config entry.""" entry_data = entry.data email = entry_data[CONF_EMAIL] timeout = entry_data[CONF_TIMEOUT] access_token = entry_data.get("access_token", "") user_id = entry_data.get("user_id", "") monitor_id = entry_data.get("monitor_id", "") client_session = async_get_clientsession(hass) gateway = ASyncSenseable(api_timeout=timeout, wss_timeout=timeout, client_session=client_session) gateway.rate_limit = ACTIVE_UPDATE_RATE try: gateway.load_auth(access_token, user_id, monitor_id) await gateway.get_monitor_data() except (SenseAuthenticationException, SenseMFARequiredException) as err: _LOGGER.warning("Sense authentication expired") raise ConfigEntryAuthFailed(err) from err except SENSE_TIMEOUT_EXCEPTIONS as err: raise ConfigEntryNotReady( str(err) or "Timed out during authentication") from err sense_devices_data = SenseDevicesData() try: sense_discovered_devices = await gateway.get_discovered_device_data() await gateway.update_realtime() except SENSE_TIMEOUT_EXCEPTIONS as err: raise ConfigEntryNotReady( str(err) or "Timed out during realtime update") from err except SENSE_EXCEPTIONS as err: raise ConfigEntryNotReady(str(err) or "Error during realtime update") from err async def _async_update_trend(): """Update the trend data.""" try: await gateway.update_trend_data() except (SenseAuthenticationException, SenseMFARequiredException) as err: _LOGGER.warning("Sense authentication expired") raise ConfigEntryAuthFailed(err) from err trends_coordinator: DataUpdateCoordinator[None] = DataUpdateCoordinator( hass, _LOGGER, name=f"Sense Trends {email}", update_method=_async_update_trend, update_interval=timedelta(seconds=300), ) # Start out as unavailable so we do not report 0 data # until the update happens trends_coordinator.last_update_success = False # This can take longer than 60s and we already know # sense is online since get_discovered_device_data was # successful so we do it later. asyncio.create_task(trends_coordinator.async_request_refresh()) hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { SENSE_DATA: gateway, SENSE_DEVICES_DATA: sense_devices_data, SENSE_TRENDS_COORDINATOR: trends_coordinator, SENSE_DISCOVERED_DEVICES_DATA: sense_discovered_devices, } hass.config_entries.async_setup_platforms(entry, PLATFORMS) async def async_sense_update(_): """Retrieve latest state.""" try: await gateway.update_realtime() except SENSE_TIMEOUT_EXCEPTIONS as ex: _LOGGER.error("Timeout retrieving data: %s", ex) except SENSE_EXCEPTIONS as ex: _LOGGER.error("Failed to update data: %s", ex) data = gateway.get_realtime() if "devices" in data: sense_devices_data.set_devices_data(data["devices"]) async_dispatcher_send( hass, f"{SENSE_DEVICE_UPDATE}-{gateway.sense_monitor_id}") remove_update_callback = async_track_time_interval( hass, async_sense_update, timedelta(seconds=ACTIVE_UPDATE_RATE)) @callback def _remove_update_callback_at_stop(event): remove_update_callback() entry.async_on_unload(remove_update_callback) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _remove_update_callback_at_stop)) return True
async def async_setup_entry(hass: HomeAssistant, entry: config_entries.ConfigEntry) -> bool: """Set up the ISY 994 integration.""" # As there currently is no way to import options from yaml # when setting up a config entry, we fallback to adding # the options to the config entry and pull them out here if # they are missing from the options _async_import_options_from_data_if_missing(hass, entry) hass.data[DOMAIN][entry.entry_id] = {} hass_isy_data = hass.data[DOMAIN][entry.entry_id] hass_isy_data[ISY994_NODES] = {} for platform in PLATFORMS: hass_isy_data[ISY994_NODES][platform] = [] hass_isy_data[ISY994_PROGRAMS] = {} for platform in PROGRAM_PLATFORMS: hass_isy_data[ISY994_PROGRAMS][platform] = [] hass_isy_data[ISY994_VARIABLES] = [] isy_config = entry.data isy_options = entry.options # Required user = isy_config[CONF_USERNAME] password = isy_config[CONF_PASSWORD] host = urlparse(isy_config[CONF_HOST]) # Optional tls_version = isy_config.get(CONF_TLS_VER) ignore_identifier = isy_options.get(CONF_IGNORE_STRING, DEFAULT_IGNORE_STRING) sensor_identifier = isy_options.get(CONF_SENSOR_STRING, DEFAULT_SENSOR_STRING) variable_identifier = isy_options.get(CONF_VAR_SENSOR_STRING, DEFAULT_VAR_SENSOR_STRING) if host.scheme == "http": https = False port = host.port or 80 session = aiohttp_client.async_create_clientsession( hass, verify_ssl=None, cookie_jar=CookieJar(unsafe=True)) elif host.scheme == "https": https = True port = host.port or 443 session = aiohttp_client.async_get_clientsession(hass) else: _LOGGER.error("The isy994 host value in configuration is invalid") return False # Connect to ISY controller. isy = ISY( host.hostname, port, username=user, password=password, use_https=https, tls_ver=tls_version, webroot=host.path, websession=session, use_websocket=True, ) try: async with async_timeout.timeout(60): await isy.initialize() except asyncio.TimeoutError as err: raise ConfigEntryNotReady( f"Timed out initializing the ISY; device may be busy, trying again later: {err}" ) from err except ISYInvalidAuthError as err: _LOGGER.error( "Invalid credentials for the ISY, please adjust settings and try again: %s", err, ) return False except ISYConnectionError as err: raise ConfigEntryNotReady( f"Failed to connect to the ISY, please adjust settings and try again: {err}" ) from err except ISYResponseParseError as err: raise ConfigEntryNotReady( f"Invalid XML response from ISY; Ensure the ISY is running the latest firmware: {err}" ) from err _categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier, sensor_identifier) _categorize_programs(hass_isy_data, isy.programs) _categorize_variables(hass_isy_data, isy.variables, variable_identifier) # Dump ISY Clock Information. Future: Add ISY as sensor to Hass with attrs _LOGGER.info(repr(isy.clock)) hass_isy_data[ISY994_ISY] = isy await _async_get_or_create_isy_device_in_registry(hass, entry, isy) # Load platforms for the devices in the ISY controller that we support. hass.config_entries.async_setup_platforms(entry, PLATFORMS) @callback def _async_stop_auto_update(event) -> None: """Stop the isy auto update on Home Assistant Shutdown.""" _LOGGER.debug("ISY Stopping Event Stream and automatic updates") isy.websocket.stop() _LOGGER.debug("ISY Starting Event Stream and automatic updates") isy.websocket.start() entry.async_on_unload(entry.add_update_listener(_async_update_listener)) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop_auto_update)) # Register Integration-wide Services: async_setup_services(hass) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the wiz integration from a config entry.""" ip_address = entry.data[CONF_HOST] _LOGGER.debug("Get bulb with IP: %s", ip_address) bulb = wizlight(ip_address) try: scenes = await bulb.getSupportedScenes() await bulb.getMac() except WIZ_CONNECT_EXCEPTIONS as err: await bulb.async_close() raise ConfigEntryNotReady(f"{ip_address}: {err}") from err if bulb.mac != entry.unique_id: # The ip address of the bulb has changed and its likely offline # and another WiZ device has taken the IP. Avoid setting up # since its the wrong device. As soon as the device comes back # online the ip will get updated and setup will proceed. raise ConfigEntryNotReady( "Found bulb {bulb.mac} at {ip_address}, expected {entry.unique_id}" ) async def _async_update() -> None: """Update the WiZ device.""" try: await bulb.updateState() except WIZ_EXCEPTIONS as ex: raise UpdateFailed(f"Failed to update device at {ip_address}: {ex}") from ex coordinator = DataUpdateCoordinator( hass=hass, logger=_LOGGER, name=entry.title, update_interval=timedelta(seconds=15), update_method=_async_update, # We don't want an immediate refresh since the device # takes a moment to reflect the state change request_refresh_debouncer=Debouncer( hass, _LOGGER, cooldown=REQUEST_REFRESH_DELAY, immediate=False ), ) try: await coordinator.async_config_entry_first_refresh() except ConfigEntryNotReady as err: await bulb.async_close() raise err async def _async_shutdown_on_stop(event: Event) -> None: await bulb.async_close() entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_shutdown_on_stop) ) @callback def _async_push_update(state: PilotParser) -> None: """Receive a push update.""" _LOGGER.debug("%s: Got push update: %s", bulb.mac, state.pilotResult) coordinator.async_set_updated_data(None) if state.get_source() == PIR_SOURCE: async_dispatcher_send(hass, SIGNAL_WIZ_PIR.format(bulb.mac)) await bulb.start_push(_async_push_update) bulb.set_discovery_callback(lambda bulb: async_trigger_discovery(hass, [bulb])) hass.data.setdefault(DOMAIN, {})[entry.entry_id] = WizData( coordinator=coordinator, bulb=bulb, scenes=scenes ) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up RainMachine as config entry.""" hass.data.setdefault(DOMAIN, {DATA_CONTROLLER: {}, DATA_COORDINATOR: {}}) hass.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = {} websession = aiohttp_client.async_get_clientsession(hass) client = Client(session=websession) try: await client.load_local( entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD], port=entry.data[CONF_PORT], ssl=entry.data.get(CONF_SSL, DEFAULT_SSL), ) except RainMachineError as err: raise ConfigEntryNotReady from err # regenmaschine can load multiple controllers at once, but we only grab the one # we loaded above: controller = hass.data[DOMAIN][DATA_CONTROLLER][ entry.entry_id] = get_client_controller(client) entry_updates = {} if not entry.unique_id or is_ip_address(entry.unique_id): # If the config entry doesn't already have a unique ID, set one: entry_updates["unique_id"] = controller.mac if CONF_ZONE_RUN_TIME in entry.data: # If a zone run time exists in the config entry's data, pop it and move it to # options: data = {**entry.data} entry_updates["data"] = data entry_updates["options"] = { **entry.options, CONF_ZONE_RUN_TIME: data.pop(CONF_ZONE_RUN_TIME), } if entry_updates: hass.config_entries.async_update_entry(entry, **entry_updates) async def async_update(api_category: str) -> dict: """Update the appropriate API data based on a category.""" try: if api_category == DATA_PROGRAMS: return await controller.programs.all(include_inactive=True) if api_category == DATA_PROVISION_SETTINGS: return await controller.provisioning.settings() if api_category == DATA_RESTRICTIONS_CURRENT: return await controller.restrictions.current() if api_category == DATA_RESTRICTIONS_UNIVERSAL: return await controller.restrictions.universal() return await controller.zones.all(details=True, include_inactive=True) except RainMachineError as err: raise UpdateFailed(err) from err controller_init_tasks = [] for api_category in [ DATA_PROGRAMS, DATA_PROVISION_SETTINGS, DATA_RESTRICTIONS_CURRENT, DATA_RESTRICTIONS_UNIVERSAL, DATA_ZONES, ]: coordinator = hass.data[DOMAIN][DATA_COORDINATOR][ entry.entry_id][api_category] = DataUpdateCoordinator( hass, LOGGER, name=f'{controller.name} ("{api_category}")', update_interval=DEFAULT_UPDATE_INTERVAL, update_method=partial(async_update, api_category), ) controller_init_tasks.append(coordinator.async_refresh()) await asyncio.gather(*controller_init_tasks) hass.config_entries.async_setup_platforms(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(async_reload_entry)) return True
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up the deCONZ sensors.""" gateway = get_gateway_from_config_entry(hass, config_entry) gateway.entities[DOMAIN] = set() battery_handler = DeconzBatteryHandler(gateway) @callback def async_add_sensor(sensors: list[PydeconzSensor] | None = None) -> None: """Add sensors from deCONZ. Create DeconzBattery if sensor has a battery attribute. Create DeconzSensor if not a battery, switch or thermostat and not a binary sensor. """ entities: list[DeconzSensor] = [] if sensors is None: sensors = gateway.api.sensors.values() for sensor in sensors: if not gateway.option_allow_clip_sensor and sensor.type.startswith( "CLIP"): continue if sensor.battery is None: battery_handler.create_tracker(sensor) known_entities = set(gateway.entities[DOMAIN]) for description in (ENTITY_DESCRIPTIONS.get(type(sensor), []) + SENSOR_DESCRIPTIONS): if (not hasattr(sensor, description.key) or description.value_fn(sensor) is None): continue new_entity = DeconzSensor(sensor, gateway, description) if new_entity.unique_id not in known_entities: entities.append(new_entity) if description.key == "battery": battery_handler.remove_tracker(sensor) if entities: async_add_entities(entities) config_entry.async_on_unload( async_dispatcher_connect( hass, gateway.signal_new_sensor, async_add_sensor, )) async_add_sensor([ gateway.api.sensors[key] for key in sorted(gateway.api.sensors, key=int) ])
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Z-Wave sensor from config entry.""" client: ZwaveClient = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT] @callback def async_add_sensor(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Sensor.""" driver = client.driver assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if info.platform_data: data: NumericSensorDataTemplateData = info.platform_data else: data = NumericSensorDataTemplateData() entity_description = ENTITY_DESCRIPTION_KEY_MAP.get( data.entity_description_key or "", SensorEntityDescription("base_sensor")) if info.platform_hint == "string_sensor": entities.append( ZWaveStringSensor(config_entry, driver, info, entity_description)) elif info.platform_hint == "numeric_sensor": entities.append( ZWaveNumericSensor( config_entry, driver, info, entity_description, data.unit_of_measurement, )) elif info.platform_hint == "list_sensor": entities.append( ZWaveListSensor(config_entry, driver, info, entity_description)) elif info.platform_hint == "config_parameter": entities.append( ZWaveConfigParameterSensor(config_entry, driver, info, entity_description)) elif info.platform_hint == "meter": entities.append( ZWaveMeterSensor(config_entry, driver, info, entity_description)) else: LOGGER.warning( "Sensor not implemented for %s/%s", info.platform_hint, info.primary_value.property_name, ) return async_add_entities(entities) @callback def async_add_node_status_sensor(node: ZwaveNode) -> None: """Add node status sensor.""" driver = client.driver assert driver is not None # Driver is ready before platforms are loaded. async_add_entities([ZWaveNodeStatusSensor(config_entry, driver, node)]) config_entry.async_on_unload( async_dispatcher_connect( hass, f"{DOMAIN}_{config_entry.entry_id}_add_{SENSOR_DOMAIN}", async_add_sensor, )) config_entry.async_on_unload( async_dispatcher_connect( hass, f"{DOMAIN}_{config_entry.entry_id}_add_node_status_sensor", async_add_node_status_sensor, )) platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( SERVICE_RESET_METER, { vol.Optional(ATTR_METER_TYPE): vol.Coerce(int), vol.Optional(ATTR_VALUE): vol.Coerce(int), }, "async_reset_meter", )
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Z-Wave sensor from config entry.""" client: ZwaveClient = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT] @callback def async_add_sensor(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Sensor.""" entities: list[ZWaveBaseEntity] = [] if info.platform_hint == "string_sensor": entities.append(ZWaveStringSensor(config_entry, client, info)) elif info.platform_hint == "numeric_sensor": entities.append(ZWaveNumericSensor(config_entry, client, info)) elif info.platform_hint == "list_sensor": entities.append(ZWaveListSensor(config_entry, client, info)) elif info.platform_hint == "config_parameter": entities.append( ZWaveConfigParameterSensor(config_entry, client, info)) elif info.platform_hint == "meter": entities.append(ZWaveMeterSensor(config_entry, client, info)) else: LOGGER.warning( "Sensor not implemented for %s/%s", info.platform_hint, info.primary_value.propertyname, ) return async_add_entities(entities) @callback def async_add_node_status_sensor(node: ZwaveNode) -> None: """Add node status sensor.""" async_add_entities([ZWaveNodeStatusSensor(config_entry, client, node)]) config_entry.async_on_unload( async_dispatcher_connect( hass, f"{DOMAIN}_{config_entry.entry_id}_add_{SENSOR_DOMAIN}", async_add_sensor, )) config_entry.async_on_unload( async_dispatcher_connect( hass, f"{DOMAIN}_{config_entry.entry_id}_add_node_status_sensor", async_add_node_status_sensor, )) platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( SERVICE_RESET_METER, { vol.Optional(ATTR_METER_TYPE): vol.Coerce(int), vol.Optional(ATTR_VALUE): vol.Coerce(int), }, "async_reset_meter", )
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the StarLine device from a config entry.""" account = StarlineAccount(hass, entry) await account.update() await account.update_obd() if not account.api.available: raise ConfigEntryNotReady if DOMAIN not in hass.data: hass.data[DOMAIN] = {} hass.data[DOMAIN][entry.entry_id] = account device_registry = await hass.helpers.device_registry.async_get_registry() for device in account.api.devices.values(): device_registry.async_get_or_create( config_entry_id=entry.entry_id, **account.device_info(device) ) hass.config_entries.async_setup_platforms(entry, PLATFORMS) async def async_set_scan_interval(call: ServiceCall) -> None: """Set scan interval.""" options = dict(entry.options) options[CONF_SCAN_INTERVAL] = call.data[CONF_SCAN_INTERVAL] hass.config_entries.async_update_entry(entry=entry, options=options) async def async_set_scan_obd_interval(call: ServiceCall) -> None: """Set OBD info scan interval.""" options = dict(entry.options) options[CONF_SCAN_OBD_INTERVAL] = call.data[CONF_SCAN_INTERVAL] hass.config_entries.async_update_entry(entry=entry, options=options) async def async_update(call: ServiceCall | None = None) -> None: """Update all data.""" await account.update() await account.update_obd() hass.services.async_register(DOMAIN, SERVICE_UPDATE_STATE, async_update) hass.services.async_register( DOMAIN, SERVICE_SET_SCAN_INTERVAL, async_set_scan_interval, schema=vol.Schema( { vol.Required(CONF_SCAN_INTERVAL): vol.All( vol.Coerce(int), vol.Range(min=10) ) } ), ) hass.services.async_register( DOMAIN, SERVICE_SET_SCAN_OBD_INTERVAL, async_set_scan_obd_interval, schema=vol.Schema( { vol.Required(CONF_SCAN_INTERVAL): vol.All( vol.Coerce(int), vol.Range(min=180) ) } ), ) entry.async_on_unload(entry.add_update_listener(async_options_updated)) await async_options_updated(hass, entry) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up RainMachine as config entry.""" websession = aiohttp_client.async_get_clientsession(hass) client = Client(session=websession) try: await client.load_local( entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD], port=entry.data[CONF_PORT], ssl=entry.data.get(CONF_SSL, DEFAULT_SSL), ) except RainMachineError as err: raise ConfigEntryNotReady from err # regenmaschine can load multiple controllers at once, but we only grab the one # we loaded above: controller = get_client_controller(client) entry_updates: dict[str, Any] = {} if not entry.unique_id or is_ip_address(entry.unique_id): # If the config entry doesn't already have a unique ID, set one: entry_updates["unique_id"] = controller.mac if CONF_ZONE_RUN_TIME in entry.data: # If a zone run time exists in the config entry's data, pop it and move it to # options: data = {**entry.data} entry_updates["data"] = data entry_updates["options"] = { **entry.options, CONF_ZONE_RUN_TIME: data.pop(CONF_ZONE_RUN_TIME), } if entry_updates: hass.config_entries.async_update_entry(entry, **entry_updates) async def async_update(api_category: str) -> dict: """Update the appropriate API data based on a category.""" data: dict = {} try: if api_category == DATA_PROGRAMS: data = await controller.programs.all(include_inactive=True) elif api_category == DATA_PROVISION_SETTINGS: data = await controller.provisioning.settings() elif api_category == DATA_RESTRICTIONS_CURRENT: data = await controller.restrictions.current() elif api_category == DATA_RESTRICTIONS_UNIVERSAL: data = await controller.restrictions.universal() else: data = await controller.zones.all(details=True, include_inactive=True) except RainMachineError as err: raise UpdateFailed(err) from err return data controller_init_tasks = [] coordinators = {} for api_category in ( DATA_PROGRAMS, DATA_PROVISION_SETTINGS, DATA_RESTRICTIONS_CURRENT, DATA_RESTRICTIONS_UNIVERSAL, DATA_ZONES, ): coordinator = coordinators[api_category] = DataUpdateCoordinator( hass, LOGGER, name=f'{controller.name} ("{api_category}")', update_interval=UPDATE_INTERVALS[api_category], update_method=partial(async_update, api_category), ) controller_init_tasks.append(coordinator.async_refresh()) await asyncio.gather(*controller_init_tasks) hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { DATA_CONTROLLER: controller, DATA_COORDINATOR: coordinators, } hass.config_entries.async_setup_platforms(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(async_reload_entry)) async def async_pause_watering(call: ServiceCall) -> None: """Pause watering for a set number of seconds.""" controller = async_get_controller_for_service_call(hass, call) await controller.watering.pause_all(call.data[CONF_SECONDS]) await async_update_programs_and_zones(hass, entry) async def async_push_weather_data(call: ServiceCall) -> None: """Push weather data to the device.""" controller = async_get_controller_for_service_call(hass, call) await controller.parsers.post_data( { CONF_WEATHER: [ { key: value for key, value in call.data.items() if key != CONF_DEVICE_ID } ] } ) async def async_stop_all(call: ServiceCall) -> None: """Stop all watering.""" controller = async_get_controller_for_service_call(hass, call) await controller.watering.stop_all() await async_update_programs_and_zones(hass, entry) async def async_unpause_watering(call: ServiceCall) -> None: """Unpause watering.""" controller = async_get_controller_for_service_call(hass, call) await controller.watering.unpause_all() await async_update_programs_and_zones(hass, entry) for service_name, schema, method in ( ( SERVICE_NAME_PAUSE_WATERING, SERVICE_PAUSE_WATERING_SCHEMA, async_pause_watering, ), ( SERVICE_NAME_PUSH_WEATHER_DATA, SERVICE_PUSH_WEATHER_DATA_SCHEMA, async_push_weather_data, ), (SERVICE_NAME_STOP_ALL, SERVICE_SCHEMA, async_stop_all), (SERVICE_NAME_UNPAUSE_WATERING, SERVICE_SCHEMA, async_unpause_watering), ): if hass.services.has_service(DOMAIN, service_name): continue hass.services.async_register(DOMAIN, service_name, method, schema=schema) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN] = FileExplorer(hass, entry.options) entry.async_on_unload(entry.add_update_listener(update_listener)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None: """Set up the Netatmo weather and homecoach platform.""" data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER] platform_not_ready = True async def find_entities(data_class_name: str) -> list: """Find all entities.""" all_module_infos = {} data = data_handler.data if data_class_name not in data: return [] if data[data_class_name] is None: return [] data_class = data[data_class_name] for station_id in data_class.stations: for module_id in data_class.get_modules(station_id): all_module_infos[module_id] = data_class.get_module(module_id) all_module_infos[station_id] = data_class.get_station(station_id) entities = [] for module in all_module_infos.values(): if "_id" not in module: _LOGGER.debug("Skipping module %s", module.get("module_name")) continue conditions = [ c.lower() for c in data_class.get_monitored_conditions( module_id=module["_id"]) if c.lower() in SENSOR_TYPES_KEYS ] for condition in conditions: if f"{condition}_value" in SENSOR_TYPES_KEYS: conditions.append(f"{condition}_value") elif f"{condition}_lvl" in SENSOR_TYPES_KEYS: conditions.append(f"{condition}_lvl") entities.extend([ NetatmoSensor(data_handler, data_class_name, module, description) for description in SENSOR_TYPES if description.key in conditions ]) _LOGGER.debug("Adding weather sensors %s", entities) return entities for data_class_name in ( WEATHERSTATION_DATA_CLASS_NAME, HOMECOACH_DATA_CLASS_NAME, ): data_class = data_handler.data.get(data_class_name) if data_class and data_class.raw_data: platform_not_ready = False async_add_entities(await find_entities(data_class_name), True) device_registry = dr.async_get(hass) async def add_public_entities(update: bool = True) -> None: """Retrieve Netatmo public weather entities.""" entities = { device.name: device.id for device in dr.async_entries_for_config_entry( device_registry, entry.entry_id) if device.model == "Public Weather stations" } new_entities = [] for area in [ NetatmoArea(**i) for i in entry.options.get(CONF_WEATHER_AREAS, {}).values() ]: signal_name = f"{PUBLICDATA_DATA_CLASS_NAME}-{area.uuid}" if area.area_name in entities: entities.pop(area.area_name) if update: async_dispatcher_send( hass, f"netatmo-config-{area.area_name}", area, ) continue await data_handler.subscribe( PUBLICDATA_DATA_CLASS_NAME, signal_name, None, lat_ne=area.lat_ne, lon_ne=area.lon_ne, lat_sw=area.lat_sw, lon_sw=area.lon_sw, ) data_class = data_handler.data.get(signal_name) if data_class and data_class.raw_data: nonlocal platform_not_ready platform_not_ready = False new_entities.extend([ NetatmoPublicSensor(data_handler, area, description) for description in SENSOR_TYPES if description.key in SUPPORTED_PUBLIC_SENSOR_TYPES ]) for device_id in entities.values(): device_registry.async_remove_device(device_id) if new_entities: async_add_entities(new_entities) async_dispatcher_connect( hass, f"signal-{DOMAIN}-public-update-{entry.entry_id}", add_public_entities) @callback def _create_entity(netatmo_device: NetatmoDevice) -> None: entity = NetatmoClimateBatterySensor(netatmo_device) _LOGGER.debug("Adding climate battery sensor %s", entity) async_add_entities([entity]) entry.async_on_unload( async_dispatcher_connect(hass, NETATMO_CREATE_BATTERY, _create_entity)) await add_public_entities(False) if platform_not_ready: raise PlatformNotReady
async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, ) -> bool: """Create a gateway.""" tradfri_data: dict[str, Any] = {} hass.data.setdefault(DOMAIN, {})[entry.entry_id] = tradfri_data factory = await APIFactory.init( entry.data[CONF_HOST], psk_id=entry.data[CONF_IDENTITY], psk=entry.data[CONF_KEY], ) tradfri_data[FACTORY] = factory # Used for async_unload_entry async def on_hass_stop(event: Event) -> None: """Close connection when hass stops.""" await factory.shutdown() # Setup listeners entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)) api = factory.request gateway = Gateway() try: gateway_info = await api(gateway.get_gateway_info(), timeout=TIMEOUT_API) devices_commands: Command = await api(gateway.get_devices(), timeout=TIMEOUT_API) devices: list[Device] = await api(devices_commands, timeout=TIMEOUT_API) except RequestError as exc: await factory.shutdown() raise ConfigEntryNotReady from exc dev_reg = await hass.helpers.device_registry.async_get_registry() dev_reg.async_get_or_create( config_entry_id=entry.entry_id, connections=set(), identifiers={(DOMAIN, entry.data[CONF_GATEWAY_ID])}, manufacturer="IKEA of Sweden", name="Gateway", # They just have 1 gateway model. Type is not exposed yet. model="E1526", sw_version=gateway_info.firmware_version, ) remove_stale_devices(hass, entry, devices) # Setup the device coordinators coordinator_data = { CONF_GATEWAY_ID: gateway, KEY_API: api, COORDINATOR_LIST: [], } for device in devices: coordinator = TradfriDeviceDataUpdateCoordinator(hass=hass, api=api, device=device) await coordinator.async_config_entry_first_refresh() entry.async_on_unload( async_dispatcher_connect(hass, SIGNAL_GW, coordinator.set_hub_available)) coordinator_data[COORDINATOR_LIST].append(coordinator) tradfri_data[COORDINATOR] = coordinator_data async def async_keep_alive(now: datetime) -> None: if hass.is_stopping: return gw_status = True try: await api(gateway.get_gateway_info()) except RequestError: LOGGER.error("Keep-alive failed") gw_status = False async_dispatcher_send(hass, SIGNAL_GW, gw_status) entry.async_on_unload( async_track_time_interval(hass, async_keep_alive, timedelta(seconds=60))) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up this integration using UI.""" config_entry.async_on_unload(config_entry.add_update_listener(async_reload_entry)) setup_result = await async_initialize_integration(hass=hass, config_entry=config_entry) hacs: HacsBase = hass.data[DOMAIN] return setup_result and not hacs.system.disabled
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up AirVisual as config entry.""" if CONF_API_KEY in entry.data: _standardize_geography_config_entry(hass, entry) websession = aiohttp_client.async_get_clientsession(hass) cloud_api = CloudAPI(entry.data[CONF_API_KEY], session=websession) async def async_update_data() -> dict[str, Any]: """Get new data from the API.""" if CONF_CITY in entry.data: api_coro = cloud_api.air_quality.city( entry.data[CONF_CITY], entry.data[CONF_STATE], entry.data[CONF_COUNTRY], ) else: api_coro = cloud_api.air_quality.nearest_city( entry.data[CONF_LATITUDE], entry.data[CONF_LONGITUDE], ) try: data = await api_coro return cast(dict[str, Any], data) except (InvalidKeyError, KeyExpiredError) as ex: raise ConfigEntryAuthFailed from ex except AirVisualError as err: raise UpdateFailed( f"Error while retrieving data: {err}") from err coordinator = DataUpdateCoordinator( hass, LOGGER, name=async_get_geography_id(entry.data), # We give a placeholder update interval in order to create the coordinator; # then, below, we use the coordinator's presence (along with any other # coordinators using the same API key) to calculate an actual, leveled # update interval: update_interval=timedelta(minutes=5), update_method=async_update_data, ) # Only geography-based entries have options: entry.async_on_unload(entry.add_update_listener(async_reload_entry)) else: # Remove outdated air_quality entities from the entity registry if they exist: ent_reg = entity_registry.async_get(hass) for entity_entry in [ e for e in ent_reg.entities.values() if e.config_entry_id == entry.entry_id and e.entity_id.startswith("air_quality") ]: LOGGER.debug('Removing deprecated air_quality entity: "%s"', entity_entry.entity_id) ent_reg.async_remove(entity_entry.entity_id) _standardize_node_pro_config_entry(hass, entry) async def async_update_data() -> dict[str, Any]: """Get new data from the API.""" try: async with NodeSamba(entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD]) as node: data = await node.async_get_latest_measurements() return cast(dict[str, Any], data) except NodeProError as err: raise UpdateFailed( f"Error while retrieving data: {err}") from err coordinator = DataUpdateCoordinator( hass, LOGGER, name="Node/Pro data", update_interval=DEFAULT_NODE_PRO_UPDATE_INTERVAL, update_method=async_update_data, ) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = {DATA_COORDINATOR: coordinator} # Reassess the interval between 2 server requests if CONF_API_KEY in entry.data: async_sync_geo_coordinator_update_intervals(hass, entry.data[CONF_API_KEY]) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up from a config entry.""" http_session = async_get_clientsession(hass, verify_ssl=False) db_file = hass.config.path("music_assistant.db") # databases is really chatty with logging at info level logging.getLogger("databases").setLevel(logging.WARNING) conf = entry.options # TODO: adjust config flow to support creating multiple provider entries providers = [] if conf.get(CONF_SPOTIFY_ENABLED): providers.append( MusicProviderConfig( ProviderType.SPOTIFY, username=conf.get(CONF_SPOTIFY_USERNAME), password=conf.get(CONF_SPOTIFY_PASSWORD), )) if conf.get(CONF_QOBUZ_ENABLED): providers.append( MusicProviderConfig( ProviderType.QOBUZ, username=conf.get(CONF_QOBUZ_USERNAME), password=conf.get(CONF_QOBUZ_PASSWORD), )) if conf.get(CONF_TUNEIN_ENABLED): providers.append( MusicProviderConfig( ProviderType.TUNEIN, username=conf.get(CONF_TUNEIN_USERNAME), )) if conf.get(CONF_FILE_ENABLED) and conf.get(CONF_FILE_DIRECTORY): providers.append( MusicProviderConfig( ProviderType.FILESYSTEM_LOCAL, path=conf.get(CONF_FILE_DIRECTORY), )) stream_ip = get_local_ip_from_internal_url(hass) mass_conf = MassConfig(database_url=f"sqlite:///{db_file}", providers=providers, stream_ip=stream_ip) mass = MusicAssistant(mass_conf, session=http_session) try: await mass.setup() except MusicAssistantError as err: await mass.stop() LOGGER.exception(err) raise ConfigEntryNotReady from err except Exception as exc: # pylint: disable=broad-except await mass.stop() raise exc hass.data[DOMAIN] = mass # initialize platforms if conf.get(CONF_CREATE_MASS_PLAYERS, True): hass.config_entries.async_setup_platforms(entry, PLATFORMS) async def on_hass_start(*args, **kwargs): """Start sync actions when Home Assistant is started.""" register_services(hass, mass) # register hass players with mass await async_register_player_controls(hass, mass, entry) # start and schedule sync (every 3 hours) await mass.music.start_sync(schedule=3) async def on_hass_stop(event: Event): """Handle an incoming stop event from Home Assistant.""" await mass.stop() async def on_mass_event(event: MassEvent): """Handle an incoming event from Music Assistant.""" # forward event to the HA eventbus if hasattr(event.data, "to_dict"): data = event.data.to_dict() else: data = event.data hass.bus.async_fire( DOMAIN_EVENT, { "type": event.type.value, "object_id": event.object_id, "data": data }, ) # setup event listeners, register their unsubscribe in the unload entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)) entry.async_on_unload(async_at_start(hass, on_hass_start)) entry.async_on_unload(entry.add_update_listener(_update_listener)) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)) entry.async_on_unload(entry.add_update_listener(_update_listener)) entry.async_on_unload(mass.subscribe(on_mass_event, FORWARD_EVENTS)) # Websocket support and frontend (panel) async_register_websockets(hass) entry.async_on_unload(await async_register_panel(hass, entry.title)) # cleanup orphan devices/entities dev_reg = dr.async_get(hass) stored_devices = dr.async_entries_for_config_entry(dev_reg, entry.entry_id) if CONF_PLAYER_ENTITIES in entry.options: for device in stored_devices: for _, player_id in device.identifiers: if player_id not in entry.options[CONF_PLAYER_ENTITIES]: dev_reg.async_remove_device(device.id) elif not entry.options[CONF_CREATE_MASS_PLAYERS]: dev_reg.async_remove_device(device.id) return True
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up switches for UniFi Network integration. Switches are controlling network access and switch ports with POE. """ controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] controller.entities[DOMAIN] = { BLOCK_SWITCH: set(), POE_SWITCH: set(), DPI_SWITCH: set(), OUTLET_SWITCH: set(), } if controller.site_role != "admin": return # Store previously known POE control entities in case their POE are turned off. known_poe_clients = [] entity_registry = er.async_get(hass) for entry in er.async_entries_for_config_entry(entity_registry, config_entry.entry_id): if not entry.unique_id.startswith(POE_SWITCH): continue mac = entry.unique_id.replace(f"{POE_SWITCH}-", "") if mac not in controller.api.clients: continue known_poe_clients.append(mac) for mac in controller.option_block_clients: if mac not in controller.api.clients and mac in controller.api.clients_all: client = controller.api.clients_all[mac] controller.api.clients.process_raw([client.raw]) @callback def items_added( clients: set = controller.api.clients, devices: set = controller.api.devices, dpi_groups: set = controller.api.dpi_groups, ) -> None: """Update the values of the controller.""" add_outlet_entities(controller, async_add_entities, devices) if controller.option_block_clients: add_block_entities(controller, async_add_entities, clients) if controller.option_poe_clients: add_poe_entities(controller, async_add_entities, clients, known_poe_clients) if controller.option_dpi_restrictions: add_dpi_entities(controller, async_add_entities, dpi_groups) for signal in (controller.signal_update, controller.signal_options_update): config_entry.async_on_unload( async_dispatcher_connect(hass, signal, items_added)) items_added() known_poe_clients.clear()
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the Goodwe components from a config entry.""" hass.data.setdefault(DOMAIN, {}) name = entry.title host = entry.data[CONF_HOST] model_family = entry.data[CONF_MODEL_FAMILY] # Connect to Goodwe inverter try: inverter = await connect( host=host, family=model_family, retries=10, ) except InverterError as err: raise ConfigEntryNotReady from err device_info = DeviceInfo( configuration_url="https://www.semsportal.com", identifiers={(DOMAIN, inverter.serial_number)}, name=entry.title, manufacturer="GoodWe", model=inverter.model_name, sw_version=f"{inverter.software_version} ({inverter.arm_version})", ) async def async_update_data(): """Fetch data from the inverter.""" try: return await inverter.read_runtime_data() except RequestFailedException as ex: # UDP communication with inverter is by definition unreliable. # It is rather normal in many environments to fail to receive # proper response in usual time, so we intentionally ignore isolated # failures and report problem with availability only after # consecutive streak of 3 of failed requests. if ex.consecutive_failures_count < 3: _LOGGER.debug("No response received (streak of %d)", ex.consecutive_failures_count) # return empty dictionary, sensors will keep their previous values return {} # Inverter does not respond anymore (e.g. it went to sleep mode) _LOGGER.debug("Inverter not responding (streak of %d)", ex.consecutive_failures_count) raise UpdateFailed(ex) from ex except InverterError as ex: raise UpdateFailed(ex) from ex # Create update coordinator coordinator = DataUpdateCoordinator( hass, _LOGGER, name=name, update_method=async_update_data, # Polling interval. Will only be polled if there are subscribers. update_interval=SCAN_INTERVAL, ) # Fetch initial data so we have data when entities subscribe await coordinator.async_config_entry_first_refresh() hass.data[DOMAIN][entry.entry_id] = { KEY_INVERTER: inverter, KEY_COORDINATOR: coordinator, KEY_DEVICE_INFO: device_info, } entry.async_on_unload(entry.add_update_listener(update_listener)) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True