RkBlog

Sending SMS through GoIP GSM gateway using HTTP API

2024-06-15

GoIP is a network device and a GSM gateway in one made by Hybertone and DBL. It can be used for SMS or VoIP features in your applications. In comparison to a simple GSM modem that can be operated through AT commands over a serial connection a GoIP device offers way more management options, is stand-alone, and can scale to many SIM cards.

Currently GoIP devices can be found on sites like Aliexpress. The starting model has a one GSM modem but the top of the line can handle over 100 SIM cards. For pure SMS handling, it can be better to use services like Twilio, but for more remote locations like islands where Twilio services are way more expensive an on-site GoIP can be a way cheaper solution for local SMS messages.

GoIP device
GoIP-1 GSM gateway device
GoIP-1 GSM gateway device
Insides of GoIP - 5VT1310 VoIP SoC based on ARM926EJS processor
Insides of GoIP - 5VT1310 VoIP SoC based on ARM926EJS processor

How to use a GoIP?

When you connect the device to your local network through an Ethernet cable and power on the device with the provided power supply the device will launch a web management panel. You have to check your router or use a port sniffer to see which local IP the device got assigned.

Open the IP in your browser (for example http://192.168.0.2/) and you should see a login request. Default login and password is admin and admin.

When you log in you should have access to GoIP admin/management panel. You can configure the device as well as test and manage some of its GSM features.

GoIP web admin panel - starting status page
GoIP web admin panel - starting status page

The goal is to configure the GSM and then set up access to the device based on your use case. GoIP can be used as is, using some of its HTTP API to send SMS or through compatible web SMS servers or through compatible apps using SMMP protocol.

If you want to use the device directly with your application then either it has to be in the same local network or you have to expose the GoIP through a public IP (which can be a security risk). Ngrok can be a handy way of exposing GoIP, at least for prototyping.

SMS servers, like the one provided by DBL, allow you to keep GoIP devices off the public net, which may be the way to go when you will be operating multiple devices, or you want better access control. There are also third-party servers like Ozeki NG SMS Gateway that are compatible with GoIP. See device documentation for more info.

GSM configuration

By default the GSM module will be off. Go to Configuration - SIM to do basic configuration as needed. If your SIM card requires an unlock PIN you have to specify it there. When all seems to be in order you can try turning the GSM module on.

SIM configuration
SIM configuration

SIM/GSM switch is one you probably never expected. Go to the main Summary page and look at the table listing available SIM channels. In the M column you will see N value. The N is a link, click it to toggle the SIM on/off.

Activating GSM module
Activating GSM module

After which you should see the GSM coming up online. In Tools - Send SMS you can try it out by sending a message to your number.

HTTP API - Sending SMS

GoIP has a basic HTTP API for sending SMS. All you need is a GET request:

http://DEVICE_IP/default/en_US/send.html?u=LOGIN&p=PASSWORD&l=CHANNEL&n=PHONE_NUMBER&m=MESSAGE_TEXT

The endpoint will return a response of success/error if it's able to process and start sending the message:

Sending,L1 Send SMS to:PHONE_NUMBER_HERE; ID:SOME_MESSAGE_ID
ERROR,L1 busy

This is not actual delivery status, but more of a device status. For given channel it will reject all incoming messages until it's done sending current message. That's why after sending a message you should check actual status using another endpoint:

http://IP/default/en_US/send_status.xml?u=LOGIN&p=PASSWORD

Which will return status for each device channel. The ID you got while sending SMS will be listed here as well:

<?xml version="1.0" encoding="utf-8"?>
<send-sms-status>
	<id1>SOME_MESSAGE_ID</id1>
	<status1>DONE</status1>
	<error1></error1>
	<id2></id2>
	<status2></status2>
	<error2></error2>
	<id3></id3>
	<status3></status3>
	<error3></error3>
	<id4></id4>
	<status4></status4>
	<error4></error4>
	<id5></id5>
	<status5></status5>
	<error5></error5>
	<id6></id6>
	<status6></status6>
	<error6></error6>
	<id7></id7>
	<status7></status7>
	<error7></error7>
	<id8></id8>
	<status8></status8>
	<error8></error8>
</send-sms-status>

Note that it's the status for the last processed message. If you send another one you will not be able to check the status of the previous one. This output is from GoIP-1 which has only one channel, yet it returns XML response for 8 channels. DONE is done while the initial status is STARTED and until it changes you can't send another message through that channel.

Libraries for software development

Official software is written in PHP while on GitHub you will be able to find quite a lot of libraries or feature implementations using GoIP. Just check if they are still supported as some are quite old.

Python wrapper class

GoIP API is simple and all we need is Python and requests library.

import urllib.parse

import requests
import xml.etree.ElementTree as xml_tree


class GoIP:
    def __init__(self, base_url, login, password):
        self.base_url = base_url
        self.login = login
        self.password = password

    def send_sms(self, number, text, sim_slot=1):
        endpoint = 'default/en_US/send.html'
        data = {
            'n': number,
            'm': text,
            'l': sim_slot,
        }
        response = self._send_request(endpoint, data)
        return self._parse_send_response(response)

    def _parse_send_response(self, response):
        response = response.strip()
        status = response.split(',')[0].strip()
        if status == 'Sending':
            message_id = response.split('ID:')[-1].strip()
            return {'status': status, 'message_id': message_id}
        else:
            error_message = response.replace(f'{status},', '')
            return {'status': status, 'error_message': error_message}

    def get_sms_status(self):
        endpoint = 'default/en_US/send_status.xml'
        data = {}
        response = self._send_request(endpoint, data)
        return self._parse_status_response(response)

    def _parse_status_response(self, response):
        xml = xml_tree.fromstring(response)
        response = {}
        for element in xml:
            channel = element.tag[-1]
            tag = element.tag[:-1]
            if channel not in response:
                response[channel] = {'id': None, 'status': None, 'error': None}
            response[channel][tag] = element.text
        return response

    def _send_request(self, endpoint, data):
        url = urllib.parse.urljoin(self.base_url, endpoint)
        data.update({
            'u': self.login,
            'p': self.password,
        })
        return requests.get(url, data=data).text

send_sms method collects all the data that has to go into the query string and makes the GET request with requests and parses the output into a dictionary.

get_sms_status method makes the request to the status endpoint and then parses the XML grouping fields by channel and returns a dict of dicts.

import time

x = GoIP('http://GOIP_IP', 'LOGIN', 'PASSWORD')
print(x.send_sms('YOUR_NUMBER', 'Hello SMS!'))

for i in range(1, 30):
    time.sleep(0.5)
    print(x.get_sms_status())

Web scrapping and automating GoIP admin panel

The web admin panel isn't very sophisticated, and it's very easy to use it through automated requests from your code. All you have to do is set the Authorization header as shown by goip-sms-parser application.

As shown by the code the Authorization header is just base64 of login and password. GoIP panel in Tools tab has a page listing incoming messages and the goip-sms-parser is parsing data from that page using some regexp and CSV parsing:

import base64
import requests
import re
import csv


class GoipGateway:
    def __init__(self, goip_addr, goip_user, goip_password):
        self.goip_addr = goip_addr
        self.goip_user = goip_user
        self.goip_password = goip_password

    def _receive_messages(self):
        auth_string = f"{self.goip_user}:{self.goip_password}"
        auth_header = {
            "Authorization": f"Basic {base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')}"
        }

        response = requests.get(f"{self.goip_addr}/default/en_US/tools.html?type=sms_inbox", headers=auth_header)
        data = response.text

        data = data.replace('\\"', '"').encode('latin1').decode('utf-8')
        sms_dump_arr = re.findall(r'sms= \[(.*?)\]', data, re.IGNORECASE | re.DOTALL)

        for sim_key, sim_val in enumerate(sms_dump_arr):
            for sms_val in csv.reader([sim_val], skipinitialspace=True, quotechar="'"):
                messages = [{'date': sms_val[i], 'phone': sms_val[i+1], 'text': sms_val[i+2]} for i in range(0, len(sms_val)-2, 3) if sms_val[i] != '""']
                for message in messages:
                    yield message

With a response looking like so (I've edited the method slightly to use a generator instead of returning a list of lists):

[{'date': '"06-15 18:15:47', 'phone': 'RECIPIENT_PHONE_NUMBER', 'text': 'some message"'}]

If you want to use GoIP to send marketing messages you may be legally required to support unsubscription STOP messages and for that, you have to read incoming messages and match the phone number with the marketing subscription and cancel it.

And the options don't end at SMS. There are VoIP options as well, and you can find some examples of them on GitHub.

Summary

GoIPs are unusual devices that serve a niche, and they can get the job done at a relatively low cost. It's likely not the most secure and feature-complete solution though. You should do some research on how to set up your system in most secure and reliable manner. Also, legal aspects can be a factor in your country - wherever it's allowed to use such devices or how to legally send things like marketing SMS messages or general bulk SMS sending.

I heard of this device first when a customer of a web service I work on was using it to send local SMS cheaper than Twilio or Vonage as it was on an island, far away from continental telecom infrastructure and that seems like a nice use case for those devices. You could play with a router and GSM modem, sending AT commands, but it's more crude and requires specific hardware which may be harder to get, manage, and troubleshoot.

Comment article