Skip to content

Instantly share code, notes, and snippets.

@yeyus
Created September 7, 2022 04:44
Show Gist options
  • Save yeyus/476306a2dc12061e0dd2f49376234b89 to your computer and use it in GitHub Desktop.
Save yeyus/476306a2dc12061e0dd2f49376234b89 to your computer and use it in GitHub Desktop.
Codeplug/Firmware upgrade tool for Retevis RT73 / Kydera CDR-300UV / Radioddity DB25-D)
""" Codeplug/Firmware upgrade tool for Retevis RT73 / Kydera CDR-300UV / Radioddity DB25-D)
This program is designed to allow you to use your radio (create/modify/save codeplug data)
as well as to upgrade the firmware (as supplied by the manufacturer)
The main purpose is to allow you to enjoy your radio without the need for manufacturer-produced,
buggy, windows-only software.
Encryption settings are (intentionally) not supported, as these are not permitted for amateur radio use.
Any feedback welcome!
73, David, M0DMP
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, see <http://www.gnu.org/licenses/>.
"""
__author__ = 'David Pye'
__contact__ = 'davidmpye@gmail.com'
__copyright__ = 'Copyright 2020-2021'
__deprecated__ = False
__email__ = 'davidmpye@gmail.com'
__license__ = 'GPLv3'
__maintainer__ = 'David Pye'
__status__ = 'Beta'
__version__ = '0.0.2'
__contributions__ = 'Dave MM7DBT'
import time, struct, sys
from enum import Enum
import csv, json, serial, argparse, platform, math
if platform.system() == 'Windows':
import ctypes
ctypes.windll.kernel32.SetConsoleTitleW('Codeplug/Firmware Tool by David M0DMP')
import functools
print = functools.partial(print, flush=True)
else:
channel_record_size = 32
zone_record_size = 32
contact_record_size = 16
message_record_size = 40
rx_group_record_size = 210
scan_list_record_size = 216
channel_count_address = 5009
channel_count_len = 2
contact_count_address = 5011
contact_count_len = 2
zone_count_address = 5007
zone_count_len = 2
contact_start_address = 111616
rx_group_start_address = 59071
scan_list_start_address = 5071
message_block_1_start_addr = 4014
message_block_1_count = 16
message_block_2_start_addr = 299
message_block_2_count = 84
aprs_dmr_channel_start_address = 3659
CTCSS_Tones = [
62.5, 67.0, 69.3, 71.9, 74.4, 77.0, 79.7, 82.5, 85.4, 88.5, 91.5, 94.8, 97.4, 100.0, 103.5, 107.2, 110.9, 114.8, 118.8,
123.0, 127.3, 131.8, 136.5, 141.3, 146.2, 151.4, 156.7, 159.8, 162.2, 165.5, 167.9, 171.3, 173.8, 177.3, 179.9, 183.5, 186.2,
189.9, 192.8, 196.6, 199.5, 203.5, 206.5, 210.7, 218.1, 225.7, 229.1, 233.6, 241.8, 250.3, 254.1]
DCS_Codes = [
17, 23, 25, 26, 31, 32, 36, 43, 47, 50, 51, 53, 54, 65, 71, 72, 73, 74, 114,
115, 116, 122, 125, 131, 132, 134, 143, 145, 152, 155, 156, 162, 165, 172, 174, 205, 212, 223,
225, 226, 243, 244, 245, 246, 251, 252, 255, 261, 263, 265, 266, 271, 274, 306, 311, 315, 325,
331, 332, 343, 346, 351, 356, 364, 365, 371, 411, 412, 413, 423, 431, 432, 445, 446, 452, 454,
455, 462, 464, 465, 466, 503, 506, 516, 523, 526, 532, 546, 565, 606, 612, 624, 627, 631, 632,
645, 646, 654, 662, 664, 703, 712, 723, 731, 732, 734, 743, 754]
Button_IDs = {0:'UNDEFINED',
1:'HI_LO_POWER',
2:'BACKLIGHT_TOGGLE',
3:'KEYLOCK_TOGGLE',
4:'VOX',
5:'ZONE_SWITCH',
6:'SCAN',
7:'SCAN_MODE_TOGGLE',
8:'RPTR_TALKAROUND',
9:'EMERGENCY_ALARM',
10:'ENCRYPTION_TOGGLE',
11:'CONTACTS',
12:'SMS',
13:'RADIO_REVIVE',
14:'RADIO_DETECTION',
15:'RADIO_KILL',
16:'REMOTE_MONITOR',
17:'MONITOR',
18:'PERMANENT_MONITOR',
19:'TONEBURST_1750HZ',
49:'DTMF_TOGGLE',
52:'ROAM_TOGGLE',
27:'GPS_TOGGLE',
44:'RECORD_TOGGLE',
38:'MANDOWN_TOGGLE',
53:'RELAY_TOGGLE',
54:'RELAY_MONITOR_TOGGLE',
62:'PROMISCUOUS_MODE',
255:'NOT_USED',
40:'MENU',
55:'UP',
56:'DOWN',
57:'BACK',
58:'DQT_QT',
59:'A_B_TOGGLE',
60:'VOL',
61:'VFO',
62:'PROMISCUOUS_MODE',
63:'DUAL_WATCH_TOGGLE'}
Frequency_Ranges = {0:'COMMERCIAL_EUROPE_0',
1:'COMMERCIAL_US_1',
2:'AMATEUR_EUROPE_3',
3:'AMATEUR_US_7',
4:'COMMERCIAL_8',
5:'AMATEUR_AUS_CAN_10',
6:'COMMERCIAL_13',
7:'AMATEUR_THAILAND_16',
8:'COMMERCIAL_THAILAND_17'}
class Parser:
def __init__(self):
pass
def fromBytes(self, definitions, bytes):
data = {}
for key in definitions:
definition = definitions[key]
if definition[0] == 'Bitmask':
b = bytes[definition[1]] & definition[2]
try:
val = definition[3][b]
except:
val = definition[4]
else:
if definition[0] == 'String':
val = bytes[definition[1]:definition[1] + definition[2]].decode('ascii', 'ignore').rstrip('\x00').replace('\x00', '')
else:
if definition[0] == 'Number':
val = int.from_bytes((bytes[definition[1]:definition[1] + definition[2]]), byteorder='little')
else:
if definition[0] == 'MaskNum':
val = bytes[definition[1]] & definition[2]
if len(definition) > 3:
val = definition[3](val)
data[key] = val
else:
return data
def toBytes(self, data, definitions, item):
for key in definitions:
definition = definitions[key]
if definition[0] == 'Bitmask':
for k, v in definition[3].items():
if v == item[key]:
data[definition[1]] |= k
if definition[0] == 'String':
encoded_str = item[key].encode('ascii', 'ignore')
for p in range(len(item[key])):
data[definition[1] + p] = encoded_str[p]
elif definition[0] == 'Number':
data[definition[1]:definition[1] + definition[2]] = item[key].to_bytes(length=(definition[2]), byteorder='little')
elif definition[0] == 'MaskNum':
if len(definition) > 3:
data[definition[1]] |= int(definition[4](item[key])) & definition[2]
else:
data[definition[1]] |= int(item[key]) & definition[2]
dev_info = {}
dev_info['Factory Number'] = [
'String', 0, 16]
dev_info['Serial Number'] = ['String', 16, 16]
dev_info['Model Number'] = ['String', 32, 16]
dev_info['FW Version'] = ['String', 48, 31]
dev_info['Frequency range'] = ['String', 80, 8]
dev_info['Update date'] = ['String', 96, 16]
dev_info['Firmware ID'] = ['String', 112, 16]
basic_parameters = {}
basic_parameters['Radio name'] = [
'String', 128, 10]
basic_parameters['DMR ID'] = ['Number', 144, 3]
basic_parameters['Language'] = [
'Bitmask', 149, 16, {0:'Chinese', 16:'English'}, 'English']
basic_parameters['TimeoutTimer'] = ['Number', 4943, 1]
basic_parameters['Busy channel lockout'] = ['Bitmask', 166, 128, {0:'Off', 128:'On'}, 'Off']
basic_parameters['VOX'] = ['Bitmask', 166, 64, {0:'Off', 64:'On'}, 'Off']
basic_parameters['VOX sensitivity'] = ['MaskNum', 166, 15, lambda x: x + 1, lambda x: x - 1]
basic_parameters['Scan mode'] = ['Bitmask', 4989, 3, {0:'CO', 1:'TO', 2:'SE'}, 'CO']
basic_parameters['End tone types'] = ['Bitmask', 4993, 3, {0:'55Hz', 1:"120'", 2:'180', 3:'240'}, '55Hz']
basic_parameters['Squelch A level'] = ['MaskNum', 147, 15]
basic_parameters['Squelch B level'] = ['MaskNum', 147, 240, lambda x: x >> 4, lambda x: x << 4]
basic_parameters['Backlight'] = [
'Bitmask', 149, 40, {0:'Off', 8:'On', 32:'Auto'}, 'On']
basic_parameters['Keylock'] = ['Bitmask', 149, 68, {0:'Off', 4:'Auto', 64:'Manual', 68:'Manual & Auto'}, 'Off']
basic_parameters['Roaming'] = ['Bitmask', 4987, 1, {0:'Off', 1:'On'}, 'Off']
basic_parameters['Roaming mode'] = ['Bitmask', 4981, 3, {0:'Auto', 1:'Manual', 2:'Strong RSSI Priority'}, 'Auto']
basic_parameters['RSSI set'] = ['MaskNum', 4982, 255, lambda x: -90 - x, lambda x: -1 * x - 90]
basic_parameters['Connect check timer'] = ['MaskNum', 4983, 255]
basic_parameters['Repeater check timer'] = ['MaskNum', 4984, 255]
basic_parameters['Connect timer'] = ['MaskNum', 4985, 9, lambda x: x + 1, lambda x: x - 1]
basic_parameters['Record set'] = ['Bitmask', 4992, 3, {0:'None', 1:'TX', 2:'RX', 3:'TX/RX'}, 'None']
basic_parameters['Power Saving'] = [
'Bitmask', 150, 4, {0:'Off', 4:'On'}, 'On']
basic_parameters['Power Saving Ratio'] = ['Bitmask', 150, 24, {0:'1:1', 8:'1:2', 16:'1:4'}, 'def']
basic_parameters['Power Saving Delay'] = ['MaskNum', 163, 255]
common_menu_parameters = {}
common_menu_parameters['Contact list'] = [
'Bitmask', 174, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['New contact'] = ['Bitmask', 174, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Manual dial'] = ['Bitmask', 174, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Ham contacts'] = ['Bitmask', 174, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['Ham groups'] = ['Bitmask', 174, 16, {0:'Off', 16:'On'}, 'On']
common_menu_parameters['Radio check'] = [
'Bitmask', 186, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Call alert'] = ['Bitmask', 186, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Radio monitor'] = ['Bitmask', 186, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Radio disable'] = ['Bitmask', 186, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['Radio enable'] = ['Bitmask', 186, 16, {0:'Off', 16:'On'}, 'On']
common_menu_parameters['SMS write'] = [
'Bitmask', 175, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['SMS quick msg'] = ['Bitmask', 175, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['SMS inbox'] = ['Bitmask', 175, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['SMS outbox'] = ['Bitmask', 175, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['SMS drafts'] = ['Bitmask', 175, 16, {0:'Off', 16:'On'}, 'On']
common_menu_parameters['Call log outgoing'] = [
'Bitmask', 176, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Call log received'] = ['Bitmask', 176, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Call log missed'] = ['Bitmask', 176, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Scan on/off'] = [
'Bitmask', 177, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Scan list'] = ['Bitmask', 177, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Scan mode'] = ['Bitmask', 177, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Roam on/off'] = ['Bitmask', 177, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['Scan running on/off'] = ['Bitmask', 16, 16, {0:'Off', 16:'On'}, 'On']
common_menu_parameters['Zone list on/off'] = [
'Bitmask', 178, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Language'] = [
'Bitmask', 179, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Keylock'] = ['Bitmask', 179, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Backlight'] = ['Bitmask', 179, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['LEDs'] = ['Bitmask', 179, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['Display mode'] = ['Bitmask', 179, 16, {0:'Off', 16:'On'}, 'On']
common_menu_parameters['Vox'] = ['Bitmask', 179, 32, {0:'Off', 32:'On'}, 'On']
common_menu_parameters['Channel sw'] = ['Bitmask', 179, 64, {0:'Off', 64:'On'}, 'On']
common_menu_parameters['Factory reset'] = ['Bitmask', 179, 128, {0:'Off', 128:'On'}, 'On']
common_menu_parameters['Local repeat'] = [
'Bitmask', 188, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['ToT'] = [
'Bitmask', 181, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Power set'] = ['Bitmask', 181, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Repeat set'] = ['Bitmask', 181, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Sleep mode'] = ['Bitmask', 181, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['Squelch level'] = ['Bitmask', 181, 16, {0:'Off', 16:'On'}, 'On']
common_menu_parameters['Wide/Narrow band'] = ['Bitmask', 181, 32, {0:'Off', 32:'On'}, 'On']
common_menu_parameters['Busy channel lockout'] = ['Bitmask', 181, 64, {0:'Off', 64:'On'}, 'On']
common_menu_parameters['Signalling'] = ['Bitmask', 181, 128, {0:'Off', 128:'On'}, 'On']
common_menu_parameters['End tone types'] = [
'Bitmask', 189, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Enc level'] = [
'Bitmask', 180, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Profiles'] = [
'Bitmask', 182, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Keytone'] = ['Bitmask', 182, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Power tone'] = ['Bitmask', 182, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Msg tone'] = ['Bitmask', 182, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['Private call tone'] = ['Bitmask', 182, 16, {0:'Off', 16:'On'}, 'On']
common_menu_parameters['Group call tone'] = ['Bitmask', 182, 32, {0:'Off', 32:'On'}, 'On']
common_menu_parameters['Call tone'] = ['Bitmask', 182, 64, {0:'Off', 64:'On'}, 'On']
common_menu_parameters['Power on tone'] = ['Bitmask', 182, 128, {0:'Off', 128:'On'}, 'On']
common_menu_parameters['GPS'] = [
'Bitmask', 183, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Torch'] = ['Bitmask', 183, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['FM radio'] = ['Bitmask', 183, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Time'] = ['Bitmask', 183, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['DTMF'] = ['Bitmask', 183, 16, {0:'Off', 16:'On'}, 'On']
common_menu_parameters['Speaker handmic'] = ['Bitmask', 183, 32, {0:'Off', 32:'On'}, 'On']
common_menu_parameters['APRS'] = ['Bitmask', 183, 64, {0:'Off', 64:'On'}, 'On']
common_menu_parameters['Record set'] = [
'Bitmask', 184, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['Record list'] = ['Bitmask', 184, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Record clear'] = ['Bitmask', 184, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Record space'] = ['Bitmask', 184, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['Radio ID'] = [
'Bitmask', 185, 1, {0:'Off', 1:'On'}, 'On']
common_menu_parameters['RX group list'] = ['Bitmask', 185, 2, {0:'Off', 2:'On'}, 'On']
common_menu_parameters['Channel contact'] = ['Bitmask', 185, 4, {0:'Off', 4:'On'}, 'On']
common_menu_parameters['Version'] = ['Bitmask', 185, 8, {0:'Off', 8:'On'}, 'On']
common_menu_parameters['VFO'] = [
'Bitmask', 187, 1, {0:'Off', 1:'On'}, 'On']
prompt_tone_parameters = {}
prompt_tone_parameters['Profiles'] = [
'Bitmask', 167, 1, {0:'Standard', 1:'Silent'}, 'Standard']
prompt_tone_parameters['SMS Prompt'] = ['MaskNum', 168, 15]
prompt_tone_parameters['Private call Tone'] = ['MaskNum', 169, 5]
prompt_tone_parameters['Group call Tone'] = ['MaskNum', 170, 5]
prompt_tone_parameters['Key tone'] = ['Bitmask', 171, 128, {0:'Off', 128:'On'}, 'On']
prompt_tone_parameters['Key tone vol'] = ['MaskNum', 171, 15]
prompt_tone_parameters['Low bat alert tone'] = ['Bitmask', 172, 128, {0:'Off', 128:'On'}, 'On']
prompt_tone_parameters['Low bat alert vol'] = ['MaskNum', 172, 15]
prompt_tone_parameters['Call hang up'] = ['Bitmask', 4824, 1, {0:'Off', 1:'On'}, 'On']
prompt_tone_parameters['Boot ringtone'] = ['Bitmask', 149, 2, {0:'Off', 2:'On'}, 'On']
prompt_tone_parameters['Roaming restart prompt'] = ['MaskNum', 4995, 15]
prompt_tone_parameters['Repeater selected prompt'] = ['MaskNum', 4995, 15]
indicator_parameters = {}
indicator_parameters['All'] = [
'Bitmask', 173, 16, {16:'On', 0:'Off'}, 'On']
indicator_parameters['Tx'] = ['Bitmask', 173, 8, {8:'On', 0:'Off'}, 'On']
indicator_parameters['Rx'] = ['Bitmask', 173, 4, {4:'On', 0:'Off'}, 'On']
indicator_parameters['Scanning'] = ['Bitmask', 173, 2, {2:'On', 0:'Off'}, 'On']
indicator_parameters['Low battery'] = ['Bitmask', 173, 1, {1:'On', 0:'Off'}, 'On']
button_preset_parameters = {}
button_preset_parameters['LongPressDuration'] = [
'Bitmask', 193, 255, {0:'0.5', 1:'1.0', 2:'1.5', 3:'2.0', 4:'2.5', 5:'3.0', 6:'3.5', 7:'4.0', 8:'4.5', 9:'5.0'}, '2.0']
button_preset_parameters['P1 LongPress'] = ['Bitmask', 194, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P1 ShortPress'] = ['Bitmask', 195, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P2 LongPress'] = ['Bitmask', 196, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P2 ShortPress'] = ['Bitmask', 197, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P3 LongPress'] = ['Bitmask', 198, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P3 ShortPress'] = ['Bitmask', 199, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P4 LongPress'] = ['Bitmask', 200, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P4 ShortPress'] = ['Bitmask', 201, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P5 LongPress'] = ['Bitmask', 202, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P5 ShortPress'] = ['Bitmask', 203, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P6 LongPress'] = ['Bitmask', 204, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P6 ShortPress'] = ['Bitmask', 205, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P7 LongPress'] = ['Bitmask', 4997, 255, Button_IDs, 'UNDEFINED']
button_preset_parameters['P7 ShortPress'] = ['Bitmask', 4998, 255, Button_IDs, 'UNDEFINED']
mic_gain_parameters = {}
mic_gain_parameters['Mic gain 1'] = [
'Bitmask', 164, 128, {0:'Off', 128:'On'}, 'On']
mic_gain_parameters['Mic gain 1 setting'] = ['MaskNum', 164, 7, lambda x: x * 4, lambda x: int(x / 4)]
mic_gain_parameters['Mic gain 2'] = ['Bitmask', 165, 128, {0:'Off', 128:'On'}, 'On']
mic_gain_parameters['Mic gain 2 setting'] = ['MaskNum', 165, 31]
dmr_parameters = {}
dmr_parameters['Remote monitor duration'] = [
'MaskNum', 4012, 255, lambda x: 10 + x * 10, lambda x: x / 10 - 1]
dmr_parameters['Remote monitor decode'] = ['Bitmask', 4013, 128, {0:'Off', 128:'On'}, 'Off']
dmr_parameters['Remote kill decode'] = ['Bitmask', 4013, 64, {0:'Off', 64:'On'}, 'Off']
dmr_parameters['Radio detection decode'] = ['Bitmask', 4013, 32, {0:'Off', 32:'On'}, 'Off']
dmr_parameters['Radio revive decode'] = ['Bitmask', 4013, 16, {0:'Off', 16:'On'}, 'On']
dmr_parameters['Call alert'] = ['Bitmask', 4013, 8, {0:'Off', 8:'On'}, 'Off']
dmr_parameters['Group call hang time'] = ['MaskNum', 4944, 255, lambda x: x * 500, lambda x: int(x / 500)]
dmr_parameters['Private call hang time'] = ['MaskNum', 4945, 15, lambda x: x * 500, lambda x: int(x / 500)]
dmr_parameters['Import delay'] = ['MaskNum', 4976, 255, lambda x: x * 10, lambda x: x / 10]
dmr_parameters['DTMF duration (on-time)'] = ['MaskNum', 4977, 255, lambda x: x * 10, lambda x: int(x / 10)]
dmr_parameters['DTMF duration (off-time)'] = ['MaskNum', 4978, 255, lambda x: x * 10, lambda x: int(x / 10)]
dmr_parameters['DTMF volume (local)'] = ['MaskNum', 4979, 15]
dmr_parameters['DTMF On/off'] = ['Bitmask', 4963, 1, {0:'Off', 1:'On'}, 'Off']
dmr_parameters['DTMF Code'] = ['String', 4963, 11]
dmr_parameters['GPS On/off'] = [
'Bitmask', 4986, 1, {0:'Off', 1:'On'}, 'Off']
dmr_parameters['GPS Interval'] = ['MaskNum', 4947, 255]
dmr_parameters['GPS Channel group ID'] = [
'Number', 4948, 2]
dmr_parameters['GPS Channel channel ID'] = ['Number', 4950, 2]
dmr_parameters['Mandown On/Off'] = [
'Bitmask', 4996, 1, {0:'Off', 1:'On'}, 'Off']
dmr_parameters['Mandown Interval'] = ['MaskNum', 4946, 255]
dmr_parameters['Mandown Angle'] = ['MaskNum', 4952, 255]
dmr_parameters['Mandown Duration'] = ['MaskNum', 4953, 255]
aprs_parameters = {}
aprs_parameters['Manual TX interval'] = [
'Number', 3723, 1]
aprs_parameters['Auto TX interval'] = [
'MaskNum', 3724, 255, lambda x: x * 30, lambda x: int(x / 30)]
aprs_parameters['Beacon'] = ['Bitmask', 3726, 1, {0:'FIXED_LOCATION', 1:'GPS_LOCATION'}, 'GPS_LOCATION']
aprs_parameters['LatNS'] = [
'Bitmask', 3727, 1, {0:'North', 1:'South'}, 'North']
aprs_parameters['Lat Degrees'] = ['Number', 3729, 4]
aprs_parameters['LongEW'] = [
'Bitmask', 3728, 1, {0:'East', 1:'West'}, 'East']
aprs_parameters['Long Degrees'] = ['Number', 3733, 4]
aprs_parameters['AX25 TX Freq'] = [
'Number', 3737, 4]
aprs_parameters['AX25 TX Power'] = ['Bitmask', 3745, 1, {0:'LOW', 1:'HIGH'}, 'LOW']
aprs_parameters['AX25 QT/DQT'] = [
'Number', 3741, 2]
aprs_parameters['AX25 APRS Tone'] = [
'Bitmask', 3746, 1, {0:'OFF', 1:'ON'}, 'OFF']
aprs_parameters['AX25 TX Delay'] = [
'MaskNum', 3743, 255, lambda x: x * 20, lambda x: int(x / 20)]
aprs_parameters['AX25 Prewave time'] = [
'MaskNum', 3744, 255, lambda x: x * 10, lambda x: int(x / 10)]
aprs_parameters['AX25 Your Callsign'] = [
'String', 3755, 6]
aprs_parameters['AX25 Your SSID'] = ['Number', 3748, 1]
aprs_parameters['AX25 Dest Callsign'] = ['String', 3749, 6]
aprs_parameters['AX25 Dest SSID'] = ['Number', 3747, 1]
aprs_parameters['AX25 APRS Symbol Table'] = [
'Number', 3761, 1]
aprs_parameters['AX25 APRS Map Icon'] = ['Number', 3762, 1]
aprs_parameters['AX25 APRS Signal Path'] = [
'String', 3763, 20]
aprs_parameters['AX25 Your Sending Text'] = ['String', 3783, 61]
aprs_dmr_record_parameters = {}
aprs_dmr_record_parameters['Zone ID'] = [
'Number', 0, 2]
aprs_dmr_record_parameters['Channel ID'] = ['Number', 2, 2]
aprs_dmr_record_parameters['Call Type'] = ['Bitmask', 7, 4, {0:'PRIVATE', 4:'GROUP'}, 'PRIVATE']
aprs_dmr_record_parameters['PTT'] = ['Bitmask', 7, 8, {0:'OFF', 8:'ON'}, 'OFF']
aprs_dmr_record_parameters['Report Slot'] = ['Bitmask', 7, 3, {0:'CURRENT', 1:'TS1', 2:'TS2', 3:'TS2'}, 'CURRENT']
aprs_dmr_record_parameters['APRS TG'] = ['Number', 4, 3]
contact_parameters = {}
contact_parameters['ID'] = [
'Number', 0, 2]
contact_parameters['Name'] = ['String', 3, 10]
contact_parameters['DMR ID'] = ['Number', 13, 3]
contact_parameters['Type'] = ['Bitmask', 2, 255, {4:'Group', 5:'Private', 6:'All Call', 7:'No-Address Call', 8:'RawData', 9:'Define Data', 10:'SPDATA'}, 'Group']
scan_list_info = {}
scan_list_info['Name'] = [
'String', 0, 10]
scan_list_info['Talkback'] = ['Bitmask', 11, 32, {0:'Off', 32:'On'}, 'Off']
scan_list_info['Scan TX Mode'] = ['Bitmask', 11, 15, {0:'Current Channel', 4:'Last Operated Channel', 8:'Appointed Channel'}, 'Current Channel']
scan_list_info['Appointed channel group ID'] = ['Number', 12, 2]
scan_list_info['Appointed channel channel ID'] = ['Number', 14, 2]
rx_group_info = {}
rx_group_info['Name'] = [
'String', 0, 10]
zone_info = {}
zone_info['ID'] = [
'Number', 0, 2]
zone_info['Name'] = ['String', 3, 10]
channel_info = {}
channel_info['ID'] = [
'Number', 0, 2]
channel_info['Type'] = ['Bitmask', 20, 192, {0:'ANALOG', 64:'DIGITAL', 128:'D_A_TX_A', 192:'D_A_TX_D'}, 'ANALOG']
channel_info['Name'] = ['String', 2, 10]
channel_info['Rx Freq'] = ['Number', 12, 4]
channel_info['Tx Freq'] = ['Number', 16, 4]
channel_info['Tx Power'] = ['Bitmask', 20, 32, {0:'LOW', 32:'HIGH'}, 'LOW']
channel_info['Rx only'] = ['Bitmask', 25, 16, {0:'OFF', 16:'ON'}, 'OFF']
channel_info['Alarm'] = ['Bitmask', 20, 8, {0:'OFF', 8:'ON'}, 'OFF']
channel_info['Prompt'] = ['Bitmask', 20, 4, {0:'OFF', 4:'ON'}, 'OFF']
channel_info['PCT'] = ['Bitmask', 20, 2, {0:'PATCS', 2:'OACSU'}, 'PATCS']
channel_info['TS Rx'] = ['Bitmask', 20, 1, {0:'TS1', 1:'TS2'}, 'TS1']
channel_info['TS Tx'] = ['Bitmask', 29, 2, {0:'TS1', 2:'TS2'}, 'TS1']
channel_info['RX CC'] = ['MaskNum', 21, 15]
channel_info['TX CC'] = ['MaskNum', 29, 240, lambda x: x >> 4, lambda x: x << 4]
channel_info['MSG Type'] = ['Bitmask', 21, 16, {0:'UNCONFIRMED', 16:'CONFIRMED'}, 'UNCONFIRMED']
channel_info['TX Policy'] = ['Bitmask', 21, 192, {0:'IMPOLITE', 64:'POLITE_TO_CC', 96:'POLITE_TO_ALL'}, 'IMPOLITE']
channel_info['Group call list'] = ['Number', 23, 1]
channel_info['Scan List ID'] = [
'Number', 24, 1]
channel_info['Default Contact ID'] = [
'Number', 30, 1]
channel_info['EAS'] = ['Bitmask', 25, 15, {0:'OFF', 1:'A1', 2:'A2', 3:'A3', 4:'A4'}, 'OFF']
channel_info['Bandwidth'] = ['Bitmask', 20, 16, {16:'25KHz', 0:'12.5KHz'}, '12.5KHz']
channel_info['Tone Type Tx'] = [
'Bitmask', 26, 12, {0:'OFF', 4:'CTCSS', 8:'DCS', 12:'DCS Invert'}, 'OFF']
channel_info['Tone Tx'] = ['Number', 28, 1]
channel_info['Tone Type Rx'] = ['Bitmask', 26, 3, {0:'OFF', 1:'CTCSS', 2:'DCS', 3:'DCS Invert'}, 'OFF']
channel_info['Tone Rx'] = ['Number', 27, 1]
channel_info['APRS Channel'] = [
'MaskNum', 31, 240, lambda x: x >> 4, lambda x: x << 4]
channel_info['Relay Monitor'] = [
'Bitmask', 21, 128, {0:'OFF', 128:'ON'}, 'OFF']
channel_info['Relay Mode'] = ['Bitmask', 25, 192, {0:'OFF', 64:'RX ONLY', 128:'TX ONLY', 192:'RX AND TX'}, 'OFF']
tson_info = {}
tson_info['TS Rx ON'] = [
'Bitmask', 29, 1, {0:'ON', 1:'OFF'}, 'OFF']
tson_info['TS Tx ON'] = ['Bitmask', 29, 4, {0:'ON', 4:'OFF'}, 'OFF']
def decompileCodeplug(data):
codeplug = {}
debugMsg(2, 'Parsing Binary Codeplug To JSON')
num_channels = int.from_bytes((data[channel_count_address:channel_count_address + channel_count_len]), byteorder='little')
num_zones = int.from_bytes((data[zone_count_address:zone_count_address + zone_count_len]), byteorder='little')
num_contacts = int.from_bytes((data[contact_count_address:contact_count_address + contact_count_len]), byteorder='little')
contact_block_size = num_contacts * contact_record_size
debugMsg(3, 'Contacts Block Size ' + str(contact_block_size))
if contact_block_size % 1024 != 0:
contact_block_size += 1024 - contact_block_size % 1024
debugMsg(3, 'Contacts Block Padded ' + str(contact_block_size))
if contact_block_size // 1024 % 2 == 0:
debugMsg(3, 'Contacts Block Count Was Even - Adding another block')
contact_block_size = contact_block_size + 1024
debugMsg(3, 'Contact Block Size ' + str(contact_block_size))
zone_start_address = contact_start_address + contact_block_size
if zone_start_address % 1024 != 0:
zone_start_address += 1024 - zone_start_address % 1024
debugMsg(3, 'Zones:' + str(num_zones) + ', start address ' + hex(zone_start_address))
debugMsg(3, 'Channels:' + str(num_channels))
debugMsg(3, 'Contacts:' + str(num_contacts) + ', start address: ' + hex(contact_start_address))
p = Parser()
debugMsg(2, 'Parsing Device info')
codeplug['Device info'] = p.fromBytes(dev_info, data)
debugMsg(2, 'Parsing Basic parameters')
codeplug['Basic parameters'] = p.fromBytes(basic_parameters, data)
debugMsg(2, 'Parsing Common menu parameters')
codeplug['Common menu parameters'] = p.fromBytes(common_menu_parameters, data)
debugMsg(2, 'Parsing Prompt Tone')
codeplug['Prompt Tone'] = p.fromBytes(prompt_tone_parameters, data)
debugMsg(2, 'Parsing Indicators')
codeplug['Indicators'] = p.fromBytes(indicator_parameters, data)
debugMsg(2, 'Parsing Buttons')
codeplug['Preset buttons'] = p.fromBytes(button_preset_parameters, data)
debugMsg(2, 'Parsing Mic gain')
codeplug['Mic gain'] = p.fromBytes(mic_gain_parameters, data)
debugMsg(2, 'Parsing APRS settings')
codeplug['APRS'] = p.fromBytes(aprs_parameters, data)
if 159 <= codeplug['APRS']['AX25 QT/DQT'] <= 267:
codeplug['APRS']['AX25 QT/DQT'] = 'D' + str(DCS_Codes[(codeplug['APRS']['AX25 QT/DQT'] - 160)]).zfill(3) + 'I'
else:
if 52 <= codeplug['APRS']['AX25 QT/DQT'] <= 158:
codeplug['APRS']['AX25 QT/DQT'] = 'D' + str(DCS_Codes[(codeplug['APRS']['AX25 QT/DQT'] - 53)]).zfill(3) + 'N'
else:
if 1 <= codeplug['APRS']['AX25 QT/DQT'] <= 51:
codeplug['APRS']['AX25 QT/DQT'] = str(CTCSS_Tones[codeplug['APRS']['AX25 QT/DQT']])
else:
codeplug['APRS']['AX25 QT/DQT'] = 'Off'
if codeplug['APRS']['Lat Degrees'] > 8999999:
codeplug['APRS']['Lat Degrees'] = 0
if codeplug['APRS']['Long Degrees'] > 17999999:
codeplug['APRS']['Long Degrees'] = 0
debugMsg(2, 'Parsing APRS DMR channels')
codeplug['APRS']['DMR channels'] = []
for i in range(8):
codeplug['APRS']['DMR channels'].append(p.fromBytes(aprs_dmr_record_parameters, data[aprs_dmr_channel_start_address + i * 8:aprs_dmr_channel_start_address + (i + 1) * 8]))
else:
debugMsg(2, 'Parsing DMR Service')
codeplug['DMR Service'] = p.fromBytes(dmr_parameters, data)
debugMsg(2, 'Parsing Quick messages')
msg_str = ''
codeplug['Quick messages'] = []
for i in range(message_block_1_count + message_block_2_count):
if i < message_block_1_count:
start_of_message = message_block_1_start_addr + i * message_record_size
else:
start_of_message = message_block_2_start_addr + (i - message_block_1_count) * message_record_size
try:
msg_str = data[start_of_message:start_of_message + message_record_size].decode('ascii', 'ignore').rstrip('\x00')
except:
debugMsg(1, ' Skipped message number ' + str(i) + ' containing non-ascii text')
else:
debugMsg(3, 'Adding message ' + str(i + 1) + ' - ' + msg_str)
codeplug['Quick messages'].append(msg_str)
else:
debugMsg(2, 'Parsing Contacts')
debugMsg(4, 'Contacts ' + str(num_contacts))
codeplug['Contacts'] = []
for i in range(num_contacts):
contact_data = data[contact_start_address + i * contact_record_size:contact_start_address + (i + 1) * contact_record_size]
parsed_contact = p.fromBytes(contact_parameters, contact_data)
debugMsg(3, 'Parsed contact - ' + str(parsed_contact))
codeplug['Contacts'].append(parsed_contact)
else:
debugMsg(2, 'Parsing Scan lists')
codeplug['Scan lists'] = []
for i in range(250):
record_start_addr = scan_list_start_address + i * scan_list_record_size
record = p.fromBytes(scan_list_info, data[record_start_addr:record_start_addr + scan_list_record_size])
if record['Name'] != '':
record['Selected channels'] = []
for j in range(50):
group = int.from_bytes((data[record_start_addr + 16 + j * 4:record_start_addr + 16 + j * 4 + 2]), byteorder='little')
channel = int.from_bytes((data[record_start_addr + 16 + j * 4 + 2:record_start_addr + 16 + j * 4 + 4]), byteorder='little')
if group != 0:
if channel != 0:
pair = {}
pair['Group'] = group
pair['Channel'] = channel
record['Selected channels'].append(pair)
debugMsg(3, 'Parsed scan list ' + str(record))
codeplug['Scan lists'].append(record)
else:
debugMsg(2, 'Parsing Rx groups')
codeplug['RX groups'] = []
for i in range(250):
record = p.fromBytes(rx_group_info, data[rx_group_start_address + i * rx_group_record_size:rx_group_start_address + (i + 1) * rx_group_record_size])
record['Contacts'] = []
for j in range(100):
id = int.from_bytes((data[rx_group_start_address + i * rx_group_record_size + 10 + j * 2:rx_group_start_address + i * rx_group_record_size + 10 + j * 2 + 2]), byteorder='little')
if id != 0:
record['Contacts'].append(id)
if not record['Name'] != '':
if len(record['Contacts']) != 0:
pass
debugMsg(3, 'Parsed rx group - ' + str(record))
codeplug['RX groups'].append(record)
debugMsg(2, 'Parsing Zones')
codeplug['Zones'] = []
for i in range(num_zones):
zone_data = data[zone_start_address + 32 * i:zone_start_address + 32 * (i + 1)]
parsed_zone = p.fromBytes(zone_info, zone_data)
num_channels = int.from_bytes((zone_data[15:17]), byteorder='little')
channels_offset = int.from_bytes((zone_data[13:15]), byteorder='little')
debugMsg(3, 'Zone ' + parsed_zone['Name'] + ' - channel offset address - ' + hex(channels_offset))
channels_offset = (channels_offset - 1) * 32
parsed_zone['Channels'] = []
debugMsg(2, 'Parsing Channels')
for i in range(num_channels):
channel_data = data[zone_start_address + channels_offset + i * 32:zone_start_address + channels_offset + (i + 1) * 32]
channel = p.fromBytes(channel_info, channel_data)
tempTSON_INFO = p.fromBytes(tson_info, channel_data)
if channel['Rx Freq'] != channel['Tx Freq']:
tempTSON_INFO['TS Rx ON'] = 'OFF'
tempTSON_INFO['TS Tx ON'] = 'OFF'
if tempTSON_INFO['TS Rx ON'] == 'ON':
channel['TS Rx'] = 'ON'
if tempTSON_INFO['TS Tx ON'] == 'ON':
channel['TS Tx'] = 'ON'
if channel['Tone Type Tx'] == 'CTCSS':
channel['Tone Tx'] = CTCSS_Tones[channel['Tone Tx']]
else:
if channel['Tone Type Tx'] != 'OFF':
channel['Tone Tx'] = DCS_Codes[channel['Tone Tx']]
elif channel['Tone Type Rx'] == 'CTCSS':
channel['Tone Rx'] = CTCSS_Tones[channel['Tone Rx']]
else:
if channel['Tone Type Rx'] != 'OFF':
channel['Tone Rx'] = DCS_Codes[channel['Tone Rx']]
parsed_zone['Channels'].append(channel)
debugMsg(3, 'Parsed channel - ' + str(channel))
else:
codeplug['Zones'].append(parsed_zone)
else:
debugMsg(2, 'Success!')
return json.dumps(codeplug, indent=2)
def compileCodeplug(data):
codeplug = json.loads(data)
num_contacts = len(codeplug['Contacts'])
debugMsg(4, 'Num Contacts ' + str(num_contacts))
num_zones = len(codeplug['Zones'])
num_channels = 0
for i in codeplug['Zones']:
num_channels += len(i['Channels'])
else:
contact_block_size = num_contacts * contact_record_size
debugMsg(2, 'Contacts Block Size ' + str(contact_block_size))
if contact_block_size % 1024 != 0:
contact_block_size += 1024 - contact_block_size % 1024
debugMsg(2, 'Contacts Block Padded ' + str(contact_block_size))
if contact_block_size // 1024 % 2 == 0:
debugMsg(2, 'Contacts Block Count Was Even')
contact_block_size = contact_block_size + 1024
zone_start_address = contact_start_address + contact_block_size
debugMsg(2, 'Contact Start Address ' + str(contact_start_address))
debugMsg(2, 'Zone Start Address ' + str(zone_start_address))
channel_start_address = zone_start_address + 32 * num_zones
debugMsg(2, 'Channel Start Address ' + str(channel_start_address))
codeplug_size = channel_start_address + num_channels * channel_record_size
if codeplug_size % 2048 != 0:
codeplug_size += 2048 - codeplug_size % 2048
debugMsg(2, 'Compiled codeplug of size ' + str(codeplug_size))
template = bytearray(b'\x00' * codeplug_size)
template[zone_count_address:zone_count_address + 2] = num_zones.to_bytes(length=2, byteorder='little')
template[contact_count_address:contact_count_address + 2] = num_contacts.to_bytes(length=2, byteorder='little')
template[channel_count_address:channel_count_address + 2] = num_channels.to_bytes(length=2, byteorder='little')
try:
exists = codeplug['DMR Service']['Mandown On/Off']
except:
codeplug['DMR Service']['Mandown On/Off'] = 'Off'
else:
try:
exists = codeplug['DMR Service']['Mandown Interval']
except:
codeplug['DMR Service']['Mandown Interval'] = 0
try:
exists = codeplug['DMR Service']['Mandown Angle']
except:
codeplug['DMR Service']['Mandown Angle'] = 0
else:
try:
exists = codeplug['DMR Service']['Mandown Duration']
except:
codeplug['DMR Service']['Mandown Duration'] = 0
else:
p = Parser()
p.toBytes(template, dev_info, codeplug['Device info'])
p.toBytes(template, basic_parameters, codeplug['Basic parameters'])
p.toBytes(template, common_menu_parameters, codeplug['Common menu parameters'])
p.toBytes(template, prompt_tone_parameters, codeplug['Prompt Tone'])
p.toBytes(template, indicator_parameters, codeplug['Indicators'])
p.toBytes(template, button_preset_parameters, codeplug['Preset buttons'])
p.toBytes(template, mic_gain_parameters, codeplug['Mic gain'])
p.toBytes(template, dmr_parameters, codeplug['DMR Service'])
if codeplug['APRS']['AX25 QT/DQT'] == 'Off':
codeplug['APRS']['AX25 QT/DQT'] = 0
else:
if codeplug['APRS']['AX25 QT/DQT'].endswith('I'):
codeplug['APRS']['AX25 QT/DQT'] = DCS_Codes.index(int(str(codeplug['APRS']['AX25 QT/DQT']).replace('D', '').replace('I', ''))) + 160
else:
if codeplug['APRS']['AX25 QT/DQT'].endswith('N'):
codeplug['APRS']['AX25 QT/DQT'] = DCS_Codes.index(int(str(codeplug['APRS']['AX25 QT/DQT']).replace('D', '').replace('N', ''))) + 53
else:
codeplug['APRS']['AX25 QT/DQT'] = CTCSS_Tones.index(codeplug['APRS']['AX25 QT/DQT'])
p.toBytes(template, aprs_parameters, codeplug['APRS'])
aprs_dmr_record_count = 0
for record in codeplug['APRS']['DMR channels']:
debugMsg(4, 'Writing APRS DMR channel record ' + str(aprs_dmr_record_count))
debugMsg(5, 'Record : ' + str(record))
aprs_dmr_record = bytearray(8)
p.toBytes(aprs_dmr_record, aprs_dmr_record_parameters, record)
template[aprs_dmr_channel_start_address + 8 * aprs_dmr_record_count:aprs_dmr_channel_start_address + 8 * (aprs_dmr_record_count + 1)] = aprs_dmr_record
aprs_dmr_record_count = aprs_dmr_record_count + 1
else:
for i in range(len(codeplug['Quick messages'])):
debugMsg(4, 'Parsing message ' + codeplug['Quick messages'][i])
if i < message_block_1_count:
start_address = message_block_1_start_addr + i * message_record_size
else:
start_address = message_block_2_start_addr + (i - message_block_1_count) * message_record_size
encoded_str = codeplug['Quick messages'][i][0:message_record_size].encode('ascii', 'ignore')
template[start_address:start_address + len(encoded_str)] = encoded_str
else:
count = 0
for contact in codeplug['Contacts']:
contact_record = bytearray(contact_record_size)
try:
p.toBytes(contact_record, contact_parameters, contact)
except:
raise SystemExit('Contact Error:\n' + str(contact))
else:
debugMsg(4, 'Contact ' + str(contact))
template[contact_start_address + contact_record_size * count:contact_start_address + contact_record_size * (count + 1)] = contact_record
count = count + 1
else:
count = 0
for scan_list in codeplug['Scan lists']:
scan_list_record = bytearray(scan_list_record_size)
try:
p.toBytes(scan_list_record, scan_list_info, scan_list)
except:
raise SystemExit('Scan List Error:\n' + str(scan_list))
else:
for i in range(len(scan_list['Selected channels'])):
scan_list_record[16 + i * 4:16 + i * 4 + 2] = scan_list['Selected channels'][i]['Group'].to_bytes(byteorder='little', length=2)
scan_list_record[16 + i * 4 + 2:16 + i * 4 + 4] = scan_list['Selected channels'][i]['Channel'].to_bytes(byteorder='little', length=2)
else:
template[scan_list_start_address + scan_list_record_size * count:scan_list_start_address + scan_list_record_size * (count + 1)] = scan_list_record
count = count + 1
else:
count = 0
for group in codeplug['RX groups']:
group_record = bytearray(rx_group_record_size)
try:
p.toBytes(group_record, rx_group_info, group)
except:
raise SystemExit('RX Group Error:\n' + str(group))
else:
for i in range(len(group['Contacts'])):
group_record[10 + i * 2:10 + (i + 1) * 2] = group['Contacts'][i].to_bytes(byteorder='little', length=2)
else:
template[rx_group_start_address + rx_group_record_size * count:rx_group_start_address + rx_group_record_size * (count + 1)] = group_record
count = count + 1
else:
count = 0
for i in codeplug['Zones']:
channel_offset = len(codeplug['Zones']) + 1
for j in range(count):
channel_offset += len(codeplug['Zones'][j]['Channels'])
else:
debugMsg(4, 'Channel offset for ' + i['Name'] + ' was ' + hex(channel_offset))
zone_record = bytearray(zone_record_size)
try:
p.toBytes(zone_record, zone_info, i)
except:
raise SystemExit('Zone Error:\n' + i['Name'])
else:
zone_record[13:15] = channel_offset.to_bytes(length=2, byteorder='little')
zone_record[15:17] = len(i['Channels']).to_bytes(length=2, byteorder='little')
template[zone_start_address + zone_record_size * count:zone_start_address + zone_record_size * (count + 1)] = zone_record
count = count + 1
else:
count = 0
for i in codeplug['Zones']:
for channel in i['Channels']:
debugMsg(4, 'Writing channel ' + str(channel['ID']) + ' - ' + channel['Name'] + ' to address : ' + hex(channel_start_address + channel_record_size * count))
if channel['Tone Type Tx'] != 'OFF':
if channel['Tone Tx'] == 0:
channel['Tone Type Tx'] = 'OFF'
if channel['Tone Type Rx'] != 'OFF':
if channel['Tone Rx'] == 0:
channel['Tone Type Rx'] = 'OFF'
if channel['Tone Type Tx'] == 'CTCSS':
channel['Tone Tx'] = CTCSS_Tones.index(channel['Tone Tx'])
elif channel['Tone Type Tx'] != 'OFF':
channel['Tone Tx'] = DCS_Codes.index(channel['Tone Tx'])
if channel['Tone Type Rx'] == 'CTCSS':
channel['Tone Rx'] = CTCSS_Tones.index(channel['Tone Rx'])
else:
if channel['Tone Type Rx'] != 'OFF':
channel['Tone Rx'] = DCS_Codes.index(channel['Tone Rx'])
tempTSON_INFO = {}
tempTSON_INFO['TS Rx ON'] = 'OFF'
tempTSON_INFO['TS Tx ON'] = 'OFF'
if channel['Rx Freq'] != channel['Tx Freq']:
tempTSON_INFO['TS Rx ON'] = 'OFF'
tempTSON_INFO['TS Tx ON'] = 'OFF'
if channel['TS Rx'] == 'ON':
tempTSON_INFO['TS Rx ON'] = 'ON'
if channel['TS Tx'] == 'ON':
tempTSON_INFO['TS Tx ON'] = 'ON'
channel_record = bytearray(channel_record_size)
try:
p.toBytes(channel_record, channel_info, channel)
except:
raise SystemExit('Channel Error:\n' + str(channel).replace(', ', '\n'))
else:
p.toBytes(channel_record, tson_info, tempTSON_INFO)
template[channel_start_address + channel_record_size * count:channel_start_address + channel_record_size * (count + 1)] = channel_record
count = count + 1
else:
if len(template) != codeplug_size:
raise SystemExit('Codeplug Size Should Be ' + str(codeplug_size) + ' But Was ' + len(template))
return template
def downloadCodeplug(serialdevice):
plug = bytearray()
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 5
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
port.reset_input_buffer()
port.reset_output_buffer()
port.write('Flash Read '.encode('ascii'))
port.write(b'\x00<\x00\x00\x00\x00\x007\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
response = port.read(2)
if len(response) < 2:
raise SystemExit('No Response From Radio!')
else:
port.timeout = 2
response = port.read(105)
if 'EARSAB' in response.decode('ascii', 'ignore'):
print('DR300UV: Begin Download...')
else:
if 'EHRSAB' in response.decode('ascii', 'ignore'):
print('DR880UV: Begin Download...')
else:
raise SystemExit('Unexpected Response From Radio!')
port.timeout = 5
port.reset_input_buffer()
port.reset_output_buffer()
if debug_level == 4:
print('Message rx from plug download handshake:')
print(response)
for i in range(len(response)):
print((hex(response[i]) + ' '), end='')
else:
num_pages = response[16] + response[18]
if num_pages < 1:
raise SystemExit('Unexpected Response From Radio! - ' + response)
else:
for i in range(num_pages):
print('Reading Block ' + str(i + 1) + ' of ' + str(num_pages))
port.write('Read'.encode('ascii'))
plug += port.read(2048)
print('Download Complete...')
return plug
def uploadCodeplug(serialdevice, data):
size = len(data)
if size % 2048 != 0:
data += b'\x00' * (2048 - size % 2048)
block_count = int(len(data) / 2048)
if block_count > 255:
raise SystemExit('Codeplug Too Large!')
response = bytearray('Flash Write'.encode('ascii')) + b'\x00<\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
response[18] = block_count
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 5
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
else:
port.reset_input_buffer()
port.reset_output_buffer()
port.write(response)
bytes = port.read(2)
if len(bytes) < 2:
raise SystemExit('No Response From Radio!')
else:
port.timeout = 2
bytes = port.read(100)
if 'EARSAB' in bytes.decode('ascii', 'ignore'):
print('DR300UV: Begin Upload...')
else:
if 'EHRSAB' in bytes.decode('ascii', 'ignore'):
print('DR880UV: Begin Upload...')
else:
raise SystemExit('Unexpected Response From Radio!')
port.timeout = 5
port.reset_input_buffer()
port.reset_output_buffer()
if 'Write' not in bytes.decode('ascii', 'ignore'):
raise SystemExit('Unexpected Response From Radio! - ' + bytes.decode('ascii', 'ignore'))
for i in range(block_count):
print('Writing Block ' + str(i + 1) + ' of ' + str(block_count))
port.write(data[2048 * i:2048 * (i + 1)])
bytes = port.read(5)
if 'Write' in bytes.decode('ascii', 'ignore'):
pass
elif i == block_count - 1 and 'Check' in bytes.decode('ascii', 'ignore'):
pass
else:
raise SystemExit('Unexpected Response From Radio! - ' + bytes.decode('ascii', 'ignore'))
print('Upload Complete...')
def downloadFactoryCodeplug(serialdevice):
plug = bytearray()
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 5
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
port.reset_input_buffer()
port.reset_output_buffer()
port.write('Flash Read '.encode('ascii'))
port.write(b'\x00<\x00\x00\x00\x00\x007\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00GHOST')
response = port.read(2)
if len(response) < 2:
raise SystemExit('No Response From Radio!')
else:
port.timeout = 2
response = port.read(105)
if 'EARSAB' in response.decode('ascii', 'ignore'):
print('DR300UV: Begin Download...')
else:
if 'EHRSAB' in response.decode('ascii', 'ignore'):
print('DR880UV: Begin Download...')
else:
raise SystemExit('Unexpected Response From Radio!')
port.timeout = 5
port.reset_input_buffer()
port.reset_output_buffer()
if debug_level == 4:
print('Message rx from plug download handshake:')
print(response)
for i in range(len(response)):
print((hex(response[i]) + ' '), end='')
else:
num_pages = response[18] + response[20]
if num_pages < 1:
raise SystemExit('Unexpected Response From Radio!')
else:
for i in range(num_pages):
print('Reading Block ' + str(i + 1) + ' of ' + str(num_pages))
port.write('Read'.encode('ascii'))
plug += port.read(2048)
print('Download Complete...')
return plug
def uploadFactoryCodeplug(serialdevice, data):
size = len(data)
if size % 2048 != 0:
data += b'\x00' * (2048 - size % 2048)
block_count = int(len(data) / 2048)
if block_count > 255:
raise SystemExit('Codeplug Too Large!')
response = bytearray('Flash Write'.encode('ascii')) + b'\x00<\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00GHOST'
response[18] = block_count
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 5
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
else:
port.reset_input_buffer()
port.reset_output_buffer()
port.write(response)
bytes = port.read(2)
if len(bytes) < 2:
raise SystemExit('No Response From Radio!')
else:
port.timeout = 2
bytes = port.read(100)
if 'EARSAB' in bytes.decode('ascii', 'ignore'):
print('DR300UV: Begin Upload...')
else:
if 'EHRSAB' in bytes.decode('ascii', 'ignore'):
print('DR880UV: Begin Upload...')
else:
raise SystemExit('Unexpected Response From Radio!')
port.timeout = 5
port.reset_input_buffer()
port.reset_output_buffer()
if 'Write' not in bytes.decode('ascii', 'ignore'):
raise SystemExit('Unexpected Response From Radio! - ' + bytes.decode('ascii', 'ignore'))
for i in range(block_count):
print('Writing Block ' + str(i + 1) + ' of ' + str(block_count))
port.write(data[2048 * i:2048 * (i + 1)])
bytes = port.read(5)
if 'Write' in bytes.decode('ascii', 'ignore'):
pass
elif i == block_count - 1 and 'Check' in bytes.decode('ascii', 'ignore'):
pass
else:
raise SystemExit('Unexpected Response From Radio! - ' + bytes.decode('ascii', 'ignore'))
print('Upload Complete...')
def uploadHamContacts(serialdevice, csvfile, contactbytes):
RADIO_ID = []
CONTACT_INFO = []
with open(csvfile, 'r', encoding='ascii', errors='surrogateescape') as (csv_file):
csv_reader = csv.DictReader(csv_file, delimiter=',')
for row in csv_reader:
RADIO_ID.append(int(row['RADIO_ID']))
if len(row['LAST_NAME']) > 0 and row['LAST_NAME'] != ' ':
CONTACT_INFO.append(row['CALLSIGN'] + ',' + row['FIRST_NAME'] + ' ' + row['LAST_NAME'] + ',' + row['CITY'] + ',' + row['STATE'] + ',' + row['COUNTRY'])
else:
CONTACT_INFO.append(row['CALLSIGN'] + ',' + row['FIRST_NAME'] + ',' + row['CITY'] + ',' + row['STATE'] + ',' + row['COUNTRY'])
contactcount = len(RADIO_ID)
hamcontacts = bytearray(b'\x00' * contactbytes)
for i in range(contactcount):
hamcontacts[contactbytes * i:contactbytes * i + 2] = RADIO_ID[i].to_bytes(length=3, byteorder='little')
hamcontacts[contactbytes * i + 3:] = CONTACT_INFO[i].encode('ascii', 'ignore')
size = len(hamcontacts[contactbytes * i:contactbytes * i + contactbytes])
if size % contactbytes != 0:
hamcontacts[contactbytes * i:contactbytes * i + contactbytes] += b'\x00' * (contactbytes - size % contactbytes)
else:
if len(hamcontacts) % 2048 != 0:
hamcontacts[len(hamcontacts):] = b'\x00' * (2048 - len(hamcontacts) % 2048)
print('Uploading ' + str(contactcount) + ' Contacts (' + str(contactbytes) + 'bytes)')
block_count = int(len(hamcontacts) / 2048)
if block_count > 18750:
raise SystemExit('Too Many Ham Contacts - 300,000 max!')
writecommand = b''
writecommand += bytearray('Flash Write'.encode('ascii'))
writecommand += b'\x81\x10\x00\x00\x00\x00'
writecommand += bytearray(block_count.to_bytes(2, 'big'))
writecommand += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+'
writecommand += bytearray(contactbytes.to_bytes(1, 'little'))
writecommand += b'+\x00'
writecommand += bytearray(contactcount.to_bytes(3, 'big'))
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 5
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
else:
port.reset_input_buffer()
port.reset_output_buffer()
port.write(writecommand)
bytes = port.read(2)
if len(bytes) < 2:
raise SystemExit('No Response From Radio!')
else:
port.timeout = 2
bytes = port.read(100)
if 'EARSAB' in bytes.decode('ascii', 'ignore'):
print('DR300UV: Begin Upload...')
else:
if 'EHRSAB' in bytes.decode('ascii', 'ignore'):
print('DR880UV: Begin Upload...')
else:
raise SystemExit('Unexpected Response From Radio!')
port.timeout = 5
port.reset_input_buffer()
port.reset_output_buffer()
if 'Write' not in bytes.decode('ascii', 'ignore'):
raise SystemExit('Unexpected Response From Radio! - ' + bytes.decode('ascii', 'ignore'))
for i in range(block_count):
print('Writing Block ' + str(i + 1) + ' of ' + str(block_count))
port.write(hamcontacts[2048 * i:2048 * (i + 1)])
bytes = port.read(5)
if 'Write' in bytes.decode('ascii', 'ignore'):
pass
elif 'Check' in bytes.decode('ascii', 'ignore'):
pass
else:
raise SystemExit('Unexpected Response From Radio! - ' + bytes.decode('ascii', 'ignore'))
print('Upload Complete...')
def uploadHamGroups(serialdevice, csvfile):
GROUP_ID = []
GROUP_NAME = []
with open(csvfile, 'r', encoding='ascii', errors='surrogateescape') as (csv_file):
csv_reader = csv.DictReader(csv_file, delimiter=',')
for row in csv_reader:
GROUP_ID.append(int(row['GROUP_ID']))
GROUP_NAME.append(row['GROUP_NAME'])
groupcount = len(GROUP_ID)
hamgroups = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
for i in range(groupcount):
hamgroups[16 * i:16 * i + 2] = GROUP_ID[i].to_bytes(length=3, byteorder='little')
hamgroups[16 * i + 3:] = GROUP_NAME[i].encode('ascii', 'ignore')
size = len(hamgroups[16 * i:16 * i + 16])
if size % 16 != 0:
hamgroups[16 * i:16 * i + 16] += b'\x00' * (16 - size % 16)
else:
if len(hamgroups) % 2048 != 0:
hamgroups[len(hamgroups):] = b'\x00' * (2048 - len(hamgroups) % 2048)
print('Uploading ' + str(groupcount) + ' Ham Groups')
block_count = int(len(hamgroups) / 2048)
if block_count > 235:
raise SystemExit('Too Many Ham Groups - 30,000 max!')
writecommand = bytearray('Flash Write'.encode('ascii'))
writecommand += b'\x82\x98\x00\x00\x00\x00'
writecommand += bytearray(block_count.to_bytes(2, 'big'))
writecommand += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x10+\x00'
writecommand += bytearray(groupcount.to_bytes(3, 'big'))
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 5
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
else:
port.reset_input_buffer()
port.reset_output_buffer()
port.write(writecommand)
bytes = port.read(2)
if len(bytes) < 2:
raise SystemExit('No Response From Radio!')
else:
port.timeout = 2
bytes = port.read(100)
if 'EARSAB' in bytes.decode('ascii', 'ignore'):
print('DR300UV: Begin Upload...')
else:
if 'EHRSAB' in bytes.decode('ascii', 'ignore'):
print('DR880UV: Begin Upload...')
else:
raise SystemExit('Unexpected Response From Radio!')
port.timeout = 5
port.reset_input_buffer()
port.reset_output_buffer()
if 'Write' not in bytes.decode('ascii', 'ignore'):
raise SystemExit('Unexpected Response From Radio! - ' + bytes.decode('ascii', 'ignore'))
for i in range(block_count):
print('Writing Block ' + str(i + 1) + ' of ' + str(block_count))
port.write(hamgroups[2048 * i:2048 * (i + 1)])
bytes = port.read(5)
if 'Write' in bytes.decode('ascii', 'ignore'):
pass
elif 'Check' in bytes.decode('ascii', 'ignore'):
pass
else:
raise SystemExit('Unexpected Response From Radio!')
print('Upload Complete...')
def uploadFirmware(serialdevice, data):
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 2
if port.isOpen() == False:
port.Open()
else:
port.reset_input_buffer()
port.reset_output_buffer()
print('Starting Firmware Upload Process')
size = len(data)
if size % 2048 != 0:
data += b'\x00' * (2048 - size % 2048)
else:
num_blocks = int(len(data) / 2048)
writecommand = bytearray('Erase'.encode('ascii'))
writecommand += b' \x00\x00\x00\x00\x00\x00'
writecommand += bytearray((num_blocks - 1).to_bytes(length=2, byteorder='big'))
port.write(writecommand)
bytes = port.read(2)
if len(bytes) < 2:
raise SystemExit('No Response From Radio!')
port.reset_input_buffer()
port.reset_output_buffer()
bytes = port.read_until(b'Erase ok')
print(bytes)
if 'IAP' not in bytes.decode('ascii', 'ignore'):
raise SystemExit('Invalid Response From Radio - Is The Radio In DFU Mode?')
else:
if 'Erase ok' in bytes.decode('ascii', 'ignore'):
print('Success: Begin Upload...')
for i in range(num_blocks):
block = data[2048 * i:2048 * (i + 1)]
print('Writing Block ' + str(i + 1) + ' of ' + str(num_blocks))
port.write(block)
bytes = port.read(3)
if bytes.decode('ascii', 'ignore') == 'kyd':
pass
else:
raise SystemExit('Unexpected Response From Radio!')
else:
bytes = port.read(13)
if 'Check' in bytes.decode('ascii', 'ignore'):
print('Firmware Upload Complete')
else:
raise SystemExit('Checksum Not Received But May Still Have Completed Successfully.')
def DownloadBootLogo(serialdevice):
bootlogo = bytearray()
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 2
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
else:
port.reset_input_buffer()
port.reset_output_buffer()
port.write('Flash Read '.encode('ascii'))
port.write(b'\x80\x04\x00\x00\x00\x00\x01~\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
bytes = port.read(2)
if len(bytes) < 2:
raise SystemExit('No Response From Radio!')
else:
bytes = port.read(36)
if len(bytes) < 36:
raise SystemExit('Invalid Response From Radio - Is The Radio In DFU Mode?')
else:
if 'Read_2M_V' in bytes.decode('ascii', 'ignore'):
print('Success: Begin Upload...')
else:
raise SystemExit('Invalid Response From Radio - Is The Radio In DFU Mode?')
port.reset_input_buffer()
port.reset_output_buffer()
num_pages = 20
if num_pages < 1:
raise SystemExit('Unexpected Response From Radio!')
else:
for i in range(num_pages):
print('Reading Block ' + str(i + 1) + ' of ' + str(num_pages))
port.write('Read'.encode('ascii'))
bootlogo += port.read(2048)
print('Download Complete...')
return bootlogo
def UploadBootLogo(serialdevice, data):
fileheader = b'\x10\x10\x00'
if data[0:3] != fileheader:
raise SystemExit('Incorrect Or Corrupt Boot Image Binary File!')
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 2
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
else:
port.reset_input_buffer()
port.reset_output_buffer()
size = len(data)
if size % 2048 != 0:
data += b'\x00' * (2048 - size % 2048)
else:
num_blocks = int(len(data) / 2048)
response = bytearray('Flash Write'.encode('ascii')) + b'\x80\x04\x00\x00\x00\x00\x00\xff'
response[18] = num_blocks
port.write(response)
bytes = port.read(2)
if len(bytes) < 2:
raise SystemExit('No Response From Radio - Is The Radio In DFU Mode?')
bytes = port.read(37)
if len(bytes) < 37:
raise SystemExit('Invalid Response From Radio - Is The Radio In DFU Mode?')
else:
if 'Write_2M_V' in bytes.decode('ascii', 'ignore'):
print('Success: Begin Upload...')
else:
raise SystemExit('Invalid Response From Radio - Is The Radio In DFU Mode?')
port.reset_input_buffer()
port.reset_output_buffer()
bytes = port.read(5)
for i in range(num_blocks):
block = data[2048 * i:2048 * (i + 1)]
print('Writing Block ' + str(i + 1) + ' of ' + str(num_blocks))
port.write(block)
bytes = port.read(5)
if 'Write' in bytes.decode('ascii', 'ignore'):
pass
else:
raise SystemExit('Unexpected Response From Radio!')
else:
bytes = port.read(13)
if 'Check' in bytes.decode('ascii', 'ignore'):
print('Boot Logo Upload Complete')
else:
raise SystemExit('Unexpected Response From Radio')
def downloadRecord(serialdevice):
raise SystemExit('Not Yet Implemented Correctly!')
record_data = bytearray()
with serial.Serial(serialdevice) as (port):
port.baudrate = 115200
port.timeout = 5
print('Establishing Connection To Radio...')
if port.isOpen() == False:
port.Open()
else:
port.write('Flash Read '.encode('ascii'))
port.write(b'\x00<\x00\x00\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
response = port.read(103)
print(response)
if len(response) < 11:
raise SystemExit('No Response From Radio!')
else:
print('Success: Begin Download...')
if debug_level == 4:
print('Message rx from record_data download handshake:')
print(response)
num_pages = response[18] + response[20]
if num_pages < 1:
raise SystemExit('Unexpected Response From Radio!')
else:
for i in range(num_pages):
print('Reading Block ' + str(i + 1) + ' of ' + str(num_pages))
port.write('Read'.encode('ascii'))
record_data += port.read(2048)
print('Download Complete...')
return record_data
def debugMsg(level, message):
if level <= debug_level:
print(message)
debug_level = 0
default_serial_device = ''
if platform.system() == 'Linux':
default_serial_device = '/dev/ttyUSB0'
else:
if platform.system() == 'Windows':
default_serial_device = 'COM1'
else:
parser = argparse.ArgumentParser(description='Codeplug/firmware upgrade tool, GNU GPL v3 or later, (C) 2020-21 David Pye davidmpye@gmail.com')
parser.add_argument('action', type=str, choices=['upload', 'upload_factory', 'download_bootlogo', 'upload_bootlogo', 'download', 'download_factory', 'flash_fw', 'download_bin', 'upload_bin', 'decompile_bin', 'upload_hamcontacts', 'upload_hamgroups', 'download_record'], help="upload - Compile and upload a JSON-formatted codeplug to the radio,upload_factory - Compile and upload a JSON-formatted codeplug to the radio as the 'factory reset' codeplug,download_bootlogo - Read the boot logo of the radio, file format is RGB565 binary with an 8 byte header,upload_bootlogo - Replace the boot logo of the radio, file format is RGB565 binary with an 8 byte header,download - Download the codeplug from the radio and convert into a JSON-formatted file,download_factory - Download the 'factory codeplug' from the radio and convert into a JSON-formatted file,flash_fw - Upgrade the radio's firmware (radio must be powered on while pressing P1, displaying a grey screen before upload),upload_hamcontacts - Upload Ham Contacts to the radio, file must be in RadioID.net CSV format and specify type with --contactbytes {16 or 128}upload_hamgroups - Upload Ham Groups to the radio, file must be in CSV format with headers 'GROUP_NAME' & 'GROUP_ID'")
parser.add_argument('filename', type=str, help='Filename to upload, or to save')
parser.add_argument('--contactbytes', default=0, type=int, choices=[16, 128], help='Ham Contacts Bytes (16 or 128)')
parser.add_argument('--device', default=default_serial_device, help='Specify device to use (default COM1 on Windows, default /dev/ttyUSB0 on Linux')
parser.add_argument('--debuglevel', default=[0], type=int, nargs=1, help='Debug level (0 = default, 4 = max)')
args = parser.parse_args()
debug_level = args.debuglevel[0]
if args.action == 'download':
data = downloadCodeplug(args.device)
json = decompileCodeplug(data)
f = open(args.filename, 'w')
f.write(json)
f.close()
else:
if args.action == 'download_factory':
data = downloadFactoryCodeplug(args.device)
json = decompileCodeplug(data)
f = open(args.filename, 'w')
f.write(json)
f.close()
else:
if args.action == 'upload':
f = open(args.filename, 'r')
input = f.read()
data = compileCodeplug(input)
f.close()
uploadCodeplug(args.device, data)
else:
if args.action == 'upload_factory':
f = open(args.filename, 'r')
input = f.read()
data = compileCodeplug(input)
f.close()
uploadFactoryCodeplug(args.device, data)
else:
if args.action == 'flash_fw':
f = open(args.filename, 'rb')
fw = f.read()
uploadFirmware(args.device, fw)
else:
if args.action == 'download_bin':
f = open(args.filename, 'wb')
f.write(downloadCodeplug(args.device))
else:
if args.action == 'upload_bin':
f = open(args.filename, 'rb')
data = f.read()
uploadCodeplug(args.device, data)
else:
if args.action == 'decompile_bin':
f = open(args.filename, 'rb')
input = f.read()
data = decompileCodeplug(input)
f.close()
ff = open(args.filename[:-4] + '.json', 'w')
ff.write(data)
else:
if args.action == 'upload_hamcontacts':
if args.contactbytes == 0:
raise SystemExit("Missing Argument '--contactbytes {16 or 128}")
uploadHamContacts(args.device, args.filename, args.contactbytes)
else:
if args.action == 'upload_hamgroups':
uploadHamGroups(args.device, args.filename)
else:
if args.action == 'download_record':
f = open(args.filename, 'wb')
f.write(downloadRecord(args.device))
else:
if args.action == 'download_bootlogo':
f = open(args.filename, 'wb')
f.write(DownloadBootLogo(args.device))
else:
if args.action == 'upload_bootlogo':
f = open(args.filename, 'rb')
fw = f.read()
UploadBootLogo(args.device, fw)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment