schulz.vie Posted July 12 Posted July 12 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); Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
Mhahlin Posted July 25 Posted July 25 (edited) 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 July 25 by Mhahlin Added some comments Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
kkoeniger Posted July 29 Posted July 29 (edited) 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 July 29 by kkoeniger Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
tvbshelly Posted July 29 Posted July 29 10 minutes ago, kkoeniger said: what do these smilys in the script stand for? Unfortunately, the forum software converts some character combinations into emojis: 😎 -> "8 )" - you can omit the space in the actual script. Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
kkoeniger Posted July 29 Posted July 29 Thanks! I am totaly new to scripting. Added it to my BT-Gateway an I get an error when starting it: "Uncaught SyntaxError: Got ?[226] expected EOF 16:12:56 at ?[226]IDFLOAT?[226]IDFLOAT?[226]IDFLOAT?[226]IDFLOAT ], 16:12:56" Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
Athis Posted August 21 Posted August 21 (edited) 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 August 21 by Athis Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
tvbshelly Posted August 21 Posted August 21 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(); 1 Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
Athis Posted August 21 Posted August 21 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. 2 Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
kkoeniger Posted August 27 Posted August 27 Thanks a lot ! Working perfect now 😄 Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
herble Posted August 31 Posted August 31 Hallo habe einen Blu HT und er hat eine andere Hex ID Debug: sending------> info/new_BTH_HexID ---> 2e Debug: sending------> info/new_data ---> Ljk6AUUTAQ== Wäre toll wenn das im Script adabtiert wird. Danke Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
AlexAn Posted August 31 Posted August 31 Mit der Komponenteneinbindung per Pro oder Gen3 brauchst du kein Script mehr! Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.