Skip to main content

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 via discover_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: True if feed command was acknowledged

async set_schedule(schedules: List[FeedSchedule]) -> bool

Set feed schedule.

  • schedules: List of FeedSchedule objects
  • Returns: True if command was sent successfully

async set_child_lock(locked: bool) -> bool

Set child lock state.

  • locked: True to lock, False to unlock
  • Returns: True if command was sent successfully

async set_sound(enabled: bool) -> bool

Set reminder tone/sound state.

  • enabled: True to enable sound, False to disable
  • Returns: True if 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"] or Weekday.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 week
  • Weekday.WEEKDAYS: Monday through Friday
  • Weekday.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: EA header + Command + Length + Data + CRC(00) + AE footer
  • Notifications: EB header + Command + Length + Data + CRC + AE footer

Based on reverse engineering of the official Petnetizen Android app.

Requirements

  • Python 3.12+ (aligned with current Home Assistant; see HA version support)
  • bleak library for BLE communication
  • Linux: Bluetooth permissions (user in bluetooth group)
  • 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 sudo or user in bluetooth group)

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)

  1. One-time setup

    • In pyproject.toml and CHANGELOG.md, replace your-username with 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_TOKEN with the PyPI token.
  2. Cut a release

    • Bump version in 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.

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

petnetizen_feeder-0.3.8.tar.gz (35.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

petnetizen_feeder-0.3.8-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

File details

Details for the file petnetizen_feeder-0.3.8.tar.gz.

File metadata

  • Download URL: petnetizen_feeder-0.3.8.tar.gz
  • Upload date:
  • Size: 35.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","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

Hashes for petnetizen_feeder-0.3.8.tar.gz
Algorithm Hash digest
SHA256 03b72d909bb96d349f92d2bd40c76377d6cf1ebf7594e3fe765a39f090ecf92d
MD5 cd32f805174a230589b4fdda2b4b4943
BLAKE2b-256 46880b372f86e5fe81b9656964dfb269399e16f7aeb14407914c57c2951f90df

See more details on using hashes here.

File details

Details for the file petnetizen_feeder-0.3.8-py3-none-any.whl.

File metadata

  • Download URL: petnetizen_feeder-0.3.8-py3-none-any.whl
  • Upload date:
  • Size: 16.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","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

Hashes for petnetizen_feeder-0.3.8-py3-none-any.whl
Algorithm Hash digest
SHA256 e1e57598a46e2006462aa3f287d995902e0ef27cb8375678052b89cbfe5cec86
MD5 ee50f5c6ae31f7518604a8daf20cc349
BLAKE2b-256 e3d854272c042638886750a13f929291023de649ad7b581ec73f69af887baee9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page