Jump to content
NOTICE / HINWEIS: iOS 18 Update and Today Widgets ×

Blu Button and a Plus device without wifi


Recommended Posts

Hi to every one, It is 2 years I'm working on Shelly devices with big soddisfaction.
Now I've a doubt and before going on I want to be sure.
I need to control some lights in a outside place where there is no internet and NO WIFI. If i set up a blu button and a Shelly 2Plus PM to work togheter on the blutooth will they work without wifi. Can i still control the lights if i use the Blu Button around the Shelly 2Plus?

Link to comment
Share on other sites

  • 4 months later...

I use it in my car to disconnect the battery!

Perhsps you can use the script. I made some changes but can't remember who was the original creator but it works great (Bluetooth-Gateway has to be deactivated):

to pair a BLU-device you press the button after connecting power (time to pair is 10 sec):

// -----------------------------------------------------------------------
// Developed by YBC
//
// Date: 01/06/2022
// Version: 1.2
//
// Shelly Script: This script allows registering up to 45x BLU Button1 to use autonomous open door.  
// When you push the registered BLU Button1, the relay turns on and turns off in 1 second.
//
// DEVICES: 
// - Shelly PLUS 1 (to use bluetooth gateway and run the script),
// - Shelly BLU Button 1 (to send push)
//
// INSTRUCTIONS
// Register BLU Button1
// When you plug your Shelly Plus, you have 10 seconds to register and unregister.
//
// - Push one time BLU Button1 within 10s to register the button
// - Push twice BLU Button1 within 10s to unregister the button
//
// www.shellycanarias.com
// -----------------------------------------------------------------------

let BTHparsedd = null;
let local_name = null;
let buttons_cache = [];
let ALLTERCO_DEVICE_NAME_PREFIX = "SBBT";
let BTHOME_SVC_ID_STR = "fcd2";
let SCAN_DURATION = BLE.Scanner.INFINITE_SCAN;
let CONFIG_DURATION = 10000;
let CONFIG_MODE = false;

// ACTION FUNCTION
let btnAction = {
    buttonPress: function (_local_name, _BTHparsed) {
        local_name = _local_name;
        BTHparsedd = _BTHparsed;

        if (BTHparsedd.Button === 1) {
            // Single Push
            Shelly.call("Switch.toggle", { 'id': 0 });
        } else if (BTHparsedd.Button === 2) {
            // Double Push
            Shelly.call("Switch.set", { 'id': 0, 'on': true });
        } else if (BTHparsedd.Button === 3) {
            // Triple Push
            Shelly.call("Switch.set", { 'id': 0, 'on': false });
            //program.startBLEScan(); // Start Pairing Mode
        } else if (BTHparsedd.Button === 4) {
            // Long Push
            program.startBLEScan(); // Start Pairing Mode
            //Shelly.call("Shelly.Reboot", {}); // Reboot the device
            //Shelly.call("Switch.set", { 'id': 0, 'on': true, toggle_after: 0.25 });
        }
    }
};

let KVSbtn = {
    loadAllButtons: function () {
        Shelly.call("KVS.GetMany", {
            match: ALLTERCO_DEVICE_NAME_PREFIX + ":*"
        }, function (result, error_code, error_message) {
            if (error_code !== 0) {
                return null;
            }
            buttons_cache = [];
            for (let item in result.items) {
                if (typeof result.items[item] === "undefined") continue;
                buttons_cache.push(result.items[item].value);
            }
            console.log("Buttons loaded ", buttons_cache.length);
        });
    },
    getAllButtons: function () {
        Shelly.call("KVS.GetMany", {
            match: ALLTERCO_DEVICE_NAME_PREFIX + ":*"
        }, function (result, error_code, error_message) {
            if (error_code !== 0) {
                return null;
            }
            for (let item in result.items) {
                if (typeof result.items[item] === "undefined") continue;
                let buttons = [];
                buttons.push({ name: item, addr: result.items[item].value });
                print(JSON.stringify(buttons));
            }
            return result.items;
        });
    },
    getButton: function (btnName, callback) {
        Shelly.call("KVS.Get", {
            key: btnName,
        }, function (result, error_code, error_message, callback) {
            if (error_code !== 0) {
                console.log('Button not found');
            } else {
                callback();
            }
        }, callback);
    },
    deleteButton: function (btnName) {
        Shelly.call("KVS.Delete", {
            key: btnName,
        }, function (result, error_code, error_message, btnName) {
            if (error_code === 0) {
                print("Button deleted: ", btnName);
            } else {
                print("Button not found: ", btnName);
            }
            return error_code === 0;
        }, btnName);
    },
    registerButton: function (btnName, addr) {
        Shelly.call("KVS.Set", {
            key: btnName,
            value: addr
        }, function (result, error_code, error_message, btnName) {
            if (error_code === 0) {
                print("Button registered successfully: ", btnName);
            } else {
                print("Button not registered: ", btnName);
            }
            return error_code === 0;
        }, btnName);
    },
    registerButtonCheck: function (btnName, addr) {
        let button1 = { name: btnName, addr: addr };
        console.log(JSON.stringify(button1));
        Shelly.call("KVS.Get", {
            key: btnName
        }, function (result, error_code, error_message, button) {
            if (error_code !== 0) {
                KVSbtn.registerButton(btnName, button.addr);
            } else {
                print("Button already registered: ", btnName);
            }
        }, button1);
    }
};

// BLUETOOTH SCANNER
// BLE DECODING FUNCTIONS -----------------------------
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;
    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) {
        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;
        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;
    },
};

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

function replace(origin, substr, replace) {
    while (origin.indexOf(substr) !== -1) {
        origin = origin.slice(0, origin.indexOf(substr)) + replace + origin.slice(origin.indexOf(substr) + substr.length, origin.length);
    }
    return origin;
}
// END BLE DECODING FUNCTIONS -----------------------------------

let last_packet_id = 0x100;

// SCAN OF PAIRING / UNPAIRING BUTTONS (One click for Pairing, Double click for Unpairing)
function scanBLE(ev, res) {
    let found = false;
    if (ev !== BLE.Scanner.SCAN_RESULT) return;
    if (typeof res.service_data === 'undefined' || typeof res.service_data[BTHOME_SVC_ID_STR] === 'undefined') return;
    if (typeof res.local_name !== 'string') return;
    if (res.local_name.indexOf(ALLTERCO_DEVICE_NAME_PREFIX) !== 0) return;

    let BTHparsed = ShellyBLUParser.getData(res);
    if (BTHparsed === null) {
        console.log("Failed to parse BTH data");
        return;
    }
    if (last_packet_id === BTHparsed.pid) return;
    last_packet_id = BTHparsed.pid;

    for (let item in buttons_cache) {
        if (buttons_cache[item] === res.addr)
            found = true;
    }
    if (found === false && CONFIG_MODE === false) return;

    console.log("Shelly BTH packet: ", JSON.stringify(BTHparsed));
    bleReceived(BTHparsed, res);
}

function bleReceived(BTHparsed, res) {
    let btnName = ALLTERCO_DEVICE_NAME_PREFIX + ':' + replace(BTHparsed.addr, ':', '');
    if (CONFIG_MODE) {
        if (BTHparsed.Button === 1) {
            KVSbtn.registerButtonCheck(btnName, BTHparsed.addr);
        } else if (BTHparsed.Button === 2) {
            KVSbtn.deleteButton(btnName);
        }
    } else {
        btnAction.buttonPress(btnName, BTHparsed);
    }
}

let program = {
    init: function () {
        let BLEConfig = Shelly.getComponentConfig('ble');
        if (BLEConfig.enable === false) {
            console.log('Error: BLE not enabled');
        } else {
            Timer.set(1000, false, this.startBLEScan);
        }
    },
    startBLEScan: function () {
        let bleScanSuccess = BLE.Scanner.Start({ duration_ms: SCAN_DURATION, active: true }, scanBLE);

        if (bleScanSuccess === false) {
            Timer.set(1000, false, this.startBLEScan);
        } else {
            CONFIG_MODE = true;
            Timer.set(CONFIG_DURATION, false, program.stopConfigMode);
            console.log('Pairing mode started.... (', CONFIG_DURATION / 1000, 's)');
        }
    },
    stopConfigMode: function () {
        CONFIG_MODE = false;
        console.log('Pairing mode ended');
        KVSbtn.loadAllButtons();
    }
};

program.init();
 

 

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.

×
×
  • Create New...