Merge branch 'master' of https://github.com/n0mjs710/hblink3
This commit is contained in:
		
						commit
						a16e426c55
					
				
							
								
								
									
										0
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										30
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								Dockerfile
									
									
									
									
									
								
							| @ -1,30 +0,0 @@ | ||||
| FROM python:3.7-slim-stretch | ||||
| 
 | ||||
| RUN apt update && \ | ||||
|     apt install -y git && \ | ||||
|     cd /usr/src/ && \ | ||||
|     git clone https://github.com/n0mjs710/dmr_utils3 && \ | ||||
|     cd /usr/src/dmr_utils3 && \ | ||||
|     ./install.sh && \ | ||||
|     rm -rf /var/lib/apt/lists/* && \ | ||||
|     cd /opt && \ | ||||
|     rm -rf /usr/src/dmr_utils3 && \ | ||||
|     git clone https://github.com/n0mjs710/hblink3 | ||||
| ENV AAA BBBB | ||||
| RUN cd /opt/hblink3/ && \ | ||||
|     sed -i s/.*python.*//g  requirements.txt && \ | ||||
|     pip install --no-cache-dir -r requirements.txt | ||||
| 
 | ||||
| 
 | ||||
| ADD entrypoint /entrypoint | ||||
| 
 | ||||
| RUN adduser -u 54000 radio && \ | ||||
|     adduser radio radio && \ | ||||
|     chmod 755 /entrypoint && \ | ||||
|     chown radio:radio /entrypoint && \ | ||||
|     chown radio /opt/hblink3 | ||||
| 
 | ||||
| USER radio  | ||||
| EXPOSE 54000 | ||||
| 
 | ||||
| ENTRYPOINT [ "/entrypoint" ] | ||||
							
								
								
									
										150
									
								
								app_template.py
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								app_template.py
									
									
									
									
									
								
							| @ -1,150 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| # | ||||
| ############################################################################### | ||||
| #   Copyright (C) 2016-2019 Cortney T. Buffington, N0MJS <n0mjs@me.com> | ||||
| # | ||||
| #   This program is free software; you can redistribute it and/or modify | ||||
| #   it under the terms of the GNU General Public License as published by | ||||
| #   the Free Software Foundation; either version 3 of the License, or | ||||
| #   (at your option) any later version. | ||||
| # | ||||
| #   This program is distributed in the hope that it will be useful, | ||||
| #   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| #   GNU General Public License for more details. | ||||
| # | ||||
| #   You should have received a copy of the GNU General Public License | ||||
| #   along with this program; if not, write to the Free Software Foundation, | ||||
| #   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA | ||||
| ############################################################################### | ||||
| 
 | ||||
| # Python modules we need | ||||
| import sys | ||||
| from bitarray import bitarray | ||||
| from time import time | ||||
| from importlib import import_module | ||||
| 
 | ||||
| # Twisted is pretty important, so I keep it separate | ||||
| from twisted.internet.protocol import Factory, Protocol | ||||
| from twisted.protocols.basic import NetstringReceiver | ||||
| from twisted.internet import reactor, task | ||||
| 
 | ||||
| # Things we import from the main hblink module | ||||
| from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases | ||||
| from dmr_utils3.utils import bytes_3, int_id, get_alias | ||||
| from dmr_utils3 import decode, bptc, const | ||||
| import config | ||||
| import log | ||||
| from const import * | ||||
| 
 | ||||
| # Stuff for socket reporting | ||||
| import pickle | ||||
| # REMOVE LATER from datetime import datetime | ||||
| # The module needs logging, but handlers, etc. are controlled by the parent | ||||
| import logging | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| # Does anybody read this stuff? There's a PEP somewhere that says I should do this. | ||||
| __author__     = 'Cortney T. Buffington, N0MJS' | ||||
| __copyright__  = 'Copyright (c) 2016-2018 Cortney T. Buffington, N0MJS and the K0USY Group' | ||||
| __credits__    = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT' | ||||
| __license__    = 'GNU GPLv3' | ||||
| __maintainer__ = 'Cort Buffington, N0MJS' | ||||
| __email__      = 'n0mjs@me.com' | ||||
| 
 | ||||
| # Module gobal varaibles | ||||
| 
 | ||||
| 
 | ||||
| class OBP(OPENBRIDGE): | ||||
| 
 | ||||
|     def __init__(self, _name, _config, _report): | ||||
|         OPENBRIDGE.__init__(self, _name, _config, _report) | ||||
| 
 | ||||
| 
 | ||||
|     def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): | ||||
|         pass | ||||
| 
 | ||||
| 
 | ||||
| class HBP(HBSYSTEM): | ||||
| 
 | ||||
|     def __init__(self, _name, _config, _report): | ||||
|         HBSYSTEM.__init__(self, _name, _config, _report) | ||||
| 
 | ||||
|     def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): | ||||
|         pass | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #************************************************ | ||||
| #      MAIN PROGRAM LOOP STARTS HERE | ||||
| #************************************************ | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
| 
 | ||||
|     import argparse | ||||
|     import sys | ||||
|     import os | ||||
|     import signal | ||||
| 
 | ||||
|     # Change the current directory to the location of the application | ||||
|     os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) | ||||
| 
 | ||||
|     # CLI argument parser - handles picking up the config file from the command line, and sending a "help" message | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually hblink.cfg)') | ||||
|     parser.add_argument('-l', '--logging', action='store', dest='LOG_LEVEL', help='Override config file logging level.') | ||||
|     cli_args = parser.parse_args() | ||||
| 
 | ||||
|     # Ensure we have a path for the config file, if one wasn't specified, then use the default (top of file) | ||||
|     if not cli_args.CONFIG_FILE: | ||||
|         cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/hblink.cfg' | ||||
| 
 | ||||
|     # Call the external routine to build the configuration dictionary | ||||
|     CONFIG = config.build_config(cli_args.CONFIG_FILE) | ||||
| 
 | ||||
|     # Start the system logger | ||||
|     if cli_args.LOG_LEVEL: | ||||
|         CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL | ||||
|     logger = log.config_logging(CONFIG['LOGGER']) | ||||
|     logger.info('\n\nCopyright (c) 2013, 2014, 2015, 2016, 2018\n\tThe Regents of the K0USY Group. All rights reserved.\n') | ||||
|     logger.debug('(GLOBAL) Logging system started, anything from here on gets logged') | ||||
| 
 | ||||
|     # Set up the signal handler | ||||
|     def sig_handler(_signal, _frame): | ||||
|         logger.info('(GLOBAL) SHUTDOWN: CONFBRIDGE IS TERMINATING WITH SIGNAL %s', str(_signal)) | ||||
|         hblink_handler(_signal, _frame) | ||||
|         logger.info('(GLOBAL) SHUTDOWN: ALL SYSTEM HANDLERS EXECUTED - STOPPING REACTOR') | ||||
|         reactor.stop() | ||||
| 
 | ||||
|     # Set signal handers so that we can gracefully exit if need be | ||||
|     for sig in [signal.SIGINT, signal.SIGTERM]: | ||||
|         signal.signal(sig, sig_handler) | ||||
| 
 | ||||
|     # Create the name-number mapping dictionaries | ||||
|     peer_ids, subscriber_ids, talkgroup_ids = mk_aliases(CONFIG) | ||||
| 
 | ||||
|     # INITIALIZE THE REPORTING LOOP | ||||
|     if CONFIG['REPORTS']['REPORT']: | ||||
|         report_server = config_reports(CONFIG, bridgeReportFactory) | ||||
|     else: | ||||
|         report_server = None | ||||
|         logger.info('(REPORT) TCP Socket reporting not configured') | ||||
| 
 | ||||
|     # HBlink instance creation | ||||
|     logger.info('(GLOBAL) HBlink \'bridge.py\' -- SYSTEM STARTING...') | ||||
|     for system in CONFIG['SYSTEMS']: | ||||
|         if CONFIG['SYSTEMS'][system]['ENABLED']: | ||||
|             if CONFIG['SYSTEMS'][system]['MODE'] == 'OPENBRIDGE': | ||||
|                 systems[system] = OBP(system, CONFIG, report_server) | ||||
|             else: | ||||
|                 systems[system] = HBP(system, CONFIG, report_server) | ||||
|             reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP']) | ||||
|             logger.debug('(GLOBAL) %s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system]) | ||||
| 
 | ||||
|     def loopingErrHandle(failure): | ||||
|         logger.error('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error in timed loop.\n %s', failure) | ||||
|         reactor.stop() | ||||
| 
 | ||||
| 
 | ||||
|     reactor.run() | ||||
							
								
								
									
										143
									
								
								blank_app.py
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								blank_app.py
									
									
									
									
									
								
							| @ -1,143 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| # | ||||
| ############################################################################### | ||||
| #   Copyright (C) 2020 Cortney T. Buffington, N0MJS <n0mjs@me.com> | ||||
| # | ||||
| #   This program is free software; you can redistribute it and/or modify | ||||
| #   it under the terms of the GNU General Public License as published by | ||||
| #   the Free Software Foundation; either version 3 of the License, or | ||||
| #   (at your option) any later version. | ||||
| # | ||||
| #   This program is distributed in the hope that it will be useful, | ||||
| #   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| #   GNU General Public License for more details. | ||||
| # | ||||
| #   You should have received a copy of the GNU General Public License | ||||
| #   along with this program; if not, write to the Free Software Foundation, | ||||
| #   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA | ||||
| ############################################################################### | ||||
| 
 | ||||
| ''' | ||||
| This is a blank application template to build on top of hblink.py. It contains | ||||
| only the things you need to, essentially, run hblink.py underneath and do nothing | ||||
| else. The expected behaviour is to override the dmrd_received function from | ||||
| hblink.py to do somethign meaningful, so that framework is completed, but as | ||||
| it stands, still does nothing with the DMRD packet. | ||||
| ''' | ||||
| 
 | ||||
| # Python modules we need | ||||
| import sys | ||||
| from bitarray import bitarray | ||||
| from time import time | ||||
| from importlib import import_module | ||||
| from types import ModuleType | ||||
| 
 | ||||
| # Twisted is pretty important, so I keep it separate | ||||
| from twisted.internet.protocol import Factory, Protocol | ||||
| from twisted.protocols.basic import NetstringReceiver | ||||
| from twisted.internet import reactor, task | ||||
| 
 | ||||
| # Things we import from the main hblink module | ||||
| from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, config_reports, mk_aliases, acl_check | ||||
| from dmr_utils3.utils import bytes_3, int_id, get_alias | ||||
| from dmr_utils3 import decode, bptc, const | ||||
| import config | ||||
| import log | ||||
| import const | ||||
| 
 | ||||
| # The module needs logging logging, but handlers, etc. are controlled by the parent | ||||
| import logging | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| # Does anybody read this stuff? There's a PEP somewhere that says I should do this. | ||||
| __author__     = 'Cortney T. Buffington, N0MJS' | ||||
| __copyright__  = 'Copyright (c) 2020 Cortney T. Buffington' | ||||
| __credits__    = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT' | ||||
| __license__    = 'GNU GPLv3' | ||||
| __maintainer__ = 'Cort Buffington, N0MJS' | ||||
| __email__      = 'n0mjs@me.com' | ||||
| __status__     = 'pre-alpha' | ||||
| 
 | ||||
| # Module gobal varaibles | ||||
| 
 | ||||
| 
 | ||||
| class blankSYSTEM(HBSYSTEM): | ||||
| 
 | ||||
|     def __init__(self, _name, _config, _report): | ||||
|         HBSYSTEM.__init__(self, _name, _config, _report) | ||||
| 
 | ||||
|     def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): | ||||
|         pass | ||||
| 
 | ||||
| 
 | ||||
| #************************************************ | ||||
| #      MAIN PROGRAM LOOP STARTS HERE | ||||
| #************************************************ | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     import argparse | ||||
|     import sys | ||||
|     import os | ||||
|     import signal | ||||
|     from dmr_utils3.utils import try_download, mk_id_dict | ||||
| 
 | ||||
|     # Change the current directory to the location of the application | ||||
|     os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) | ||||
| 
 | ||||
|     # CLI argument parser - handles picking up the config file from the command line, and sending a "help" message | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually hblink.cfg)') | ||||
|     parser.add_argument('-l', '--logging', action='store', dest='LOG_LEVEL', help='Override config file logging level.') | ||||
|     cli_args = parser.parse_args() | ||||
| 
 | ||||
|     # Ensure we have a path for the config file, if one wasn't specified, then use the default (top of file) | ||||
|     if not cli_args.CONFIG_FILE: | ||||
|         cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/hblink.cfg' | ||||
| 
 | ||||
|     # Call the external routine to build the configuration dictionary | ||||
|     CONFIG = config.build_config(cli_args.CONFIG_FILE) | ||||
| 
 | ||||
|     # Start the system logger | ||||
|     if cli_args.LOG_LEVEL: | ||||
|         CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL | ||||
|     logger = log.config_logging(CONFIG['LOGGER']) | ||||
|     logger.info('\n\nCopyright (c) 2013, 2014, 2015, 2016, 2018, 2019\n\tThe Regents of the K0USY Group. All rights reserved.\n') | ||||
|     logger.debug('Logging system started, anything from here on gets logged') | ||||
| 
 | ||||
|     # Set up the signal handler | ||||
|     def sig_handler(_signal, _frame): | ||||
|         logger.info('SHUTDOWN: >>>BLANK APP<<< IS TERMINATING WITH SIGNAL %s', str(_signal)) | ||||
|         hblink_handler(_signal, _frame) | ||||
|         logger.info('SHUTDOWN: ALL SYSTEM HANDLERS EXECUTED - STOPPING REACTOR') | ||||
|         reactor.stop() | ||||
| 
 | ||||
|     # Set signal handers so that we can gracefully exit if need be | ||||
|     for sig in [signal.SIGTERM, signal.SIGINT]: | ||||
|         signal.signal(sig, sig_handler) | ||||
| 
 | ||||
|     # Create the name-number mapping dictionaries | ||||
|     peer_ids, subscriber_ids, talkgroup_ids = mk_aliases(CONFIG) | ||||
|      | ||||
|      | ||||
|     # INITIALIZE THE REPORTING LOOP | ||||
|     if CONFIG['REPORTS']['REPORT']: | ||||
|         report_server = config_reports(CONFIG, reportFactory) | ||||
|     else: | ||||
|         report_server = None | ||||
|         logger.info('(REPORT) TCP Socket reporting not configured') | ||||
| 
 | ||||
|     # HBlink instance creation | ||||
|     logger.info('HBlink \'blank_app.py\' -- SYSTEM STARTING...') | ||||
|     for system in CONFIG['SYSTEMS']: | ||||
|         if CONFIG['SYSTEMS'][system]['ENABLED']: | ||||
|             if CONFIG['SYSTEMS'][system]['MODE'] == 'OPENBRIDGE': | ||||
|                 systems[system] = OPENBRIDGE(system, CONFIG, report_server) | ||||
|             else: | ||||
|                 systems[system] = HBSYSTEM(system, CONFIG, report_server) | ||||
|                  | ||||
|             reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP']) | ||||
|             logger.debug('%s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system]) | ||||
| 
 | ||||
|     reactor.run() | ||||
							
								
								
									
										47
									
								
								config.py
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								config.py
									
									
									
									
									
								
							| @ -199,52 +199,6 @@ def build_config(_config_file): | ||||
|                         'LAST_PING_ACK_TIME': 0, | ||||
|                     }}) | ||||
| 
 | ||||
|                 if config.get(section, 'MODE') == 'XLXPEER': | ||||
|                     CONFIG['SYSTEMS'].update({section: { | ||||
|                         'MODE': config.get(section, 'MODE'), | ||||
|                         'ENABLED': config.getboolean(section, 'ENABLED'), | ||||
|                         'LOOSE': config.getboolean(section, 'LOOSE'), | ||||
|                         'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')), | ||||
|                         'IP': gethostbyname(config.get(section, 'IP')), | ||||
|                         'PORT': config.getint(section, 'PORT'), | ||||
|                         'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')), | ||||
|                         'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')), | ||||
|                         'MASTER_PORT': config.getint(section, 'MASTER_PORT'), | ||||
|                         'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'), | ||||
|                         'CALLSIGN': bytes(config.get(section, 'CALLSIGN').ljust(8)[:8], 'utf-8'), | ||||
|                         'RADIO_ID': config.getint(section, 'RADIO_ID').to_bytes(4, 'big'), | ||||
|                         'RX_FREQ': bytes(config.get(section, 'RX_FREQ').ljust(9)[:9], 'utf-8'), | ||||
|                         'TX_FREQ': bytes(config.get(section, 'TX_FREQ').ljust(9)[:9], 'utf-8'), | ||||
|                         'TX_POWER': bytes(config.get(section, 'TX_POWER').rjust(2,'0'), 'utf-8'), | ||||
|                         'COLORCODE': bytes(config.get(section, 'COLORCODE').rjust(2,'0'), 'utf-8'), | ||||
|                         'LATITUDE': bytes(config.get(section, 'LATITUDE').ljust(8)[:8], 'utf-8'), | ||||
|                         'LONGITUDE': bytes(config.get(section, 'LONGITUDE').ljust(9)[:9], 'utf-8'), | ||||
|                         'HEIGHT': bytes(config.get(section, 'HEIGHT').rjust(3,'0'), 'utf-8'), | ||||
|                         'LOCATION': bytes(config.get(section, 'LOCATION').ljust(20)[:20], 'utf-8'), | ||||
|                         'DESCRIPTION': bytes(config.get(section, 'DESCRIPTION').ljust(19)[:19], 'utf-8'), | ||||
|                         'SLOTS': bytes(config.get(section, 'SLOTS'), 'utf-8'), | ||||
|                         'URL': bytes(config.get(section, 'URL').ljust(124)[:124], 'utf-8'), | ||||
|                         'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'), | ||||
|                         'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'), | ||||
|                         'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'), | ||||
|                         'XLXMODULE': config.getint(section, 'XLXMODULE'), | ||||
|                         'OPTIONS': '', | ||||
|                         'USE_ACL': config.getboolean(section, 'USE_ACL'), | ||||
|                         'SUB_ACL': config.get(section, 'SUB_ACL'), | ||||
|                         'TG1_ACL': config.get(section, 'TGID_TS1_ACL'), | ||||
|                         'TG2_ACL': config.get(section, 'TGID_TS2_ACL') | ||||
|                     }}) | ||||
|                     CONFIG['SYSTEMS'][section].update({'XLXSTATS': { | ||||
|                         'CONNECTION': 'NO',             # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES  | ||||
|                         'CONNECTED': None, | ||||
|                         'PINGS_SENT': 0, | ||||
|                         'PINGS_ACKD': 0, | ||||
|                         'NUM_OUTSTANDING': 0, | ||||
|                         'PING_OUTSTANDING': False, | ||||
|                         'LAST_PING_TX_TIME': 0, | ||||
|                         'LAST_PING_ACK_TIME': 0, | ||||
|                     }}) | ||||
| 
 | ||||
|                 elif config.get(section, 'MODE') == 'MASTER': | ||||
|                     CONFIG['SYSTEMS'].update({section: { | ||||
|                         'MODE': config.get(section, 'MODE'), | ||||
| @ -274,6 +228,7 @@ def build_config(_config_file): | ||||
|                         'TARGET_SOCK': (gethostbyname(config.get(section, 'TARGET_IP')), config.getint(section, 'TARGET_PORT')), | ||||
|                         'TARGET_IP': gethostbyname(config.get(section, 'TARGET_IP')), | ||||
|                         'TARGET_PORT': config.getint(section, 'TARGET_PORT'), | ||||
|                         'BOTH_SLOTS': config.getboolean(section, 'BOTH_SLOTS'), | ||||
|                         'USE_ACL': config.getboolean(section, 'USE_ACL'), | ||||
|                         'SUB_ACL': config.get(section, 'SUB_ACL'), | ||||
|                         'TG1_ACL': config.get(section, 'TGID_ACL'), | ||||
|  | ||||
							
								
								
									
										19
									
								
								entrypoint
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								entrypoint
									
									
									
									
									
								
							| @ -1,19 +0,0 @@ | ||||
| #!/bin/sh | ||||
| 
 | ||||
| if [ -z "$GIT_REPO" ]; then | ||||
|   mkdir -p /var/tmp/config  | ||||
|   cd /var/tmp/config | ||||
|   git clone https://${GIT_USER}:${GIT_PASSWORD}@${GIT_REPO} | ||||
| 
 | ||||
|   DIR=$(echo ${GIT_REPO}| sed s/.git$//g | sed s#^.*/##g) | ||||
| 
 | ||||
|   cp -a /var/tmp/config/${DIR}/*cfg /opt/hblink3/ | ||||
|   cp -a /var/tmp/config/${DIR}/*csv /opt/hblink3/ | ||||
|   cp -a /var/tmp/config/${DIR}/*json /opt/hblink3/ | ||||
| else | ||||
|   cp -a /opt/config/*cfg /opt/hblink3/ | ||||
|   cp -a /opt/config/*csv /opt/hblink3/ | ||||
|   cp -a /opt/config/*json /opt/hblink3/ | ||||
| fi | ||||
| cd /opt/hblink3 | ||||
| python /opt/hblink3/hblink.py -c hblink.cfg | ||||
							
								
								
									
										127
									
								
								hblink-750.cfg
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										127
									
								
								hblink-750.cfg
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,127 @@ | ||||
| [GLOBAL] | ||||
| PATH: ./ | ||||
| PING_TIME: 5 | ||||
| MAX_MISSED: 3 | ||||
| USE_ACL: False | ||||
| REG_ACL: DENY:1 | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: PERMIT:ALL | ||||
| TGID_TS2_ACL: PERMIT:ALL | ||||
| 
 | ||||
| [REPORTS] | ||||
| REPORT: False | ||||
| REPORT_INTERVAL: 60 | ||||
| REPORT_PORT: 4321 | ||||
| REPORT_CLIENTS: 127.0.0.1 | ||||
| 
 | ||||
| [LOGGER] | ||||
| LOG_FILE: /tmp/hblink.log | ||||
| LOG_HANDLERS: console-timed | ||||
| LOG_LEVEL: INFO | ||||
| LOG_NAME: 444.750 | ||||
| 
 | ||||
| [ALIASES] | ||||
| TRY_DOWNLOAD: False | ||||
| PATH: ./ | ||||
| PEER_FILE: peer_ids.json | ||||
| SUBSCRIBER_FILE: subscriber_ids.json | ||||
| TGID_FILE: talkgroup_ids.json | ||||
| PEER_URL: https://www.radioid.net/static/rptrs.json | ||||
| SUBSCRIBER_URL: https://www.radioid.net/static/users.json | ||||
| STALE_DAYS: 7 | ||||
| 
 | ||||
| [OBP] | ||||
| MODE: OPENBRIDGE | ||||
| ENABLED: True | ||||
| IP: | ||||
| PORT: 50100 | ||||
| NETWORK_ID: 1 | ||||
| PASSPHRASE: deadbeef | ||||
| #TARGET_IP: olympic.k0usy.org | ||||
| TARGET_IP: 127.0.0.1 | ||||
| #TARGET_PORT: 50666 | ||||
| TARGET_PORT: 50101 | ||||
| BOTH_SLOTS: True | ||||
| USE_ACL: False | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_ACL: PERMIT:ALL | ||||
| 
 | ||||
| [444.750] | ||||
| MODE: MASTER | ||||
| ENABLED: True | ||||
| REPEAT: True | ||||
| MAX_PEERS: 5 | ||||
| EXPORT_AMBE: False | ||||
| IP: | ||||
| PORT: 50001 | ||||
| PASSPHRASE: jimmy | ||||
| GROUP_HANGTIME: 10 | ||||
| USE_ACL: False | ||||
| REG_ACL: DENY:1 | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: DENY:8 | ||||
| TGID_TS2_ACL: PERMIT:3120 | ||||
| 
 | ||||
| [TWO] | ||||
| MODE: MASTER | ||||
| ENABLED: False | ||||
| REPEAT: True | ||||
| MAX_PEERS: 5 | ||||
| EXPORT_AMBE: False | ||||
| IP: | ||||
| PORT:50002 | ||||
| PASSPHRASE: jimmy | ||||
| GROUP_HANGTIME: 10 | ||||
| USE_ACL: False | ||||
| REG_ACL: DENY:1 | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: DENY:8 | ||||
| TGID_TS2_ACL: PERMIT:3120 | ||||
| 
 | ||||
| [THREE] | ||||
| MODE: MASTER | ||||
| ENABLED: False | ||||
| REPEAT: True | ||||
| MAX_PEERS: 5 | ||||
| EXPORT_AMBE: False | ||||
| IP: | ||||
| PORT:50003 | ||||
| PASSPHRASE: jimmy | ||||
| GROUP_HANGTIME: 10 | ||||
| USE_ACL: False | ||||
| REG_ACL: DENY:1 | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: DENY:8 | ||||
| TGID_TS2_ACL: PERMIT:3120 | ||||
| 
 | ||||
| [KS-DMR] | ||||
| MODE: PEER | ||||
| ENABLED: False | ||||
| LOOSE: False | ||||
| EXPORT_AMBE: False | ||||
| IP:  | ||||
| PORT: 54001 | ||||
| MASTER_IP: olympic.k0usy.org | ||||
| MASTER_PORT: 62071 | ||||
| PASSPHRASE: c0ffee | ||||
| CALLSIGN: W1ABC | ||||
| RADIO_ID: 312312 | ||||
| RX_FREQ: 449000000 | ||||
| TX_FREQ: 444000000 | ||||
| TX_POWER: 25 | ||||
| COLORCODE: 1 | ||||
| SLOTS: 1 | ||||
| LATITUDE: 38.0000 | ||||
| LONGITUDE: -095.0000 | ||||
| HEIGHT: 75 | ||||
| LOCATION: Anywhere, USA | ||||
| DESCRIPTION: This is a cool repeater | ||||
| URL: www.w1abc.org | ||||
| SOFTWARE_ID: 20170620 | ||||
| PACKAGE_ID: MMDVM_HBlink | ||||
| GROUP_HANGTIME: 5 | ||||
| OPTIONS: | ||||
| USE_ACL: True | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: PERMIT:ALL | ||||
| TGID_TS2_ACL: PERMIT:ALL | ||||
							
								
								
									
										127
									
								
								hblink-800.cfg
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										127
									
								
								hblink-800.cfg
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,127 @@ | ||||
| [GLOBAL] | ||||
| PATH: ./ | ||||
| PING_TIME: 5 | ||||
| MAX_MISSED: 3 | ||||
| USE_ACL: False | ||||
| REG_ACL: DENY:1 | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: PERMIT:ALL | ||||
| TGID_TS2_ACL: PERMIT:ALL | ||||
| 
 | ||||
| [REPORTS] | ||||
| REPORT: False | ||||
| REPORT_INTERVAL: 60 | ||||
| REPORT_PORT: 4321 | ||||
| REPORT_CLIENTS: 127.0.0.1 | ||||
| 
 | ||||
| [LOGGER] | ||||
| LOG_FILE: /tmp/hblink.log | ||||
| LOG_HANDLERS: console-timed | ||||
| LOG_LEVEL: INFO | ||||
| LOG_NAME: 444.800 | ||||
| 
 | ||||
| [ALIASES] | ||||
| TRY_DOWNLOAD: False | ||||
| PATH: ./ | ||||
| PEER_FILE: peer_ids.json | ||||
| SUBSCRIBER_FILE: subscriber_ids.json | ||||
| TGID_FILE: talkgroup_ids.json | ||||
| PEER_URL: https://www.radioid.net/static/rptrs.json | ||||
| SUBSCRIBER_URL: https://www.radioid.net/static/users.json | ||||
| STALE_DAYS: 7 | ||||
| 
 | ||||
| [OBP] | ||||
| MODE: OPENBRIDGE | ||||
| ENABLED: True | ||||
| IP: | ||||
| PORT: 50101 | ||||
| NETWORK_ID: 2 | ||||
| PASSPHRASE: deadbeef | ||||
| #TARGET_IP: olympic.k0usy.org | ||||
| TARGET_IP: 127.0.0.1 | ||||
| #TARGET_PORT: 50666 | ||||
| TARGET_PORT: 50100 | ||||
| BOTH_SLOTS: True | ||||
| USE_ACL: False | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_ACL: PERMIT:ALL | ||||
| 
 | ||||
| [444.800] | ||||
| MODE: MASTER | ||||
| ENABLED: True | ||||
| REPEAT: True | ||||
| MAX_PEERS: 5 | ||||
| EXPORT_AMBE: False | ||||
| IP: | ||||
| PORT: 50011 | ||||
| PASSPHRASE: jimmy | ||||
| GROUP_HANGTIME: 10 | ||||
| USE_ACL: False | ||||
| REG_ACL: DENY:1 | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: DENY:8 | ||||
| TGID_TS2_ACL: PERMIT:3120 | ||||
| 
 | ||||
| [TWO] | ||||
| MODE: MASTER | ||||
| ENABLED: False | ||||
| REPEAT: True | ||||
| MAX_PEERS: 5 | ||||
| EXPORT_AMBE: False | ||||
| IP: | ||||
| PORT:50012 | ||||
| PASSPHRASE: jimmy | ||||
| GROUP_HANGTIME: 10 | ||||
| USE_ACL: False | ||||
| REG_ACL: DENY:1 | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: DENY:8 | ||||
| TGID_TS2_ACL: PERMIT:3120 | ||||
| 
 | ||||
| [THREE] | ||||
| MODE: MASTER | ||||
| ENABLED: False | ||||
| REPEAT: True | ||||
| MAX_PEERS: 5 | ||||
| EXPORT_AMBE: False | ||||
| IP: | ||||
| PORT:50013 | ||||
| PASSPHRASE: jimmy | ||||
| GROUP_HANGTIME: 10 | ||||
| USE_ACL: False | ||||
| REG_ACL: DENY:1 | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: DENY:8 | ||||
| TGID_TS2_ACL: PERMIT:3120 | ||||
| 
 | ||||
| [KS-DMR] | ||||
| MODE: PEER | ||||
| ENABLED: False | ||||
| LOOSE: False | ||||
| EXPORT_AMBE: False | ||||
| IP:  | ||||
| PORT: 54011 | ||||
| MASTER_IP: olympic.k0usy.org | ||||
| MASTER_PORT: 62071 | ||||
| PASSPHRASE: c0ffee | ||||
| CALLSIGN: W1ABC | ||||
| RADIO_ID: 312312 | ||||
| RX_FREQ: 449000000 | ||||
| TX_FREQ: 444000000 | ||||
| TX_POWER: 25 | ||||
| COLORCODE: 1 | ||||
| SLOTS: 1 | ||||
| LATITUDE: 38.0000 | ||||
| LONGITUDE: -095.0000 | ||||
| HEIGHT: 75 | ||||
| LOCATION: Anywhere, USA | ||||
| DESCRIPTION: This is a cool repeater | ||||
| URL: www.w1abc.org | ||||
| SOFTWARE_ID: 20170620 | ||||
| PACKAGE_ID: MMDVM_HBlink | ||||
| GROUP_HANGTIME: 5 | ||||
| OPTIONS: | ||||
| USE_ACL: True | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: PERMIT:ALL | ||||
| TGID_TS2_ACL: PERMIT:ALL | ||||
| @ -120,7 +120,9 @@ STALE_DAYS: 7 | ||||
| # | ||||
| # ACLs: | ||||
| # OpenBridge does not 'register', so registration ACL is meaningless. | ||||
| # OpenBridge passes all traffic on TS1, so there is only 1 TGID ACL. | ||||
| # Proper OpenBridge passes all traffic on TS1. | ||||
| # HBlink can extend OPB to use both slots for unit calls only. | ||||
| # Setting "BOTH_SLOTS" True ONLY affects unit traffic! | ||||
| # Otherwise ACLs work as described in the global stanza | ||||
| [OBP-1] | ||||
| MODE: OPENBRIDGE | ||||
| @ -131,6 +133,7 @@ NETWORK_ID: 3129100 | ||||
| PASSPHRASE: password | ||||
| TARGET_IP: 1.2.3.4 | ||||
| TARGET_PORT: 62035 | ||||
| BOTH_SLOTS: True | ||||
| USE_ACL: True | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_ACL: PERMIT:ALL | ||||
| @ -206,36 +209,4 @@ OPTIONS: | ||||
| USE_ACL: True | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: PERMIT:ALL | ||||
| TGID_TS2_ACL: PERMIT:ALL | ||||
| 
 | ||||
| [XLX-1] | ||||
| MODE: XLXPEER | ||||
| ENABLED: True | ||||
| LOOSE: True | ||||
| EXPORT_AMBE: False | ||||
| IP:  | ||||
| PORT: 54002 | ||||
| MASTER_IP: 172.16.1.1 | ||||
| MASTER_PORT: 62030 | ||||
| PASSPHRASE: passw0rd | ||||
| CALLSIGN: W1ABC | ||||
| RADIO_ID: 312000 | ||||
| RX_FREQ: 449000000 | ||||
| TX_FREQ: 444000000 | ||||
| TX_POWER: 25 | ||||
| COLORCODE: 1 | ||||
| SLOTS: 1 | ||||
| LATITUDE: 38.0000 | ||||
| LONGITUDE: -095.0000 | ||||
| HEIGHT: 75 | ||||
| LOCATION: Anywhere, USA | ||||
| DESCRIPTION: This is a cool repeater | ||||
| URL: www.w1abc.org | ||||
| SOFTWARE_ID: 20170620 | ||||
| PACKAGE_ID: MMDVM_HBlink | ||||
| GROUP_HANGTIME: 5 | ||||
| XLXMODULE: 4004 | ||||
| USE_ACL: True | ||||
| SUB_ACL: DENY:1 | ||||
| TGID_TS1_ACL: PERMIT:ALL | ||||
| TGID_TS2_ACL: PERMIT:ALL | ||||
| TGID_TS2_ACL: PERMIT:ALL | ||||
							
								
								
									
										42
									
								
								hblink.py
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								hblink.py
									
									
									
									
									
								
							| @ -160,8 +160,8 @@ class OPENBRIDGE(DatagramProtocol): | ||||
|                 _stream_id = _data[16:20] | ||||
|                 #logger.debug('(%s) DMRD - Seqence: %s, RF Source: %s, Destination ID: %s', self._system, int_id(_seq), int_id(_rf_src), int_id(_dst_id)) | ||||
| 
 | ||||
|                 # Sanity check for OpenBridge -- all calls must be on Slot 1 | ||||
|                 if _slot != 1: | ||||
|                 # Sanity check for OpenBridge -- all calls must be on Slot 1 for Brandmeister or DMR+. Other HBlinks can process timeslot on OPB if the flag is set | ||||
|                 if _slot != 1 and not self._config['BOTH_SLOTS'] and not _call_type == 'unit': | ||||
|                     logger.error('(%s) OpenBridge packet discarded because it was not received on slot 1. SID: %s, TGID %s', self._system, int_id(_rf_src), int_id(_dst_id)) | ||||
|                     return | ||||
| 
 | ||||
| @ -223,13 +223,6 @@ class HBSYSTEM(DatagramProtocol): | ||||
|             self.datagramReceived = self.peer_datagramReceived | ||||
|             self.dereg = self.peer_dereg | ||||
| 
 | ||||
|         elif self._config['MODE'] == 'XLXPEER': | ||||
|             self._stats = self._config['XLXSTATS'] | ||||
|             self.send_system = self.send_master | ||||
|             self.maintenance_loop = self.peer_maintenance_loop | ||||
|             self.datagramReceived = self.peer_datagramReceived | ||||
|             self.dereg = self.peer_dereg | ||||
| 
 | ||||
|     def startProtocol(self): | ||||
|         # Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds | ||||
|         self._system_maintenance = task.LoopingCall(self.maintenance_loop) | ||||
| @ -289,29 +282,6 @@ class HBSYSTEM(DatagramProtocol): | ||||
|         # KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!! | ||||
|         # logger.debug('(%s) TX Packet to %s:%s -- %s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'], ahex(_packet)) | ||||
| 
 | ||||
|     def send_xlxmaster(self, radio, xlx, mastersock): | ||||
|         radio3 = int.from_bytes(radio, 'big').to_bytes(3, 'big') | ||||
|         radio4 = int.from_bytes(radio, 'big').to_bytes(4, 'big') | ||||
|         xlx3   = xlx.to_bytes(3, 'big') | ||||
|         streamid = randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big') | ||||
|         # Wait for .5 secs for the XLX to log us in | ||||
|         for packetnr in range(5): | ||||
|             if packetnr < 3: | ||||
|                 # First 3 packets, voice start, stream type e1 | ||||
|                 strmtype = 225 | ||||
|                 payload = bytearray.fromhex('4f2e00b501ae3a001c40a0c1cc7dff57d75df5d5065026f82880bd616f13f185890000') | ||||
|             else: | ||||
|                 # Last 2 packets, voice end, stream type e2 | ||||
|                 strmtype = 226 | ||||
|                 payload = bytearray.fromhex('4f410061011e3a781c30a061ccbdff57d75df5d2534425c02fe0b1216713e885ba0000') | ||||
|             packetnr1 = packetnr.to_bytes(1, 'big') | ||||
|             strmtype1 = strmtype.to_bytes(1, 'big') | ||||
|             _packet = b''.join([DMRD, packetnr1, radio3, xlx3, radio4, strmtype1, streamid, payload]) | ||||
|             self.transport.write(_packet, mastersock) | ||||
|             # KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!! | ||||
|             #logger.debug('(%s) XLX Module Change Packet: %s', self._system, ahex(_packet)) | ||||
|         return | ||||
| 
 | ||||
|     def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): | ||||
|         pass | ||||
| 
 | ||||
| @ -656,11 +626,7 @@ class HBSYSTEM(DatagramProtocol): | ||||
|                             self._stats['CONNECTION'] = 'YES' | ||||
|                             self._stats['CONNECTED'] = time() | ||||
|                             logger.info('(%s) Connection to Master Completed', self._system) | ||||
|                             # If we are an XLX, send the XLX module request here. | ||||
|                             if self._config['MODE'] == 'XLXPEER': | ||||
|                                 self.send_xlxmaster(self._config['RADIO_ID'], int(4000), self._config['MASTER_SOCKADDR']) | ||||
|                                 self.send_xlxmaster(self._config['RADIO_ID'], self._config['XLXMODULE'], self._config['MASTER_SOCKADDR']) | ||||
|                                 logger.info('(%s) Sending XLX Module request', self._system) | ||||
|                              | ||||
|                     else: | ||||
|                         self._stats['CONNECTION'] = 'NO' | ||||
|                         logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system) | ||||
| @ -797,7 +763,7 @@ if __name__ == '__main__': | ||||
|     if cli_args.LOG_LEVEL: | ||||
|         CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL | ||||
|     logger = log.config_logging(CONFIG['LOGGER']) | ||||
|     logger.info('\n\nCopyright (c) 2013, 2014, 2015, 2016, 2018, 2019\n\tThe Regents of the K0USY Group. All rights reserved.\n') | ||||
|     logger.info('\n\nCopyright (c) 2013, 2014, 2015, 2016, 2018, 2019, 2020\n\tThe Regents of the K0USY Group. All rights reserved.\n') | ||||
|     logger.debug('(GLOBAL) Logging system started, anything from here on gets logged') | ||||
| 
 | ||||
|     # Set up the signal handler | ||||
|  | ||||
							
								
								
									
										16
									
								
								rules-750.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								rules-750.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,16 @@ | ||||
| BRIDGES = { | ||||
|     '1/2': [ | ||||
|             {'SYSTEM': '444.750',    'TS': 1, 'TGID': 2,    'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE',   'ON': [], 'OFF': [], 'RESET': []}, | ||||
|             {'SYSTEM': 'OBP',        'TS': 1, 'TGID': 2,    'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE',   'ON': [], 'OFF': [], 'RESET': []} | ||||
|         ], | ||||
|     'KANSAS': [ | ||||
|             {'SYSTEM': '444.750',    'TS': 2, 'TGID': 3120, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE',   'ON': [], 'OFF': [], 'RESET': []}, | ||||
|             {'SYSTEM': 'OBP',        'TS': 1, 'TGID': 3120, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE',   'ON': [], 'OFF': [], 'RESET': []} | ||||
|         ] | ||||
| } | ||||
| 
 | ||||
| UNIT = ['444.750', 'OBP'] | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     from pprint import pprint | ||||
|     pprint(BRIDGES) | ||||
							
								
								
									
										16
									
								
								rules-800.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								rules-800.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,16 @@ | ||||
| BRIDGES = { | ||||
|     '1/2': [ | ||||
|             {'SYSTEM': '444.800',    'TS': 1, 'TGID': 2,    'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE',   'ON': [], 'OFF': [], 'RESET': []}, | ||||
|             {'SYSTEM': 'OBP',        'TS': 1, 'TGID': 2,    'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE',   'ON': [], 'OFF': [], 'RESET': []} | ||||
|         ], | ||||
|     'KANSAS': [ | ||||
|             {'SYSTEM': '444.800',    'TS': 2, 'TGID': 3120, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE',   'ON': [], 'OFF': [], 'RESET': []}, | ||||
|             {'SYSTEM': 'OBP',        'TS': 1, 'TGID': 3120, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE',   'ON': [], 'OFF': [], 'RESET': []} | ||||
|         ] | ||||
| } | ||||
| 
 | ||||
| UNIT = ["444.800", "OBP"] | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     from pprint import pprint | ||||
|     pprint(BRIDGES) | ||||
| @ -47,6 +47,18 @@ BRIDGES = { | ||||
|         ] | ||||
| } | ||||
| 
 | ||||
| ''' | ||||
| list the names of each system that should bridge unit to unit (individual) calls. | ||||
| ''' | ||||
| 
 | ||||
| UNIT = ['ONE', 'TWO'] | ||||
| 
 | ||||
| ''' | ||||
| This is for testing the syntax of the file. It won't eliminate all errors, but running this file | ||||
| like it were a Python program itself will tell you if the syntax is correct! | ||||
| ''' | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     from pprint import pprint | ||||
|     pprint(BRIDGES) | ||||
|     print(UNIT) | ||||
|  | ||||
							
								
								
									
										0
									
								
								voice_lib.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								voice_lib.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user