diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index f543c2356f..bdb94b3d9b 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -176,6 +176,10 @@ message DeviceInfoResponse { string model = 6; bool has_deep_sleep = 7; + + // The esphome project details if set + string project_name = 8; + string project_version = 9; } message ListEntitiesRequest { diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index eca95de3c2..175c3c4487 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -664,6 +664,10 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { #endif #ifdef USE_DEEP_SLEEP resp.has_deep_sleep = deep_sleep::global_has_deep_sleep; +#endif +#ifdef ESPHOME_PROJECT_NAME + resp.project_name = ESPHOME_PROJECT_NAME; + resp.project_version = ESPHOME_PROJECT_VERSION; #endif return resp; } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 76cdc44e1e..a9e9d64bc1 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -360,6 +360,14 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v this->model = value.as_string(); return true; } + case 8: { + this->project_name = value.as_string(); + return true; + } + case 9: { + this->project_version = value.as_string(); + return true; + } default: return false; } @@ -372,6 +380,8 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(5, this->compilation_time); buffer.encode_string(6, this->model); buffer.encode_bool(7, this->has_deep_sleep); + buffer.encode_string(8, this->project_name); + buffer.encode_string(9, this->project_version); } void DeviceInfoResponse::dump_to(std::string &out) const { char buffer[64]; @@ -403,6 +413,14 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append(" has_deep_sleep: "); out.append(YESNO(this->has_deep_sleep)); out.append("\n"); + + out.append(" project_name: "); + out.append("'").append(this->project_name).append("'"); + out.append("\n"); + + out.append(" project_version: "); + out.append("'").append(this->project_version).append("'"); + out.append("\n"); out.append("}"); } void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {} diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 365ea0025d..04d5834572 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -184,6 +184,8 @@ class DeviceInfoResponse : public ProtoMessage { std::string compilation_time{}; std::string model{}; bool has_deep_sleep{false}; + std::string project_name{}; + std::string project_version{}; void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; @@ -697,23 +699,23 @@ class CameraImageRequest : public ProtoMessage { }; class ListEntitiesClimateResponse : public ProtoMessage { public: - std::string object_id{}; // NOLINT - uint32_t key{0}; // NOLINT - std::string name{}; // NOLINT - std::string unique_id{}; // NOLINT - bool supports_current_temperature{false}; // NOLINT - bool supports_two_point_target_temperature{false}; // NOLINT - std::vector supported_modes{}; // NOLINT - float visual_min_temperature{0.0f}; // NOLINT - float visual_max_temperature{0.0f}; // NOLINT - float visual_temperature_step{0.0f}; // NOLINT - bool supports_away{false}; // NOLINT - bool supports_action{false}; // NOLINT - std::vector supported_fan_modes{}; // NOLINT - std::vector supported_swing_modes{}; // NOLINT - std::vector supported_custom_fan_modes{}; // NOLINT - std::vector supported_presets{}; // NOLINT - std::vector supported_custom_presets{}; // NOLINT + std::string object_id{}; + uint32_t key{0}; + std::string name{}; + std::string unique_id{}; + bool supports_current_temperature{false}; + bool supports_two_point_target_temperature{false}; + std::vector supported_modes{}; + float visual_min_temperature{0.0f}; + float visual_max_temperature{0.0f}; + float visual_temperature_step{0.0f}; + bool supports_away{false}; + bool supports_action{false}; + std::vector supported_fan_modes{}; + std::vector supported_swing_modes{}; + std::vector supported_custom_fan_modes{}; + std::vector supported_presets{}; + std::vector supported_custom_presets{}; void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; @@ -724,19 +726,19 @@ class ListEntitiesClimateResponse : public ProtoMessage { }; class ClimateStateResponse : public ProtoMessage { public: - uint32_t key{0}; // NOLINT - enums::ClimateMode mode{}; // NOLINT - float current_temperature{0.0f}; // NOLINT - float target_temperature{0.0f}; // NOLINT - float target_temperature_low{0.0f}; // NOLINT - float target_temperature_high{0.0f}; // NOLINT - bool away{false}; // NOLINT - enums::ClimateAction action{}; // NOLINT - enums::ClimateFanMode fan_mode{}; // NOLINT - enums::ClimateSwingMode swing_mode{}; // NOLINT - std::string custom_fan_mode{}; // NOLINT - enums::ClimatePreset preset{}; // NOLINT - std::string custom_preset{}; // NOLINT + uint32_t key{0}; + enums::ClimateMode mode{}; + float current_temperature{0.0f}; + float target_temperature{0.0f}; + float target_temperature_low{0.0f}; + float target_temperature_high{0.0f}; + bool away{false}; + enums::ClimateAction action{}; + enums::ClimateFanMode fan_mode{}; + enums::ClimateSwingMode swing_mode{}; + std::string custom_fan_mode{}; + enums::ClimatePreset preset{}; + std::string custom_preset{}; void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; @@ -747,27 +749,27 @@ class ClimateStateResponse : public ProtoMessage { }; class ClimateCommandRequest : public ProtoMessage { public: - uint32_t key{0}; // NOLINT - bool has_mode{false}; // NOLINT - enums::ClimateMode mode{}; // NOLINT - bool has_target_temperature{false}; // NOLINT - float target_temperature{0.0f}; // NOLINT - bool has_target_temperature_low{false}; // NOLINT - float target_temperature_low{0.0f}; // NOLINT - bool has_target_temperature_high{false}; // NOLINT - float target_temperature_high{0.0f}; // NOLINT - bool has_away{false}; // NOLINT - bool away{false}; // NOLINT - bool has_fan_mode{false}; // NOLINT - enums::ClimateFanMode fan_mode{}; // NOLINT - bool has_swing_mode{false}; // NOLINT - enums::ClimateSwingMode swing_mode{}; // NOLINT - bool has_custom_fan_mode{false}; // NOLINT - std::string custom_fan_mode{}; // NOLINT - bool has_preset{false}; // NOLINT - enums::ClimatePreset preset{}; // NOLINT - bool has_custom_preset{false}; // NOLINT - std::string custom_preset{}; // NOLINT + uint32_t key{0}; + bool has_mode{false}; + enums::ClimateMode mode{}; + bool has_target_temperature{false}; + float target_temperature{0.0f}; + bool has_target_temperature_low{false}; + float target_temperature_low{0.0f}; + bool has_target_temperature_high{false}; + float target_temperature_high{0.0f}; + bool has_away{false}; + bool away{false}; + bool has_fan_mode{false}; + enums::ClimateFanMode fan_mode{}; + bool has_swing_mode{false}; + enums::ClimateSwingMode swing_mode{}; + bool has_custom_fan_mode{false}; + std::string custom_fan_mode{}; + bool has_preset{false}; + enums::ClimatePreset preset{}; + bool has_custom_preset{false}; + std::string custom_preset{}; void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; diff --git a/esphome/const.py b/esphome/const.py index 907a49e2ad..24c4df7877 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -459,6 +459,7 @@ CONF_PRESET_ECO = "preset_eco" CONF_PRESET_SLEEP = "preset_sleep" CONF_PRESSURE = "pressure" CONF_PRIORITY = "priority" +CONF_PROJECT = "project" CONF_PROTOCOL = "protocol" CONF_PULL_MODE = "pull_mode" CONF_PULSE_LENGTH = "pulse_length" @@ -616,6 +617,7 @@ CONF_UUID = "uuid" CONF_VALUE = "value" CONF_VARIABLES = "variables" CONF_VARIANT = "variant" +CONF_VERSION = "version" CONF_VISUAL = "visual" CONF_VOLTAGE = "voltage" CONF_VOLTAGE_ATTENUATION = "voltage_attenuation" diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 5e23c6250b..99726b6248 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -103,6 +103,9 @@ void Application::loop() { if (this->dump_config_at_ >= 0 && this->dump_config_at_ < this->components_.size()) { if (this->dump_config_at_ == 0) { ESP_LOGI(TAG, "ESPHome version " ESPHOME_VERSION " compiled on %s", this->compilation_time_.c_str()); +#ifdef ESPHOME_PROJECT_NAME + ESP_LOGI(TAG, "Project " ESPHOME_PROJECT_NAME " version " ESPHOME_PROJECT_VERSION); +#endif } this->components_[this->dump_config_at_]->dump_config(); diff --git a/esphome/core/config.py b/esphome/core/config.py index c6404fc6de..cdd98afc66 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -21,10 +21,12 @@ from esphome.const import ( CONF_PLATFORM, CONF_PLATFORMIO_OPTIONS, CONF_PRIORITY, + CONF_PROJECT, CONF_TRIGGER_ID, CONF_ESP8266_RESTORE_FROM_FLASH, ARDUINO_VERSION_ESP8266, ARDUINO_VERSION_ESP32, + CONF_VERSION, ESP_PLATFORMS, ) from esphome.core import CORE, coroutine_with_priority @@ -144,6 +146,15 @@ 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") + + value = value.replace(" ", "_") + + return value + + CONFIG_SCHEMA = cv.Schema( { cv.Required(CONF_NAME): cv.valid_name, @@ -184,6 +195,12 @@ CONFIG_SCHEMA = cv.Schema( 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("esphome_core_version"): cv.invalid( "The esphome_core_version option has been " "removed in 1.13 - the esphome core source " @@ -331,3 +348,7 @@ async def to_code(config): if config[CONF_INCLUDES]: CORE.add_job(add_includes, config[CONF_INCLUDES]) + + if CONF_PROJECT in config: + cg.add_define("ESPHOME_PROJECT_NAME", config[CONF_PROJECT][CONF_NAME]) + cg.add_define("ESPHOME_PROJECT_VERSION", config[CONF_PROJECT][CONF_VERSION]) diff --git a/esphome/core/util.cpp b/esphome/core/util.cpp index 73086a750a..4d27c04d2c 100644 --- a/esphome/core/util.cpp +++ b/esphome/core/util.cpp @@ -94,6 +94,11 @@ void network_setup_mdns(IPAddress address, int interface) { MDNS.addServiceTxt("esphomelib", "tcp", "version", ESPHOME_VERSION); MDNS.addServiceTxt("esphomelib", "tcp", "address", network_get_address().c_str()); MDNS.addServiceTxt("esphomelib", "tcp", "mac", get_mac_address().c_str()); + +#ifdef ESPHOME_PROJECT_NAME + MDNS.addServiceTxt("esphomelib", "tcp", "project_name", ESPHOME_PROJECT_NAME); + MDNS.addServiceTxt("esphomelib", "tcp", "project_version", ESPHOME_PROJECT_VERSION); +#endif } else { #endif // Publish "http" service if not using native API nor the webserver component diff --git a/tests/test5.yaml b/tests/test5.yaml index 993c421bdd..d4e19d985b 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -3,6 +3,9 @@ esphome: platform: ESP32 board: nodemcu-32s build_path: build/test5 + project: + name: esphome.test5_project + version: "1.0.0" wifi: networks: