diff --git a/LICENSE b/LICENSE index 353e988b0f7cb3db920a4af611e32fc1e1a4d865..4c8225b9d2ee683106e884502c077311771ba7c5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2013 OnBeep, Inc. +Copyright 2015 Orion Labs, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index b2fd593b431060572167910abe4d6ba644fa62a9..38ec6b297feb78eee46c263466fdd27ee8224828 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # Makefile for APRS Python Module. # # Source:: https://github.com/ampledata/aprs -# Author:: Greg Albrecht W2GMD <gba@onbeep.com> -# Copyright:: Copyright 2013 OnBeep, Inc. +# Author:: Greg Albrecht W2GMD <gba@orionlabs.co> +# Copyright:: Copyright 2015 Orion Labs, Inc. # License:: Apache License, Version 2.0 # diff --git a/README.rst b/README.rst index 4cd263537220ac9a79978cccd15f142df85c5210..f3937ba8abdc51d662429340fef8f26e3935cc80 100644 --- a/README.rst +++ b/README.rst @@ -15,12 +15,13 @@ sent to my callback *my_cb* and printed. import aprs - a = aprs.APRS('W2GMD') - - def my_cb(line): + def my_callback(line): print line - a.receive(callback=my_cb) + a = aprs.APRS('W2GMD', '12345') + a.connect() + a.send('W2GMD>APRS:>Test!') + a.receive(callback=my_callback) Example output: @@ -33,12 +34,12 @@ Github: https://github.com/ampledata/aprs Author ====== -Greg Albrecht W2GMD <gba@gregalbrecht.com> +Greg Albrecht W2GMD <gba@orionlabs.co> Copyright ========= -Copyright 2013 Greg Albrecht +Copyright 2015 Orion Labs, Inc. License ======= -Creative Commons Attribution 3.0 Unported License +Apache License, Version 2.0 diff --git a/aprs/__init__.py b/aprs/__init__.py index c89c4799580cfea8dae3913364ca43bf23eccf39..c9c0b3fa570ced895f893277dc10644d7af320d7 100644 --- a/aprs/__init__.py +++ b/aprs/__init__.py @@ -8,8 +8,8 @@ APRS Python Module. ~~~~ -:author: Greg Albrecht W2GMD <gba@onbeep.com> -:copyright: Copyright 2013 OnBeep, Inc. +:author: Greg Albrecht W2GMD <gba@orionlabs.co> +:copyright: Copyright 2015 Orion Labs, Inc. :license: Apache License, Version 2.0 :source: <https://github.com/ampledata/aprs> diff --git a/aprs/classes.py b/aprs/classes.py index 491ef3fe6da9c5f16c83d25e017ae46523ded849..9147b6c67d10954c5e41ab6cdb34cf2dd094774a 100755 --- a/aprs/classes.py +++ b/aprs/classes.py @@ -3,15 +3,14 @@ """APRS Class Definitions""" -__author__ = 'Greg Albrecht W2GMD <gba@onbeep.com>' +__author__ = 'Greg Albrecht W2GMD <gba@orionlabs.co>' __license__ = 'Apache License, Version 2.0' -__copyright__ = 'Copyright 2013 OnBeep, Inc.' +__copyright__ = 'Copyright 2015 Orion Labs, Inc.' import logging import logging.handlers import socket -import time import requests @@ -37,30 +36,21 @@ class APRS(object): def __init__(self, user, password='-1', input_url=None): self.user = user self._url = input_url or aprs.constants.APRSIS_URL - self._auth = ' '.join(['user', user, 'pass', password]) + self._auth = ' '.join( + ['user', user, 'pass', password, 'vers', 'APRS Python Module v2']) + self.aprsis_sock = None - def send(self, message, headers=None): + def connect(self, server=None, port=None, filter=None): """ - Sends message to APRS-IS send-only interface. - - :param message: Message to send to APRS-IS. - :param headers: Optional headers to post. - :type message: str - :type headers: dict - - :return: True on 204 success, False otherwise. - :rtype: bool + Connects & logs in to APRS-IS. + + :param server: Optional alternative APRS-IS erver. + :param port: Optional APRS-IS port. + :param filter: Optional filter to use. + :type server: str + :type port: int + :type filte: str """ - self.logger.debug('message=%s headers=%s', message, headers) - - headers = headers or aprs.constants.APRSIS_HTTP_HEADERS - content = "\n".join([self._auth, message]) - - result = requests.post(self._url, data=content, headers=headers) - - return result.status_code == 204 - - def receive(self, server=None, port=None, filter=None, callback=None): if not server: server = aprs.constants.APRSIS_SERVER if not port: @@ -68,19 +58,60 @@ class APRS(object): if not filter: filter = "p/%s" % self.user - full_auth = ' '.join([self._auth, 'vers omg 1.0', 'filter', filter]) + full_auth = ' '.join([self._auth, 'filter', filter]) - recvd_data = '' - - aprsis_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - aprsis_sock.connect((server, int(port))) + self.aprsis_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.aprsis_sock.connect((server, int(port))) self.logger.info('Connected to server=%s port=%s', server, port) self.logger.debug('Sending full_auth=%s', full_auth) - aprsis_sock.sendall(full_auth + '\n\r') + self.aprsis_sock.sendall(full_auth + '\n\r') + + def send(self, message, headers=None, protocol='TCP'): + """ + Sends message to APRS-IS. + + :param message: Message to send to APRS-IS. + :param headers: Optional HTTP headers to post. + :param protocol: Protocol to use: One of TCP, HTTP or UDP. + :type message: str + :type headers: dict + + :return: True on success, False otherwise. + :rtype: bool + """ + self.logger.debug( + 'message=%s headers=%s protocol=%s', message, headers, protocol) + + if protocol == 'TCP': + self.logger.debug('sending message=%s', message) + self.aprsis_sock.sendall(message + '\n\r') + return True + elif protocol == 'HTTP': + content = "\n".join([self._auth, message]) + headers = headers or aprs.constants.APRSIS_HTTP_HEADERS + result = requests.post(self._url, data=content, headers=headers) + return result.status_code == 204 + elif protocol == 'UDP': + content = "\n".join([self._auth, message]) + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.sendto( + content, + (aprs.constants.APRSIS_SERVER, aprs.constants.APRSIS_RX_PORT) + ) + return True + + def receive(self, callback=None): + """ + Receives from APRS-IS. + + :param callback: Optional callback to deliver data to. + :type callback: func + """ + recvd_data = '' try: while 1: - recv_data = aprsis_sock.recv(aprs.constants.RECV_BUFFER) + recv_data = self.aprsis_sock.recv(aprs.constants.RECV_BUFFER) if not recv_data: break diff --git a/aprs/constants.py b/aprs/constants.py index 185e7f888ab6550ef5ed24234f7a945c9ce5aaec..37b7117528e9a317daf061dcb66349f1168e4587 100644 --- a/aprs/constants.py +++ b/aprs/constants.py @@ -5,9 +5,9 @@ Constants for APRS Module. """ -__author__ = 'Greg Albrecht W2GMD <gba@onbeep.com>' +__author__ = 'Greg Albrecht W2GMD <gba@orionlabs.co>' __license__ = 'Apache License, Version 2.0' -__copyright__ = 'Copyright 2013 OnBeep, Inc.' +__copyright__ = 'Copyright 2015 Orion Labs, Inc.' import logging @@ -20,6 +20,7 @@ APRSIS_HTTP_HEADERS = { } APRSIS_SERVER = 'rotate.aprs.net' APRSIS_FILTER_PORT = 14580 +APRSIS_RX_PORT = 8080 RECV_BUFFER = 1024 diff --git a/aprs/util.py b/aprs/util.py index 7e904e72c7667a48ae555491232b787ea655b84b..f03810f2204f4bcc8d695878c8fe577581a59a6b 100755 --- a/aprs/util.py +++ b/aprs/util.py @@ -3,9 +3,9 @@ """Utilities for the APRS Python Module.""" -__author__ = 'Greg Albrecht W2GMD <gba@onbeep.com>' +__author__ = 'Greg Albrecht W2GMD <gba@orionlabs.co>' __license__ = 'Apache License, Version 2.0' -__copyright__ = 'Copyright 2013 OnBeep, Inc.' +__copyright__ = 'Copyright 2015 Orion Labs, Inc.' import logging @@ -15,16 +15,6 @@ import aprs.decimaldegrees import kiss.constants -logger = logging.getLogger(__name__) -logger.setLevel(aprs.constants.LOG_LEVEL) -console_handler = logging.StreamHandler() -console_handler.setLevel(aprs.constants.LOG_LEVEL) -console_handler.setFormatter( - logging.Formatter(aprs.constants.LOG_FORMAT)) -logger.addHandler(console_handler) -logger.propagate = False - - def dec2dm_lat(dec): """Converts DecDeg to APRS Coord format. See: http://ember2ash.com/lat.htm @@ -83,15 +73,15 @@ def decode_aprs_ascii_frame(ascii_frame): :returns: Dictionary of APRS Frame parts: source, destination, path, text. :rtype: dict """ - logger.debug('frame=%s', ascii_frame) + logging.debug('frame=%s', ascii_frame) decoded_frame = {} frame_so_far = '' for char in ascii_frame: - if '>' in char and not 'source' in decoded_frame: + if '>' in char and 'source' not in decoded_frame: decoded_frame['source'] = frame_so_far frame_so_far = '' - elif ':' in char and not 'path' in decoded_frame: + elif ':' in char and 'path' not in decoded_frame: decoded_frame['path'] = frame_so_far frame_so_far = '' else: @@ -162,7 +152,7 @@ def valid_callsign(callsign): :returns: True if valid, False otherwise. :rtype: bool """ - logger.debug('callsign=%s', callsign) + logging.debug('callsign=%s', callsign) if '-' in callsign: if not callsign.count('-') == 1: @@ -172,7 +162,7 @@ def valid_callsign(callsign): else: ssid = 0 - logger.debug('callsign=%s ssid=%s', callsign, ssid) + logging.debug('callsign=%s ssid=%s', callsign, ssid) if (len(callsign) < 2 or len(callsign) > 6 or len(str(ssid)) < 1 or len(str(ssid)) > 2): @@ -327,7 +317,7 @@ def decode_frame(raw_frame): def run_doctest(): # pragma: no cover """Runs doctests for this module.""" import doctest - import aprs.util # pylint: disable=W0406 + import aprs.util # pylint: disable=W0406,W0621 return doctest.testmod(aprs.util) diff --git a/requirements.txt b/requirements.txt index 1396a2d55928fc1bcb8c65f1810e00765d240f3f..184270861d51c5d986852ca79eadc2979eca553f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ # Python Distribution Package Requirements for APRS. # # Source:: https://github.com/ampledata/aprs -# Author:: Greg Albrecht W2GMD <gba@onbeep.com> -# Copyright:: Copyright 2013 OnBeep, Inc. and Contributors +# Author:: Greg Albrecht W2GMD <gba@orionlabs.co> +# Copyright:: Copyright 2015 Orion Labs, Inc. # License:: Apache License, Version 2.0 diff --git a/setup.cfg b/setup.cfg index 828204dda494d29ae36cf35c9b06e8fff692228a..771109304582af55b26225c5eb4a1257ac738d91 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,8 +1,8 @@ # Nosetests configuration for APRS. # # Source:: https://github.com/ampledata/aprs -# Author:: Greg Albrecht W2GMD <gba@onbeep.com> -# Copyright:: Copyright 2013 OnBeep, Inc. +# Author:: Greg Albrecht W2GMD <gba@orionlabs.co> +# Copyright:: Copyright 2015 Orion Labs, Inc. # License:: Apache License, Version 2.0 # diff --git a/setup.py b/setup.py index 9533632b7ff4e4955cced9e3315f200b6996d607..4ead7bfe31e6636bea50dc1214ecf419f52e3289 100644 --- a/setup.py +++ b/setup.py @@ -10,9 +10,9 @@ Source:: https://github.com/ampledata/aprs __title__ = 'aprs' __version__ = '2.2.0' -__author__ = 'Greg Albrecht W2GMD <gba@onbeep.com>' +__author__ = 'Greg Albrecht W2GMD <gba@orionlabs.co>' __license__ = 'Apache License, Version 2.0' -__copyright__ = 'Copyright 2014 OnBeep, Inc.' +__copyright__ = 'Copyright 2015 Orion Labs, Inc.' import os @@ -39,7 +39,7 @@ setup( version=__version__, description='Python Bindings for APRS-IS API.', author='Greg Albrecht', - author_email='gba@onbeep.com', + author_email='gba@orionlabs.co', packages=['aprs'], package_data={'': ['LICENSE']}, license=open('LICENSE').read(), diff --git a/tests/constants.py b/tests/constants.py index d97eb5efc1b98b7988c84b6d199487d98198f2c5..01db6f7e2c023c0f7e83c53912a7cf4b5a40ccb3 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -3,9 +3,9 @@ """Constants for APRS Module Tests.""" -__author__ = 'Greg Albrecht W2GMD <gba@onbeep.com>' +__author__ = 'Greg Albrecht W2GMD <gba@orionlabs.co>' __license__ = 'Apache License, Version 2.0' -__copyright__ = 'Copyright 2013 OnBeep, Inc.' +__copyright__ = 'Copyright 2015 Orion Labs, Inc.' TEST_FRAMES = 'tests/test_frames.log' diff --git a/tests/context.py b/tests/context.py index a9ab68e5b0afe32be888a18a567acdb6c943e6ec..37f3193a10a7f53373409f66ebecda4e91d0c9c5 100644 --- a/tests/context.py +++ b/tests/context.py @@ -3,6 +3,10 @@ """Context for tests for APRS Python Module.""" +__author__ = 'Greg Albrecht W2GMD <gba@orionlabs.co>' +__license__ = 'Apache License, Version 2.0' +__copyright__ = 'Copyright 2015 Orion Labs, Inc.' + import os import sys diff --git a/tests/test_aprs.py b/tests/test_aprs.py index 33ff8e01414ac0da970cb8ab17d7dd676be4114b..be9c476e2a52192f5f2d8e6e6dd2060d7c0f6475 100644 --- a/tests/test_aprs.py +++ b/tests/test_aprs.py @@ -3,9 +3,9 @@ """Tests for Python APRS-IS Bindings.""" -__author__ = 'Greg Albrecht W2GMD <gba@onbeep.com>' +__author__ = 'Greg Albrecht W2GMD <gba@orionlabs.co>' __license__ = 'Apache License, Version 2.0' -__copyright__ = 'Copyright 2013 OnBeep, Inc.' +__copyright__ = 'Copyright 2015 Orion Labs, Inc.' import random @@ -36,7 +36,8 @@ class APRSTest(unittest.TestCase): # pylint: disable=R0904 logger.addHandler(console_handler) logger.propagate = False - def random(self, length=8, alphabet=ALPHANUM): + @classmethod + def random(cls, length=8, alphabet=ALPHANUM): """ Generates a random string for test cases. @@ -65,8 +66,11 @@ class APRSTest(unittest.TestCase): # pylint: disable=R0904 self.real_server = 'http://localhost:14580' self.real_callsign = '-'.join(['W2GMD', self.random(1, '123456789')]) - self.logger.debug("fake_server=%s fake_callsign=%s" - % (self.fake_server, self.fake_callsign)) + self.logger.debug( + "fake_server=%s fake_callsign=%s", + self.fake_server, + self.fake_callsign + ) @httpretty.httprettified def test_fake_good_auth(self): @@ -83,6 +87,7 @@ class APRSTest(unittest.TestCase): # pylint: disable=R0904 user=self.fake_callsign, input_url=self.fake_server ) + aprs_conn.connect() msg = '>'.join([ self.fake_callsign, @@ -95,7 +100,7 @@ class APRSTest(unittest.TestCase): # pylint: disable=R0904 self.assertTrue(result) @httpretty.httprettified - def test_fake_bad_auth(self): + def test_fake_bad_auth_http(self): """ Tests authenticating against APRS-IS using an invalid call+pass. """ @@ -109,6 +114,7 @@ class APRSTest(unittest.TestCase): # pylint: disable=R0904 user=self.fake_callsign, input_url=self.fake_server ) + aprs_conn.connect() msg = '>'.join([ self.fake_callsign, @@ -116,7 +122,7 @@ class APRSTest(unittest.TestCase): # pylint: disable=R0904 ]) self.logger.debug(locals()) - result = aprs_conn.send(msg) + result = aprs_conn.send(msg, protocol='HTTP') self.assertFalse(result) @@ -129,6 +135,7 @@ class APRSTest(unittest.TestCase): # pylint: disable=R0904 user=self.real_callsign, input_url=self.real_server ) + aprs_conn.connect() msg = '>'.join([ self.real_callsign, diff --git a/tests/test_util.py b/tests/test_util.py index 8fba5ba52fd0a81a79cb4a53061e419fd733b2b4..04c8aff5fd87f52606158de6565bbb9c4697e59b 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,9 +3,9 @@ """Tests for Python APRS util methods.""" -__author__ = 'Greg Albrecht W2GMD <gba@onbeep.com>' -__copyright__ = 'Copyright 2013 OnBeep, Inc.' +__author__ = 'Greg Albrecht W2GMD <gba@orionlabs.co>' __license__ = 'Apache License, Version 2.0' +__copyright__ = 'Copyright 2015 Orion Labs, Inc.' import unittest @@ -72,7 +72,7 @@ class APRSUtilTestCase(unittest.TestCase): # pylint: disable=R0904 self.logger.debug('aprs_lat=%s', aprs_lat) lat_deg = int(aprs_lat.split('.')[0][:1]) - #lat_hsec = aprs_lat.split('.')[1] + # lat_hsec = aprs_lat.split('.')[1] self.assertTrue(len(aprs_lat) == 8) self.assertTrue(lat_deg >= 00) @@ -103,7 +103,7 @@ class APRSUtilTestCase(unittest.TestCase): # pylint: disable=R0904 self.logger.debug('aprs_lat=%s', aprs_lat) lat_deg = int(aprs_lat.split('.')[0][:1]) - #lat_hsec = aprs_lat.split('.')[1] + # lat_hsec = aprs_lat.split('.')[1] self.assertTrue(len(aprs_lat) == 8) self.assertTrue(lat_deg >= 00) @@ -136,7 +136,7 @@ class APRSUtilTestCase(unittest.TestCase): # pylint: disable=R0904 self.logger.debug('aprs_lng=%s', aprs_lng) lng_deg = int(aprs_lng.split('.')[0][:2]) - #lng_hsec = aprs_lng.split('.')[1] + # lng_hsec = aprs_lng.split('.')[1] self.assertTrue(len(aprs_lng) == 9) self.assertTrue(lng_deg >= 000) @@ -169,7 +169,7 @@ class APRSUtilTestCase(unittest.TestCase): # pylint: disable=R0904 self.logger.debug('aprs_lng=%s', aprs_lng) lng_deg = int(aprs_lng.split('.')[0][:2]) - #lng_hsec = aprs_lng.split('.')[1] + # lng_hsec = aprs_lng.split('.')[1] self.assertTrue(len(aprs_lng) == 9) self.assertTrue(lng_deg >= 000)