Skip to content

Instantly share code, notes, and snippets.

Last active May 23, 2024 16:59
Show Gist options
  • Save rcoup/93460ea39b05e957e884 to your computer and use it in GitHub Desktop.
Save rcoup/93460ea39b05e957e884 to your computer and use it in GitHub Desktop.
Gammu config and script for receiving SMS via a USB modem attached to an OSX computer and forwarding it into iMessage, where it will appear on all your devices.
port = /dev/tty.HUAWEIMobile-Modem
connection = at19200
model = at
synchronizetime = yes
logfile = /Users/me/.gammu/log
logformat = errorsdate
gammucoding = utf8
Service = files
CheckSecurity = 0
LogFile = /Users/me/Library/logs/gammu.log
InboxPath = /Users/me/.gammu/inbox/
OutboxPath = /Users/me/.gammu/outbox/
SentSMSPath = /Users/me/.gammu/sent/
ErrorSMSPath = /Users/me/.gammu/error/
DebugLevel = 1
RunOnReceive = python /Users/me/.gammu/ +447700900000 NZ
CheckBattery = 0

Idea (swap out the countries & numbers):

  • You live in the UK, with a UK mobile number and an iPhone (eg. +447700900000)
  • And have a New Zealand sim card and number (eg. +6421000000), in a USB modem attached to your Mac (permanently roaming)
  • When somebody SMSs your New Zealand number, make it appear in iMessage on your UK phone
  1. Get a USB modem that works on OSX. I use an ancient Huawei E220 I found on eBay. Check it's unlocked for your SIM and can send/receive texts using the vendor's Mac software.
  2. Install Gammu via git or homebrew.
  3. Put into ~/.gammu, and the .gammurc file into ~. Edit the paths in the files.
  4. Manually start the daemon with gammu-smsd -c .gammurc --pid .gammu/pid, then look at ~Library/logs/gammu.log and ~/Library/logs/gammu-forwardToIMessage.log for debugging help. Send yourself some SMS messages to test.
  5. Once it's working, to start it on boot (so it will pick up messages whenever you connect/disconnect the USB dongle), update the paths in the .plist file and drop it into ~/Library/LaunchAgents, then:
    launchctl load -w ~/Library/LaunchAgents/gammu.IMessageForwarder.plist`
    launchctl start gammu.IMessageForwarder


  • SMSing an iMessage number via iMessage won't actually send an SMS. Using Skype if you need to test SMS is pretty cheap and easy.
  • If you get AppleScript errors, have a go at sending to your iMessage email address rather than your iMessage number. (change RunOnReceive in ~/.gammurc)
  • Has been working under Sierra, El Capitan, and Mavericks.
#!/usr/bin/env python
from __future__ import print_function
import logging
import os
import re
import subprocess
import sys
TEMPLATE = u"%(country)s%(number)s: %(message)s"
on run {targetBuddyPhone, targetMessage}
tell application "Messages"
set targetService to 1st service whose service type = iMessage
set targetBuddy to buddy targetBuddyPhone of targetService
send targetMessage to targetBuddy
end tell
end run
LOG = os.path.expanduser('~/Library/logs/gammu-forwardToIMessage.log')
L = logging.getLogger("forwardToIMessage")
class SMSError(Exception):
class IMessageError(Exception):
def flag(code):
OFFSET = 127397
if not code:
return u''
points = [ord(x) + OFFSET for x in code.upper()]
return chr(points[0]) + chr(points[1])
except ValueError:
return ('\\U%08x\\U%08x' % tuple(points)).decode('unicode-escape')
def get_message(files):
if not files:
# get from environment
number = os.environ['SMS_1_NUMBER']
# Are there any decoded parts?
numparts = int(os.environ['DECODED_PARTS'])
if numparts:
# Get all text parts
text = ''
for i in range(0, numparts):
varname = 'DECODED_%d_TEXT' % i
if varname in os.environ:
text = text + os.environ[varname]
text = os.environ['SMS_1_TEXT']
files.sort() # make sure we get the parts in the right order
number = re.match(r'^IN\d+_\d+_\d+_(\+?\d+)_\d+\.txt', os.path.split(files[0])[1]).group(1)
L.debug('Files: parsed sending number as: %s', number)
text = ''
for f in files:
L.debug('Files: parsing: %s', f)
text += open(f, 'r').read()
text = text.decode('UTF-8', 'strict')
except UnicodeDecodeError:
L.exception("Error decoding message as utf-8, falling back to '?' replacement: [%s]", repr(text))
text = text.decode('UTF-8', 'replace')
return number, text
def send_imessage(recipient, text):
args = ['osascript', '-', recipient, text.encode('utf-8')]
L.debug("Invoking AppleScript: %s", args)
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate(APPLESCRIPT)
L.debug("AppleScript results: %d: %s", p.returncode, stdout)
if p.returncode:
raise IMessageError("Error %d sending (%r %r): %s" % (p.returncode, recipient, text, stdout))
return stdout
def main():
filename=os.path.join(os.path.split(__file__)[0], LOG),
format='%(asctime)-15s %(levelname)s %(funcName)s:%(lineno)d %(message)s',
)"Starting: args=%s", sys.argv[1:])
L.debug("Relevant environment=%s", repr(dict([(k, v) for k, v in os.environ.iteritems() if k.startswith(("SMS_", "DECODED_"))])))
if len(sys.argv) < 3:
print >>sys.stderr, USAGE % sys.argv[0]
if 'SMS_1_NUMBER' in os.environ:
# parse from environment (default)"Getting message info from environment...")
msg_files = None
# parse from message files"No data found in environment, parsing from message files...")
msg_files = [os.path.join(os.path.split(__file__)[0], 'inbox', m) for m in sys.argv[3:]]
if not len(msg_files):
print >>sys.stderr, "No message found in environment, and no message paths specified"
print >>sys.stderr, USAGE % sys.argv[0]
sys.exit(2)"Message file paths: %s", msg_files)
recipient = sys.argv[1]
country = flag(sys.argv[2]) or sys.argv[2].upper()
number, text = get_message(msg_files)"From %s: %s", number, repr(text))
message = TEMPLATE % {'number': number, 'message': text, 'country': country}
send_imessage(recipient, message)
L.exception("Error processing message")
raise"Done :)")
if __name__ == "__main__":
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN >
<plist version="1.0">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment