[Modul] Shelly

Kannst du mal ein Debug von der Instanz posten?

Grüße,
Kai

Von welcher?
Bei dem eine Shelly kommt nichts und bei den anderen nur manchmal.

Gruß Burkhard

Hier mal vom Shelly der manchmal geht.

dump.txt (4,1 KB)

Hier von dem den nichts schickt.
Nach einem Geräteneustart schickt er das:

dump(1).txt (1,1 KB)

Danach ist Sendepause

Ach ja, Firmware ist bei beiden 0.14.1-g22a4cb7.

Hallo Kai,

ich habe den neuen ShellyBlue Door/Window am Bluetooth des Shelly Plus 1PM über die Shelly App angemeldet und möchte den Fensterkontakt auch in IPS auswerten. Im Debug ist vom Fensterkontakt nichts zu sehen.
Hast Du eine Idee ?

viele Grüße

Soweit ich das gelesen habe senden die ShellyBlue nur etwas an den Shelly Plus/Pro. Um dann etwas zu MQTT zu bekommen muss auf dem Plus/Pro entsprechende Skripte erstellt werden die das dann weiterreichen. Sprich out-of-the-box geht das nicht und es ist nicht standardisiert. Somit wäre es für Kai sehr Schwer da etwas zu integrieren.
Gruß
Dennis

Da gebe ich meinem Namensvetter recht, Du musst auf einem Deiner Shelly ein Script dauerhaft laufen lassen.

Ich habe das mal mit dem folgenden Script testweise implementiert (da sind bestimmt noch Fehler dabei), sodass mein BLU Door/Window über einen Plus i4 die Daten an den MQTT Server von Symcon überträgt.

/* ============================= Config CHANGE HERE =============================== */
let mqttID = "shellyBLU"; //MQTT Topic ID, (behind topicPrefix)
let sendMQTT = true; //send Button Data also per MQTT, true/false 
let topicPrefix = ""; //Here you can set your own mqtt Topic Prefix
let vSwitch = false; // create MQTT virtual Switch states, true/false
let debug = false; //show debug msg in log, true/false, warning setting this to 'true' will delay reaktion speed a lot
/* ============================== STOP CHANGING HERE =============================== */

//========= Skript Setup =========
let activeScan = true; //active or passiv Bluetooth Scan, true/false
let _cid = "0ba9"; //Allterco, Company ID(MFD)
let uuid = "fcd2"; //BTHome, Service ID
let devID1 = "SBBT"; // BluButton1, deviceID, --> SBBT-002C evt. 002C = charge?
let devID2 = "SBDW"; // Blu Door/Window, deviceID
let bthObjectIDs = []; //Used BTHome Service.Payload ObjectID List for BluButton1,(here you can find a full List --> " https://bthome.io/format ")
bthObjectIDs[0x00] = { key: 'pid', dataType: 'uint8' };
bthObjectIDs[0x01] = { key: 'battery', dataType: 'uint8', unit: '%' };
bthObjectIDs[0x3a] = { key: 'buttonID', dataType: 'uint8' };
bthObjectIDs[0x05] = { key: 'illuminance', dataType: 'uint24', factor: 0.01 };
bthObjectIDs[0x1a] = { key: 'doorStateID', dataType: 'uint8' };
bthObjectIDs[0x20] = { key: 'moistureLvl', dataType: 'uint8' };
bthObjectIDs[0x2d] = { key: 'windowStateID', dataType: 'uint8' };
bthObjectIDs[0x3f] = { key: 'rotationLvl', dataType: 'int16', factor: 0.1 };
let vSwitchCache = {}, last_packet_id = 0x100, last_addr = 0, scanDuration = BLE.Scanner.INFINITE_SCAN;
//=========== End Setup ===========
function SendMQTTmsg(mac,event){
    if(MQTT.isConnected()){
        let mainTopic = mqttID +'-'+ mac + '/', inputID = JSON.stringify(event.info.data.inputID);
        if(topicPrefix.length !== 0) mainTopic = topicPrefix +'/'+ mqttID +'-'+ mac + '/';
        if(inputID !== '0' && inputID !== '254' && vSwitch){ // do not trigger virtual switch logik for beacon or hold events.
            if(vSwitchCache['s'+inputID] === undefined || vSwitchCache['s'+inputID] === 'false'){
                vSwitchCache['s'+inputID] = 'true';
            }else{
                vSwitchCache['s'+inputID] = 'false';
            }
        }
        if(debug) print('MQTT Publishing: _Topic: ', mainTopic, 'events/rpc _Payload: ', JSON.stringify(event));
        MQTT.publish(mainTopic + 'events/rpc', JSON.stringify(event), 1, false);
        MQTT.publish(mainTopic + 'status/input:' + inputID, JSON.stringify(event.info.ts), 1, false);
        if(inputID !== '0' && inputID !== '254' && vSwitch) MQTT.publish(mainTopic + 'status/switch:' + inputID, vSwitchCache['s'+inputID], 1, false);
        MQTT.publish(mainTopic + 'info/battery', JSON.stringify(event.info.data.battery_value), 1, true);
        MQTT.publish(mainTopic + 'info/rssi', JSON.stringify(event.info.data.rssi), 1, true);
        MQTT.publish(mainTopic + 'info/lastTimeStamp', JSON.stringify(event.info.ts), 1, true);
        MQTT.publish(mainTopic + 'info/lastAktion', event.info.event, 1, true);
        MQTT.publish(mainTopic + 'info/lastAktionID', inputID, 1, true);
        MQTT.publish(mainTopic + 'info/mac', event.info.data.mac, 1, true);
        MQTT.publish(mainTopic + 'info/device', event.info.data.device, 1, true);
        MQTT.publish(mainTopic + 'info/encryption', JSON.stringify(event.info.data.encryption), 1, true);
        MQTT.publish(mainTopic + 'info/gateway', event.info.data.gateway, 1, true);
        MQTT.publish(mainTopic + 'info/windowStateID', JSON.stringify(event.info.data.windowStateID), 1, true); 
        
    }else{
        print('Error: MQTT is still not ready, cant send msg');
    }
}
function FilterEvents(obj){
    if(obj.info === undefined || obj.info.data === undefined || obj.info.data.generation !== 'BLU') return;
    SendMQTTmsg(obj.info.data.mac,obj);
}
let BTHomeDecoder = {
    UInt_To_Int: function (uint, bitsz) {
        let mask = 1 << (bitsz - 1); //create mask to filter last bit
        if (uint & mask) {
            return uint - (1 << bitsz); //convert uint to negative int
        } else {
            return uint; //uint is positive, no change needed.
        }
    },
    GetMaxBytes: function (dataType) {
        if (dataType === 'uint8' || dataType === 'int8') return 1;
        if (dataType === 'uint16' || dataType === 'int16') return 2;
        if (dataType === 'uint24' || dataType === 'int24') return 3;
        return 255;
    },
    GetValue: function (dataType, buffer) {
        let maxBytes = this.GetMaxBytes(dataType);
        if (buffer.length < maxBytes) {
            print('Error: ValueBuffer has, ', buffer.length, ' Bytes with DataType, ', dataType);
            return null;
        }
        let data = null;
        let _1stByte = buffer.at(0); //get 1.Byte of Buffer
        let _2ndByte = null;
        if (maxBytes > 1) _2ndByte = buffer.at(1); //get 2.Byte of Buffer
        let _3rdByte = null;
        if (maxBytes > 2) _3rdByte = buffer.at(2); //get 3.Byte of Buffer
        if (dataType === 'uint8') data = _1stByte;
        if (dataType === 'int8') data = this.UInt_To_Int(_1stByte, 8);
        if (dataType === 'uint16') data = 0xffff & ((_2ndByte << 8) | _1stByte);
        if (dataType === 'int16') data = this.UInt_To_Int(0xffff & ((_2ndByte << 8) | _1stByte), 16);
        if (dataType === 'uint24') data = 0x00ffffff & ((_3rdByte << 16) | (_2ndByte << 8) | _1stByte);
        if (dataType === 'int24') data = this.UInt_To_Int(0x00ffffff & ((_3rdByte << 16) | (_2ndByte << 8) | _1stByte), 24);
        return data;
    },
    Unpack: function (payload) {
        if (typeof payload !== "string" || payload.length === 0) return null;
        let btHomeObj = {}, byte = payload.at(0), value, objectID; //byte = payload.at(0) --> get 1.Byte of Payload
        if (byte & 0x1) {
            btHomeObj['encryption'] = true; //analyze 1. Bit and set btHomeObj encryption boolean
        } else {
            btHomeObj['encryption'] = false;
        }
        btHomeObj['version'] = byte >> 5; //cut,move to 6.Bit, save the rest as version   
        if (btHomeObj['version'] !== 2) return null; //analyse the rest Bits 6,7,8 inside version, exit if BTHome Version is not 2
        if (btHomeObj['encryption']) return btHomeObj; //exit if payload is encrypted
        payload = payload.slice(1); //remove used 1.Byte from payload
        while (payload.length > 0) {
            byte = payload.at(0); //get 1.Byte of Payload
            objectID = bthObjectIDs[byte]; //compare 1.Byte with known BTHome objectIds list, add parameter if known object
            if (typeof objectID === 'undefined') {
                print('Error: Unknown BThome Object, decimal_ID: ', byte, ' convert to hex and compare hex_ID "0x.." with full objID list --> https://bthome.io/format');
                break; //exit loop, if objectID is unknown
            }
            payload = payload.slice(1); //remove used 1.Byte from payload
            value = this.GetValue(objectID.dataType, payload);
            if (value === null) break; //exit loof, if value null
            if (typeof objectID.factor !== 'undefined') value = (value * objectID.factor); //add factor
            btHomeObj[objectID.key] = value;
            if (typeof objectID.unit !== 'undefined') value = (JSON.stringify(value) + objectID.unit); //add unit
            if (typeof objectID.unit !== 'undefined') btHomeObj[(objectID.key+"String")] = value;
            payload = payload.slice(this.GetMaxBytes(objectID.dataType)); //remove used value Bytes from payload
        }
        return btHomeObj;
    },
};
function ScanCB(status, response) {
    if (status !== BLE.Scanner.SCAN_RESULT) return; //exit if Scan status is stopped or unknown
    if (response.service_data === undefined || response.service_data[uuid] === undefined) return; //Filter only BThome Responses
    let BTHomeObj = BTHomeDecoder.Unpack(response.service_data[uuid]); //decode Sevice Data
    if (BTHomeObj === null) {print('Error: Failed to Unpack service_data of, ', response.addr); return;}
    if (last_packet_id === BTHomeObj.pid && last_addr === response.addr) return; //exit if packet is already known.
    if (debug) print('Received packet, raw Data:', JSON.stringify(response));
    if (response.local_name !== undefined) BTHomeObj.device = 'Unknown-Device--> ' + response.local_name;
    if (response.local_name !== undefined && response.local_name.indexOf(devID1) >= 0) BTHomeObj.device = 'Blu Button1'; //search for Blu Button1
    if (response.local_name !== undefined && response.local_name.indexOf(devID2) >= 0) BTHomeObj.device = 'Blu Door/Window'; //search for Blu Door/Window
    if (response.local_name === undefined) BTHomeObj.device = 'Hidden-Device';
    last_packet_id = BTHomeObj.pid, last_addr = response.addr, BTHomeObj.addr = response.addr, BTHomeObj.rssi = response.rssi, BTHomeObj.gateway = Shelly.getDeviceInfo().id;
    if (debug) print('Received BTHome packet: ', JSON.stringify(BTHomeObj));
    if (typeof BTHomeObj.buttonID === 'number' && BTHomeObj.illuminance === undefined) { //somehow filter for blu Button
        let buttonEventMap = ['wake_up','single_push','double_push','triple_push','long_push','pairing_push','default_reset_push'], buttonInput = BTHomeObj.buttonID;
        if(buttonInput > 6 && buttonInput !== 254)buttonInput = 'unknown_push';
        if(buttonInput < 7)buttonInput = buttonEventMap[buttonInput];
        if(buttonInput === 254)buttonInput = 'hold_push';
        Shelly.emitEvent(buttonInput, {
            generation: 'BLU',
            gateway: BTHomeObj.gateway,
            device: BTHomeObj.device,
            battery_value: BTHomeObj.battery,
            battery_string: BTHomeObj.batteryString,
            inputID: BTHomeObj.buttonID,
            mac: BTHomeObj.addr,
            rssi: BTHomeObj.rssi,
            pid: BTHomeObj.pid,
            encryption: BTHomeObj.encryption
        });
    }
    if (typeof BTHomeObj.illuminance === 'number'){ //somehow, filter for blu door/window
         Shelly.emitEvent('blu_DW_ChangedStatus', {
            generation: 'BLU',
            gateway: BTHomeObj.gateway,
            device: BTHomeObj.device,
            battery_value: BTHomeObj.battery,
            battery_string: BTHomeObj.batteryString,
            doorStateID: BTHomeObj.doorStateID,
            windowStateID: BTHomeObj.windowStateID,
            illuminance: BTHomeObj.illuminance,
            moistureLvl: BTHomeObj.moistureLvl,
            rotationLvl: BTHomeObj.rotationLvl,
            buttonID: BTHomeObj.buttonID,
            mac: BTHomeObj.addr,
            rssi: BTHomeObj.rssi,
            pid: BTHomeObj.pid,
            encryption: BTHomeObj.encryption
        });
    }
}
function Start_BLE_Scan() {
    let bleScanStarted = BLE.Scanner.Start({ duration_ms: scanDuration, active: activeScan }, ScanCB);
    if (bleScanStarted === false) {
        print('Error: BTHome Scanner could not be started, will try again in 5sek.');
        Timer.set(5000, false, Start_BLE_Scan);
    } else {
        let scanType = 'Passiv';
        if (activeScan) scanType = 'Active';
        print('Success: BTHome ', scanType, ' Scanner running in Background');
    }
}
//__Main__
Start_BLE_Scan();
if(sendMQTT) Shelly.addEventHandler(FilterEvents);

Ich könnte mir für das Modul vorstellen, dass Kai entweder vorgaben für die Struktur macht oder für diese Gerätekategorie die Konfiguration erweitert wird, sodass für jede Variable auch der MQTT-Topic bzw. zumindest der Suffix angegeben werden muss.

Beste Grüße
Dennis

Das sieht komisch aus, kannst du mal die MQTT Einstellungen vom Shelly als Screenshot posten?

Grüße,
Kai

1 „Gefällt mir“

Da ich selbst noch keine Bluetooth Geräte habe ist das sehr schwer nachzuvollziehen.
Ich finde die Idee mit einer vorgegebenen Struktur aber gar nicht so verkehrt.
Das Script habe ich gerade überflogen, du sendest dort also direkt per MQTT die Daten weiter?

„mainTopic + 'info/windowStateID“ ist also das Topic für den Status…
„mainTopic + ‚info/device‘“ hier sieht man also von welchem BLE Gerät das Payload kommt, das wiederum ist eher schlecht, denn wenn das BLE Gerät dann mal mit einem anderen Shelly verbunden ist, dann kommt der Status nicht mehr an der richtigen Stelle in Symcon an.

Also am geschicktesten wäre es, wenn das Topic irgendwie so in der Art aufgebaut wäre:

shelly/ble/%device%/%mac%/battery
shelly/ble/%device%/%mac%/rssi
shelly/ble/%device%/%mac%/lastAktion
shelly/ble/%device%/%mac%/lastAktionID
shelly/ble/%device%/%mac%/windowStateID

%deviced% und %max% sind Platzhalter für den tatsächlichen Namen bzw. die tatsächliche Mac Adresse der BLE Geräte.
Das ist nur eine Idee, ich habe mir die Doku noch nicht angeschaut.
Aber so könnte man dann für die BLE Geräte eine eigene Instanz erstellen, die dann immer die richtigen Daten auswertet, egal an welchem Shelly die BLE Geräte hängen?
Wäre das eine Idee?

Grüße,
Kai

Hier die Konfig:

Aktiviere mal bitte „Generic staus update over MQTT“ und teste es dann nochmal.

Grüße,
Kai

Leider funktioniert das auch nicht.
Anscheinend bin ich der einzige mit dem Problem.
Kann es an meinen Shellys liegen?
Denkst du ein Rücksetzen auf Werkseinstellungen könnte helfen?
Gibt es eine Möglichkeit dass ich das „alte“ Modul wieder installieren könnte?

Alle Shellys ohne PLUS funktionieren.

Gruß Burkhard

Bitte schalte das mal ein und poste dann ein Debug, der Haken muss definitiv gesetzt sen.

Grüße,
Kai

Kai,

das ist mir jetzt besonders peinlich und tut mir sehr leid.

Ich habe jetzt das Password für MQTT in den Shellys noch einmal neu eingegeben und jetzt läuft alles wie immer.
Keine Ahnung warum die Passwörter weg waren.

Gruß Burkhard

Alles gut, dass passiert schon mal.
Kannst du dann mal schauen, ob nun alle Variablen wie gewünscht in Symcon gefüllt werden, oder ob es noch Probleme mit einzelnen Variablen gibt?

Grüße,
Kai

Sieht gut aus:

Sehr gut, dann beobachte das bitte mal. :slight_smile:

Grüße,
Kai

OK.
Wenn mir noch etwas auffällt prüfe ich vorher ob der Fehler nicht bei mir liegt.

Dankeschön für deine Mühe und Geduld.

Gruß Burkhard

Eine Frage habe ich noch.
Ich habe Firmware 0.14.1 auf den Shellys installiert.
Es gibt ein Update auf 1.0.3.
Würdest du die alte Version lassen oder die neue Installieren.

Mit der alten läuft alles und ich vermisse nichts.

Gruß Burkhard

Ich würde das Update machen.

Grüße,
Kai

1 „Gefällt mir“