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_ON_CONNECT, | ||||
|     CONF_ON_DISCONNECT, | ||||
|     CONF_ON_ERROR, | ||||
|     CONF_PASSWORD, | ||||
|     CONF_POWER_SAVE_MODE, | ||||
|     CONF_PRIORITY, | ||||
| @@ -34,6 +35,7 @@ from esphome.const import ( | ||||
|     CONF_SSID, | ||||
|     CONF_STATIC_IP, | ||||
|     CONF_SUBNET, | ||||
|     CONF_TIMEOUT, | ||||
|     CONF_TTLS_PHASE_2, | ||||
|     CONF_USE_ADDRESS, | ||||
|     CONF_USERNAME, | ||||
| @@ -46,6 +48,7 @@ from . import wpa2_eap | ||||
| AUTO_LOAD = ["network"] | ||||
|  | ||||
| NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2] | ||||
| CONF_SAVE = "save" | ||||
|  | ||||
| wifi_ns = cg.esphome_ns.namespace("wifi") | ||||
| EAPAuth = wifi_ns.struct("EAPAuth") | ||||
| @@ -63,6 +66,9 @@ WiFiConnectedCondition = wifi_ns.class_("WiFiConnectedCondition", Condition) | ||||
| WiFiEnabledCondition = wifi_ns.class_("WiFiEnabledCondition", Condition) | ||||
| WiFiEnableAction = wifi_ns.class_("WiFiEnableAction", automation.Action) | ||||
| WiFiDisableAction = wifi_ns.class_("WiFiDisableAction", automation.Action) | ||||
| WiFiConfigureAction = wifi_ns.class_( | ||||
|     "WiFiConfigureAction", automation.Action, cg.Component | ||||
| ) | ||||
|  | ||||
|  | ||||
| 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({})) | ||||
| async def wifi_disable_to_code(config, action_id, template_arg, args): | ||||
|     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(); | ||||
|  | ||||
|   void set_sta(const WiFiAP &ap); | ||||
|   WiFiAP get_sta() { return this->selected_ap_; } | ||||
|   void add_sta(const WiFiAP &ap); | ||||
|   void clear_sta(); | ||||
|  | ||||
| @@ -443,6 +444,84 @@ template<typename... Ts> class WiFiDisableAction : public Action<Ts...> { | ||||
|   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 esphome | ||||
| #endif | ||||
|   | ||||
| @@ -3,6 +3,13 @@ esphome: | ||||
|     then: | ||||
|       - wifi.disable | ||||
|       - 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: | ||||
|   ssid: MySSID | ||||
|   | ||||
		Reference in New Issue
	
	Block a user