[web_server] v3 entity grouping (#6833)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
RFDarter 2024-10-07 04:52:26 +02:00 committed by GitHub
parent 6a2ed8241e
commit 86a34f4b17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 265 additions and 110 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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]):

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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(

View File

@ -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]):

View File

@ -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):

View File

@ -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))

View File

@ -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(

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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.Optional(CONF_WEB_SERVER): cv.Schema(
{
cv.OnlyWith(CONF_WEB_SERVER_ID, "web_server"): cv.use_id(WebServer),
cv.Optional(CONF_WEB_SERVER_SORTING_WEIGHT): cv.All(
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)

View File

@ -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 (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) {

View File

@ -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};

View File

@ -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"

View File

@ -0,0 +1,37 @@
packages:
device_base: !include common.yaml
web_server:
port: 8080
version: 3
sorting_groups:
- id: sorting_group_1
name: "Group 1 Diplayed Last"
sorting_weight: 40
- id: sorting_group_2
name: "Group 2 Displayed Third"
sorting_weight: 30
- id: sorting_group_3
name: "Group 3 Displayed Second"
sorting_weight: 20
- id: sorting_group_4
name: "Group 4 Displayed First"
sorting_weight: 10
number:
- platform: template
name: "Template number"
optimistic: true
min_value: 0
max_value: 100
step: 1
web_server:
sorting_group_id: sorting_group_1
sorting_weight: -1
switch:
- platform: template
name: "Template Switch"
optimistic: true
web_server:
sorting_group_id: sorting_group_2
sorting_weight: -10

View File

@ -0,0 +1 @@
<<: !include common_v3.yaml