Jump to content
🌟 NOTIFICATION/Benachrichtigung: Welcome to our New Store! - shelly.com 🌟 ×

blu H&T to mqtt


Recommended Posts

Hello,

 

I use an shelly blu H&T on an shelly plus (blu gateway function).

Mqtt on the shelly plus works, but , also with script from GitHub or others, it is not possible to get the temperature and humidity into mqtt.

Battery Status and mac is possible.

 

Has anyone make it work?

 

An college does it with the blu gateway usb dongle, with it, it works.

 

I use following script:

 

/**
 * This script uses the BLE scan functionality in scripting
 * Selects Shelly BLU DoorWindow from the aired advertisements, decodes
 * the service data payload and transfert it to an MQTT broker
 */

// Shelly BLU devices:
// SBBT - Shelly BLU Button
// SBDW - Shelly BLU DoorWindow

// sample Shelly DW service_data payload
// 0x40 0x00 0x4E 0x01 0x64 0x05 0x00 0x00 0x00 0x2D 0x01 0x3F 0x00 0x00

// First byte: BTHome device info, 0x40 - no encryption, BTHome v.2
// bit 0: “Encryption flag”
// bit 1-4: “Reserved for future use”
// bit 5-7: “BTHome Version”

// AD 0: PID, 0x00
// Value: 0x4E

// AD 1: Battery, 0x01
// Value, 100%

// AD 2: Illuminance, 0x05
// Value: 0

// AD 3: Window, 0x2D
// Value: true, open

// AD 4: Rotation, 0x3F
// Value: 0

 

// CHANGE THE CONFIG OBJECT TO MATCH YOUR NEEDS

let CONFIG = {
    shelly_blu_address: {
        "b0:c7:de:04:28:c1": "shellies/BadEG",
       
    },
};

// END OF CHANGE

// MQTT publish function
function mqtt_publish(topic, payload) {
    let message = JSON.stringify(payload);
    MQTT.publish(topic, message, 0, false);
}

let ALLTERCO_MFD_ID_STR = "0ba9";
let BTHOME_SVC_ID_STR = "fcd2";

let ALLTERCO_MFD_ID = JSON.parse("0x" + ALLTERCO_MFD_ID_STR);
let BTHOME_SVC_ID = JSON.parse("0x" + BTHOME_SVC_ID_STR);

let SCAN_DURATION = BLE.Scanner.INFINITE_SCAN;

let uint8 = 0;
let int8 = 1;
let uint16 = 2;
let int16 = 3;
let uint24 = 4;
let int24 = 5;

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;
}

let BTH = [];
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 };


let 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;
    },
    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 (_bth === "undefined") {
                console.log("BTH: unknown type");
                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;
    },
};

let ShellyBLUParser = {
    getData: function (res) {
        let result = BTHomeDecoder.unpack(res.service_data[BTHOME_SVC_ID_STR]);
        result.addr = res.addr;
        result.rssi = res.rssi;
        return result;
    },
};

let last_packet_id = 0x100;
function scanCB(ev, res) {
    if (ev !== BLE.Scanner.SCAN_RESULT) return;
    // skip if there is no service_data member
    if (
        typeof res.service_data === "undefined" ||
        typeof res.service_data[BTHOME_SVC_ID_STR] === "undefined"
    )
        return;
    // skip if we are looking for name match but don't have active scan as we don't have name
    if (
        typeof CONFIG.shelly_blu_name_prefix !== "undefined" &&
        (typeof res.local_name === "undefined" ||
            res.local_name.indexOf(CONFIG.shelly_blu_name_prefix) !== 0)
    )
        return;
    // skip if we don't have address match
    if (
        typeof CONFIG.shelly_blu_address !== "undefined" &&
        !CONFIG.shelly_blu_address.hasOwnProperty(res.addr.toUpperCase())
    )
        return;
    let BTHparsed = ShellyBLUParser.getData(res);
    // // skip if parsing failed
    if (BTHparsed === null) {
        console.log("Failed to parse BTH data");
        return;
    }
    // skip, we are deduping results
    if (last_packet_id === BTHparsed.pid) return;
    last_packet_id = BTHparsed.pid;
    console.log("Shelly BTH packet: ", JSON.stringify(BTHparsed));
    // Get the topic for the current address
    let topic = CONFIG.shelly_blu_address[res.addr.toUpperCase()];
    console.log("Topic for the current address: ", topic);
    // Publish the data
    mqtt_publish(topic, BTHparsed);

}

print("Starting BLE scan");
BLE.Scanner.Start({ duration_ms: SCAN_DURATION, active: false }, scanCB);

Link to comment
Share on other sites

  • 2 weeks later...

Hi,

Im using this script for transmit ble device type data to mqtt.

It manages most of the shelly ble device types.

 

/**
 * This script reads selected Blu devices using AllowedMacs to publish received data to MQTT
 */
 
//Shelly device types ["SBBT", "SBDW","SBDW","SBMO","SBHT"];
let BLE_DESCR = ["Ytterdörr hall", "Dörr veranda"]; // declare descriptive name for each device. Just for easier mapping of mac adresses
let ALLOWEDMACS = ["00:00:00:00:00:00","00:00:00:00:00:00"]; // declare device macs
let DEVICETYPES = ["SBDW","SBBT"]; // need to declare device type
let LastBeaconUnixTimes = {};
let LastWindowStates = {};
 
/** =============================== CHANGE HERE =============================== */
let CONFIG = {
    //bluButtonAddress: "00:00:00:00:00:00", //the mac address of shelly blu button1 that will trigger the actions
    mqtt_topic: Shelly.getComponentConfig("mqtt").topic_prefix/*, // Get MQTT topic from configuration
    actions: { //urls to be called on a event
        //when adding urls you must separate them with commas and put them in quotation marks
        singlePush: [ //urls that will be executed at singlePush event from the blu button1
            //"http://192.168.1.35/roller/0?go=open",
            //"http://192.168.1.36/relay/0?turn=off",
            //"http://192.168.1.36/relay/1?turn=on"
        ],
        doublePush: [ //urls that will be execubuttonData)
                );              
              }
          }
          else
          {
            LastBeaconUnixTimes[result.addr] = Shelly.getComponentStatus("sys").unixtime;
      
            console.log("Button Publishing to MQTT");
            
            let buttonData = {
              type_name: "SBBT-002C",
              type: result.local_name,
              data: receivedData
            };
                                                      
            MQTT.publish(
              CONFIG.mqtt_topic + "/blubuttonstatus",
              JSON.stringify(buttonData)
            );      
          }
      }
      else if(deviceType.indexOf("SBDW"))
      {
        if (typeof  LastWindowStates[result.addr] === "undefined")
        {
          console.log("Initializing Last window state");
              LastWindowStates[result.addr]=255;
        }
   
        console.log("Window state", receivedData.Window);
        console.log("Last Window state", LastWindowStates[result.addr]);
                 
        if(receivedData.Window === LastWindowStates[rted at doublePush event from the blu button1
            //"http://192.168.1.35/roller/0?go=close"
        ],
        triplePush: [ //urls that will be executed at triplePush event from the blu button1
            //"http://192.168.1.38/color/0?turn=on&red=200&green=0&blue=0",
            //"http://192.168.1.38/light/0?turn=on",
            //"http://192.168.1.40/rpc/Switch.Set?id=0&on=false",
            //"http://192.168.1.40/rpc/Switch.Set?id=1&on=false"
        ],
        longPush: [ //urls that will be executed at longPush event from the blu button1
            //"http://192.168.1.41/rpc/Cover.Close",
            //"http://192.168.1.42/rpc/Cover.Close"
        ]
    }
    */
};
/** =============================== STOP CHANGING HERE =============================== */
 
let urlsPerCall = 3;
let urlsQueue = [];
let callsCounter = 0;
 
let ALLTERCO_MFD_ID_STR = "0ba9";
let BTHOME_SVC_ID_STR = "fcd2";
 
let uint8 = 0;
let int8 = 1;
let uint16 = 2;
let int16 = 3;
let uint24 = 4;
let int24 = 5;
 
let BTH = {};
BTH[0x00] = { n: "pid", t: uint8 };
BTH[0x01] = { n: "Battery", t: uint8, u: "%" };
BTH[0x02] = { n: "Temperature", t: int16, f: 0.01, u: "tC" };
BTH[0x03] = { n: "Humidity", t: uint16, f: 0.01, u: "%" };
BTH[0x05] = { n: "Illuminance", t: uint24, f: 0.01 };
BTH[0x21] = { n: "motion", t: uint8 };
BTH[0x1a] = { n: "Door", t: uint8 };
BTH[0x20] = { n: "Moisture", t: uint8 };
BTH[0x2d] = { n: "Window", t: uint8 };
BTH[0x2e] = { n: "Humidity", t: uint8, u: "%" };
BTH[0x3a] = { n: "Button", t: uint8 };
BTH[0x45] = { n: "Temperature", t: int16, f: 0.1, u: "tC" }
 
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;
}
 
let 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) << 😎 | 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) << 😎 | 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;
    },
    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);
            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;
    },
};
 
function callQueue() {
    if(callsCounter < 6 - urlsPerCall) {
        for(let i = 0; i < urlsPerCall && i < urlsQueue.length; i++) {
            let url = urlsQueue.splice(0, 1)[0];
            callsCounter++;
            Shelly.call("HTTP.GET", {
                url: url,
                timeout: 5
                },
                function(_, error_code, _, data) {
                    if(error_code !== 0) {
                        console.log("Calling", data.url, "failed");
                    }
                    else {
                        console.log("Calling", data.url, "successed");
                    }
 
                    callsCounter--;
                },
                { url: url }
            );
        }
    }
 
    //if there are more urls in the queue
    if(urlsQueue.length > 0) {
        Timer.set(
            1000, //the delay
            false,
            function() {
                callQueue();
            }
        );
    }
}
 
let lastPacketId = 0x100;
function bleScanCallback(event, result) {
  //exit if the call is not for a received result
  if (event !== BLE.Scanner.SCAN_RESULT) {
      return;
  }
 
  //console.log("Allowed Macs: ", JSON.stringify(ALLOWEDMACS));
  let allowedMacIdx = 0;
  for (allowedMacIdx in ALLOWEDMACS) {
  {
    //console.log("Allowed Mac index: ", allowedMacIdx);
    //console.log("Allowed Mac: ", ALLOWEDMACS[allowedMacIdx]);
           
    if(result.addr.indexOf(ALLOWEDMACS[allowedMacIdx])===0)
    {
      //console.log("Local name: ", result.local_name);
      //console.log("Mac: ", result.addr);    
      let deviceType = DEVICETYPES[allowedMacIdx];
      let deviceName = BLE_DESCR[allowedMacIdx];
     
      //console.log("Shelly BTH res before: ", JSON.stringify(result));    
 
      let servData = result.service_data;
     
      //exit if service data is null/device is encrypted
      if(servData === null || typeof servData === "undefined" || typeof servData[BTHOME_SVC_ID_STR] === "undefined") {
          console.log("Can't handle encrypted devices");
          return;
      }
 
      let receivedData = BTHomeDecoder.unpack(servData[BTHOME_SVC_ID_STR]);
      receivedData.addr = result.addr;
      receivedData.rssi = result.rssi;
     
      //console.log("Shelly BTH res unpacked: ", JSON.stringify(receivedData));
     
      //exit if unpacked data is null or the device is encrypted
      if(receivedData === null || typeof receivedData === "undefined" || receivedData["encryption"]) {
          console.log("Can't handle encrypted devices");
          return;
      }
 
      //exit if the event is duplicated
      if (lastPacketId === receivedData.pid) {
          return;
      }
 
      lastPacketId = receivedData["pid"];
     
      if (typeof  LastBeaconUnixTimes[receivedData.addr] === "undefined")
      {
        console.log("Initializing Last beacon time");
        LastBeaconUnixTimes[receivedData.addr]=-12001;
      }
 
      let unixtime = Shelly.getComponentStatus("sys").unixtime;        
                     
      if(deviceType==="SBBT")
      {
          if(receivedData.Button === 0)
          {
              console.log("Button Check to send beacon MQTT, " + deviceName);
      
              let timeDiff = unixtime - LastBeaconUnixTimes[receivedData.addr];
      
              //console.log(unixtime);
              //console.log(LastBeaconUnixTimes[receivedData.addr]);
              //console.log(timeDiff );
                      
              if(timeDiff >= 6000)
              {
                console.log("Button Sending beacon MQTT, " + deviceName);
               LastBeaconUnixTimes[receivedData.addr] = unixtime;
                let buttonData = {
                  type_name: "SBBT-002C",
                  type: result.local_name,
                  data: receivedData
                };
                                                          
                MQTT.publish(
                  CONFIG.mqtt_topic + "/blubuttonstatus",
                  JSON.stringify(buttonData)
                );              
              }
          }
          else
          {
            LastBeaconUnixTimes[receivedData.addr] = unixtime;
      
            console.log("Button Publishing to MQTT, " + deviceName);
            
            let buttonData = {
              type_name: "SBBT-002C",
              type: result.local_name,
              data: receivedData
            };
                                                      
            MQTT.publish(
              CONFIG.mqtt_topic + "/blubuttonstatus",
              JSON.stringify(buttonData)
            );      
          }  
      }
      else if(deviceType==="SBDW")
      {
        if (typeof  LastWindowStates[receivedData.addr] === "undefined")
        {
          console.log("Initializing Last window state, " + deviceName);
              LastWindowStates[receivedData.addr]=255;
        }
   
        //console.log("Window state", receivedData.Window);
        //console.log("Last Window state", LastWindowStates[receivedData.addr]);
                 
      if(receivedData.Window === LastWindowStates[receivedData.addr])
      {
        console.log("Dw Check to send beacon MQTT, " + deviceName);
        let timeDiff = unixtime - LastBeaconUnixTimes[receivedData.addr];
      
        //console.log(unixtime);
        //console.log(LastBeaconUnixTimes[receivedData.addr]);
        //console.log(timeDiff );
                      
        if(timeDiff >= 6000)
        {
          console.log("Dw Sending beacon MQTT, " + deviceName);
          LastBeaconUnixTimes[receivedData.addr] = unixtime;
          let windowData = {
            type_name: "SBDW-002C",
            type: result.local_name,
            data: receivedData
          };
                                                          
          MQTT.publish(
            CONFIG.mqtt_topic + "/bludwstatus",
            JSON.stringify(windowData)
          );              
        }
      }
      else
      {
        LastBeaconUnixTimes[receivedData.addr] = unixtime;
        LastWindowStates[receivedData.addr] = receivedData.Window;
      
        console.log("Dw Publishing to MQTT, " + deviceName);
                      
        let dwData = {
          type_name: "SBDW-002C",
          data: receivedData
        };
              
        MQTT.publish(
          CONFIG.mqtt_topic + "/bludwstatus",
          JSON.stringify(dwData)
        );  
      }
       }  
      else if(deviceType ==="SBHT")
      {
            console.log("HT Publishing to MQTT, " + deviceName);
            
            let deviceData = {
              type_name: "SBHT-003C",
              type: result.local_name,
              data: receivedData
            };
                                                      
            MQTT.publish(
              CONFIG.mqtt_topic + "/bluhtstatus",
              JSON.stringify(deviceData)
            );      
      }      
      else if(deviceType==="SBMO")
      {
        if (typeof  LastMotionStates[receivedData.addr] === "undefined")
        {
          console.log("Initializing Last motion state, " + deviceName);
              LastMotionStates[receivedData.addr]=255;
        }
   
        //console.log("Motion state", receivedData.motion);
        //console.log("Last Motion state", LastMotionStates[receivedData.addr]);
                 
      if(receivedData.motion === LastMotionStates[receivedData.addr])
      {
        console.log("Motion Check to send beacon MQTT, " + deviceName);
        let timeDiff = unixtime - LastBeaconUnixTimes[receivedData.addr];
      
        //console.log(unixtime);
        //console.log(LastBeaconUnixTimes[receivedData.addr]);
        //console.log(timeDiff );
                      
        if(timeDiff >= 6000)
        {
          console.log("Motion Sending beacon MQTT, " + deviceName);
          LastBeaconUnixTimes[receivedData.addr] = unixtime;
          let motionData = {
            type_name: "SBMO-003Z",
            type: result.local_name,
            data: receivedData
          };
                                                          
          MQTT.publish(
            CONFIG.mqtt_topic + "/blumotionstatus",
            JSON.stringify(motionData)
          );              
        }
      }
      else
      {
        LastBeaconUnixTimes[receivedData.addr] = unixtime;
        LastMotionStates[receivedData.addr] = receivedData.motion;
      
        console.log("Motion Publishing to MQTT, " + deviceName);
                      
        let motionData = {
          type_name: "SBMO-003Z",
          data: receivedData
        };
              
        MQTT.publish(
          CONFIG.mqtt_topic + "/blumotionstatus",
          JSON.stringify(motionData)
        );  
      }
       }        
      }
     
      /*
      //getting and execuing the action
      let actionType = ["", "singlePush", "doublePush", "triplePush", "longPush"][receivedData["Button"]];
 
      let actionUrls = CONFIG.actions[actionType];
 
      //exit if the event doesn't exist in the config    
      if(typeof actionType === "undefined") {
          console.log("Unknown event type in the config");
          return;
      }
 
      //save all urls into the queue for the current event
      for(let i in actionUrls) {
          urlsQueue.push(actionUrls[i]);
      }
 
      callQueue();
      */
    }
  }
}
 
function bleScan() {
    //check whether the bluethooth is enabled
    let bleConfig = Shelly.getComponentConfig("ble");
 
    //exit if the bluetooth is not enabled
    if(bleConfig.enable === false) {
        console.log("BLE is not enabled");
        return;
    }
 
    //start the scanner
    let bleScanner = BLE.Scanner.Start({
        duration_ms: BLE.Scanner.INFINITE_SCAN,
        active: true
    });
 
    //exist if the scanner can not be started
    if(bleScanner === false) {
        console.log("Error when starting the BLE scanner");
        return;
    }
 
    BLE.Scanner.Subscribe(bleScanCallback);
    console.log("BLE is successfully started");
}
 
function init() {
    //exit if there isn't a config
    if(typeof CONFIG === "undefined") {
        console.log("Can't read the config");
        return;
    }
 
  /*
    //exit if there isn't a blu button address
    if(typeof CONFIG.bluButtonAddress !== "string") {
        console.log("Error with the Shelly BLU button1's address");
        return;
    }
    */
    //exit if there isn't action object
    //if(typeof CONFIG.actions === "undefined") {
    //    console.log("Can't find the actions object in the config");
    //    return;
    //}
 
    //start the ble scan
    bleScan();
}
 
//init the script
init();
Edited by Mhahlin
Added some comments
Link to comment
Share on other sites

Quote
getUInt16LE: function (buffer) {
        return 0xffff & ((buffer.at(1) << 😎 | 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) << 😎 | buffer.at(0))

what do these smilys in the script stand for?

Edited by kkoeniger
Link to comment
Share on other sites

  • 4 weeks later...

Good morning,

I have the same problem. Did you find a solution?

For my part I use a Shelly Blu Gateway key to run this script and retrieve data from a Shelly BLU H&T temperature sensor, and then recover them in mqtt.

The mqtt works because I get a message on my broker saying that the script has crashed.

The script starts well...it monitors the appearance of the Blu material.

It is when you press the temperature sensor button that the script crash with same error, so there is communication between the gateway and the temperature sensor.
I can't find the error in the script, but I'm not an expert either.

Thank

Edited by Athis
Link to comment
Share on other sites

I was able to reproduce the error. Invalid characters creep in when you copy the script from the forum. @kkoeniger @Athis

Hopefully this is an executable version:

/**
 * This script reads selected Blu devices using AllowedMacs to publish received data to MQTT
 */
 
//Shelly device types ["SBBT", "SBDW","SBDW","SBMO","SBHT"];
let BLE_DESCR = ["Ytterdörr hall", "Dörr veranda"]; // declare descriptive name for each device. Just for easier mapping of mac adresses
let ALLOWEDMACS = ["00:00:00:00:00:00","00:00:00:00:00:00"]; // declare device macs
let DEVICETYPES = ["SBDW","SBBT"]; // need to declare device type
let LastBeaconUnixTimes = {};
let LastWindowStates = {};
 
/** =============================== CHANGE HERE =============================== */
let CONFIG = {
    //bluButtonAddress: "00:00:00:00:00:00", //the mac address of shelly blu button1 that will trigger the actions
    mqtt_topic: Shelly.getComponentConfig("mqtt").topic_prefix/*, // Get MQTT topic from configuration
    actions: { //urls to be called on a event
        //when adding urls you must separate them with commas and put them in quotation marks
        singlePush: [ //urls that will be executed at singlePush event from the blu button1
            //"http://192.168.1.35/roller/0?go=open",
            //"http://192.168.1.36/relay/0?turn=off",
            //"http://192.168.1.36/relay/1?turn=on"
        ],
        doublePush: [ //urls that will be execubuttonData)
            );              
          }
      }
      else
      {
        LastBeaconUnixTimes[result.addr] = Shelly.getComponentStatus("sys").unixtime;
  
        console.log("Button Publishing to MQTT");
        
        let buttonData = {
          type_name: "SBBT-002C",
          type: result.local_name,
          data: receivedData
        };
                                                  
        MQTT.publish(
          CONFIG.mqtt_topic + "/blubuttonstatus",
          JSON.stringify(buttonData)
        );      
      }
      }
      else if(deviceType.indexOf("SBDW"))
      {
        if (typeof  LastWindowStates[result.addr] === "undefined")
        {
          console.log("Initializing Last window state");
        LastWindowStates[result.addr]=255;
        }
   
        console.log("Window state", receivedData.Window);
        console.log("Last Window state", LastWindowStates[result.addr]);
                 
      if(receivedData.Window === LastWindowStates[rted at doublePush event from the blu button1
            //"http://192.168.1.35/roller/0?go=close"
        ],
        triplePush: [ //urls that will be executed at triplePush event from the blu button1
            //"http://192.168.1.38/color/0?turn=on&red=200&green=0&blue=0",
            //"http://192.168.1.38/light/0?turn=on",
            //"http://192.168.1.40/rpc/Switch.Set?id=0&on=false",
            //"http://192.168.1.40/rpc/Switch.Set?id=1&on=false"
        ],
        longPush: [ //urls that will be executed at longPush event from the blu button1
            //"http://192.168.1.41/rpc/Cover.Close",
            //"http://192.168.1.42/rpc/Cover.Close"
        ]
    }
    */
};
/** =============================== STOP CHANGING HERE =============================== */
 
let urlsPerCall = 3;
let urlsQueue = [];
let callsCounter = 0;
 
let ALLTERCO_MFD_ID_STR = "0ba9";
let BTHOME_SVC_ID_STR = "fcd2";
 
let uint8 = 0;
let int8 = 1;
let uint16 = 2;
let int16 = 3;
let uint24 = 4;
let int24 = 5;
 
let BTH = {};
BTH[0x00] = { n: "pid", t: uint8 };
BTH[0x01] = { n: "Battery", t: uint8, u: "%" };
BTH[0x02] = { n: "Temperature", t: int16, f: 0.01, u: "tC" };
BTH[0x03] = { n: "Humidity", t: uint16, f: 0.01, u: "%" };
BTH[0x05] = { n: "Illuminance", t: uint24, f: 0.01 };
BTH[0x21] = { n: "motion", t: uint8 };
BTH[0x1a] = { n: "Door", t: uint8 };
BTH[0x20] = { n: "Moisture", t: uint8 };
BTH[0x2d] = { n: "Window", t: uint8 };
BTH[0x2e] = { n: "Humidity", t: uint8, u: "%" };
BTH[0x3a] = { n: "Button", t: uint8 };
BTH[0x45] = { n: "Temperature", t: int16, f: 0.1, u: "tC" }
 
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;
}
 
let 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;
    },
    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);
            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;
    },
};
 
function callQueue() {
    if(callsCounter < 6 - urlsPerCall) {
        for(let i = 0; i < urlsPerCall && i < urlsQueue.length; i++) {
            let url = urlsQueue.splice(0, 1)[0];
            callsCounter++;
            Shelly.call("HTTP.GET", {
                url: url,
                timeout: 5
                },
                function(_, error_code, _, data) {
                    if(error_code !== 0) {
                        console.log("Calling", data.url, "failed");
                    }
                    else {
                        console.log("Calling", data.url, "successed");
                    }
 
                    callsCounter--;
                },
                { url: url }
            );
        }
    }
 
    //if there are more urls in the queue
    if(urlsQueue.length > 0) {
        Timer.set(
            1000, //the delay
            false,
            function() {
                callQueue();
            }
        );
    }
}
 
let lastPacketId = 0x100;
function bleScanCallback(event, result) {
  //exit if the call is not for a received result
  if (event !== BLE.Scanner.SCAN_RESULT) {
      return;
  }
 
  //console.log("Allowed Macs: ", JSON.stringify(ALLOWEDMACS));
  let allowedMacIdx = 0;
  for (allowedMacIdx in ALLOWEDMACS) {
  {
    //console.log("Allowed Mac index: ", allowedMacIdx);
    //console.log("Allowed Mac: ", ALLOWEDMACS[allowedMacIdx]);
           
    if(result.addr.indexOf(ALLOWEDMACS[allowedMacIdx])===0)
    {
      //console.log("Local name: ", result.local_name);
      //console.log("Mac: ", result.addr);    
      let deviceType = DEVICETYPES[allowedMacIdx];
      let deviceName = BLE_DESCR[allowedMacIdx];
     
      //console.log("Shelly BTH res before: ", JSON.stringify(result));    
 
      let servData = result.service_data;
     
      //exit if service data is null/device is encrypted
      if(servData === null || typeof servData === "undefined" || typeof servData[BTHOME_SVC_ID_STR] === "undefined") {
          console.log("Can't handle encrypted devices");
          return;
      }
 
      let receivedData = BTHomeDecoder.unpack(servData[BTHOME_SVC_ID_STR]);
      receivedData.addr = result.addr;
      receivedData.rssi = result.rssi;
     
      //console.log("Shelly BTH res unpacked: ", JSON.stringify(receivedData));
     
      //exit if unpacked data is null or the device is encrypted
      if(receivedData === null || typeof receivedData === "undefined" || receivedData["encryption"]) {
          console.log("Can't handle encrypted devices");
          return;
      }
 
      //exit if the event is duplicated
      if (lastPacketId === receivedData.pid) {
          return;
      }
 
      lastPacketId = receivedData["pid"];
     
      if (typeof  LastBeaconUnixTimes[receivedData.addr] === "undefined")
      {
        console.log("Initializing Last beacon time");
        LastBeaconUnixTimes[receivedData.addr]=-12001;
      }
 
      let unixtime = Shelly.getComponentStatus("sys").unixtime;        
                     
      if(deviceType==="SBBT")
      {
      if(receivedData.Button === 0)
      {
          console.log("Button Check to send beacon MQTT, " + deviceName);
  
          let timeDiff = unixtime - LastBeaconUnixTimes[receivedData.addr];
  
          //console.log(unixtime);
          //console.log(LastBeaconUnixTimes[receivedData.addr]);
          //console.log(timeDiff );
                  
          if(timeDiff >= 6000)
          {
            console.log("Button Sending beacon MQTT, " + deviceName);
           LastBeaconUnixTimes[receivedData.addr] = unixtime;
            let buttonData = {
              type_name: "SBBT-002C",
              type: result.local_name,
              data: receivedData
            };
                                                      
            MQTT.publish(
              CONFIG.mqtt_topic + "/blubuttonstatus",
              JSON.stringify(buttonData)
            );              
          }
      }
      else
      {
        LastBeaconUnixTimes[receivedData.addr] = unixtime;
  
        console.log("Button Publishing to MQTT, " + deviceName);
        
        let buttonData = {
          type_name: "SBBT-002C",
          type: result.local_name,
          data: receivedData
        };
                                                  
        MQTT.publish(
          CONFIG.mqtt_topic + "/blubuttonstatus",
          JSON.stringify(buttonData)
        );      
      }  
      }
      else if(deviceType==="SBDW")
      {
        if (typeof  LastWindowStates[receivedData.addr] === "undefined")
        {
          console.log("Initializing Last window state, " + deviceName);
        LastWindowStates[receivedData.addr]=255;
        }
   
        //console.log("Window state", receivedData.Window);
        //console.log("Last Window state", LastWindowStates[receivedData.addr]);
                 
    if(receivedData.Window === LastWindowStates[receivedData.addr])
    {
      console.log("Dw Check to send beacon MQTT, " + deviceName);
      let timeDiff = unixtime - LastBeaconUnixTimes[receivedData.addr];
    
      //console.log(unixtime);
      //console.log(LastBeaconUnixTimes[receivedData.addr]);
      //console.log(timeDiff );
                    
      if(timeDiff >= 6000)
      {
        console.log("Dw Sending beacon MQTT, " + deviceName);
        LastBeaconUnixTimes[receivedData.addr] = unixtime;
        let windowData = {
          type_name: "SBDW-002C",
          type: result.local_name,
          data: receivedData
        };
                                                        
        MQTT.publish(
          CONFIG.mqtt_topic + "/bludwstatus",
          JSON.stringify(windowData)
        );              
      }
    }
    else
    {
      LastBeaconUnixTimes[receivedData.addr] = unixtime;
      LastWindowStates[receivedData.addr] = receivedData.Window;
    
      console.log("Dw Publishing to MQTT, " + deviceName);
                    
      let dwData = {
        type_name: "SBDW-002C",
        data: receivedData
      };
            
      MQTT.publish(
        CONFIG.mqtt_topic + "/bludwstatus",
        JSON.stringify(dwData)
      );  
    }
       }  
      else if(deviceType ==="SBHT")
      {
         console.log("HT Publishing to MQTT, " + deviceName);
        
        let deviceData = {
          type_name: "SBHT-003C",
          type: result.local_name,
          data: receivedData
        };
                                                  
        MQTT.publish(
          CONFIG.mqtt_topic + "/bluhtstatus",
          JSON.stringify(deviceData)
        );      
      }      
      else if(deviceType==="SBMO")
      {
        if (typeof  LastMotionStates[receivedData.addr] === "undefined")
        {
          console.log("Initializing Last motion state, " + deviceName);
        LastMotionStates[receivedData.addr]=255;
        }
   
        //console.log("Motion state", receivedData.motion);
        //console.log("Last Motion state", LastMotionStates[receivedData.addr]);
                 
    if(receivedData.motion === LastMotionStates[receivedData.addr])
    {
      console.log("Motion Check to send beacon MQTT, " + deviceName);
      let timeDiff = unixtime - LastBeaconUnixTimes[receivedData.addr];
    
      //console.log(unixtime);
      //console.log(LastBeaconUnixTimes[receivedData.addr]);
      //console.log(timeDiff );
                    
      if(timeDiff >= 6000)
      {
        console.log("Motion Sending beacon MQTT, " + deviceName);
        LastBeaconUnixTimes[receivedData.addr] = unixtime;
        let motionData = {
          type_name: "SBMO-003Z",
          type: result.local_name,
          data: receivedData
        };
                                                        
        MQTT.publish(
          CONFIG.mqtt_topic + "/blumotionstatus",
          JSON.stringify(motionData)
        );              
      }
    }
    else
    {
      LastBeaconUnixTimes[receivedData.addr] = unixtime;
      LastMotionStates[receivedData.addr] = receivedData.motion;
    
      console.log("Motion Publishing to MQTT, " + deviceName);
                    
      let motionData = {
        type_name: "SBMO-003Z",
        data: receivedData
      };
            
      MQTT.publish(
        CONFIG.mqtt_topic + "/blumotionstatus",
        JSON.stringify(motionData)
      );  
    }
       }        
      }
     
      /*
      //getting and execuing the action
      let actionType = ["", "singlePush", "doublePush", "triplePush", "longPush"][receivedData["Button"]];
 
      let actionUrls = CONFIG.actions[actionType];
 
      //exit if the event doesn't exist in the config    
      if(typeof actionType === "undefined") {
          console.log("Unknown event type in the config");
          return;
      }
 
      //save all urls into the queue for the current event
      for(let i in actionUrls) {
          urlsQueue.push(actionUrls[i]);
      }
 
      callQueue();
      */
    }
  }
}
 
function bleScan() {
    //check whether the bluethooth is enabled
    let bleConfig = Shelly.getComponentConfig("ble");
 
    //exit if the bluetooth is not enabled
    if(bleConfig.enable === false) {
        console.log("BLE is not enabled");
        return;
    }
 
    //start the scanner
    let bleScanner = BLE.Scanner.Start({
        duration_ms: BLE.Scanner.INFINITE_SCAN,
        active: true
    });
 
    //exist if the scanner can not be started
    if(bleScanner === false) {
        console.log("Error when starting the BLE scanner");
        return;
    }
 
    BLE.Scanner.Subscribe(bleScanCallback);
    console.log("BLE is successfully started");
}
 
function init() {
    //exit if there isn't a config
    if(typeof CONFIG === "undefined") {
        console.log("Can't read the config");
        return;
    }
 
  /*
    //exit if there isn't a blu button address
    if(typeof CONFIG.bluButtonAddress !== "string") {
        console.log("Error with the Shelly BLU button1's address");
        return;
    }
    */
    //exit if there isn't action object
    //if(typeof CONFIG.actions === "undefined") {
    //    console.log("Can't find the actions object in the config");
    //    return;
    //}
 
    //start the ble scan
    bleScan();
}
 
//init the script
init();

 

Link to comment
Share on other sites

THANK YOU very much
I have my feedback via mqtt

Quote

{"type_name":"SBHT-003C","data":{"encryption":false,"BTHome_version":2,"pid":224,"Battery":100,"Humidity":52,"Button":1,"Temperature":24,"addr":"7c:c6:b6:61:ea:a0","rssi":-57}}

I didn't expect such a quick response, thank you again.

Capture d’écran du 2024-08-21 21-27-24.png

Link to comment
Share on other sites

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.

×
×
  • Erstelle neue...