Reliable Flipper Zero filesystem operations via serial CLI
FlipperFS is a Python library that provides direct, reliable access to the Flipper Zero filesystem through serial communication at 230400 baud. Unlike other libraries with buggy storage operations, FlipperFS uses the proven serial CLI interface for rock-solid file operations.
- 🔌 Direct Serial Communication - No middleware, talks directly to Flipper CLI at 230400 baud
- 📁 Complete Filesystem Operations - read, write, list, stat, mkdir, remove, copy, rename
- 🔢 Binary File Support - Handle both text and binary files with chunk operations
- 📡 Sub-GHz Utilities - Optional specialized operations for Sub-GHz signal files
- 🛡️ Robust Error Handling - Custom exceptions with meaningful error messages
- 🐍 Pythonic API - Context managers, type hints, clean interfaces
- ✅ Production Ready - Comprehensive test coverage and proven in production
pip install flipper-fsImport as:
import flipperfs# Clone the repository
git clone https://github.com/AndreMiras/flipper-fs.py
cd flipper-fs.py
# Install in development mode with dev dependencies
pip install -e ".[dev]"For reproducible CI/CD builds:
pip install -r requirements-dev.txtfrom flipperfs import FlipperStorage
# Connect to Flipper Zero
with FlipperStorage(port='/dev/ttyACM0') as storage:
# List files
files = storage.list('/any/subghz')
print(f"Found {len(files)} items")
# Read a file
content = storage.read('/any/subghz/signal.sub')
print(content)
# Write a file
storage.write('/any/test.txt', 'Hello from FlipperFS!')
# Check if path exists
if storage.exists('/any/subghz'):
print("Sub-GHz directory exists")
# Get file info
info = storage.stat('/any/test.txt')
print(f"File size: {info['size']} bytes")
# Clean up
storage.remove('/any/test.txt')from flipperfs.extras import SubGhzStorage
with SubGhzStorage(port='/dev/ttyACM0') as subghz:
# List all signal files
signals = subghz.list_signals()
print(f"Found {len(signals)} signal files")
# Read and parse signal file
signal_data = subghz.read_signal('my_signal.sub')
print(f"Frequency: {signal_data['Frequency']}")
print(f"Protocol: {signal_data['Protocol']}")
# Create a new signal file
subghz.write_signal(
signal_name='test_signal',
hex_key='00000012F6CC053C',
frequency=433920000,
protocol='Dooya',
bit_length=40
)
# Create temporary signal for transmission
temp_path = subghz.create_temp_signal(
hex_key='00000012F6CC053C',
protocol='Dooya'
)
print(f"Temp signal created at: {temp_path}")
# Clean up
subghz.remove(temp_path)flipper-fs supports connecting to Flipper Zero over network via socat or ser2net, enabling usage in containerized environments (Docker/Podman) and remote access scenarios.
On the host machine with Flipper Zero connected:
# Bridge /dev/ttyACM0 to TCP port 3333
socat TCP-LISTEN:3333,reuseaddr,fork FILE:/dev/ttyACM0,b230400,raw,echo=0In your application (can be in Docker container):
from flipperfs import FlipperStorage
# Connect via network (tcp:// and socket:// are equivalent)
storage = FlipperStorage(port='tcp://172.17.0.1:3333')
# or
storage = FlipperStorage(port='socket://172.17.0.1:3333')
# Works the same as serial connection
files = storage.list('/ext/subghz')tcp://host:port- Raw TCP socket (alias for socket://)socket://host:port- Raw TCP socketrfc2217://host:port- RFC2217 (Telnet-based)/dev/ttyACM0- Direct serial port (traditional)
See pyserial URL handlers for more options.
Main class for filesystem operations.
storage = FlipperStorage(port='/dev/ttyACM0', baud_rate=230400)Parameters:
port(str): Serial port path (default:/dev/ttyACM0)baud_rate(int): Serial baud rate (default:230400)
info(path='/any') -> Dict[str, str]
Get filesystem information (size, free space, etc.)
list(path='/any') -> List[Dict[str, Union[str, int]]]
List files and directories. Returns list of dicts with keys: type, name, size, path
read(file_path) -> str
Read text file content
write(file_path, content) -> bool
Write text content to file
read_binary(file_path, chunk_size=1024) -> bytes
Read binary file using chunk operations
write_binary(file_path, data, chunk_size=1024) -> bool
Write binary data to file using chunk operations
stat(path) -> Optional[Dict[str, Union[str, int]]]
Get file or directory statistics
exists(path) -> bool
Check if file or directory exists
remove(path) -> bool
Delete file or directory
mkdir(path) -> bool
Create directory
copy(source, destination) -> bool
Copy file to new location
rename(old_path, new_path) -> bool
Rename or move file
md5(file_path) -> str
Calculate MD5 hash of file
tree(path='/any') -> str
Get recursive directory listing as formatted string
close()
Close serial connection
Extended storage operations for Sub-GHz signal files (inherits from FlipperStorage).
list_signals(directory='/any/subghz') -> List[str]
List all .sub signal files in directory
read_signal(signal_name) -> Dict[str, str]
Read and parse .sub file content into dictionary
write_signal(signal_name, hex_key, frequency=433920000, protocol='Dooya', preset='FuriHalSubGhzPresetOok650Async', bit_length=40) -> bool
Create a new .sub signal file with specified parameters
create_temp_signal(hex_key, **kwargs) -> str
Create temporary signal file and return path
All exceptions inherit from FlipperFilesystemError:
ConnectionError- Failed to connect or communicate with FlipperFileNotFoundError- File or directory not found on FlipperWriteError- Failed to write file to FlipperReadError- Failed to read file from Flipper
You can set default values via environment variables:
export FLIPPER_PORT=/dev/ttyACM1
export FLIPPER_BAUD=230400Then in your code:
import os
from flipperfs import FlipperStorage
port = os.getenv('FLIPPER_PORT', '/dev/ttyACM0')
storage = FlipperStorage(port=port)On Linux, Flipper Zero typically appears as /dev/ttyACM0 or /dev/ttyACM1.
To find your Flipper's port:
# List serial ports
ls /dev/tty* | grep ACM
# Or use dmesg
dmesg | grep ttyOn macOS:
ls /dev/cu.usbmodem*On Windows:
# Check Device Manager for COM ports
# Install dev dependencies
pip install -e ".[dev]"
# Run all tests (integration tests skip without hardware)
pytest
# Run with coverage
pytest --cov=flipperfs --cov-report=html
# Run only unit tests (exclude integration tests)
pytest tests/flipperfs/All unit tests use mocked serial connections and run without hardware.
Integration tests verify real communication with Flipper Zero devices. They run automatically if hardware is available, or skip gracefully if not.
Requirements:
- Flipper Zero device connected via USB serial or network
- Serial port permissions (Linux: user in
dialoutgroup) - Network bridge configured (optional, for Docker/remote scenarios)
Running with USB Serial:
# Find your Flipper's serial port
ls /dev/ttyACM* # Linux
ls /dev/cu.usbmodem* # macOS
# Enable integration tests
export FLIPPER_PORT=/dev/ttyACM0
# Run all tests (includes integration tests)
pytest
# Run only integration tests
pytest tests/test_integration.py -vRunning with Network Connection:
For Docker/containerized environments, use socat to bridge serial to network:
# On host with Flipper connected
socat TCP-LISTEN:3333,reuseaddr,fork FILE:/dev/ttyACM0,b230400,raw,echo=0
# In container or another machine
export FLIPPER_PORT=tcp://172.17.0.1:3333
pytest tests/test_integration.py -vWithout Hardware:
Integration tests skip automatically when FLIPPER_PORT is not set:
# Integration tests will show as "SKIPPED"
pytest tests/test_integration.py -vWhat Integration Tests Verify:
- Real serial communication at 230400 baud
- File write/read operations with actual device I/O
- Filesystem metadata retrieval (stat, exists)
- File cleanup and error handling
- Network connection compatibility (tcp:// URLs)
# Format code
ruff format flipperfs tests examples
# Check formatting
ruff format --check flipperfs tests examples
# Lint code
ruff check flipperfs tests examples
# Auto-fix linting issues
ruff check --fix flipperfs tests examples
# Type checking
mypy flipperfsSee the examples/ directory for complete examples:
basic_operations.py- Basic filesystem operationsbinary_files.py- Binary file handlingsubghz_signals.py- Sub-GHz signal management
Existing libraries like PyFlipper have known bugs in their storage operations, particularly with file writes. FlipperFS was built from the ground up using direct serial CLI communication, which has proven reliable in production.
- ✅ Proven reliability - Battle-tested in production environments
- ✅ Direct serial - No wrapper layers to cause issues
- ✅ Complete API - All filesystem operations supported
- ✅ Well tested - Comprehensive test suite
- ✅ Clean code - Well-structured, documented, maintainable
Add your user to the dialout group:
sudo usermod -a -G dialout $USER
# Log out and back in for changes to take effectOr run with sudo (not recommended):
sudo python your_script.py- Ensure Flipper is powered on
- Check the serial port is correct (
ls /dev/tty*) - Verify no other programs are using the serial port
- Try unplugging and replugging the USB cable
Make sure pyserial is installed:
pip install pyserialContributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Run the test suite
- Submit a pull request
MIT License - see LICENSE file for details
Developed to provide reliable filesystem operations for Flipper Zero devices.
- Repository: https://github.com/AndreMiras/flipper-fs.py
- Issues: https://github.com/AndreMiras/flipper-fs.py/issues
- PyPI: https://pypi.org/project/flipper-fs/
- Flipper Zero: https://flipperzero.one/