Skip to main content

Python library for communicating with FlowerCare (Xiaomi MiFlora) Bluetooth plant sensors

Project description

PyFlowerCare

A Python library for communicating with FlowerCare (Xiaomi MiFlora) Bluetooth plant sensors.

Features

  • Device Discovery: Scan and discover FlowerCare devices via Bluetooth Low Energy
  • Real-time Sensor Data: Read temperature, brightness, soil moisture, and conductivity
  • Historical Data: Access stored historical measurements from the device
  • Device Management: Connect, disconnect, and manage multiple devices
  • Async Support: Full asyncio support for non-blocking operations
  • Error Handling: Comprehensive exception handling with meaningful error messages

Installation

pip install pyflowercare

Requirements

  • Python 3.9+
  • Bluetooth Low Energy support
  • Linux/macOS/Windows with Bluetooth adapter

Quick Start

Basic Usage

import asyncio
from pyflowercare import FlowerCareScanner

async def main():
    scanner = FlowerCareScanner()
    
    # Scan for devices
    devices = await scanner.scan_for_devices(timeout=10.0)
    
    if devices:
        device = devices[0]
        
        # Connect and read data
        async with device:
            sensor_data = await device.read_sensor_data()
            print(f"Temperature: {sensor_data.temperature}°C")
            print(f"Moisture: {sensor_data.moisture}%")
            # brightness/conductivity may be None ("not measured")
            if sensor_data.brightness is not None:
                print(f"Brightness: {sensor_data.brightness} lux")
            if sensor_data.conductivity is not None:
                print(f"Conductivity: {sensor_data.conductivity} µS/cm")

asyncio.run(main())

Device Information

async with device:
    info = await device.get_device_info()
    print(f"Device: {info.name}")
    print(f"MAC: {info.mac_address}")
    print(f"Battery: {info.battery_level}%")
    print(f"Firmware: {info.firmware_version}")

Historical Data

async with device:
    history = await device.get_historical_data()
    
    for entry in history[-5:]:  # Last 5 entries
        print(f"{entry.timestamp}: {entry.sensor_data}")

Continuous Monitoring

import asyncio
from datetime import datetime
from pyflowercare import FlowerCareScanner

async def monitor_device(device):
    while True:
        try:
            async with device:
                while True:
                    data = await device.read_sensor_data()
                    timestamp = datetime.now().strftime("%H:%M:%S")
                    print(f"[{timestamp}] {data}")
                    await asyncio.sleep(60)  # Read every minute
        except Exception as e:
            print(f"Error: {e}")
            await asyncio.sleep(5)  # Retry after 5 seconds

async def main():
    scanner = FlowerCareScanner()
    devices = await scanner.scan_for_devices()
    
    # Monitor all found devices
    tasks = [monitor_device(device) for device in devices]
    await asyncio.gather(*tasks)

asyncio.run(main())

API Reference

FlowerCareScanner

  • scan_for_devices(timeout=10.0): Scan for FlowerCare devices
  • find_device_by_address(device_address, timeout=10.0): Build a device directly from an address — MAC (C4:7C:8D:...) or UUID-style identifier (macOS) — without scanning. Returns None if the address is malformed.
  • scan_continuously(callback, timeout=None): Continuous scanning with callback
  • scan_stream(timeout=None): Async generator for device discovery

FlowerCareDevice

  • connect(timeout=10.0): Connect to device
  • disconnect(): Disconnect from device
  • read_sensor_data(): Read current sensor measurements
  • get_device_info(): Get device information (name, MAC, battery, firmware)
  • get_historical_data(): Get stored historical measurements. On a mid-read BLE drop it reconnects and resumes; if it still can't finish it returns the entries collected so far. Check last_history_complete to tell a full read from a partial one.
  • last_history_complete (property): True if the last get_historical_data() ran to completion, False if it returned partial data after an interrupted read.
  • blink_led(): Make device LED blink

Data Models

SensorData

  • temperature: Temperature in Celsius (signed; negative readings are valid)
  • brightness: Light intensity in lux, or None if the sensor did not measure it
  • moisture: Soil moisture percentage
  • conductivity: Soil conductivity in µS/cm, or None if the sensor did not measure it
  • timestamp: Measurement timestamp

In historical data, slots the sensor never fully wrote are skipped, and unmeasured brightness/conductivity fields come back as None rather than as sentinel values. Guard for None before using them.

DeviceInfo

  • name: Device name
  • mac_address: MAC address
  • firmware_version: Firmware version
  • battery_level: Battery level percentage

Error Handling

The library provides specific exception types:

from pyflowercare.exceptions import (
    FlowerCareError,      # Base exception
    ConnectionError,      # Connection failures
    DeviceError,         # Device operation errors
    DataParsingError,    # Data parsing errors
    TimeoutError         # Operation timeouts
)

try:
    async with device:
        data = await device.read_sensor_data()
except ConnectionError as e:
    print(f"Failed to connect: {e}")
except DeviceError as e:
    print(f"Device error: {e}")

Logging

Enable logging to see detailed operation information:

from pyflowercare import setup_logging

setup_logging("DEBUG")  # Enable debug logging

Examples

See the examples/ directory for more comprehensive examples:

  • basic_usage.py: Simple device connection and data reading
  • continuous_monitoring.py: Continuous monitoring of multiple devices
  • historical_data.py: Historical data retrieval and CSV export

Troubleshooting

Permission Issues (Linux)

sudo setcap cap_net_raw+eip $(eval readlink -f `which python`)

Bluetooth Not Available

Ensure your system has Bluetooth Low Energy support and the adapter is enabled.

Device Not Found

  • Ensure the FlowerCare device is nearby and not connected to another app
  • Try increasing the scan timeout
  • Check that the device battery is not depleted

License

This project is licensed under the MIT License.

Contributing

Contributions are welcome! Please feel free to submit issues and enhancement requests.

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

pyflowercare-0.4.3.tar.gz (14.9 kB view details)

Uploaded Source

Built Distribution

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

pyflowercare-0.4.3-py3-none-any.whl (15.1 kB view details)

Uploaded Python 3

File details

Details for the file pyflowercare-0.4.3.tar.gz.

File metadata

  • Download URL: pyflowercare-0.4.3.tar.gz
  • Upload date:
  • Size: 14.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pyflowercare-0.4.3.tar.gz
Algorithm Hash digest
SHA256 1f5e7deb8cf2187f6fe0b17b25ba7c8443569aa546e8d8b382f79d0662efd116
MD5 d3bfdd8fb94f285952fe3cea90cd1425
BLAKE2b-256 ace83e38850c7a281ff1f22047f2346b22fe8d7e53dff864cd64a66f1cefd96f

See more details on using hashes here.

File details

Details for the file pyflowercare-0.4.3-py3-none-any.whl.

File metadata

  • Download URL: pyflowercare-0.4.3-py3-none-any.whl
  • Upload date:
  • Size: 15.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pyflowercare-0.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 48419b161cbee5ea3dce1d087fe0608d0360c0058ec5182bb513e61481f30182
MD5 1827d2aa720ba252e566f27ba7cde841
BLAKE2b-256 ea60aa9dd7e26239ab3eccc29285480dba17e331c9d68f7e350d0913eef4385c

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