diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 684540ffa6..6fd6f05c45 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -213,6 +213,7 @@ message DeviceInfoResponse { // The esphome project details if set string project_name = 8; string project_version = 9; + string project_manufacturer = 18; uint32 webserver_port = 10; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index bb55a2ccf6..6e979e20cc 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1479,7 +1479,13 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { resp.has_deep_sleep = deep_sleep::global_has_deep_sleep; #endif #ifdef ESPHOME_PROJECT_NAME - resp.project_name = ESPHOME_PROJECT_NAME; + if (this->client_api_version_major_ > 1 || + (this->client_api_version_major_ == 1 && this->client_api_version_minor_ >= 11)) { + resp.project_name = ESPHOME_PROJECT_NAME; + resp.project_manufacturer = ESPHOME_PROJECT_MANUFACTURER; + } else { + resp.project_name = ESPHOME_PROJECT_MANUFACTURER "." ESPHOME_PROJECT_NAME; + } resp.project_version = ESPHOME_PROJECT_VERSION; #endif #ifdef USE_WEBSERVER diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 8df152881c..e058b9c2b3 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -826,6 +826,10 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v this->project_version = value.as_string(); return true; } + case 18: { + this->project_manufacturer = value.as_string(); + return true; + } case 12: { this->manufacturer = value.as_string(); return true; @@ -852,6 +856,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(7, this->has_deep_sleep); buffer.encode_string(8, this->project_name); buffer.encode_string(9, this->project_version); + buffer.encode_string(18, this->project_manufacturer); buffer.encode_uint32(10, this->webserver_port); buffer.encode_uint32(11, this->legacy_bluetooth_proxy_version); buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags); @@ -901,6 +906,10 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append("'").append(this->project_version).append("'"); out.append("\n"); + out.append(" project_manufacturer: "); + out.append("'").append(this->project_manufacturer).append("'"); + out.append("\n"); + out.append(" webserver_port: "); sprintf(buffer, "%" PRIu32, this->webserver_port); out.append(buffer); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 063c217bf7..97e9d2a0d6 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -346,6 +346,7 @@ class DeviceInfoResponse : public ProtoMessage { bool has_deep_sleep{false}; std::string project_name{}; std::string project_version{}; + std::string project_manufacturer{}; uint32_t webserver_port{0}; uint32_t legacy_bluetooth_proxy_version{0}; uint32_t bluetooth_proxy_feature_flags{0}; diff --git a/esphome/const.py b/esphome/const.py index 3d3bfcc244..7bc118ddd6 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -463,6 +463,7 @@ CONF_MAGNITUDE = "magnitude" CONF_MAINS_FILTER = "mains_filter" CONF_MAKE_ID = "make_id" CONF_MANUAL_IP = "manual_ip" +CONF_MANUFACTURER = "manufacturer" CONF_MANUFACTURER_ID = "manufacturer_id" CONF_MASK_DISTURBER = "mask_disturber" CONF_MAX_BRIGHTNESS = "max_brightness" diff --git a/esphome/core/config.py b/esphome/core/config.py index eee8b73934..a4214dd597 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -19,6 +19,7 @@ from esphome.const import ( CONF_FRIENDLY_NAME, CONF_INCLUDES, CONF_LIBRARIES, + CONF_MANUFACTURER, CONF_MIN_VERSION, CONF_NAME, CONF_NAME_ADD_MAC_SUFFIX, @@ -96,10 +97,33 @@ def valid_include(value): return value -def valid_project_name(value: str): - if value.count(".") != 1: - raise cv.Invalid("project name needs to have a namespace") - return value +def _validate_project_info(config): + name = config[CONF_NAME] + dot_count = name.count(".") + if CONF_MANUFACTURER not in config: + if dot_count != 1: + raise cv.Invalid( + f"'{CONF_MANUFACTURER}' is a required option for [{CONF_PROJECT}]" + ) + author, name = name.split(".", 1) + config[CONF_MANUFACTURER] = author + config[CONF_NAME] = name + _LOGGER.warning( + "Prefixing %s %s with %s namespace is deprecated and will be disallowed in a future release. " + "Please see current documentation for the correct way to define the %s information.", + CONF_PROJECT, + CONF_NAME, + CONF_MANUFACTURER, + CONF_PROJECT, + ) + elif dot_count != 0: + author, name = name.split(".", 1) + raise cv.Invalid( + f"Remove {CONF_MANUFACTURER} namespace '{author}.' from the {CONF_PROJECT} {CONF_NAME}", + path=[CONF_NAME], + ) + + return config if "ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT" in os.environ: @@ -110,7 +134,6 @@ if "ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT" in os.environ: else: _compile_process_limit_default = cv.UNDEFINED - CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONFIG_SCHEMA = cv.All( cv.Schema( @@ -145,20 +168,22 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(valid_include), cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict), cv.Optional(CONF_NAME_ADD_MAC_SUFFIX, default=False): cv.boolean, - cv.Optional(CONF_PROJECT): cv.Schema( - { - cv.Required(CONF_NAME): cv.All( - cv.string_strict, valid_project_name - ), - cv.Required(CONF_VERSION): cv.string_strict, - cv.Optional(CONF_ON_UPDATE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ProjectUpdateTrigger - ), - } - ), - } + cv.Optional(CONF_PROJECT): cv.All( + cv.Schema( + { + cv.Optional(CONF_MANUFACTURER): cv.string_strict, + cv.Required(CONF_NAME): cv.string_strict, + cv.Required(CONF_VERSION): cv.string_strict, + cv.Optional(CONF_ON_UPDATE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ProjectUpdateTrigger + ), + } + ), + } + ), + _validate_project_info, ), cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All( cv.version_number, cv.validate_esphome_version @@ -394,6 +419,7 @@ async def to_code(config): if project_conf := config.get(CONF_PROJECT): cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME]) + cg.add_define("ESPHOME_PROJECT_AUTHOR", project_conf[CONF_MANUFACTURER]) cg.add_define("ESPHOME_PROJECT_VERSION", project_conf[CONF_VERSION]) cg.add_define("ESPHOME_PROJECT_VERSION_30", project_conf[CONF_VERSION][:29]) for conf in project_conf.get(CONF_ON_UPDATE, []): diff --git a/esphome/core/defines.h b/esphome/core/defines.h index eb3b20d007..34bdec338b 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -10,6 +10,7 @@ // Informative flags #define ESPHOME_BOARD "dummy_board" #define ESPHOME_PROJECT_NAME "dummy project" +#define ESPHOME_PROJECT_MANUFACTURER "ESPHome" #define ESPHOME_PROJECT_VERSION "v2" #define ESPHOME_PROJECT_VERSION_30 "v2" #define ESPHOME_VARIANT "ESP32"