def async_register_entity_service(self, name, schema, func, required_features=None): """Register an entity service.""" if isinstance(schema, dict): schema = ENTITY_SERVICE_SCHEMA.extend(schema) async def handle_service(call): """Handle the service.""" service_name = f"{self.domain}.{name}" await self.hass.helpers.service.entity_service_call( self._platforms.values(), func, call, service_name, required_features) self.hass.services.async_register(self.domain, name, handle_service, schema)
_LOGGER = logging.getLogger(__name__) DOMAIN = "input_select" ENTITY_ID_FORMAT = DOMAIN + ".{}" CONF_INITIAL = "initial" CONF_OPTIONS = "options" ATTR_OPTION = "option" ATTR_OPTIONS = "options" SERVICE_SELECT_OPTION = "select_option" SERVICE_SELECT_OPTION_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_OPTION): cv.string} ) SERVICE_SELECT_NEXT = "select_next" SERVICE_SELECT_PREVIOUS = "select_previous" SERVICE_SET_OPTIONS = "set_options" SERVICE_SET_OPTIONS_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( { vol.Required(ATTR_OPTIONS): vol.All( cv.ensure_list, vol.Length(min=1), [cv.string] ) } )
ATTR_FAN_SPEED = "fan_speed" ATTR_FAN_SPEED_LIST = "fan_speed_list" ATTR_PARAMS = "params" ATTR_STATUS = "status" SERVICE_CLEAN_SPOT = "clean_spot" SERVICE_LOCATE = "locate" SERVICE_RETURN_TO_BASE = "return_to_base" SERVICE_SEND_COMMAND = "send_command" SERVICE_SET_FAN_SPEED = "set_fan_speed" SERVICE_START_PAUSE = "start_pause" SERVICE_START = "start" SERVICE_PAUSE = "pause" SERVICE_STOP = "stop" VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_FAN_SPEED): cv.string}) VACUUM_SEND_COMMAND_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ vol.Required(ATTR_COMMAND): cv.string, vol.Optional(ATTR_PARAMS): vol.Any(dict, cv.ensure_list), }) STATE_CLEANING = "cleaning" STATE_DOCKED = "docked" STATE_IDLE = STATE_IDLE STATE_PAUSED = STATE_PAUSED STATE_RETURNING = "returning" STATE_ERROR = "error"
DEFAULT_INITIAL = 0 DEFAULT_STEP = 1 DOMAIN = "counter" ENTITY_ID_FORMAT = DOMAIN + ".{}" SERVICE_DECREMENT = "decrement" SERVICE_INCREMENT = "increment" SERVICE_RESET = "reset" SERVICE_CONFIGURE = "configure" SERVICE_SCHEMA_CONFIGURE = ENTITY_SERVICE_SCHEMA.extend( { vol.Optional(ATTR_MINIMUM): vol.Any(None, vol.Coerce(int)), vol.Optional(ATTR_MAXIMUM): vol.Any(None, vol.Coerce(int)), vol.Optional(ATTR_STEP): cv.positive_int, vol.Optional(ATTR_INITIAL): cv.positive_int, vol.Optional(VALUE): cv.positive_int, } ) CONFIG_SCHEMA = vol.Schema( { DOMAIN: cv.schema_with_slug_keys( vol.Any( { vol.Optional(CONF_ICON): cv.icon, vol.Optional( CONF_INITIAL, default=DEFAULT_INITIAL ): cv.positive_int, vol.Optional(CONF_NAME): cv.string,
DIRECTION_FORWARD = "forward" DIRECTION_REVERSE = "reverse" ATTR_SPEED = "speed" ATTR_SPEED_LIST = "speed_list" ATTR_OSCILLATING = "oscillating" ATTR_DIRECTION = "direction" PROP_TO_ATTR = { "speed": ATTR_SPEED, "speed_list": ATTR_SPEED_LIST, "oscillating": ATTR_OSCILLATING, "direction": ATTR_DIRECTION, } # type: dict FAN_SET_SPEED_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_SPEED): cv.string}) # type: dict FAN_TURN_ON_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Optional(ATTR_SPEED): cv.string}) # type: dict FAN_OSCILLATE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_OSCILLATING): cv.boolean}) # type: dict FAN_SET_DIRECTION_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Optional(ATTR_DIRECTION): cv.string}) # type: dict @bind_hass def is_on(hass, entity_id: str = None) -> bool: """Return if the fans are on based on the statemachine.""" entity_id = entity_id or ENTITY_ID_ALL_FANS
SERVICE_RESET, SERVICE_SELECT_TARIFF, SERVICE_SELECT_NEXT_TARIFF, ATTR_TARIFF, ) _LOGGER = logging.getLogger(__name__) TARIFF_ICON = "mdi:clock-outline" ATTR_TARIFFS = "tariffs" DEFAULT_OFFSET = timedelta(hours=0) SERVICE_SELECT_TARIFF_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_TARIFF): cv.string} ) METER_CONFIG_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( { vol.Required(CONF_SOURCE_SENSOR): cv.entity_id, vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_METER_TYPE): vol.In(METER_TYPES), vol.Optional(CONF_METER_OFFSET, default=DEFAULT_OFFSET): vol.All( cv.time_period, cv.positive_timedelta ), vol.Optional(CONF_METER_NET_CONSUMPTION, default=False): cv.boolean, vol.Optional(CONF_TARIFFS, default=[]): vol.All(cv.ensure_list, [cv.string]), } )
vol.Inclusive(CONF_CLIENT_ID, CONF_OAUTH, msg=CONF_MISSING_OAUTH_MSG): cv.string, vol.Inclusive(CONF_CLIENT_SECRET, CONF_OAUTH, msg=CONF_MISSING_OAUTH_MSG): cv.string, vol.Optional(CONF_LOCAL_CONTROL, default=False): cv.boolean, }) }, extra=vol.ALLOW_EXTRA, ) RENAME_DEVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_NAME): cv.string}, extra=vol.ALLOW_EXTRA) DELETE_DEVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA) SET_PAIRING_MODE_SCHEMA = vol.Schema( { vol.Required(ATTR_HUB_NAME): cv.string, vol.Required(ATTR_PAIRING_MODE): cv.string, vol.Optional(ATTR_KIDDE_RADIO_CODE): cv.string, }, extra=vol.ALLOW_EXTRA, ) SET_VOLUME_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_VOLUME): vol.In(VOLUMES)})
ENTITY_SERVICE_SCHEMA, PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE, ) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent DOMAIN = "alarm_control_panel" SCAN_INTERVAL = timedelta(seconds=30) ATTR_CHANGED_BY = "changed_by" FORMAT_TEXT = "text" FORMAT_NUMBER = "number" ATTR_CODE_ARM_REQUIRED = "code_arm_required" ENTITY_ID_FORMAT = DOMAIN + ".{}" ALARM_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Optional(ATTR_CODE): cv.string}) async def async_setup(hass, config): """Track states and offer events for sensors.""" component = hass.data[DOMAIN] = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) await component.async_setup(config) component.async_register_entity_service(SERVICE_ALARM_DISARM, ALARM_SERVICE_SCHEMA, "async_alarm_disarm") component.async_register_entity_service(SERVICE_ALARM_ARM_HOME, ALARM_SERVICE_SCHEMA, "async_alarm_arm_home")
vol.Optional(CONF_DESCRIPTION, default=""): cv.string, vol.Optional(CONF_FIELDS, default={}): { cv.string: { vol.Optional(CONF_DESCRIPTION): cv.string, vol.Optional(CONF_EXAMPLE): cv.string, } }, }) CONFIG_SCHEMA = vol.Schema( {DOMAIN: cv.schema_with_slug_keys(SCRIPT_ENTRY_SCHEMA)}, extra=vol.ALLOW_EXTRA) SCRIPT_SERVICE_SCHEMA = vol.Schema(dict) SCRIPT_TURN_ONOFF_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Optional(ATTR_VARIABLES): dict}) RELOAD_SERVICE_SCHEMA = vol.Schema({}) @bind_hass def is_on(hass, entity_id): """Return if the script is on based on the statemachine.""" return hass.states.is_state(entity_id, STATE_ON) async def async_setup(hass, config): """Load the scripts from the configuration.""" component = EntityComponent(_LOGGER, DOMAIN, hass, group_name=GROUP_NAME_ALL_SCRIPTS)
ATTR_AUTO = "auto" ATTR_CONTROL = "control" ATTR_ENTITIES = "entities" ATTR_OBJECT_ID = "object_id" ATTR_ORDER = "order" ATTR_VIEW = "view" ATTR_VISIBLE = "visible" ATTR_ALL = "all" SERVICE_SET_VISIBILITY = "set_visibility" SERVICE_SET = "set" SERVICE_REMOVE = "remove" CONTROL_TYPES = vol.In(["hidden", None]) SET_VISIBILITY_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_VISIBLE): cv.boolean}) RELOAD_SERVICE_SCHEMA = vol.Schema({}) SET_SERVICE_SCHEMA = vol.Schema({ vol.Required(ATTR_OBJECT_ID): cv.slug, vol.Optional(ATTR_NAME): cv.string, vol.Optional(ATTR_VIEW): cv.boolean, vol.Optional(ATTR_ICON): cv.string, vol.Optional(ATTR_CONTROL): CONTROL_TYPES, vol.Optional(ATTR_VISIBLE):
CONF_STEP = "step" MODE_SLIDER = "slider" MODE_BOX = "box" ATTR_INITIAL = "initial" ATTR_VALUE = "value" ATTR_MIN = "min" ATTR_MAX = "max" ATTR_STEP = "step" SERVICE_SET_VALUE = "set_value" SERVICE_INCREMENT = "increment" SERVICE_DECREMENT = "decrement" SERVICE_SET_VALUE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_VALUE): vol.Coerce(float)}) def _cv_input_number(cfg): """Configure validation helper for input number (voluptuous).""" minimum = cfg.get(CONF_MIN) maximum = cfg.get(CONF_MAX) if minimum >= maximum: raise vol.Invalid( f"Maximum ({minimum}) is not greater than minimum ({maximum})") state = cfg.get(CONF_INITIAL) if state is not None and (state < minimum or state > maximum): raise vol.Invalid( f"Initial value {state} not in range {minimum}-{maximum}") return cfg
ENTITY_IMAGE_CACHE = { CACHE_IMAGES: collections.OrderedDict(), CACHE_MAXSIZE: 16 } SCAN_INTERVAL = timedelta(seconds=10) DEVICE_CLASS_TV = "tv" DEVICE_CLASS_SPEAKER = "speaker" DEVICE_CLASSES = [DEVICE_CLASS_TV, DEVICE_CLASS_SPEAKER] DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES)) # Service call validation schemas MEDIA_PLAYER_SET_VOLUME_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float}) MEDIA_PLAYER_MUTE_VOLUME_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_MEDIA_VOLUME_MUTED): cv.boolean}) MEDIA_PLAYER_MEDIA_SEEK_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ vol.Required(ATTR_MEDIA_SEEK_POSITION): vol.All(vol.Coerce(float), vol.Range(min=0)) }) MEDIA_PLAYER_SELECT_SOURCE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_INPUT_SOURCE): cv.string}) MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_SOUND_MODE): cv.string})
STATUS_ACTIVE = "active" STATUS_PAUSED = "paused" EVENT_TIMER_FINISHED = "timer.finished" EVENT_TIMER_CANCELLED = "timer.cancelled" EVENT_TIMER_STARTED = "timer.started" EVENT_TIMER_RESTARTED = "timer.restarted" EVENT_TIMER_PAUSED = "timer.paused" SERVICE_START = "start" SERVICE_PAUSE = "pause" SERVICE_CANCEL = "cancel" SERVICE_FINISH = "finish" SERVICE_SCHEMA_DURATION = ENTITY_SERVICE_SCHEMA.extend({ vol.Optional(ATTR_DURATION, default=timedelta(DEFAULT_DURATION)): cv.time_period }) CONFIG_SCHEMA = vol.Schema( { DOMAIN: cv.schema_with_slug_keys( vol.Any( { vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_DURATION, timedelta(DEFAULT_DURATION)): cv.time_period, },
DEFAULT_MIN_TEMP = 7 DEFAULT_MAX_TEMP = 35 DEFAULT_MIN_HUMIDITY = 30 DEFAULT_MAX_HUMIDITY = 99 ENTITY_ID_FORMAT = DOMAIN + ".{}" SCAN_INTERVAL = timedelta(seconds=60) CONVERTIBLE_ATTRIBUTE = [ ATTR_TEMPERATURE, ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH ] _LOGGER = logging.getLogger(__name__) SET_AUX_HEAT_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_AUX_HEAT): cv.boolean}) SET_TEMPERATURE_SCHEMA = vol.Schema( vol.All( cv.has_at_least_one_key(ATTR_TEMPERATURE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW), ENTITY_SERVICE_SCHEMA.extend({ vol.Exclusive(ATTR_TEMPERATURE, "temperature"): vol.Coerce(float), vol.Inclusive(ATTR_TARGET_TEMP_HIGH, "temperature"): vol.Coerce(float), vol.Inclusive(ATTR_TARGET_TEMP_LOW, "temperature"): vol.Coerce(float), vol.Optional(ATTR_HVAC_MODE): vol.In(HVAC_MODES), }), ))
CONF_INITIAL = "initial" CONF_MIN = "min" CONF_MAX = "max" MODE_TEXT = "text" MODE_PASSWORD = "******" ATTR_VALUE = "value" ATTR_MIN = "min" ATTR_MAX = "max" ATTR_PATTERN = "pattern" ATTR_MODE = "mode" SERVICE_SET_VALUE = "set_value" SERVICE_SET_VALUE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_VALUE): cv.string}) def _cv_input_text(cfg): """Configure validation helper for input box (voluptuous).""" minimum = cfg.get(CONF_MIN) maximum = cfg.get(CONF_MAX) if minimum > maximum: raise vol.Invalid( f"Max len ({minimum}) is not greater than min len ({maximum})") state = cfg.get(CONF_INITIAL) if state is not None and (len(state) < minimum or len(state) > maximum): raise vol.Invalid( f"Initial value {state} length not in range {minimum}-{maximum}") return cfg
SUPPORT_STOP = 8 SUPPORT_OPEN_TILT = 16 SUPPORT_CLOSE_TILT = 32 SUPPORT_STOP_TILT = 64 SUPPORT_SET_TILT_POSITION = 128 ATTR_CURRENT_POSITION = "current_position" ATTR_CURRENT_TILT_POSITION = "current_tilt_position" ATTR_POSITION = "position" ATTR_TILT_POSITION = "tilt_position" INTENT_OPEN_COVER = "HassOpenCover" INTENT_CLOSE_COVER = "HassCloseCover" COVER_SET_COVER_POSITION_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ vol.Required(ATTR_POSITION): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)) }) COVER_SET_COVER_TILT_POSITION_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ vol.Required(ATTR_TILT_POSITION): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)) }) @bind_hass def is_closed(hass, entity_id=None): """Return if the cover is closed based on the statemachine.""" entity_id = entity_id or ENTITY_ID_ALL_COVERS return hass.states.is_state(entity_id, STATE_CLOSED)
ENTITY_ID_FORMAT = DOMAIN + ".{}" CONF_HAS_DATE = "has_date" CONF_HAS_TIME = "has_time" CONF_INITIAL = "initial" DEFAULT_VALUE = "1970-01-01 00:00:00" ATTR_DATETIME = "datetime" SERVICE_SET_DATETIME = "set_datetime" SERVICE_SET_DATETIME_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ vol.Optional(ATTR_DATE): cv.date, vol.Optional(ATTR_TIME): cv.time, vol.Optional(ATTR_DATETIME): cv.datetime, }) def has_date_or_time(conf): """Check at least date or time is true.""" if conf[CONF_HAS_DATE] or conf[CONF_HAS_TIME]: return conf raise vol.Invalid("Entity needs at least a date or a time") CONFIG_SCHEMA = vol.Schema( {
SERVICE_SET_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ vol.Optional(ATTR_VALUE): cv.match_all, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_QUERY): vol.All(cv.string, validate_sql_select), vol.Optional(CONF_COLUMN): cv.string, vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string, vol.Optional(CONF_RESTORE): cv.boolean, vol.Optional(CONF_FORCE_UPDATE): cv.boolean, vol.Optional(ATTR_FRIENDLY_NAME): cv.string, vol.Optional(CONF_FRIENDLY_NAME_TEMPLATE): cv.template, vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_ICON_TEMPLATE): cv.template, vol.Optional(ATTR_ENTITY_PICTURE): cv.string, vol.Optional(CONF_ENTITY_PICTURE_TEMPLATE): cv.template, vol.Optional(CONF_TRACKED_ENTITY_ID): cv.entity_ids, vol.Optional(CONF_TRACKED_EVENT_TYPE): validate_event_types, })
VALID_BRIGHTNESS = vol.All(vol.Coerce(int), vol.Clamp(min=0, max=255)) VALID_BRIGHTNESS_PCT = vol.All(vol.Coerce(float), vol.Range(min=0, max=100)) LIGHT_TURN_ON_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ vol.Exclusive(ATTR_PROFILE, COLOR_GROUP): cv.string, ATTR_TRANSITION: VALID_TRANSITION, ATTR_BRIGHTNESS: VALID_BRIGHTNESS, ATTR_BRIGHTNESS_PCT: VALID_BRIGHTNESS_PCT, vol.Exclusive(ATTR_COLOR_NAME, COLOR_GROUP): cv.string, vol.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): vol.All(vol.ExactSequence((cv.byte, cv.byte, cv.byte)), vol.Coerce(tuple)), vol.Exclusive(ATTR_XY_COLOR, COLOR_GROUP): vol.All(vol.ExactSequence((cv.small_float, cv.small_float)), vol.Coerce(tuple)), vol.Exclusive(ATTR_HS_COLOR, COLOR_GROUP): vol.All(vol.ExactSequence( (vol.All(vol.Coerce(float), vol.Range(min=0, max=360)), vol.All(vol.Coerce(float), vol.Range(min=0, max=100)))), vol.Coerce(tuple)), vol.Exclusive(ATTR_COLOR_TEMP, COLOR_GROUP): vol.All(vol.Coerce(int), vol.Range(min=1)), vol.Exclusive(ATTR_KELVIN, COLOR_GROUP): vol.All(vol.Coerce(int), vol.Range(min=0)), ATTR_WHITE_VALUE: vol.All(vol.Coerce(int), vol.Range(min=0, max=255)), ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]), ATTR_EFFECT: cv.string, }) LIGHT_TURN_OFF_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ ATTR_TRANSITION: VALID_TRANSITION,
GROUP_NAME_ALL_REMOTES = "all remotes" MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) SERVICE_SEND_COMMAND = "send_command" SERVICE_LEARN_COMMAND = "learn_command" SERVICE_SYNC = "sync" DEFAULT_NUM_REPEATS = 1 DEFAULT_DELAY_SECS = 0.4 DEFAULT_HOLD_SECS = 0 SUPPORT_LEARN_COMMAND = 1 REMOTE_SERVICE_ACTIVITY_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Optional(ATTR_ACTIVITY): cv.string}) REMOTE_SERVICE_SEND_COMMAND_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({ vol.Required(ATTR_COMMAND): vol.All(cv.ensure_list, [cv.string]), vol.Optional(ATTR_DEVICE): cv.string, vol.Optional(ATTR_NUM_REPEATS, default=DEFAULT_NUM_REPEATS): cv.positive_int, vol.Optional(ATTR_DELAY_SECS): vol.Coerce(float), vol.Optional(ATTR_HOLD_SECS, default=DEFAULT_HOLD_SECS): vol.Coerce(float), }) REMOTE_SERVICE_LEARN_COMMAND_SCHEMA = ENTITY_SERVICE_SCHEMA.extend({
str, CONF_ALIAS: cv.string, vol.Optional(CONF_INITIAL_STATE): cv.boolean, vol.Optional(CONF_HIDE_ENTITY, default=DEFAULT_HIDE_ENTITY): cv.boolean, vol.Required(CONF_TRIGGER): _TRIGGER_SCHEMA, vol.Optional(CONF_CONDITION): _CONDITION_SCHEMA, vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA, }) TRIGGER_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Optional(ATTR_VARIABLES, default={}): dict}) RELOAD_SERVICE_SCHEMA = vol.Schema({}) @bind_hass def is_on(hass, entity_id): """ Return true if specified automation entity_id is on. Async friendly. """ return hass.states.is_state(entity_id, STATE_ON) async def async_setup(hass, config):
SERVICE_SET_HUMIDITY, SERVICE_SET_HUMIDIFIER_MODE, SERVICE_SET_PRESET_MODE, SUPPORT_PRESET_MODE, SUPPORT_TARGET_HUMIDITY, ) DEFAULT_MIN_HUMIDITY = 30 DEFAULT_MAX_HUMIDITY = 99 ENTITY_ID_FORMAT = DOMAIN + ".{}" SCAN_INTERVAL = timedelta(seconds=60) _LOGGER = logging.getLogger(__name__) SET_PRESET_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_PRESET_MODE): cv.string}) SET_HUMIDIFIER_MODE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_HUMIDIFIER_MODE): vol.In(HUMIDIFIER_MODES)}) SET_HUMIDITY_SCHEMA = ENTITY_SERVICE_SCHEMA.extend( {vol.Required(ATTR_HUMIDITY): vol.Coerce(float)}) async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool: """Set up humidity devices.""" component = hass.data[DOMAIN] = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) await component.async_setup(config) component.async_register_entity_service(SERVICE_TURN_ON, ENTITY_SERVICE_SCHEMA, "async_turn_on")