def _test_selector( selector_type, schema, valid_selections, invalid_selections, converter=None ): """Help test a selector.""" def default_converter(x): return x if converter is None: converter = default_converter # Validate selector configuration selector.validate_selector({selector_type: schema}) # Use selector in schema and validate vol_schema = vol.Schema({"selection": selector.selector({selector_type: schema})}) for selection in valid_selections: assert vol_schema({"selection": selection}) == { "selection": converter(selection) } for selection in invalid_selections: with pytest.raises(vol.Invalid): vol_schema({"selection": selection}) # Serialize selector selector_instance = selector.selector({selector_type: schema}) assert cv.custom_serializer(selector_instance) == { "selector": {selector_type: selector_instance.config} }
async def async_step_adv(self, user_input=None): """Handle getting advanced config options from the user.""" if user_input is not None: self.data.update(user_input) errors = validate_config(user_input) if not errors: # config complete, store entry self.data.update(user_input) hide_player_entities( self.hass, self.data[CONF_PLAYER_ENTITIES], self.data[CONF_HIDE_SOURCE_PLAYERS], ) return self.async_create_entry(title=DEFAULT_NAME, data={**self.data}) return self.async_show_form( step_id="adv", data_schema=vol.Schema({ vol.Required( CONF_HIDE_SOURCE_PLAYERS, default=self.data[CONF_HIDE_SOURCE_PLAYERS], ): selector.selector({"boolean": {}}), vol.Required( CONF_CREATE_MASS_PLAYERS, default=self.data[CONF_CREATE_MASS_PLAYERS], ): selector.selector({"boolean": {}}), }), last_step=True, )
def basic_group_options_schema(domain: str) -> vol.Schema: """Generate options schema.""" return vol.Schema( { vol.Required(CONF_ENTITIES): selector.selector( {"entity": {"domain": domain, "multiple": True}} ), vol.Required(CONF_HIDE_MEMBERS, default=False): selector.selector( {"boolean": {}} ), } )
async def async_step_secure_manual(self, user_input: dict | None = None ) -> FlowResult: """Configure ip secure manually.""" errors: dict = {} if user_input is not None: assert self._tunneling_config entry_data: KNXConfigEntryData = { **self._tunneling_config, # type: ignore[misc] CONF_KNX_SECURE_USER_ID: user_input[CONF_KNX_SECURE_USER_ID], CONF_KNX_SECURE_USER_PASSWORD: user_input[CONF_KNX_SECURE_USER_PASSWORD], CONF_KNX_SECURE_DEVICE_AUTHENTICATION: user_input[CONF_KNX_SECURE_DEVICE_AUTHENTICATION], CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, } return self.async_create_entry( title= f"Secure {CONF_KNX_TUNNELING.capitalize()} @ {self._tunneling_config[CONF_HOST]}", data=entry_data, ) fields = { vol.Required(CONF_KNX_SECURE_USER_ID, default=2): vol.All( selector.selector( {"number": { "min": 1, "max": 127, "mode": "box" }}), vol.Coerce(int), ), vol.Required(CONF_KNX_SECURE_USER_PASSWORD): selector.selector({"text": { "type": "password" }}), vol.Required(CONF_KNX_SECURE_DEVICE_AUTHENTICATION): selector.selector({"text": { "type": "password" }}), } return self.async_show_form(step_id="secure_manual", data_schema=vol.Schema(fields), errors=errors)
async def async_step_secure_knxkeys(self, user_input: dict | None = None ) -> FlowResult: """Configure secure knxkeys used to authenticate.""" errors = {} if user_input is not None: try: assert self._tunneling_config storage_key: str = (CONST_KNX_STORAGE_KEY + user_input[CONF_KNX_KNXKEY_FILENAME]) load_key_ring( self.hass.config.path( STORAGE_DIR, storage_key, ), user_input[CONF_KNX_KNXKEY_PASSWORD], ) entry_data: KNXConfigEntryData = { **self._tunneling_config, # type: ignore[misc] CONF_KNX_KNXKEY_FILENAME: storage_key, CONF_KNX_KNXKEY_PASSWORD: user_input[CONF_KNX_KNXKEY_PASSWORD], CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, } return self.async_create_entry( title= f"Secure {CONF_KNX_TUNNELING.capitalize()} @ {self._tunneling_config[CONF_HOST]}", data=entry_data, ) except InvalidSignature: errors["base"] = "invalid_signature" except FileNotFoundError: errors["base"] = "file_not_found" fields = { vol.Required(CONF_KNX_KNXKEY_FILENAME): selector.selector({"text": {}}), vol.Required(CONF_KNX_KNXKEY_PASSWORD): selector.selector({"text": {}}), } return self.async_show_form(step_id="secure_knxkeys", data_schema=vol.Schema(fields), errors=errors)
def get_players_schema(hass: HomeAssistant, cur_conf: dict) -> vol.Schema: """Return player config schema.""" control_entities = hass.states.async_entity_ids(MP_DOMAIN) # filter any non existing device id's from the list to prevent errors cur_ids = [ item for item in cur_conf[CONF_PLAYER_ENTITIES] if item in control_entities ] # blacklist unsupported and mass entities exclude_entities = [] for entity_id in control_entities: if entity_id in cur_ids: continue entity_comp = hass.data.get(DATA_INSTANCES, {}).get(MP_DOMAIN) entity: MediaPlayerEntity = entity_comp.get_entity(entity_id) if (not entity or entity.platform.platform_name == DOMAIN or entity.platform.platform_name in BLACKLIST_DOMAINS): exclude_entities.append(entity_id) continue # require some basic features, most important `play_media` if not (entity.support_play_media and entity.support_play and entity.support_volume_set): exclude_entities.append(entity_id) return vol.Schema({ vol.Optional(CONF_PLAYER_ENTITIES, default=cur_ids): selector.selector({ "entity": { "domain": "media_player", "multiple": True, "exclude_entities": exclude_entities, } }) })
def _show_form_user( self, user_input: dict[str, Any] | None = None, errors: dict[str, Any] | None = None, ) -> FlowResult: if user_input is None: user_input = {} return self.async_show_form( step_id="user", data_schema=vol.Schema( { vol.Required( CONF_NAME, default=user_input.get(CONF_NAME, "") ): cv.string, vol.Required( CONF_API_KEY, default=user_input.get(CONF_API_KEY, "") ): cv.string, vol.Required( CONF_FUEL_TYPES, default=user_input.get(CONF_FUEL_TYPES, list(FUEL_TYPES)), ): cv.multi_select(FUEL_TYPES), vol.Required( CONF_LOCATION, default=user_input.get( CONF_LOCATION, { "latitude": self.hass.config.latitude, "longitude": self.hass.config.longitude, }, ), ): selector({"location": {}}), vol.Required( CONF_RADIUS, default=user_input.get(CONF_RADIUS, DEFAULT_RADIUS) ): selector( { "number": { "min": 0.1, "max": 25, "step": 0.1, CONF_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS, } } ), } ), errors=errors, )
def test_selector_in_serializer(): """Test selector with custom_serializer.""" assert cv.custom_serializer(selector.selector({"text": {}})) == { "selector": { "text": { "multiline": False, } } }
def binary_sensor_options_schema( handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, options: dict[str, Any], ) -> vol.Schema: """Generate options schema.""" return basic_group_options_schema("binary_sensor", handler, options).extend( { vol.Required(CONF_ALL, default=False): selector.selector({"boolean": {}}), } )
async def async_step_arrival_time(self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Configure arrival time.""" if user_input is not None: self._config[CONF_ARRIVAL_TIME] = user_input[CONF_ARRIVAL_TIME] return self.async_create_entry(title="", data=self._config) options = {"arrival_time": selector({TimeSelector.selector_type: {}})} return self.async_show_form(step_id="arrival_time", data_schema=vol.Schema(options))
def light_switch_options_schema( domain: str, handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, options: dict[str, Any], ) -> vol.Schema: """Generate options schema.""" return basic_group_options_schema(domain, handler, options).extend( { vol.Required( CONF_ALL, default=False, description={"advanced": True} ): selector.selector({"boolean": {}}), } )
def _test_selector( selector_type, schema, valid_selections, invalid_selections, converter=None ): """Help test a selector.""" def default_converter(x): return x if converter is None: converter = default_converter # Validate selector configuration config = {selector_type: schema} selector.validate_selector(config) selector_instance = selector.selector(config) # We do not allow enums in the config, as they cannot serialize assert not any(isinstance(val, Enum) for val in selector_instance.config.values()) # Use selector in schema and validate vol_schema = vol.Schema({"selection": selector_instance}) for selection in valid_selections: assert vol_schema({"selection": selection}) == { "selection": converter(selection) } for selection in invalid_selections: with pytest.raises(vol.Invalid): vol_schema({"selection": selection}) # Serialize selector selector_instance = selector.selector({selector_type: schema}) assert ( selector.selector(selector_instance.serialize()["selector"]).config == selector_instance.config ) # Test serialized selector can be dumped to YAML yaml.dump(selector_instance.serialize())
async def async_step_destination_entity( self, user_input: dict[str, Any] | None = None, ) -> FlowResult: """Configure destination by using an entity.""" if user_input is not None: self._config[CONF_DESTINATION_ENTITY_ID] = user_input[ CONF_DESTINATION_ENTITY_ID] return self.async_create_entry(title=self._config[CONF_NAME], data=self._config) schema = vol.Schema({ CONF_DESTINATION_ENTITY_ID: selector({EntitySelector.selector_type: {}}) }) return self.async_show_form(step_id="destination_entity", data_schema=schema)
async def async_step_destination_coordinates( self, user_input: dict[str, Any] | None = None, ) -> FlowResult: """Configure destination by using gps coordinates.""" if user_input is not None: self._config[CONF_DESTINATION_LATITUDE] = user_input[ "destination"]["latitude"] self._config[CONF_DESTINATION_LONGITUDE] = user_input[ "destination"]["longitude"] return self.async_create_entry(title=self._config[CONF_NAME], data=self._config) schema = vol.Schema( {"destination": selector({LocationSelector.selector_type: {}})}) return self.async_show_form(step_id="destination_coordinates", data_schema=schema)
async def async_step_origin_coordinates(self, user_input: dict[str, Any] | None = None) -> FlowResult: """Configure origin by using gps coordinates.""" if user_input is not None: self._config[CONF_ORIGIN_LATITUDE] = user_input["origin"][ "latitude"] self._config[CONF_ORIGIN_LONGITUDE] = user_input["origin"][ "longitude"] return self.async_show_menu( step_id="destination_menu", menu_options=["destination_coordinates", "destination_entity"], ) schema = vol.Schema( {"origin": selector({LocationSelector.selector_type: {}})}) return self.async_show_form(step_id="origin_coordinates", data_schema=schema)
async def async_step_origin_entity(self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Configure origin by using an entity.""" if user_input is not None: self._config[CONF_ORIGIN_ENTITY_ID] = user_input[ CONF_ORIGIN_ENTITY_ID] return self.async_show_menu( step_id="destination_menu", menu_options=["destination_coordinates", "destination_entity"], ) schema = vol.Schema({ CONF_ORIGIN_ENTITY_ID: selector({EntitySelector.selector_type: {}}) }) return self.async_show_form(step_id="origin_entity", data_schema=schema)
def basic_group_options_schema( domain: str, handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, options: dict[str, Any], ) -> vol.Schema: """Generate options schema.""" handler = cast(SchemaOptionsFlowHandler, handler) return vol.Schema( { vol.Required(CONF_ENTITIES): entity_selector_without_own_entities( handler, {"domain": domain, "multiple": True} ), vol.Required(CONF_HIDE_MEMBERS, default=False): selector.selector( {"boolean": {}} ), } )
def _get_config_schema( hass: core.HomeAssistant, source: str | None, input_dict: dict[str, Any] = None ) -> vol.Schema: """ Return schema defaults for init step based on user input/config dict. Retain info already provided for future form views by setting them as defaults in schema. """ if input_dict is None: input_dict = {} api_key_schema = { vol.Required(CONF_API_KEY, default=input_dict.get(CONF_API_KEY)): str, } # For imports we just need to ask for the API key if source == config_entries.SOURCE_IMPORT: return vol.Schema(api_key_schema, extra=vol.REMOVE_EXTRA) default_location = ( input_dict[CONF_LOCATION] if CONF_LOCATION in input_dict else { CONF_LATITUDE: hass.config.latitude, CONF_LONGITUDE: hass.config.longitude, } ) return vol.Schema( { **api_key_schema, vol.Required( CONF_LOCATION, default=default_location, ): selector({"location": {"radius": False}}), }, )
async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle a flow initialized by the user.""" if user_input is not None: await self.async_set_unique_id(user_input[CONF_ZONE]) self._abort_if_unique_id_configured() state = self.hass.states.get(user_input[CONF_ZONE]) return self.async_create_entry( title=state.name if state else "Open-Meteo", data={CONF_ZONE: user_input[CONF_ZONE]}, ) return self.async_show_form( step_id="user", data_schema=vol.Schema( { vol.Required(CONF_ZONE): selector( {"entity": {"domain": ZONE_DOMAIN}} ), } ), )
from homeassistant.helpers.helper_config_entry_flow import ( HelperConfigFlowHandler, HelperFlowFormStep, HelperFlowMenuStep, wrapped_entity_config_entry_title, ) from .const import CONF_TARGET_DOMAIN, DOMAIN CONFIG_FLOW: dict[str, HelperFlowFormStep | HelperFlowMenuStep] = { "user": HelperFlowFormStep( vol.Schema({ vol.Required(CONF_ENTITY_ID): selector.selector({"entity": { "domain": Platform.SWITCH }}), vol.Required(CONF_TARGET_DOMAIN): selector.selector({ "select": { "options": [ { "value": Platform.COVER, "label": "Cover" }, { "value": Platform.FAN, "label": "Fan" }, { "value": Platform.LIGHT,
async def async_step_init(self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Manage KNX options.""" if user_input is not None: self.general_settings = user_input return await self.async_step_tunnel() supported_connection_types = [ CONF_KNX_AUTOMATIC, CONF_KNX_TUNNELING, CONF_KNX_ROUTING, ] self.current_config = self.config_entry.data # type: ignore[assignment] data_schema = { vol.Required( CONF_KNX_CONNECTION_TYPE, default=(CONF_KNX_TUNNELING if self.current_config.get(CONF_KNX_CONNECTION_TYPE) == CONF_KNX_TUNNELING_TCP else self.current_config.get(CONF_KNX_CONNECTION_TYPE)), ): vol.In(supported_connection_types), vol.Required( CONF_KNX_INDIVIDUAL_ADDRESS, default=self.current_config[CONF_KNX_INDIVIDUAL_ADDRESS], ): selector.selector({"text": {}}), vol.Required( CONF_KNX_MCAST_GRP, default=self.current_config.get(CONF_KNX_MCAST_GRP, DEFAULT_MCAST_GRP), ): _IP_SELECTOR, vol.Required( CONF_KNX_MCAST_PORT, default=self.current_config.get(CONF_KNX_MCAST_PORT, DEFAULT_MCAST_PORT), ): _PORT_SELECTOR, } if self.show_advanced_options: local_ip = (self.current_config.get(CONF_KNX_LOCAL_IP) if self.current_config.get(CONF_KNX_LOCAL_IP) is not None else CONF_DEFAULT_LOCAL_IP) data_schema[vol.Required( CONF_KNX_LOCAL_IP, default=local_ip, )] = _IP_SELECTOR data_schema[vol.Required( CONF_KNX_STATE_UPDATER, default=self.current_config.get( CONF_KNX_STATE_UPDATER, CONF_KNX_DEFAULT_STATE_UPDATER, ), )] = selector.selector({"boolean": {}}) data_schema[vol.Required( CONF_KNX_RATE_LIMIT, default=self.current_config.get( CONF_KNX_RATE_LIMIT, CONF_KNX_DEFAULT_RATE_LIMIT, ), )] = vol.All( selector.selector({ "number": { "min": 1, "max": CONF_MAX_RATE_LIMIT, "mode": "box", } }), vol.Coerce(int), ) return self.async_show_form( step_id="init", data_schema=vol.Schema(data_schema), last_step=self.current_config.get(CONF_KNX_CONNECTION_TYPE) != CONF_KNX_TUNNELING, )
import voluptuous as vol from homeassistant.const import CONF_ENTITY_ID from homeassistant.helpers import selector from homeassistant.helpers.helper_config_entry_flow import ( HelperConfigFlowHandler, HelperFlowFormStep, HelperFlowMenuStep, ) from .const import DOMAIN OPTIONS_SCHEMA = vol.Schema({ vol.Required(CONF_ENTITY_ID): selector.selector({"entity": { "domain": "sensor" }}), }) CONFIG_SCHEMA = vol.Schema({ vol.Required("name"): selector.selector({"text": {}}), }).extend(OPTIONS_SCHEMA.schema) CONFIG_FLOW: dict[str, HelperFlowFormStep | HelperFlowMenuStep] = { "user": HelperFlowFormStep(CONFIG_SCHEMA) } OPTIONS_FLOW: dict[str, HelperFlowFormStep | HelperFlowMenuStep] = { "init": HelperFlowFormStep(OPTIONS_SCHEMA) }
}, { "value": METHOD_LEFT, "label": "Left Riemann sum" }, { "value": METHOD_RIGHT, "label": "Right Riemann sum" }, ] OPTIONS_SCHEMA = vol.Schema({ vol.Required(CONF_ROUND_DIGITS, default=2): selector.selector({"number": { "min": 0, "max": 6, "mode": "box" }}), }) CONFIG_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): selector.selector({"text": {}}), vol.Required(CONF_SOURCE_SENSOR): selector.selector({"entity": { "domain": "sensor" }}, ), vol.Required(CONF_METHOD, default=METHOD_TRAPEZOIDAL): selector.selector({"select": { "options": INTEGRATION_METHODS }}),
def binary_sensor_options_schema( handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, options: dict[str, Any], ) -> vol.Schema: """Generate options schema.""" return basic_group_options_schema("binary_sensor", handler, options).extend( { vol.Required(CONF_ALL, default=False): selector.selector({"boolean": {}}), } ) BINARY_SENSOR_CONFIG_SCHEMA = basic_group_config_schema("binary_sensor").extend( { vol.Required(CONF_ALL, default=False): selector.selector({"boolean": {}}), } ) def light_switch_options_schema( domain: str, handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, options: dict[str, Any], ) -> vol.Schema: """Generate options schema.""" return basic_group_options_schema(domain, handler, options).extend( { vol.Required( CONF_ALL, default=False, description={"advanced": True} ): selector.selector({"boolean": {}}),
def basic_group_config_schema(domain: str) -> vol.Schema: """Generate config schema.""" return vol.Schema({vol.Required("name"): selector.selector({"text": {}})}).extend( basic_group_options_schema(domain).schema )
{"boolean": {}} ), } ) def basic_group_config_schema(domain: str) -> vol.Schema: """Generate config schema.""" return vol.Schema({vol.Required("name"): selector.selector({"text": {}})}).extend( basic_group_options_schema(domain).schema ) BINARY_SENSOR_OPTIONS_SCHEMA = basic_group_options_schema("binary_sensor").extend( { vol.Required(CONF_ALL, default=False): selector.selector({"boolean": {}}), } ) LIGHT_OPTIONS_SCHEMA = basic_group_options_schema("light").extend( { vol.Required( CONF_ALL, default=False, description={"advanced": True} ): selector.selector({"boolean": {}}), } ) BINARY_SENSOR_CONFIG_SCHEMA = vol.Schema( {vol.Required("name"): selector.selector({"text": {}})} ).extend(BINARY_SENSOR_OPTIONS_SCHEMA.schema)
"label": "Arithmetic mean" }, { "value": "median", "label": "Median" }, { "value": "last", "label": "Most recently updated" }, ] OPTIONS_SCHEMA = vol.Schema({ vol.Required(CONF_ENTITY_IDS): selector.selector({"entity": { "domain": "sensor", "multiple": True }}), vol.Required(CONF_TYPE): selector.selector({"select": { "options": _STATISTIC_MEASURES }}), vol.Required(CONF_ROUND_DIGITS, default=2): selector.selector({"number": { "min": 0, "max": 6, "mode": "box" }}), }) CONFIG_SCHEMA = vol.Schema({ vol.Required("name"):
) from .const import CONF_HYSTERESIS, CONF_LOWER, CONF_UPPER, DEFAULT_HYSTERESIS, DOMAIN def _validate_mode(data: Any) -> Any: """Validate the threshold mode, and set limits to None if not set.""" if CONF_LOWER not in data and CONF_UPPER not in data: raise HelperFlowError("need_lower_upper") return {CONF_LOWER: None, CONF_UPPER: None, **data} OPTIONS_SCHEMA = vol.Schema({ vol.Required(CONF_HYSTERESIS, default=DEFAULT_HYSTERESIS): selector.selector({"number": { "mode": "box" }}), vol.Optional(CONF_LOWER): selector.selector({"number": { "mode": "box" }}), vol.Optional(CONF_UPPER): selector.selector({"number": { "mode": "box" }}), }) CONFIG_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): selector.selector({"text": {}}), vol.Required(CONF_ENTITY_ID):
def build_schema( config_entry: config_entries | None, hass: HomeAssistant, show_advanced: bool = False, step: str = "user", ) -> vol.Schema: """Build configuration schema. :param config_entry: config entry for getting current parameters on None :param hass: Home Assistant instance :param show_advanced: bool: should we show advanced options? :param step: for which step we should build schema :return: Configuration schema with default parameters """ entity_registry_instance = entity_registry.async_get(hass) humidity_sensors = get_sensors_by_device_class(entity_registry_instance, hass, SensorDeviceClass.HUMIDITY, show_advanced) temperature_sensors = get_sensors_by_device_class( entity_registry_instance, hass, SensorDeviceClass.TEMPERATURE, show_advanced) if not temperature_sensors or not humidity_sensors: return None schema = vol.Schema( { vol.Required(CONF_NAME, default=get_value(config_entry, CONF_NAME, DEFAULT_NAME)): str, vol.Required(CONF_TEMPERATURE_SENSOR): selector({"entity": { "include_entities": temperature_sensors, }}), vol.Required(CONF_HUMIDITY_SENSOR): selector({"entity": { "include_entities": humidity_sensors, }}), }, ) if show_advanced: schema = schema.extend({ vol.Optional(CONF_POLL, default=get_value(config_entry, CONF_POLL, POLL_DEFAULT)): bool, vol.Optional( CONF_SCAN_INTERVAL, default=get_value(config_entry, CONF_SCAN_INTERVAL, SCAN_INTERVAL_DEFAULT), ): vol.All(vol.Coerce(int), vol.Range(min=1)), vol.Optional( CONF_CUSTOM_ICONS, default=get_value(config_entry, CONF_CUSTOM_ICONS, False), ): bool, }) if step == "user": schema = schema.extend({ vol.Optional( CONF_ENABLED_SENSORS, default=list(SensorType), ): cv.multi_select({ sensor_type: sensor_type.to_title() for sensor_type in SensorType }), }) return schema
import voluptuous as vol from homeassistant.const import CONF_NAME from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowFormStep, SchemaFlowMenuStep, ) from .const import CONF_AFTER_TIME, CONF_BEFORE_TIME, DOMAIN OPTIONS_SCHEMA = vol.Schema({ vol.Required(CONF_AFTER_TIME): selector.selector({"time": {}}), vol.Required(CONF_BEFORE_TIME): selector.selector({"time": {}}), }) CONFIG_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): selector.selector({"text": {}}), }).extend(OPTIONS_SCHEMA.schema) CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "user": SchemaFlowFormStep(CONFIG_SCHEMA) } OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "init": SchemaFlowFormStep(OPTIONS_SCHEMA)