diff --git a/README.md b/README.md index ca6221b..52037a1 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ $ hytera-homebrew-bridge $ git clone https://github.com/OK-DMR/Hytera_Homebrew_Bridge.git # change path into cloned repo $ cd Hytera_Homebrew_Bridge -# You can use either settings.ini.default (all configuration params) or settings.ini.minimal.default (only required params) -$ cp settings.ini.default settings.ini +# You can use either settings.default.ini (all configuration params) or settings.minimal.default.ini (only required params) +$ cp settings.default.ini settings.ini # install current directory to local site-packages in editable mode $ python3 -m pip install -e . # run hytera-homebrew-bridge with params @@ -53,6 +53,9 @@ $ hytera-homebrew-bridge - A: Check if SNMP port is set to 161 in `Conventional > General Settings > Network` section `SNMP` at the bottom - Q: I'm not getting the upstream connection and/or I'm seeing a lot of logs similar to "MMDVMProtocol - Sending Login Request" - A: This is usually misconfiguration of Hytera repeater, if you do not see any logs with 'RDAC' or the long packet with 'REPEATER SNMP CONFIGURATION' info. In such cases you should check if the Hytera repeater is programmed correctly as slave and the IP/ports do match the HHB startup log saying 'Hytera Repeater is expected to connect at xxx.xxx.xxx.xxx' +- Q: where is hytera-homebrew-bridge.py launcher script? + - A: It was replaced by script installed by python environment, now you can use just `hytera-homebrew-bridge` command instead + - A: You can use `python -m okdmr.hhb.hytera_homebrew_bridge ` as an alternative command ---- diff --git a/okdmr/hhb/__main__.py b/okdmr/hhb/__main__.py new file mode 100644 index 0000000..1b7f4c8 --- /dev/null +++ b/okdmr/hhb/__main__.py @@ -0,0 +1,75 @@ +import asyncio +import importlib.util +import logging.config +import os +import sys +from signal import SIGINT, SIGTERM + +from okdmr.hhb.hytera_homebrew_bridge import HyteraHomebrewBridge + + +def main(): + logger_configured: bool = False + if len(sys.argv) > 2: + if os.path.isfile(path=sys.argv[2]): + logging.config.fileConfig(fname=sys.argv[2]) + logger_configured = True + else: + logging.getLogger().error(f"logging ini file not valid {sys.argv[2]}") + exit() + if not logger_configured: + logging.basicConfig( + level=logging.DEBUG, + format="%(levelname)s - %(asctime)s - %(name)s - %(message)s", + ) + logging.getLogger(name="puresnmp.transport").setLevel(logging.WARN) + + mainlog = logging.getLogger(name="hytera-homebrew-bridge") + + mainlog.info("Hytera Homebrew Bridge") + + if len(sys.argv) < 2: + mainlog.error( + "use as hytera-homebrew-bridge " + ) + mainlog.error( + "If you do not have the settings.ini file, you can obtain one here: " + "https://github.com/OK-DMR/Hytera_Homebrew_Bridge/blob/master/settings.ini.default" + ) + exit(1) + + uvloop_spec = importlib.util.find_spec(name="uvloop") + if uvloop_spec: + # noinspection PyUnresolvedReferences + import uvloop + + uvloop.install() + + # suppress puresnmp_plugins experimental warning + if not sys.warnoptions: + import warnings + + warnings.filterwarnings( + message="Experimental SNMPv1 support", category=UserWarning, action="ignore" + ) + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop=loop) + # order is IMPORTANT, various asyncio object are created at bridge init + # and those must be created after the main loop is created + bridge: HyteraHomebrewBridge = HyteraHomebrewBridge(settings_ini_path=sys.argv[1]) + if os.name != "nt": + for signal in [SIGINT, SIGTERM]: + loop.add_signal_handler(signal, bridge.stop_running) + + try: + loop.run_until_complete(future=bridge.go()) + loop.run_forever() + except BaseException as e: + mainlog.exception(msg="", exc_info=e) + finally: + mainlog.info("Hytera Homebrew Bridge Ended") + + +if __name__ == "__main__": + main() diff --git a/okdmr/hhb/callback_interface.py b/okdmr/hhb/callback_interface.py index 6b0a609..1e52e38 100644 --- a/okdmr/hhb/callback_interface.py +++ b/okdmr/hhb/callback_interface.py @@ -1,3 +1,7 @@ class CallbackInterface: - async def homebrew_connect(self, ip: str) -> None: + """ + This interface doesn't have other purpose than code de-duplication + """ + + async def homebrew_connect(self, ip: str, port: int) -> None: pass diff --git a/okdmr/hhb/custom_bridge_datagram_protocol.py b/okdmr/hhb/custom_bridge_datagram_protocol.py index 1716173..d4c0e85 100644 --- a/okdmr/hhb/custom_bridge_datagram_protocol.py +++ b/okdmr/hhb/custom_bridge_datagram_protocol.py @@ -1,21 +1,43 @@ #!/usr/bin/env python3 from asyncio import protocols +from okdmr.dmrlib.utils.logging_trait import LoggingTrait + from okdmr.hhb.settings import BridgeSettings from okdmr.hhb.snmp import SNMP class CustomBridgeDatagramProtocol(protocols.DatagramProtocol, LoggingTrait): + """ + Code de-duplication + """ + def __init__(self, settings: BridgeSettings) -> None: + """ + + :param settings: + """ super().__init__() self.settings = settings async def hytera_repeater_obtain_snmp( self, address: tuple, force: bool = False ) -> None: + """ + + :param address: + :param force: + :return: + """ self.settings.hytera_repeater_ip = address[0] - if self.settings.snmp_enabled: - if force or not self.settings.hytera_snmp_data.get(address[0]): - await SNMP().walk_ip(address, self.settings) + if self.settings.snmp_enabled and ( + force or not self.settings.hytera_snmp_data.get(address[0]) + ): + await SNMP().walk_ip(address, self.settings) else: - self.log_warning("SNMP is disabled") + self.log_warning( + f"SNMP is disabled or not available " + f"snmp_enabled:{self.settings.snmp_enabled} " + f"force:{force} " + f"hytera_snmp_data:{address[0] in self.settings.hytera_snmp_data}" + ) diff --git a/okdmr/hhb/hytera_homebrew_bridge.py b/okdmr/hhb/hytera_homebrew_bridge.py index 49174b8..a61d19a 100755 --- a/okdmr/hhb/hytera_homebrew_bridge.py +++ b/okdmr/hhb/hytera_homebrew_bridge.py @@ -1,14 +1,11 @@ #!/usr/bin/env python3 import asyncio -import importlib.util -import logging.config -import os import socket -import sys from asyncio import AbstractEventLoop, Queue -from signal import SIGINT, SIGTERM from typing import Optional, Dict +from okdmr.hhb.callback_interface import CallbackInterface +from okdmr.hhb.hytera_mmdvm_translator import HyteraMmdvmTranslator from okdmr.hhb.hytera_protocols import ( HyteraP2PProtocol, HyteraDMRProtocol, @@ -16,9 +13,6 @@ ) from okdmr.hhb.mmdvm_protocol import MMDVMProtocol from okdmr.hhb.settings import BridgeSettings -from okdmr.hhb.hytera_mmdvm_translator import HyteraMmdvmTranslator - -from okdmr.hhb.callback_interface import CallbackInterface class HyteraRepeater(CallbackInterface): @@ -56,8 +50,8 @@ def __init__(self, ip: str, settings: BridgeSettings, asyncloop: AbstractEventLo hytera_repeater_ip=self.ip, ) - def homebrew_connection_lost(self, ip: str) -> None: - asyncio.run(self.homebrew_connect(ip=ip)) + def homebrew_connection_lost(self, ip: str, port: int) -> None: + asyncio.run(self.homebrew_connect(ip=ip, port=port)) async def hytera_dmr_connect(self) -> None: (transport, _) = await self.loop.create_datagram_endpoint( @@ -65,15 +59,16 @@ async def hytera_dmr_connect(self) -> None: sock=self.settings.hytera_repeater_data[self.ip].dmr_socket, ) - async def homebrew_connect(self, ip: str) -> None: + async def homebrew_connect(self, ip: str, port: int) -> None: incorrect_config_params = self.settings.get_incorrect_configurations(ip) if len(incorrect_config_params) > 0: self.homebrew_protocol.log_error( "Current configuration is not valid for connection" ) - for triplet in incorrect_config_params: + self.settings.print_settings() + for param, current_value, error_message in incorrect_config_params: self.homebrew_protocol.log_error( - f"PARAM: {triplet[0]} CURRENT_VALUE: {triplet[1]} MESSAGE: {triplet[2]}" + f"PARAM: {param} CURRENT_VALUE: {current_value} MESSAGE: {error_message}" ) return @@ -130,23 +125,30 @@ async def go(self) -> None: if not self.settings.hytera_disable_rdac: await self.hytera_rdac_connect() - async def homebrew_connect(self, ip: str) -> None: + async def homebrew_connect(self, ip: str, port: int) -> None: if not self.repeaters.get(ip): self.repeaters[ip] = HyteraRepeater( ip=ip, settings=self.settings, asyncloop=self.loop ) await self.repeaters[ip].go() - await self.repeaters[ip].homebrew_connect(ip) + await self.repeaters[ip].homebrew_connect(ip=ip, port=port) async def hytera_p2p_connect(self) -> None: - # P2P/IPSC Service address + """ + Start P2P/IPSC Service handler + :return: + """ await self.loop.create_datagram_endpoint( lambda: self.hytera_p2p_protocol, local_addr=(self.settings.ipsc_ip, self.settings.p2p_port), ) async def hytera_rdac_connect(self) -> None: + """ + Start RDAC service handler + :return: + """ await self.loop.create_datagram_endpoint( lambda: self.hytera_rdac_protocol, local_addr=(self.settings.ipsc_ip, self.settings.rdac_port), @@ -161,54 +163,3 @@ def stop_running(self) -> None: for task in asyncio.all_tasks(): task.cancel() task.done() - - -if __name__ == "__main__": - loggerConfigured: bool = False - if len(sys.argv) > 2: - if os.path.isfile(sys.argv[2]): - logging.config.fileConfig(sys.argv[2]) - loggerConfigured = True - if not loggerConfigured: - logging.basicConfig( - level=logging.DEBUG, - format="%(levelname)s - %(asctime)s - %(name)s - %(message)s", - ) - logging.getLogger("puresnmp.transport").setLevel(logging.WARN) - - mainlog = logging.getLogger("hytera-homebrew-bridge.py") - - mainlog.info("Hytera Homebrew Bridge") - - if len(sys.argv) < 2: - mainlog.error( - "use as hytera-homebrew-bridge " - ) - mainlog.error( - "If you do not have the settings.ini file, you can obtain one here: " - "https://github.com/OK-DMR/Hytera_Homebrew_Bridge/blob/master/settings.ini.default" - ) - exit(1) - - uvloop_spec = importlib.util.find_spec("uvloop") - if uvloop_spec: - import uvloop - - uvloop.install() - - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - # order is IMPORTANT, various asyncio object are created at bridge init - # and those must be created after the main loop is created - bridge: HyteraHomebrewBridge = HyteraHomebrewBridge(sys.argv[1]) - if os.name != "nt": - for signal in [SIGINT, SIGTERM]: - loop.add_signal_handler(signal, bridge.stop_running) - - try: - loop.run_until_complete(bridge.go()) - loop.run_forever() - except BaseException as e: - mainlog.exception(e) - finally: - mainlog.info("Hytera Homebrew Bridge Ended") diff --git a/okdmr/hhb/hytera_mmdvm_translator.py b/okdmr/hhb/hytera_mmdvm_translator.py index 2713eb7..c794730 100644 --- a/okdmr/hhb/hytera_mmdvm_translator.py +++ b/okdmr/hhb/hytera_mmdvm_translator.py @@ -55,8 +55,8 @@ async def translate_from_hytera(self): IpSiteConnectProtocol.SlotTypes.slot_type_wakeup_request, IpSiteConnectProtocol.SlotTypes.slot_type_sync, ]: - print( - "Slot Type", packet.slot_type, "Frame Type", packet.frame_type + self.log_info( + f"Slot Type: {packet.slot_type}, Frame Type: {packet.frame_type}" ) burst.debug() self.queue_hytera_to_translate.task_done() diff --git a/okdmr/hhb/hytera_protocols.py b/okdmr/hhb/hytera_protocols.py index a87bfff..4f64e00 100644 --- a/okdmr/hhb/hytera_protocols.py +++ b/okdmr/hhb/hytera_protocols.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import asyncio +import logging import socket from asyncio import transports, Queue from binascii import hexlify @@ -71,12 +72,13 @@ def handle_registration(self, data: bytes, address: Tuple[str, int]) -> None: asyncio.gather(self.hytera_repeater_obtain_snmp(address)) self.settings.hytera_is_registered[address[0]] = True + self.log_info(f"handle_registration for {address} launching homebrew_connect") asyncio.get_running_loop().create_task( - self.repeater_accepted_callback.homebrew_connect(address[0]) + self.repeater_accepted_callback.homebrew_connect(address[0], address[1]) ) def handle_rdac_request(self, data: bytes, address: Tuple[str, int]) -> None: - if not self.settings.hytera_is_registered.get(address[0]): + if not self.settings.is_repeater_registered(address[0]): self.log_debug( f"Rejecting RDAC request for not-registered repeater {address[0]}" ) @@ -103,7 +105,7 @@ def handle_rdac_request(self, data: bytes, address: Tuple[str, int]) -> None: @staticmethod def get_redirect_packet(data: bytearray, target_port: int): - print(f"Providing redirect packet to port {target_port}") + logging.getLogger().debug(f"Providing redirect packet to port {target_port}") data = data[: len(data) - 1] data[4] = 0x0B data[12] = 0xFF @@ -115,7 +117,7 @@ def get_redirect_packet(data: bytearray, target_port: int): return data def handle_dmr_request(self, data: bytes, address: Tuple[str, int]) -> None: - if not self.settings.hytera_is_registered.get(address[0]): + if not self.settings.is_repeater_registered(address[0]): self.log_debug( f"Rejecting DMR request for not-registered repeater {address[0]}" ) @@ -485,8 +487,9 @@ def step13(self, data: bytes, address: Tuple[str, int]) -> None: self.log_debug("rdac completed identification") self.settings.print_repeater_configuration() asyncio.gather(self.hytera_repeater_obtain_snmp(address)) + self.log_info(f"RDAC step13 for {address} launching homebrew_connect") asyncio.get_running_loop().create_task( - self.rdac_completed_callback.homebrew_connect(address[0]) + self.rdac_completed_callback.homebrew_connect(address[0], address[1]) ) def step14(self, data: bytes, address: Tuple[str, int]) -> None: @@ -509,11 +512,11 @@ def datagram_received(self, data: bytes, addr: Tuple[str, int]) -> None: if len(data) == 1 and self.step[addr[0]] != 14: if self.step[addr[0]] == 4: - self.log_warning( + self.log_error( "check repeater zone programming, if Digital IP" "Multi-Site Connect mode allows data pass from timeslots" ) - self.log_warning( + self.log_error( "restart process if response is protocol reset and current step is not 14" ) self.step[addr[0]] = 0 @@ -541,7 +544,7 @@ def __init__( self.queue_incoming = queue_incoming self.queue_outgoing = queue_outgoing self.ip: str = hytera_repeater_ip - print( + self.log_info( f"HyteraDMRProtocol on creation expecting ip {self.ip} and port {self.settings.get_repeater_dmr_port(self.ip)}" ) diff --git a/okdmr/hhb/mmdvm_utils.py b/okdmr/hhb/mmdvm_utils.py index c3c26b9..958d808 100644 --- a/okdmr/hhb/mmdvm_utils.py +++ b/okdmr/hhb/mmdvm_utils.py @@ -9,10 +9,6 @@ from okdmr.kaitai.hytera.ip_site_connect_protocol import IpSiteConnectProtocol -def get_mmdvm_timeslot(mmdvmdata: Mmdvm2020.TypeDmrData) -> int: - return 1 if mmdvmdata.slot_no == Mmdvm2020.Timeslots.timeslot_1 else 2 - - def get_ipsc_timeslot(ipscdata: IpSiteConnectProtocol) -> int: return ( 1 if ipscdata.timeslot_raw == IpSiteConnectProtocol.Timeslots.timeslot_1 else 2 diff --git a/okdmr/hhb/packet_format.py b/okdmr/hhb/packet_format.py index a109dc8..7d2df94 100644 --- a/okdmr/hhb/packet_format.py +++ b/okdmr/hhb/packet_format.py @@ -4,7 +4,7 @@ import zlib from binascii import hexlify -from dmr_utils3.decode import voice_head_term +from okdmr.dmrlib.etsi.layer2.burst import Burst from okdmr.dmrlib.utils.bits_bytes import byteswap_bytes from okdmr.kaitai.homebrew.mmdvm2020 import Mmdvm2020 from okdmr.kaitai.hytera.hytera_radio_network_protocol import HyteraRadioNetworkProtocol @@ -163,10 +163,9 @@ def format_ipsc_data(ipsc: IpSiteConnectProtocol) -> str: or ipsc.slot_type == IpSiteConnectProtocol.SlotTypes.slot_type_terminator_with_lc ): - lc = voice_head_term(byteswap_bytes(ipsc.ipsc_payload)) - dmr_data_type: str = dmr_data_types.get( - int(lc["DTYPE"][0]), "DMR DT %d" % int(lc["DTYPE"][0]) - ) + + b: Burst = Burst.from_hytera_ipsc(ipsc) + dmr_data_type: str = b.data_type.name else: dmr_data_type: str = "DMR DT ?" diff --git a/okdmr/hhb/settings.py b/okdmr/hhb/settings.py index 5fb373f..d0b4a35 100644 --- a/okdmr/hhb/settings.py +++ b/okdmr/hhb/settings.py @@ -74,7 +74,6 @@ class BridgeSettings(LoggingTrait): """ def __init__(self, filepath: str = None, filedata: str = None) -> None: - if not filepath and not filedata: raise SystemError( "Cannot init BridgeSettings without filepath and filedata, at least one must be provided" @@ -271,6 +270,9 @@ def get_incorrect_configurations(self, ip: str) -> list: return rtn + def is_repeater_registered(self, repeater_ip: str) -> bool: + return repeater_ip in self.hytera_is_registered.keys() + def print_settings(self) -> None: self.log_info("Settings Loaded") self.log_info( diff --git a/okdmr/hhb/snmp.py b/okdmr/hhb/snmp.py index 5a98a58..f513c8e 100755 --- a/okdmr/hhb/snmp.py +++ b/okdmr/hhb/snmp.py @@ -3,9 +3,9 @@ import sys import asyncio + import puresnmp from okdmr.dmrlib.utils.logging_trait import LoggingTrait -from puresnmp.exc import Timeout from okdmr.hhb.settings import BridgeSettings from okdmr.hhb.utils import octet_string_to_utf8 @@ -13,39 +13,39 @@ class SNMP(LoggingTrait): # in milli-volts (V * 1000) - OID_PSU_VOLTAGE: str = "iso.3.6.1.4.1.40297.1.2.1.2.1.0" + OID_PSU_VOLTAGE: str = "1.3.6.1.4.1.40297.1.2.1.2.1.0" # in milli-celsius (C * 1000) - OID_PA_TEMPERATURE: str = "iso.3.6.1.4.1.40297.1.2.1.2.2.0" + OID_PA_TEMPERATURE: str = "1.3.6.1.4.1.40297.1.2.1.2.2.0" # voltage ratio on the TX in dB - OID_VSWR: str = "iso.3.6.1.4.1.40297.1.2.1.2.4.0" + OID_VSWR: str = "1.3.6.1.4.1.40297.1.2.1.2.4.0" # Forward power in milli-watt - OID_TX_FWD_POWER: str = "iso.3.6.1.4.1.40297.1.2.1.2.5.0" + OID_TX_FWD_POWER: str = "1.3.6.1.4.1.40297.1.2.1.2.5.0" # Reflected power in milli-watt - OID_TX_REF_POWER: str = "iso.3.6.1.4.1.40297.1.2.1.2.6.0" - OID_RSSI_TS1: str = "iso.3.6.1.4.1.40297.1.2.1.2.9.0" - OID_RSSI_TS2: str = "iso.3.6.1.4.1.40297.1.2.1.2.10.0" + OID_TX_REF_POWER: str = "1.3.6.1.4.1.40297.1.2.1.2.6.0" + OID_RSSI_TS1: str = "1.3.6.1.4.1.40297.1.2.1.2.9.0" + OID_RSSI_TS2: str = "1.3.6.1.4.1.40297.1.2.1.2.10.0" - OID_REPEATER_MODEL: str = "iso.3.6.1.4.1.40297.1.2.4.1.0" - OID_MODEL_NUMBER: str = "iso.3.6.1.4.1.40297.1.2.4.2.0" + OID_REPEATER_MODEL: str = "1.3.6.1.4.1.40297.1.2.4.1.0" + OID_MODEL_NUMBER: str = "1.3.6.1.4.1.40297.1.2.4.2.0" # string - OID_FIRMWARE_VERSION: str = "iso.3.6.1.4.1.40297.1.2.4.3.0" + OID_FIRMWARE_VERSION: str = "1.3.6.1.4.1.40297.1.2.4.3.0" # Radio Data Version, string - OID_RCDB_VERSION: str = "iso.3.6.1.4.1.40297.1.2.4.4.0" - OID_SERIAL_NUMBER: str = "iso.3.6.1.4.1.40297.1.2.4.5.0" + OID_RCDB_VERSION: str = "1.3.6.1.4.1.40297.1.2.4.4.0" + OID_SERIAL_NUMBER: str = "1.3.6.1.4.1.40297.1.2.4.5.0" # callsign - OID_RADIO_ALIAS: str = "iso.3.6.1.4.1.40297.1.2.4.6.0" + OID_RADIO_ALIAS: str = "1.3.6.1.4.1.40297.1.2.4.6.0" # integer - OID_RADIO_ID: str = "iso.3.6.1.4.1.40297.1.2.4.7.0" + OID_RADIO_ID: str = "1.3.6.1.4.1.40297.1.2.4.7.0" # digital=0, analog=1, mixed=2 - OID_CUR_CHANNEL_MODE: str = "iso.3.6.1.4.1.40297.1.2.4.8.0" - OID_CUR_CHANNEL_NAME: str = "iso.3.6.1.4.1.40297.1.2.4.9.0" + OID_CUR_CHANNEL_MODE: str = "1.3.6.1.4.1.40297.1.2.4.8.0" + OID_CUR_CHANNEL_NAME: str = "1.3.6.1.4.1.40297.1.2.4.9.0" # Hz - OID_TX_FREQUENCE: str = "iso.3.6.1.4.1.40297.1.2.4.10.0" + OID_TX_FREQUENCE: str = "1.3.6.1.4.1.40297.1.2.4.10.0" # Hz - OID_RX_FREQUENCE: str = "iso.3.6.1.4.1.40297.1.2.4.11.0" + OID_RX_FREQUENCE: str = "1.3.6.1.4.1.40297.1.2.4.11.0" # receive=0, transmit=1 - OID_WORK_STATUS: str = "iso.3.6.1.4.1.40297.1.2.4.12.0" - OID_CUR_ZONE_ALIAS: str = "iso.3.6.1.4.1.40297.1.2.4.13.0" + OID_WORK_STATUS: str = "1.3.6.1.4.1.40297.1.2.4.12.0" + OID_CUR_ZONE_ALIAS: str = "1.3.6.1.4.1.40297.1.2.4.13.0" READABLE_LABELS = { OID_PSU_VOLTAGE: ("PSU Voltage", "%d mV"), @@ -138,8 +138,8 @@ async def walk_ip( try: for oid in SNMP.ALL_KNOWN: - raw_oid = oid.replace("iso", "1") - snmp_result = await client.get(oid=raw_oid) + snmp_result = await client.get(oid=oid) + if oid in SNMP.ALL_STRINGS: snmp_result = octet_string_to_utf8(str(snmp_result, "utf8")) elif oid in SNMP.ALL_FLOATS: @@ -148,7 +148,7 @@ async def walk_ip( is_success = True except SystemError: self.log_error("SNMP failed to obtain repeater info") - except Timeout: + except asyncio.Timeout: if first_try: self.log_debug( "Failed with SNMP family %s, trying with %s as well" @@ -173,7 +173,7 @@ async def walk_ip( return settings_storage.hytera_snmp_data[address[0]] def print_snmp_data(self, settings_storage: BridgeSettings, address: tuple): - self.log_debug( + self.log_info( "-------------- REPEATER SNMP CONFIGURATION ----------------------------" ) # ip address longest 15 letters (255.255.255.255) @@ -185,7 +185,7 @@ def print_snmp_data(self, settings_storage: BridgeSettings, address: tuple): if len(address) == 2: # log IP address first - self.log_debug( + self.log_info( "%s| %s" % ( str("IP Address").ljust(longest_label + 5), @@ -197,14 +197,14 @@ def print_snmp_data(self, settings_storage: BridgeSettings, address: tuple): print_settings = SNMP.READABLE_LABELS.get(key) if print_settings: value = settings_storage.hytera_snmp_data[address[0]].get(key) - self.log_debug( + self.log_info( "%s| %s" % ( str(print_settings[0]).ljust(longest_label + 5), print_settings[1] % value, ) ) - self.log_debug( + self.log_info( "-------------- REPEATER SNMP CONFIGURATION ----------------------------" ) @@ -214,7 +214,18 @@ def print_snmp_data(self, settings_storage: BridgeSettings, address: tuple): print("use as snmp.py ") exit(1) - logging.basicConfig(level=logging.NOTSET) + logging.basicConfig(level=logging.INFO) + # suppress puresnmp verbose/debug logs + logging.getLogger("puresnmp.transport").setLevel(logging.INFO) + # suppress puresnmp_plugins experimental warning + if not sys.warnoptions: + import warnings + + warnings.filterwarnings( + message="Experimental SNMPv1 support", category=UserWarning, action="ignore" + ) settings: BridgeSettings = BridgeSettings(filedata=BridgeSettings.MINIMAL_SETTINGS) - asyncio.gather(SNMP().walk_ip((sys.argv[1], 0), settings_storage=settings)) + _target: SNMP = SNMP() + _target_address: tuple = (sys.argv[1], 0) + asyncio.run(_target.walk_ip(_target_address, settings_storage=settings)) diff --git a/okdmr/tests/hhb/test_callback_interface.py b/okdmr/tests/hhb/test_callback_interface.py index bb39f5b..ae4cbc2 100644 --- a/okdmr/tests/hhb/test_callback_interface.py +++ b/okdmr/tests/hhb/test_callback_interface.py @@ -1,6 +1,9 @@ from okdmr.hhb.callback_interface import CallbackInterface +from okdmr.hhb.hytera_homebrew_bridge import HyteraRepeater, HyteraHomebrewBridge def test_do_succ(): c = CallbackInterface() assert isinstance(c, CallbackInterface) + assert issubclass(HyteraRepeater, CallbackInterface) + assert issubclass(HyteraHomebrewBridge, CallbackInterface) diff --git a/pyproject.toml b/pyproject.toml index 47bc99c..e99c960 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ test = [ ] [project.scripts] -hytera-homebrew-bridge = "okdmr.hhb.hytera_homebrew_bridge:__main__" +hytera-homebrew-bridge = "okdmr.hhb.__main__:main" [project.urls] repository = "https://github.com/OK-DMR/Hytera_Homebrew_Bridge" diff --git a/settings.ini.default b/settings.ini.default index 346b938..b202a2d 100644 --- a/settings.ini.default +++ b/settings.ini.default @@ -33,6 +33,7 @@ local_port = 0 master_ip = 192.168.1.3 master_port = 62031 password = B3S3CURE +# common homebrew settings, shared by all repeaters # callsign and repeater_dmr_id, if provided will override the one read from repeater callsign = repeater_dmr_id =