
Python CLI-обёртка для Flipper Zero
Flipper Zero — Python CLI-обёртка

Этот релиз приводит обёртку в соответствие с текущим CLI Flipper Zero
(проверено на реальном оборудовании, прошивка unlshd-089e):
info: flipper.info.device_info() / power_info() /
power_debug_info() (современные команды info device / info power).power info → info power: flipper.power.info() продолжает работать и теперь
под капотом читает info power. Добавлены flipper.power.otg_on()/otg_off().date: используйте flipper.date.datetime(); разбор был исправлен для текущего
формата вывода.KB, так и в KiB, read() теперь возвращает
полное многострочное содержимое файла, а карты exFAT / с пустой меткой разбираются
корректно (исправляет #21).NECext, Kaseikyo, RCA, Pioneer, … поддерживаются автоматически (исправляет #15).
Новый flipper.ir.protocols().debug → sysctl debug, subghz tx/rx получили обязательный
аргумент device, bt hci_info без изменений.scan, dump,
emulate, field, apdu, raw, mfu) с утилитами кэша ключей Mifare Classic
для каждого UID (import_nfc_keys, write_key_cache).ps (→ top), music_player, nfc detect.$ pip install pyflipper
unlshd-089e)from pyflipper import PyFlipper
# Локальный последовательный порт
flipper = PyFlipper(com="/dev/ttyACM0")
# ИЛИ
# Удалённый сервер serial2websocket
flipper = PyFlipper(ws="ws://192.168.1.5:1337")
# TCP
flipper = PyFlipper(tcp='192.168.89.222:22170')
# Информация (на текущей прошивке под капотом читает `info power`)
info = flipper.power.info()
# Выключение
flipper.power.off()
# Перезагрузка
flipper.power.reboot()
# Перезагрузка в режиме DFU
flipper.power.reboot2dfu()
# Включение/выключение вывода 5V OTG на пине GPIO
flipper.power.otg_on()
flipper.power.otg_off()
# Установка обновления из файла .fuf
flipper.update.install(fuf_file="/ext/update.fuf")
# Резервное копирование Flipper в файл .tar
flipper.update.backup(dest_tar_file="/ext/backup.tar")
# Восстановление Flipper из резервного .tar файла
flipper.update.restore(bak_tar_file="/ext/backup.tar")
# Список установленных приложений
apps = flipper.loader.list()
# Открыть приложение
flipper.loader.open(app_name="Clock")
# Получить дату Flipper (объект datetime)
date = flipper.date.datetime()
# Получить метку времени Flipper
timestamp = flipper.date.timestamp()
# Получить словарь информации об устройстве (текущая прошивка `info device`, ключи через точку)
device_info = flipper.info.device_info()
# Получить словарь информации о питании (текущая прошивка `info power`)
power_info = flipper.info.power_info()
# Расширенная отладочная информация о питании/датчике
power_debug = flipper.info.power_debug_info()
# Устаревшая команда `device_info` (ключи с подчёркиваниями, всё ещё поддерживается)
legacy_info = flipper.device_info.info()
# Получить словарь информации о куче
heap = flipper.free.info()
# Получить строку free_blocks
free_blocks = flipper.free.blocks()
# Получить информацию Bluetooth
bt_info = flipper.bt.info()
Совместимость прошивок. Эта библиотека теперь нацелена на текущий CLI Flipper Zero (прошивка 1.x, проверено на Unleashed
unlshd-089e). Некоторые устаревшие команды были удалены из официальной прошивки и поэтому больше не доступны:ps(заменён интерактивнымtop),music_playerиnfc detect(NFC теперь интерактивный подшелл).power infoперемещён вinfo power,debugперемещён вsysctl debug, аstorageтеперь сообщает размеры вKiB— всё обрабатывается прозрачно обёртками выше.
# Получить информацию о файловой системе хранилища
ext_info = flipper.storage.info(fs="/ext")
# Получить словарь содержимого /ext
ext_list = flipper.storage.list(path="/ext")
# Получить дерево каталогов /ext
ext_tree = flipper.storage.tree(path="/ext")
# Получить информацию о файле
file_info = flipper.storage.stat(file="/ext/foo/bar.txt")
# Создать каталог
flipper.storage.mkdir(new_dir="/ext/foo")
# Прочитать файл
plain_text = flipper.storage.read(file="/ext/foo/bar.txt")
# Удалить файл
flipper.storage.remove(file="/ext/foo/bar.txt")
# Скопировать файл
flipper.storage.copy(src="/ext/foo/source.txt", dest="/ext/bar/destination.txt")
# Переименовать файл
flipper.storage.rename(file="/ext/foo/bar.txt", new_file="/ext/foo/rab.txt")
# MD5-хеш файла
md5_hash = flipper.storage.md5(file="/ext/foo/bar.txt")
# Записать файл одним блоком
file = "/ext/bar.txt"
text = """There are many variations of passages of Lorem Ipsum available,
but the majority have suffered alteration in some form, by injected humour,
or randomised words which don't look even slightly believable.
If you are going to use a passage of Lorem Ipsum,
you need to be sure there isn't anything embarrassing hidden in the middle of text.
"""
flipper.storage.write.file(path=file, text=text)
# Записать файл с помощью слушателя
file = "/ext/foo.txt"
text_one = """There are many variations of passages of Lorem Ipsum available,
but the majority have suffered alteration in some form, by injected humour,
or randomised words which don't look even slightly believable.
If you are going to use a passage of Lorem Ipsum,
you need to be sure there isn't anything embarrassing hidden in the middle of text.
"""
flipper.storage.write.start(file)
time.sleep(2)
flipper.storage.write.send(text_one)
text_two = """All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as
necessary, making this the first true generator on the Internet.
It uses a dictionary of over 200 Latin words, combined with a handful of
model sentence structures, to generate Lorem Ipsum which looks reasonable.
The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
"""
flipper.storage.write.send(text_two)
time.sleep(3)
# Не забудьте остановить
flipper.storage.write.stop()
# Установить общий светодиод (r,b,g,bl)
flipper.led.set(led='r', value=255)
# Выключить синий светодиод
flipper.led.blue(value=0)
# Установить значение зелёного светодиода
flipper.led.green(value=175)
# Включить подсветку
flipper.led.backlight_on()
# Выключить подсветку
flipper.led.backlight_off()
# Выключить все светодиоды
flipper.led.off()
# Включить/выключить вибро (True или False)
flipper.vibro.set(True)
# Включить вибро
flipper.vibro.on()
# Выключить вибро
flipper.vibro.off()
# Установить режим GPIO: 0 - вход, 1 - выход
flipper.gpio.mode(pin_name=PIN_NAME, value=1)
# Установить значение пина GPIO: 0 - выкл, 1 - вкл
flipper.gpio.set(pin_name=PIN_NAME, value=1)
# Прочитать значение пина GPIO
flipper.gpio.read(pin_name=PIN_NAME)
⚠️ Удалено из официальной прошивки. Команда CLI
music_playerбыла удалена из стандартной прошивки (приложение теперь отдельный.fap). Эти вызовы всё ещё работают на пользовательских прошивках, которые сохраняют команду (например, сборки Unleashed / RogueMaster, включающие её); на стандартной прошивке они возвращают ответ "command not found".
# Воспроизвести песню в формате RTTTL
rttl_song = "Littleroot Town - Pokemon:d=4,o=5,b=100:8c5,8f5,8g5,4a5,8p,8g5,8a5,8g5,8a5,8a#5,8p,4c6,8d6,8a5,8g5,8a5,8c#6,4d6,4e6,4d6,8a5,8g5,8f5,8e5,8f5,8a5,4d6,8d5,8e5,2f5,8c6,8a#5,8a#5,8a5,2f5,8d6,8a5,8a5,8g5,2f5,8p,8f5,8d5,8f5,8e5,4e5,8f5,8g5"
# Воспроизвести в цикле
flipper.music_player.play(rtttl_code=rttl_song)
# Остановить цикл
flipper.music_player.stop()
# Воспроизвести 20 секунд
flipper.music_player.play(rtttl_code=rttl_song, duration=20)
# Пикнуть
flipper.music_player.beep()
# Пикнуть 5 секунд
flipper.music_player.beep(duration=5)
На текущей прошивке NFC — это интерактивный подшелл (scanner,
dump, emulate, field, apdu, raw, mfu). Эта обёртка входит в
подшелл, выполняет одну команду и выходит, поэтому каждый метод является
обычным блокирующим вызовом. detect() сохранён как псевдоним scan().
# Определить тип метки -> {'protocols': ['Mifare Classic'], 'raw': '...'} или None
tag = flipper.nfc.scan(timeout=5)
# Включить поле NFC на несколько секунд
flipper.nfc.field(timeout=3)
# Отправить APDU (ISO14443-4 / ISO15693-3); протокол определяется автоматически, если опущен
resp = flipper.nfc.apdu(["00A4 04 00", "A0 00"], protocol="14_4a")
# Отправить сырые байты с добавлением CRC
flipper.nfc.raw("93 20", protocol="14a", crc=True)
# Утилиты для Mifare Ultralight
info = flipper.nfc.mfu_info()
block4 = flipper.nfc.mfu_read_block(4)
flipper.nfc.mfu_write_block(4, "00 01 02 03")
# Эмулировать сохранённый .nfc файл
flipper.nfc.emulate(file="/ext/nfc/mycard.nfc", timeout=10)
Команда CLI dump не выполняет атаку по словарю: она читает ключи из
кэша для каждого UID по пути /ext/nfc/.cache/<UID>.keys (файл, который
GUI записывает после успешного чтения). Если кэш отсутствует, dump
возвращает "failed to read". Вы можете (пере)создать его из существующего
дампа Flipper .nfc — ключи берутся из каждого секторного трейлера:
# Загрузить ключи из родного .nfc дампа в кэш ключей для данного UID
uid, cache_path = flipper.nfc.import_nfc_keys("/path/to/card.nfc")
# Теперь CLI dump может прочитать карту (токены протоколов: mfc, mfu, 14_4a, ...)
flipper.nfc.dump(protocol="mfc", file="/ext/nfc/card_dump.nfc")
# Синхронно, таймаут по умолчанию 5 секунд
# Чтение RFID
rfid = flipper.rfid.read()
# Эмуляция RFID
emulated = flipper.rfid.emulate(key_type="EM4100", key_data="5500824806")
# Запись RFID
written = flipper.rfid.write(key_type="EM4100", key_data="5500824806")
# Передать hex_key N раз (по умолчанию count=10, device=0 -> CC1101_INT, 1 -> CC1101_EXT)
flipper.subghz.tx(hex_key="DEADBEEF", frequency=433920000, count=5, device=0)
# Приём (по умолчанию frequency=433920000 device=0 raw=False timeout=5 секунд)
received = flipper.subghz.rx(frequency=433920000, device=0, raw=True, timeout=10)
# Повтор записанной передачи
flipper.subghz.tx_from_file("/ext/subghz/foo.sub")
# Декодировать сырой .sub файл
decoded = flipper.subghz.decode_raw(sub_file="/ext/subghz/foo.sub")
# Список протоколов, поддерживаемых подключённой прошивкой (читается в реальном времени из `ir help`)
protocols = flipper.ir.protocols()
# Передать hex_address и hex_command с выбором протокола
flipper.ir.tx(protocol="Samsung32", hex_address="C000FFEE", hex_command="DEADBEEF")
# NECext теперь поддерживается (список протоколов читается из прошивки, не захардкожен)
flipper.ir.tx(protocol="NECext", hex_address="EF00", hex_command="FD02")
# Примеры сырой передачи
flipper.ir.tx_raw(frequency=38000, duty_cycle=0.33, samples=[1337, 8888, 3000, 5555])
# Синхронно, таймаут по умолчанию 5 секунд
# Приём сигнала
r = flipper.ir.rx(timeout=10)
# Чтение (таймаут по умолчанию 5 секунд)
ikey = flipper.ikey.read()
# Запись (таймаут по умолчанию 5 секунд)
flipper.ikey.write(key_type="Dallas", key_data="DEADBEEFCOOOFFEE")
# Эмуляция (таймаут по умолчанию 5 секунд)
flipper.ikey.emulate(key_type="Dallas", key_data="DEADBEEFCOOOFFEE")
# Подключить регистратор событий (таймаут по умолчанию 10 секунд)
logs = flipper.log.attach()
# Включить режим отладки (выполняет `sysctl debug 1` на текущей прошивке)
flipper.debug.on()
# Выключить режим отладки
flipper.debug.off()
# Поиск
response = flipper.onewire.search()
# Получить
response = flipper.i2c.get()
# Дампинг ввода
dump = flipper.input.dump()
# Отправить ввод
flipper.input.send("up", "press")
Набор разделён на две части:
test_serial_wrapper.py, test_commands.py)
работают везде — они тестируют библиотеку с помощью симулированного
последовательного порта (tests/fake_serial.py), который воспроизводит
фрейминг CLI Flipper, поэтому устройство не требуется. Эти тесты
выполняются в CI.test_hardware.py) общаются с
реальным Flipper и пропускаются автоматически, когда устройство не
подключено. Установите FLIPPER_COM=/dev/ttyACM0, чтобы закрепить
конкретный порт.# Всё (тесты с оборудованием пропускаются сами, если Flipper не подключён)
python -m unittest discover -s tests -p "test_*.py" -v
# Только тесты без оборудования
python -m unittest tests.test_serial_wrapper tests.test_commands -v
Не стесняйтесь вносить вклад любым способом
ZEC: zs13zdde4mu5rj5yjm2kt6al5yxz2qjjjgxau9zaxs6np9ldxj65cepfyw55qvfp9v8cvd725f7tz7
ETH: 0xef3cF1Eb85382EdEEE10A2df2b348866a35C6A54
BTC: 15umRZXBzgUacwLVgpLPoa2gv7MyoTrKat