OnTrue
// David Wallis 17/11/15
// Version 1.4 01/12/15
var moduleDomain = "HomeAutomation.SMS";
Program.Setup(() =>
{
// Used for calling web service from other programs
Program.AddVirtualModules(moduleDomain, "Switch", "homegenie/generic/program", 1, 1);
// Serial Settings
Program.AddOption("Port", "/dev/ttyAMA0", "Port of GSM Modem","text");
Program.AddOption("BaudRate", "9600", "Baud Rate of GSM Modem","text");
// Logging Settings
Program.AddOption("Logging", "TRUE", "Enable logging","text");
Program.AddOption("LogPath", @"/usr/local/bin/homegenie/log/sms.log", "Path to debugging log file","text");
Program.Parameter("LastUpdate").Value = DateTime.Now.ToString();
});
return true;
// David Wallis 17/11/15
// Version 1.4 01/12/15
// Version 1.5 20/12/15
// Version 1.6 03/04/16
// Disable balance notifications from provider (Giffgaff)
var moduleDomain = "HomeAutomation.SMS";
string logging = Program.Option("Logging").Value;
var logPath = Program.Option("LogPath").Value;
string sender = "";
string lastCommand = "";
bool deleteSMS = true; // change to Program.Option
// TODO - Split out command Processor
// TODO - Periodic Modem Status Check.
// Logging functionality
Action<string> Log = (string logtext) => {
if ((string.IsNullOrEmpty(logPath) == false) && (logging.ToUpper() == "TRUE"))
{
System.IO.File.AppendAllText(logPath, DateTime.Now.ToLocalTime().ToString("dd-MM-yyyy HH.mm.ss.ffff") + " " + logtext + "\n");
// Uncomment below line for debugging on screen rather than to the log
// Program.Notify("SMS Debug",logtext);
}
};
// Validate the sender's number - we could define what to do with numbers we dont recognise.
Func<string, bool> ValidateNumber = (string number) => {
string[] validNumbers = { "+447733007886", "+447734088550", "+447596420164" }; // Valid phone numbers
bool result = validNumbers.Contains(number);
Log(number + (result ? " Successfully validated" : " Invalid Number"));
return result;
};
// Does nothing
Action<byte[]> HandleMessageReceived = (byte[] message) => { /* Do nothing, string received doesnt seem to work without this handler */ };
// Handles serial port state changes
Action<bool> HandleStatusChanged = (bool connected) => {
Program.RaiseEvent("GSM Modem", (connected ? "Connected" : "Disconnected"), "GSM Modem");
Log("GSM Modem " + (connected ? "Connected" : "Disconnected"));
};
// Sends commands and optionally logs.
Action<string> SendCommand = (string command) => {
lastCommand = command;
SerialPort.SendMessage(command + "\r\n");
Log(string.Format("Sending Command: {0}",command));
Pause(1);
};
// Sends a text message
Func<string, string, bool> SendMessage = (string number, string msg) => {
// Add 44 to start and trim leading 0 if not present (UK)
if (!number.StartsWith("+44")) { number = "+44" + number.TrimStart(new char[] { '0' }); }
Log(String.Format("Attempting to send text message to {0}",number));
// Trim message if it exceeds 160 characters
if (msg.Length > 160)
{
Log("Message exceeds permitted length, trimming");
msg = msg.Substring(0, 160);
}
// Send SMS
SendCommand("AT+CMGS=\"" + number + "\"");
Pause(.5);
Log(string.Format("Sending SMS: {0}",(msg.Trim() + (Char)26)));
lastCommand = "SMS Body: " + (msg.Trim() + (Char)26);
SerialPort.SendMessage(msg.Trim() + (Char)26);
return true;
};
Action<string, string> ProcessMessage = (string messageText, string number) => {
var cmdProcessor = Modules.InDomain("HomeAutomation.CommandProcessor").WithAddress("1").Get();
cmdProcessor.Command(number).Set(messageText);
};
// Process messages received from the modem
Action<string> HandleStringReceived = (string message) => {
// TODO: Use switch statement
if (message == "> ") { // Sending text message
return;
}
if (message.StartsWith("ERROR")) {
Log("Command Error, Last Command " + lastCommand);
return;
}
if (message == "OK")
{
Log("Received: OK");
return;
}
if (message.StartsWith("+CMGL:")) // Process List of stored messages
{
System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"\+CMGL: (\d+),""(.+)"",""(.+)"",(.*),""(.+)");
System.Text.RegularExpressions.Match match = regex.Match(message);
if (match.Success)
{
int index = int.Parse(match.Groups[1].Value);
sender = match.Groups[3].Value.Replace(" ", ""); // Sender
var sent = match.Groups[5].Value.Replace(" ", "");
Log(string.Format("Unprocessed message, Index {0} Sender {1} Sent {2}", index, sender, sent));
// Delete Message
if (deleteSMS) {
Log(string.Format("Deleting Message, Index: {0}",index));
Pause(1.5);
SendCommand("AT+CMGD=" + index);
}
return;
}
}
if (message.StartsWith("+CMTI:")) // New Message Notification
{
string[] fields = message.Split(new Char[] { ',', '"' });
if (fields.Length < 3)
{
Log(string.Format("Notification Message too short. Expected 3 fields Message: {0}", message));
return;
}
var index = fields[3];
Log(string.Format("New Message Notification, Index: {0}",index));
SendCommand("AT+CMGR=" + index); // Issue read message command
// Delete message
if (deleteSMS) {
Pause(1.5);
Log(string.Format("Deleting Message, Index: {0}",index));
SendCommand("AT+CMGD=" + index);
}
return;
}
if (message.StartsWith("+CMGR:")) // Read Message
{
Log("CMGR Text: " + message);
try
{
System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"\+CMGR: ""REC UNREAD"",""(\+\d+)"",,""(.+)""");
System.Text.RegularExpressions.Match m = r.Match(message);
if (m.Success)
{
sender = m.Groups[1].Value.ToString();
string Sent = m.Groups[2].Value;
Log(string.Format("Sender {0} Sent: {1}", sender, Sent));
}
else
{
Log("No Match for +CMGR RegEx");
}
}
catch (Exception e)
{
Log(string.Format("CMGR ERROR! {0}",e.Message));
}
return;
}
if (message.StartsWith("+CUSD:")) // GSM Balance
{
Log("CUSD Text: " + message);
try
{
System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"\+CUSD:\s*(\d+)(?:,\s*""([^,]*)""(?:,(\d+))?)?");
System.Text.RegularExpressions.Match m = r.Match(message);
if (m.Success)
{
Program.Notify("SMS Balance ", m.Groups[2].Value);
Log(string.Format("SMS Balance {0}", m.Groups[2].Value));
}
else
{
Log("No Match for +CUSD RegEx");
}
}
catch (Exception e)
{
Log(string.Format("CUSD ERROR! {0}",e.Message));
}
return;
}
if (message.StartsWith("+CMGS:")) // Sent Message
{
Log ("Message Sent, Resp: " + message);
return;
}
// This should be the message
ProcessMessage(message, sender);
};
// Handle web service calls, enabling messages from other automation programs.
When.WebServiceCallReceived(moduleDomain, (args) => {
try
{
string[] reqs = ((string)args).Split('/');
string command = reqs[2].Substring(0, reqs[2].IndexOf(':'));
string number = reqs[2].Substring(reqs[2].IndexOf(':') + 1);
string msg = reqs[3];
Log(string.Format("Sending message to number {0}, Message text: {1}", number, msg));
switch (command.ToUpper())
{
case "SEND":
bool resp = SendMessage(number, msg);
if (resp)
{
Log("Success!");
return "{ 'ResponseValue' : 'OK' }";
}
Log("ERROR!");
return "{ 'ResponseValue' : 'ERROR' }";
default:
Log("Unknown command");
return "{ 'ResponseValue' : 'ERROR' }";
}
}
catch (Exception e)
{
Log(string.Format("ERROR! {0}",e.Message));
}
return "{ 'ResponseValue' : 'ERROR' }";
});
Log("Program starting");
// Loop whilst program is running
while (Program.IsEnabled){
if(!SerialPort.IsConnected){
Log("Connecting to GSM Modem on port " + Program.Option("Port").Value);
SerialPort
.WithName(Program.Option("Port").Value)
.OnStatusChanged(HandleStatusChanged)
.OnMessageReceived(HandleMessageReceived)
.OnStringReceived(HandleStringReceived)
.Connect(9600);
SerialPort.EndOfLine = "\r\n";
Log("Initialising GSM Modem");
Log("Turning local echo off");
SendCommand("ATE0"); // Local Echo off
Log("Switching to data mode");
SendCommand("AT+CMGF=1"); // SMS Mode
SendCommand("AT+CSCS=\"GSM\"");
Log("Enabling message notifications");
SendCommand("AT+CNMI=2,1,0,0,1"); // Enable Message Notifications
// Issue command to list any unread messages stored on the gsm modem (assume read = deleted)
Log("Getting any stored messages");
//SendCommand("AT+CMGL=\"REC UNREAD\""); // Unread
SendCommand("AT+CMGL=\"ALL\""); // All
// Get Balance (GiffGaff)
// TODO: Make this periodic
SendCommand("ATD*100*7#;");
}
// Stop excessive cpu usage
Pause(60);
}
Program update is required.
HomeAutomation.HomeGenie.Automation
1009
SMS Messaging
Allows messaging via SMS
Wallis
2016-10-10T18:23:02.260632Z
CSharp
true