From bc0956019b4638720f967cedc999f5e1f7f02937 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 13 May 2025 13:24:13 +1200 Subject: [PATCH] [config] Deprecate more *_SCHEMA constants (#8763) --- .../alarm_control_panel/__init__.py | 39 ++++++++++++- esphome/components/climate/__init__.py | 37 +++++++++++- esphome/components/cover/__init__.py | 39 ++++++++++++- esphome/components/fan/__init__.py | 41 +++++++++++-- esphome/components/light/__init__.py | 57 +++++++++++++++++++ 5 files changed, 202 insertions(+), 11 deletions(-) diff --git a/esphome/components/alarm_control_panel/__init__.py b/esphome/components/alarm_control_panel/__init__.py index 379fbf32f9..1bcb83bce7 100644 --- a/esphome/components/alarm_control_panel/__init__.py +++ b/esphome/components/alarm_control_panel/__init__.py @@ -5,6 +5,8 @@ from esphome.components import mqtt, web_server import esphome.config_validation as cv from esphome.const import ( CONF_CODE, + CONF_ENTITY_CATEGORY, + CONF_ICON, CONF_ID, CONF_MQTT_ID, CONF_ON_STATE, @@ -12,6 +14,7 @@ from esphome.const import ( CONF_WEB_SERVER, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@grahambrown11", "@hwstar"] @@ -78,12 +81,11 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_( "AlarmControlPanelCondition", automation.Condition ) -ALARM_CONTROL_PANEL_SCHEMA = ( +_ALARM_CONTROL_PANEL_SCHEMA = ( cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) .extend( { - cv.GenerateID(): cv.declare_id(AlarmControlPanel), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( mqtt.MQTTAlarmControlPanelComponent ), @@ -146,6 +148,33 @@ ALARM_CONTROL_PANEL_SCHEMA = ( ) ) + +def alarm_control_panel_schema( + class_: MockObjClass, + *, + entity_category: str = cv.UNDEFINED, + icon: str = cv.UNDEFINED, +) -> cv.Schema: + schema = { + cv.GenerateID(): cv.declare_id(class_), + } + + for key, default, validator in [ + (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category), + (CONF_ICON, icon, cv.icon), + ]: + if default is not cv.UNDEFINED: + schema[cv.Optional(key, default=default)] = validator + + return _ALARM_CONTROL_PANEL_SCHEMA.extend(schema) + + +# Remove before 2025.11.0 +ALARM_CONTROL_PANEL_SCHEMA = alarm_control_panel_schema(AlarmControlPanel) +ALARM_CONTROL_PANEL_SCHEMA.add_extra( + cv.deprecated_schema_constant("alarm_control_panel") +) + ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id( { cv.GenerateID(): cv.use_id(AlarmControlPanel), @@ -209,6 +238,12 @@ async def register_alarm_control_panel(var, config): await setup_alarm_control_panel_core_(var, config) +async def new_alarm_control_panel(config, *args): + var = cg.new_Pvariable(config[CONF_ID], *args) + await register_alarm_control_panel(var, config) + return var + + @automation.register_action( "alarm_control_panel.arm_away", ArmAwayAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA ) diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index 445507c620..43c86dcdc2 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -11,9 +11,11 @@ from esphome.const import ( CONF_CURRENT_TEMPERATURE_STATE_TOPIC, CONF_CUSTOM_FAN_MODE, CONF_CUSTOM_PRESET, + CONF_ENTITY_CATEGORY, CONF_FAN_MODE, CONF_FAN_MODE_COMMAND_TOPIC, CONF_FAN_MODE_STATE_TOPIC, + CONF_ICON, CONF_ID, CONF_MAX_TEMPERATURE, CONF_MIN_TEMPERATURE, @@ -46,6 +48,7 @@ from esphome.const import ( CONF_WEB_SERVER, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity IS_PLATFORM_COMPONENT = True @@ -151,12 +154,11 @@ ControlTrigger = climate_ns.class_( "ControlTrigger", automation.Trigger.template(ClimateCall.operator("ref")) ) -CLIMATE_SCHEMA = ( +_CLIMATE_SCHEMA = ( cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) .extend( { - cv.GenerateID(): cv.declare_id(Climate), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent), cv.Optional(CONF_VISUAL, default={}): cv.Schema( { @@ -245,6 +247,31 @@ CLIMATE_SCHEMA = ( ) +def climate_schema( + class_: MockObjClass, + *, + entity_category: str = cv.UNDEFINED, + icon: str = cv.UNDEFINED, +) -> cv.Schema: + schema = { + cv.GenerateID(): cv.declare_id(Climate), + } + + for key, default, validator in [ + (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category), + (CONF_ICON, icon, cv.icon), + ]: + if default is not cv.UNDEFINED: + schema[cv.Optional(key, default=default)] = validator + + return _CLIMATE_SCHEMA.extend(schema) + + +# Remove before 2025.11.0 +CLIMATE_SCHEMA = climate_schema(Climate) +CLIMATE_SCHEMA.add_extra(cv.deprecated_schema_constant("climate")) + + async def setup_climate_core_(var, config): await setup_entity(var, config) @@ -419,6 +446,12 @@ async def register_climate(var, config): await setup_climate_core_(var, config) +async def new_climate(config, *args): + var = cg.new_Pvariable(config[CONF_ID], *args) + await register_climate(var, config) + return var + + CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema( { cv.Required(CONF_ID): cv.use_id(Climate), diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index e7e3ac3bb0..13f117c3f0 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -5,6 +5,8 @@ from esphome.components import mqtt, web_server import esphome.config_validation as cv from esphome.const import ( CONF_DEVICE_CLASS, + CONF_ENTITY_CATEGORY, + CONF_ICON, CONF_ID, CONF_MQTT_ID, CONF_ON_OPEN, @@ -31,6 +33,7 @@ from esphome.const import ( DEVICE_CLASS_WINDOW, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity IS_PLATFORM_COMPONENT = True @@ -89,12 +92,11 @@ CoverClosedTrigger = cover_ns.class_( CONF_ON_CLOSED = "on_closed" -COVER_SCHEMA = ( +_COVER_SCHEMA = ( cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) .extend( { - cv.GenerateID(): cv.declare_id(Cover), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent), cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True), cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All( @@ -124,6 +126,33 @@ COVER_SCHEMA = ( ) +def cover_schema( + class_: MockObjClass, + *, + device_class: str = cv.UNDEFINED, + entity_category: str = cv.UNDEFINED, + icon: str = cv.UNDEFINED, +) -> cv.Schema: + schema = { + cv.GenerateID(): cv.declare_id(class_), + } + + for key, default, validator in [ + (CONF_DEVICE_CLASS, device_class, cv.one_of(*DEVICE_CLASSES, lower=True)), + (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category), + (CONF_ICON, icon, cv.icon), + ]: + if default is not cv.UNDEFINED: + schema[cv.Optional(key, default=default)] = validator + + return _COVER_SCHEMA.extend(schema) + + +# Remove before 2025.11.0 +COVER_SCHEMA = cover_schema(Cover) +COVER_SCHEMA.add_extra(cv.deprecated_schema_constant("cover")) + + async def setup_cover_core_(var, config): await setup_entity(var, config) @@ -163,6 +192,12 @@ async def register_cover(var, config): await setup_cover_core_(var, config) +async def new_cover(config, *args): + var = cg.new_Pvariable(config[CONF_ID], *args) + await register_cover(var, config) + return var + + COVER_ACTION_SCHEMA = maybe_simple_id( { cv.Required(CONF_ID): cv.use_id(Cover), diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 9c9cb6327b..960809ff70 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -7,6 +7,8 @@ from esphome.const import ( CONF_DIRECTION, CONF_DIRECTION_COMMAND_TOPIC, CONF_DIRECTION_STATE_TOPIC, + CONF_ENTITY_CATEGORY, + CONF_ICON, CONF_ID, CONF_MQTT_ID, CONF_OFF_SPEED_CYCLE, @@ -82,12 +84,11 @@ FanPresetSetTrigger = fan_ns.class_( FanIsOnCondition = fan_ns.class_("FanIsOnCondition", automation.Condition.template()) FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.template()) -FAN_SCHEMA = ( +_FAN_SCHEMA = ( cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) .extend( { - cv.GenerateID(): cv.declare_id(Fan), cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum( RESTORE_MODES, upper=True, space="_" ), @@ -159,6 +160,37 @@ FAN_SCHEMA = ( ) ) + +def fan_schema( + class_: cg.Pvariable, + *, + entity_category: str = cv.UNDEFINED, + icon: str = cv.UNDEFINED, + default_restore_mode: str = cv.UNDEFINED, +) -> cv.Schema: + schema = { + cv.GenerateID(): cv.declare_id(class_), + } + + for key, default, validator in [ + (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category), + (CONF_ICON, icon, cv.icon), + ( + CONF_RESTORE_MODE, + default_restore_mode, + cv.enum(RESTORE_MODES, upper=True, space="_"), + ), + ]: + if default is not cv.UNDEFINED: + schema[cv.Optional(key, default=default)] = validator + + return _FAN_SCHEMA.extend(schema) + + +# Remove before 2025.11.0 +FAN_SCHEMA = fan_schema(Fan) +FAN_SCHEMA.add_extra(cv.deprecated_schema_constant("fan")) + _PRESET_MODES_SCHEMA = cv.All( cv.ensure_list(cv.string_strict), cv.Length(min=1), @@ -267,10 +299,9 @@ async def register_fan(var, config): await setup_fan_core_(var, config) -async def create_fan_state(config): - var = cg.new_Pvariable(config[CONF_ID]) +async def new_fan(config, *args): + var = cg.new_Pvariable(config[CONF_ID], *args) await register_fan(var, config) - await cg.register_component(var, config) return var diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index feac385b66..237ab45f38 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -1,3 +1,5 @@ +import enum + import esphome.automation as auto import esphome.codegen as cg from esphome.components import mqtt, power_supply, web_server @@ -13,15 +15,18 @@ from esphome.const import ( CONF_COLOR_TEMPERATURE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, + CONF_ENTITY_CATEGORY, CONF_FLASH_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_GREEN, + CONF_ICON, CONF_ID, CONF_INITIAL_STATE, CONF_MQTT_ID, CONF_ON_STATE, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, + CONF_OUTPUT_ID, CONF_POWER_SUPPLY, CONF_RED, CONF_RESTORE_MODE, @@ -33,6 +38,7 @@ from esphome.const import ( CONF_WHITE, ) from esphome.core import coroutine_with_priority +from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity from .automation import LIGHT_STATE_SCHEMA @@ -141,6 +147,51 @@ ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend( ) +class LightType(enum.IntEnum): + """Light type enum.""" + + BINARY = 0 + BRIGHTNESS_ONLY = 1 + RGB = 2 + ADDRESSABLE = 3 + + +def light_schema( + class_: MockObjClass, + type_: LightType, + *, + entity_category: str = cv.UNDEFINED, + icon: str = cv.UNDEFINED, + default_restore_mode: str = cv.UNDEFINED, +) -> cv.Schema: + schema = { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(class_), + } + + for key, default, validator in [ + (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category), + (CONF_ICON, icon, cv.icon), + ( + CONF_RESTORE_MODE, + default_restore_mode, + cv.enum(RESTORE_MODES, upper=True, space="_"), + ), + ]: + if default is not cv.UNDEFINED: + schema[cv.Optional(key, default=default)] = validator + + if type_ == LightType.BINARY: + return BINARY_LIGHT_SCHEMA.extend(schema) + if type_ == LightType.BRIGHTNESS_ONLY: + return BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend(schema) + if type_ == LightType.RGB: + return RGB_LIGHT_SCHEMA.extend(schema) + if type_ == LightType.ADDRESSABLE: + return ADDRESSABLE_LIGHT_SCHEMA.extend(schema) + + raise ValueError(f"Invalid light type: {type_}") + + def validate_color_temperature_channels(value): if ( CONF_COLD_WHITE_COLOR_TEMPERATURE in value @@ -223,6 +274,12 @@ async def register_light(output_var, config): await setup_light_core_(light_var, output_var, config) +async def new_light(config, *args): + output_var = cg.new_Pvariable(config[CONF_OUTPUT_ID], *args) + await register_light(output_var, config) + return output_var + + @coroutine_with_priority(100.0) async def to_code(config): cg.add_define("USE_LIGHT")