From ab889169888201baba781485bd474d9f721cd3e0 Mon Sep 17 00:00:00 2001
From: oarcher <olivier.archer@gmail.com>
Date: Thu, 16 May 2024 19:57:49 +0200
Subject: [PATCH] removed power management

---
 esphome/components/modem/__init__.py         |   7 +
 esphome/components/modem/modem_component.cpp | 160 ++-----------------
 esphome/components/modem/modem_component.h   |  24 +--
 3 files changed, 32 insertions(+), 159 deletions(-)

diff --git a/esphome/components/modem/__init__.py b/esphome/components/modem/__init__.py
index ebc3661f26..9d59e43fca 100644
--- a/esphome/components/modem/__init__.py
+++ b/esphome/components/modem/__init__.py
@@ -11,6 +11,7 @@ import esphome.codegen as cg
 import esphome.config_validation as cv
 from esphome.core import coroutine_with_priority
 from esphome.components.esp32 import add_idf_component, add_idf_sdkconfig_option
+from esphome.components.binary_sensor import BinarySensor
 
 CODEOWNERS = ["@oarcher"]
 DEPENDENCIES = ["esp32"]
@@ -23,6 +24,7 @@ CONF_APN = "apn"
 CONF_STATUS_PIN = "status_pin"
 CONF_DTR_PIN = "dtr_pin"
 CONF_INIT_AT = "init_at"
+CONF_READY = "ready"
 
 
 modem_ns = cg.esphome_ns.namespace("modem")
@@ -37,6 +39,7 @@ CONFIG_SCHEMA = cv.All(
             cv.Required(CONF_RX_PIN): cv.positive_int,
             cv.Required(CONF_MODEL): cv.string,
             cv.Required(CONF_APN): cv.string,
+            cv.Optional(CONF_READY): cv.use_id(BinarySensor),
             cv.Optional(CONF_FLIGHT_PIN): cv.positive_int,
             cv.Optional(CONF_POWER_PIN): cv.positive_int,
             cv.Optional(CONF_STATUS_PIN): cv.positive_int,
@@ -96,6 +99,10 @@ async def to_code(config):
         for cmd in init_at:
             cg.add(var.add_init_at_command(cmd))
 
+    if modem_ready := config.get(CONF_READY, None):
+        modem_ready_sensor = await cg.get_variable(modem_ready)
+        cg.add(var.set_ready_bsensor(modem_ready_sensor))
+
     cg.add(var.set_model(config[CONF_MODEL]))
     cg.add(var.set_apn(config[CONF_APN]))
 
diff --git a/esphome/components/modem/modem_component.cpp b/esphome/components/modem/modem_component.cpp
index 1f7622f4f5..51d14ccc4d 100644
--- a/esphome/components/modem/modem_component.cpp
+++ b/esphome/components/modem/modem_component.cpp
@@ -74,13 +74,6 @@ bool ModemComponent::is_connected() { return this->state_ == ModemComponentState
 void ModemComponent::setup() {
   ESP_LOGI(TAG, "Setting up Modem...");
 
-  this->config_gpio_();
-
-  if (this->get_status()) {
-    // at setup, the modem must be down
-    this->powerdown();
-  }
-
   ESP_LOGV(TAG, "DTE setup");
   esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
   this->dte_config_ = dte_config;
@@ -152,30 +145,6 @@ void ModemComponent::setup() {
 
   assert(this->dce);
 
-  this->poweron();
-
-  esp_modem::command_result res;
-  res = this->dce->sync();
-  int retry = 0;
-  while (res != command_result::OK) {
-    res = this->dce->sync();
-    if (res != command_result::OK) {
-      App.feed_wdt();
-      vTaskDelay(pdMS_TO_TICKS(1000));
-    }
-    retry++;
-    if (retry > 20)
-      break;
-  }
-
-  // send initial AT commands from yaml
-  for (const auto &cmd : this->init_at_commands_) {
-    std::string result;
-    command_result err = this->dce->at(cmd.c_str(), result, 1000);
-    delay(100);
-    ESP_LOGI(TAG, "Init AT command: %s (status %d) -> %s", cmd.c_str(), (int) err, result.c_str());
-  }
-
   this->started_ = true;
   ESP_LOGV(TAG, "Setup finished");
 }
@@ -184,19 +153,9 @@ void ModemComponent::start_connect_() {
   this->connect_begin_ = millis();
   this->status_set_warning("Starting connection");
 
-  if (!this->get_status()) {
-    this->poweron();
-  }
-
-  // esp_err_t err;
-  // err = esp_netif_set_hostname(this->ppp_netif_, App.get_name().c_str());
-  // if (err != ERR_OK) {
-  //   ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err));
-  // }
-
   global_modem_component->got_ipv4_address_ = false;  // why not this ?
 
-  this->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND);
+  this->dce->set_mode(modem_mode::COMMAND_MODE);
   vTaskDelay(pdMS_TO_TICKS(2000));
 
   command_result res = command_result::TIMEOUT;
@@ -205,7 +164,6 @@ void ModemComponent::start_connect_() {
 
   if (res != command_result::OK) {
     ESP_LOGW(TAG, "Unable to sync modem. Will retry later");
-    this->powerdown();
     return;
   }
 
@@ -240,13 +198,20 @@ void ModemComponent::start_connect_() {
     return;
   }
   vTaskDelay(pdMS_TO_TICKS(2000));
+
+  // send initial AT commands from yaml
+  for (const auto &cmd : this->init_at_commands_) {
+    std::string result;
+    command_result err = this->dce->at(cmd.c_str(), result, 1000);
+    delay(100);
+    ESP_LOGI(TAG, "Init AT command: %s (status %d) -> %s", cmd.c_str(), (int) err, result.c_str());
+  }
 }
 
 void ModemComponent::got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
   ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
   const esp_netif_ip_info_t *ip_info = &event->ip_info;
   ESP_LOGW(TAG, "[IP event] Got IP " IPSTR, IP2STR(&ip_info->ip));
-  vTaskDelay(pdMS_TO_TICKS(1000));  // FIXME tmp
   global_modem_component->got_ipv4_address_ = true;
   global_modem_component->connected_ = true;
 }
@@ -257,14 +222,18 @@ void ModemComponent::loop() {
   switch (this->state_) {
     case ModemComponentState::STOPPED:
       if (this->started_) {
-        ESP_LOGI(TAG, "Starting modem connection");
-        this->state_ = ModemComponentState::CONNECTING;
-        this->start_connect_();
+        if (!this->modem_ready()) {
+          break;
+        } else {
+          ESP_LOGI(TAG, "Starting modem connection");
+          this->state_ = ModemComponentState::CONNECTING;
+          this->start_connect_();
+        }
       }
       break;
     case ModemComponentState::CONNECTING:
       if (!this->started_) {
-        ESP_LOGI(TAG, "Stopped ethernet connection");
+        ESP_LOGI(TAG, "Stopped modem connection");
         this->state_ = ModemComponentState::STOPPED;
       } else if (this->connected_) {
         // connection established
@@ -308,101 +277,6 @@ void ModemComponent::dump_connect_params_() {
   ESP_LOGCONFIG(TAG, "  DNS fallback: %s", network::IPAddress(dns_fallback_ip).str().c_str());
 }
 
-void ModemComponent::config_gpio_() {
-  ESP_LOGV(TAG, "Configuring GPIOs...");
-  gpio_config_t io_conf = {};
-  io_conf.intr_type = GPIO_INTR_DISABLE;
-  io_conf.mode = GPIO_MODE_OUTPUT;
-  io_conf.pin_bit_mask = 0ULL;
-  if (this->power_pin_ != gpio_num_t::GPIO_NUM_NC) {
-    io_conf.pin_bit_mask = io_conf.pin_bit_mask | (1ULL << this->power_pin_);
-  }
-  if (this->flight_pin_ != gpio_num_t::GPIO_NUM_NC) {
-    io_conf.pin_bit_mask = io_conf.pin_bit_mask | (1ULL << this->flight_pin_);
-  }
-  if (this->dtr_pin_ != gpio_num_t::GPIO_NUM_NC) {
-    io_conf.pin_bit_mask = io_conf.pin_bit_mask | (1ULL << this->dtr_pin_);
-  }
-  // io_conf.pin_bit_mask = ((1ULL << this->power_pin_) | (1ULL << this->flight_pin_));
-
-  io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
-  io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
-
-  gpio_config(&io_conf);
-
-  io_conf.pin_bit_mask = 0ULL;
-  if (this->status_pin_ != gpio_num_t::GPIO_NUM_NC) {
-    io_conf.pin_bit_mask = io_conf.pin_bit_mask | (1ULL << this->status_pin_);
-  }
-  io_conf.mode = GPIO_MODE_INPUT;
-  gpio_config(&io_conf);
-}
-
-void ModemComponent::poweron() {
-  ESP_LOGI(TAG, "Power on  modem");
-
-  if (this->get_status()) {
-    ESP_LOGW(TAG, "modem is already on");
-  } else {
-    // https://github.com/Xinyuan-LilyGO/LilyGO-T-SIM7000G/issues/251
-    if (this->power_pin_ != gpio_num_t::GPIO_NUM_NC) {
-      vTaskDelay(pdMS_TO_TICKS(1000));
-      ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 0));  // low = on, high = off
-      vTaskDelay(pdMS_TO_TICKS(10));
-      ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 1));
-      vTaskDelay(pdMS_TO_TICKS(1010));
-      ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 0));
-      vTaskDelay(pdMS_TO_TICKS(4050));  // Ton uart 4.5sec but seems to need ~7sec after hard (button) reset
-      int retry = 0;
-      while (!this->get_status()) {
-        vTaskDelay(pdMS_TO_TICKS(1000));
-        retry++;
-        if (retry > 10) {
-          ESP_LOGW(TAG, "Unable to power on modem");
-          break;
-        }
-      }
-
-    } else {
-      ESP_LOGW(TAG, "No power pin defined. Trying to continue");
-    }
-  }
-
-  if (this->flight_pin_ != gpio_num_t::GPIO_NUM_NC) {
-    ESP_ERROR_CHECK(gpio_set_level(this->flight_pin_, 1));  // need to be high
-  } else {
-    ESP_LOGW(TAG, "No flight pin defined. Trying to continue");
-  }
-  if (this->dtr_pin_ != gpio_num_t::GPIO_NUM_NC) {
-    ESP_ERROR_CHECK(gpio_set_level(this->dtr_pin_, 1));
-  } else {
-    ESP_LOGW(TAG, "No dtr pin defined. Trying to continue");
-  }
-  vTaskDelay(pdMS_TO_TICKS(15000));
-  App.feed_wdt();
-}
-
-void ModemComponent::powerdown() {
-  ESP_LOGI(TAG, "Power down modem");
-  if (this->get_status()) {
-    // https://github.com/Xinyuan-LilyGO/T-SIM7600X/blob/master/examples/PowefOffModem/PowefOffModem.ino#L69-L71
-    ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 1));
-    vTaskDelay(pdMS_TO_TICKS(2600));
-    ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 0));
-    int retry = 0;
-    while (this->get_status()) {
-      vTaskDelay(pdMS_TO_TICKS(1000));
-      retry++;
-      if (retry > 20) {
-        ESP_LOGW(TAG, "Unable to power down modem");
-        break;
-      }
-    }
-  } else {
-    ESP_LOGW(TAG, "modem is already down");
-  }
-}
-
 }  // namespace modem
 }  // namespace esphome
 
diff --git a/esphome/components/modem/modem_component.h b/esphome/components/modem/modem_component.h
index f747784d00..520b61143b 100644
--- a/esphome/components/modem/modem_component.h
+++ b/esphome/components/modem/modem_component.h
@@ -4,11 +4,14 @@
 #include "esphome/core/component.h"
 #include "esphome/core/log.h"
 #include "esphome/components/network/util.h"
+#include "esphome/components/binary_sensor/binary_sensor.h"
+#include "esphome/components/template/binary_sensor/template_binary_sensor.h"
 
 #ifdef USE_ESP_IDF
 
-using esphome::esp_log_printf_;  // esp_modem will use esphome logger (needed if other components include
-                                 // esphome/core/log.h)
+// esp_modem will use esphome logger (needed if other components include esphome/core/log.h)
+// We need to do this because "cxx_include/esp_modem_api.hpp" is not a pure C++ header, and use logging.
+using esphome::esp_log_printf_;
 #include <cxx_include/esp_modem_api.hpp>
 #include <driver/gpio.h>
 #include <esp_modem_config.h>
@@ -39,37 +42,27 @@ class ModemComponent : public Component {
   bool is_connected();
   float get_setup_priority() const override;
   bool can_proceed() override;
-  void on_shutdown() override { powerdown(); }
   network::IPAddresses get_ip_addresses();
   std::string get_use_address() const;
   void set_use_address(const std::string &use_address);
-  void poweron();
-  void powerdown();
   void set_rx_pin(gpio_num_t rx_pin) { this->rx_pin_ = rx_pin; }
   void set_tx_pin(gpio_num_t tx_pin) { this->tx_pin_ = tx_pin; }
-  void set_power_pin(gpio_num_t power_pin) { this->power_pin_ = power_pin; }
-  void set_flight_pin(gpio_num_t flight_pin) { this->flight_pin_ = flight_pin; }
   void set_username(const std::string username) { this->username_ = std::move(username); }
   void set_password(const std::string password) { this->password_ = std::move(password); }
   void set_pin_code(const std::string pin_code) { this->pin_code_ = std::move(pin_code); }
   void set_apn(const std::string apn) { this->apn_ = std::move(apn); }
-  void set_status_pin(gpio_num_t status_pin) { this->status_pin_ = status_pin; }
-  void set_dtr_pin(gpio_num_t dtr_pin) { this->dtr_pin_ = dtr_pin; }
   void set_model(const std::string &model) {
     this->model_ = this->modem_model_map_.count(model) ? modem_model_map_[model] : ModemModel::UNKNOWN;
   }
+  void set_ready_bsensor(binary_sensor::BinarySensor *modem_ready) { this->modem_ready_ = modem_ready; }
   void add_init_at_command(const std::string &cmd) { this->init_at_commands_.push_back(cmd); }
-  bool get_status() { return gpio_get_level(this->status_pin_); }
+  bool modem_ready() { return this->modem_ready_->state; }
   std::unique_ptr<DCE> dce;
 
  protected:
   gpio_num_t rx_pin_ = gpio_num_t::GPIO_NUM_NC;
   gpio_num_t tx_pin_ = gpio_num_t::GPIO_NUM_NC;
-  gpio_num_t power_pin_ = gpio_num_t::GPIO_NUM_NC;
-  gpio_num_t flight_pin_ = gpio_num_t::GPIO_NUM_NC;
-  gpio_num_t status_pin_ = gpio_num_t::GPIO_NUM_NC;
-  gpio_num_t dtr_pin_ = gpio_num_t::GPIO_NUM_NC;
-
+  binary_sensor::BinarySensor *modem_ready_;
   std::string pin_code_;
   std::string username_;
   std::string password_;
@@ -94,7 +87,6 @@ class ModemComponent : public Component {
   static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
   void dump_connect_params_();
   std::string use_address_;
-  void config_gpio_();
 };
 
 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)