diff --git a/esphome/components/modem/helpers.cpp b/esphome/components/modem/helpers.cpp index cc7b7e3c03..07ae746271 100644 --- a/esphome/components/modem/helpers.cpp +++ b/esphome/components/modem/helpers.cpp @@ -49,6 +49,142 @@ std::string state_to_string(ModemComponentState state) { return str; } +std::string network_system_mode_to_string(int mode) { + std::string network_type = "Not available"; + // Access Technology from AT+CNSMOD? + // see https://www.waveshare.com/w/upload/a/af/SIM7500_SIM7600_Series_AT_Command_Manual_V3.00.pdf, page 109 + switch (mode) { + case 0: + network_type = "No service"; + break; + case 1: + network_type = "GSM"; + break; + case 2: + network_type = "GPRS"; + break; + case 3: + network_type = "EGPRS (EDGE)"; + break; + case 4: + network_type = "WCDMA"; + break; + case 5: + network_type = "HSDPA only (WCDMA)"; + break; + case 6: + network_type = "HSUPA only (WCDMA)"; + break; + case 7: + network_type = "HSPA (HSDPA and HSUPA, WCDMA)"; + break; + case 8: + network_type = "LTE"; + break; + case 9: + network_type = "TDS-CDMA"; + break; + case 10: + network_type = "TDS-HSDPA only"; + break; + case 11: + network_type = "TDS-HSUPA only"; + break; + case 12: + network_type = "TDS-HSPA (HSDPA and HSUPA)"; + break; + case 13: + network_type = "CDMA"; + break; + case 14: + network_type = "EVDO"; + break; + case 15: + network_type = "HYBRID (CDMA and EVDO)"; + break; + case 16: + network_type = "1XLTE (CDMA and LTE)"; + break; + case 23: + network_type = "EHRPD"; + break; + case 24: + network_type = "HYBRID (CDMA and EHRPD)"; + break; + default: + network_type = "Unknown"; + } + return network_type; +} + +std::string get_signal_bars(float rssi) { return get_signal_bars(rssi, true); } +std::string get_signal_bars(float rssi, bool color) { + // adapted from wifi_component.cpp + // LOWER ONE QUARTER BLOCK + // Unicode: U+2582, UTF-8: E2 96 82 + // LOWER HALF BLOCK + // Unicode: U+2584, UTF-8: E2 96 84 + // LOWER THREE QUARTERS BLOCK + // Unicode: U+2586, UTF-8: E2 96 86 + // FULL BLOCK + // Unicode: U+2588, UTF-8: E2 96 88 + + if (!color) { + if (std::isnan(rssi)) { + return {}; + } else if (rssi >= -50) { + return "High"; + } else if (rssi >= -65) { + return "Good"; + } else if (rssi >= -85) { + return "Medium"; + } else { + return "Low"; + } + } else { + if (std::isnan(rssi)) { + return "\033[0;31m" // red + "!" + "\033[0;37m" + "\xe2\x96\x84" + "\xe2\x96\x86" + "\xe2\x96\x88" + "\033[0m"; + } else if (rssi >= -50) { + return "\033[0;32m" // green + "\xe2\x96\x82" + "\xe2\x96\x84" + "\xe2\x96\x86" + "\xe2\x96\x88" + "\033[0m"; + } else if (rssi >= -65) { + return "\033[0;33m" // yellow + "\xe2\x96\x82" + "\xe2\x96\x84" + "\xe2\x96\x86" + "\033[0;37m" + "\xe2\x96\x88" + "\033[0m"; + } else if (rssi >= -85) { + return "\033[0;33m" // yellow + "\xe2\x96\x82" + "\xe2\x96\x84" + "\033[0;37m" + "\xe2\x96\x86" + "\xe2\x96\x88" + "\033[0m"; + } else { + return "\033[0;31m" // red + "\xe2\x96\x82" + "\033[0;37m" + "\xe2\x96\x84" + "\xe2\x96\x86" + "\xe2\x96\x88" + "\033[0m"; + } + } +} + } // namespace modem } // namespace esphome #endif // USE_ESP_IDF diff --git a/esphome/components/modem/helpers.h b/esphome/components/modem/helpers.h index 92b9b718f5..380fe56890 100644 --- a/esphome/components/modem/helpers.h +++ b/esphome/components/modem/helpers.h @@ -10,8 +10,10 @@ namespace esphome { namespace modem { std::string command_result_to_string(command_result err); - std::string state_to_string(ModemComponentState state); +std::string network_system_mode_to_string(int mode); +std::string get_signal_bars(float rssi); +std::string get_signal_bars(float rssi, bool color); } // namespace modem } // namespace esphome diff --git a/esphome/components/modem/text_sensor/__init__.py b/esphome/components/modem/text_sensor/__init__.py index e466ad0550..560796a21e 100644 --- a/esphome/components/modem/text_sensor/__init__.py +++ b/esphome/components/modem/text_sensor/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg from esphome.components import text_sensor import esphome.config_validation as cv -from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY +from esphome.const import CONF_ID, CONF_SIGNAL_STRENGTH, DEVICE_CLASS_EMPTY from .. import MODEM_COMPONENT_SCHEMA, final_validate_platform, modem_ns @@ -24,6 +24,9 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_NETWORK_TYPE): text_sensor.text_sensor_schema( device_class=DEVICE_CLASS_EMPTY, ), + cv.Optional(CONF_SIGNAL_STRENGTH): text_sensor.text_sensor_schema( + device_class=DEVICE_CLASS_EMPTY, + ), } ) .extend(MODEM_COMPONENT_SCHEMA) @@ -40,4 +43,8 @@ async def to_code(config): network_type_text_sensor = await text_sensor.new_text_sensor(network_type) cg.add(var.set_network_type_text_sensor(network_type_text_sensor)) + if signal_strength := config.get(CONF_SIGNAL_STRENGTH, None): + signal_strength_text_sensor = await text_sensor.new_text_sensor(signal_strength) + cg.add(var.set_signal_strength_text_sensor(signal_strength_text_sensor)) + await cg.register_component(var, config) diff --git a/esphome/components/modem/text_sensor/modem_text_sensor.cpp b/esphome/components/modem/text_sensor/modem_text_sensor.cpp index b5fa13ee8b..4c15609e0e 100644 --- a/esphome/components/modem/text_sensor/modem_text_sensor.cpp +++ b/esphome/components/modem/text_sensor/modem_text_sensor.cpp @@ -11,6 +11,7 @@ #include "esphome/core/application.h" #include "../modem_component.h" +#include "../helpers.h" #define ESPHL_ERROR_CHECK(err, message) \ if ((err) != ESP_OK) { \ @@ -38,6 +39,7 @@ void ModemTextSensor::update() { ESP_LOGD(TAG, "Modem text_sensor update"); if (modem::global_modem_component->dce && modem::global_modem_component->modem_ready()) { this->update_network_type_text_sensor_(); + this->update_signal_strength_text_sensor_(); } } @@ -46,74 +48,22 @@ void ModemTextSensor::update_network_type_text_sensor_() { int act; std::string network_type = "Not available"; if (modem::global_modem_component->dce->get_network_system_mode(act) == command_result::OK) { - // Access Technology from AT+CNSMOD? - // see https://www.waveshare.com/w/upload/a/af/SIM7500_SIM7600_Series_AT_Command_Manual_V3.00.pdf, page 109 - switch (act) { - case 0: - network_type = "No service"; - break; - case 1: - network_type = "GSM"; - break; - case 2: - network_type = "GPRS"; - break; - case 3: - network_type = "EGPRS (EDGE)"; - break; - case 4: - network_type = "WCDMA"; - break; - case 5: - network_type = "HSDPA only (WCDMA)"; - break; - case 6: - network_type = "HSUPA only (WCDMA)"; - break; - case 7: - network_type = "HSPA (HSDPA and HSUPA, WCDMA)"; - break; - case 8: - network_type = "LTE"; - break; - case 9: - network_type = "TDS-CDMA"; - break; - case 10: - network_type = "TDS-HSDPA only"; - break; - case 11: - network_type = "TDS-HSUPA only"; - break; - case 12: - network_type = "TDS-HSPA (HSDPA and HSUPA)"; - break; - case 13: - network_type = "CDMA"; - break; - case 14: - network_type = "EVDO"; - break; - case 15: - network_type = "HYBRID (CDMA and EVDO)"; - break; - case 16: - network_type = "1XLTE (CDMA and LTE)"; - break; - case 23: - network_type = "EHRPD"; - break; - case 24: - network_type = "HYBRID (CDMA and EHRPD)"; - break; - default: - network_type = "Unknown"; - } + network_type = network_system_mode_to_string(act); } this->network_type_text_sensor_->publish_state(network_type); } } +void ModemTextSensor::update_signal_strength_text_sensor_() { + if (modem::global_modem_component->modem_ready() && this->network_type_text_sensor_) { + float rssi, ber; + if (modem::global_modem_component->get_signal_quality(rssi, ber)) { + std::string bars = get_signal_bars(rssi, false); + this->signal_strength_text_sensor_->publish_state(bars); + } + } +} + } // namespace modem } // namespace esphome diff --git a/esphome/components/modem/text_sensor/modem_text_sensor.h b/esphome/components/modem/text_sensor/modem_text_sensor.h index 4ff714adc6..44013a89ce 100644 --- a/esphome/components/modem/text_sensor/modem_text_sensor.h +++ b/esphome/components/modem/text_sensor/modem_text_sensor.h @@ -18,6 +18,9 @@ class ModemTextSensor : public PollingComponent { void set_network_type_text_sensor(text_sensor::TextSensor *network_type_text_sensor) { this->network_type_text_sensor_ = network_type_text_sensor; } + void set_signal_strength_text_sensor(text_sensor::TextSensor *signal_strength_text_sensor) { + this->signal_strength_text_sensor_ = signal_strength_text_sensor; + } // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) @@ -28,7 +31,9 @@ class ModemTextSensor : public PollingComponent { protected: text_sensor::TextSensor *network_type_text_sensor_{nullptr}; + text_sensor::TextSensor *signal_strength_text_sensor_{nullptr}; void update_network_type_text_sensor_(); + void update_signal_strength_text_sensor_(); }; } // namespace modem diff --git a/tests/components/modem/test.esp32-idf.yaml b/tests/components/modem/test.esp32-idf.yaml index ac36f8936d..5fff1bbcc4 100644 --- a/tests/components/modem/test.esp32-idf.yaml +++ b/tests/components/modem/test.esp32-idf.yaml @@ -12,19 +12,22 @@ modem: pin_code: "0000" enable_on_boot: True enable_cmux: True + reboot_timeout: 5min init_at: - AT on_not_responding: logger.log: "modem not responding" on_connect: - logger.log: "got IP" + logger.log: "modem connected" on_disconnect: - logger.log: "lost IP" + logger.log: "modem disconnected" text_sensor: - platform: modem network_type: name: network type + signal_strength: + name: Signal strength sensor: - platform: modem