Python BLE library for Petnetizen automatic pet feeders (Home Assistant–friendly)
Project description
Petnetizen Feeder BLE Library
A Python BLE library for controlling Petnetizen automatic pet feeders. Suitable for use with uv and as a dependency for Home Assistant custom integrations.
Features
- ✅ Manual Feed: Trigger feeding with configurable portion count (1-15 portions)
- ✅ Feed Schedule: Set automated feeding schedules with time, weekdays, and portion count
- ✅ Child Lock: Enable/disable child lock to prevent accidental feeding
- ✅ Sound Control: Enable/disable reminder tone/sound notifications
- ✅ Device Status: Query device information, feeding status, and fault codes
- ✅ Autodiscovery: Scan for feeders via BLE (
discover_feeders()) - ✅ Time sync: Sync device clock to host time (
sync_time())
Installation
From PyPI (after publishing):
pip install petnetizen-feeder
# or
uv add petnetizen-feeder
From source (project root):
# Using uv (recommended)
uv sync
This creates a virtual environment (.venv), installs the package in editable mode, and pins dependencies (e.g. bleak). Then run scripts with:
uv run python your_script.py
To install elsewhere (e.g. for a Home Assistant integration), use pip install -e . in the project root or publish to PyPI and add petnetizen-feeder to your integration’s dependencies.
Quick Start
import asyncio
from petnetizen_feeder import FeederDevice, FeedSchedule, Weekday
async def main():
# Connect to device
feeder = FeederDevice("E6:C0:07:09:A3:D3")
await feeder.connect()
# Manual feed with 2 portions
await feeder.feed(portions=2)
# Set schedule: 8:00 AM every day, 1 portion
schedules = [
FeedSchedule(
weekdays=Weekday.ALL_DAYS,
time="08:00",
portions=1,
enabled=True
)
]
await feeder.set_schedule(schedules)
# Toggle child lock
await feeder.set_child_lock(False) # Unlock
# Toggle sound
await feeder.set_sound(True) # Enable
await feeder.disconnect()
asyncio.run(main())
Autodiscovery and reading settings
Discover feeders on BLE, connect to the first one, read device info and schedule, then sync time:
uv run python examples/read_settings_and_sync_time.py
from petnetizen_feeder import discover_feeders, FeederDevice
async def main():
feeders = await discover_feeders(timeout=10.0) # [(address, name, device_type), ...]
if not feeders:
return
address, name, device_type = feeders[0]
feeder = FeederDevice(address, device_type=device_type)
await feeder.connect()
info = await feeder.get_device_info() # {"device_name": "...", "device_version": "..."}
schedules = await feeder.query_schedule()
await feeder.sync_time() # sync device clock to now
await feeder.disconnect()
API Reference
discover_feeders(timeout: float = 10.0) -> List[Tuple[str, str, str]]
Scan for Petnetizen feeders via BLE. Uses an unfiltered BLE scan (no service-UUID filter), then recognizes feeders by advertised name prefix (like the Android app: bleNames / getDeviceTypeByName). Returns a list of (address, name, device_type) for each feeder found. device_type is "standard", "jk", or "ali". Use device_type when constructing FeederDevice for correct service UUIDs. Name prefixes: Du, JK, ALI, PET, FEED (see FEEDER_NAME_PREFIXES in protocol.py to extend).
FeederDevice
Main class for controlling feeder devices.
__init__(address: str, verification_code: str = "00000000", device_type: Optional[str] = None)
Initialize feeder device controller.
address: BLE device MAC address (e.g., "E6:C0:07:09:A3:D3")verification_code: Verification code (default: "00000000")device_type: Optional"standard","jk", or"ali"(auto-detected from name if not set; use when discovered viadiscover_feeders())
async connect() -> bool
Connect to the feeder device. Returns True if successful.
async disconnect()
Disconnect from the device.
async feed(portions: int = 1) -> bool
Trigger manual feed with specified number of portions.
portions: Number of portions to feed (1-15, typically 1-3)- Returns:
Trueif feed command was acknowledged
async set_schedule(schedules: List[FeedSchedule]) -> bool
Set feed schedule.
schedules: List ofFeedScheduleobjects- Returns:
Trueif command was sent successfully
async set_child_lock(locked: bool) -> bool
Set child lock state.
locked:Trueto lock,Falseto unlock- Returns:
Trueif command was sent successfully
async set_sound(enabled: bool) -> bool
Set reminder tone/sound state.
enabled:Trueto enable sound,Falseto disable- Returns:
Trueif command was sent successfully
async query_schedule() -> List[Dict]
Query current feed schedule. Returns list of schedule dictionaries.
async get_device_info() -> Dict
Query device name and firmware version. Returns {"device_name": "...", "device_version": "..."}.
async sync_time(dt: Optional[datetime] = None) -> None
Sync device clock to the given time (default: now).
is_connected: bool
Property to check if device is connected.
FeedSchedule
Represents a single feed schedule entry.
__init__(weekdays: List[str], time: str, portions: int, enabled: bool = True)
weekdays: List of weekday names (e.g.,["mon", "wed", "fri"]orWeekday.ALL_DAYS)time: Time in HH:MM format (e.g., "08:00")portions: Number of portions to feed (1-15)enabled: Whether this schedule is enabled
Weekday
Weekday constants for schedules.
Weekday.SUNDAY,Weekday.MONDAY, etc.Weekday.ALL_DAYS: All days of the weekWeekday.WEEKDAYS: Monday through FridayWeekday.WEEKEND: Saturday and Sunday
Examples
Basic Manual Feed
from petnetizen_feeder import FeederDevice
async def feed_pet():
feeder = FeederDevice("E6:C0:07:09:A3:D3")
await feeder.connect()
await feeder.feed(portions=1)
await feeder.disconnect()
Set Multiple Schedules
from petnetizen_feeder import FeederDevice, FeedSchedule, Weekday
async def setup_schedule():
feeder = FeederDevice("E6:C0:07:09:A3:D3")
await feeder.connect()
schedules = [
# Morning: 8:00 AM every day, 1 portion
FeedSchedule(Weekday.ALL_DAYS, "08:00", 1, True),
# Evening: 6:00 PM weekdays only, 2 portions
FeedSchedule(Weekday.WEEKDAYS, "18:00", 2, True),
]
await feeder.set_schedule(schedules)
await feeder.disconnect()
Control Child Lock and Sound
async def configure_device():
feeder = FeederDevice("E6:C0:07:09:A3:D3")
await feeder.connect()
# Unlock device (allow manual feeding)
await feeder.set_child_lock(False)
# Enable sound notifications
await feeder.set_sound(True)
await feeder.disconnect()
Protocol Details
The library uses the Tuya BLE protocol format:
- Commands:
EAheader + Command + Length + Data + CRC(00) +AEfooter - Notifications:
EBheader + Command + Length + Data + CRC +AEfooter
Based on reverse engineering of the official Petnetizen Android app.
Requirements
- Python 3.12+ (aligned with current Home Assistant; see HA version support)
bleaklibrary for BLE communication- Linux: Bluetooth permissions (user in
bluetoothgroup) - macOS: Bluetooth access permissions
- Windows: Bluetooth adapter
Troubleshooting
Connection fails
- Ensure Bluetooth is enabled
- Make sure device is powered on
- Check device address is correct
- Try running with appropriate permissions (Linux may need
sudoor user inbluetoothgroup)
Feed doesn't occur
- Check child lock status (must be unlocked)
- Verify device is not in fault state
- Ensure device has food loaded
- Check feeding status before feeding
Permission errors (Linux)
sudo usermod -aG bluetooth $USER
# Then log out and back in
Development
From the project root:
uv sync --all-extras # install with dev dependencies
uv run pytest tests/ -v
uv build # build sdist + wheel in dist/
Releasing (PyPI + GitHub)
-
One-time setup
- In pyproject.toml and CHANGELOG.md, replace
your-usernamewith your GitHub username (or org) so URLs point to your repo. - On PyPI, create an API token (Account → API tokens).
- In your GitHub repo: Settings → Secrets and variables → Actions → add secret
PYPI_API_TOKENwith the PyPI token.
- In pyproject.toml and CHANGELOG.md, replace
-
Cut a release
- Bump
versionin pyproject.toml and add an entry in CHANGELOG.md under[Unreleased]/ new version. - Commit, push, then create and push a tag:
git tag v0.1.0 git push origin v0.1.0
- The Release workflow runs: builds the package, publishes to PyPI, and creates a GitHub Release with generated notes and
dist/artifacts.
- Bump
CI runs on every push/PR to main (or master) and tests Python 3.12–3.14 (aligned with current Home Assistant).
License
This library is based on reverse engineering of the Petnetizen Android app for educational and personal use. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file petnetizen_feeder-0.4.0.tar.gz.
File metadata
- Download URL: petnetizen_feeder-0.4.0.tar.gz
- Upload date:
- Size: 36.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a124f9d46e192bf9dc6a62b63b698e9c5b644ccd9746d8df1b67a2273b37688b
|
|
| MD5 |
c210eed86f67b1847dd33b933d8091b9
|
|
| BLAKE2b-256 |
6100cf19e1059944082e4a5d3d54fedf590885ea3bca2f46b18f938c159539f4
|
File details
Details for the file petnetizen_feeder-0.4.0-py3-none-any.whl.
File metadata
- Download URL: petnetizen_feeder-0.4.0-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7817cdb05f9e4035551328073780720fcb9f83366baaf4cb300f8d24ec02c265
|
|
| MD5 |
d03f3c328ec677b88d44a6a237c2d206
|
|
| BLAKE2b-256 |
2581d7e4faf420e611f31ae182bd0206d1124759db26253edbca189b1814447f
|