Showing 11 changed files with 826 additions and 0 deletions
+3
address.csv
... ...
@@ -0,0 +1,3 @@
1
+20635F02010000DF,100009247,1
2
+20635F01C1000008,100009247,2
3
+20635F01E1000005,100001138,3
BIN
loraSMS.db
Binary file not shown.
+17
loraSMS.json
... ...
@@ -0,0 +1,17 @@
1
+{
2
+  "brokerName":"broker.preview.thingpark.com",
3
+  "brokerPort": 1883,
4
+  "username":"actility",
5
+  "password":"actility",
6
+  "topic":"lorasms/#",
7
+  "address_book":"address.csv",
8
+  "database": "loraSMS.db",
9
+  "store_all": false,
10
+  "server":
11
+  {
12
+    "name": "LoRaSMS Admin",
13
+    "port": 9000,
14
+    "address": "127.0.0.1",
15
+    "version": "0.1"
16
+  }
17
+}
+306
loraSMS.py
... ...
@@ -0,0 +1,306 @@
1
+#!/usr/bin/python3
2
+from datetime import datetime
3
+import time
4
+import argparse
5
+import json
6
+import paho.mqtt.client as mqtt
7
+import sqlite3
8
+import csv
9
+import html
10
+import threading
11
+from userio import * 
12
+import web
13
+
14
+conn = None
15
+c = None
16
+topic=""
17
+outfile=""
18
+address_book_filename=""
19
+myDBFilename="loraSMS.db"
20
+configuration = None
21
+
22
+THDEFERSTART=2
23
+THSLEEPLOOP=30
24
+
25
+def myDatabase_create():
26
+  global conn
27
+  global c
28
+  conn = sqlite3.connect(myDBFilename)
29
+  c = conn.cursor()
30
+  c.execute('''CREATE TABLE IF NOT EXISTS address
31
+             (DevEUI text,
32
+             SubID text,
33
+             DID int,
34
+             UNIQUE(DevEUI) )''')
35
+  c.execute('''CREATE TABLE IF NOT EXISTS sms
36
+             (epoch int,
37
+             DevEUIfrom text,
38
+             DevEUIto text,
39
+             urgent int,
40
+             content text,
41
+             date text,
42
+             UNIQUE(epoch) )''')
43
+             
44
+def myDatabase_fill_address():
45
+  global conn
46
+  global c
47
+  global configuration
48
+  with open(address_book_filename) as csv_file:
49
+    csv_reader = csv.reader(csv_file, delimiter=',')
50
+    line_count = 0
51
+    for row in csv_reader:
52
+      sqlquery="INSERT OR IGNORE INTO address VALUES ('"+row[0]+"','"+row[1]+"',"+str(row[2])+");"
53
+      c.execute(sqlquery)
54
+  conn.commit()
55
+
56
+def on_publish(client,userdata,result):
57
+  # Do nothing more
58
+  pass
59
+          
60
+def on_connect(client, userdata, flags, rc):
61
+  dateExec = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
62
+  with open(outfile,'a+') as f:
63
+    csvLine="CONNECT: "+dateExec+" : "+str(rc)
64
+    say(csvLine)
65
+    f.write(csvLine+"\n")
66
+  client.subscribe(topic)
67
+
68
+def on_message(client, userdata, msg):
69
+  global conn
70
+  global c
71
+  global configuration
72
+  json_data=str(msg.payload.decode('ASCII'))
73
+
74
+  dateExec = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
75
+  
76
+  with open(outfile,'a+') as f:
77
+    try:
78
+      parsed=json.loads(json_data)
79
+    except:
80
+      print(msg)
81
+      f.write(msg)
82
+      f.close()
83
+      return
84
+
85
+    # MQTT message type  
86
+    mqttType=-1
87
+    try:
88
+      parsed['DevEUI_downlink']
89
+      mqttType=1
90
+    except:
91
+      pass
92
+    try:
93
+      parsed['DevEUI_downlink_Sent']
94
+      mqttType=2
95
+    except:
96
+      pass
97
+    try:
98
+      parsed['DevEUI_uplink']
99
+      mqttType=3
100
+    except:
101
+      pass
102
+    try:
103
+      parsed['deviceEUI']
104
+      mqttType=4
105
+    except:
106
+      pass
107
+    try:
108
+      parsed['DevEUI_notification']
109
+      mqttType=5
110
+    except:
111
+      pass
112
+    try:
113
+      parsed['DevEUI_downlink_Rejected']
114
+      mqttType=6
115
+    except:
116
+      pass
117
+    try:
118
+      parsed['DevEUI_location']
119
+      mqttType=7
120
+    except:
121
+      pass
122
+
123
+    csvLine=""
124
+    if mqttType == 3:
125
+      # Is a pure uplink
126
+      try:
127
+        parsed['DevEUI_uplink']['payload']['messageType']
128
+      except:
129
+        try:
130
+          parsed['DevEUI_uplink']['rawJoinRequest']
131
+          csvLine+="JOIN   : "+msg.topic+" : "+parsed['DevEUI_uplink']['Time']+" : "+parsed['DevEUI_uplink']['DevEUI']
132
+        except:
133
+          csvLine+="EXCEPT : "+msg.topic+" : "+parsed['DevEUI_uplink']['Time']+" : "+parsed['DevEUI_uplink']['DevEUI']
134
+        print(json_data)
135
+        return
136
+        
137
+      if parsed['DevEUI_uplink']['payload']['messageType'] == "SMS":
138
+        # Is SMS
139
+        destinationID=parsed['DevEUI_uplink']['payload']['destinationID']
140
+        messageAscii=parsed['DevEUI_uplink']['payload']['messageAscii']
141
+        messageHex=parsed['DevEUI_uplink']['payload']['messageHex']
142
+        CustomerID=parsed['DevEUI_uplink']['CustomerID']
143
+        payloadHex=parsed['DevEUI_uplink']['payload_hex']
144
+        DateSec=parsed['DevEUI_uplink']['Time'].split(".")[0]
145
+        DateEpoch=int(time.mktime(datetime.strptime(DateSec, "%Y-%m-%dT%H:%M:%S").timetuple()))
146
+        
147
+        # Protection
148
+        # ~ messageAscii=messageAscii.replace("'","‘")
149
+        messageAscii=html.escape(messageAscii,quote=True)
150
+        
151
+        sqlquery="SELECT DevEUI from address where did="+str(destinationID)+";"
152
+        c.execute(sqlquery)
153
+        rows = c.fetchall()
154
+        try:
155
+          DevEUIDestination=rows[0][0]
156
+        except:
157
+          DevEUIDestination="FFFFFFFFFFFFFFFF"
158
+          
159
+        sqlquery="SELECT did from address where DevEUI='"+parsed['DevEUI_uplink']['DevEUI']+"';"
160
+        c.execute(sqlquery)
161
+        rows = c.fetchall()
162
+        try:
163
+          sourceID=rows[0][0]
164
+        except:
165
+          sourceID="-1"
166
+          
167
+        csvLine+="----------------------------------\n"
168
+        csvLine+="SMS    : "+msg.topic+" : "+parsed['DevEUI_uplink']['Time']+" : "+parsed['DevEUI_uplink']['DevEUI']+"\n"
169
+        csvLine+="SubID  : "+CustomerID+" : to: "+str(destinationID)+" : "+DevEUIDestination+"\n"
170
+        csvLine+="Content: "+messageHex+"\n"
171
+        csvLine+="Content: "+messageAscii+"\n"
172
+        csvLine+="----------------------------------"
173
+        sqlquery="INSERT OR IGNORE INTO sms VALUES ("+str(DateEpoch)+",'"+parsed['DevEUI_uplink']['DevEUI']+"','"+DevEUIDestination+"',0,'"+messageAscii+"','"+DateSec+"');"
174
+        #debug(sqlquery)
175
+        c.execute(sqlquery)
176
+        conn.commit()
177
+        
178
+        print(csvLine)
179
+        f.write(csvLine+" : "+json_data+"\n")
180
+        if not DevEUIDestination == "FFFFFFFFFFFFFFFF":
181
+          #Sending Downlink
182
+          pubTopic=configuration['topic'].replace("#","")
183
+          pubTopic+="things/"
184
+          pubTopic+=DevEUIDestination
185
+          pubTopic+="/downlink"
186
+          pubMessage={'DevEUI_downlink': {
187
+                'DevEUI': DevEUIDestination,
188
+                'FPort': 2,
189
+                'Confirmed': 0,
190
+                'FlushDownlinkQueue': 0,
191
+                'payload_hex': payloadHex
192
+              }
193
+            }
194
+          jsonPubMessage=json.dumps(pubMessage)
195
+          ret= client.publish(pubTopic,jsonPubMessage)      
196
+          
197
+      else:
198
+        # Is Other Uplink message
199
+        csvLine+="UL     : "+msg.topic+" : "+parsed['DevEUI_uplink']['Time']+" : "+parsed['DevEUI_uplink']['DevEUI']+" : "+str(parsed['DevEUI_uplink']['FCntUp'])+" : "+parsed['DevEUI_uplink']['payload']['messageType']
200
+        say(csvLine)
201
+    elif mqttType == 1:
202
+      # Is pure downlink
203
+      csvLine+="DL     : "+msg.topic+" : "+parsed['DevEUI_downlink']['DevEUI']+" Queued: "+parsed['DevEUI_downlink']['payload_hex']
204
+      say(csvLine)
205
+    elif mqttType == 2:
206
+      # Is pure downlink sent indicator
207
+      csvLine+="DLSI   : "+msg.topic+" : "+parsed['DevEUI_downlink_Sent']['Time']+" : "+parsed['DevEUI_downlink_Sent']['DevEUI']
208
+      say(csvLine)
209
+    elif mqttType == 5:
210
+      # Is pure notification
211
+      csvLine+="NOTIF  : "+msg.topic+" : "+parsed['DevEUI_notification']['Time']+" : "+parsed['DevEUI_notification']['DevEUI']+" : "+parsed['DevEUI_notification']['Type']
212
+      say(csvLine)
213
+    elif mqttType == 6:
214
+      # Is pure notification
215
+      csvLine+="REJECT : "+msg.topic+" : "+parsed['DevEUI_downlink_Rejected']['Time']+" : "+parsed['DevEUI_downlink_Rejected']['DevEUI']+" : "+parsed['DevEUI_downlink_Rejected']['DownlinkRejectionCause']
216
+      say(csvLine)
217
+    elif mqttType == 7:
218
+      # Is pure notification
219
+      csvLine+="NETLOC : "+msg.topic+" : "+parsed['DevEUI_location']['Time']+" : "+parsed['DevEUI_location']['DevEUI']
220
+      say(csvLine)
221
+    elif mqttType == 4:
222
+      # TPX LE output
223
+      messageType=parsed['resolvedTracker']['messageType']
224
+      try:
225
+        uplinkMode=parsed['uplinkPayload']['deviceConfiguration']['mode']
226
+      except:
227
+        uplinkMode="UNKNOWN"
228
+      endOfLine=""
229
+      if "POSITION_MESSAGE" == messageType:
230
+        try:
231
+          endOfLine=" : "+uplinkMode
232
+        except:
233
+          print(json_data)
234
+      FCnt=parsed['processedFeed']['sequenceNumber']
235
+      csvLine+="TPXLE: "+msg.topic+" : "+parsed['time']+" : "+parsed['deviceEUI'].upper()+" : #"+str(FCnt)+" : "+messageType+endOfLine
236
+      #csvLine+=" #"+str(FCnt)
237
+      print(csvLine)
238
+    else:
239
+      csvLine="RAW:      "+" : "+msg.topic+" : "+dateExec
240
+      print(csvLine)
241
+      print(parsed)
242
+
243
+    if configuration['store_all']:
244
+      f.write(csvLine+" : "+json_data+"\n")
245
+
246
+
247
+def configDump(configuration):
248
+  say("MQTT broker: "+configuration['brokerName']+":"+str(configuration['brokerPort']))
249
+  say("MQTT topic : "+configuration['topic'])
250
+  say("MQTT log   : "+outfile)
251
+
252
+
253
+def thCreateWeb():
254
+  thName="WEB"
255
+  thSleep=THSLEEPLOOP
256
+  say("Thread "+thName+" Starting")
257
+  time.sleep(THDEFERSTART) 
258
+  while True:
259
+    web.web.start(configuration)
260
+    time.sleep(thSleep) 
261
+    
262
+if __name__ == "__main__":
263
+  #
264
+  parser = argparse.ArgumentParser()
265
+  parser.add_argument("-c", "--config", help="config file")
266
+  args = parser.parse_args()
267
+  
268
+  jsonFile=args.config
269
+  fJson = open(jsonFile)
270
+  configuration = json.loads(fJson.read())
271
+  broker_address=configuration['brokerName']
272
+  port=configuration['brokerPort']
273
+  user=configuration['username']
274
+  password=configuration['password']
275
+  topic=configuration['topic']
276
+  address_book_filename=configuration['address_book']
277
+  outfile=configuration['brokerName']+"-"+topic
278
+  outfile=outfile.replace("/","_").replace("#","_").replace("__","")+".log"
279
+  
280
+  try:
281
+    thWeb = threading.Thread(target=thCreateWeb)
282
+    thWeb.start()
283
+  except KeyboardInterrupt:
284
+    thWeb.join()  
285
+  
286
+  myDatabase_create()
287
+  myDatabase_fill_address()
288
+    
289
+  client = mqtt.Client()
290
+  client.on_publish = on_publish
291
+  client.on_connect = on_connect
292
+  client.on_message = on_message
293
+
294
+  try:
295
+    client.username_pw_set(user, password=password)
296
+  except:
297
+    error("ERROR: MQTT Set failed")
298
+    quit()
299
+
300
+  try:
301
+    client.connect(broker_address, port, 60)
302
+  except:
303
+    error("ERROR: MQTT connect failed")
304
+    quit()
305
+  
306
+  client.loop_forever()
+244
pageBegin.skel
... ...
@@ -0,0 +1,244 @@
1
+
2
+<!doctype html>
3
+<html lang="en-us">
4
+  <head>
5
+    <meta charset="utf-8">
6
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
7
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+    <link rel="stylesheet" href="css/font-awesome/css/all.css" crossorigin="anonymous">
9
+    <link rel="stylesheet" type="text/css" href="css/style.css" />
10
+    <link rel="alternate stylesheet" type="text/css" id="style-dynamic" href="" />
11
+    <link rel="shortcut icon" href="favicon.png">
12
+    <link rel="icon" type="image/png" href="favicon.png" sizes="16x16">
13
+    <link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32">
14
+    <script>
15
+    function changeCSS(cssFile, cssLinkIndex) {
16
+      var oldlink = document.getElementsByTagName("link").item(cssLinkIndex);
17
+      var newlink = document.createElement("link");
18
+      newlink.setAttribute("rel", "stylesheet");
19
+      newlink.setAttribute("type", "text/css");
20
+      newlink.setAttribute("href", cssFile);
21
+      document.getElementsByTagName("head").item(0).replaceChild(newlink, oldlink);
22
+    }
23
+    function getStylesheet() {
24
+      var currentTime = new Date().getHours();
25
+      if (0 <= currentTime && currentTime < 7) {
26
+        changeCSS('css/style-night.css',3);
27
+      } else if (7 <= currentTime && currentTime < 19) {
28
+        changeCSS('css/style-day.css',3);
29
+      } else if (19 <= currentTime && currentTime < 24) {
30
+        changeCSS('css/style-night.css',3);
31
+      }
32
+    }
33
+    getStylesheet();
34
+
35
+    function hideMenu() {
36
+      var x = document.getElementById("menu-news");
37
+      var btn = document.getElementById("menu-btn");
38
+      if (x.style.display === "none") {
39
+        x.style.display = "block";
40
+        btn.innerHTML = '<i class="fa fa-times fa-fw"></i>';
41
+        //document.getElementById('article-display').setAttribute("class", "col-6");
42
+      } else {
43
+        x.style.display = "none";
44
+        btn.innerHTML = '<i class="fa fa-bars fa-fw"></i>';
45
+        //document.getElementById('article-display').setAttribute("class", "col-8");
46
+      }
47
+    }
48
+    function forceStylesheet() {
49
+      var x = document.getElementById("style-active");
50
+      var btn = document.getElementById("style-btn");
51
+
52
+      if (x.className === "night") {
53
+        x.className = "day";
54
+        btn.innerHTML = '<i class="fa fa-sun-o fa-fw"></i>';
55
+        changeCSS('css/style-day.css',3);
56
+      } else {
57
+        x.className = "night";
58
+        btn.innerHTML = '<i class="fa fa-moon-o fa-fw"></i>';
59
+        changeCSS('css/style-night.css',3);
60
+      }
61
+    }
62
+
63
+    window.onload = function () {
64
+      // Set Day/Night icon after load
65
+      var x = document.getElementById("style-active");
66
+      var btn = document.getElementById("style-btn");
67
+      var currentTime = new Date().getHours();
68
+      if (0 <= currentTime && currentTime < 7) {
69
+        x.className = "night";
70
+        btn.innerHTML = '<i class="fa fa-moon-o fa-fw"></i>';
71
+        changeCSS('css/style-night.css',3);
72
+      } else if (7 <= currentTime && currentTime < 19) {
73
+        x.className = "day";
74
+        btn.innerHTML = '<i class="fa fa-sun-o fa-fw"></i>';
75
+        changeCSS('css/style-day.css',3);
76
+      } else if (19 <= currentTime && currentTime < 24) {
77
+        x.className = "night";
78
+        btn.innerHTML = '<i class="fa fa-moon-o fa-fw"></i>';
79
+        changeCSS('css/style-night.css',3);
80
+      }
81
+      var w = window;
82
+      var d = document;
83
+      var e = d.documentElement;
84
+      var g = d.getElementsByTagName('body')[0];
85
+      screenX = w.innerWidth || e.clientWidth || g.clientWidth;
86
+      if(screenX > 767) {
87
+        var sectionMenu = document.getElementById("menu-news");
88
+        sectionMenu.style.display = "none";
89
+      }
90
+      hideMenu();
91
+    }
92
+    </script>
93
+
94
+    <title>CSTAPPNAME</title>
95
+<style>
96
+* {
97
+    box-sizing: border-box;
98
+}
99
+.row::after {
100
+  content: "";
101
+  clear: both;
102
+  display: table;
103
+}
104
+[class*="col-"] {
105
+  float: left;
106
+  padding: 15px;
107
+}
108
+html {
109
+  font-family: "Lucida Sans", sans-serif;
110
+}
111
+.header {
112
+  background-color: #5b7e96;
113
+  color: #ffffff;
114
+  padding: 2px;
115
+}
116
+
117
+tr:nth-child(even) {
118
+  background: #dcdcdc;
119
+}
120
+tr:hover {
121
+  background: #8cf;
122
+}
123
+td {
124
+}
125
+td.center {
126
+}
127
+tbody {
128
+  background-color: #e4f0f5;
129
+}
130
+thead {
131
+  background-color: #3f87a6;
132
+  color: #fff;
133
+  font-weight: bold;
134
+}
135
+
136
+
137
+/* For mobile phones: */
138
+[class*="col-"] {
139
+    width: 100%;
140
+
141
+  }
142
+@media only screen and (min-width: 500px) and (max-width: 699px) {
143
+  /* For phone */
144
+  #menu-news {
145
+    display: none;
146
+  }
147
+  #pdf-btn { display: none; }
148
+
149
+}
150
+@media only screen and (min-width: 700px) {
151
+    /* For tablets: */
152
+    .col-s-1 {width: 8.33%;}
153
+    .col-s-2 {width: 16.66%;}
154
+    .col-s-3 {width: 25%;}
155
+    .col-s-4 {width: 33.33%;}
156
+    .col-s-5 {width: 41.66%;}
157
+    .col-s-6 {width: 50%;}
158
+    .col-s-7 {width: 58.33%;}
159
+    .col-s-8 {width: 66.66%;}
160
+    .col-s-9 {width: 75%;}
161
+    .col-s-10 {width: 83.33%;}
162
+    .col-s-11 {width: 91.66%;}
163
+    .col-s-12 {width: 100%;}
164
+}
165
+@media only screen and (min-width: 768px) {
166
+    /* For desktop: */
167
+    .col-1 {width: 8.33%;}
168
+    .col-2 {width: 16.66%;}
169
+    .col-3 {width: 25%;}
170
+    .col-4 {width: 33.33%;}
171
+    .col-5 {width: 41.66%;}
172
+    .col-6 {width: 50%;}
173
+    .col-7 {width: 58.33%;}
174
+    .col-8 {width: 66.66%;}
175
+    .col-9 {width: 75%;}
176
+    .col-10 {width: 83.33%;}
177
+    .col-11 {width: 91.66%;}
178
+    .col-12 {width: 100%;}
179
+}
180
+
181
+.topnav {
182
+  overflow: hidden;
183
+  background-color: #5b7e96;
184
+}
185
+
186
+
187
+.title {
188
+  width: 200px;
189
+  /*white-space: nowrap;
190
+  overflow: hidden;*/
191
+  float: left;
192
+  /*text-overflow: ellipsis;*/
193
+  font-size: 1.2em;
194
+  color:#fff;
195
+  padding: 4px 4px;
196
+  position: relative;
197
+}
198
+.title:hover {
199
+}
200
+.title-tooltip {
201
+  display: none;
202
+}
203
+.title:hover .title-tooltip {
204
+  display: block;
205
+  font-size: 1.0em;
206
+  overflow: visible;
207
+  white-space: wrap;
208
+}
209
+
210
+.topnav a {
211
+  float: left;
212
+  display: block;
213
+  color: #fff;
214
+  text-align: center;
215
+  padding: 4px 4px;
216
+  text-decoration: none;
217
+  font-size: 17px;
218
+}
219
+
220
+.topnav a:hover {
221
+  background-color: #888;
222
+  color: black;
223
+}
224
+
225
+.topnav a.active {
226
+  background-color: #eee;
227
+  color: black;
228
+}
229
+
230
+.topnav .icon {
231
+  display: none;
232
+}
233
+
234
+h1 { font-size:20px; font-weight:bold; }
235
+h2 { font-size:16px; font-weight:bold; }
236
+h3 { font-size:14px; font-weight:bold; }
237
+/*a:link { color: #5b7e96; }
238
+a:visited { color: #5b7e96; }
239
+a:hover { color: #888; }
240
+*/
241
+</style>
242
+</head>
243
+<body>
244
+
+3
pageEnd.skel
... ...
@@ -0,0 +1,3 @@
1
+  </body>
2
+</html>
3
+
+60
userio.py
... ...
@@ -0,0 +1,60 @@
1
+#
2
+# Copyright (C) Actility, SA. All Rights Reserved.
3
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
4
+#
5
+# This program is free software; you can redistribute it and/or
6
+# modify it under the terms of the GNU General Public License version
7
+# 2 only, as published by the Free Software Foundation.
8
+#
9
+# This program is distributed in the hope that it will be useful, but
10
+# WITHOUT ANY WARRANTY; without even the implied warranty of
11
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+# General Public License version 2 for more details (a copy is
13
+# included at /legal/license.txt).
14
+#
15
+# You should have received a copy of the GNU General Public License
16
+# version 2 along with this work; if not, write to the Free Software
17
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18
+# 02110-1301 USA
19
+#
20
+# Please contact Actility, SA.,  4, rue Ampere 22300 LANNION FRANCE
21
+# or visit www.actility.com if you need additional
22
+# information or have any questions.
23
+#
24
+from colorama import Fore, Back, Style
25
+from time import gmtime, strftime
26
+
27
+name = "LoRaSMS"
28
+
29
+def say(message):
30
+  date_string = strftime("%Y-%m-%d %H:%M:%S %z", gmtime())
31
+  prefix = Fore.CYAN + name + " " + Fore.RESET + date_string + " "
32
+  print(prefix + Style.DIM + message + Style.RESET_ALL)
33
+
34
+
35
+def ok(message, detail=""):
36
+  date_string = strftime("%Y-%m-%d %H:%M:%S %z", gmtime())
37
+  level = Fore.GREEN + "[OK] " + Fore.RESET
38
+  prefix = Fore.CYAN + name + " " + Fore.RESET + date_string + " "
39
+  print(prefix + level + Style.BRIGHT + message + Style.RESET_ALL + " " + detail + Style.RESET_ALL)
40
+
41
+
42
+def warn(message, detail=""):
43
+  date_string = strftime("%Y-%m-%d %H:%M:%S %z", gmtime())
44
+  prefix = Fore.CYAN + name + " " + Fore.RESET + date_string + " "
45
+  level = Fore.YELLOW + "[WARN] " + Fore.RESET
46
+  print(prefix  + level +  Style.BRIGHT + message + Style.RESET_ALL + " " + detail + Style.RESET_ALL)
47
+
48
+
49
+def error(message, detail=""):
50
+  date_string = strftime("%Y-%m-%d %H:%M:%S %z", gmtime())
51
+  prefix = Fore.CYAN + name + " " + Fore.RESET + date_string + " "
52
+  level = Fore.RED + "[ERR] " + Fore.RESET
53
+  print(prefix + level +  Style.BRIGHT + message + Style.RESET_ALL + " " + detail + Style.RESET_ALL)
54
+
55
+
56
+def debug(message, detail=""):
57
+  date_string = strftime("%Y-%m-%d %H:%M:%S %z", gmtime())
58
+  prefix = Fore.CYAN + name + " " + Fore.RESET + date_string + " "
59
+  level = Fore.MAGENTA + "[DEBUG] " + Fore.RESET
60
+  print(prefix + level +  Style.DIM + message + Style.RESET_ALL + " " + detail + Style.RESET_ALL)
+8
web/__init__.py
... ...
@@ -0,0 +1,8 @@
1
+#!/usr/bin/env python3
2
+# encoding: UTF-8
3
+__author__ = 'Yanik Cawidrone'
4
+__version__ = '0.1'
5
+
6
+from userio import *
7
+
8
+from . import web
BIN
web/__pycache__/__init__.cpython-39.pyc
Binary file not shown.
BIN
web/__pycache__/web.cpython-39.pyc
Binary file not shown.
+185
web/web.py
... ...
@@ -0,0 +1,185 @@
1
+#!/usr/bin/env python3
2
+from http.server import BaseHTTPRequestHandler, HTTPServer
3
+import datetime
4
+from time import perf_counter 
5
+import os
6
+import socket
7
+import requests
8
+from userio import * 
9
+import time
10
+import sqlite3
11
+import urllib.parse
12
+
13
+server = None
14
+db_name = None
15
+webServer = None
16
+server_name="ADMIN  : "
17
+connectionResults = None
18
+data_begin = None
19
+data_end = None
20
+
21
+conn = None
22
+c = None
23
+
24
+class MyServer(BaseHTTPRequestHandler):
25
+  def log_message(self, format, *args):
26
+    # To silence the default output of server (too verbose)
27
+    return	
28
+  def do_GET(self):
29
+    rootdir = '.'
30
+    if not os.path.exists(rootdir + self.path) or self.path == '/index.html' or self.path == '/':
31
+      sourceIP=""
32
+      if self.headers['X-Real-IP'] is None:
33
+        sourceIP="From: "+self.client_address[0]+" GET"
34
+      else:
35
+        sourceIP="From: "+self.headers['X-Real-IP']+" GET"
36
+      page=[]
37
+
38
+      data_page = "";
39
+      data_page += "<div class=\"topnav\" id=\"myTopnav\">\n"
40
+      data_page += "<span class=\"title\">"+server['name']+"</span>\n"
41
+      data_page += "<a href=\"?action=message\">Message</a>\n"
42
+      data_page += "<a href=\"?action=address\">Addresses</a>\n"
43
+      data_page += "</div>\n"
44
+      
45
+      s = self.path
46
+      urlArgs = urllib.parse.parse_qs(s[2:])
47
+      try:
48
+        action = None
49
+        if "action" in urlArgs:
50
+          action = urlArgs['action'][0]
51
+        #data_page += "<br>["+action+"]<br>"  
52
+      except:
53
+        return
54
+      
55
+      if action == "message":
56
+        sqlquery='select * from sms order by epoch desc limit 10;'
57
+        c.execute(sqlquery)
58
+        rows = c.fetchall()
59
+        data_page+="<table>\n"
60
+        data_page+="<thead>\n"
61
+        data_page+="  <th>Date</th>\n"
62
+        data_page+="  <th>From</th>\n"
63
+        data_page+="  <th>To</th>\n"
64
+        data_page+="  <th>Content</th>\n"    
65
+        data_page+="</thead>\n"
66
+        data_page+="<tbody>\n"
67
+        for row in rows:
68
+          data_page+="<tr><td>"+row[5]+"</td><td>"+row[1]+"</td><td>"+row[2]+"</td><td>"+row[4]+"</td></tr>\n"
69
+        data_page+="</tbody>\n"
70
+        data_page+="<table>\n"
71
+      elif action == "address":
72
+        sqlquery='select * from address;'
73
+        c.execute(sqlquery)
74
+        rows = c.fetchall()
75
+        data_page+="<table>\n"
76
+        data_page+="<thead>\n"
77
+        data_page+="  <th>DevEUI</th>\n"
78
+        data_page+="  <th>SubscriberID</th>\n"
79
+        data_page+="  <th>SMSid</th>\n"
80
+        data_page+="</thead>\n"
81
+        data_page+="<tbody>\n"
82
+        for row in rows:
83
+          data_page+="<tr><td>"+row[0]+"</td><td>"+row[1]+"</td><td>"+str(row[2])+"</td></tr>\n"
84
+        data_page+="</tbody>\n"
85
+        data_page+="<table>\n"
86
+      else:
87
+        data_page+="<p>Pick one action in the bar above</p>\n"
88
+      
89
+      page.append(data_begin)
90
+      page.append(data_page)
91
+      page.append(data_end)
92
+      content = ''.join(page)
93
+      self.send_response(200)
94
+      self.send_header("Content-type", "text/html")
95
+      self.send_header('Server',server['name']+" v"+server['version'])
96
+      self.end_headers()
97
+      self.wfile.write(content.encode('utf-8'))
98
+      return
99
+    elif not os.path.exists(rootdir + self.path):
100
+      self.send_header('Server',server['name']+" v"+server['version'])
101
+      self.send_error(404, 'file not found')
102
+    else:
103
+      try:
104
+        f = open(rootdir + self.path,'rb') #open requested file
105
+        self.send_response(200)
106
+        if self.path.endswith('.css'):
107
+            self.send_header('Content-type','text/css')
108
+        elif self.path.endswith('.bmp'):
109
+            self.send_header('Content-type','image/x-ms-bmp')
110
+        elif self.path.endswith('.ico'):
111
+            self.send_header('Content-type','image/x-icon')
112
+        elif self.path.endswith('.png'):
113
+            self.send_header('Content-type','image/png')
114
+        elif self.path.endswith('.jpg'):
115
+            self.send_header('Content-type','image/jpeg')
116
+        else:
117
+            self.send_header('Content-type','text/html')
118
+        self.send_header('Server',server['name']+" v"+server['version'])
119
+        self.end_headers()
120
+        self.wfile.write(f.read())
121
+        f.close()
122
+        return
123
+      except IOError:
124
+        self.send_header('Server',server['name']+" v"+server['version'])
125
+        self.send_error(404, 'file not found')
126
+
127
+
128
+def start(server_config):
129
+  #
130
+  global server
131
+  global data_begin
132
+  global data_end
133
+  global conn
134
+  global c
135
+  
136
+  server = server_config['server']
137
+  db_name = server_config['database']
138
+  
139
+  conn = sqlite3.connect(db_name)
140
+  c = conn.cursor()
141
+  
142
+  data_begin = ""
143
+  with open('pageBegin.skel','r') as fStart:
144
+    data_begin += fStart.read().replace( 'CSTAPPNAME', server['name'] )
145
+  data_end = ""
146
+  with open('pageEnd.skel','r') as fStart:
147
+    data_end += fStart.read().replace( 'CSTAPPNAME', server['name'] )
148
+  
149
+  say(server_name+server['name']+" v"+server['version'])
150
+  webServer = HTTPServer((server['address'], server['port']), MyServer)
151
+  say(server['address']+":"+str(server['port']))
152
+  webServer.serve_forever()
153
+  
154
+if __name__ == "__main__":        
155
+  #global server
156
+  server = configuration.get_server()
157
+  say("----------------------")
158
+  say(server['name']+" v"+server['version'])
159
+  webServer = HTTPServer((server['address'], server['port']), MyServer)
160
+  say("Server started http://%s:%s" % (server['address'], server['port']))
161
+  
162
+  getMenuItems()
163
+  try:
164
+    thWidget = threading.Thread(target=thCreateWidget)
165
+    thWidget.start()
166
+    thMqtt = threading.Thread(target=thMqttProbe)
167
+    thMqtt.start()
168
+    thMetar = threading.Thread(target=thCreateMetar)
169
+    thMetar.start()
170
+    thLightning = threading.Thread(target=thCreateLightning)
171
+    thLightning.start()
172
+    thIp = threading.Thread(target=thCreateIp)
173
+    thIp.start()
174
+    webServer.serve_forever()
175
+  except KeyboardInterrupt:
176
+    thWidget.join()
177
+    thMqtt.join()
178
+    thMetar.join()
179
+    thLightning.join()
180
+    thLightning.join()
181
+    thIp.join()
182
+    pass
183
+  
184
+  webServer.server_close()
185
+  print("Server stopped.")