import logging import sys import time import aprslib from aprslib.exceptions import LoginError import click import aprsd from aprsd import cli_helper, client, packets from aprsd.aprsd import cli LOG = logging.getLogger("APRSD") @cli.command() @cli_helper.add_options(cli_helper.common_options) @click.option( "--aprs-login", envvar="APRS_LOGIN", show_envvar=True, help="What callsign to send the message from. Defaults to config entry.", ) @click.option( "--aprs-password", envvar="APRS_PASSWORD", show_envvar=True, help="the APRS-IS password for APRS_LOGIN. Defaults to config entry.", ) @click.option( "--no-ack", "-n", is_flag=True, show_default=True, default=False, help="Don't wait for an ack, just sent it to APRS-IS and bail.", ) @click.option( "--wait-response", "-w", is_flag=True, show_default=True, default=False, help="Wait for a response to the message?", ) @click.option("--raw", default=None, help="Send a raw message. Implies --no-ack") @click.argument("tocallsign", required=True) @click.argument("command", nargs=-1, required=True) @click.pass_context @cli_helper.process_standard_options def send_message( ctx, aprs_login, aprs_password, no_ack, wait_response, raw, tocallsign, command, ): """Send a message to a callsign via APRS_IS.""" global got_ack, got_response config = ctx.obj["config"] quiet = ctx.obj["quiet"] if not aprs_login: if not config.exists("aprs.login"): click.echo("Must set --aprs_login or APRS_LOGIN") ctx.exit(-1) return else: config["aprs"]["login"] = aprs_login if not aprs_password: if not config.exists("aprs.password"): click.echo("Must set --aprs-password or APRS_PASSWORD") ctx.exit(-1) return else: config["aprs"]["password"] = aprs_password LOG.info(f"APRSD LISTEN Started version: {aprsd.__version__}") if type(command) is tuple: command = " ".join(command) if not quiet: if raw: LOG.info(f"L'{aprs_login}' R'{raw}'") else: LOG.info(f"L'{aprs_login}' To'{tocallsign}' C'{command}'") packets.PacketList(config=config) packets.WatchList(config=config) packets.SeenList(config=config) got_ack = False got_response = False def rx_packet(packet): global got_ack, got_response cl = client.factory.create() packet = cl.decode_packet(packet) packet.log("RX_PKT") # LOG.debug("Got packet back {}".format(packet)) if isinstance(packet, packets.AckPacket): got_ack = True else: got_response = True from_call = packet.from_call our_call = config["aprsd"]["callsign"].lower() ack_pkt = packets.AckPacket( from_call=our_call, to_call=from_call, msgNo=packet.msgNo, ) ack_pkt.send_direct() if got_ack: if wait_response: if got_response: sys.exit(0) else: sys.exit(0) try: client.ClientFactory.setup(config) client.factory.create().client except LoginError: sys.exit(-1) # Send a message # then we setup a consumer to rx messages # We should get an ack back as well as a new message # we should bail after we get the ack and send an ack back for the # message if raw: pkt = packets.Packet(from_call="", to_call="", raw=raw) pkt.send_direct() sys.exit(0) else: pkt = packets.MessagePacket( from_call=aprs_login, to_call=tocallsign, message_text=command, ) pkt.send_direct() if no_ack: sys.exit(0) try: # This will register a packet consumer with aprslib # When new packets come in the consumer will process # the packet aprs_client = client.factory.create().client aprs_client.consumer(rx_packet, raw=False) except aprslib.exceptions.ConnectionDrop: LOG.error("Connection dropped, reconnecting") time.sleep(5) # Force the deletion of the client object connected to aprs # This will cause a reconnect, next time client.get_client() # is called aprs_client.reset()