diff --git a/esphome/components/sim800l/__init__.py b/esphome/components/sim800l/__init__.py index 564b685b37..698e3cda9e 100644 --- a/esphome/components/sim800l/__init__.py +++ b/esphome/components/sim800l/__init__.py @@ -18,15 +18,42 @@ Sim800LReceivedMessageTrigger = sim800l_ns.class_( "Sim800LReceivedMessageTrigger", automation.Trigger.template(cg.std_string, cg.std_string), ) +Sim800LIncomingCallTrigger = sim800l_ns.class_( + "Sim800LIncomingCallTrigger", + automation.Trigger.template(cg.std_string), +) +Sim800LCallConnectedTrigger = sim800l_ns.class_( + "Sim800LCallConnectedTrigger", + automation.Trigger.template(), +) +Sim800LCallDisconnectedTrigger = sim800l_ns.class_( + "Sim800LCallDisconnectedTrigger", + automation.Trigger.template(), +) + +Sim800LReceivedUssdTrigger = sim800l_ns.class_( + "Sim800LReceivedUssdTrigger", + automation.Trigger.template(cg.std_string), +) # Actions Sim800LSendSmsAction = sim800l_ns.class_("Sim800LSendSmsAction", automation.Action) +Sim800LSendUssdAction = sim800l_ns.class_("Sim800LSendUssdAction", automation.Action) Sim800LDialAction = sim800l_ns.class_("Sim800LDialAction", automation.Action) +Sim800LConnectAction = sim800l_ns.class_("Sim800LConnectAction", automation.Action) +Sim800LDisconnectAction = sim800l_ns.class_( + "Sim800LDisconnectAction", automation.Action +) CONF_SIM800L_ID = "sim800l_id" CONF_ON_SMS_RECEIVED = "on_sms_received" +CONF_ON_USSD_RECEIVED = "on_ussd_received" +CONF_ON_INCOMING_CALL = "on_incoming_call" +CONF_ON_CALL_CONNECTED = "on_call_connected" +CONF_ON_CALL_DISCONNECTED = "on_call_disconnected" CONF_RECIPIENT = "recipient" CONF_MESSAGE = "message" +CONF_USSD = "ussd" CONFIG_SCHEMA = cv.All( cv.Schema( @@ -39,6 +66,34 @@ CONFIG_SCHEMA = cv.All( ), } ), + cv.Optional(CONF_ON_INCOMING_CALL): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LIncomingCallTrigger + ), + } + ), + cv.Optional(CONF_ON_CALL_CONNECTED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LCallConnectedTrigger + ), + } + ), + cv.Optional(CONF_ON_CALL_DISCONNECTED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LCallDisconnectedTrigger + ), + } + ), + cv.Optional(CONF_ON_USSD_RECEIVED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LReceivedUssdTrigger + ), + } + ), } ) .extend(cv.polling_component_schema("5s")) @@ -59,6 +114,19 @@ async def to_code(config): await automation.build_automation( trigger, [(cg.std_string, "message"), (cg.std_string, "sender")], conf ) + for conf in config.get(CONF_ON_INCOMING_CALL, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.std_string, "caller_id")], conf) + for conf in config.get(CONF_ON_CALL_CONNECTED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_CALL_DISCONNECTED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + for conf in config.get(CONF_ON_USSD_RECEIVED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.std_string, "ussd")], conf) SIM800L_SEND_SMS_SCHEMA = cv.Schema( @@ -98,3 +166,44 @@ async def sim800l_dial_to_code(config, action_id, template_arg, args): template_ = await cg.templatable(config[CONF_RECIPIENT], args, cg.std_string) cg.add(var.set_recipient(template_)) return var + + +@automation.register_action( + "sim800l.connect", + Sim800LConnectAction, + cv.Schema({cv.GenerateID(): cv.use_id(Sim800LComponent)}), +) +async def sim800l_connect_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + return var + + +SIM800L_SEND_USSD_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(Sim800LComponent), + cv.Required(CONF_USSD): cv.templatable(cv.string_strict), + } +) + + +@automation.register_action( + "sim800l.send_ussd", Sim800LSendUssdAction, SIM800L_SEND_USSD_SCHEMA +) +async def sim800l_send_ussd_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = await cg.templatable(config[CONF_USSD], args, cg.std_string) + cg.add(var.set_ussd(template_)) + return var + + +@automation.register_action( + "sim800l.disconnect", + Sim800LDisconnectAction, + cv.Schema({cv.GenerateID(): cv.use_id(Sim800LComponent)}), +) +async def sim800l_disconnect_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + return var diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index a935978747..3ae5de491a 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -16,20 +16,38 @@ void Sim800LComponent::update() { this->write(26); } + if (this->expect_ack_) + return; + if (state_ == STATE_INIT) { if (this->registered_ && this->send_pending_) { this->send_cmd_("AT+CSCS=\"GSM\""); - this->state_ = STATE_SENDINGSMS1; + this->state_ = STATE_SENDING_SMS_1; } else if (this->registered_ && this->dial_pending_) { this->send_cmd_("AT+CSCS=\"GSM\""); this->state_ = STATE_DIALING1; + } else if (this->registered_ && this->connect_pending_) { + this->connect_pending_ = false; + ESP_LOGI(TAG, "Connecting..."); + this->send_cmd_("ATA"); + this->state_ = STATE_ATA_SENT; + } else if (this->registered_ && this->send_ussd_pending_) { + this->send_cmd_("AT+CSCS=\"GSM\""); + this->state_ = STATE_SEND_USSD1; + } else if (this->registered_ && this->disconnect_pending_) { + this->disconnect_pending_ = false; + ESP_LOGI(TAG, "Disconnecting..."); + this->send_cmd_("ATH"); + } else if (this->registered_ && this->call_state_ != 6) { + send_cmd_("AT+CLCC"); + this->state_ = STATE_CHECK_CALL; + return; } else { this->send_cmd_("AT"); - this->state_ = STATE_CHECK_AT; + this->state_ = STATE_SETUP_CMGF; } this->expect_ack_ = true; - } - if (state_ == STATE_RECEIVEDSMS) { + } else if (state_ == STATE_RECEIVED_SMS) { // Serial Buffer should have flushed. // Send cmd to delete received sms char delete_cmd[20]; @@ -49,16 +67,29 @@ void Sim800LComponent::send_cmd_(const std::string &message) { } void Sim800LComponent::parse_cmd_(std::string message) { - ESP_LOGV(TAG, "R: %s - %d", message.c_str(), this->state_); - if (message.empty()) return; + ESP_LOGV(TAG, "R: %s - %d", message.c_str(), this->state_); + + if (this->state_ != STATE_RECEIVE_SMS) { + if (message == "RING") { + // Incoming call... + this->state_ = STATE_PARSE_CLIP; + this->expect_ack_ = false; + } else if (message == "NO CARRIER") { + if (this->call_state_ != 6) { + this->call_state_ = 6; + this->call_disconnected_callback_.call(); + } + } + } + + bool ok = message == "OK"; if (this->expect_ack_) { - bool ok = message == "OK"; this->expect_ack_ = false; if (!ok) { - if (this->state_ == STATE_CHECK_AT && message == "AT") { + if (this->state_ == STATE_SETUP_CMGF && message == "AT") { // Expected ack but AT echo received this->state_ = STATE_DISABLE_ECHO; this->expect_ack_ = true; @@ -68,6 +99,10 @@ void Sim800LComponent::parse_cmd_(std::string message) { return; } } + } else if (ok && (this->state_ != STATE_PARSE_SMS_RESPONSE && this->state_ != STATE_CHECK_CALL && + this->state_ != STATE_RECEIVE_SMS && this->state_ != STATE_DIALING2)) { + ESP_LOGW(TAG, "Received unexpected OK. Ignoring"); + return; } switch (this->state_) { @@ -75,30 +110,88 @@ void Sim800LComponent::parse_cmd_(std::string message) { // While we were waiting for update to check for messages, this notifies a message // is available. bool message_available = message.compare(0, 6, "+CMTI:") == 0; - if (!message_available) + if (!message_available) { + if (message == "RING") { + // Incoming call... + this->state_ = STATE_PARSE_CLIP; + } else if (message == "NO CARRIER") { + if (this->call_state_ != 6) { + this->call_state_ = 6; + this->call_disconnected_callback_.call(); + } + } else if (message.compare(0, 6, "+CUSD:") == 0) { + // Incoming USSD MESSAGE + this->state_ = STATE_CHECK_USSD; + } break; + } + // Else fall thru ... } case STATE_CHECK_SMS: send_cmd_("AT+CMGL=\"ALL\""); - this->state_ = STATE_PARSE_SMS; + this->state_ = STATE_PARSE_SMS_RESPONSE; this->parse_index_ = 0; break; case STATE_DISABLE_ECHO: send_cmd_("ATE0"); - this->state_ = STATE_CHECK_AT; + this->state_ = STATE_SETUP_CMGF; this->expect_ack_ = true; break; - case STATE_CHECK_AT: + case STATE_SETUP_CMGF: send_cmd_("AT+CMGF=1"); + this->state_ = STATE_SETUP_CLIP; + this->expect_ack_ = true; + break; + case STATE_SETUP_CLIP: + send_cmd_("AT+CLIP=1"); this->state_ = STATE_CREG; this->expect_ack_ = true; break; + case STATE_SETUP_USSD: + send_cmd_("AT+CUSD=1"); + this->state_ = STATE_CREG; + this->expect_ack_ = true; + break; + case STATE_SEND_USSD1: + this->send_cmd_("AT+CUSD=1, \"" + this->ussd_ + "\""); + this->state_ = STATE_SEND_USSD2; + break; + case STATE_SEND_USSD2: + ESP_LOGD(TAG, "SendUssd2: '%s'", message.c_str()); + if (message == "OK") { + // Dialing + ESP_LOGD(TAG, "Dialing ussd code: '%s' done.", this->ussd_.c_str()); + this->state_ = STATE_CHECK_USSD; + this->send_ussd_pending_ = false; + } else { + this->set_registered_(false); + this->state_ = STATE_INIT; + this->send_cmd_("AT+CMEE=2"); + this->write(26); + } + break; + case STATE_CHECK_USSD: + ESP_LOGD(TAG, "Check ussd code: '%s'", message.c_str()); + if (message.compare(0, 6, "+CUSD:") == 0) { + this->state_ = STATE_RECEIVED_USSD; + this->ussd_ = ""; + size_t start = 10; + size_t end = message.find_last_of(','); + if (end > start) { + this->ussd_ = message.substr(start + 1, end - start - 2); + this->ussd_received_callback_.call(this->ussd_); + } + } + // Otherwise we receive another OK, we do nothing just wait polling to continuously check for SMS + if (message == "OK") + this->state_ = STATE_INIT; + break; case STATE_CREG: send_cmd_("AT+CREG?"); - this->state_ = STATE_CREGWAIT; + this->state_ = STATE_CREG_WAIT; break; - case STATE_CREGWAIT: { + case STATE_CREG_WAIT: { // Response: "+CREG: 0,1" -- the one there means registered ok // "+CREG: -,-" means not registered ok bool registered = message.compare(0, 6, "+CREG:") == 0 && (message[9] == '1' || message[9] == '5'); @@ -112,10 +205,10 @@ void Sim800LComponent::parse_cmd_(std::string message) { if (message[7] == '0') { // Network registration is disable, enable it send_cmd_("AT+CREG=1"); this->expect_ack_ = true; - this->state_ = STATE_CHECK_AT; + this->state_ = STATE_SETUP_CMGF; } else { // Keep waiting registration - this->state_ = STATE_CREG; + this->state_ = STATE_INIT; } } set_registered_(registered); @@ -145,9 +238,6 @@ void Sim800LComponent::parse_cmd_(std::string message) { this->expect_ack_ = true; this->state_ = STATE_CHECK_SMS; break; - case STATE_PARSE_SMS: - this->state_ = STATE_PARSE_SMS_RESPONSE; - break; case STATE_PARSE_SMS_RESPONSE: if (message.compare(0, 6, "+CMGL:") == 0 && this->parse_index_ == 0) { size_t start = 7; @@ -158,10 +248,11 @@ void Sim800LComponent::parse_cmd_(std::string message) { if (item == 1) { // Slot Index this->parse_index_ = parse_number(message.substr(start, end - start)).value_or(0); } - // item 2 = STATUS, usually "REC UNERAD" + // item 2 = STATUS, usually "REC UNREAD" if (item == 3) { // recipient // Add 1 and remove 2 from substring to get rid of "quotes" this->sender_ = message.substr(start + 1, end - start - 2); + this->message_.clear(); break; } // item 4 = "" @@ -174,42 +265,83 @@ void Sim800LComponent::parse_cmd_(std::string message) { ESP_LOGD(TAG, "Invalid message %d %s", this->state_, message.c_str()); return; } - this->state_ = STATE_RECEIVESMS; + this->state_ = STATE_RECEIVE_SMS; + } + // Otherwise we receive another OK + if (ok) { + send_cmd_("AT+CLCC"); + this->state_ = STATE_CHECK_CALL; } - // Otherwise we receive another OK, we do nothing just wait polling to continuously check for SMS - if (message == "OK") - this->state_ = STATE_INIT; break; - case STATE_RECEIVESMS: + case STATE_CHECK_CALL: + if (message.compare(0, 6, "+CLCC:") == 0 && this->parse_index_ == 0) { + this->expect_ack_ = true; + size_t start = 7; + size_t end = message.find(',', start); + uint8_t item = 0; + while (end != start) { + item++; + // item 1 call index for +CHLD + // item 2 dir 0 Mobile originated; 1 Mobile terminated + if (item == 3) { // stat + uint8_t current_call_state = parse_number(message.substr(start, end - start)).value_or(6); + if (current_call_state != this->call_state_) { + ESP_LOGD(TAG, "Call state is now: %d", current_call_state); + if (current_call_state == 0) + this->call_connected_callback_.call(); + } + this->call_state_ = current_call_state; + break; + } + // item 4 = "" + // item 5 = Received timestamp + start = end + 1; + end = message.find(',', start); + } + + if (item < 2) { + ESP_LOGD(TAG, "Invalid message %d %s", this->state_, message.c_str()); + return; + } + } else if (ok) { + if (this->call_state_ != 6) { + // no call in progress + this->call_state_ = 6; // Disconnect + this->call_disconnected_callback_.call(); + } + } + this->state_ = STATE_INIT; + break; + case STATE_RECEIVE_SMS: /* Our recipient is set and the message body is in message kick ESPHome callback now */ - ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str()); - ESP_LOGD(TAG, "%s", message.c_str()); - this->callback_.call(message, this->sender_); - /* If the message is multiline, next lines will contain message data. - If there were other messages in the list, next line will be +CMGL: ... - At the end of the list the new line and the OK should be received. - To keep this simple just first line of message if considered, then - the next state will swallow all received data and in next poll event - this message index is marked for deletion. - */ - this->state_ = STATE_RECEIVEDSMS; + if (ok || message.compare(0, 6, "+CMGL:") == 0) { + ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str()); + ESP_LOGD(TAG, "%s", this->message_.c_str()); + this->sms_received_callback_.call(this->message_, this->sender_); + this->state_ = STATE_RECEIVED_SMS; + } else { + if (this->message_.length() > 0) + this->message_ += "\n"; + this->message_ += message; + } break; - case STATE_RECEIVEDSMS: + case STATE_RECEIVED_SMS: + case STATE_RECEIVED_USSD: // Let the buffer flush. Next poll will request to delete the parsed index message. break; - case STATE_SENDINGSMS1: + case STATE_SENDING_SMS_1: this->send_cmd_("AT+CMGS=\"" + this->recipient_ + "\""); - this->state_ = STATE_SENDINGSMS2; + this->state_ = STATE_SENDING_SMS_2; break; - case STATE_SENDINGSMS2: + case STATE_SENDING_SMS_2: if (message == ">") { // Send sms body - ESP_LOGD(TAG, "Sending message: '%s'", this->outgoing_message_.c_str()); + ESP_LOGI(TAG, "Sending to %s message: '%s'", this->recipient_.c_str(), this->outgoing_message_.c_str()); this->write_str(this->outgoing_message_.c_str()); this->write(26); - this->state_ = STATE_SENDINGSMS3; + this->state_ = STATE_SENDING_SMS_3; } else { set_registered_(false); this->state_ = STATE_INIT; @@ -217,7 +349,7 @@ void Sim800LComponent::parse_cmd_(std::string message) { this->write(26); } break; - case STATE_SENDINGSMS3: + case STATE_SENDING_SMS_3: if (message.compare(0, 6, "+CMGS:") == 0) { ESP_LOGD(TAG, "SMS Sent OK: %s", message.c_str()); this->send_pending_ = false; @@ -230,23 +362,55 @@ void Sim800LComponent::parse_cmd_(std::string message) { this->state_ = STATE_DIALING2; break; case STATE_DIALING2: - if (message == "OK") { - // Dialing - ESP_LOGD(TAG, "Dialing: '%s'", this->recipient_.c_str()); - this->state_ = STATE_INIT; + if (ok) { + ESP_LOGI(TAG, "Dialing: '%s'", this->recipient_.c_str()); this->dial_pending_ = false; } else { this->set_registered_(false); - this->state_ = STATE_INIT; this->send_cmd_("AT+CMEE=2"); this->write(26); } + this->state_ = STATE_INIT; + break; + case STATE_PARSE_CLIP: + if (message.compare(0, 6, "+CLIP:") == 0) { + std::string caller_id; + size_t start = 7; + size_t end = message.find(',', start); + uint8_t item = 0; + while (end != start) { + item++; + if (item == 1) { // Slot Index + // Add 1 and remove 2 from substring to get rid of "quotes" + caller_id = message.substr(start + 1, end - start - 2); + break; + } + // item 4 = "" + // item 5 = Received timestamp + start = end + 1; + end = message.find(',', start); + } + if (this->call_state_ != 4) { + this->call_state_ = 4; + ESP_LOGI(TAG, "Incoming call from %s", caller_id.c_str()); + incoming_call_callback_.call(caller_id); + } + this->state_ = STATE_INIT; + } + break; + case STATE_ATA_SENT: + ESP_LOGI(TAG, "Call connected"); + if (this->call_state_ != 0) { + this->call_state_ = 0; + this->call_connected_callback_.call(); + } + this->state_ = STATE_INIT; break; default: - ESP_LOGD(TAG, "Unhandled: %s - %d", message.c_str(), this->state_); + ESP_LOGW(TAG, "Unhandled: %s - %d", message.c_str(), this->state_); break; } -} +} // namespace sim800l void Sim800LComponent::loop() { // Read message @@ -265,7 +429,7 @@ void Sim800LComponent::loop() { byte = '?'; // need to be valid utf8 string for log functions. this->read_buffer_[this->read_pos_] = byte; - if (this->state_ == STATE_SENDINGSMS2 && this->read_pos_ == 0 && byte == '>') + if (this->state_ == STATE_SENDING_SMS_2 && this->read_pos_ == 0 && byte == '>') this->read_buffer_[++this->read_pos_] = ASCII_LF; if (this->read_buffer_[this->read_pos_] == ASCII_LF) { @@ -276,13 +440,23 @@ void Sim800LComponent::loop() { this->read_pos_++; } } + if (state_ == STATE_INIT && this->registered_ && + (this->call_state_ != 6 // A call is in progress + || this->send_pending_ || this->dial_pending_ || this->connect_pending_ || this->disconnect_pending_)) { + this->update(); + } } void Sim800LComponent::send_sms(const std::string &recipient, const std::string &message) { - ESP_LOGD(TAG, "Sending to %s: %s", recipient.c_str(), message.c_str()); this->recipient_ = recipient; this->outgoing_message_ = message; this->send_pending_ = true; +} + +void Sim800LComponent::send_ussd(const std::string &ussd_code) { + ESP_LOGD(TAG, "Sending USSD code: %s", ussd_code.c_str()); + this->ussd_ = ussd_code; + this->send_ussd_pending_ = true; this->update(); } void Sim800LComponent::dump_config() { @@ -295,11 +469,11 @@ void Sim800LComponent::dump_config() { #endif } void Sim800LComponent::dial(const std::string &recipient) { - ESP_LOGD(TAG, "Dialing %s", recipient.c_str()); this->recipient_ = recipient; this->dial_pending_ = true; - this->update(); } +void Sim800LComponent::connect() { this->connect_pending_ = true; } +void Sim800LComponent::disconnect() { this->disconnect_pending_ = true; } void Sim800LComponent::set_registered_(bool registered) { this->registered_ = registered; diff --git a/esphome/components/sim800l/sim800l.h b/esphome/components/sim800l/sim800l.h index 3535b96283..bf7efd6915 100644 --- a/esphome/components/sim800l/sim800l.h +++ b/esphome/components/sim800l/sim800l.h @@ -16,31 +16,35 @@ namespace esphome { namespace sim800l { -const uint8_t SIM800L_READ_BUFFER_LENGTH = 255; +const uint16_t SIM800L_READ_BUFFER_LENGTH = 1024; enum State { STATE_IDLE = 0, STATE_INIT, - STATE_CHECK_AT, + STATE_SETUP_CMGF, + STATE_SETUP_CLIP, STATE_CREG, - STATE_CREGWAIT, + STATE_CREG_WAIT, STATE_CSQ, STATE_CSQ_RESPONSE, - STATE_IDLEWAIT, - STATE_SENDINGSMS1, - STATE_SENDINGSMS2, - STATE_SENDINGSMS3, + STATE_SENDING_SMS_1, + STATE_SENDING_SMS_2, + STATE_SENDING_SMS_3, STATE_CHECK_SMS, - STATE_PARSE_SMS, STATE_PARSE_SMS_RESPONSE, - STATE_RECEIVESMS, - STATE_READSMS, - STATE_RECEIVEDSMS, - STATE_DELETEDSMS, + STATE_RECEIVE_SMS, + STATE_RECEIVED_SMS, STATE_DISABLE_ECHO, - STATE_PARSE_SMS_OK, STATE_DIALING1, - STATE_DIALING2 + STATE_DIALING2, + STATE_PARSE_CLIP, + STATE_ATA_SENT, + STATE_CHECK_CALL, + STATE_SETUP_USSD, + STATE_SEND_USSD1, + STATE_SEND_USSD2, + STATE_CHECK_USSD, + STATE_RECEIVED_USSD }; class Sim800LComponent : public uart::UARTDevice, public PollingComponent { @@ -58,10 +62,25 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { void set_rssi_sensor(sensor::Sensor *rssi_sensor) { rssi_sensor_ = rssi_sensor; } #endif void add_on_sms_received_callback(std::function callback) { - this->callback_.add(std::move(callback)); + this->sms_received_callback_.add(std::move(callback)); + } + void add_on_incoming_call_callback(std::function callback) { + this->incoming_call_callback_.add(std::move(callback)); + } + void add_on_call_connected_callback(std::function callback) { + this->call_connected_callback_.add(std::move(callback)); + } + void add_on_call_disconnected_callback(std::function callback) { + this->call_disconnected_callback_.add(std::move(callback)); + } + void add_on_ussd_received_callback(std::function callback) { + this->ussd_received_callback_.add(std::move(callback)); } void send_sms(const std::string &recipient, const std::string &message); + void send_ussd(const std::string &ussd_code); void dial(const std::string &recipient); + void connect(); + void disconnect(); protected: void send_cmd_(const std::string &message); @@ -76,6 +95,7 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { sensor::Sensor *rssi_sensor_{nullptr}; #endif std::string sender_; + std::string message_; char read_buffer_[SIM800L_READ_BUFFER_LENGTH]; size_t read_pos_{0}; uint8_t parse_index_{0}; @@ -86,10 +106,19 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { std::string recipient_; std::string outgoing_message_; + std::string ussd_; bool send_pending_; bool dial_pending_; + bool connect_pending_; + bool disconnect_pending_; + bool send_ussd_pending_; + uint8_t call_state_{6}; - CallbackManager callback_; + CallbackManager sms_received_callback_; + CallbackManager incoming_call_callback_; + CallbackManager call_connected_callback_; + CallbackManager call_disconnected_callback_; + CallbackManager ussd_received_callback_; }; class Sim800LReceivedMessageTrigger : public Trigger { @@ -100,6 +129,33 @@ class Sim800LReceivedMessageTrigger : public Trigger { } }; +class Sim800LIncomingCallTrigger : public Trigger { + public: + explicit Sim800LIncomingCallTrigger(Sim800LComponent *parent) { + parent->add_on_incoming_call_callback([this](const std::string &caller_id) { this->trigger(caller_id); }); + } +}; + +class Sim800LCallConnectedTrigger : public Trigger<> { + public: + explicit Sim800LCallConnectedTrigger(Sim800LComponent *parent) { + parent->add_on_call_connected_callback([this]() { this->trigger(); }); + } +}; + +class Sim800LCallDisconnectedTrigger : public Trigger<> { + public: + explicit Sim800LCallDisconnectedTrigger(Sim800LComponent *parent) { + parent->add_on_call_disconnected_callback([this]() { this->trigger(); }); + } +}; +class Sim800LReceivedUssdTrigger : public Trigger { + public: + explicit Sim800LReceivedUssdTrigger(Sim800LComponent *parent) { + parent->add_on_ussd_received_callback([this](const std::string &ussd) { this->trigger(ussd); }); + } +}; + template class Sim800LSendSmsAction : public Action { public: Sim800LSendSmsAction(Sim800LComponent *parent) : parent_(parent) {} @@ -116,6 +172,20 @@ template class Sim800LSendSmsAction : public Action { Sim800LComponent *parent_; }; +template class Sim800LSendUssdAction : public Action { + public: + Sim800LSendUssdAction(Sim800LComponent *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(std::string, ussd) + + void play(Ts... x) { + auto ussd_code = this->ussd_.value(x...); + this->parent_->send_ussd(ussd_code); + } + + protected: + Sim800LComponent *parent_; +}; + template class Sim800LDialAction : public Action { public: Sim800LDialAction(Sim800LComponent *parent) : parent_(parent) {} @@ -129,6 +199,25 @@ template class Sim800LDialAction : public Action { protected: Sim800LComponent *parent_; }; +template class Sim800LConnectAction : public Action { + public: + Sim800LConnectAction(Sim800LComponent *parent) : parent_(parent) {} + + void play(Ts... x) { this->parent_->connect(); } + + protected: + Sim800LComponent *parent_; +}; + +template class Sim800LDisconnectAction : public Action { + public: + Sim800LDisconnectAction(Sim800LComponent *parent) : parent_(parent) {} + + void play(Ts... x) { this->parent_->disconnect(); } + + protected: + Sim800LComponent *parent_; +}; } // namespace sim800l } // namespace esphome