mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	feat(WiFi): Add wifi.configure action (#7335)
This commit is contained in:
		| @@ -27,6 +27,7 @@ from esphome.const import ( | |||||||
|     CONF_NETWORKS, |     CONF_NETWORKS, | ||||||
|     CONF_ON_CONNECT, |     CONF_ON_CONNECT, | ||||||
|     CONF_ON_DISCONNECT, |     CONF_ON_DISCONNECT, | ||||||
|  |     CONF_ON_ERROR, | ||||||
|     CONF_PASSWORD, |     CONF_PASSWORD, | ||||||
|     CONF_POWER_SAVE_MODE, |     CONF_POWER_SAVE_MODE, | ||||||
|     CONF_PRIORITY, |     CONF_PRIORITY, | ||||||
| @@ -34,6 +35,7 @@ from esphome.const import ( | |||||||
|     CONF_SSID, |     CONF_SSID, | ||||||
|     CONF_STATIC_IP, |     CONF_STATIC_IP, | ||||||
|     CONF_SUBNET, |     CONF_SUBNET, | ||||||
|  |     CONF_TIMEOUT, | ||||||
|     CONF_TTLS_PHASE_2, |     CONF_TTLS_PHASE_2, | ||||||
|     CONF_USE_ADDRESS, |     CONF_USE_ADDRESS, | ||||||
|     CONF_USERNAME, |     CONF_USERNAME, | ||||||
| @@ -46,6 +48,7 @@ from . import wpa2_eap | |||||||
| AUTO_LOAD = ["network"] | AUTO_LOAD = ["network"] | ||||||
|  |  | ||||||
| NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2] | NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2] | ||||||
|  | CONF_SAVE = "save" | ||||||
|  |  | ||||||
| wifi_ns = cg.esphome_ns.namespace("wifi") | wifi_ns = cg.esphome_ns.namespace("wifi") | ||||||
| EAPAuth = wifi_ns.struct("EAPAuth") | EAPAuth = wifi_ns.struct("EAPAuth") | ||||||
| @@ -63,6 +66,9 @@ WiFiConnectedCondition = wifi_ns.class_("WiFiConnectedCondition", Condition) | |||||||
| WiFiEnabledCondition = wifi_ns.class_("WiFiEnabledCondition", Condition) | WiFiEnabledCondition = wifi_ns.class_("WiFiEnabledCondition", Condition) | ||||||
| WiFiEnableAction = wifi_ns.class_("WiFiEnableAction", automation.Action) | WiFiEnableAction = wifi_ns.class_("WiFiEnableAction", automation.Action) | ||||||
| WiFiDisableAction = wifi_ns.class_("WiFiDisableAction", automation.Action) | WiFiDisableAction = wifi_ns.class_("WiFiDisableAction", automation.Action) | ||||||
|  | WiFiConfigureAction = wifi_ns.class_( | ||||||
|  |     "WiFiConfigureAction", automation.Action, cg.Component | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def validate_password(value): | def validate_password(value): | ||||||
| @@ -483,3 +489,39 @@ async def wifi_enable_to_code(config, action_id, template_arg, args): | |||||||
| @automation.register_action("wifi.disable", WiFiDisableAction, cv.Schema({})) | @automation.register_action("wifi.disable", WiFiDisableAction, cv.Schema({})) | ||||||
| async def wifi_disable_to_code(config, action_id, template_arg, args): | async def wifi_disable_to_code(config, action_id, template_arg, args): | ||||||
|     return cg.new_Pvariable(action_id, template_arg) |     return cg.new_Pvariable(action_id, template_arg) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "wifi.configure", | ||||||
|  |     WiFiConfigureAction, | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.Required(CONF_SSID): cv.templatable(cv.ssid), | ||||||
|  |             cv.Required(CONF_PASSWORD): cv.templatable(validate_password), | ||||||
|  |             cv.Optional(CONF_SAVE, default=True): cv.templatable(cv.boolean), | ||||||
|  |             cv.Optional(CONF_TIMEOUT, default="30000ms"): cv.templatable( | ||||||
|  |                 cv.positive_time_period_milliseconds | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True), | ||||||
|  |             cv.Optional(CONF_ON_ERROR): automation.validate_automation(single=True), | ||||||
|  |         } | ||||||
|  |     ), | ||||||
|  | ) | ||||||
|  | async def wifi_set_sta_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     ssid = await cg.templatable(config[CONF_SSID], args, cg.std_string) | ||||||
|  |     password = await cg.templatable(config[CONF_PASSWORD], args, cg.std_string) | ||||||
|  |     save = await cg.templatable(config[CONF_SAVE], args, cg.bool_) | ||||||
|  |     timeout = await cg.templatable(config.get(CONF_TIMEOUT), args, cg.uint32) | ||||||
|  |     cg.add(var.set_ssid(ssid)) | ||||||
|  |     cg.add(var.set_password(password)) | ||||||
|  |     cg.add(var.set_save(save)) | ||||||
|  |     cg.add(var.set_connection_timeout(timeout)) | ||||||
|  |     if on_connect_config := config.get(CONF_ON_CONNECT): | ||||||
|  |         await automation.build_automation( | ||||||
|  |             var.get_connect_trigger(), [], on_connect_config | ||||||
|  |         ) | ||||||
|  |     if on_error_config := config.get(CONF_ON_ERROR): | ||||||
|  |         await automation.build_automation(var.get_error_trigger(), [], on_error_config) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     return var | ||||||
|   | |||||||
| @@ -209,6 +209,7 @@ class WiFiComponent : public Component { | |||||||
|   WiFiComponent(); |   WiFiComponent(); | ||||||
|  |  | ||||||
|   void set_sta(const WiFiAP &ap); |   void set_sta(const WiFiAP &ap); | ||||||
|  |   WiFiAP get_sta() { return this->selected_ap_; } | ||||||
|   void add_sta(const WiFiAP &ap); |   void add_sta(const WiFiAP &ap); | ||||||
|   void clear_sta(); |   void clear_sta(); | ||||||
|  |  | ||||||
| @@ -443,6 +444,84 @@ template<typename... Ts> class WiFiDisableAction : public Action<Ts...> { | |||||||
|   void play(Ts... x) override { global_wifi_component->disable(); } |   void play(Ts... x) override { global_wifi_component->disable(); } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class WiFiConfigureAction : public Action<Ts...>, public Component { | ||||||
|  |  public: | ||||||
|  |   TEMPLATABLE_VALUE(std::string, ssid) | ||||||
|  |   TEMPLATABLE_VALUE(std::string, password) | ||||||
|  |   TEMPLATABLE_VALUE(bool, save) | ||||||
|  |   TEMPLATABLE_VALUE(uint32_t, connection_timeout) | ||||||
|  |  | ||||||
|  |   void play(Ts... x) override { | ||||||
|  |     auto ssid = this->ssid_.value(x...); | ||||||
|  |     auto password = this->password_.value(x...); | ||||||
|  |     // Avoid multiple calls | ||||||
|  |     if (this->connecting_) | ||||||
|  |       return; | ||||||
|  |     // If already connected to the same AP, do nothing | ||||||
|  |     if (global_wifi_component->wifi_ssid() == ssid) { | ||||||
|  |       // Callback to notify the user that the connection was successful | ||||||
|  |       this->connect_trigger_->trigger(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // Create a new WiFiAP object with the new SSID and password | ||||||
|  |     this->new_sta_.set_ssid(ssid); | ||||||
|  |     this->new_sta_.set_password(password); | ||||||
|  |     // Save the current STA | ||||||
|  |     this->old_sta_ = global_wifi_component->get_sta(); | ||||||
|  |     // Disable WiFi | ||||||
|  |     global_wifi_component->disable(); | ||||||
|  |     // Set the state to connecting | ||||||
|  |     this->connecting_ = true; | ||||||
|  |     // Store the new STA so once the WiFi is enabled, it will connect to it | ||||||
|  |     // This is necessary because the WiFiComponent will raise an error and fallback to the saved STA | ||||||
|  |     // if trying to connect to a new STA while already connected to another one | ||||||
|  |     if (this->save_.value(x...)) { | ||||||
|  |       global_wifi_component->save_wifi_sta(new_sta_.get_ssid(), new_sta_.get_password()); | ||||||
|  |     } else { | ||||||
|  |       global_wifi_component->set_sta(new_sta_); | ||||||
|  |     } | ||||||
|  |     // Enable WiFi | ||||||
|  |     global_wifi_component->enable(); | ||||||
|  |     // Set timeout for the connection | ||||||
|  |     this->set_timeout("wifi-connect-timeout", this->connection_timeout_.value(x...), [this]() { | ||||||
|  |       this->connecting_ = false; | ||||||
|  |       // If the timeout is reached, stop connecting and revert to the old AP | ||||||
|  |       global_wifi_component->disable(); | ||||||
|  |       global_wifi_component->save_wifi_sta(old_sta_.get_ssid(), old_sta_.get_password()); | ||||||
|  |       global_wifi_component->enable(); | ||||||
|  |       // Callback to notify the user that the connection failed | ||||||
|  |       this->error_trigger_->trigger(); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Trigger<> *get_connect_trigger() const { return this->connect_trigger_; } | ||||||
|  |   Trigger<> *get_error_trigger() const { return this->error_trigger_; } | ||||||
|  |  | ||||||
|  |   void loop() override { | ||||||
|  |     if (!this->connecting_) | ||||||
|  |       return; | ||||||
|  |     if (global_wifi_component->is_connected()) { | ||||||
|  |       // The WiFi is connected, stop the timeout and reset the connecting flag | ||||||
|  |       this->cancel_timeout("wifi-connect-timeout"); | ||||||
|  |       this->connecting_ = false; | ||||||
|  |       if (global_wifi_component->wifi_ssid() == this->new_sta_.get_ssid()) { | ||||||
|  |         // Callback to notify the user that the connection was successful | ||||||
|  |         this->connect_trigger_->trigger(); | ||||||
|  |       } else { | ||||||
|  |         // Callback to notify the user that the connection failed | ||||||
|  |         this->error_trigger_->trigger(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool connecting_{false}; | ||||||
|  |   WiFiAP new_sta_; | ||||||
|  |   WiFiAP old_sta_; | ||||||
|  |   Trigger<> *connect_trigger_{new Trigger<>()}; | ||||||
|  |   Trigger<> *error_trigger_{new Trigger<>()}; | ||||||
|  | }; | ||||||
|  |  | ||||||
| }  // namespace wifi | }  // namespace wifi | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -3,6 +3,13 @@ esphome: | |||||||
|     then: |     then: | ||||||
|       - wifi.disable |       - wifi.disable | ||||||
|       - wifi.enable |       - wifi.enable | ||||||
|  |       - wifi.configure: | ||||||
|  |           ssid: MySSID | ||||||
|  |           password: password1 | ||||||
|  |           on_connect: | ||||||
|  |             - logger.log: "Connected to WiFi!" | ||||||
|  |           on_error: | ||||||
|  |             - logger.log: "Failed to connect to WiFi!" | ||||||
|  |  | ||||||
| wifi: | wifi: | ||||||
|   ssid: MySSID |   ssid: MySSID | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user