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 GitHub
parent 6a8f4e92df
commit aaf50fc2e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 442 additions and 70 deletions

View File

@ -18,15 +18,42 @@ Sim800LReceivedMessageTrigger = sim800l_ns.class_(
"Sim800LReceivedMessageTrigger", "Sim800LReceivedMessageTrigger",
automation.Trigger.template(cg.std_string, cg.std_string), 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 # Actions
Sim800LSendSmsAction = sim800l_ns.class_("Sim800LSendSmsAction", automation.Action) Sim800LSendSmsAction = sim800l_ns.class_("Sim800LSendSmsAction", automation.Action)
Sim800LSendUssdAction = sim800l_ns.class_("Sim800LSendUssdAction", automation.Action)
Sim800LDialAction = sim800l_ns.class_("Sim800LDialAction", 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_SIM800L_ID = "sim800l_id"
CONF_ON_SMS_RECEIVED = "on_sms_received" 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_RECIPIENT = "recipient"
CONF_MESSAGE = "message" CONF_MESSAGE = "message"
CONF_USSD = "ussd"
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema( 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")) .extend(cv.polling_component_schema("5s"))
@ -59,6 +114,19 @@ async def to_code(config):
await automation.build_automation( await automation.build_automation(
trigger, [(cg.std_string, "message"), (cg.std_string, "sender")], conf 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( 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) template_ = await cg.templatable(config[CONF_RECIPIENT], args, cg.std_string)
cg.add(var.set_recipient(template_)) cg.add(var.set_recipient(template_))
return var 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); this->write(26);
} }
if (this->expect_ack_)
return;
if (state_ == STATE_INIT) { if (state_ == STATE_INIT) {
if (this->registered_ && this->send_pending_) { if (this->registered_ && this->send_pending_) {
this->send_cmd_("AT+CSCS=\"GSM\""); this->send_cmd_("AT+CSCS=\"GSM\"");
this->state_ = STATE_SENDINGSMS1; this->state_ = STATE_SENDING_SMS_1;
} else if (this->registered_ && this->dial_pending_) { } else if (this->registered_ && this->dial_pending_) {
this->send_cmd_("AT+CSCS=\"GSM\""); this->send_cmd_("AT+CSCS=\"GSM\"");
this->state_ = STATE_DIALING1; 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 { } else {
this->send_cmd_("AT"); this->send_cmd_("AT");
this->state_ = STATE_CHECK_AT; this->state_ = STATE_SETUP_CMGF;
} }
this->expect_ack_ = true; this->expect_ack_ = true;
} } else if (state_ == STATE_RECEIVED_SMS) {
if (state_ == STATE_RECEIVEDSMS) {
// Serial Buffer should have flushed. // Serial Buffer should have flushed.
// Send cmd to delete received sms // Send cmd to delete received sms
char delete_cmd[20]; char delete_cmd[20];
@ -49,16 +67,29 @@ void Sim800LComponent::send_cmd_(const std::string &message) {
} }
void Sim800LComponent::parse_cmd_(std::string message) { void Sim800LComponent::parse_cmd_(std::string message) {
ESP_LOGV(TAG, "R: %s - %d", message.c_str(), this->state_);
if (message.empty()) if (message.empty())
return; 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_) { if (this->expect_ack_) {
bool ok = message == "OK";
this->expect_ack_ = false; this->expect_ack_ = false;
if (!ok) { if (!ok) {
if (this->state_ == STATE_CHECK_AT && message == "AT") { if (this->state_ == STATE_SETUP_CMGF && message == "AT") {
// Expected ack but AT echo received // Expected ack but AT echo received
this->state_ = STATE_DISABLE_ECHO; this->state_ = STATE_DISABLE_ECHO;
this->expect_ack_ = true; this->expect_ack_ = true;
@ -68,6 +99,10 @@ void Sim800LComponent::parse_cmd_(std::string message) {
return; 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_) { 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 // While we were waiting for update to check for messages, this notifies a message
// is available. // is available.
bool message_available = message.compare(0, 6, "+CMTI:") == 0; 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; break;
}
// Else fall thru ... // Else fall thru ...
} }
case STATE_CHECK_SMS: case STATE_CHECK_SMS:
send_cmd_("AT+CMGL=\"ALL\""); send_cmd_("AT+CMGL=\"ALL\"");
this->state_ = STATE_PARSE_SMS; this->state_ = STATE_PARSE_SMS_RESPONSE;
this->parse_index_ = 0; this->parse_index_ = 0;
break; break;
case STATE_DISABLE_ECHO: case STATE_DISABLE_ECHO:
send_cmd_("ATE0"); send_cmd_("ATE0");
this->state_ = STATE_CHECK_AT; this->state_ = STATE_SETUP_CMGF;
this->expect_ack_ = true; this->expect_ack_ = true;
break; break;
case STATE_CHECK_AT: case STATE_SETUP_CMGF:
send_cmd_("AT+CMGF=1"); 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->state_ = STATE_CREG;
this->expect_ack_ = true; this->expect_ack_ = true;
break; 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: case STATE_CREG:
send_cmd_("AT+CREG?"); send_cmd_("AT+CREG?");
this->state_ = STATE_CREGWAIT; this->state_ = STATE_CREG_WAIT;
break; break;
case STATE_CREGWAIT: { case STATE_CREG_WAIT: {
// Response: "+CREG: 0,1" -- the one there means registered ok // Response: "+CREG: 0,1" -- the one there means registered ok
// "+CREG: -,-" means not registered ok // "+CREG: -,-" means not registered ok
bool registered = message.compare(0, 6, "+CREG:") == 0 && (message[9] == '1' || message[9] == '5'); 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 if (message[7] == '0') { // Network registration is disable, enable it
send_cmd_("AT+CREG=1"); send_cmd_("AT+CREG=1");
this->expect_ack_ = true; this->expect_ack_ = true;
this->state_ = STATE_CHECK_AT; this->state_ = STATE_SETUP_CMGF;
} else { } else {
// Keep waiting registration // Keep waiting registration
this->state_ = STATE_CREG; this->state_ = STATE_INIT;
} }
} }
set_registered_(registered); set_registered_(registered);
@ -145,9 +238,6 @@ void Sim800LComponent::parse_cmd_(std::string message) {
this->expect_ack_ = true; this->expect_ack_ = true;
this->state_ = STATE_CHECK_SMS; this->state_ = STATE_CHECK_SMS;
break; break;
case STATE_PARSE_SMS:
this->state_ = STATE_PARSE_SMS_RESPONSE;
break;
case STATE_PARSE_SMS_RESPONSE: case STATE_PARSE_SMS_RESPONSE:
if (message.compare(0, 6, "+CMGL:") == 0 && this->parse_index_ == 0) { if (message.compare(0, 6, "+CMGL:") == 0 && this->parse_index_ == 0) {
size_t start = 7; size_t start = 7;
@ -158,10 +248,11 @@ void Sim800LComponent::parse_cmd_(std::string message) {
if (item == 1) { // Slot Index if (item == 1) { // Slot Index
this->parse_index_ = parse_number<uint8_t>(message.substr(start, end - start)).value_or(0); 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 if (item == 3) { // recipient
// Add 1 and remove 2 from substring to get rid of "quotes" // Add 1 and remove 2 from substring to get rid of "quotes"
this->sender_ = message.substr(start + 1, end - start - 2); this->sender_ = message.substr(start + 1, end - start - 2);
this->message_.clear();
break; break;
} }
// item 4 = "" // 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()); ESP_LOGD(TAG, "Invalid message %d %s", this->state_, message.c_str());
return; 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; 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 /* Our recipient is set and the message body is in message
kick ESPHome callback now kick ESPHome callback now
*/ */
ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str()); if (ok || message.compare(0, 6, "+CMGL:") == 0) {
ESP_LOGD(TAG, "%s", message.c_str()); ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str());
this->callback_.call(message, this->sender_); ESP_LOGD(TAG, "%s", this->message_.c_str());
/* If the message is multiline, next lines will contain message data. this->sms_received_callback_.call(this->message_, this->sender_);
If there were other messages in the list, next line will be +CMGL: ... this->state_ = STATE_RECEIVED_SMS;
At the end of the list the new line and the OK should be received. } else {
To keep this simple just first line of message if considered, then if (this->message_.length() > 0)
the next state will swallow all received data and in next poll event this->message_ += "\n";
this message index is marked for deletion. this->message_ += message;
*/ }
this->state_ = STATE_RECEIVEDSMS;
break; 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. // Let the buffer flush. Next poll will request to delete the parsed index message.
break; break;
case STATE_SENDINGSMS1: case STATE_SENDING_SMS_1:
this->send_cmd_("AT+CMGS=\"" + this->recipient_ + "\""); this->send_cmd_("AT+CMGS=\"" + this->recipient_ + "\"");
this->state_ = STATE_SENDINGSMS2; this->state_ = STATE_SENDING_SMS_2;
break; break;
case STATE_SENDINGSMS2: case STATE_SENDING_SMS_2:
if (message == ">") { if (message == ">") {
// Send sms body // 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_str(this->outgoing_message_.c_str());
this->write(26); this->write(26);
this->state_ = STATE_SENDINGSMS3; this->state_ = STATE_SENDING_SMS_3;
} else { } else {
set_registered_(false); set_registered_(false);
this->state_ = STATE_INIT; this->state_ = STATE_INIT;
@ -217,7 +349,7 @@ void Sim800LComponent::parse_cmd_(std::string message) {
this->write(26); this->write(26);
} }
break; break;
case STATE_SENDINGSMS3: case STATE_SENDING_SMS_3:
if (message.compare(0, 6, "+CMGS:") == 0) { if (message.compare(0, 6, "+CMGS:") == 0) {
ESP_LOGD(TAG, "SMS Sent OK: %s", message.c_str()); ESP_LOGD(TAG, "SMS Sent OK: %s", message.c_str());
this->send_pending_ = false; this->send_pending_ = false;
@ -230,23 +362,55 @@ void Sim800LComponent::parse_cmd_(std::string message) {
this->state_ = STATE_DIALING2; this->state_ = STATE_DIALING2;
break; break;
case STATE_DIALING2: case STATE_DIALING2:
if (message == "OK") { if (ok) {
// Dialing ESP_LOGI(TAG, "Dialing: '%s'", this->recipient_.c_str());
ESP_LOGD(TAG, "Dialing: '%s'", this->recipient_.c_str());
this->state_ = STATE_INIT;
this->dial_pending_ = false; this->dial_pending_ = false;
} else { } else {
this->set_registered_(false); this->set_registered_(false);
this->state_ = STATE_INIT;
this->send_cmd_("AT+CMEE=2"); this->send_cmd_("AT+CMEE=2");
this->write(26); 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; break;
default: default:
ESP_LOGD(TAG, "Unhandled: %s - %d", message.c_str(), this->state_); ESP_LOGW(TAG, "Unhandled: %s - %d", message.c_str(), this->state_);
break; break;
} }
} } // namespace sim800l
void Sim800LComponent::loop() { void Sim800LComponent::loop() {
// Read message // Read message
@ -265,7 +429,7 @@ void Sim800LComponent::loop() {
byte = '?'; // need to be valid utf8 string for log functions. byte = '?'; // need to be valid utf8 string for log functions.
this->read_buffer_[this->read_pos_] = byte; 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; this->read_buffer_[++this->read_pos_] = ASCII_LF;
if (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_++; 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) { 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->recipient_ = recipient;
this->outgoing_message_ = message; this->outgoing_message_ = message;
this->send_pending_ = true; 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(); this->update();
} }
void Sim800LComponent::dump_config() { void Sim800LComponent::dump_config() {
@ -295,11 +469,11 @@ void Sim800LComponent::dump_config() {
#endif #endif
} }
void Sim800LComponent::dial(const std::string &recipient) { void Sim800LComponent::dial(const std::string &recipient) {
ESP_LOGD(TAG, "Dialing %s", recipient.c_str());
this->recipient_ = recipient; this->recipient_ = recipient;
this->dial_pending_ = true; 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) { void Sim800LComponent::set_registered_(bool registered) {
this->registered_ = registered; this->registered_ = registered;

View File

@ -16,31 +16,35 @@
namespace esphome { namespace esphome {
namespace sim800l { namespace sim800l {
const uint8_t SIM800L_READ_BUFFER_LENGTH = 255; const uint16_t SIM800L_READ_BUFFER_LENGTH = 1024;
enum State { enum State {
STATE_IDLE = 0, STATE_IDLE = 0,
STATE_INIT, STATE_INIT,
STATE_CHECK_AT, STATE_SETUP_CMGF,
STATE_SETUP_CLIP,
STATE_CREG, STATE_CREG,
STATE_CREGWAIT, STATE_CREG_WAIT,
STATE_CSQ, STATE_CSQ,
STATE_CSQ_RESPONSE, STATE_CSQ_RESPONSE,
STATE_IDLEWAIT, STATE_SENDING_SMS_1,
STATE_SENDINGSMS1, STATE_SENDING_SMS_2,
STATE_SENDINGSMS2, STATE_SENDING_SMS_3,
STATE_SENDINGSMS3,
STATE_CHECK_SMS, STATE_CHECK_SMS,
STATE_PARSE_SMS,
STATE_PARSE_SMS_RESPONSE, STATE_PARSE_SMS_RESPONSE,
STATE_RECEIVESMS, STATE_RECEIVE_SMS,
STATE_READSMS, STATE_RECEIVED_SMS,
STATE_RECEIVEDSMS,
STATE_DELETEDSMS,
STATE_DISABLE_ECHO, STATE_DISABLE_ECHO,
STATE_PARSE_SMS_OK,
STATE_DIALING1, 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 { 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; } void set_rssi_sensor(sensor::Sensor *rssi_sensor) { rssi_sensor_ = rssi_sensor; }
#endif #endif
void add_on_sms_received_callback(std::function<void(std::string, std::string)> callback) { 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_sms(const std::string &recipient, const std::string &message);
void send_ussd(const std::string &ussd_code);
void dial(const std::string &recipient); void dial(const std::string &recipient);
void connect();
void disconnect();
protected: protected:
void send_cmd_(const std::string &message); void send_cmd_(const std::string &message);
@ -76,6 +95,7 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent {
sensor::Sensor *rssi_sensor_{nullptr}; sensor::Sensor *rssi_sensor_{nullptr};
#endif #endif
std::string sender_; std::string sender_;
std::string message_;
char read_buffer_[SIM800L_READ_BUFFER_LENGTH]; char read_buffer_[SIM800L_READ_BUFFER_LENGTH];
size_t read_pos_{0}; size_t read_pos_{0};
uint8_t parse_index_{0}; uint8_t parse_index_{0};
@ -86,10 +106,19 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent {
std::string recipient_; std::string recipient_;
std::string outgoing_message_; std::string outgoing_message_;
std::string ussd_;
bool send_pending_; bool send_pending_;
bool dial_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> { 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...> { template<typename... Ts> class Sim800LSendSmsAction : public Action<Ts...> {
public: public:
Sim800LSendSmsAction(Sim800LComponent *parent) : parent_(parent) {} Sim800LSendSmsAction(Sim800LComponent *parent) : parent_(parent) {}
@ -116,6 +172,20 @@ template<typename... Ts> class Sim800LSendSmsAction : public Action<Ts...> {
Sim800LComponent *parent_; 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...> { template<typename... Ts> class Sim800LDialAction : public Action<Ts...> {
public: public:
Sim800LDialAction(Sim800LComponent *parent) : parent_(parent) {} Sim800LDialAction(Sim800LComponent *parent) : parent_(parent) {}
@ -129,6 +199,25 @@ template<typename... Ts> class Sim800LDialAction : public Action<Ts...> {
protected: protected:
Sim800LComponent *parent_; 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 sim800l
} // namespace esphome } // namespace esphome