Updated: 2017-06-05
Raspbian OS: 2017-04-10-raspbian-jessie-lite
MiFlora sensor firmware: 2.9.2
miflora library: miflora-0.1.16

About the Xiaomi MiFlora sensor

Xiaomi MiFlora is a cost-effective plant sensor, which uses Bluetooth Low Energy (BLE) to communicate wirelessly, and is capable of sensing temperature, soil humidity, light, soil fertility and it’s own battery level. You can find more details about this smart sensor on the official Xiaomi site here.

Daniel Matuschek (openha) has written a library for the device, and his python code is capable of reading these data on computer running Linux. I have been running it Raspbian Lite operating system, on my Raspberry Pi 3 and also have tested it on my Raspberry Pi Zero W (both devices have onboard Wifi and Bluetooth, no dongles needed).

As I’m using pimatic for my other home automation projects, I wanted to come up with a way to display the MiFlora sensor’s data on my pimatic web interface dashboard. To date, there is no native plugin for the Miflora sensor, but with a simple workaround of using 3 already existing pimatic plugins (pimatic-cron, pimatic-shell-execute, pimatic-log-reader) the data can be displayed in pimatic.

In the tutorial I will assume that you have pimatic up and running on your Pi3, there’s an excellent guide to get you started.

What do we need

So let’s go though every step you need to take to get your MiFlora send data to Pimatic.

Necessary hardware

  • Raspberry Pi 3 or Raspberry Pi Zero W
  • Xiaomi MiFlora plant sensor

Other Raspberry Pi types like the Pi 1/Pi B+/Pi2/Pi Zero will not work out of the box, as they lack the onboard Bluetooth, but with the use of a Bluetooth dongle, you can make this work on any other type of Raspberry Pi.

Necessary Software

  • 2017-04-10-raspbian-jessie-lite
  • miflora library (by Daniel Matuschek) + dependencies
  • copy 2 files to the SD card: ssh, wpa_supplicant.conf with your WiFi settings

Checking if the sensor works, using the Android app

Open the panel of the MiFlora and pull out the plastic peg so the battery gets in contact and the sensor powers up (the LED at the top blinks a few times).

MiFlora panel (image courtesy of Xiaomi)

MiFlora is using a CR2032 battery which is located under the protective panel (image: xiaomi-mi.com)

Install Flower care app on your Android (BLE enabled) phone. You can find the app on the Play Store.

Turn on Bluetooth and Location services (GPS!) on your phone. It’s strange, but enabling Bluetooth on my phone wasn’t enough, my phone couldn’t find the MiFlora with the Flower care app, only if location access (GPS) was enabled as well.

Start the Flower care app on your phone, and add the sensor using the + button. Hold the MiFlora close to the phone while it’s scanning. As soon as if finds the MiFlora, bind it. It offers to add a plant, do so. After this the MiFlora starts sending the plant’s data to your phone. Now, we know that the MiFlora sensor works. Now exit the app and disable Bluetooth so it won’t interfere with the Raspberry Pi 3 Bluetooth.

Note: When I first started my MiFlora, it was running firmware version 2.6.2, but since then the Flower care app offered to update it to firmware 2.7, and then to firmware 2.8.6. This works with the latest MiFlora library.

As the next step, log into your Pi3 via SSH or locally.

We start with the raspi-config tool, to set the basic configuration:
pi@raspberrypi:~ $ sudo raspi-config

1 Change User Password
2 Hostname
4 Localisation Options -> I2 Change Timezone
5 Interfacing Options -> P2 SSH -> Enable
7 Advanced Options -> A1 Expand Filesystem
7 Advanced Options -> A3 Memory Split -> 16
-> Reboot

Then we run apt-get update to download the package lists from the repositories and update them, to get information on the newest versions of packages and their dependencies.
pi@pi0w-miflora:~ $ sudo apt-get update -y

Get:1 http://archive.raspberrypi.org jessie InRelease [22.9 kB] Get:2 http://mirrordirector.raspbian.org jessie InRelease [14.9 kB] Get:3 http://mirrordirector.raspbian.org jessie/main armhf Packages [9,533 kB] Get:4 http://archive.raspberrypi.org jessie/main armhf Packages [163 kB] Get:5 http://archive.raspberrypi.org jessie/ui armhf Packages [57.9 kB] Ign http://archive.raspberrypi.org jessie/main Translation-en_GB
Ign http://archive.raspberrypi.org jessie/main Translation-en
Ign http://archive.raspberrypi.org jessie/ui Translation-en_GB
Ign http://archive.raspberrypi.org jessie/ui Translation-en
Get:6 http://mirrordirector.raspbian.org jessie/contrib armhf Packages [43.3 kB] Get:7 http://mirrordirector.raspbian.org jessie/non-free armhf Packages [84.2 kB] Get:8 http://mirrordirector.raspbian.org jessie/rpi armhf Packages [1,356 B] Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en_GB
Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en
Ign http://mirrordirector.raspbian.org jessie/main Translation-en_GB
Ign http://mirrordirector.raspbian.org jessie/main Translation-en
Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en_GB
Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en
Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en_GB
Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en
Fetched 9,920 kB in 37s (262 kB/s)
Reading package lists… Done

The miflora script needs python3 to run, so we install it now:
pi@pi0w-miflora:~ $ sudo apt-get install python3

Reading package lists… Done
Building dependency tree
Reading state information… Done
The following extra packages will be installed:
dh-python libmpdec2 libpython3-stdlib libpython3.4-minimal libpython3.4-stdlib python3-minimal python3.4 python3.4-minimal
Suggested packages:
python3-doc python3-tk python3-venv python3.4-venv python3.4-doc binfmt-support
The following NEW packages will be installed:
dh-python libmpdec2 libpython3-stdlib libpython3.4-minimal libpython3.4-stdlib python3 python3-minimal python3.4 python3.4-minimal
0 upgraded, 9 newly installed, 0 to remove and 52 not upgraded.
Need to get 4,259 kB of archives.
After this operation, 17.2 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main libmpdec2 armhf 2.4.1-1 [65.8 kB] Get:2 http://mirrordirector.raspbian.org/raspbian/ jessie/main libpython3.4-minimal armhf 3.4.2-1 [483 kB] Get:3 http://mirrordirector.raspbian.org/raspbian/ jessie/main libpython3.4-stdlib armhf 3.4.2-1 [2,011 kB] Get:4 http://mirrordirector.raspbian.org/raspbian/ jessie/main python3.4-minimal armhf 3.4.2-1 [1,355 kB] Get:5 http://mirrordirector.raspbian.org/raspbian/ jessie/main python3.4 armhf 3.4.2-1 [204 kB] Get:6 http://mirrordirector.raspbian.org/raspbian/ jessie/main python3-minimal armhf 3.4.2-2 [34.7 kB] Get:7 http://mirrordirector.raspbian.org/raspbian/ jessie/main libpython3-stdlib armhf 3.4.2-2 [18.1 kB] Get:8 http://mirrordirector.raspbian.org/raspbian/ jessie/main python3 armhf 3.4.2-2 [21.2 kB] Get:9 http://mirrordirector.raspbian.org/raspbian/ jessie/main dh-python all 1.20141111-2 [66.4 kB] Fetched 4,259 kB in 5s (795 kB/s)
Selecting previously unselected package libmpdec2:armhf.
(Reading database … 31449 files and directories currently installed.)
Preparing to unpack …/libmpdec2_2.4.1-1_armhf.deb …
Unpacking libmpdec2:armhf (2.4.1-1) …
Selecting previously unselected package libpython3.4-minimal:armhf.
Preparing to unpack …/libpython3.4-minimal_3.4.2-1_armhf.deb …
Unpacking libpython3.4-minimal:armhf (3.4.2-1) …
Selecting previously unselected package libpython3.4-stdlib:armhf.
Preparing to unpack …/libpython3.4-stdlib_3.4.2-1_armhf.deb …
Unpacking libpython3.4-stdlib:armhf (3.4.2-1) …
Selecting previously unselected package python3.4-minimal.
Preparing to unpack …/python3.4-minimal_3.4.2-1_armhf.deb …
Unpacking python3.4-minimal (3.4.2-1) …
Selecting previously unselected package python3.4.
Preparing to unpack …/python3.4_3.4.2-1_armhf.deb …
Unpacking python3.4 (3.4.2-1) …
Selecting previously unselected package python3-minimal.
Preparing to unpack …/python3-minimal_3.4.2-2_armhf.deb …
Unpacking python3-minimal (3.4.2-2) …
Selecting previously unselected package libpython3-stdlib:armhf.
Preparing to unpack …/libpython3-stdlib_3.4.2-2_armhf.deb …
Unpacking libpython3-stdlib:armhf (3.4.2-2) …
Selecting previously unselected package python3.
Preparing to unpack …/python3_3.4.2-2_armhf.deb …
Unpacking python3 (3.4.2-2) …
Selecting previously unselected package dh-python.
Preparing to unpack …/dh-python_1.20141111-2_all.deb …
Unpacking dh-python (1.20141111-2) …
Processing triggers for man-db ( …
Processing triggers for mime-support (3.58) …
Setting up libmpdec2:armhf (2.4.1-1) …
Setting up libpython3.4-minimal:armhf (3.4.2-1) …
Setting up libpython3.4-stdlib:armhf (3.4.2-1) …
Setting up python3.4-minimal (3.4.2-1) …
Setting up python3.4 (3.4.2-1) …
Setting up python3-minimal (3.4.2-2) …
Setting up libpython3-stdlib:armhf (3.4.2-2) …
Setting up dh-python (1.20141111-2) …
Setting up python3 (3.4.2-2) …
running python rtupdate hooks for python3.4…
running python post-rtupdate hooks for python3.4…
Processing triggers for libc-bin (2.19-18+deb8u7) …

Bluetooth software is already a part of the raspbian-jessie-lite image, so we can just do a Bluetooth Low Energy scan to see if we find the MiFlora sensors. I have 2 sensors in range (you can only exit the scan by pressing Ctrl+C):
pi@pi0w-miflora:~ $ sudo hcitool lescan

LE Scan …
C4:7C:8D:61:57:A0 (unknown)
C4:7C:8D:62:41:BE (unknown)
C4:7C:8D:61:57:A0 Flower care
C4:7C:8D:62:41:BE Flower care

For some reason it sees every sensor 2x (once as unknown, and once by it’s Bluetooth broadcast name).

What matters is now we know the MAC address of the MiFlora, let’s test one if we get any data. If we got only zeros using the next command, that would mean that there’s some issue in the communication or the sensor is faulty.
pi@pi0w-miflora:~ $ gatttool --device=C4:7C:8D:61:57:A0 --char-read -a 0x35

Characteristic value/descriptor: aa bb cc dd ee ff 99 88 77 66 00 00 00 00 00 00

Seems like the sensor is OK!

Now we need to install git to be able to download Daniel’s miflora library from github.
pi@pi0w-miflora:~ $ sudo apt-get install git -y

Reading package lists… Done
Building dependency tree
Reading state information… Done
The following extra packages will be installed:
git-man liberror-perl rsync
Suggested packages:
git-daemon-run git-daemon-sysvinit git-doc git-el git-email git-gui gitk gitweb git-arch git-cvs git-mediawiki git-svn
The following NEW packages will be installed:
git git-man liberror-perl rsync
0 upgraded, 4 newly installed, 0 to remove and 52 not upgraded.
Need to get 4,389 kB of archives.
After this operation, 21.0 MB of additional disk space will be used.
Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main liberror-perl all 0.17-1.1 [22.4 kB] Get:2 http://mirrordirector.raspbian.org/raspbian/ jessie/main git-man all 1:2.1.4-2.1+deb8u3 [1,268 kB] Get:3 http://mirrordirector.raspbian.org/raspbian/ jessie/main git armhf 1:2.1.4-2.1+deb8u3 [2,725 kB] Get:4 http://mirrordirector.raspbian.org/raspbian/ jessie/main rsync armhf 3.1.1-3 [373 kB] Fetched 4,389 kB in 5s (789 kB/s)
Selecting previously unselected package liberror-perl.
(Reading database … 32366 files and directories currently installed.)
Preparing to unpack …/liberror-perl_0.17-1.1_all.deb …
Unpacking liberror-perl (0.17-1.1) …
Selecting previously unselected package git-man.
Preparing to unpack …/git-man_1%3a2.1.4-2.1+deb8u3_all.deb …
Unpacking git-man (1:2.1.4-2.1+deb8u3) …
Selecting previously unselected package git.
Preparing to unpack …/git_1%3a2.1.4-2.1+deb8u3_armhf.deb …
Unpacking git (1:2.1.4-2.1+deb8u3) …
Selecting previously unselected package rsync.
Preparing to unpack …/rsync_3.1.1-3_armhf.deb …
Unpacking rsync (3.1.1-3) …
Processing triggers for man-db ( …
Processing triggers for systemd (215-17+deb8u6) …
Setting up liberror-perl (0.17-1.1) …
Setting up git-man (1:2.1.4-2.1+deb8u3) …
Setting up git (1:2.1.4-2.1+deb8u3) …
Setting up rsync (3.1.1-3) …
Processing triggers for systemd (215-17+deb8u6) …

We can now download the miflora library:
pi@pi0w-miflora:~ $ git clone https://github.com/open-homeautomation/miflora.git

Cloning into ‘miflora’…
remote: Counting objects: 256, done.
remote: Total 256 (delta 0), reused 0 (delta 0), pack-reused 256
Receiving objects: 100% (256/256), 88.61 KiB | 0 bytes/s, done.
Resolving deltas: 100% (119/119), done.
Checking connectivity… done.

Let’s change to the miflora folder, check the contents of it, then make a copy of the original demo.py file (we’ll be working with the copied one, named demo1.py), and make it executable:

pi@pi0w-miflora:~ $ cd miflora/
pi@pi0w-miflora:~/miflora $ ls
build.sh  demo.py  dist  LICENSE  miflora  README.rst  setup.py  test.sh
pi@pi0w-miflora:~/miflora $ cp demo.py demo1.py
pi@pi0w-miflora:~/miflora $ chmod +x demo1.py

Then change the MAC address present in the demo1.py file to the one we got earlier when we scanned the devices.

pi@pi0w-miflora:~/miflora $ nano demo1.py

Change this:
poller = MiFloraPoller("C4:7C:8D:60:8F:E6")
To the MAC address of your own MiFlora sensor:
poller = MiFloraPoller("C4:7C:8D:61:57:A0")

Then try to run the script:

pi@pi0w-miflora:~/miflora $ python3 demo1.py
Getting data from Mi Flora
FW: 2.9.2
Name: Flower care
Temperature: 27.7
Moisture: 6
Light: 743
Conductivity: 1
Battery: 98

Yay, we got the data from the sensor! If you have more sensors like I do, just make another copy of the original demo.py file, e.g. demo2.py, make it executable, and edit the MAC address to the one you need. Repeat for every sensor you have.

Sending the sensor data to Pimatic

Now that we can fetch the data to the Pi3, we need to tell Pimatic to display them.

Integration in pimatic

We need the pimatic-cron, pimatic-shell-execute, pimatic-log-reader plugins to be installed for this, so do that first.

It needs to look like this in the config:

"plugins": [
      "plugin": "cron"
      "plugin": "shell-execute",
      "active": true
	  "plugin": "log-reader",
      "active": true,
	  "debug": true

Then create the rule which will run the script every 10 minutes (there is no point to run it more freqently, as the MiFlora itself refreshes it’s data every 5 minutes only). The rule uses the cron plugin and the shell-execute plugin, and overwrites the contents of the “/home/pi/miflora/miflora-pimatic.log” file, so it will always contain only the latest data.

In the pimatic web GUI you can set it up like this:

every 10 minutes
execute "/home/pi/miflora/demo.py"

It will look like this in the config file:

"rules": [
      "id": "poll-miflora",
      "name": "Poll MiFlora",
      "rule": "when every 10 minutes then execute \"python3 /home/pi/miflora/demo.py > /home/pi/miflora/miflora-pimatic.log\"",
      "active": true,
      "logging": false

Then we will create a LogWatcher device which will keep an eye on the /home/pi/miflora/miflora-pimatic.log file, and display the 5 important variables: Temperature, Moisture, Light, Conductivity, Battery.

The device config looks like this:

"devices": [
      "file": "/home/pi/miflora/miflora-pimatic.log",
      "attributes": [
          "name": "Temperature",
          "type": "number",
          "unit": "°C"
          "name": "Moisture",
          "type": "number",
          "unit": "%"
          "name": "Light",
          "type": "number",
          "unit": "lux"
          "name": "Conductivity",
          "type": "number",
          "unit": "us/cm"
          "name": "Battery",
          "type": "number",
          "unit": "%"
      "lines": [
          "match": "Temperature: (.+)",
          "predicate": "miflora.temperature",
          "Temperature": "$1"
          "match": "Moisture: (.+)",
          "predicate": "miflora.moisture",
          "Moisture": "$1"
          "match": "Light: (.+)",
          "predicate": "miflora.light",
          "Light": "$1"
          "match": "Conductivity: (.+)",
          "predicate": "miflora.conductivity",
          "Conductivity": "$1"
          "match": "Battery: (.+)",
          "predicate": "miflora.battery",
          "Battery": "$1"
      "xAttributeOptions": [],
      "id": "miflora",
      "name": "MiFlora",
      "class": "LogWatcher"

The LogWatcher device will then update the data on the Pimatic frontend.

You need to stick the sensor into the soil of the plant you wish to monitor (the soil level must be at the point where the prongs of the sensor split into two).

There is an ongoing discussion on the pimatic forums about the MiFlora sensor, please feel free to join and comment if you’ve liked this tutorial.

That is all!

All images are a courtesy of Xiaomi or my own screenshots.