Sim800l add calls, multiline sms and ussd (#3630)

Co-authored-by: Matus Ivanecky <matus.ivanecky@gmail.com>
Co-authored-by: Matus Ivanecky <maty535@users.noreply.github.com>
This commit is contained in:
Guillermo Ruffino 2022-09-14 16:43:03 -03:00 committed by Jesse Hills
parent c61abf6aca
commit cea7deab91
No known key found for this signature in database
GPG Key ID: BEAAE804EFD8E83A
3 changed files with 442 additions and 70 deletions

View File

@ -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

View File

@ -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<uint8_t>(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<uint8_t>(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;

View File

@ -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<void(std::string, std::string)> callback) {
this->callback_.add(std::move(callback));
this->sms_received_callback_.add(std::move(callback));
}
void add_on_incoming_call_callback(std::function<void(std::string)> callback) {
this->incoming_call_callback_.add(std::move(callback));
}
void add_on_call_connected_callback(std::function<void()> callback) {
this->call_connected_callback_.add(std::move(callback));
}
void add_on_call_disconnected_callback(std::function<void()> callback) {
this->call_disconnected_callback_.add(std::move(callback));
}
void add_on_ussd_received_callback(std::function<void(std::string)> 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<void(std::string, std::string)> callback_;
CallbackManager<void(std::string, std::string)> sms_received_callback_;
CallbackManager<void(std::string)> incoming_call_callback_;
CallbackManager<void()> call_connected_callback_;
CallbackManager<void()> call_disconnected_callback_;
CallbackManager<void(std::string)> ussd_received_callback_;
};
class Sim800LReceivedMessageTrigger : public Trigger<std::string, std::string> {
@ -100,6 +129,33 @@ class Sim800LReceivedMessageTrigger : public Trigger<std::string, std::string> {
}
};
class Sim800LIncomingCallTrigger : public Trigger<std::string> {
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<std::string> {
public:
explicit Sim800LReceivedUssdTrigger(Sim800LComponent *parent) {
parent->add_on_ussd_received_callback([this](const std::string &ussd) { this->trigger(ussd); });
}
};
template<typename... Ts> class Sim800LSendSmsAction : public Action<Ts...> {
public:
Sim800LSendSmsAction(Sim800LComponent *parent) : parent_(parent) {}
@ -116,6 +172,20 @@ template<typename... Ts> class Sim800LSendSmsAction : public Action<Ts...> {
Sim800LComponent *parent_;
};
template<typename... Ts> class Sim800LSendUssdAction : public Action<Ts...> {
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<typename... Ts> class Sim800LDialAction : public Action<Ts...> {
public:
Sim800LDialAction(Sim800LComponent *parent) : parent_(parent) {}
@ -129,6 +199,25 @@ template<typename... Ts> class Sim800LDialAction : public Action<Ts...> {
protected:
Sim800LComponent *parent_;
};
template<typename... Ts> class Sim800LConnectAction : public Action<Ts...> {
public:
Sim800LConnectAction(Sim800LComponent *parent) : parent_(parent) {}
void play(Ts... x) { this->parent_->connect(); }
protected:
Sim800LComponent *parent_;
};
template<typename... Ts> class Sim800LDisconnectAction : public Action<Ts...> {
public:
Sim800LDisconnectAction(Sim800LComponent *parent) : parent_(parent) {}
void play(Ts... x) { this->parent_->disconnect(); }
protected:
Sim800LComponent *parent_;
};
} // namespace sim800l
} // namespace esphome