Shelly BLU devices

Begonnen von gvzdus, 20 Oktober 2024, 21:53:03

Vorheriges Thema - Nächstes Thema

Phlox

script und bridgeRegexp .*:bthome/bth_([a-z_0-9_]+):.* "bth_$1" funktionieren auch mit dem shelly blu H&T.

Danke nochmals, dachte schon ich kann den shelly blu H&T nicht mit fhem verwenden....doch es geht :-)

gvzdus

Eine Sache noch: Das Skript kann auch auf mehreren Shelly Gen3/4-Geräten installiert werden. WorstCase kommen dann mehrere Events an. Bietet sich ja für die Buttons o.ä. an.

Mumpitz

Zitat von: gvzdus am 21 Juli 2025, 09:18:10Ich probier's mal:
Gegeben sei ein per MQTT an FHEM angebundener Shelly Gen2/3/4 als "BTHome"-Gateway.
Du gehst auf die Shelly-UI des Gerätes:
- In "Settings -> Bluetooth" schaltest Du alles ein (Enable BT, Enable RPC, Enable BT Gateway).
- In "Scripts" legst Du ein Script an, kopierst das Folgende hinein, startet es und denkst an "Run on startup" einschalten, damit es nach Reboot wieder anläuft:


Vielen Dank für deine Zusammenfassung. Hat bei mir bei 4 Shelly Blur Door/Window auf Anhieb funktioniert!

DigiH

Nur als FYI - die Shelly BLU devices können auch mit Theengs Gateway empfangen werden und dann als dekodierte MQTT Messages direkt in FHEM eingebunden werden. Das ganze auch mit optional aktivierter Encryption auf den Shelly BLU Devices.

Gruß

Phlox

#19
Hallo,
nachdem das hier Beschriebene mit dem shelly h&t so gut funktionierte,
habe ich mir ein shelly blu h&t display zb zugelegt.
Es gefällt mir sehr gut, leider werden mit dem oben beschriebenen script keine Temperaturen usw übergeben.
Hat von euch schon jemand ein shelly blu h&t display zb per mqtt in fhem einbinden können?

Danke für èure Zeit.

Schöne Grüße

Phlox

Hermann

Hallo zusammen.

Ich habe z.Zt. 3 Shelly Blu Geräte in Betrieb.
Habe auf den Shelly Gen 3 Geräten das selbe BLU-->MQTT Script installiert.
Die  BLU werte kommen dann alle an mit "params_events_1_data_?????".
Bin Augenblicklich mit dem Control ZB beschäftigt,
sieht aber gut aus Werte kommen alle an.

Das BLU Script im Anhang.
Kann man ja Probieren oder auch nicht.

LG. Hermann


/**
 * @title Shelly BLU devices event handler for scripts
 * @description Script that handles and parses events from all Shelly BLU devices and
 *   emits event with the received data. Made to be used with
 *   "ble-events-handler.shelly.js" script that handles the events
 *   (Requires firmware version: 1.0.0-beta or newer)
 * @status production
 * @link https://github.com/ALLTERCO/shelly-script-examples/blob/main/ble/ble-shelly-blu.shelly.js
 */

/**
 * This script will use BLE scanner to listen for advertising data from nearby Shelly BLU devices,
 * decodes the data using a BTHome data structure, and emits the decoded data for further processing.
 *
 * This script DOESN'T execute actions, only emit events. Can be used with `ble-events-handler.shelly.js` example.
 * You can configure the event name, by default its `shelly-blu`, the body of the event contains all the data
 * parsed from the BLE device
 *
 * Represents data provided by each device.
 * Every value illustrating a sensor reading (e.g., button) may be a singular sensor value or
 * an array of values if the object has multiple instances.
 *
 * @typedef {Object} DeviceData
 * @property {number} pid - Packet ID.
 * @property {number} battery - The battery level of the device in percentage (%).
 * @property {number} rssi - The signal strength in decibels (dB).
 * @property {string} address - The MAC address of the Shelly BLU device.
 * @property {string} model - The model of the Shelly BLU device.
 * @property {number | number[]} [temperature] - The temperature value in degrees Celsius if the device has a temperature sensor. (Can be an array if has multiple instances)
 * @property {number | number[]} [humidity] - The humidity value in percentage (%) if the device has a humidity sensor. (Can be an array if has multiple instances)
 * @property {number | number[]} [illuminance] - The illuminance value in lux if the device has a light sensor. (Can be an array if has multiple instances)
 * @property {number | number[]} [motion] - Motion status: 0 for clear, 1 for motion (if the device has a motion sensor). (Can be an array if has multiple instances)
 * @property {number | number[]} [window] - Window status: 0 for closed, 1 for open (if the device has a reed switch). (Can be an array if has multiple instances)
 * @property {number | number[]} [button] - The number of presses if the device has a button. (Can be an array if has multiple instances)
 * @property {number | number[]} [rotation] - The angle of rotation in degrees if the device has a gyroscope. (Can be an array if has multiple instances)
 * @property {number | number[]} [channel] - The channel number if the device has a channel. (Can be an array if has multiple instances)
 * @property {number | number[]} [dimmer] - The direction of rotation if the device has a dimmer. (Can be an array if has multiple instances)
 * @property {number | number[]} [dimmersteps] - The number of steps if the device has a dimmer. (Can be an array if has multiple instances)
 *
 * @example
 * {"component":"script:*","name":"script","id":*,"now":*,"info":{"component":"script:*","id":*,"event":"shelly-blu","data":{"encryption":false,"BTHome_version":2,"pid":118,"battery":100,"button":1,"rssi":-76,"address":*},"ts":*}}
 */

/******************* START CHANGE HERE *******************/
const CONFIG = {
  // Specify the destination event where the decoded BLE data will be emitted. It allows for easy identification by other applications/scripts
  eventName: "shelly-blu",

  // If this value is set to true, the scan will be active.
  // If this value is set to false, the scan will be passive.
  // Active scan means the scanner will ping back the Bluetooth device to receive all its data, but it will drain the battery faster
  active: false,

  // When set to true, debug messages will be logged to the console
  debug: false,
};
/******************* STOP CHANGE HERE *******************/

const BTHOME_SVC_ID_STR = "fcd2";

const uint8 = 0;
const int8 = 1;
const uint16 = 2;
const int16 = 3;
const uint24 = 4;
const int24 = 5;
const dimmert = 6; // special data type for dimmer event

// 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" },
  0x04: { n: "atm. pressure", t: int24, f: 0.01, u: "hPa" },
  0x03: { n: "humidity", t: uint16, f: 0.01, u: "%" },
  0x05: { n: "illuminance", t: uint24, f: 0.01 },
  0x08: { n: "dew point", t: uint16, f: 0.01, u: "tC" },
  0x0C: { n: "capacitor voltage", t: uint16, f: 0.001, u: "V" },
  0x20: { n: "raining", t: uint8},
  0x21: { n: "motion", t: uint8 },
  0x2d: { n: "window", t: uint8 },
  0x2e: { n: "humidity", t: uint8, u: "%" },
  0x3a: { n: "button", t: uint8 },
  0x3c: { n: "dimmer", t: dimmert },
  0x3f: { n: "rotation", t: int16, f: 0.1 },
  0x44: { n: "wind speed", t: int16, f: 0.01, u: "m/s" },
  0x45: { n: "temperature", t: int16, f: 0.1, u: "tC" },
  0x46: { n: "UV index", t: int8, f: 0.1 },
  0x5E: { n: "wind direction", t: int16, f: 0.01 },
  0x5F: { n: "precipitation", t: int16, f: 0.1, u: "mm" },
  0x60: { n: "channel", t: uint8 },
};

function getByteSize(type) {
  if (type === uint8 || type === int8) return 1;
  if (type === uint16 || type === int16 || type === dimmert) 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) {
    const mask = 1 << (bitsz - 1);
    return num & mask ? num - (1 << bitsz) : num;
  },
  getUInt8: function (buffer) {
    return buffer.at(0);
  },
  getDimmer: function (buffer) {
    return {"dimmer": buffer.at(0), "dimmersteps": buffer.at(1)};
  },
  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);
    if (type === dimmert) res = this.getDimmer(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") {
        console.log("BTH: Unknown type");
        break;
      }
      buffer = buffer.slice(1);
      _value = this.getBufValue(_bth.t, buffer);

      //handle dimmer special case
      if (typeof _value === "object" && _bth.t === dimmert) {
        result["dimmersteps"] = _value["dimmersteps"];
        _value = _value["dimmer"];
      }

      if (_value === null) break;
      if (typeof _bth.f !== "undefined") _value = _value * _bth.f;

      if (typeof result[_bth.n] === "undefined") {
        result[_bth.n] = _value;
      }
      else {
        if (Array.isArray(result[_bth.n])) {
          result[_bth.n].push(_value);
        }
        else {
          result[_bth.n] = [
            result[_bth.n],
            _value
          ];
        }
      }

      buffer = buffer.slice(getByteSize(_bth.t));
    }
    return result;
  },
};

/**
 * Еmitting the decoded BLE data to a specified event. It allows other scripts to receive and process the emitted data
 * @param {DeviceData} data
 */
function emitData(data) {
  if (typeof data !== "object") {
    return;
  }
  Shelly.emitEvent(CONFIG.eventName, data);
}

//saving the id of the last packet, this is used to filter the duplicated packets
let lastPacketId = 0x100;

// Callback for the BLE scanner object
function BLEScanCallback(event, result) {
  //exit if not a result of a scan
  if (event !== BLE.Scanner.SCAN_RESULT) {
    return;
  }

  //exit if service_data member is missing
  if (
    typeof result.service_data === "undefined" ||
    typeof result.service_data[BTHOME_SVC_ID_STR] === "undefined"
  ) {
    return;
  }

  let unpackedData = BTHomeDecoder.unpack(
    result.service_data[BTHOME_SVC_ID_STR]
  );

  //exit if unpacked data is null or the device is encrypted
  if (
    unpackedData === null ||
    typeof unpackedData === "undefined" ||
    unpackedData["encryption"]
  ) {
    console.log("Error: Encrypted devices are not supported");
    return;
  }

  //exit if the event is duplicated
  if (lastPacketId === unpackedData.pid) {
    return;
  }

  lastPacketId = unpackedData.pid;

  unpackedData.rssi = result.rssi;
  unpackedData.address = result.addr;
  unpackedData.model = result.local_name;

  emitData(unpackedData);
}

// Initializes the script and performs the necessary checks and configurations
function init() {
  //exit if can't find the config
  if (typeof CONFIG === "undefined") {
    console.log("Error: Undefined config");
    return;
  }

  //get the config of ble component
  const BLEConfig = Shelly.getComponentConfig("ble");

  //exit if the BLE isn't enabled
  if (!BLEConfig.enable) {
    console.log(
      "Error: The Bluetooth is not enabled, please enable it from settings"
    );
    return;
  }

  //check if the scanner is already running
  if (BLE.Scanner.isRunning()) {
    console.log("Info: The BLE gateway is running, the BLE scan configuration is managed by the device");
  }
  else {
    //start the scanner
    const bleScanner = BLE.Scanner.Start({
      duration_ms: BLE.Scanner.INFINITE_SCAN,
      active: CONFIG.active
    });

    if (!bleScanner) {
      console.log("Error: Can not start new scanner");
    }
  }

  //subscribe a callback to BLE scanner
  BLE.Scanner.Subscribe(BLEScanCallback);

  // disable console.log when logs are disabled
  if (!CONFIG.debug) {
    console.log = function () { };
  }
}

init();
Schöne Grüße aus dem Münsterland
PI3+ Fritzbox , Homematic , FS20 , 1Wire , Shelly , EspXXXX , Duofern , Modbus