async def test_domain_control_not_async(hass, mock_entities): """Test domain verification in a service call with an unknown user.""" calls = [] def mock_service_log(call): """Define a protected service.""" calls.append(call) with pytest.raises(exceptions.HomeAssistantError): service.verify_domain_control(hass, "test_domain")(mock_service_log)
async def test_domain_control_unknown(hass, mock_entities): """Test domain verification in a service call with an unknown user.""" calls = [] async def mock_service_log(call): """Define a protected service.""" calls.append(call) with patch( "homeassistant.helpers.entity_registry.async_get", return_value=Mock(entities=mock_entities), ): protected_mock_service = service.verify_domain_control( hass, "test_domain")(mock_service_log) hass.services.async_register("test_domain", "test_service", protected_mock_service, schema=None) with pytest.raises(exceptions.UnknownUser): await hass.services.async_call( "test_domain", "test_service", {}, blocking=True, context=ha.Context(user_id="fake_user_id"), ) assert len(calls) == 0
async def async_setup_entry(hass, config_entry): """Set up Alexa Media Player as config entry.""" async def close_alexa_media(event=None) -> None: """Clean up Alexa connections.""" _LOGGER.debug("Received shutdown request: %s", event) for email, _ in (hass.data[DATA_ALEXAMEDIA]['accounts'].items()): await close_connections(hass, email) _verify_domain_control = verify_domain_control(hass, DOMAIN) if DATA_ALEXAMEDIA not in hass.data: hass.data[DATA_ALEXAMEDIA] = {} hass.data[DATA_ALEXAMEDIA]['accounts'] = {} from alexapy import AlexaLogin, __version__ as alexapy_version _LOGGER.info(STARTUP) _LOGGER.info("Loaded alexapy==%s", alexapy_version) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, close_alexa_media) account = config_entry.data email = account.get(CONF_EMAIL) password = account.get(CONF_PASSWORD) url = account.get(CONF_URL) login = AlexaLogin(url, email, password, hass.config.path, account.get(CONF_DEBUG)) if email not in hass.data[DATA_ALEXAMEDIA]['accounts']: hass.data[DATA_ALEXAMEDIA]['accounts'][email] = {} (hass.data[DATA_ALEXAMEDIA]['accounts'][email]['login_obj']) = login await login.login_with_cookie() await test_login_status(hass, config_entry, login, setup_platform_callback) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up OpenUV as config entry.""" hass.data.setdefault(DOMAIN, {DATA_CLIENT: {}, DATA_LISTENER: {}}) _verify_domain_control = verify_domain_control(hass, DOMAIN) try: websession = aiohttp_client.async_get_clientsession(hass) openuv = OpenUV( config_entry, Client( config_entry.data[CONF_API_KEY], config_entry.data.get(CONF_LATITUDE, hass.config.latitude), config_entry.data.get(CONF_LONGITUDE, hass.config.longitude), altitude=config_entry.data.get(CONF_ELEVATION, hass.config.elevation), session=websession, ), ) await openuv.async_update() hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = openuv except OpenUvError as err: LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady from err hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) @_verify_domain_control async def update_data(_: ServiceCall) -> None: """Refresh all OpenUV data.""" LOGGER.debug("Refreshing all OpenUV data") await openuv.async_update() async_dispatcher_send(hass, TOPIC_UPDATE) @_verify_domain_control async def update_uv_index_data(_: ServiceCall) -> None: """Refresh OpenUV UV index data.""" LOGGER.debug("Refreshing OpenUV UV index data") await openuv.async_update_uv_index_data() async_dispatcher_send(hass, TOPIC_UPDATE) @_verify_domain_control async def update_protection_data(_: ServiceCall) -> None: """Refresh OpenUV protection window data.""" LOGGER.debug("Refreshing OpenUV protection window data") await openuv.async_update_protection_data() async_dispatcher_send(hass, TOPIC_UPDATE) for service, method in ( ("update_data", update_data), ("update_uv_index_data", update_uv_index_data), ("update_protection_data", update_protection_data), ): hass.services.async_register(DOMAIN, service, method) return True
async def async_setup_entry(hass, config_entry): """Set up OpenUV as config entry.""" _verify_domain_control = verify_domain_control(hass, DOMAIN) try: websession = aiohttp_client.async_get_clientsession(hass) openuv = OpenUV( Client( config_entry.data[CONF_API_KEY], config_entry.data.get(CONF_LATITUDE, hass.config.latitude), config_entry.data.get(CONF_LONGITUDE, hass.config.longitude), websession, altitude=config_entry.data.get(CONF_ELEVATION, hass.config.elevation), )) await openuv.async_update() hass.data[DOMAIN][DATA_OPENUV_CLIENT][config_entry.entry_id] = openuv except OpenUvError as err: _LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady for component in ("binary_sensor", "sensor"): hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, component)) @_verify_domain_control async def update_data(service): """Refresh all OpenUV data.""" _LOGGER.debug("Refreshing all OpenUV data") await openuv.async_update() async_dispatcher_send(hass, TOPIC_UPDATE) hass.services.async_register(DOMAIN, "update_data", update_data) @_verify_domain_control async def update_uv_index_data(service): """Refresh OpenUV UV index data.""" _LOGGER.debug("Refreshing OpenUV UV index data") await openuv.async_update_uv_index_data() async_dispatcher_send(hass, TOPIC_UPDATE) hass.services.async_register(DOMAIN, "update_uv_index_data", update_uv_index_data) @_verify_domain_control async def update_protection_data(service): """Refresh OpenUV protection window data.""" _LOGGER.debug("Refreshing OpenUV protection window data") await openuv.async_update_protection_data() async_dispatcher_send(hass, TOPIC_UPDATE) hass.services.async_register(DOMAIN, "update_protection_data", update_protection_data) return True
async def async_setup_entry(hass, config_entry): """Set up OpenUV as config entry.""" from pyopenuv import Client from pyopenuv.errors import OpenUvError _verify_domain_control = verify_domain_control(hass, DOMAIN) try: websession = aiohttp_client.async_get_clientsession(hass) openuv = OpenUV( Client( config_entry.data[CONF_API_KEY], config_entry.data.get(CONF_LATITUDE, hass.config.latitude), config_entry.data.get(CONF_LONGITUDE, hass.config.longitude), websession, altitude=config_entry.data.get(CONF_ELEVATION, hass.config.elevation), ), config_entry.data.get(CONF_BINARY_SENSORS, {}).get(CONF_MONITORED_CONDITIONS, list(BINARY_SENSORS)), config_entry.data.get(CONF_SENSORS, {}).get(CONF_MONITORED_CONDITIONS, list(SENSORS)), ) await openuv.async_update() hass.data[DOMAIN][DATA_OPENUV_CLIENT][config_entry.entry_id] = openuv except OpenUvError as err: _LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady for component in ("binary_sensor", "sensor"): hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, component)) @_verify_domain_control async def update_data(service): """Refresh OpenUV data.""" _LOGGER.debug("Refreshing OpenUV data") try: await openuv.async_update() except OpenUvError as err: _LOGGER.error("Error during data update: %s", err) return async_dispatcher_send(hass, TOPIC_UPDATE) hass.services.async_register(DOMAIN, "update_data", update_data) return True
def async_register_services(hass: HomeAssistant) -> None: """Register services for Hue integration.""" async def hue_activate_scene(call: ServiceCall, skip_reload=True) -> None: """Handle activation of Hue scene.""" # Get parameters group_name = call.data[ATTR_GROUP_NAME] scene_name = call.data[ATTR_SCENE_NAME] transition = call.data.get(ATTR_TRANSITION) dynamic = call.data.get(ATTR_DYNAMIC, False) # Call the set scene function on each bridge tasks = [ hue_activate_scene_v1(bridge, group_name, scene_name, transition) if bridge.api_version == 1 else hue_activate_scene_v2( bridge, group_name, scene_name, transition, dynamic ) for bridge in hass.data[DOMAIN].values() if isinstance(bridge, HueBridge) ] results = await asyncio.gather(*tasks) # Did *any* bridge succeed? # Note that we'll get a "True" value for a successful call if True not in results: LOGGER.warning( "No bridge was able to activate scene %s in group %s", scene_name, group_name, ) if not hass.services.has_service(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE): # Register a local handler for scene activation hass.services.async_register( DOMAIN, SERVICE_HUE_ACTIVATE_SCENE, verify_domain_control(hass, DOMAIN)(hue_activate_scene), schema=vol.Schema( { vol.Required(ATTR_GROUP_NAME): cv.string, vol.Required(ATTR_SCENE_NAME): cv.string, vol.Optional(ATTR_TRANSITION): cv.positive_int, vol.Optional(ATTR_DYNAMIC): cv.boolean, } ), )
def _register_services(hass): """Register Hue services.""" async def hue_activate_scene(call, skip_reload=True): """Handle activation of Hue scene.""" # Get parameters group_name = call.data[ATTR_GROUP_NAME] scene_name = call.data[ATTR_SCENE_NAME] # Call the set scene function on each bridge tasks = [ bridge.hue_activate_scene( call.data, skip_reload=skip_reload, hide_warnings=skip_reload ) for bridge in hass.data[DOMAIN].values() if isinstance(bridge, HueBridge) ] results = await asyncio.gather(*tasks) # Did *any* bridge succeed? If not, refresh / retry # Note that we'll get a "None" value for a successful call if None not in results: if skip_reload: await hue_activate_scene(call, skip_reload=False) return _LOGGER.warning( "No bridge was able to activate " "scene %s in group %s", scene_name, group_name, ) if not hass.services.has_service(DOMAIN, SERVICE_HUE_SCENE): # Register a local handler for scene activation hass.services.async_register( DOMAIN, SERVICE_HUE_SCENE, verify_domain_control(hass, DOMAIN)(hue_activate_scene), schema=vol.Schema( { vol.Required(ATTR_GROUP_NAME): cv.string, vol.Required(ATTR_SCENE_NAME): cv.string, vol.Optional(ATTR_TRANSITION): cv.positive_int, } ), )
async def test_domain_control_unauthorized(hass, hass_read_only_user): """Test domain verification in a service call with an unauthorized user.""" mock_registry( hass, { "light.kitchen": ent_reg.RegistryEntry( entity_id="light.kitchen", unique_id="kitchen", platform="test_domain", ) }, ) calls = [] async def mock_service_log(call): """Define a protected service.""" calls.append(call) protected_mock_service = service.verify_domain_control( hass, "test_domain")(mock_service_log) hass.services.async_register("test_domain", "test_service", protected_mock_service, schema=None) with pytest.raises(exceptions.Unauthorized): await hass.services.async_call( "test_domain", "test_service", {}, blocking=True, context=ha.Context(user_id=hass_read_only_user.id), ) assert len(calls) == 0
async def test_domain_control_no_user(hass): """Test domain verification in a service call with no user.""" mock_registry( hass, { "light.kitchen": ent_reg.RegistryEntry( entity_id="light.kitchen", unique_id="kitchen", platform="test_domain", ) }, ) calls = [] async def mock_service_log(call): """Define a protected service.""" calls.append(call) protected_mock_service = service.verify_domain_control( hass, "test_domain")(mock_service_log) hass.services.async_register("test_domain", "test_service", protected_mock_service, schema=None) await hass.services.async_call( "test_domain", "test_service", {}, blocking=True, context=ha.Context(user_id=None), ) assert len(calls) == 1
async def async_setup_entry(hass, config_entry): """Set up RainMachine as config entry.""" if not config_entry.unique_id: hass.config_entries.async_update_entry( config_entry, unique_id=config_entry.data[CONF_IP_ADDRESS]) _verify_domain_control = verify_domain_control(hass, DOMAIN) websession = aiohttp_client.async_get_clientsession(hass) client = Client(session=websession) try: await client.load_local( config_entry.data[CONF_IP_ADDRESS], config_entry.data[CONF_PASSWORD], port=config_entry.data[CONF_PORT], ssl=config_entry.data.get(CONF_SSL, DEFAULT_SSL), ) except RainMachineError as err: _LOGGER.error("An error occurred: %s", err) raise ConfigEntryNotReady else: # regenmaschine can load multiple controllers at once, but we only grab the one # we loaded above: controller = next(iter(client.controllers.values())) rainmachine = RainMachine( hass, controller, config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN), config_entry.data.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL.total_seconds()), ) # Update the data object, which at this point (prior to any sensors registering # "interest" in the API), will focus on grabbing the latest program and zone data: await rainmachine.async_update() hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = rainmachine for component in ("binary_sensor", "sensor", "switch"): hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, component)) @_verify_domain_control async def disable_program(call): """Disable a program.""" await rainmachine.controller.programs.disable( call.data[CONF_PROGRAM_ID]) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def disable_zone(call): """Disable a zone.""" await rainmachine.controller.zones.disable(call.data[CONF_ZONE_ID]) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def enable_program(call): """Enable a program.""" await rainmachine.controller.programs.enable(call.data[CONF_PROGRAM_ID] ) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def enable_zone(call): """Enable a zone.""" await rainmachine.controller.zones.enable(call.data[CONF_ZONE_ID]) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def pause_watering(call): """Pause watering for a set number of seconds.""" await rainmachine.controller.watering.pause_all(call.data[CONF_SECONDS] ) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def start_program(call): """Start a particular program.""" await rainmachine.controller.programs.start(call.data[CONF_PROGRAM_ID]) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def start_zone(call): """Start a particular zone for a certain amount of time.""" await rainmachine.controller.zones.start(call.data[CONF_ZONE_ID], call.data[CONF_ZONE_RUN_TIME]) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def stop_all(call): """Stop all watering.""" await rainmachine.controller.watering.stop_all() await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def stop_program(call): """Stop a program.""" await rainmachine.controller.programs.stop(call.data[CONF_PROGRAM_ID]) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def stop_zone(call): """Stop a zone.""" await rainmachine.controller.zones.stop(call.data[CONF_ZONE_ID]) await rainmachine.async_update_programs_and_zones() @_verify_domain_control async def unpause_watering(call): """Unpause watering.""" await rainmachine.controller.watering.unpause_all() await rainmachine.async_update_programs_and_zones() for service, method, schema in [ ("disable_program", disable_program, SERVICE_ALTER_PROGRAM), ("disable_zone", disable_zone, SERVICE_ALTER_ZONE), ("enable_program", enable_program, SERVICE_ALTER_PROGRAM), ("enable_zone", enable_zone, SERVICE_ALTER_ZONE), ("pause_watering", pause_watering, SERVICE_PAUSE_WATERING), ("start_program", start_program, SERVICE_START_PROGRAM_SCHEMA), ("start_zone", start_zone, SERVICE_START_ZONE_SCHEMA), ("stop_all", stop_all, {}), ("stop_program", stop_program, SERVICE_STOP_PROGRAM_SCHEMA), ("stop_zone", stop_zone, SERVICE_STOP_ZONE_SCHEMA), ("unpause_watering", unpause_watering, {}), ]: hass.services.async_register(DOMAIN, service, method, schema=schema) return True
async def async_setup_entry(hass, config_entry): """Set up RainMachine as config entry.""" from regenmaschine import login from regenmaschine.errors import RainMachineError _verify_domain_control = verify_domain_control(hass, DOMAIN) websession = aiohttp_client.async_get_clientsession(hass) try: client = await login(config_entry.data[CONF_IP_ADDRESS], config_entry.data[CONF_PASSWORD], websession, port=config_entry.data[CONF_PORT], ssl=config_entry.data[CONF_SSL]) rainmachine = RainMachine( client, config_entry.data.get(CONF_BINARY_SENSORS, {}).get(CONF_MONITORED_CONDITIONS, list(BINARY_SENSORS)), config_entry.data.get(CONF_SENSORS, {}).get(CONF_MONITORED_CONDITIONS, list(SENSORS)), config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN)) await rainmachine.async_update() except RainMachineError as err: _LOGGER.error('An error occurred: %s', err) raise ConfigEntryNotReady hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = rainmachine for component in ('binary_sensor', 'sensor', 'switch'): hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, component)) async def refresh(event_time): """Refresh RainMachine sensor data.""" _LOGGER.debug('Updating RainMachine sensor data') await rainmachine.async_update() async_dispatcher_send(hass, SENSOR_UPDATE_TOPIC) hass.data[DOMAIN][DATA_LISTENER][ config_entry.entry_id] = async_track_time_interval( hass, refresh, timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL])) @_verify_domain_control async def disable_program(call): """Disable a program.""" await rainmachine.client.programs.disable(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) @_verify_domain_control async def disable_zone(call): """Disable a zone.""" await rainmachine.client.zones.disable(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) @_verify_domain_control async def enable_program(call): """Enable a program.""" await rainmachine.client.programs.enable(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) @_verify_domain_control async def enable_zone(call): """Enable a zone.""" await rainmachine.client.zones.enable(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) @_verify_domain_control async def pause_watering(call): """Pause watering for a set number of seconds.""" await rainmachine.client.watering.pause_all(call.data[CONF_SECONDS]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) @_verify_domain_control async def start_program(call): """Start a particular program.""" await rainmachine.client.programs.start(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) @_verify_domain_control async def start_zone(call): """Start a particular zone for a certain amount of time.""" await rainmachine.client.zones.start(call.data[CONF_ZONE_ID], call.data[CONF_ZONE_RUN_TIME]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) @_verify_domain_control async def stop_all(call): """Stop all watering.""" await rainmachine.client.watering.stop_all() async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) @_verify_domain_control async def stop_program(call): """Stop a program.""" await rainmachine.client.programs.stop(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) @_verify_domain_control async def stop_zone(call): """Stop a zone.""" await rainmachine.client.zones.stop(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) @_verify_domain_control async def unpause_watering(call): """Unpause watering.""" await rainmachine.client.watering.unpause_all() async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) for service, method, schema in [ ('disable_program', disable_program, SERVICE_ALTER_PROGRAM), ('disable_zone', disable_zone, SERVICE_ALTER_ZONE), ('enable_program', enable_program, SERVICE_ALTER_PROGRAM), ('enable_zone', enable_zone, SERVICE_ALTER_ZONE), ('pause_watering', pause_watering, SERVICE_PAUSE_WATERING), ('start_program', start_program, SERVICE_START_PROGRAM_SCHEMA), ('start_zone', start_zone, SERVICE_START_ZONE_SCHEMA), ('stop_all', stop_all, {}), ('stop_program', stop_program, SERVICE_STOP_PROGRAM_SCHEMA), ('stop_zone', stop_zone, SERVICE_STOP_ZONE_SCHEMA), ('unpause_watering', unpause_watering, {}), ]: hass.services.async_register(DOMAIN, service, method, schema=schema) return True
async def async_setup_entry(hass, config_entry): """Set up SimpliSafe as config entry.""" _verify_domain_control = verify_domain_control(hass, DOMAIN) websession = aiohttp_client.async_get_clientsession(hass) try: api = await API.login_via_token(config_entry.data[CONF_TOKEN], websession) except InvalidCredentialsError: _LOGGER.error("Invalid credentials provided") return False except SimplipyError as err: _LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady _async_save_refresh_token(hass, config_entry, api.refresh_token) systems = await api.get_systems() simplisafe = SimpliSafe(hass, config_entry, systems) await simplisafe.async_update() hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, "alarm_control_panel")) async def refresh(event_time): """Refresh data from the SimpliSafe account.""" await simplisafe.async_update() _LOGGER.debug("Updated data for all SimpliSafe systems") async_dispatcher_send(hass, TOPIC_UPDATE) hass.data[DOMAIN][DATA_LISTENER][ config_entry.entry_id] = async_track_time_interval( hass, refresh, timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL])) # Register the base station for each system: for system in systems.values(): hass.async_create_task( async_register_base_station(hass, system, config_entry.entry_id)) @_verify_domain_control async def remove_pin(call): """Remove a PIN.""" system = systems[int(call.data[ATTR_SYSTEM_ID])] await system.remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE]) @_verify_domain_control async def set_pin(call): """Set a PIN.""" system = systems[int(call.data[ATTR_SYSTEM_ID])] await system.set_pin(call.data[ATTR_PIN_LABEL], call.data[ATTR_PIN_VALUE]) for service, method, schema in [ ("remove_pin", remove_pin, SERVICE_REMOVE_PIN_SCHEMA), ("set_pin", set_pin, SERVICE_SET_PIN_SCHEMA), ]: hass.services.async_register(DOMAIN, service, method, schema=schema) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Elexa Guardian from a config entry.""" _verify_domain_control = verify_domain_control(hass, DOMAIN) guardian = Guardian(hass, entry) await guardian.async_update() hass.data[DOMAIN][DATA_CLIENT][entry.entry_id] = guardian for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) @_verify_domain_control async def disable_ap(call): """Disable the device's onboard access point.""" try: async with guardian.client: await guardian.client.device.wifi_disable_ap() except GuardianError as err: LOGGER.error("Error during service call: %s", err) return @_verify_domain_control async def enable_ap(call): """Enable the device's onboard access point.""" try: async with guardian.client: await guardian.client.device.wifi_enable_ap() except GuardianError as err: LOGGER.error("Error during service call: %s", err) return @_verify_domain_control async def reboot(call): """Reboot the device.""" try: async with guardian.client: await guardian.client.device.reboot() except GuardianError as err: LOGGER.error("Error during service call: %s", err) return @_verify_domain_control async def reset_valve_diagnostics(call): """Fully reset system motor diagnostics.""" try: async with guardian.client: await guardian.client.valve.valve_reset() except GuardianError as err: LOGGER.error("Error during service call: %s", err) return @_verify_domain_control async def upgrade_firmware(call): """Upgrade the device firmware.""" try: async with guardian.client: await guardian.client.device.upgrade_firmware( url=call.data[CONF_URL], port=call.data[CONF_PORT], filename=call.data[CONF_FILENAME], ) except GuardianError as err: LOGGER.error("Error during service call: %s", err) return for service, method, schema in [ ("disable_ap", disable_ap, None), ("enable_ap", enable_ap, None), ("reboot", reboot, None), ("reset_valve_diagnostics", reset_valve_diagnostics, None), ("upgrade_firmware", upgrade_firmware, SERVICE_UPGRADE_FIRMWARE_SCHEMA), ]: async_register_admin_service(hass, DOMAIN, service, method, schema=schema) return True
async def async_setup_entry(hass, config_entry): """Set up SimpliSafe as config entry.""" _verify_domain_control = verify_domain_control(hass, DOMAIN) websession = aiohttp_client.async_get_clientsession(hass) try: api = await API.login_via_token(config_entry.data[CONF_TOKEN], websession) except InvalidCredentialsError: _LOGGER.error("Invalid credentials provided") return False except SimplipyError as err: _LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady _async_save_refresh_token(hass, config_entry, api.refresh_token) systems = await api.get_systems() simplisafe = SimpliSafe(hass, api, systems, config_entry) await simplisafe.async_update() hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe for component in ("alarm_control_panel", "lock"): hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, component) ) async def refresh(event_time): """Refresh data from the SimpliSafe account.""" await simplisafe.async_update() _LOGGER.debug("Updated data for all SimpliSafe systems") async_dispatcher_send(hass, TOPIC_UPDATE) hass.data[DOMAIN][DATA_LISTENER][config_entry.entry_id] = async_track_time_interval( hass, refresh, timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL]) ) # Register the base station for each system: for system in systems.values(): hass.async_create_task( async_register_base_station(hass, system, config_entry.entry_id) ) @callback def verify_system_exists(coro): """Log an error if a service call uses an invalid system ID.""" async def decorator(call): """Decorate.""" system_id = int(call.data[ATTR_SYSTEM_ID]) if system_id not in systems: _LOGGER.error("Unknown system ID in service call: %s", system_id) return await coro(call) return decorator @callback def v3_only(coro): """Log an error if the decorated coroutine is called with a v2 system.""" async def decorator(call): """Decorate.""" system = 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 remove_pin(call): """Remove a PIN.""" system = systems[call.data[ATTR_SYSTEM_ID]] try: await system.remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE]) except SimplipyError as err: _LOGGER.error("Error during service call: %s", err) return @verify_system_exists @v3_only @_verify_domain_control async def set_alarm_duration(call): """Set the duration of a running alarm.""" system = systems[call.data[ATTR_SYSTEM_ID]] try: await system.set_alarm_duration(call.data[ATTR_SECONDS]) except SimplipyError as err: _LOGGER.error("Error during service call: %s", err) return @verify_system_exists @v3_only @_verify_domain_control async def set_delay(call): """Set the delay duration for entry/exit, away/home (any combo).""" system = systems[call.data[ATTR_SYSTEM_ID]] coro = getattr( system, f"set_{call.data[ATTR_TRANSITION]}_delay_{call.data[ATTR_ARRIVAL_STATE]}", ) try: await coro(call.data[ATTR_SECONDS]) except SimplipyError as err: _LOGGER.error("Error during service call: %s", err) return @verify_system_exists @v3_only @_verify_domain_control async def set_armed_light(call): """Turn the base station light on/off.""" system = systems[call.data[ATTR_SYSTEM_ID]] try: await system.set_light(call.data[ATTR_ARMED_LIGHT_STATE]) except SimplipyError as err: _LOGGER.error("Error during service call: %s", err) return @verify_system_exists @_verify_domain_control async def set_pin(call): """Set a PIN.""" system = systems[call.data[ATTR_SYSTEM_ID]] try: await system.set_pin(call.data[ATTR_PIN_LABEL], call.data[ATTR_PIN_VALUE]) except SimplipyError as err: _LOGGER.error("Error during service call: %s", err) return @verify_system_exists @v3_only @_verify_domain_control async def set_volume_property(call): """Set a volume parameter in an appropriate service call.""" system = systems[call.data[ATTR_SYSTEM_ID]] try: volume = V3Volume[call.data[ATTR_VOLUME]] except KeyError: _LOGGER.error("Unknown volume string: %s", call.data[ATTR_VOLUME]) return except SimplipyError as err: _LOGGER.error("Error during service call: %s", err) return else: coro = getattr(system, f"set_{call.data[ATTR_VOLUME_PROPERTY]}_volume") await coro(volume) for service, method, schema in [ ("remove_pin", remove_pin, SERVICE_REMOVE_PIN_SCHEMA), ("set_alarm_duration", set_alarm_duration, SERVICE_SET_DELAY_SCHEMA), ("set_delay", set_delay, SERVICE_SET_DELAY_SCHEMA), ("set_armed_light", set_armed_light, SERVICE_SET_LIGHT_SCHEMA), ("set_pin", set_pin, SERVICE_SET_PIN_SCHEMA), ("set_volume_property", set_volume_property, SERVICE_SET_VOLUME_SCHEMA), ]: hass.services.async_register(DOMAIN, service, method, schema=schema) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up RainMachine as config entry.""" hass.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = {} entry_updates = {} if not entry.unique_id: # If the config entry doesn't already have a unique ID, set one: entry_updates["unique_id"] = entry.data[CONF_IP_ADDRESS] 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) _verify_domain_control = verify_domain_control(hass, DOMAIN) 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: LOGGER.error("An error occurred: %s", 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] = next( iter(client.controllers.values())) 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) for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) @_verify_domain_control async def disable_program(call: ServiceCall): """Disable a program.""" await controller.programs.disable(call.data[CONF_PROGRAM_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def disable_zone(call: ServiceCall): """Disable a zone.""" await controller.zones.disable(call.data[CONF_ZONE_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def enable_program(call: ServiceCall): """Enable a program.""" await controller.programs.enable(call.data[CONF_PROGRAM_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def enable_zone(call: ServiceCall): """Enable a zone.""" await controller.zones.enable(call.data[CONF_ZONE_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def pause_watering(call: ServiceCall): """Pause watering for a set number of seconds.""" await controller.watering.pause_all(call.data[CONF_SECONDS]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def start_program(call: ServiceCall): """Start a particular program.""" await controller.programs.start(call.data[CONF_PROGRAM_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def start_zone(call: ServiceCall): """Start a particular zone for a certain amount of time.""" await controller.zones.start(call.data[CONF_ZONE_ID], call.data[CONF_ZONE_RUN_TIME]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def stop_all(call: ServiceCall): """Stop all watering.""" await controller.watering.stop_all() await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def stop_program(call: ServiceCall): """Stop a program.""" await controller.programs.stop(call.data[CONF_PROGRAM_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def stop_zone(call: ServiceCall): """Stop a zone.""" await controller.zones.stop(call.data[CONF_ZONE_ID]) await async_update_programs_and_zones(hass, entry) @_verify_domain_control async def unpause_watering(call: ServiceCall): """Unpause watering.""" await controller.watering.unpause_all() await async_update_programs_and_zones(hass, entry) for service, method, schema in [ ("disable_program", disable_program, SERVICE_ALTER_PROGRAM), ("disable_zone", disable_zone, SERVICE_ALTER_ZONE), ("enable_program", enable_program, SERVICE_ALTER_PROGRAM), ("enable_zone", enable_zone, SERVICE_ALTER_ZONE), ("pause_watering", pause_watering, SERVICE_PAUSE_WATERING), ("start_program", start_program, SERVICE_START_PROGRAM_SCHEMA), ("start_zone", start_zone, SERVICE_START_ZONE_SCHEMA), ("stop_all", stop_all, {}), ("stop_program", stop_program, SERVICE_STOP_PROGRAM_SCHEMA), ("stop_zone", stop_zone, SERVICE_STOP_ZONE_SCHEMA), ("unpause_watering", unpause_watering, {}), ]: hass.services.async_register(DOMAIN, service, method, schema=schema) hass.data[DOMAIN][DATA_LISTENER] = entry.add_update_listener( async_reload_entry) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up OpenUV as config entry.""" _verify_domain_control = verify_domain_control(hass, DOMAIN) websession = aiohttp_client.async_get_clientsession(hass) openuv = OpenUV( entry, Client( entry.data[CONF_API_KEY], entry.data.get(CONF_LATITUDE, hass.config.latitude), entry.data.get(CONF_LONGITUDE, hass.config.longitude), altitude=entry.data.get(CONF_ELEVATION, hass.config.elevation), session=websession, ), ) # We disable the client's request retry abilities here to avoid a lengthy (and # blocking) startup: openuv.client.disable_request_retries() try: await openuv.async_update() except HomeAssistantError as err: LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady from err # Once we've successfully authenticated, we re-enable client request retries: openuv.client.enable_request_retries() hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = openuv hass.config_entries.async_setup_platforms(entry, PLATFORMS) @_verify_domain_control async def update_data(_: ServiceCall) -> None: """Refresh all OpenUV data.""" LOGGER.debug("Refreshing all OpenUV data") await openuv.async_update() async_dispatcher_send(hass, TOPIC_UPDATE) @_verify_domain_control async def update_uv_index_data(_: ServiceCall) -> None: """Refresh OpenUV UV index data.""" LOGGER.debug("Refreshing OpenUV UV index data") await openuv.async_update_uv_index_data() async_dispatcher_send(hass, TOPIC_UPDATE) @_verify_domain_control async def update_protection_data(_: ServiceCall) -> None: """Refresh OpenUV protection window data.""" LOGGER.debug("Refreshing OpenUV protection window data") await openuv.async_update_protection_data() async_dispatcher_send(hass, TOPIC_UPDATE) for service, method in ( ("update_data", update_data), ("update_uv_index_data", update_uv_index_data), ("update_protection_data", update_protection_data), ): hass.services.async_register(DOMAIN, service, method) return True
async def async_setup_entry(hass, config_entry): """Set up SimpliSafe as config entry.""" _verify_domain_control = verify_domain_control(hass, DOMAIN) websession = aiohttp_client.async_get_clientsession(hass) try: api = await API.login_via_token(config_entry.data[CONF_TOKEN], websession) except InvalidCredentialsError: _LOGGER.error("Invalid credentials provided") return False except SimplipyError as err: _LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady _async_save_refresh_token(hass, config_entry, api.refresh_token) simplisafe = SimpliSafe(hass, api, config_entry) await simplisafe.async_init() hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe for component in ("alarm_control_panel", "lock"): hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, component)) @callback def verify_system_exists(coro): """Log an error if a service call uses an invalid system ID.""" async def decorator(call): """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): """Log an error if the decorated coroutine is called with a v2 system.""" async def decorator(call): """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 remove_pin(call): """Remove a PIN.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE]) except SimplipyError as err: _LOGGER.error("Error during service call: %s", err) return @verify_system_exists @_verify_domain_control async def set_pin(call): """Set a PIN.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.set_pin(call.data[ATTR_PIN_LABEL], call.data[ATTR_PIN_VALUE]) except SimplipyError as err: _LOGGER.error("Error during service call: %s", err) return @verify_system_exists @v3_only @_verify_domain_control async def set_system_properties(call): """Set one or more system parameters.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.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) return for service, method, schema in [ ("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) return True
async def async_setup_entry(hass, config_entry): # noqa: C901 """Set up SimpliSafe as config entry.""" hass.data[DOMAIN][DATA_LISTENER][config_entry.entry_id] = [] entry_updates = {} if not config_entry.unique_id: # If the config entry doesn't already have a unique ID, set one: entry_updates["unique_id"] = config_entry.data[CONF_USERNAME] if CONF_CODE in config_entry.data: # If an alarm code was provided as part of configuration.yaml, pop it out of # the config entry's data and move it to options: data = {**config_entry.data} entry_updates["data"] = data entry_updates["options"] = { **config_entry.options, CONF_CODE: data.pop(CONF_CODE), } if entry_updates: hass.config_entries.async_update_entry(config_entry, **entry_updates) _verify_domain_control = verify_domain_control(hass, DOMAIN) client_id = await async_get_client_id(hass) websession = aiohttp_client.async_get_clientsession(hass) try: api = await API.login_via_token(config_entry.data[CONF_TOKEN], client_id=client_id, session=websession) except InvalidCredentialsError: LOGGER.error("Invalid credentials provided") return False except SimplipyError as err: LOGGER.error("Config entry failed: %s", err) raise ConfigEntryNotReady from err _async_save_refresh_token(hass, config_entry, api.refresh_token) simplisafe = SimpliSafe(hass, api, config_entry) try: await simplisafe.async_init() except SimplipyError as err: raise ConfigEntryNotReady from err hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) @callback def verify_system_exists(coro): """Log an error if a service call uses an invalid system ID.""" async def decorator(call): """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): """Log an error if the decorated coroutine is called with a v2 system.""" async def decorator(call): """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): """Clear all active notifications.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.clear_notifications() except SimplipyError as err: LOGGER.error("Error during service call: %s", err) return @verify_system_exists @_verify_domain_control async def remove_pin(call): """Remove a PIN.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE]) except SimplipyError as err: LOGGER.error("Error during service call: %s", err) return @verify_system_exists @_verify_domain_control async def set_pin(call): """Set a PIN.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.set_pin(call.data[ATTR_PIN_LABEL], call.data[ATTR_PIN_VALUE]) except SimplipyError as err: LOGGER.error("Error during service call: %s", err) return @verify_system_exists @v3_only @_verify_domain_control async def set_system_properties(call): """Set one or more system parameters.""" system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]] try: await system.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) return 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) hass.data[DOMAIN][DATA_LISTENER][config_entry.entry_id].append( config_entry.add_update_listener(async_reload_entry)) 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