async def async_step_init(self, user_input=None): """Handle options flow.""" if self.config_entry.source == SOURCE_IMPORT: return await self.async_step_yaml(user_input) if user_input is not None: self.hk_options.update(user_input) return await self.async_step_include_exclude() self.hk_options = dict(self.config_entry.options) entity_filter = self.hk_options.get(CONF_FILTER, {}) homekit_mode = self.hk_options.get(CONF_HOMEKIT_MODE, DEFAULT_HOMEKIT_MODE) domains = entity_filter.get(CONF_INCLUDE_DOMAINS, []) include_entities = entity_filter.get(CONF_INCLUDE_ENTITIES) if include_entities: domains.extend(_domains_set_from_entities(include_entities)) return self.async_show_form( step_id="init", data_schema=vol.Schema( { vol.Required(CONF_HOMEKIT_MODE, default=homekit_mode): vol.In( HOMEKIT_MODES ), vol.Required( CONF_DOMAINS, default=domains, ): cv.multi_select(SUPPORTED_DOMAINS), } ), )
async def async_step_cameras(self, user_input=None): """Choose camera config.""" if user_input is not None: entity_config = self.hk_options[CONF_ENTITY_CONFIG] for entity_id in self.included_cameras: if entity_id in user_input[CONF_CAMERA_COPY]: entity_config.setdefault(entity_id, {})[ CONF_VIDEO_CODEC ] = VIDEO_CODEC_COPY elif ( entity_id in entity_config and CONF_VIDEO_CODEC in entity_config[entity_id] ): del entity_config[entity_id][CONF_VIDEO_CODEC] return await self.async_step_advanced() cameras_with_copy = [] entity_config = self.hk_options.setdefault(CONF_ENTITY_CONFIG, {}) for entity in self.included_cameras: hk_entity_config = entity_config.get(entity, {}) if hk_entity_config.get(CONF_VIDEO_CODEC) == VIDEO_CODEC_COPY: cameras_with_copy.append(entity) data_schema = vol.Schema( { vol.Optional( CONF_CAMERA_COPY, default=cameras_with_copy, ): cv.multi_select(self.included_cameras), } ) return self.async_show_form(step_id="cameras", data_schema=data_schema)
async def async_step_public_weather_areas(self, user_input=None): """Manage configuration of Netatmo public weather areas.""" errors = {} if user_input is not None: new_client = user_input.pop(CONF_NEW_AREA, None) areas = user_input.pop(CONF_WEATHER_AREAS, None) user_input[CONF_WEATHER_AREAS] = { area: self.options[CONF_WEATHER_AREAS][area] for area in areas } self.options.update(user_input) if new_client: return await self.async_step_public_weather( user_input={CONF_NEW_AREA: new_client}) return self._create_options_entry() weather_areas = list(self.options[CONF_WEATHER_AREAS]) data_schema = vol.Schema({ vol.Optional( CONF_WEATHER_AREAS, default=weather_areas, ): cv.multi_select(weather_areas), vol.Optional(CONF_NEW_AREA): str, }) return self.async_show_form( step_id="public_weather_areas", data_schema=data_schema, errors=errors, )
async def async_step_client_control(self, user_input=None): """Manage configuration of network access controlled clients.""" errors = {} if user_input is not None: self.options.update(user_input) return await self.async_step_statistics_sensors() clients_to_block = {} for client in self.controller.api.clients.values(): clients_to_block[ client. mac] = f"{client.name or client.hostname} ({client.mac})" return self.async_show_form( step_id="client_control", data_schema=vol.Schema({ vol.Optional(CONF_BLOCK_CLIENT, default=self.options[CONF_BLOCK_CLIENT]): cv.multi_select(clients_to_block), vol.Optional( CONF_POE_CLIENTS, default=self.options.get(CONF_POE_CLIENTS, DEFAULT_POE_CLIENTS), ): bool, vol.Optional( CONF_DPI_RESTRICTIONS, default=self.options.get(CONF_DPI_RESTRICTIONS, DEFAULT_DPI_RESTRICTIONS), ): bool, }), errors=errors, last_step=False, )
async def async_step_options_2(self, user_input=None): """Manage the options 2.""" if user_input is not None: self.options.update(user_input) return await self._update_options() return self.async_show_form( step_id="options_2", data_schema=vol.Schema({ vol.Optional( CONF_STRING, default=self.config_entry.options.get( CONF_STRING, "Default", ), ): str, vol.Optional( CONF_SELECT, default=self.config_entry.options.get( CONF_SELECT, "default"), ): vol.In(["default", "other"]), vol.Optional( CONF_MULTISELECT, default=self.config_entry.options.get( CONF_MULTISELECT, ["default"]), ): cv.multi_select({ "default": "Default", "other": "Other" }), }), )
async def async_step_simple_options(self, user_input=None): """For users without advanced settings enabled.""" if user_input is not None: self.options.update(user_input) return await self._update_options() clients_to_block = {} for client in self.controller.api.clients.values(): clients_to_block[ client. mac] = f"{client.name or client.hostname} ({client.mac})" return self.async_show_form( step_id="simple_options", data_schema=vol.Schema({ vol.Optional( CONF_TRACK_CLIENTS, default=self.controller.option_track_clients, ): bool, vol.Optional( CONF_TRACK_DEVICES, default=self.controller.option_track_devices, ): bool, vol.Optional(CONF_BLOCK_CLIENT, default=self.options[CONF_BLOCK_CLIENT]): cv.multi_select(clients_to_block), }), last_step=True, )
def test_multi_select_in_serializer(): """Test multi_select with custom_serializer.""" assert cv.custom_serializer(cv.multi_select({"paulus": "Paulus"})) == { "type": "multi_select", "options": { "paulus": "Paulus" }, }
async def async_step_init(self, user_input=None): """Handle options flow.""" if self.config_entry.state is not config_entries.ConfigEntryState.LOADED: _LOGGER.error("Tuya integration not yet loaded") return self.async_abort(reason=RESULT_CONN_ERROR) if user_input is not None: dev_ids = user_input.get(CONF_LIST_DEVICES) if dev_ids: return await self.async_step_device(None, dev_ids) user_input.pop(CONF_LIST_DEVICES, []) return self._save_config(data=user_input) data_schema = vol.Schema( { vol.Optional( CONF_DISCOVERY_INTERVAL, default=self.config_entry.options.get( CONF_DISCOVERY_INTERVAL, DEFAULT_DISCOVERY_INTERVAL ), ): vol.All(vol.Coerce(int), vol.Clamp(min=30, max=900)), } ) query_devices = self._get_tuya_devices_filtered( TUYA_TYPE_NOT_QUERY, True, False ) if query_devices: devices = {ENTITY_MATCH_NONE: "Default"} devices.update(query_devices) def_val = self.config_entry.options.get(CONF_QUERY_DEVICE) if not def_val or not query_devices.get(def_val): def_val = ENTITY_MATCH_NONE data_schema = data_schema.extend( { vol.Optional( CONF_QUERY_INTERVAL, default=self.config_entry.options.get( CONF_QUERY_INTERVAL, DEFAULT_QUERY_INTERVAL ), ): vol.All(vol.Coerce(int), vol.Clamp(min=30, max=240)), vol.Optional(CONF_QUERY_DEVICE, default=def_val): vol.In(devices), } ) config_devices = self._get_tuya_devices_filtered(TUYA_TYPE_CONFIG, False, True) if config_devices: data_schema = data_schema.extend( {vol.Optional(CONF_LIST_DEVICES): cv.multi_select(config_devices)} ) return self.async_show_form( step_id="init", data_schema=data_schema, errors=self._get_form_error(), )
async def async_step_device_tracker(self, user_input=None): """Manage the device tracker options.""" if user_input is not None: self.options.update(user_input) return await self.async_step_client_control() ssids = (set(self.controller.api.wlans) | { f"{wlan.name}{wlan.name_combine_suffix}" for wlan in self.controller.api.wlans.values() if not wlan.name_combine_enabled } | { wlan["name"] for ap in self.controller.api.devices.values() for wlan in ap.wlan_overrides if "name" in wlan }) ssid_filter = {ssid: ssid for ssid in sorted(ssids)} return self.async_show_form( step_id="device_tracker", data_schema=vol.Schema({ vol.Optional( CONF_TRACK_CLIENTS, default=self.controller.option_track_clients, ): bool, vol.Optional( CONF_TRACK_WIRED_CLIENTS, default=self.controller.option_track_wired_clients, ): bool, vol.Optional( CONF_TRACK_DEVICES, default=self.controller.option_track_devices, ): bool, vol.Optional(CONF_SSID_FILTER, default=self.controller.option_ssid_filter): cv.multi_select(ssid_filter), vol.Optional( CONF_DETECTION_TIME, default=int(self.controller.option_detection_time.total_seconds( )), ): int, vol.Optional( CONF_IGNORE_WIRED_BUG, default=self.controller.option_ignore_wired_bug, ): bool, }), last_step=False, )
async def async_step_user(self, user_input=None): """Handle a flow initialized by the user.""" if user_input is not None: return self.async_create_entry(title="", data=user_input) return self.async_show_form( step_id="user", data_schema=vol.Schema({ vol.Optional(CONF_IGNORED_SOURCES, default=self.ignored_sources): cv.multi_select(self.source_list) }), )
async def async_step_user(self, user_input: ConfigType | None = None ) -> FlowResult: """Manage the device tracker options.""" if user_input is not None: return self.async_create_entry(title="", data=user_input) options = vol.Schema( { vol.Required( CONF_SCAN_INTERVAL, default=self.config_entry.options.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL), ): int, vol.Required( CONF_CONSIDER_HOME, default=self.config_entry.options.get( CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME), ): int, vol.Required( CONF_INTERFACES, default=self.config_entry.options.get( CONF_INTERFACES, [DEFAULT_INTERFACE]), ): cv.multi_select(self._interface_options), vol.Optional( CONF_TRY_HOTSPOT, default=self.config_entry.options.get( CONF_TRY_HOTSPOT, True), ): bool, vol.Optional( CONF_INCLUDE_ARP, default=self.config_entry.options.get( CONF_INCLUDE_ARP, True), ): bool, vol.Optional( CONF_INCLUDE_ASSOCIATED, default=self.config_entry.options.get( CONF_INCLUDE_ASSOCIATED, True), ): bool, }) return self.async_show_form(step_id="user", data_schema=options)
async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Manage the options.""" effects = {source: source for source in const.KEY_COMPONENTID_EXTERNAL_SOURCES} async with self._create_client() as hyperion_client: if not hyperion_client: return self.async_abort(reason="cannot_connect") for effect in hyperion_client.effects or []: if const.KEY_NAME in effect: effects[effect[const.KEY_NAME]] = effect[const.KEY_NAME] # If a new effect is added to Hyperion, we always want it to show by default. So # rather than store a 'show list' in the config entry, we store a 'hide list'. # However, it's more intuitive to ask the user to select which effects to show, # so we inverse the meaning prior to storage. if user_input is not None: effect_show_list = user_input.pop(CONF_EFFECT_SHOW_LIST) user_input[CONF_EFFECT_HIDE_LIST] = sorted( set(effects) - set(effect_show_list) ) return self.async_create_entry(title="", data=user_input) default_effect_show_list = list( set(effects) - set(self._config_entry.options.get(CONF_EFFECT_HIDE_LIST, [])) ) return self.async_show_form( step_id="init", data_schema=vol.Schema( { vol.Optional( CONF_PRIORITY, default=self._config_entry.options.get( CONF_PRIORITY, DEFAULT_PRIORITY ), ): vol.All(vol.Coerce(int), vol.Range(min=0, max=255)), vol.Optional( CONF_EFFECT_SHOW_LIST, default=default_effect_show_list, ): cv.multi_select(effects), } ), )
def test_multi_select(): """Test multi select validation. Expected behavior: - Will not accept any input but a list - Will not accept selections outside of configured scope """ schema = vol.Schema( cv.multi_select({ "paulus": "Paulus", "robban": "Robban" })) with pytest.raises(vol.Invalid): schema("robban") schema(["paulus", "martinhj"]) schema(["robban", "paulus"])
def _resource_schema_base(available_resources, selected_resources): """Resource selection schema.""" known_available_resources = { sensor_id: sensor[SENSOR_NAME] for sensor_id, sensor in SENSOR_TYPES.items() if sensor_id in available_resources } if KEY_STATUS in known_available_resources: known_available_resources[KEY_STATUS_DISPLAY] = SENSOR_TYPES[ KEY_STATUS_DISPLAY ][SENSOR_NAME] return { vol.Required(CONF_RESOURCES, default=selected_resources): cv.multi_select( known_available_resources ) }
async def async_step_user(self, user_input=None): """Choose specific domains in bridge mode.""" if user_input is not None: entity_filter = _EMPTY_ENTITY_FILTER.copy() entity_filter[CONF_INCLUDE_DOMAINS] = user_input[CONF_INCLUDE_DOMAINS] self.hk_data[CONF_FILTER] = entity_filter return await self.async_step_pairing() self.hk_data[CONF_HOMEKIT_MODE] = HOMEKIT_MODE_BRIDGE default_domains = [] if self._async_current_names() else DEFAULT_DOMAINS return self.async_show_form( step_id="user", data_schema=vol.Schema( { vol.Required( CONF_INCLUDE_DOMAINS, default=default_domains ): cv.multi_select(SUPPORTED_DOMAINS), } ), )
async def async_step_init(self, user_input=None): """Manage the options.""" errors = {} if not self.departure_filters: departure_list = {} self.hub = self.opp.data[DOMAIN][self.config_entry.entry_id] try: departure_list = await self.hub.gti.departureList( { "station": self.config_entry.data[CONF_STATION], "time": {"date": "heute", "time": "jetzt"}, "maxList": 5, "maxTimeOffset": 200, "useRealtime": True, "returnFilters": True, } ) except CannotConnect: errors["base"] = "cannot_connect" except InvalidAuth: errors["base"] = "invalid_auth" if not errors: self.departure_filters = { str(i): departure_filter for i, departure_filter in enumerate(departure_list.get("filter")) } if user_input is not None and not errors: options = { CONF_FILTER: [ self.departure_filters[x] for x in user_input[CONF_FILTER] ], CONF_OFFSET: user_input[CONF_OFFSET], CONF_REAL_TIME: user_input[CONF_REAL_TIME], } return self.async_create_entry(title="", data=options) if CONF_FILTER in self.config_entry.options: old_filter = [ i for (i, f) in self.departure_filters.items() if f in self.config_entry.options.get(CONF_FILTER) ] else: old_filter = [] return self.async_show_form( step_id="init", data_schema=vol.Schema( { vol.Optional(CONF_FILTER, default=old_filter): cv.multi_select( { key: f"{departure_filter['serviceName']}, {departure_filter['label']}" for key, departure_filter in self.departure_filters.items() } ), vol.Required( CONF_OFFSET, default=self.config_entry.options.get(CONF_OFFSET, 0), ): cv.positive_int, vol.Optional( CONF_REAL_TIME, default=self.config_entry.options.get(CONF_REAL_TIME, True), ): bool, } ), errors=errors, )
async def async_step_prompt_options(self, user_input=None): """Prompt for options.""" errors = {} if user_input is not None: self._global_options = { CONF_AUTOMATIC_ADD: user_input[CONF_AUTOMATIC_ADD], } if CONF_DEVICE in user_input: entry_id = user_input[CONF_DEVICE] device_data = self._get_device_data(entry_id) self._selected_device_entry_id = entry_id event_code = device_data[CONF_EVENT_CODE] self._selected_device_event_code = event_code self._selected_device = self._config_entry.data[CONF_DEVICES][ event_code] self._selected_device_object = get_rfx_object(event_code) return await self.async_step_set_device_options() if CONF_REMOVE_DEVICE in user_input: remove_devices = user_input[CONF_REMOVE_DEVICE] devices = {} for entry_id in remove_devices: device_data = self._get_device_data(entry_id) event_code = device_data[CONF_EVENT_CODE] device_id = device_data[CONF_DEVICE_ID] self.opp.helpers.dispatcher.async_dispatcher_send( f"{DOMAIN}_{CONF_REMOVE_DEVICE}_{device_id}") self._device_registry.async_remove_device(entry_id) if event_code is not None: devices[event_code] = None self.update_config_data(global_options=self._global_options, devices=devices) return self.async_create_entry(title="", data={}) if CONF_EVENT_CODE in user_input: self._selected_device_event_code = user_input[CONF_EVENT_CODE] self._selected_device = {} selected_device_object = get_rfx_object( self._selected_device_event_code) if selected_device_object is None: errors[CONF_EVENT_CODE] = "invalid_event_code" elif not self._can_add_device(selected_device_object): errors[CONF_EVENT_CODE] = "already_configured_device" else: self._selected_device_object = selected_device_object return await self.async_step_set_device_options() if not errors: self.update_config_data(global_options=self._global_options) return self.async_create_entry(title="", data={}) device_registry = await async_get_device_registry(self.opp) device_entries = async_entries_for_config_entry( device_registry, self._config_entry.entry_id) self._device_registry = device_registry self._device_entries = device_entries remove_devices = { entry.id: entry.name_by_user if entry.name_by_user else entry.name for entry in device_entries } configure_devices = { entry.id: entry.name_by_user if entry.name_by_user else entry.name for entry in device_entries if self._get_device_event_code(entry.id) is not None } options = { vol.Optional( CONF_AUTOMATIC_ADD, default=self._config_entry.data[CONF_AUTOMATIC_ADD], ): bool, vol.Optional(CONF_EVENT_CODE): str, vol.Optional(CONF_DEVICE): vol.In(configure_devices), vol.Optional(CONF_REMOVE_DEVICE): cv.multi_select(remove_devices), } return self.async_show_form(step_id="prompt_options", data_schema=vol.Schema(options), errors=errors)