Code Monkey home page Code Monkey logo

mijia-homie's Introduction

About this project

This is a project for capturing BLE data from a Xiaomi Mijia 2 hygrometer-thermometer and publishing it. The repository includes a number of related crates:

  • mijia-homie, a service to connect to a number of Mijia sensors over BLE and publish their readings to an MQTT broker following the Homie convention.
  • homie-influx, a service to discover devices on an MQTT broker following the Homie convention and record their property value changes to an InfluxDB database.
  • homie-device, a library for implementing Homie devices.
  • homie-controller, a library for implementing Homie controllers.
  • mijia, a library for reading Mijia sensors.

The project originated from a blog post, and some ideas came from a Python utility for talking to the same sensors, especially this issue thread.

If you want a bit more back-story, there is also a slide deck.

Serving Suggestion

Setup

To run this code on your Raspberry Pi, you will need:

  • Some Xiaomi Mijia version 2 Bluetooth temperature and humidity sensors.
  • A working Rust toolchain and Docker on your laptop.
  • An MQTT broker to connect to (test.mosquitto.org works okay for testing, but you will want to deploy your own if you're monitoring your house).
  • Something to read the measurements from MQTT. Homie Device Discovery is probably easiest for debugging.

After following these steps, you should end up with something that looks like this:

System overview

  • Start by finding out which sensors you have:

    [email protected] EXAMPLE=list-sensors ./run.sh
    
  • Add each of the sensors to /home/pi/sensor-names.toml on the Raspberry Pi. Each line should be of the form:

    "A4:C1:38:D7:21:17"="Landing"
    

    If you don't know the sensor names yet, just make some names up for now.

  • You will also want to copy mijia-homie/mijia-homie.example.toml to /home/pi/mijia-homie.example.toml and edit it to suit your needs.

  • You should then be able to run the publisher using run.sh with default arguments:

    This will start the publisher under systemd and show you the logs.

  • It takes a while to connect to all of the sensors and start getting readings. Once everything is running, you can use HoDD to see your readings.

  • If everything is visible in HoDD then you can add other integrations. Anything that understands Homie should be able to read your sensors. OpenHAB is what we're using, but take a look at the Homie Implementations page and see if anything inspires you.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

mijia-homie's People

Contributors

afflux avatar alsuren avatar anusha-ramdarshan avatar dependabot[bot] avatar dwrobel avatar ikelutra avatar kw217 avatar lcsfelix avatar matteswhite avatar nzsmartie avatar qwandor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

mijia-homie's Issues

Debian packages link against the wrong version of libssl

The package for homie-influx 0.2.1 apparently links against libssl 1.0.2, but Debian stable has 1.0.0. This prevents it from running.

$ ldd -d /usr/bin/homie-influx 
/usr/bin/homie-influx: /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0: version `OPENSSL_1.0.2' not found (required by /usr/bin/homie-influx)
/usr/bin/homie-influx: /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0: version `OPENSSL_1.0.2' not found (required by /usr/bin/homie-influx)
        linux-vdso.so.1 (0x00007ffeed3fb000)
        libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007ff4302b2000)
        libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007ff42feb5000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff42fe9b000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007ff42fe91000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff42fe70000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff42fced000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff42fce6000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff42fb25000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff430eb0000)
symbol X509_VERIFY_PARAM_set1_host version OPENSSL_1.0.2 not defined in file libcrypto.so.1.0.0 with link time reference        (/usr/bin/homie-influx)
symbol X509_VERIFY_PARAM_set_hostflags version OPENSSL_1.0.2 not defined in file libcrypto.so.1.0.0 with link time reference    (/usr/bin/homie-influx)
symbol X509_VERIFY_PARAM_set1_ip version OPENSSL_1.0.2 not defined in file libcrypto.so.1.0.0 with link time reference  (/usr/bin/homie-influx)
symbol SSL_get0_param version OPENSSL_1.0.2 not defined in file libssl.so.1.0.0 with link time reference        (/usr/bin/homie-influx)

mijia-homie crash

Jan 20 01:12:55 cottagepi systemd[1]: Started Bluetooth temperature sensor to MQTT publisher.
Jan 20 01:12:56 cottagepi mijia-homie[465]: The application panicked (crashed).
Jan 20 01:12:56 cottagepi mijia-homie[465]: Message:  called `Option::unwrap()` on a `None` value
Jan 20 01:12:56 cottagepi mijia-homie[465]: Location: /project/homie-device/src/lib.rs:588
Jan 20 01:12:56 cottagepi mijia-homie[465]: Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.
Jan 20 01:12:56 cottagepi mijia-homie[465]: Run with RUST_BACKTRACE=full to include source snippets.
Jan 20 01:12:56 cottagepi mijia-homie[465]: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Jan 20 01:12:56 cottagepi mijia-homie[465]:  1: __libc_start_main
Jan 20 01:12:56 cottagepi mijia-homie[465]:     at <unknown source file>
Jan 20 01:12:56 cottagepi systemd[1]: mijia-homie.service: Main process exited, code=exited, status=101/n/a
Jan 20 01:12:56 cottagepi systemd[1]: mijia-homie.service: Failed with result 'exit-code'.
Jan 20 01:13:06 cottagepi systemd[1]: mijia-homie.service: Service RestartSec=10s expired, scheduling restart.
Jan 20 01:13:06 cottagepi systemd[1]: mijia-homie.service: Scheduled restart job, restart counter is at 1.
Jan 20 01:13:06 cottagepi systemd[1]: Stopped Bluetooth temperature sensor to MQTT publisher.
Jan 20 01:13:06 cottagepi systemd[1]: Started Bluetooth temperature sensor to MQTT publisher.
Jan 20 01:13:06 cottagepi mijia-homie[1041]: The application panicked (crashed).
Jan 20 01:13:06 cottagepi mijia-homie[1041]: Message:  called `Option::unwrap()` on a `None` value
Jan 20 01:13:06 cottagepi mijia-homie[1041]: Location: /project/homie-device/src/lib.rs:588
Jan 20 01:13:06 cottagepi mijia-homie[1041]: Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.
Jan 20 01:13:06 cottagepi mijia-homie[1041]: Run with RUST_BACKTRACE=full to include source snippets.
Jan 20 01:13:06 cottagepi mijia-homie[1041]: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Jan 20 01:13:06 cottagepi mijia-homie[1041]:  1: __libc_start_main
Jan 20 01:13:06 cottagepi mijia-homie[1041]:     at <unknown source file>
J

homie-controller fails to discover some property datatypes when there are many properties

E.g. for mijia-bridge-raspi:

 # mijia-bridge-raspi not ready Device { id: "mijia-bridge-raspi", homie_version: "4.0", name: Some("Raspberry Pi Mijia bridge"), state: Ready, implementation: Some("homie-rs"), nodes: {"A4C138487922": Node { id: "A4C138487922", name: Some("Bedside table"), node_type: Some("Mijia sensor"), properties: {"battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("39") }, "humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("61") }, "temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("19.98") }} }, "A4C13885646D": Node { id: "A4C13885646D", name: Some("Table dangly"), node_type: Some("Mijia sensor"), properties: {"humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("92") }, "battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("38") }, "temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("9.78") }} }, "A4C138C950F2": Node { id: "A4C138C950F2", name: Some("Outside chair"), node_type: Some("Mijia sensor"), properties: {"battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("47") }, "humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("99") }, "temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("9.99") }} }, "A4C1381E0AE8": Node { id: "A4C1381E0AE8", name: Some("Pallet bookshelf"), node_type: Some("Mijia sensor"), properties: {"temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("20.49") }, "battery": Property { id: "battery", name: Some("Battery level"), datatype: None, settable: false, retained: true, unit: None, format: None, value: Some("84") }, "humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("67") }} }, "A4C138D6D08B": Node { id: "A4C138D6D08B", name: Some("Fridge drawer"), node_type: Some("Mijia sensor"), properties: {"humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("98") }, "battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("17") }, "temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("0.36") }} }, "A4C13821459B": Node { id: "A4C13821459B", name: Some("Fridge door"), node_type: Some("Mijia sensor"), properties: {"temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("11.43") }, "humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("57") }, "battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("74") }} }, "A4C138598607": Node { id: "A4C138598607", name: Some("Anusha desk"), node_type: Some("Mijia sensor"), properties: {"battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("68") }, "temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("21.24") }, "humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("63") }} }, "A4C138E98330": Node { id: "A4C138E98330", name: Some("Kitchen shelf"), node_type: Some("Mijia sensor"), properties: {"temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("20.76") }, "humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("59") }, "battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("95") }} }, "A4C13859D9A2": Node { id: "A4C13859D9A2", name: Some("Office"), node_type: Some("Mijia sensor"), properties: {"temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("19.84") }, "humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("63") }, "battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("29") }} }, "A4C1381F003F": Node { id: "A4C1381F003F", name: Some("Living room shelves"), node_type: Some("Mijia sensor"), properties: {"humidity": Property { id: "humidity", name: Some("Humidity"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("63") }, "battery": Property { id: "battery", name: Some("Battery level"), datatype: Some(Integer), settable: false, retained: true, unit: Some("%"), format: None, value: Some("20") }, "temperature": Property { id: "temperature", name: Some("Temperature"), datatype: Some(Float), settable: false, retained: true, unit: Some("ºC"), format: None, value: Some("21.24") }} }} }.
 ## A4C138487922 ready? true
 ### battery: Some(Integer)
 ### humidity: Some(Integer)
 ### temperature: Some(Float)
 ## A4C13885646D ready? true
 ### humidity: Some(Integer)
 ### battery: Some(Integer)
 ### temperature: Some(Float)
 ## A4C138C950F2 ready? true
 ### battery: Some(Integer)
 ### humidity: Some(Integer)
 ### temperature: Some(Float)
 ## A4C1381E0AE8 ready? false
 ### temperature: Some(Float)
 ### battery: None
 ### humidity: Some(Integer)
 ## A4C138D6D08B ready? true
 ### humidity: Some(Integer)
 ### battery: Some(Integer)
 ### temperature: Some(Float)
 ## A4C13821459B ready? true
 ### temperature: Some(Float)
 ### humidity: Some(Integer)
 ### battery: Some(Integer)
 ## A4C138598607 ready? true
 ### battery: Some(Integer)
 ### temperature: Some(Float)
 ### humidity: Some(Integer)
 ## A4C138E98330 ready? true
 ### temperature: Some(Float)
 ### humidity: Some(Integer)
 ### battery: Some(Integer)
 ## A4C13859D9A2 ready? true
 ### temperature: Some(Float)
 ### humidity: Some(Integer)
 ### battery: Some(Integer)
 ## A4C1381F003F ready? true
 ### humidity: Some(Integer)
 ### battery: Some(Integer)
 ### temperature: Some(Float)

This appears to be because rumqttc is not giving us some retained topics when we subscribe to a topic filter which matches many retained topics.

controller: properties sometimes have no payload if not updated at runtime

Core issue is this:

// TODO: What about values of properties we don't yet know about? They may arrive
// before the $properties of the node, because the "homie/node_id/+" subscription
// matches both.

I have a node that updates very rarely. After restart of the controller, my broker publishes the retained payload topic before the node's $properties attribute, causing those payloads to get lost.

I'd suggest not to subscribe to wildcards, instead subscribe explicitly to the attributes, then to the payloads as discovered.

Influxdb v2

Hello,

I'm struggling getting this connected to influxdb2 (which uses a token and not user/pass). Can anyone help with the correct syntax?

[influxdb]
# The URL of the InfluxDB to which to connect.
url="http://localhost:8086"
# The username with which to authenticate to InfluxDB, if any.
#username=""
# The password with which to authenticate to InfluxDB, if any.
#password=""

publish bluez-generated to crates.io

  • Remove the event type that we stole from blurz
  • Add README describing how to use it, and warning that you will probably need to run introspect.sh yourself and send us patches.
  • 🚢

Upgrade to tokio 0.3

The latest version of tokio makes some incompatible API changes, so some amount of work will be required to migrate to it.

Support using multiple Bluetooth adapters simultaneously

The mijia crate should already discover sensors on multiple adapters, we just need to add some state logic to mijia-homie to avoid trying to connect to the same sensor via two different adapters at once. The first to successfully connect should win, I guess.

SOLVED: the topic for the device was corrupted and malformed

I have a homie device sending messages to an mqtt broker, but using the discover example, I see no events on Property change in the event loop. It matches but returns an empty vector of events.

looking at lib.rs in the homie-controller where the topic is subscribed appears to be the issue:

587     /// Start discovering Homie devices.
 588     async fn start(&self) -> Result<(), ClientError> {
 589         // Clear set of known devices so that we correctly subscribe to their topics again.
 590         *self.devices.lock().unwrap() = Arc::new(HashMap::new());
 591 
 592         //let topic = format!("{}/+/$homie", self.base_topic);
 593         let topic = format!("homie/#");
 594         log::trace!("Subscribe to {}", topic);
 595         self.mqtt_client.subscribe(topic, QoS::AtLeastOnce).await
 596     }   
 597     

if i modify the code to change the topic, i get messages. if the topic is homie/aurora-230202051/abc/entering-water-temperature why wouldn't home be the correct topic in the discovery example?

Handle reconnection cleanly

If the connection is lost to the MQTT broker, publish-mqtt will currently crash due to send errors. We should figure out a way to handle this more cleanly and keep trying to reconnect instead.

Integration tests for `homie-device` and `homie-controller` together

Add a test to the homie-device crate which sets up a local MQTT broker, connects to it along with a Homie controller, and makes sure that the device advertised by homie-device is correctly picked up by homie-controller, and property values can be sent in both directions.

Build Debian package for release

It would be useful to build Debian packages for Raspberry Pi (and maybe other targets) that users can install rather than building from source.

replace read-all-devices.

Its different use-cases should be split into:

  • a no-mqtt mode for publish-mqtt.
  • an example in mijia crate that takes a bluetooth address as env var.
  • an example in mijia crate that continuously scans and connects to each sensor in turn for a configurable time/reading-count.

Make manufacturer specific data available

First of all thank you for this interesting BLE library.

From the docs I don't see the possibility to access the manufacturer specific data advertised by some BLE devices. Would it be possible to expose them, maybe as an event?

Furthermore, it would be nice to be able to configure the scan process. Especially, I have the filter duplicates option in mind.


Background

I'm using Ruuvi Tags in my application. They use the manufacturer specific data advertisements to broadcast their sensor data. To be able to access the sensor data, I need access to the manufacturer specific data and be able to turn off the duplicates filter as otherwise newer values wouldn't be recognized.

Fix ldd auto-dependency warning for armv7 Debian package

As mentioned on #55, building a Debian package for armv7 gives a warning that ldd doesn't recognise the binary format. This means that cargo-deb's automatic dependency feature doesn't work. We need to figure out how do get an ldd which supports other architectures, or patch cargo-deb to use something else.

TARGET=armv7-unknown-linux-gnueabihf ./package.sh
...
warning: ldd (/home/qwandor/projects/reading-xiaomi-temp/target/armv7-unknown-linux-gnueabihf/release/mijia-homie):  (no auto deps for /home/qwandor/projects/reading-xiaomi-temp/target/armv7-unknown-linux-gnueabihf/release/mijia-homie)

Use correct hci device in mijia crate

On some devices (e.g. VIM3L Ubuntu), the default Bluetooth adapter is hci1 rather than hci0. However, mijia is hardcoded to use hci0, so it fails.

The mijia crate should automatically detect the correct adapter to use.

bluez-generated: Error: DbusError(D-Bus error: Method "WriteValue" with signature "aya{sv}" on interface "org.bluez.GattCharacteristic1" doesn't exist

Hello,

I'm trying to use bluez-generated to talk to a meat thermometer. Thank you for writing buez-generated and I also borrowed your bluetooth.rs code. However I seem to have trouble writing to a GattCharacteristic. After finding and connecting to the device, I'm trying to call WriteValue. But I get an error

Error: DbusError(D-Bus error: Method "WriteValue" with signature "aya{sv}" on interface "org.bluez.GattCharacteristic1" doesn't exist
 (org.freedesktop.DBus.Error.UnknownObject))

I used QDBusViewer to look at the interface and it does seem to have that method:

<method name="WriteValue"><arg name="value" type="ay" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>

Any idea or suggestion on what I could be doing wrong? (I suspect that this is not really an issue in bluez-generated. But I thought I start here).

My code: https://github.com/ruediger/cloudbbq2-rs/blob/main/src/main.rs#L98

Thanks in advance.

(btw. would you be interested in moving bluetooth.rs into a separate crate? It seems very useful as a library. I'm happy to send a PR.)

mijia-homie is failing to connect to sensors because "The maximum number of pending replies per connection has been reached"

Dec 21 21:06:36 cottagepi mijia-homie[31980]: Trying to connect to Tree top from status: Disconnected
Dec 21 21:06:36 cottagepi mijia-homie[31980]: Failed to connect to Tree top: Error connecting to Tree top: [DbusError(D-Bus error: The maximum number of pending replies per connection has been reached (org.freedesktop.DBus.Error.LimitsExceeded)), DbusError(D-Bus error: The maximum number of pending replies per connection has been reached (org.freedesktop.DBus.Error.LimitsExceeded))]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.