Xiaomi Miflora Sensor Integration

Moving from other topic.

Here is an alternative way of getting MiFlora plant sensor data into openhab:

There are several threads about this already. I’m posting my setup,
it can probably be labeled as hack, it’s not clean, it’s not beautiful, it’s not easy.
However it works for me, and it’s easy to maintain.

First This is the monitor I’m talking about
http://xiaomi-mi.com/smart-home/xiaomi-huahuacacao-flower-care-smart-monitor/
http://xiaomi-mi.com/uploads/CatalogueImage/pvm_xiaomi-flower-monitor-02_14361_1466174108.jpg

It can be bought on ebay aliexpress and similar, I payed 11 euros a piece on aliexpress.

This thing transfers data using Bluetooth (BT). BT is short ranged, typically you will have plants/flowers scattered around your house on different floors and whatnot.
Therefor I’m using RPis (2-3) to collect data and to poll the devices.
The RPI:s will then post data to Openhab using MQTT.

Source code in java, bash script is available here:
(Extract them from the jar file if you wish to alter)
http://www.zoidberg.se/pub/sproute.jar

Java source files (they are included in the jar as well):
http://www.zoidberg.se/pub/SprouteConfig.java
http://www.zoidberg.se/pub/Sproute.java
http://www.zoidberg.se/pub/PlantConfig.java

  1. Get some RPI or similar
  2. Install Bluetooth dongle or use the built in in RPi3 (not tested this)
    https://www.raspberrypi.org/learning/robo-butler/bluetooth-setup/
  3. Install Java 8
    http://www.rpiblog.com/2014/03/installing-oracle-jdk-8-on-raspberry-pi.html
  4. Install mqtt clients: sudo apt-get install mosquitto-clients
  5. Install git: sudo apt-get install git
  6. Install python3
    sudo apt-get install python3
    sudo apt-get install python3-pip
    sudo apt-get install python3-gattlib (or sudo pip3 install gattlib)
  7. Install miflora python library: https://github.com/open-homeautomation/miflora
    git clone https://github.com/open-homeautomation/miflora
    (I’m not the author of miflora)
  8. Build it, or use the build source inside the repo
  9. Building (./build.sh)
  10. Verify that the lib works by invoking python3 demo.py (change the macaddress inside)

Add this python script to miflora:

import sys
import time
import datetime

from miflora.miflora_poller import MiFloraPoller, \
    MI_CONDUCTIVITY, MI_MOISTURE, MI_LIGHT, MI_TEMPERATURE

poller = MiFloraPoller(str(sys.argv[1]))
now = time.time()
dtnow=datetime.datetime.fromtimestamp(now)
millis = int(round(now * 1000))
yyyymmddhhmmss =  datetime.datetime.strftime(dtnow, '%Y-%m-%d %H:%M:%S')

temp = poller.parameter_value("temperature")
moist = poller.parameter_value(MI_MOISTURE)
light = poller.parameter_value(MI_LIGHT)
cond = poller.parameter_value(MI_CONDUCTIVITY)
battery = poller.battery_level()

print(yyyymmddhhmmss + " C: {:.2f} M: {:d} L: {:d} CD: {:d} B: {:d}".format(temp, moist, light, cond, battery) + " time:" +  str(millis))

Edit the sproute.sh file (located inside the jar if you want to extract it):
Change the parameters on top so the locations are correct:

#!/bin/bash -

#sproute.sh -l   - Will list all mac-addresses with Mi Flower
#sproute.sh -o   - Will generate Openhab items calling java program
#sproute.sh -p   - Will poll and publish data using mqtt

MOSQ_SERVER="10.1.1.1"
CHECK_PLANT="/home/pi/miflora/checkplant.py"
SCAN_RESULT_FILE="/tmp/result.txt"
PLANT_ITEMS_FILE="/home/pi/plants.items"
SPROUTE_JAR="/home/pi/sproute/sproute.jar"
SPROUTE_PROPS="/home/pi/sproute.properties"
PLANT_FILES_DIR="/home/pi/plants"

plantCheck() {
  LOGROW=`python3 $CHECK_PLANT "${plant_mac}"`
  echo $LOGROW >> "/var/log/plant.${plant_log_suffix}.log"

  TEMP0=`echo $LOGROW | awk '{print $4}'`
  MOIST0=`echo $LOGROW | awk '{print $6}'`
  LIGHT0=`echo $LOGROW | awk '{print $8}'`
  COND0=`echo $LOGROW | awk '{print $10}'`
  BATTERY0=`echo $LOGROW | awk '{print $12}'`

  mosquitto_pub -h $MOSQ_SERVER -m $TEMP0 -t plants/"${plant_name,,}"/temp -q 1
  mosquitto_pub -h $MOSQ_SERVER -m $MOIST0 -t plants/"${plant_name,,}"/moist -q 1
  mosquitto_pub -h $MOSQ_SERVER -m $LIGHT0 -t plants/"${plant_name,,}"/light -q 1
  mosquitto_pub -h $MOSQ_SERVER -m $COND0 -t plants/"${plant_name,,}"/cond -q 1
  mosquitto_pub -h $MOSQ_SERVER -m $BATTERY0 -t plants/"${plant_name,,}"/battery -q 1

  mosquitto_pub -h $MOSQ_SERVER -m ${plant_moist_low} -t plants/"${plant_name,,}"/moistlow -q 1
  mosquitto_pub -h $MOSQ_SERVER -m ${plant_moist_high} -t plants/"${plant_name,,}"/moisthigh -q 1

  mosquitto_pub -h $MOSQ_SERVER -m ${plant_conductivity_low} -t plants/"${plant_name,,}"/condlow -q 1
  mosquitto_pub -h $MOSQ_SERVER -m ${plant_conductivity_high} -t plants/"${plant_name,,}"/condhigh -q 1

  echo "Published ${plant_log_suffix}: TEMP=$TEMP0 MOIST=$MOIST0 LIGHT=$LIGHT0 COND=$COND0 BATTERY=$BATTERY0"
  echo "Published COND_LOW=${plant_conductivity_low} COND_HIGH=${plant_conductivity_high}"

  sleep 10
  MILLIS=`date +%s%N | cut -b1-13`
  mosquitto_pub -h $MOSQ_SERVER -m $MILLIS -t plants/update -q 1
}

readPlantConfig() {
  configFile=$1
if [ -f "$configFile" ]; then
  echo "$configFile found."

  while IFS='=' read -r key value
  do
    key=$(echo $key | tr '.' '_')
    eval "${key}='${value}'"
  done < "$configFile"
  echo "Plant name   = " ${plant_name}
  echo "MacAddress   = " ${plant_mac}
else
  echo "$configFile not found."
fi
verbose='false'
}

lflag=''
oflag=''
pflag=''
files=''

if [ "$#" -ne 1 ]; then
  echo "sproute.sh -l   - Will list all mac-addresses with Mi Flower"
  echo "sproute.sh -o   - Will generate Openhab items calling java program"
  echo "sproute.sh -p   - Will poll and publish data using mqtt"
fi

while getopts 'lofp' flag; do
  case "${flag}" in
    l) lflag='true' ;;
    o) oflag='true' ;;
    p) pflag='true' ;;
    f) files="${OPTARG}" ;;
    *) error "Unexpected option ${flag}" ;;
  esac
done

if [ "$lflag" == "true" ]; then
  echo "Searching for mi flower firmware 2.6.6"
  ##Hack to restart hci, when kill will stop working otherwise
  #
  sudo hciconfig hci0 down
  sudo hciconfig hci0 up
  # End Hack
  sudo hcitool lescan> $SCAN_RESULT_FILE &
  sleep 5
  sudo pkill --signal SIGINT hcitool
  sudo hciconfig hci0 down
  sudo hciconfig hci0 up
  result=$(sudo timeout 5s hcitool lescan)
  ##Hack to restart hci, when kill will stop working otherwise
  sudo hciconfig hci0 down
  sudo hciconfig hci0 up
  # End Hack
  echo "Firmware 2.6.6"
  sudo cat $SCAN_RESULT_FILE |grep "Flower care"
  echo "Firmware 2.6.2"
  sudo cat $SCAN_RESULT_FILE |grep "Flower mate"
fi

if [ "$oflag" == "true" ]; then
   rm $PLANT_ITEMS_FILE
   java -jar $SPROUTE_JAR $SPROUTE_PROPS -o $PLANT_ITEMS_FILE
   echo "Wrote $PLANT_ITEMS_FILE"
   cat $PLANT_ITEMS_FILE
fi

if [ "$pflag" == "true" ]; then
   PLANT_FILES=`ls ${PLANT_FILES_DIR}/*`
   echo $PLANT_FILES
   for i in $PLANT_FILES; do
      readPlantConfig $i
      plantCheck
   done
fi

  1. Check your plants with this command:
pi@colibri:~/plants $ /sbin/sproute.sh -l
Searching for mi flower firmware 2.6.6
Firmware 2.6.6
C4:7C:8D:61:CB:75 Flower care
C4:7C:8D:61:CD:11 Flower care
Firmware 2.6.2
C4:7C:8D:62:51:51 Flower mate
  1. Create plants file inside the plant folder (see sh-script)
i@colibri:~/plants $ cat benji.zpc 
plant.name=Benji
plant.mac=C4:7C:8D:61:CB:75
plant.moist.low=20
plant.moist.high=60
plant.temp.low=8.0
plant.temp.high=32.0
plant.conductivity.low=500
plant.conductivity.high=2000
plant.log.suffix=p0

Create openhab items:
This will create openhab items for all plants you have.

pi@colibri:~/plants $ /sbin/sproute.sh -o
Wrote file successfully: /home/pi/plants.items
Wrote /home/pi/plants.items
Group Plants
Group PlantTemperature
Group PlantMoisture
Group PlantLight
Group PlantConductivity
Group PlantBattery

Number   PlantsUpdate          "Plants Updated [%s]"  <clock> (Plants) { mqtt="<[mosquitto:plants/update:state:default]" }

Group    PlantBenji             "Plant Benji"               <plant2>        (Plants)
Number   PlantBenjiTemperature  "Benji  [%.2f °C]"       <ptemp>  (Temperature, PlantTemperature, PlantBenji) { mqtt="<[mosquitto:plants/benji/temp:state:default]" }
Number   PlantBenjiTempHigh     "Benji [%.2f °C]"                          (PlantBenji)  { mqtt="<[mosquitto:plants/benji/temphigh:state:default]" }
Number   PlantBenjiTempLow     "Benji [%.2f °C]"                          (PlantBenji)  { mqtt="<[mosquitto:plants/benji/templow:state:default]" }
Number   PlantBenjiMoisture     "Benji [%d %%]"             <pmoisture>     (PlantMoisture, PlantBenji)     { mqtt="<[mosquitto:plants/benji/moist:state:default]" }
Number   PlantBenjiMoistLow     "Benji [%d %%]"             <pmoisture>     (PlantMoisture, PlantBenji)     { mqtt="<[mosquitto:plants/benji/moistlow:state:default]" }
Number   PlantBenjiMoistHigh    "Benji [%d %%]"             <pmoisture>     (PlantMoisture, PlantBenji)     { mqtt="<[mosquitto:plants/benji/moisthigh:state:default]" }
Switch   PlantBenjiMoistWarn    "Benji Moisture Level Warning"                                  (PlantMoisture, PlantBenji) 
Number   PlantBenjiConductivity "Benji [%d uS/cm]"          <pconduct> (PlantConductivity, PlantBenji) { mqtt="<[mosquitto:plants/benji/cond:state:default]" }
Number   PlantBenjiConductHigh  "Benji [%d uS/cm]"           (PlantConductivity, PlantBenji) { mqtt="<[mosquitto:plants/benji/condhigh:state:default]" }
Number   PlantBenjiConductLow   "Benji [%d uS/cm]"           (PlantConductivity, PlantBenji) { mqtt="<[mosquitto:plants/benji/condlow:state:default]" }
Switch   PlantBenjiConductWarn    "Benji Conductivity Level Warning"                                  (PlantConductivity, PlantBenji) 
Number   PlantBenjiLight        "Benji [%d Lux]"            <psun>          (PlantLight, PlantBenji)        { mqtt="<[mosquitto:plants/benji/light:state:default]" }
Number   PlantBenjiBattery      "Benji [%d %%]"             <pbattery>      (PlantBattery, Battery, PlantBenji)       { mqtt="<[mosquitto:plants/benji/battery:state:default]" }
DateTime PlantBenjiLastUpdate   "Last Update [%1$tY-%1$tm-%1$td %1$tH:%1$tM]" <clock>        (PlantLastUpdate, PlantBenji)
  1. Send result to openhab
pi@colibri:~/plants $ /sbin/sproute.sh -p
/home/pi/plants/benji.zpc found.
Plant name   =  Benji
MacAddress   =  C4:7C:8D:61:CB:75
Published p0: TEMP=20.60 MOIST=79 LIGHT=2185 COND=3471 BATTERY=92
Published COND_LOW=500 COND_HIGH=2000

In openhab you can add the following warning to send out notification when to water the plant:

import java.util.Map
import java.util.HashMap
val String LOG_PLANTS = "Plants"
var Map<String, Integer> mNameToMoistLow = new HashMap<String, Integer>()
var Map<String, Integer> mNameToMoistHigh = new HashMap<String, Integer>()

val Functions$Function2 populateMoistHighLow = [Map<String, Integer> mNameToMoistLow, Map<String, Integer> mNameToMoistHigh |
     mNameToMoistLow.clear()
     mNameToMoistHigh.clear()
     PlantMoistLow.members.forEach[ moistLow |
        mNameToMoistLow.put(moistLow.label, new Integer(moistLow.state.toString))
     ]
   
     PlantMoistHigh.members.forEach[ moistHigh |
        mNameToMoistHigh.put(moistHigh.label, new Integer(moistHigh.state.toString))
     ]
    
]

rule "PlantThirstyWarning"
when
  Item PlantsUpdate received update
then
  if (mNameToMoistLow == null) {
       mNameToMoistLow = new HashMap()
   }
   if (mNameToMoistHigh == null) {
       mNameToMoistHigh = new HashMap()
   }
   
   if (mNameToMoistLow.empty || mNameToMoistHigh.empty) {
      populateMoistHighLow.apply(mNameToMoistLow, mNameToMoistHigh)
      logInfo(LOG_PLANTS, "Created map for plants high low low:" + mNameToMoistLow + " high: " + mNameToMoistHigh)
   }
   
   logInfo(LOG_PLANTS, "Moisture updated, checking if any plant is thirsty")
   PlantMoist.members.forEach[ moist |
       val int moistLow = mNameToMoistLow.get(moist.label)
       val int moistHigh = mNameToMoistHigh.get(moist.label)
       var int moistValue = (moist.state as DecimalType).intValue
       logInfo("Plants", "Parsed low: " + moistLow + " high: " + moistHigh + " moist: " + moist.state.toString)
       if (moistValue <= moistLow) {
            logInfo(LOG_PLANTS, "Warning plant: " + moist.label + " is Below lower threshold")
            NotifyPlants.postUpdate("My thirsty warning")
       } else if (moistValue >= moistHigh) {
               logInfo(LOG_PLANTS, "Warning plant: " + moist.label + " is above higher threshold")
           } else {
               logInfo(LOG_PLANTS, "Plant is " + moist.label + " is ok on water level")
       }
     ]
end

Edit crontab (I run this every 3 hours)

1  */3  * * *   root    /sbin/sproute.sh -p

So some things

  • I use java to generate the openhab items, why because I know java better than python, and I have limited time.
  • I use bash because I know bash, could be done in python as well.
  • I log each check to a log-file, why because I graph this data with Graphite/Grafana
  • This is a hack, but it enables me to add or remove plants just by creating a plant file, giving the plant a name and specify a mac-address. My goal is to be able to add and change plants on the fly, but without rewriting code.
  • Watering rule is generic and will not have to be altered.
  • Is this solution ugly, yes, but still does the job for me, feel free to change whatever, or perhaps it can inspire to something better :slight_smile:
3 Likes