├── README.md ├── aprs_js8call.desktop └── aprs_msgJS8Call.py /README.md: -------------------------------------------------------------------------------- 1 | # js8call_aprsmessaging_interface 2 | 3 | A simple User Interface to send Email and SMS messages into the APRS network using JS8Call 4 | 5 | With thanks to Jordan, KN4CRD for JS8Call - http://js8call.com 6 | 7 | Uses unix os to send the UDP message to JS8Call, adaper from a script by Jason, KM4ACK. 8 | 9 | Written for Raspberry Pi Debian Buster, but sould work on other unix builds. 10 | I'll make a version for windows when I casn figure out the equivilent windows command to send the message 11 | Requires Python3 but may work on older versions 12 | Install pre-requisites before running: 13 | 14 | pip3 install psutil 15 | 16 | Ive added a desktop shortcut file in response top some questions. 17 | Download the file to your desktop, edit the file to the correct path to where you downloaded the aprs_msgJS8Call.py app to. Double click on the .desktop file and click on execute or execute in terminal. 18 | 19 | Enjoy! 20 | 21 | 73 22 | Mark 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /aprs_js8call.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=APRS Messages for JS8Call 3 | Comment=Send APRS Messages using JS8Call By Mark M0IAX 4 | Icon=/usr/share/pixmaps/python3.xpm 5 | Exec=/home/pi/aprs_messaging_interface_js8call/aprs_msgJS8Call.py 6 | Type=Application 7 | Encoding=UTF-8 8 | Terminal=false 9 | Categories=Hamradio; -------------------------------------------------------------------------------- /aprs_msgJS8Call.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | ''' 3 | Created on 23 September 2019 4 | APRS Messageing Using JS8Call Copyright 2019 M0IAX 5 | 6 | With thanks to Jordan, KN4CRD for JS8Call - http://js8call.com 7 | 8 | @author: Mark Bumstead M0IAX 9 | http://m0iax.com 10 | 11 | this is the lite version and uses a unix command to send the message to JS8Call, 12 | this was adapted from a script by Jason, KM4ACK. 13 | 14 | I may consider writing a more complicated version to use more of the JS8Call API 15 | ''' 16 | 17 | from tkinter import * 18 | from tkinter import messagebox 19 | 20 | from tkinter.ttk import * 21 | 22 | from tkinter.scrolledtext import ScrolledText 23 | from subprocess import call 24 | import os 25 | import psutil 26 | 27 | TYPE_TX_SEND='TX.SEND_MESSAGE' 28 | TYPE_TX_SETMESSAGE='TX.SET_TEXT' 29 | MSG_ERROR='ERROR' 30 | MSG_INFO='INFO' 31 | MSG_WARN='WARN' 32 | 33 | 34 | class UserInterface: 35 | mycall="M0IAX" 36 | first=True 37 | addr = ('127.0.0.1',65500) 38 | getResponse=False 39 | laststatusString="" 40 | seq=1 41 | def showMessage(self, messagetype, messageString): 42 | if messagetype==MSG_ERROR: 43 | messagebox.showerror("Error", messageString) 44 | elif messagetype==MSG_WARN: 45 | messagebox.showwarning("Warning",messageString) 46 | elif messagetype==MSG_INFO: 47 | messagebox.showinfo("Information",messageString) 48 | 49 | def sendMessageToJS8Call(self, messageType, messageString): 50 | if self.checkJS8CallRunning()==False: 51 | self.showMessage(MSG_ERROR, "JS8Call is not runnung. Please run it before clicking the button,") 52 | return False 53 | 54 | if messageString==None: 55 | return False 56 | 57 | # print(messageType+" "+messageString) 58 | 59 | cmdstring = "echo '{\"params\": {}, \"type\": \""+messageType+"\", \"value\":\""+messageString+"\"'} | nc -l -u -w 10 2237" 60 | #print(cmdstring) 61 | os.system(cmdstring) 62 | 63 | return True 64 | 65 | def createMessageString(self): 66 | messageString="" 67 | mode="" 68 | if self.combo.get()=="Email": 69 | mode="EMAIL-2" 70 | elif self.combo.get()=="SMS": 71 | mode = "SMSGTE" 72 | elif self.combo.get()=="APRS": 73 | mode=self.combo.get() 74 | 75 | mode = mode.ljust(9) 76 | # print(mode) 77 | if self.tocall.get()=="": 78 | return "Error, no email address is set" 79 | 80 | text=self.st.get('1.0', 'end-1c') # Get all text in widget. 81 | 82 | if text=="": 83 | return "Error, message is empty, please enter a message to send" 84 | 85 | number = self.seq 86 | number = format(number, '02d') 87 | # t.rjust(10, '0') 88 | if self.combo.get()=="Email": 89 | message = "@APRSIS CMD :"+mode+":"+self.tocall.get()+" "+text+"{"+number+"}" 90 | elif self.combo.get()=="APRS": 91 | tocallsign=self.tocall.get() 92 | tocallsign=tocallsign.ljust(9) 93 | message = "@APRSIS CMD :"+tocallsign+":"+text+"{"+number+"}" 94 | else: 95 | message = "@APRSIS CMD :"+mode+":@"+self.tocall.get()+" "+text+"{"+number+"}" 96 | 97 | self.seq=self.seq+1 98 | #APRS sequence number is 2 char, so reset if >99 99 | if self.seq>99: 100 | self.seq=1 101 | 102 | 103 | messageString = message #mode+" "+self.tocall.get()+" "+text 104 | return messageString 105 | 106 | def checkJS8CallRunning(self): 107 | 108 | retval = False 109 | #js8callText = "JS8Call Is not running." 110 | if "js8call" in (p.name() for p in psutil.process_iter()): 111 | retval = True 112 | #print("JS8Call is RUNNING") 113 | 114 | #print ("retval is "+str(retval)) 115 | return retval 116 | 117 | def setMessage(self): 118 | messageType=TYPE_TX_SETMESSAGE 119 | 120 | messageString=self.createMessageString() 121 | 122 | if messageString.startswith("Error"): 123 | self.showMessage(MSG_ERROR, messageString) 124 | return 125 | 126 | success = self.sendMessageToJS8Call(messageType, messageString) 127 | 128 | if success==True: 129 | self.showMessage(MSG_INFO, "Message text set in JS8Call, please use JS8Call to send the message.") 130 | 131 | def txMessage(self): 132 | 133 | messageType=TYPE_TX_SEND 134 | messageString=self.createMessageString() 135 | 136 | if messageString.startswith("Error"): 137 | # print(messageString) 138 | return 139 | 140 | success = self.sendMessageToJS8Call(messageType, messageString) 141 | if success==True: 142 | self.showMessage(MSG_INFO,"JS8Call will now transmit the message,") 143 | def comboChange(self, event): 144 | # print(self.combo.get()) 145 | mode = self.combo.get() 146 | if mode=="APRS": 147 | self.callLbl.config(text='Enter Callsign (including SSID)') 148 | elif mode=="Email": 149 | self.callLbl.config(text='Enter Email Address to send to') 150 | elif mode=="SMS": 151 | self.callLbl.config(text='Enter cell phone number') 152 | 153 | def __init__(self): 154 | 155 | self.window = Tk() 156 | 157 | self.window.title("APRS Messaging for JS8Call") 158 | 159 | self.window.geometry('350x200+300+300') 160 | 161 | self.combo = Combobox(self.window, state='readonly') 162 | 163 | self.combo.bind('<>', self.comboChange) 164 | 165 | self.combo['values']= ("Email", "SMS", "APRS") 166 | 167 | self.combo.current(0) #set the selected item 168 | 169 | self.combo.grid(column=0, row=0,columnspan=2) 170 | 171 | self.lbl1 = Label(self.window, text="JS8Call Mode", justify="left") 172 | 173 | self.lbl1.grid(column=0, row=1,columnspan=2) 174 | 175 | self.combo2 = Combobox(self.window, state='readonly') 176 | 177 | self.combo2['values']= ("Normal") 178 | 179 | self.combo2.current(0) #set the selected item 180 | 181 | self.combo2.grid(column=0, row=2,columnspan=2) 182 | 183 | 184 | self.callLbl = Label(self.window, text="Enter Email Address", justify="left") 185 | 186 | self.callLbl.grid(column=0, row=3,columnspan=2) 187 | 188 | self.tocall = Entry(self.window,width=30) 189 | 190 | self.tocall.grid(column=0, row=4, columnspan=2) 191 | 192 | self.msgLabel = Label(self.window, text="Message Text", justify="left") 193 | 194 | self.msgLabel.grid(column=0, row=5,columnspan=2) 195 | 196 | self.st = ScrolledText(self.window, height=5, width=40) 197 | self.st.grid(row=6, column=0,columnspan=2) 198 | 199 | self.btn = Button(self.window, text="Set JS8Call Text", command=self.setMessage) 200 | 201 | self.btn.grid(column=0, row=9) 202 | 203 | self.btn2 = Button(self.window, text="TX With JS8Call", command=self.txMessage) 204 | 205 | self.btn2.grid(column=1, row=9) 206 | 207 | self.note1label = Label(self.window, text="Click Set JS8Call text to set the message text in JS8Call", justify="center", wraplength=300) 208 | 209 | self.note1label.grid(column=0, row=10,columnspan=2) 210 | 211 | self.note1label = Label(self.window, text="Click TX with JS8Call to set the message text in JS8Call and start transmitting", justify="center", wraplength=300) 212 | 213 | self.note1label.grid(column=0, row=11,columnspan=2) 214 | 215 | self.window.geometry("350x350+300+300") 216 | self.window.mainloop() 217 | 218 | ui = UserInterface() 219 | 220 | --------------------------------------------------------------------------------