mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
Merge remote-tracking branch 'origin/main' into cli-overhaul
This commit is contained in:
@@ -29,13 +29,12 @@ LITE = r"""
|
||||
|
||||
# create by http://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Chameleon%20Ultra
|
||||
BANNER = f"""
|
||||
██████╗██╗ ██╗ █████╗ ███╗ ███╗███████╗██╗ ███████╗ ██████╗ ███╗ ██╗
|
||||
██╔════╝██║ ██║██╔══██╗████╗ ████║██╔════╝██║ ██╔════╝██╔═══██╗████╗ ██║
|
||||
██║ ███████║███████║██╔████╔██║█████╗ ██║ █████╗ ██║ ██║██╔██╗ ██║
|
||||
██║ ██╔══██║██╔══██║██║╚██╔╝██║██╔══╝ ██║ ██╔══╝ ██║ ██║██║╚██╗██║
|
||||
╚██████╗██║ ██║██║ ██║██║ ╚═╝ ██║███████╗███████╗███████╗╚██████╔╝██║ ╚████║
|
||||
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═══╝
|
||||
|
||||
██████╗██╗ ██╗ █████╗ ██╗ ██╗███████╗██╗ ███████╗ █████╗ ██╗ ██╗
|
||||
██╔════╝██║ ██║██╔══██╗███╗ ███║██╔════╝██║ ██╔════╝██╔══██╗███╗ ██║
|
||||
██║ ███████║███████║████████║█████╗ ██║ █████╗ ██║ ██║████╗██║
|
||||
██║ ██╔══██║██╔══██║██╔██╔██║██╔══╝ ██║ ██╔══╝ ██║ ██║██╔████║
|
||||
╚██████╗██║ ██║██║ ██║██║╚═╝██║███████╗███████╗███████╗╚█████╔╝██║╚███║
|
||||
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚════╝ ╚═╝ ╚══╝
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -157,6 +157,8 @@ hw_address = hw.subgroup('address', 'Device address get')
|
||||
hw_mode = hw.subgroup('mode', 'Device mode get/set')
|
||||
hw_slot = hw.subgroup('slot', 'Emulation tag slot.')
|
||||
hw_slot_nick = hw_slot.subgroup('nick', 'Get/Set tag nick name for slot')
|
||||
hw_settings = hw.subgroup('settings', 'Chameleon settings management')
|
||||
hw_settings_animation = hw_settings.subgroup('animation', 'Manage wake-up and sleep animation modes')
|
||||
|
||||
hf = CLITree('hf', 'high frequency tag/reader')
|
||||
hf_14a = hf.subgroup('14a', 'ISO14443-a tag read/write/info...')
|
||||
@@ -248,6 +250,19 @@ class HWAddressGet(DeviceRequiredUnit):
|
||||
print(f' - Device address: ' + self.cmd.get_device_address())
|
||||
|
||||
|
||||
@hw.command('version', 'Get current device firmware version')
|
||||
class HWVersion(DeviceRequiredUnit):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
return None
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
fw_version_int = self.cmd.get_firmware_version()
|
||||
fw_version = f'v{fw_version_int // 256}.{fw_version_int % 256}'
|
||||
git_version = self.cmd.get_git_version()
|
||||
print(f' - Version: {fw_version} ({git_version})')
|
||||
|
||||
|
||||
@hf_14a.command('scan', 'Scan 14a tag, and print basic information')
|
||||
class HF14AScan(ReaderRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
@@ -795,11 +810,14 @@ class SlotIndexRequireUnit(DeviceRequiredUnit):
|
||||
|
||||
@staticmethod
|
||||
def add_slot_args(parser: ArgumentParserNoExit):
|
||||
slot_choices = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
slot_choices = [x.value for x in chameleon_cmd.SlotNumber]
|
||||
help_str = f"Slot Indexes: {slot_choices}"
|
||||
|
||||
parser.add_argument('-s', "--slot", type=int, required=True,
|
||||
help="Slot index", metavar="number", choices=slot_choices)
|
||||
help=help_str, metavar="number", choices=slot_choices)
|
||||
return parser
|
||||
|
||||
|
||||
class SenseTypeRequireUnit(DeviceRequiredUnit):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
@@ -810,12 +828,34 @@ class SenseTypeRequireUnit(DeviceRequiredUnit):
|
||||
|
||||
@staticmethod
|
||||
def add_sense_type_args(parser: ArgumentParserNoExit):
|
||||
slot_choices = [1, 2]
|
||||
sense_choices = chameleon_cmd.TagSenseType.list()
|
||||
|
||||
help_str = ""
|
||||
for s in chameleon_cmd.TagSenseType:
|
||||
if s == chameleon_cmd.TagSenseType.TAG_SENSE_NO:
|
||||
continue
|
||||
help_str += f"{s.value} = {s}, "
|
||||
|
||||
parser.add_argument('-st', "--sense_type", type=int, required=True,
|
||||
help="Sense type", metavar="number", choices=slot_choices)
|
||||
help=help_str, metavar="number", choices=sense_choices)
|
||||
return parser
|
||||
|
||||
|
||||
@hw_slot.command('info', 'Get information about slots')
|
||||
class HWSlotInfo(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
return
|
||||
|
||||
# hw slot info
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
data = self.cmd.get_slot_info().data
|
||||
selected = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot().data[0])
|
||||
for slot in chameleon_cmd.SlotNumber:
|
||||
print(f' - Slot {slot} data{" (active)" if slot == selected else ""}:')
|
||||
print(f' HF: {chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2])}')
|
||||
print(f' LF: {chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2 + 1])}')
|
||||
|
||||
|
||||
@hw_slot.command('change', 'Set emulation tag slot activated.')
|
||||
class HWSlotSet(SlotIndexRequireUnit):
|
||||
|
||||
@@ -836,10 +876,10 @@ class TagTypeRequiredUnit(DeviceRequiredUnit):
|
||||
def add_type_args(parser: ArgumentParserNoExit):
|
||||
type_choices = chameleon_cmd.TagSpecificType.list()
|
||||
help_str = ""
|
||||
for name, value in chameleon_cmd.TagSpecificType.__members__.items():
|
||||
if value == chameleon_cmd.TagSpecificType.TAG_TYPE_UNKNOWN:
|
||||
for t in chameleon_cmd.TagSpecificType:
|
||||
if t == chameleon_cmd.TagSpecificType.TAG_TYPE_UNKNOWN:
|
||||
continue
|
||||
help_str += f"{value} = {name.replace('TAG_TYPE_', '')}, "
|
||||
help_str += f"{t.value} = {t}, "
|
||||
parser.add_argument('-t', "--type", type=int, required=True, help=help_str,
|
||||
metavar="number", choices=type_choices)
|
||||
return parser
|
||||
@@ -923,7 +963,7 @@ class HWSlotNickSet(SlotIndexRequireUnit, SenseTypeRequireUnit):
|
||||
parser = ArgumentParserNoExit()
|
||||
self.add_slot_args(parser)
|
||||
self.add_sense_type_args(parser)
|
||||
parser.add_argument('-n', '--name', type=str, required=True, help="Yout tag nick name for slot")
|
||||
parser.add_argument('-n', '--name', type=str, required=True, help="Your tag nick name for slot")
|
||||
return parser
|
||||
|
||||
# hw slot nick set -s 1 -st 1 -n 测试名称保存
|
||||
@@ -931,7 +971,8 @@ class HWSlotNickSet(SlotIndexRequireUnit, SenseTypeRequireUnit):
|
||||
slot_num = args.slot
|
||||
sense_type = args.sense_type
|
||||
name: str = args.name
|
||||
if len(name.encode(encoding="gbk")) > 32:
|
||||
uname = name.encode(encoding="utf8")
|
||||
if len(uname) > 32:
|
||||
raise ValueError("Your tag nick name too long.")
|
||||
self.cmd.set_slot_tag_nick_name(slot_num, sense_type, name)
|
||||
print(f' - Set tag nick name for slot {slot_num} success.')
|
||||
@@ -950,7 +991,7 @@ class HWSlotNickGet(SlotIndexRequireUnit, SenseTypeRequireUnit):
|
||||
slot_num = args.slot
|
||||
sense_type = args.sense_type
|
||||
res = self.cmd.get_slot_tag_nick_name(slot_num, sense_type)
|
||||
print(f' - Get tag nick name for slot {slot_num}: {res.data.decode(encoding="gbk")}')
|
||||
print(f' - Get tag nick name for slot {slot_num}: {res.data.decode(encoding="utf8")}')
|
||||
|
||||
|
||||
@hw_slot.command('update', 'Update config & data to device flash')
|
||||
@@ -978,8 +1019,8 @@ class HWSlotOpenAll(DeviceRequiredUnit):
|
||||
lf_type = chameleon_cmd.TagSpecificType.TAG_TYPE_EM410X
|
||||
|
||||
# set all slot
|
||||
for slot in range(1,9):
|
||||
print(f' Slot{slot} setting...')
|
||||
for slot in chameleon_cmd.SlotNumber:
|
||||
print(f' Slot {slot} setting...')
|
||||
# first to set tag type
|
||||
self.cmd.set_slot_tag_type(slot, hf_type)
|
||||
self.cmd.set_slot_tag_type(slot, lf_type)
|
||||
@@ -988,11 +1029,11 @@ class HWSlotOpenAll(DeviceRequiredUnit):
|
||||
self.cmd.set_slot_data_default(slot, lf_type)
|
||||
# finally, we can enable this slot.
|
||||
self.cmd.set_slot_enable(slot, True)
|
||||
print(f' Open slot{slot} finish')
|
||||
print(f' Slot {slot} setting done.')
|
||||
|
||||
# update config and save to flash
|
||||
self.cmd.update_slot_data_config()
|
||||
print(f' - Open all slot and set data to default success.')
|
||||
print(f' - Succeeded opening all slots and setting data to default.')
|
||||
|
||||
|
||||
@hw.command('dfu', 'Restart application to bootloader mode(Not yet implement dfu).')
|
||||
@@ -1011,3 +1052,86 @@ class HWDFU(DeviceRequiredUnit):
|
||||
print(" - Enter success @.@~")
|
||||
# let time for comm thread to send dfu cmd and close port
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
@hw_settings_animation.command('get', 'Get current animation mode value')
|
||||
class HWSettingsAnimationGet(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
return None
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
resp: chameleon_com.Response = self.cmd.get_settings_animation()
|
||||
if resp.data[0] == 0:
|
||||
print("Full animation")
|
||||
elif resp.data[0] == 1:
|
||||
print("Minimal animation")
|
||||
elif resp.data[0] == 2:
|
||||
print("No animation")
|
||||
else:
|
||||
print("Unknown setting value, something failed.")
|
||||
|
||||
|
||||
@hw_settings_animation.command('set', 'Change chameleon animation mode')
|
||||
class HWSettingsAnimationSet(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
parser.add_argument('-m', '--mode', type=int, required=True, help="0 is full (default), 1 is minimal (only single pass on button wakeup), 2 is none", choices=[0, 1, 2])
|
||||
return parser
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
mode = args.mode
|
||||
self.cmd.set_settings_animation(mode)
|
||||
print("Animation mode change success. Do not forget to store your settings in flash!")
|
||||
|
||||
|
||||
@hw_settings.command('store', 'Store current settings to flash')
|
||||
class HWSettingsStore(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
return None
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
print("Storing settings...")
|
||||
resp: chameleon_com.Response = self.cmd.store_settings()
|
||||
if resp.status == chameleon_status.Device.STATUS_DEVICE_SUCCESS:
|
||||
print(" - Store success @.@~")
|
||||
else:
|
||||
print(" - Store failed")
|
||||
|
||||
|
||||
@hw_settings.command('reset', 'Reset settings to default values')
|
||||
class HWSettingsReset(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
return None
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
print("Initializing settings...")
|
||||
resp: chameleon_com.Response = self.cmd.reset_settings()
|
||||
if resp.status == chameleon_status.Device.STATUS_DEVICE_SUCCESS:
|
||||
print(" - Reset success @.@~")
|
||||
else:
|
||||
print(" - Reset failed")
|
||||
|
||||
|
||||
@hw.command('factory_reset', 'Wipe all data and return to factory settings')
|
||||
class HWFactoryReset(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit:
|
||||
parser = ArgumentParserNoExit()
|
||||
parser.description = "Permanently wipes Chameleon to factory settings. " \
|
||||
"This will delete all your slot data and custom settings. " \
|
||||
"There's no going back."
|
||||
parser.add_argument(
|
||||
"--i-know-what-im-doing",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Just to be sure :)"
|
||||
)
|
||||
return parser
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
if not args.i_know_what_im_doing:
|
||||
print("This time your data's safe. Read the command documentation next time.")
|
||||
return
|
||||
resp = self.cmd.factory_reset()
|
||||
if resp.status == chameleon_status.Device.STATUS_DEVICE_SUCCESS:
|
||||
print(" - Reset successful! Please reconnect.")
|
||||
print(" - A Serial Error below is normal, please ignore it")
|
||||
else:
|
||||
print(" - Reset failed!")
|
||||
|
||||
@@ -21,6 +21,18 @@ DATA_CMD_ENTER_BOOTLOADER = 1010
|
||||
DATA_CMD_GET_DEVICE_CHIP_ID = 1011
|
||||
DATA_CMD_GET_DEVICE_ADDRESS = 1012
|
||||
|
||||
DATA_CMD_SAVE_SETTINGS = 1013
|
||||
DATA_CMD_RESET_SETTINGS = 1014
|
||||
DATA_CMD_SET_ANIMATION_MODE = 1015
|
||||
DATA_CMD_GET_ANIMATION_MODE = 1016
|
||||
|
||||
DATA_CMD_GET_GIT_VERSION = 1017
|
||||
|
||||
DATA_CMD_GET_ACTIVE_SLOT = 1018
|
||||
DATA_CMD_GET_SLOT_INFO = 1019
|
||||
|
||||
DATA_CMD_WIPE_FDS = 1020
|
||||
|
||||
DATA_CMD_SCAN_14A_TAG = 2000
|
||||
DATA_CMD_MF1_SUPPORT_DETECT = 2001
|
||||
DATA_CMD_MF1_NT_LEVEL_DETECT = 2002
|
||||
@@ -43,6 +55,27 @@ DATA_CMD_SET_MF1_DETECTION_ENABLE = 5003
|
||||
DATA_CMD_GET_MF1_DETECTION_COUNT = 5004
|
||||
DATA_CMD_GET_MF1_DETECTION_RESULT = 5005
|
||||
|
||||
@enum.unique
|
||||
class SlotNumber(enum.IntEnum):
|
||||
SLOT_1 = 1,
|
||||
SLOT_2 = 2,
|
||||
SLOT_3 = 3,
|
||||
SLOT_4 = 4,
|
||||
SLOT_5 = 5,
|
||||
SLOT_6 = 6,
|
||||
SLOT_7 = 7,
|
||||
SLOT_8 = 8,
|
||||
|
||||
@staticmethod
|
||||
def to_fw(index: int): # can be int or SlotNumber
|
||||
# SlotNumber() will raise error for us if index not in slot range
|
||||
return SlotNumber(index).value - 1
|
||||
|
||||
@staticmethod
|
||||
def from_fw(index: int):
|
||||
# SlotNumber() will raise error for us if index not in fw range
|
||||
return SlotNumber(index + 1)
|
||||
|
||||
|
||||
@enum.unique
|
||||
class TagSenseType(enum.IntEnum):
|
||||
@@ -54,6 +87,20 @@ class TagSenseType(enum.IntEnum):
|
||||
TAG_SENSE_HF = 2,
|
||||
|
||||
|
||||
@staticmethod
|
||||
def list(exclude_unknown=True):
|
||||
enum_list = list(map(int, TagSenseType))
|
||||
if exclude_unknown:
|
||||
enum_list.remove(TagSenseType.TAG_SENSE_NO)
|
||||
return enum_list
|
||||
|
||||
def __str__(self):
|
||||
if self == TagSenseType.TAG_SENSE_LF:
|
||||
return "LF"
|
||||
elif self == TagSenseType.TAG_SENSE_HF:
|
||||
return "HF"
|
||||
return "None"
|
||||
|
||||
@enum.unique
|
||||
class TagSpecificType(enum.IntEnum):
|
||||
# 特定的且必须存在的标志不存在的类型
|
||||
@@ -77,6 +124,25 @@ class TagSpecificType(enum.IntEnum):
|
||||
enum_list.remove(TagSpecificType.TAG_TYPE_UNKNOWN)
|
||||
return enum_list
|
||||
|
||||
def __str__(self):
|
||||
if self == TagSpecificType.TAG_TYPE_EM410X:
|
||||
return "EM410X"
|
||||
elif self == TagSpecificType.TAG_TYPE_MIFARE_Mini:
|
||||
return "Mifare Mini"
|
||||
elif self == TagSpecificType.TAG_TYPE_MIFARE_1024:
|
||||
return "Mifare Classic 1k"
|
||||
elif self == TagSpecificType.TAG_TYPE_MIFARE_2048:
|
||||
return "Mifare Classic 2k"
|
||||
elif self == TagSpecificType.TAG_TYPE_MIFARE_4096:
|
||||
return "Mifare Classic 4k"
|
||||
elif self == TagSpecificType.TAG_TYPE_NTAG_213:
|
||||
return "NTAG 213"
|
||||
elif self == TagSpecificType.TAG_TYPE_NTAG_215:
|
||||
return "NTAG 215"
|
||||
elif self == TagSpecificType.TAG_TYPE_NTAG_216:
|
||||
return "NTAG 216"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
class ChameleonCMD:
|
||||
"""
|
||||
@@ -109,7 +175,10 @@ class ChameleonCMD:
|
||||
"""
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_ADDRESS, 0x00, None)
|
||||
return resp.data[::-1].hex()
|
||||
|
||||
|
||||
def get_git_version(self) -> str:
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_GIT_VERSION, 0x00, None)
|
||||
return resp.data.decode('utf-8')
|
||||
|
||||
def is_reader_device_mode(self) -> bool:
|
||||
"""
|
||||
@@ -280,21 +349,34 @@ class ChameleonCMD:
|
||||
data.extend(key)
|
||||
return self.device.send_cmd_sync(DATA_CMD_WRITE_EM410X_TO_T5577, 0x00, data)
|
||||
|
||||
def get_slot_info(self):
|
||||
"""
|
||||
Get slots info
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_INFO, 0x00, None)
|
||||
|
||||
def get_active_slot(self):
|
||||
"""
|
||||
Get selected slot
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_ACTIVE_SLOT, 0x00, None)
|
||||
|
||||
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
def set_slot_activated(self, slot_index):
|
||||
def set_slot_activated(self, slot_index: SlotNumber):
|
||||
"""
|
||||
设置当前激活使用的卡槽
|
||||
:param slot_index: 卡槽索引,从 1 - 8(不是从0下标开始)
|
||||
:return:
|
||||
"""
|
||||
if slot_index < 1 or slot_index > 8:
|
||||
raise ValueError("The slot index range error(1-8)")
|
||||
# SlotNumber() will raise error for us if slot_index not in slot range
|
||||
data = bytearray()
|
||||
data.append(slot_index - 1)
|
||||
data.append(SlotNumber.to_fw(slot_index))
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_ACTIVATED, 0x00, data)
|
||||
|
||||
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
def set_slot_tag_type(self, slot_index: int, tag_type: TagSpecificType):
|
||||
def set_slot_tag_type(self, slot_index: SlotNumber, tag_type: TagSpecificType):
|
||||
"""
|
||||
设置当前卡槽的模拟卡的标签类型
|
||||
注意:此操作并不会更改flash中的数据,flash中的数据的变动仅在下次保存时更新
|
||||
@@ -302,15 +384,14 @@ class ChameleonCMD:
|
||||
:param tag_type: 标签类型
|
||||
:return:
|
||||
"""
|
||||
if slot_index < 1 or slot_index > 8:
|
||||
raise ValueError("The slot index range error(1-8)")
|
||||
# SlotNumber() will raise error for us if slot_index not in slot range
|
||||
data = bytearray()
|
||||
data.append(slot_index - 1)
|
||||
data.append(SlotNumber.to_fw(slot_index))
|
||||
data.append(tag_type)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_TAG_TYPE, 0x00, data)
|
||||
|
||||
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
def set_slot_data_default(self, slot_index: int, tag_type: TagSpecificType):
|
||||
def set_slot_data_default(self, slot_index: SlotNumber, tag_type: TagSpecificType):
|
||||
"""
|
||||
设置指定卡槽的模拟卡的数据为缺省数据
|
||||
注意:此API会将flash中的数据一并进行设置
|
||||
@@ -318,25 +399,23 @@ class ChameleonCMD:
|
||||
:param tag_type: 要设置的缺省标签类型
|
||||
:return:
|
||||
"""
|
||||
if slot_index < 1 or slot_index > 8:
|
||||
raise ValueError("The slot index range error(1-8)")
|
||||
# SlotNumber() will raise error for us if slot_index not in slot range
|
||||
data = bytearray()
|
||||
data.append(slot_index - 1)
|
||||
data.append(SlotNumber.to_fw(slot_index))
|
||||
data.append(tag_type)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_DATA_DEFAULT, 0x00, data)
|
||||
|
||||
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
def set_slot_enable(self, slot_index: int, enable: bool):
|
||||
def set_slot_enable(self, slot_index: SlotNumber, enable: bool):
|
||||
"""
|
||||
设置指定的卡槽是否使能
|
||||
:param slot_index: 卡槽号码
|
||||
:param enable: 是否使能
|
||||
:return:
|
||||
"""
|
||||
if slot_index < 1 or slot_index > 8:
|
||||
raise ValueError("The slot index range error(1-8)")
|
||||
# SlotNumber() will raise error for us if slot_index not in slot range
|
||||
data = bytearray()
|
||||
data.append(slot_index - 1)
|
||||
data.append(SlotNumber.to_fw(slot_index))
|
||||
data.append(0x01 if enable else 0x00)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_ENABLE, 0X00, data)
|
||||
|
||||
@@ -409,7 +488,7 @@ class ChameleonCMD:
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_ANTI_COLLISION_RES, 0X00, data)
|
||||
|
||||
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
def set_slot_tag_nick_name(self, slot: int, sense_type: int, name: str):
|
||||
def set_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType, name: bytes):
|
||||
"""
|
||||
设置MF1的模拟卡的防冲撞资源信息
|
||||
:param slot: 卡槽号码
|
||||
@@ -417,13 +496,14 @@ class ChameleonCMD:
|
||||
:param name: 卡槽昵称
|
||||
:return:
|
||||
"""
|
||||
# SlotNumber() will raise error for us if slot not in slot range
|
||||
data = bytearray()
|
||||
data.extend([slot, sense_type])
|
||||
data.extend(name.encode(encoding="gbk"))
|
||||
data.extend([SlotNumber.to_fw(slot), sense_type])
|
||||
data.extend(name)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_TAG_NICK, 0x00, data)
|
||||
|
||||
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
def get_slot_tag_nick_name(self, slot: int, sense_type: int):
|
||||
def get_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType):
|
||||
"""
|
||||
设置MF1的模拟卡的防冲撞资源信息
|
||||
:param slot: 卡槽号码
|
||||
@@ -431,8 +511,9 @@ class ChameleonCMD:
|
||||
:param name: 卡槽昵称
|
||||
:return:
|
||||
"""
|
||||
# SlotNumber() will raise error for us if slot not in slot range
|
||||
data = bytearray()
|
||||
data.extend([slot, sense_type])
|
||||
data.extend([SlotNumber.to_fw(slot), sense_type])
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_TAG_NICK, 0x00, data)
|
||||
|
||||
def update_slot_data_config(self):
|
||||
@@ -448,6 +529,36 @@ class ChameleonCMD:
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_auto(DATA_CMD_ENTER_BOOTLOADER, 0x00, close=True)
|
||||
|
||||
def get_settings_animation(self):
|
||||
"""
|
||||
Get animation mode value
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_ANIMATION_MODE, 0x00, None)
|
||||
|
||||
def set_settings_animation(self, value: int):
|
||||
"""
|
||||
Set animation mode value
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_ANIMATION_MODE, 0x00, bytearray([value]))
|
||||
|
||||
def reset_settings(self):
|
||||
"""
|
||||
Reset settings stored in flash memory
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_RESET_SETTINGS, 0x00)
|
||||
|
||||
def store_settings(self):
|
||||
"""
|
||||
Store settings to flash memory
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SAVE_SETTINGS, 0x00)
|
||||
|
||||
def factory_reset(self):
|
||||
"""
|
||||
Reset to factory settings
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_WIPE_FDS, 0x00)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -41,6 +41,8 @@ class Device(metaclass=MetaDevice):
|
||||
STATUS_INVALID_CMD = 0x67 # 无效的指令
|
||||
STATUS_DEVICE_SUCCESS = 0x68 # 设备相关操作成功执行
|
||||
STATUS_NOT_IMPLEMENTED = 0x69 # 调用了某些未实现的操作,属于开发者遗漏的错误
|
||||
STATUS_FLASH_WRITE_FAIL = 0x70 # flash写入失败
|
||||
STATUS_FLASH_READ_FAIL = 0x71 # flash读取失败
|
||||
|
||||
|
||||
message = {
|
||||
@@ -68,4 +70,6 @@ message = {
|
||||
Device.STATUS_INVALID_CMD : "API request fail, cmd invalid",
|
||||
Device.STATUS_DEVICE_SUCCESS : "Device operation succeeded",
|
||||
Device.STATUS_NOT_IMPLEMENTED : "Some api not implemented",
|
||||
Device.STATUS_FLASH_WRITE_FAIL : "Flash write failed",
|
||||
Device.STATUS_FLASH_READ_FAIL : "Flash read failed"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user