From e4d0a2755b1b058de0b1247b8cb9b32b6cf8d981 Mon Sep 17 00:00:00 2001 From: denis Date: Fri, 16 Apr 2021 00:47:48 +0200 Subject: [PATCH] README install --- README.md | 43 +++- build/lib/smsquitto/__init__.py | 0 build/lib/smsquitto/smsquitto-conf.yaml | 9 + build/lib/smsquitto/smsquittod.py | 197 ++++++++++++++++++ dist/smsquitto-1.0-py3-none-any.whl | Bin 0 -> 18519 bytes dist/smsquitto-1.0.tar.gz | Bin 0 -> 18481 bytes setup.cfg | 2 + setup.py | 49 +++++ smsquitto-install/installer.sh | 12 ++ smsquitto-install/smsquitto-start | 6 +- smsquitto-install/smsquitto-stop | 4 +- .../supervisor/patch/http.py.patch | 11 + .../{sms-client.conf => smsquitto.conf} | 0 smsquitto-install/supervisor/supervisord | 3 + smsquitto.egg-info/PKG-INFO | 197 ++++++++++++++++++ smsquitto.egg-info/SOURCES.txt | 12 ++ smsquitto.egg-info/dependency_links.txt | 1 + smsquitto.egg-info/requires.txt | 4 + smsquitto.egg-info/top_level.txt | 1 + smsquitto.toml | 3 + 20 files changed, 540 insertions(+), 14 deletions(-) create mode 100644 build/lib/smsquitto/__init__.py create mode 100644 build/lib/smsquitto/smsquitto-conf.yaml create mode 100644 build/lib/smsquitto/smsquittod.py create mode 100644 dist/smsquitto-1.0-py3-none-any.whl create mode 100644 dist/smsquitto-1.0.tar.gz create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 smsquitto-install/installer.sh create mode 100644 smsquitto-install/supervisor/patch/http.py.patch rename smsquitto-install/supervisor/{sms-client.conf => smsquitto.conf} (100%) create mode 100755 smsquitto-install/supervisor/supervisord create mode 100644 smsquitto.egg-info/PKG-INFO create mode 100644 smsquitto.egg-info/SOURCES.txt create mode 100644 smsquitto.egg-info/dependency_links.txt create mode 100644 smsquitto.egg-info/requires.txt create mode 100644 smsquitto.egg-info/top_level.txt create mode 100644 smsquitto.toml diff --git a/README.md b/README.md index ce27dd1..acbfc5b 100644 --- a/README.md +++ b/README.md @@ -56,33 +56,56 @@ https://github.com/termux/termux-app - Termux: API - Termux: Widget +<<<<<<< Upstream, based on origin/main #### MQTT client installing: +======= + +#### Installing the mqtt client: +>>>>>>> 5d3172f README install The following python packages will be installed: - termux-sms-gateway - paho-mqtt - supervisor +<<<<<<< Upstream, based on origin/main +======= + - supervisor is a process control system + - because Android do not support hard links, it will be necessary to patch http.py from library +>>>>>>> 5d3172f README install - rsa >> https://stuvel.eu/python-rsa-doc/ ### Installation procedure -After installing termux on the laptop, launch Termux +After installing termux on the smartphone, launch Termux - Copy and paste the following command in the termux terminal - export PACKAGE="TermuxSmsGateway"&&export VERSION="1.0"&&mkdir -p $HOME/.termux/tasks&& - apt install termux-api git -y&&rm -rf termux-sms-gateway&& - git clone && - cp -f $PACKAGE/smsquitto-st* $HOME/.termux/tasks/&&cp -f $PACKAGE/smsquitto/smsquitto-conf.yaml $HOME/.termux/&& - pip install $PACKAGE/dist/smsquitto-$VERSION.tar.gz + export PACKAGE="TermuxSmsGateway"&& + export VERSION="1.0"&& + mkdir -p $HOME/.termux/tasks&& + apt install python git termux-api -y&& + rm -rf TermuxSmsGateway&& + git clone https://github.com/deunix-educ/TermuxSmsGateway.git&& + cp -f $PACKAGE/smsquitto-install/smsquitto-st* $HOME/.termux/tasks/&& + cp -f $PACKAGE/smsquitto/smsquitto-conf.yaml $HOME/.termux/&& + cp -f $PACKAGE/smsquitto-install/supervisor/supervisord $HOME/.termux/boot/&& + cp -f $PACKAGE/smsquitto-install/supervisor/supervisord.conf $PREFIX/etc/&& + cp -f $PACKAGE/smsquitto-install/supervisor/smsquitto.conf $PREFIX/etc/supervisor.d/&& + pip install $PACKAGE/dist/smsquitto-$VERSION.tar.gz&& + cd $PREFIX/lib/python3.9/site-packages/supervisor&& + patch < $PACKAGE/smsquitto-install/supervisor/patch/http.py.patch - Enter to start installation ### Installation parameters +<<<<<<< Upstream, based on origin/main - Installation parameters are in ~/.termux/smsquitto-conf.yaml +======= +- smsquitto server parameters are in ~/.termux/smsquitto-conf.yaml +>>>>>>> 5d3172f README install - host: ip or domain of the MQTT server - port: 1883 or 8883 - keepalive: 60 @@ -96,11 +119,9 @@ After installing termux on the laptop, launch Termux - ex: SQTT010203040506079 - Copy and paste the following command in the terminal - nano $HOME/.termux/smsquitto-conf.yaml - Install services (start, stop, status) - - Install the Tremux widget:Widget on the phone's desktop by keeping your finger on the screen - Choose shortcut - Change the icon and the name if you want @@ -134,11 +155,13 @@ After installing termux on the laptop, launch Termux - Notification - script://notify.sh #FIELD1 #FIELD2 #FIELD3 #FIELD4 #TO #MESSAGE --tls or nothing - - /usr/bin/python3 /home/pi/domoticz/scripts/notify.py --method=notify --host=$1 --port=$2 --user=$3 --password=$4 --apikey=$5 --text="$6" $7 + - command + - /usr/bin/python3 /home/pi/domoticz/scripts/notify.py --method=notify --host=$1 --port=$2 --user=$3 --password=$4 --apikey=$5 --text="$6" $7 - SMS - script://notify.sh #FIELD1 #FIELD2 #FIELD3 #FIELD4 #TO #MESSAGE "00330645953706,0033791246318" --tls or nothing - - /usr/bin/python3 /home/pi/domoticz/scripts/smsquitto.py --method=sms --host=$1 --port=$2 --user=$3 --password=$4 --apikey=$5 --text="$6" --phone="$7" $8 + - command + - /usr/bin/python3 /home/pi/domoticz/scripts/smsquitto.py --method=sms --host=$1 --port=$2 --user=$3 --password=$4 --apikey=$5 --text="$6" --phone="$7" $8 - SSL certification - argument --tls, in $7 or $8. diff --git a/build/lib/smsquitto/__init__.py b/build/lib/smsquitto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/smsquitto/smsquitto-conf.yaml b/build/lib/smsquitto/smsquitto-conf.yaml new file mode 100644 index 0000000..addcfc4 --- /dev/null +++ b/build/lib/smsquitto/smsquitto-conf.yaml @@ -0,0 +1,9 @@ +settings: + host: broker.emqx.io + port: 1883 + keepalive: 60 + username: emqx + password: public + useSSL: False + ca_cert: None + apikey: SQTT0192837495867 diff --git a/build/lib/smsquitto/smsquittod.py b/build/lib/smsquitto/smsquittod.py new file mode 100644 index 0000000..d5accba --- /dev/null +++ b/build/lib/smsquitto/smsquittod.py @@ -0,0 +1,197 @@ +# +# encoding: utf-8 +import atexit +import sys, os +import datetime +import json +import ssl +import subprocess +import paho.mqtt.client as mqtt +import yaml + +def yaml_load(f): + with open(f, 'r') as stream: + return yaml.load(stream, Loader=yaml.FullLoader) + return {} + +class ClientMQTTBase(mqtt.Client): + def __init__(self, + host='localhost', + port=1883, + keepalive=60, + username=None, + password=None, + useSSL=False, + ca_cert=None, + apikey='0192837495867' + ): + + super(ClientMQTTBase, self).__init__() + self.host = host + self.port = port + self.keepalive = keepalive + if username: + self.username_pw_set(username, password) + + if useSSL and ca_cert: + self.tls_set(ca_certs=ca_cert, tls_version=ssl.PROTOCOL_TLSv1_2) + #self.tls_insecure_set(True) + + self.topic = '%s/emit/' % apikey + self.subscriptions = [['%s/pub/#' % apikey, 0],] + + def now(self): + return int(datetime.datetime.now().timestamp()) + + def sub(self): + subs = [(topic, qos) for topic, qos in self.subscriptions] + return subs + + def unsub(self): + return [topic for topic, _ in self.subscriptions ] + + def startMQTT(self): + self.connect(self.host, self.port, self.keepalive) + self.subscribe(self.sub()) + + def stopMQTT(self): + self.unsubscribe(self.unsub()) + self.disconnect() + self.exit() + + def publish_message(self, topic, **payload): + try: + qos = payload.pop('qos', 0) + retain = payload.pop('retain', False) + message = json.dumps(payload) + self.publish(topic, payload=message.encode('utf_8'), qos=qos, retain=retain) + except Exception as e: + print("ClientMQTTBase::publish_message error:", e, flush=True) + + def on_connect(self, mqttc, obj, flags, rc): # @UnusedVariable + #print("ClientMQTTBase on_connect ") + pass + + def on_message(self, mqttc, obj, msg): + #print("ClientMQTTBase on_message:", msg.topic, msg.qos, msg.payload) + pass + + def on_publish(self, mqttc, obj, mid): + #print("on_publish:", mid, obj) + pass + + def on_subscribe(self, mqttc, obj, mid, granted_qos): # @UnusedVariable + #print("ClientMQTTBase on_subscribe:", mid, granted_qos) + pass + + def on_unsubscribe(self, mqttc, userdata, mid): # @UnusedVariable + #print("ClientMQTTBase on_unsubscribe:", mid, userdata) + pass + + def on_disconnect(self, mqttc, userdata, mid): # @UnusedVariable + #print("ClientMQTTBase on_disconnect:", mqttc) + pass + + def on_log(self, mqttc, obj, level, string): # @UnusedVariable + #print("onlog:", level, string) + pass + + +class SmsQuitto(ClientMQTTBase): + + def __init__(self, **mosquitto): + super(SmsQuitto, self).__init__(**mosquitto) + + def exit(self): + self.gw_publish('stop', msg='SmsQuitto service stop!') + + def fetchSMS(self, mtype='inbox'): + p = subprocess.Popen(["termux-sms-list", "-t", mtype], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p.wait() + msg = p.stdout.read() + payload = json.loads(msg.decode("utf-8")) + for p in payload: + evt = p.pop('type') + dte = p.pop('received') + time = datetime.datetime.strptime(dte, '%Y-%m-%d %H:%M').timestamp() + self.publish_message(self.topic + evt, time=time, **p) + + def fetchLocation(self): + p = subprocess.Popen("termux-location", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p.wait() + msg = p.stdout.read() + payload = json.loads(msg.decode("utf-8")) + self.publish_message(self.topic + 'location', time=self.now(), **payload) + + def notification(self, title, msg): + subprocess.run(["termux-notification", "--sound", '--title', title, '--content', msg]) + #print("SmsQuitto::notification", title, msg, flush=True) + + def sendSMS(self, mobile, text): + subprocess.run(["termux-sms-send", "-n", mobile, text]) + print("SmsQuitto::sendSMS", mobile, text, flush=True) + + def gw_publish(self, evt, **payload): + self.publish_message(self.topic + evt, time=self.now(), **payload) + #print("SmsQuitto::gw_publish topic: %s payload: %s"% (self.topic+evt, payload), flush=True) + + def on_connect(self, mqttc, obj, flags, rc): # @UnusedVariable + self.gw_publish('start', msg='SmsQuitto service started!') + self.notification('SmsQuitto', 'Service started!') + #print("SmsQuitto::on_connect SmsQuitto service started!", flush=True) + + def on_disconnect(self, mqttc, obj, flags, rc): # @UnusedVariable + self.gw_publish('off', msg='SmsQuitto disconnected!') + self.notification('SmsQuitto', 'Service disconnected!') + #print("SmsQuitto::on_connect SmsQuitto service disconnected!", flush=True) + + def on_message(self, mqttc, obj, msg): # @UnusedVariable + try: + topic = msg.topic + evt = topic.rsplit('/', 1)[1] + payload = json.loads(msg.payload.decode("utf-8")) + if evt == 'sms': + mobile = payload.get('mobile', None) + text = '%s\n%s' % (payload.get('subject', ''), payload.get('msg', '')) + if mobile: + if not isinstance(mobile, list): + mobile = [mobile] + for mob in mobile: + self.sendSMS(mob, text) + + elif evt == 'location': + self.fetchLocation() + + elif evt in ['all','inbox','sent','draft','outbox']: + self.fetchSMS(evt) + + elif evt == 'notify': + title = payload.get('subject', '') + text = payload.get('msg', '') + self.notification(title, text) + + elif evt == 'kill-service': + self.stopMQTT() + + except Exception as e: + self.gw_publish('error', msg=str(e)) + print("SmsQuitto::on_message error:", e) + +def main(path): + try: + pf = path[0] if path else os.path.dirname(os.path.abspath(__file__)) + config = yaml_load(os.path.join(pf, 'smsquitto-conf.yaml')) + settings = dict(config.get('settings')) + + daemon = SmsQuitto(**settings) + atexit.register(daemon.stopMQTT) + daemon.startMQTT() + daemon.loop_forever() + + except Exception as e: + print("smsquitto.main error", e) + +if __name__ == '__main__': + main(sys.argv[1:]) + +# diff --git a/dist/smsquitto-1.0-py3-none-any.whl b/dist/smsquitto-1.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..53f920b51066a864ec4b892d657477657fda1549 GIT binary patch literal 18519 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 lPg2S^sP$6}{|-C|>nmI(}3-Sxl> z+9*jl$v^9$_AVhouQDFpQHL+MRL4@zQJjP94lP8E5I0IJ>;Kx&f>C5Z9CwK{#}(gW{a!X|5va zHNhm9sWV<5Kkko*!SUr_$&;Vs5m=6k!!$02j_XhVT9!f~4GO@G=SZE+L4KzCQl!GT ztqdDIY?`Bx2WVdFb?sms`nu275eDbCqMBhvqaw4L{9oK(*>2NZQlCzh8^@hDq<*Z? zg5*q-E}(gjMd{FfE4r=^&KSX5Nv|B!WPA(Yyn_-C=48FSb3V^I@^Ge7tKGgk=|GdzAv*H9B%2KHzj|Oka=U zJXoIvU%k9I>u>Zln+c28h0{=fK#wuvLMU_+mx19*aj-u3`p>7qdOi;7P&Rap{R|V_ z$qn3(b$Qa{H|l-$pj|_M!&$IC4!#mZZCFE9;CFs0%5R$Hzv0bfS$|GHusz1Tc{ojK zy}eUE7A~{-oIgjkF7L9e{3!kQ5NLSqs{efO=M7MAen+qR;bNb*Rd2M>uvoKrX6VWc zh8|64#IJ;kdth+)CV%t69GT&OWe6|W>3z3R#3dS#%tRe9ytkS2{NWv^Nv$`8P4(zo zqYkRWM;zATi@BZ>`iuN2%t3)G@rtj8{tb*;Q?$(kx%TJ(8ggl%(Yid⁢dkWk6?e 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 literal 0 HcmV?d00001 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..8183238 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +license_files = LICENSE diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..bb5712d --- /dev/null +++ b/setup.py @@ -0,0 +1,49 @@ +''' +Created on 30 mars 2021 + +@author: denis +''' +from setuptools import setup, find_packages +import pathlib + +here = pathlib.Path(__file__).parent.resolve() + +long_description = (here / 'README.md').read_text(encoding='utf-8') + +setup( + name = 'smsquitto', + version = '1.0', + description = 'SMS gateway using an Android phone', + long_description = long_description, + long_description_content_type = 'text/markdown', + author = 'Deunix from e-educ', + author_email = 'deunix@e-educ.fr', + #url='https://github.com/pypa/termux-smsquitto', + classifiers = [ + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 4 - Beta', + 'Intended Audience :: Any public', + 'Topic :: Software Development :: Sms Tools', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3 :: Only', + ], + keywords = 'Termux, sms, Android, notification, mqtt', + + package = ['smsquitto'], + package_data = {'smsquitto': ['*.yaml'] }, + package_dir = {'smsquitto': './smsquitto' }, + packages=find_packages(where='.'), + + python_requires = '>=3.7', + install_requires=['paho-mqtt', 'rsa', 'PyYAML', 'supervisor'], + + + + +) + + diff --git a/smsquitto-install/installer.sh b/smsquitto-install/installer.sh new file mode 100644 index 0000000..5673a18 --- /dev/null +++ b/smsquitto-install/installer.sh @@ -0,0 +1,12 @@ +#!/data/data/com.termux/files/usr/bin/bash + +export PACKAGE="TermuxSmsGateway"&&export VERSION="1.0"&&mkdir -p $HOME/.termux/tasks&& \ +apt install termux-api git -y&&rm -rf TermuxSmsGateway&& \ +git clone https://github.com/deunix-educ/TermuxSmsGateway.git&& \ +cp -f $PACKAGE/smsquitto-install/smsquitto-st* $HOME/.termux/tasks/&&cp -f $PACKAGE/smsquitto/smsquitto-conf.yaml $HOME/.termux/&& +cp -f $PACKAGE/smsquitto-install/supervisor/supervisord.conf $PREFIX/etc/ \ +cp -f $PACKAGE/smsquitto-install/supervisor/smsquitto.conf $PREFIX/etc/supervisor.d/ \ +pip install $PACKAGE/dist/smsquitto-$VERSION.tar.gz \ +cd $PREFIX/lib/python3.9/site-packages/supervisor \ +patch < $PACKAGE/smsquitto-install/supervisor/patch/http.py.patch \ + diff --git a/smsquitto-install/smsquitto-start b/smsquitto-install/smsquitto-start index 53e353c..9accc89 100644 --- a/smsquitto-install/smsquitto-start +++ b/smsquitto-install/smsquitto-start @@ -1,2 +1,4 @@ -# -/data/data/com.termux/files/usr/bin/python3 -m smsquitto.smsquittod $HOME/.termux/ &>/dev/null +#!/data/data/com.termux/files/usr/bin/bash +#/data/data/com.termux/files/usr/bin/python3 -m smsquitto.smsquittod $HOME/.termux/ &>/dev/null + +data/data/com.termux/files/usr/bin/supervisorctl stop termux_workers:smsquitto \ No newline at end of file diff --git a/smsquitto-install/smsquitto-stop b/smsquitto-install/smsquitto-stop index a0de830..5dde091 100644 --- a/smsquitto-install/smsquitto-stop +++ b/smsquitto-install/smsquitto-stop @@ -1,3 +1,3 @@ #!/data/data/com.termux/files/usr/bin/bash -ps -aux | grep smsquittod | grep -v grep | awk '{print $2}' | xargs kill -9 - +#ps -aux | grep smsquittod | grep -v grep | awk '{print $2}' | xargs kill -9 +data/data/com.termux/files/usr/bin/supervisorctl stop termux_workers:smsquitto diff --git a/smsquitto-install/supervisor/patch/http.py.patch b/smsquitto-install/supervisor/patch/http.py.patch new file mode 100644 index 0000000..f201ed7 --- /dev/null +++ b/smsquitto-install/supervisor/patch/http.py.patch @@ -0,0 +1,11 @@ +--- http-origine.py 2021-04-15 23:41:09.333691030 +0200 ++++ http.py 2021-04-15 23:40:49.734284135 +0200 +@@ -575,7 +575,7 @@ + os.chmod(tempname, sockchmod) + try: + # hard link +- os.link(tempname, socketname) ++ os.symlink(tempname, socketname) + except OSError: + # Lock contention, or stale socket. + used = self.checkused(socketname) diff --git a/smsquitto-install/supervisor/sms-client.conf b/smsquitto-install/supervisor/smsquitto.conf similarity index 100% rename from smsquitto-install/supervisor/sms-client.conf rename to smsquitto-install/supervisor/smsquitto.conf diff --git a/smsquitto-install/supervisor/supervisord b/smsquitto-install/supervisor/supervisord new file mode 100755 index 0000000..8ee4b45 --- /dev/null +++ b/smsquitto-install/supervisor/supervisord @@ -0,0 +1,3 @@ +#!/data/data/com.termux/files/usr/bin/sh +termux-wake-lock +/data/data/com.termux/files/usr/bin/supervisord \ No newline at end of file diff --git a/smsquitto.egg-info/PKG-INFO b/smsquitto.egg-info/PKG-INFO new file mode 100644 index 0000000..68cb6d8 --- /dev/null +++ b/smsquitto.egg-info/PKG-INFO @@ -0,0 +1,197 @@ +Metadata-Version: 2.1 +Name: smsquitto +Version: 1.0 +Summary: SMS gateway using an Android phone +Home-page: UNKNOWN +Author: Deunix from e-educ +Author-email: deunix@e-educ.fr +License: UNKNOWN +Description: # Termux Sms Gateway + SMS and notification gateway using Android smartphone + + #### Goal: + + Make any laptop with an Internet connection a SMS gateway + Send SMS from his desktop using a web interface or other. + Receive SMS on his desk in order to use the content. + + #### Method used + + - SMS sending + + - SMS are created on one machine (1 or more) on the network. + - They are then published to a public or private MQTT server + - The messages are received on an MQTT client installed on the laptop (1 or more). + - The messages are then sent to the recipients. + + #### SMS reception + + - The SMS are received on the smartphone and published to the MQTT server + - The messages are received on a MQTT client installed on the subscribed machine (1 or more) + + #### Non-exhaustive use + + - Email message usage + - Receive and process alert messages (IOT or other) + - etc ... + + #### Conditions for receiving or sending + - Take a public or private MQTT server to relay messages + - Subscribe MQTT customers to the same topics + + > You can install your own mosquitto server + >> https://mosquitto.org/download/ + > + > More information on MQTT: https://fr.wikipedia.org/wiki/MQTT + + - Security of data transmitted + > We may need to encrypt the content for security reasons. + >> Use encryption with public key / private key on clients + + ### Smartphone installation + + https://github.com/termux/termux-app + + #### Installation of termux + - Termux application obtained by F-Droid: + - https://www.f-droid.org/fr/ + + - Or by google store: + - https://play.google.com/ + + > IMPORTANT: We will choose either one or the other. + + - Install + - Termux + - Termux: API + - Termux: Widget + + + #### Installing the mqtt client: + + The following python packages will be installed: + + - termux-sms-gateway + - paho-mqtt + - supervisor + - supervisor is a process control system + - because Android do not support hard links, it will be necessary to patch http.py from library + - rsa >> https://stuvel.eu/python-rsa-doc/ + + ### Installation procedure + + After installing termux on the smartphone, launch Termux + + - Copy and paste the following command in the terminal + + export PACKAGE="TermuxSmsGateway"&& + export VERSION="1.0"&& + mkdir -p $HOME/.termux/tasks&& + apt install python git termux-api -y&& + rm -rf TermuxSmsGateway&& + git clone https://github.com/deunix-educ/TermuxSmsGateway.git&& + cp -f $PACKAGE/smsquitto-install/smsquitto-st* $HOME/.termux/tasks/&& + cp -f $PACKAGE/smsquitto/smsquitto-conf.yaml $HOME/.termux/&& + cp -f $PACKAGE/smsquitto-install/supervisor/supervisord $HOME/.termux/boot/&& + cp -f $PACKAGE/smsquitto-install/supervisor/supervisord.conf $PREFIX/etc/&& + cp -f $PACKAGE/smsquitto-install/supervisor/smsquitto.conf $PREFIX/etc/supervisor.d/&& + pip install $PACKAGE/dist/smsquitto-$VERSION.tar.gz&& + cd $PREFIX/lib/python3.9/site-packages/supervisor&& + patch < $PACKAGE/smsquitto-install/supervisor/patch/http.py.patch + + - Enter to start the installation + + ### Installation parameters + + - smsquitto server parameters are in ~/.termux/smsquitto-conf.yaml + - host: ip or domain of the MQTT server + - port: 1883 or 8883 + - keepalive: 60 + - username: login + - password: password + - useSSL: False or True + - ca_cert: path/to/file/ca.cert + - apikey: *string* + + - ** IMPORTANT **: This key is used to synchronize the topics with the subscriptions + - ex: SQTT010203040506079 + + - Copy and paste the following command in the terminal + nano $HOME/.termux/smsquitto-conf.yaml + + - Install services (start, stop, status) + - Install the Tremux widget:Widget on the phone's desktop by keeping your finger on the screen + - Choose shortcut + - Change the icon and the name if you want + - Start, Stop, or Status service by clicking the icon + + ### Uses + + - A MQTT client is required to operate the phone. + - The smsquittod.py module gives an example of a client written in python. + - Library is the Eclipse foundation paho-mqtt + - Simply implemente ClientMQTTBase class + + - The sms-client.html file is another example but in websocket mode. + - Directly modify the sms-client.html file + - Launch it directly in a browser + - by drag and drop + - by an address of this type + - file: ///path/to/file/sms-client.html + + - demonstration + + - sms-client.html is fully functional + - You just have to update the apikey in this file and in the configuration file on the smartphone + - Set up some mobile numbers to do the tests + + ### Domotiz + + - https://www.domoticz.com/wiki/Getting_started + + - Send notification from custom HTTP/Action + + - Notification + - script://notify.sh #FIELD1 #FIELD2 #FIELD3 #FIELD4 #TO #MESSAGE --tls or nothing + - command + - /usr/bin/python3 /home/pi/domoticz/scripts/notify.py --method=notify --host=$1 --port=$2 --user=$3 --password=$4 --apikey=$5 --text="$6" $7 + + - SMS + - script://notify.sh #FIELD1 #FIELD2 #FIELD3 #FIELD4 #TO #MESSAGE "00330645953706,0033791246318" --tls or nothing + - command + - /usr/bin/python3 /home/pi/domoticz/scripts/smsquitto.py --method=sms --host=$1 --port=$2 --user=$3 --password=$4 --apikey=$5 --text="$6" --phone="$7" $8 + + - SSL certification + - argument --tls, in $7 or $8. + - the certification key is in domoticz/scripts/smsquitto.py. + + - You must install python3 paho-mqtt library: pip3 install paho-mqtt + + ### API methods + + Message sent and received format: + + - (topic, payload) + - topic: is a string that starts with apikey see above + - apikey/pub/sms + - apikey/emit/inbox + - payload: is a Json string + - {mobile: [mobile1, mobile2, etc ...], subject: "the subject", msg: "the message"} + - {time: time, payload: payload} + - topics + - apikey/pub/sms send SMS + - apikey/pub/notify send notification + - apikey/pub/location request for Geographic positioning + - apikey/pub/inbox or all or sent or draft or outbox Read SMS message + - apikey/pub/kill-service stop the service + +Keywords: Termux,sms,Android,notification,mqtt +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Any public +Classifier: Topic :: Software Development :: Sms Tools +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.7 +Description-Content-Type: text/markdown diff --git a/smsquitto.egg-info/SOURCES.txt b/smsquitto.egg-info/SOURCES.txt new file mode 100644 index 0000000..e04c7d1 --- /dev/null +++ b/smsquitto.egg-info/SOURCES.txt @@ -0,0 +1,12 @@ +LICENSE +README.md +setup.cfg +setup.py +./smsquitto/__init__.py +./smsquitto/smsquitto-conf.yaml +./smsquitto/smsquittod.py +smsquitto.egg-info/PKG-INFO +smsquitto.egg-info/SOURCES.txt +smsquitto.egg-info/dependency_links.txt +smsquitto.egg-info/requires.txt +smsquitto.egg-info/top_level.txt \ No newline at end of file diff --git a/smsquitto.egg-info/dependency_links.txt b/smsquitto.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/smsquitto.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/smsquitto.egg-info/requires.txt b/smsquitto.egg-info/requires.txt new file mode 100644 index 0000000..ba1bc2d --- /dev/null +++ b/smsquitto.egg-info/requires.txt @@ -0,0 +1,4 @@ +paho-mqtt +rsa +PyYAML +supervisor diff --git a/smsquitto.egg-info/top_level.txt b/smsquitto.egg-info/top_level.txt new file mode 100644 index 0000000..f830c8e --- /dev/null +++ b/smsquitto.egg-info/top_level.txt @@ -0,0 +1 @@ +smsquitto diff --git a/smsquitto.toml b/smsquitto.toml new file mode 100644 index 0000000..07de284 --- /dev/null +++ b/smsquitto.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" \ No newline at end of file