From dcc3c56120e68a5c49e5ac4561adf8fa98d4cb9c Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Mon, 22 Jun 2020 17:46:26 -0400 Subject: [PATCH 1/4] Update scanner.py --- bc125csv/scanner.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bc125csv/scanner.py b/bc125csv/scanner.py index aad278d..e432f73 100644 --- a/bc125csv/scanner.py +++ b/bc125csv/scanner.py @@ -42,7 +42,7 @@ "732","734","743","754", ] -SUPPORTED_MODELS = ("BC125AT", "UBC125XLT", "UBC126AT") +SUPPORTED_MODELS = ("BC125AT", "UBC125XLT", "UBC126AT", "SR30C") class Channel(object): @@ -100,8 +100,8 @@ class Scanner(serial.Serial, object): (?P\d{1,3}), (?P[^,]{0,16}), (?P\d{5,8}), # 4 decimals, so at least 5 digits - (?PAUTO|AM|FM|NFM), - (?P\d{1,3}), + (?P|AUTO|AM|FM|NFM), + (?P\d{0,3}), (?P-10|-5|0|1|2|3|4|5), (?P0|1), (?P0|1) # no comma! @@ -177,7 +177,7 @@ def get_channel(self, index): "name": data["name"].strip(), "frequency": frequency, "modulation": data["modulation"], - "tqcode": int(data["tq"]), + "tqcode": int(data["tq"] or "0"), "delay": int(data["delay"]), "lockout": data["lockout"] == "1", "priority": data["priority"] == "1", From a7e2eba747ee8bbb6438ff5838ccfb3f77f31774 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Mon, 22 Jun 2020 18:36:07 -0400 Subject: [PATCH 2/4] up --- bc125csv/handler.py | 20 +++++++++-------- bc125csv/scanner.py | 54 +++++++++++++++++++-------------------------- setup.py | 2 +- 3 files changed, 35 insertions(+), 41 deletions(-) diff --git a/bc125csv/handler.py b/bc125csv/handler.py index 3f1fd25..80c7ca2 100644 --- a/bc125csv/handler.py +++ b/bc125csv/handler.py @@ -153,7 +153,7 @@ def format_help(self): dest="noscanner") parser.add_argument("-o", "--output", dest="output") parser.add_argument("-r", "--rate", type=int, dest="rate", - choices=(4800, 9600, 19200, 38400, 57600, 115200), default=9600) + choices=(4800, 9600, 19200, 38400, 57600, 115200)) parser.add_argument("-s", "--sparse", action="store_true", dest="sparse") parser.add_argument("-v", "--verbose", action="store_true", @@ -204,17 +204,16 @@ def get_scanner(self): if not device: sys.exit("No compatible scanner was found.") - if not lookup.is_tty(device): - sys.exit("Found a compatible scanner, but no serial tty.\n" - "Please run the following commands with root privileges:\n" - "modprobe usbserial vendor=0x{0} product=0x{1}" - .format(device.get("ID_VENDOR_ID"), device.get("ID_MODEL_ID"))) - # Make sure device is writable by current user - if not os.access(device.get("DEVNAME", ""), os.W_OK): + if not os.access(device["port"], os.W_OK): sys.exit("Found a compatible scanner, but can not write to it.") - scanner = Scanner(device.get("DEVNAME"), self.params.rate) + # the baud rate can be overriden + baudrate = self.params.rate + if baudrate: + device["baudrate"] = baudrate + + scanner = Scanner(device) try: model = scanner.get_model() @@ -222,6 +221,9 @@ def get_scanner(self): sys.exit("Could not get model name from scanner.\n" "Please try again or reconnect your device.") + if not model in SUPPORTED_MODELS: + sys.exit("Got unsupported model: ", model) + self.print_verbose("Found scanner", model) return scanner diff --git a/bc125csv/scanner.py b/bc125csv/scanner.py index e432f73..99e0452 100644 --- a/bc125csv/scanner.py +++ b/bc125csv/scanner.py @@ -4,14 +4,9 @@ import re import sys -try: - import pyudev -except ImportError: # pragma: no cover - sys.exit("Failed to import pyudev (https://pyudev.readthedocs.org/):," - " install using:\n pip install pyudev") - try: import serial + from serial.tools.list_ports import comports except ImportError: # pragma: no cover sys.exit("Failed to import pyserial (http://pyserial.sourceforge.net/)," " install using:\n pip install pyserial") @@ -108,8 +103,8 @@ class Scanner(serial.Serial, object): $ # No characters after """, flags=re.VERBOSE) - def __init__(self, port, baudrate=9600): # pragma: no cover - super(Scanner, self).__init__(port=port, baudrate=baudrate) + def __init__(self, device): # pragma: no cover + super(Scanner, self).__init__(**device) def writeread(self, command): # pragma: no cover self.write((command + "\r").encode()) @@ -267,29 +262,26 @@ class DeviceLookup(object): # pragma: no cover """ def __init__(self): - self.context = pyudev.Context() - - def is_scanner(self, device): - """Given USB device is a compatible scanner.""" - return device.get("ID_VENDOR_ID") == "1965" and \ - device.get("ID_MODEL") in SUPPORTED_MODELS + self.device = self._search_devices() - def is_tty(self, device): - """Given USB device is a serial tty.""" - return device.get("SUBSYSTEM") == "tty" + def _search_devices(self): + """ + Find compatible scanner and return usb device. Returns arguments for Serial constructor. + """ + ports = comports(include_links=False) + for port in ports: + # Uniden Vendor + if port.vid == 6501 and port.product in SUPPORTED_MODELS: + return { + "port": port.device, + "baudrate": 9600 + } + # Silicon Laboratories Vendor (SR30C uses this chipset for its USB controller): + if port.vid == 4292 and port.pid == 60000: + return { + "port": port.device, + "baudrate": 57600 + } def get_device(self): - """Find compatible scanner and return usb device. - - If found a tty device will be returned, otherwise the - usb device will be returned. - """ - # Look for scanner tty - for device in self.context.list_devices(): - if self.is_scanner(device) and self.is_tty(device): - return device - - # No scanner with tty, look for scanner - for device in self.context.list_devices(): - if self.is_scanner(device): - return device + return self.device diff --git a/setup.py b/setup.py index daed861..de81b31 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ author = "Folkert de Vries", author_email = "bc125csv@fdev.nl", packages = ["bc125csv"], - install_requires = ["pyudev", "pyserial"], + install_requires = ["pyserial"], entry_points=""" [console_scripts] bc125csv = bc125csv:main From 224f9cac9ba2a30d20e4aba282d01b00ddc29d2f Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Mon, 22 Jun 2020 18:36:11 -0400 Subject: [PATCH 3/4] update docs --- README.md | 12 ++++++++---- setup.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7338649..314a79b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ bc125csv ============= -Channel import and export tool for the Uniden BC125AT, UBC125XLT and UBC126AT. +Channel import and export tool for the Uniden BC125AT, UBC125XLT, UBC126AT, and SR30C. [![Build Status](https://travis-ci.org/fdev/bc125csv.svg)](https://travis-ci.org/fdev/bc125csv) [![Code Climate](https://codeclimate.com/github/fdev/bc125csv/badges/gpa.svg)](https://codeclimate.com/github/fdev/bc125csv) @@ -28,7 +28,6 @@ Requirements ------------ * Python 2.7+ or 3.4+ -* [pyudev](https://pyudev.readthedocs.org/) * [pySerial](http://pyserial.sourceforge.net/) Both pyudev and pySerial will be automatically installed on installation. @@ -192,8 +191,13 @@ include a carriage return yourself. Compatibility ------------- -This application is compatible with the Uniden Bearcat models BC125AT, UBC125XLT -and UBC126AT. +This application is compatible with the Uniden Bearcat models BC125AT, UBC125XLT, +UBC126AT, and SR30C. + +Note: the SR30C uses a stock UART serial USB chipset (specifically, the CP2104). +Linux kernel v2.6.12+ appears to have the driver, but in other operating systems +it may be necessary to get drivers from the manufacturer: +https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers License (MIT) diff --git a/setup.py b/setup.py index de81b31..703dae0 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup -description = "Channel import and export tool for the BC125AT, UBC125XLT and UBC126AT." +description = "Channel import and export tool for the BC125AT, UBC125XLT, UBC126AT, and SR30C." try: # Convert from Markdown to reStructuredText (supported by PyPi). import os From a23e778a6bcf31cb024e0cbbddba020b391b5873 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Thu, 25 Jun 2020 10:32:20 -0400 Subject: [PATCH 4/4] fall back to zeroing out channel --- bc125csv/scanner.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bc125csv/scanner.py b/bc125csv/scanner.py index 99e0452..31f52d7 100644 --- a/bc125csv/scanner.py +++ b/bc125csv/scanner.py @@ -206,7 +206,10 @@ def delete_channel(self, index): if channel: result = self.send("DCH,%d" % index) if not result or result != "DCH,OK": - raise ScannerException("Could not delete channel %d." % index) + # Fall back to zeroing out the channel + result = self.send("CIN,%d,,00000000,,,0,1,0" % index) + if not result or result != "CIN,OK": + raise ScannerException("Could not delete channel %d." % index) class VirtualScanner(Scanner):