mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[web_server] v3 entity grouping (#6833)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -9,7 +9,7 @@ from esphome.const import ( | ||||
|     CONF_MQTT_ID, | ||||
|     CONF_ON_STATE, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| @@ -195,9 +195,8 @@ async def setup_alarm_control_panel_core_(var, config): | ||||
|     for conf in config.get(CONF_ON_READY, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|     if mqtt_id := config.get(CONF_MQTT_ID): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|   | ||||
| @@ -25,7 +25,7 @@ from esphome.const import ( | ||||
|     CONF_STATE, | ||||
|     CONF_TIMING, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     DEVICE_CLASS_BATTERY, | ||||
|     DEVICE_CLASS_BATTERY_CHARGING, | ||||
|     DEVICE_CLASS_CARBON_MONOXIDE, | ||||
| @@ -543,9 +543,8 @@ async def setup_binary_sensor_core_(var, config): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_binary_sensor(var, config): | ||||
|   | ||||
| @@ -11,7 +11,7 @@ from esphome.const import ( | ||||
|     CONF_MQTT_ID, | ||||
|     CONF_ON_PRESS, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_IDENTIFY, | ||||
|     DEVICE_CLASS_RESTART, | ||||
| @@ -97,9 +97,8 @@ async def setup_button_core_(var, config): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_button(var, config): | ||||
|   | ||||
| @@ -43,7 +43,7 @@ from esphome.const import ( | ||||
|     CONF_TEMPERATURE_STEP, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_VISUAL, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| @@ -408,9 +408,8 @@ async def setup_climate_core_(var, config): | ||||
|             trigger, [(ClimateCall.operator("ref"), "x")], conf | ||||
|         ) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_climate(var, config): | ||||
|   | ||||
| @@ -17,7 +17,7 @@ from esphome.const import ( | ||||
|     CONF_TILT_COMMAND_TOPIC, | ||||
|     CONF_TILT_STATE_TOPIC, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     DEVICE_CLASS_AWNING, | ||||
|     DEVICE_CLASS_BLIND, | ||||
|     DEVICE_CLASS_CURTAIN, | ||||
| @@ -137,10 +137,6 @@ async def setup_cover_core_(var, config): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|  | ||||
|     if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
| @@ -156,6 +152,9 @@ async def setup_cover_core_(var, config): | ||||
|         if (tilt_command_topic := config.get(CONF_TILT_COMMAND_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_tilt_command_topic(tilt_command_topic)) | ||||
|  | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_cover(var, config): | ||||
|     if not CORE.has_id(config[CONF_ID]): | ||||
|   | ||||
| @@ -18,7 +18,7 @@ from esphome.const import ( | ||||
|     CONF_TIME_ID, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_TYPE, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     CONF_YEAR, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| @@ -138,9 +138,8 @@ async def setup_datetime_core_(var, config): | ||||
|     if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|     for conf in config.get(CONF_ON_VALUE, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf) | ||||
|   | ||||
| @@ -25,7 +25,7 @@ from esphome.const import ( | ||||
|     CONF_SPEED_LEVEL_STATE_TOPIC, | ||||
|     CONF_SPEED_STATE_TOPIC, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| @@ -218,9 +218,8 @@ async def setup_fan_core_(var, config): | ||||
|         if (speed_command_topic := config.get(CONF_SPEED_COMMAND_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_speed_command_topic(speed_command_topic)) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_STATE, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|   | ||||
| @@ -18,7 +18,7 @@ from esphome.const import ( | ||||
|     CONF_RESTORE_MODE, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WARM_WHITE_COLOR_TEMPERATURE, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| from esphome.core import coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| @@ -181,9 +181,8 @@ async def setup_light_core_(light_var, output_var, config): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, light_var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, light_var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(light_var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_light(output_var, config): | ||||
|   | ||||
| @@ -9,7 +9,7 @@ from esphome.const import ( | ||||
|     CONF_ON_LOCK, | ||||
|     CONF_ON_UNLOCK, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| @@ -66,9 +66,8 @@ async def setup_lock_core_(var, config): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_lock(var, config): | ||||
|   | ||||
| @@ -18,7 +18,7 @@ from esphome.const import ( | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_UNIT_OF_MEASUREMENT, | ||||
|     CONF_VALUE, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     DEVICE_CLASS_APPARENT_POWER, | ||||
|     DEVICE_CLASS_AQI, | ||||
|     DEVICE_CLASS_ATMOSPHERIC_PRESSURE, | ||||
| @@ -254,10 +254,8 @@ async def setup_number_core_( | ||||
|     if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_number( | ||||
|   | ||||
| @@ -14,7 +14,7 @@ from esphome.const import ( | ||||
|     CONF_OPERATION, | ||||
|     CONF_OPTION, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_generator import MockObjClass | ||||
| @@ -104,9 +104,8 @@ async def setup_select_core_(var, config, *, options: list[str]): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_select(var, config, *, options: list[str]): | ||||
|   | ||||
| @@ -36,7 +36,7 @@ from esphome.const import ( | ||||
|     CONF_TYPE, | ||||
|     CONF_UNIT_OF_MEASUREMENT, | ||||
|     CONF_VALUE, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     CONF_WINDOW_SIZE, | ||||
|     DEVICE_CLASS_APPARENT_POWER, | ||||
|     DEVICE_CLASS_AQI, | ||||
| @@ -800,9 +800,8 @@ async def setup_sensor_core_(var, config): | ||||
|             else: | ||||
|                 cg.add(mqtt_.set_expire_after(expire_after)) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_sensor(var, config): | ||||
|   | ||||
| @@ -14,7 +14,7 @@ from esphome.const import ( | ||||
|     CONF_ON_TURN_ON, | ||||
|     CONF_RESTORE_MODE, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_OUTLET, | ||||
|     DEVICE_CLASS_SWITCH, | ||||
| @@ -156,9 +156,8 @@ async def setup_switch_core_(var, config): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|     if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: | ||||
|         cg.add(var.set_device_class(device_class)) | ||||
|   | ||||
| @@ -11,7 +11,7 @@ from esphome.const import ( | ||||
|     CONF_ON_VALUE, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_VALUE, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| @@ -82,9 +82,8 @@ async def setup_text_core_( | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_text( | ||||
|   | ||||
| @@ -15,7 +15,7 @@ from esphome.const import ( | ||||
|     CONF_STATE, | ||||
|     CONF_TO, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     DEVICE_CLASS_DATE, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_TIMESTAMP, | ||||
| @@ -212,9 +212,8 @@ async def setup_text_sensor_core_(var, config): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_text_sensor(var, config): | ||||
|   | ||||
| @@ -8,7 +8,7 @@ from esphome.const import ( | ||||
|     CONF_FORCE_UPDATE, | ||||
|     CONF_ID, | ||||
|     CONF_MQTT_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_FIRMWARE, | ||||
|     ENTITY_CATEGORY_CONFIG, | ||||
| @@ -73,9 +73,8 @@ async def setup_update_core_(var, config): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id_config, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|     if web_server_id_config := config.get(CONF_WEB_SERVER_ID): | ||||
|         web_server_ = await cg.get_variable(web_server_id_config) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_update(var, config): | ||||
|   | ||||
| @@ -14,7 +14,7 @@ from esphome.const import ( | ||||
|     CONF_STATE, | ||||
|     CONF_STOP, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_GAS, | ||||
|     DEVICE_CLASS_WATER, | ||||
| @@ -124,9 +124,8 @@ async def setup_valve_core_(var, config): | ||||
|                 mqtt_.set_custom_position_command_topic(position_command_topic_config) | ||||
|             ) | ||||
|  | ||||
|     if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: | ||||
|         web_server_ = await cg.get_variable(webserver_id) | ||||
|         web_server.add_entity_to_sorting_list(web_server_, var, config) | ||||
|     if web_server_config := config.get(CONF_WEB_SERVER): | ||||
|         await web_server.add_entity_config(var, web_server_config) | ||||
|  | ||||
|  | ||||
| async def register_valve(var, config): | ||||
|   | ||||
| @@ -17,13 +17,14 @@ from esphome.const import ( | ||||
|     CONF_JS_URL, | ||||
|     CONF_LOCAL, | ||||
|     CONF_LOG, | ||||
|     CONF_NAME, | ||||
|     CONF_OTA, | ||||
|     CONF_PASSWORD, | ||||
|     CONF_PORT, | ||||
|     CONF_USERNAME, | ||||
|     CONF_VERSION, | ||||
|     CONF_WEB_SERVER, | ||||
|     CONF_WEB_SERVER_ID, | ||||
|     CONF_WEB_SERVER_SORTING_WEIGHT, | ||||
|     PLATFORM_BK72XX, | ||||
|     PLATFORM_ESP32, | ||||
|     PLATFORM_ESP8266, | ||||
| @@ -34,9 +35,15 @@ import esphome.final_validate as fv | ||||
|  | ||||
| AUTO_LOAD = ["json", "web_server_base"] | ||||
|  | ||||
| CONF_SORTING_GROUP_ID = "sorting_group_id" | ||||
| CONF_SORTING_GROUPS = "sorting_groups" | ||||
| CONF_SORTING_WEIGHT = "sorting_weight" | ||||
|  | ||||
| web_server_ns = cg.esphome_ns.namespace("web_server") | ||||
| WebServer = web_server_ns.class_("WebServer", cg.Component, cg.Controller) | ||||
|  | ||||
| sorting_groups = {} | ||||
|  | ||||
|  | ||||
| def default_url(config): | ||||
|     config = config.copy() | ||||
| @@ -70,42 +77,74 @@ def validate_ota(config): | ||||
|     return config | ||||
|  | ||||
|  | ||||
| def _validate_no_sorting_weight( | ||||
|     webserver_version: int, config: dict, path: list[str] | None = None | ||||
| ) -> None: | ||||
|     if path is None: | ||||
|         path = [] | ||||
|     if CONF_WEB_SERVER_SORTING_WEIGHT in config: | ||||
|         raise cv.FinalExternalInvalid( | ||||
|             f"Sorting weight on entities is not supported in web_server version {webserver_version}", | ||||
|             path=path + [CONF_WEB_SERVER_SORTING_WEIGHT], | ||||
| def validate_sorting_groups(config): | ||||
|     if CONF_SORTING_GROUPS in config and config[CONF_VERSION] != 3: | ||||
|         raise cv.Invalid( | ||||
|             f"'{CONF_SORTING_GROUPS}' is only supported in 'web_server' version 3" | ||||
|         ) | ||||
|     for p, value in config.items(): | ||||
|         if isinstance(value, dict): | ||||
|             _validate_no_sorting_weight(webserver_version, value, path + [p]) | ||||
|         elif isinstance(value, list): | ||||
|             for i, item in enumerate(value): | ||||
|                 if isinstance(item, dict): | ||||
|                     _validate_no_sorting_weight(webserver_version, item, path + [p, i]) | ||||
|  | ||||
|  | ||||
| def _final_validate_sorting_weight(config): | ||||
|     if (webserver_version := config.get(CONF_VERSION)) != 3: | ||||
|         _validate_no_sorting_weight(webserver_version, fv.full_config.get()) | ||||
|  | ||||
|     return config | ||||
|  | ||||
|  | ||||
| FINAL_VALIDATE_SCHEMA = _final_validate_sorting_weight | ||||
| def _validate_no_sorting_component( | ||||
|     sorting_component: str, | ||||
|     webserver_version: int, | ||||
|     config: dict, | ||||
|     path: list[str] | None = None, | ||||
| ) -> None: | ||||
|     if path is None: | ||||
|         path = [] | ||||
|     if CONF_WEB_SERVER in config and sorting_component in config[CONF_WEB_SERVER]: | ||||
|         raise cv.FinalExternalInvalid( | ||||
|             f"{sorting_component} on entities is not supported in web_server version {webserver_version}", | ||||
|             path=path + [sorting_component], | ||||
|         ) | ||||
|     for p, value in config.items(): | ||||
|         if isinstance(value, dict): | ||||
|             _validate_no_sorting_component( | ||||
|                 sorting_component, webserver_version, value, path + [p] | ||||
|             ) | ||||
|         elif isinstance(value, list): | ||||
|             for i, item in enumerate(value): | ||||
|                 if isinstance(item, dict): | ||||
|                     _validate_no_sorting_component( | ||||
|                         sorting_component, webserver_version, item, path + [p, i] | ||||
|                     ) | ||||
|  | ||||
|  | ||||
| def _final_validate_sorting(config): | ||||
|     if (webserver_version := config.get(CONF_VERSION)) != 3: | ||||
|         _validate_no_sorting_component( | ||||
|             CONF_SORTING_WEIGHT, webserver_version, fv.full_config.get() | ||||
|         ) | ||||
|         _validate_no_sorting_component( | ||||
|             CONF_SORTING_GROUP_ID, webserver_version, fv.full_config.get() | ||||
|         ) | ||||
|     return config | ||||
|  | ||||
|  | ||||
| FINAL_VALIDATE_SCHEMA = _final_validate_sorting | ||||
|  | ||||
| sorting_group = { | ||||
|     cv.Required(CONF_ID): cv.declare_id(cg.int_), | ||||
|     cv.Required(CONF_NAME): cv.string, | ||||
|     cv.Optional(CONF_SORTING_WEIGHT): cv.float_, | ||||
| } | ||||
|  | ||||
| WEBSERVER_SORTING_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.OnlyWith(CONF_WEB_SERVER_ID, "web_server"): cv.use_id(WebServer), | ||||
|         cv.Optional(CONF_WEB_SERVER_SORTING_WEIGHT): cv.All( | ||||
|             cv.requires_component("web_server"), | ||||
|             cv.float_, | ||||
|         ), | ||||
|         cv.Optional(CONF_WEB_SERVER): cv.Schema( | ||||
|             { | ||||
|                 cv.OnlyWith(CONF_WEB_SERVER_ID, "web_server"): cv.use_id(WebServer), | ||||
|                 cv.Optional(CONF_SORTING_WEIGHT): cv.All( | ||||
|                     cv.requires_component("web_server"), | ||||
|                     cv.float_, | ||||
|                 ), | ||||
|                 cv.Optional(CONF_SORTING_GROUP_ID): cv.All( | ||||
|                     cv.requires_component("web_server"), | ||||
|                     cv.use_id(cg.int_), | ||||
|                 ), | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| ) | ||||
|  | ||||
| @@ -145,24 +184,38 @@ CONFIG_SCHEMA = cv.All( | ||||
|             ): cv.boolean, | ||||
|             cv.Optional(CONF_LOG, default=True): cv.boolean, | ||||
|             cv.Optional(CONF_LOCAL): cv.boolean, | ||||
|             cv.Optional(CONF_SORTING_GROUPS): cv.ensure_list(sorting_group), | ||||
|         } | ||||
|     ).extend(cv.COMPONENT_SCHEMA), | ||||
|     cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]), | ||||
|     default_url, | ||||
|     validate_local, | ||||
|     validate_ota, | ||||
|     validate_sorting_groups, | ||||
| ) | ||||
|  | ||||
|  | ||||
| def add_entity_to_sorting_list(web_server, entity, config): | ||||
|     sorting_weight = 50 | ||||
|     if CONF_WEB_SERVER_SORTING_WEIGHT in config: | ||||
|         sorting_weight = config[CONF_WEB_SERVER_SORTING_WEIGHT] | ||||
| def add_sorting_groups(web_server_var, config): | ||||
|     for group in config: | ||||
|         sorting_groups[group[CONF_ID]] = group[CONF_NAME] | ||||
|         group_sorting_weight = group.get(CONF_SORTING_WEIGHT, 50) | ||||
|         cg.add( | ||||
|             web_server_var.add_sorting_group( | ||||
|                 hash(group[CONF_ID]), group[CONF_NAME], group_sorting_weight | ||||
|             ) | ||||
|         ) | ||||
|  | ||||
|  | ||||
| async def add_entity_config(entity, config): | ||||
|     web_server = await cg.get_variable(config[CONF_WEB_SERVER_ID]) | ||||
|     sorting_weight = config.get(CONF_SORTING_WEIGHT, 50) | ||||
|     sorting_group_hash = hash(config.get(CONF_SORTING_GROUP_ID)) | ||||
|  | ||||
|     cg.add( | ||||
|         web_server.add_entity_to_sorting_list( | ||||
|         web_server.add_entity_config( | ||||
|             entity, | ||||
|             sorting_weight, | ||||
|             sorting_group_hash, | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
| @@ -241,3 +294,6 @@ async def to_code(config): | ||||
|     cg.add(var.set_include_internal(config[CONF_INCLUDE_INTERNAL])) | ||||
|     if CONF_LOCAL in config and config[CONF_LOCAL]: | ||||
|         cg.add_define("USE_WEBSERVER_LOCAL") | ||||
|  | ||||
|     if (sorting_group_config := config.get(CONF_SORTING_GROUPS)) is not None: | ||||
|         add_sorting_groups(var, sorting_group_config) | ||||
|   | ||||
| @@ -105,6 +105,14 @@ void WebServer::setup() { | ||||
|     // Configure reconnect timeout and send config | ||||
|     client->send(this->get_config_json().c_str(), "ping", millis(), 30000); | ||||
|  | ||||
|     for (auto &group : this->sorting_groups_) { | ||||
|       client->send(json::build_json([group](JsonObject root) { | ||||
|                      root["name"] = group.second.name; | ||||
|                      root["sorting_weight"] = group.second.weight; | ||||
|                    }).c_str(), | ||||
|                    "sorting_group"); | ||||
|     } | ||||
|  | ||||
|     this->entities_iterator_.begin(this->include_internal_); | ||||
|   }); | ||||
|  | ||||
| @@ -246,6 +254,9 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|       if (!obj->get_unit_of_measurement().empty()) | ||||
|         root["uom"] = obj->get_unit_of_measurement(); | ||||
| @@ -284,6 +295,9 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std: | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -332,6 +346,9 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail | ||||
|       root["assumed_state"] = obj->assumed_state(); | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -368,6 +385,9 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -404,6 +424,9 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -487,6 +510,9 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -603,6 +629,9 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi | ||||
|       } | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -684,6 +713,9 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -745,6 +777,9 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail | ||||
|         root["uom"] = obj->traits.get_unit_of_measurement(); | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     if (std::isnan(value)) { | ||||
| @@ -814,6 +849,9 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -872,6 +910,9 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -931,6 +972,9 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -992,6 +1036,9 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json | ||||
|       root["mode"] = (int) obj->traits.get_mode(); | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -1048,6 +1095,9 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value | ||||
|       } | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -1164,6 +1214,9 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf | ||||
|       } | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @@ -1257,6 +1310,9 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -1326,8 +1382,13 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { | ||||
|  | ||||
|     if (obj->get_traits().get_supports_position()) | ||||
|       root["position"] = obj->position; | ||||
|     if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|       root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| } | ||||
| @@ -1367,6 +1428,9 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -1453,6 +1517,9 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c | ||||
|       root["release_url"] = obj->update_info.release_url; | ||||
|       if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { | ||||
|         root["sorting_weight"] = this->sorting_entitys_[obj].weight; | ||||
|         if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { | ||||
|           root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -1751,8 +1818,12 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { | ||||
|  | ||||
| bool WebServer::isRequestHandlerTrivial() { return false; } | ||||
|  | ||||
| void WebServer::add_entity_to_sorting_list(EntityBase *entity, float weight) { | ||||
|   this->sorting_entitys_[entity] = SortingComponents{weight}; | ||||
| void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t group) { | ||||
|   this->sorting_entitys_[entity] = SortingComponents{weight, group}; | ||||
| } | ||||
|  | ||||
| void WebServer::add_sorting_group(uint64_t group_id, const std::string &group_name, float weight) { | ||||
|   this->sorting_groups_[group_id] = SortingGroup{group_name, weight}; | ||||
| } | ||||
|  | ||||
| void WebServer::schedule_(std::function<void()> &&f) { | ||||
|   | ||||
| @@ -44,6 +44,12 @@ struct UrlMatch { | ||||
|  | ||||
| struct SortingComponents { | ||||
|   float weight; | ||||
|   uint64_t group_id; | ||||
| }; | ||||
|  | ||||
| struct SortingGroup { | ||||
|   std::string name; | ||||
|   float weight; | ||||
| }; | ||||
|  | ||||
| enum JsonDetail { DETAIL_ALL, DETAIL_STATE }; | ||||
| @@ -337,7 +343,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { | ||||
|   /// This web handle is not trivial. | ||||
|   bool isRequestHandlerTrivial() override;  // NOLINT(readability-identifier-naming) | ||||
|  | ||||
|   void add_entity_to_sorting_list(EntityBase *entity, float weight); | ||||
|   void add_entity_config(EntityBase *entity, float weight, uint64_t group); | ||||
|   void add_sorting_group(uint64_t group_id, const std::string &group_name, float weight); | ||||
|  | ||||
|  protected: | ||||
|   void schedule_(std::function<void()> &&f); | ||||
| @@ -346,6 +353,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { | ||||
|   AsyncEventSource events_{"/events"}; | ||||
|   ListEntitiesIterator entities_iterator_; | ||||
|   std::map<EntityBase *, SortingComponents> sorting_entitys_; | ||||
|   std::map<uint64_t, SortingGroup> sorting_groups_; | ||||
|  | ||||
| #if USE_WEBSERVER_VERSION == 1 | ||||
|   const char *css_url_{nullptr}; | ||||
|   const char *js_url_{nullptr}; | ||||
|   | ||||
| @@ -934,7 +934,6 @@ CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature" | ||||
| CONF_WATCHDOG_THRESHOLD = "watchdog_threshold" | ||||
| CONF_WEB_SERVER = "web_server" | ||||
| CONF_WEB_SERVER_ID = "web_server_id" | ||||
| CONF_WEB_SERVER_SORTING_WEIGHT = "web_server_sorting_weight" | ||||
| CONF_WEIGHT = "weight" | ||||
| CONF_WHILE = "while" | ||||
| CONF_WHITE = "white" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user