Jump to content

Recommended Posts

Posted

Hi everyone,

I wanted to use the BLU devices with my MQTT setup.

I made this to answer the question :

https://github.com/Refhi/Shelly-BLU-to-MQTT

It's a script you put on a BLU Gateway capable device where MQTT is enabled (Shelly BLU Gateway, or any Shelly Plus, Pro or Gen3 device (excluding sensors) to transfer your BLU devices events to the MQTT broker.

For now it should work with Shelly BLU Button and Shelly BLU DoorWindow.

You must have the mac address for it to work.

 

I hope it will help a few.

  • 1 month later...
Posted

Thanks! This works 😄

Here's a suggestion, don't use capital letters on the object names. object.Window is an property but object.window is not. So I just changed these:
BTH[0x00] = { n: "pid", t: uint8 };
BTH[0x01] = { n: "battery", t: uint8, u: "%" };
BTH[0x05] = { n: "illuminance", t: uint24, f: 0.01 };
BTH[0x1a] = { n: "door", t: uint8 };
BTH[0x20] = { n: "moisture", t: uint8 };
BTH[0x2d] = { n: "window", t: uint8 };
BTH[0x3a] = { n: "button", t: uint8 };
BTH[0x3f] = { n: "rotation", t: int16, f: 0.1 };

 

 

  • 1 month later...
  • 2 weeks later...
  • 3 weeks later...
Posted (edited)

Hi,

Sorry maybe for my silly question but could you please precise me what kind of informations need to be changed in the CONFIG ?

Do I need to change only the Mac Address or more ?

Is it possible to have an example to understand better ?

Here I don't understand what I need to indicate :

"another_address": "another_topic"

"yet_another_address": "yet_another_topic"

Edited by meematt372365
  • 3 months later...
  • 2 weeks later...
Posted

there is script which sends all arriving BLE messages to MQTT. It is not even necessary to directly reference the MACs in the script. The script is already in the Shelly App as a template. All you have to do is adjust the shelly topic.

 

/**
 * Universal BLU to MQTT Script

Short description

  - This script is about shares any BLU product's complete payload to MQTT.

  - The script should not require an individual BLU device's MAC address, rather it will "listen" for any BLU device.

  - It would use the MAC address to create the topic, with a key-value for each data point in the payload.

Running Process

Here is the summary of how it works:

  - Scanning: The script will open a BLU passive scan to receive any BLU device data. Using the BLE.Scanner.Start and BLE.Scanner.Subscribe modules.

  - Data Extraction: For each device found, it checks for service data associated with the BTHome standard (Service ID: fcd2).

  - Decoding: The BTHomeDecoder decodes the service data into human-readable format, including details such as temperature, battery, etc.

  - MQTT Push: The MQTT uses each device MAC address as a topic, then publishes device service_data or any other data you want to the MQTT.

 */


// *********************** Decoding Method ***********************
const uint8 = 0;
const int8 = 1;
const uint16 = 2;
const int16 = 3;
const uint24 = 4;
const int24 = 5;

// The BTH object defines the structure of the BTHome data
const BTH = {
  0x00: { n: "pid", t: uint8 },
  0x01: { n: "battery", t: uint8, u: "%" },
  0x02: { n: "temperature", t: int16, f: 0.01, u: "tC" },
  0x03: { n: "humidity", t: uint16, f: 0.01, u: "%" },
  0x05: { n: "illuminance", t: uint24, f: 0.01 },
  0x21: { n: "motion", t: uint8 },
  0x2d: { n: "window", t: uint8 },
  0x3a: { n: "button", t: uint8 },
  0x3f: { n: "rotation", t: int16, f: 0.1 },
};

function getByteSize(type) {
  if (type === uint8 || type === int8) return 1;
  if (type === uint16 || type === int16) return 2;
  if (type === uint24 || type === int24) return 3;
  //impossible as advertisements are much smaller;
  return 255;
}

// functions for decoding and unpacking the service data from Shelly BLU devices
const BTHomeDecoder = {
  utoi: function (num, bitsz) {
    let mask = 1 << (bitsz - 1);
    return num & mask ? num - (1 << bitsz) : num;
  },
  getUInt8: function (buffer) {
    return buffer.at(0);
  },
  getInt8: function (buffer) {
    return this.utoi(this.getUInt8(buffer), 8);
  },
  getUInt16LE: function (buffer) {
    return 0xffff & ((buffer.at(1) << 8) | buffer.at(0));
  },
  getInt16LE: function (buffer) {
    return this.utoi(this.getUInt16LE(buffer), 16);
  },
  getUInt24LE: function (buffer) {
    return (
      0x00ffffff & ((buffer.at(2) << 16) | (buffer.at(1) << 8) | buffer.at(0))
    );
  },
  getInt24LE: function (buffer) {
    return this.utoi(this.getUInt24LE(buffer), 24);
  },
  getBufValue: function (type, buffer) {
    if (buffer.length < getByteSize(type)) return null;
    let res = null;
    if (type === uint8) res = this.getUInt8(buffer);
    if (type === int8) res = this.getInt8(buffer);
    if (type === uint16) res = this.getUInt16LE(buffer);
    if (type === int16) res = this.getInt16LE(buffer);
    if (type === uint24) res = this.getUInt24LE(buffer);
    if (type === int24) res = this.getInt24LE(buffer);
    return res;
  },

  // Unpacks the service data buffer from a Shelly BLU device
  unpack: function (buffer) {
    //beacons might not provide BTH service data
    if (typeof buffer !== "string" || buffer.length === 0) return null;
    let result = {};
    let _dib = buffer.at(0);
    result["encryption"] = _dib & 0x1 ? true : false;
    result["BTHome_version"] = _dib >> 5;
    if (result["BTHome_version"] !== 2) return null;
    //can not handle encrypted data
    if (result["encryption"]) return result;
    buffer = buffer.slice(1);

    let _bth;
    let _value;
    while (buffer.length > 0) {
      _bth = BTH[buffer.at(0)];
      if (typeof _bth === "undefined") {
        // logger("unknown type", "BTH");
        break;
      }
      buffer = buffer.slice(1);
      _value = this.getBufValue(_bth.t, buffer);
      if (_value === null) break;
      if (typeof _bth.f !== "undefined") _value = _value * _bth.f;
      result[_bth.n] = _value;
      buffer = buffer.slice(getByteSize(_bth.t));
    }
    return result;
  },
};

// ***********************  Main Methods ***********************

const BTHOME_SVC_ID_STR = "fcd2";

// Bluetooth scan options
const SCAN_OPTION = {
  duration_ms: BLE.Scanner.INFINITE_SCAN, // Scan duration
  active: false,
}

// Push BLE devices to MQTT
function pushToMQ(addr, message) {
  if (!MQTT.isConnected()) return false; // Check the MQTT status
  
  MQTT.publish(addr, message);
  
  return true
}

// BLE scan callback
function scanCB(ev, res) {
  if (ev !== BLE.Scanner.SCAN_RESULT) return;
  const addr = 'BLE/'+res.addr; // Get devive's MAC address
  if (typeof res.service_data === 'undefined' || typeof res.service_data[BTHOME_SVC_ID_STR] === 'undefined') return;
  if (typeof addr === 'undefined') return; // If the device addredd is empty, return
 
  try {
    const decodeData = BTHomeDecoder.unpack(res.service_data[BTHOME_SVC_ID_STR]); // Decode service data
    
    // Combine data
    const postMessage = {
      addr: addr,
      rssi: res.rssi,
      local_name: res.local_name || "",
      service_data: decodeData,
    };
    
    // console.log(res.local_name, JSON.stringify(postMessage));
    
    // post data to MQTT
    pushToMQ(addr, JSON.stringify(postMessage));
    
  } catch(err) {
    console.log(err)
  }
  
}

// Start Scan BLE Devices
function startBLEScan() {
  if (!BLE.Scanner.isRunning()) {
    BLE.Scanner.Start(SCAN_OPTION, scanCB);
  } else {
    // If scanner is running, create a subscribe method
    BLE.Scanner.Subscribe(scanCB);
  }
}

startBLEScan()

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...