Abilita un'interazione fluida tra Shelly BLU Button1 e altri dispositivi Shelly con questo script Gen2, che facilita le richieste API locali basate sugli eventi del pulsante. Gli utenti possono associare URL specifici alle azioni del pulsante come singolo, doppio e triplo clic. Lo script gestisce in modo efficiente la coda e l'elaborazione delle richieste URL, soggetta al limite di cinque chiamate RPC simultanee. Prima di utilizzarlo, configura l'indirizzo MAC del tuo BLU Button1 e definisci le azioni desiderate per ogni evento del pulsante. Ideale per automatizzare risposte tra diversi ecosistemi Shelly, questo script ti permette di semplificare il controllo di dispositivi di varie generazioni.
* Cosa dovresti modificare prima di usarlo:
bluButtonAddress -> Qui devi inserire l'indirizzo MAC del tuo blu button.
Questo script ti aiuterà a trovare l'indirizzo MAC: https://github.com/ALLTERCO/shelly-script-examples/blob/main/ble-shelly-scanner.js
Azioni:
Qui devi inserire gli URL da eseguire all'evento specificato. Gli URL che devono essere chiamati al singolo/corto clic del pulsante, devono essere inseriti nell'oggetto singlePush. Lo stesso vale per il doppio e triplo clic. Esempio sotto.
Limitazioni:
Al momento c'è un limite di 5 chiamate RPC contemporanee e per questo motivo lo script eseguirà ogni 3 URL con un ritardo di 1 secondo.
Le limitazioni possono essere verificate qui: https://shelly-api-docs.shelly.cloud/gen2/Scripts/ShellyScriptLanguageFeatures#resource-limits
L'ordine di esecuzione degli URL non può essere garantito
javascript
/* =========MODIFICA QUI========= */
let CONFIG = {
bluButtonAddress: "b4:35:22:fe:56:e5", //l'indirizzo mac del pulsante Shelly blu button1 che attiverà le azioni
actions: { //url da chiamare su un evento
//quando aggiungi url devi separarli con virgole e metterli tra virgolette
singlePush: [ //url che verranno eseguiti all'evento singlePush dal pulsante 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: [ //url che verranno eseguiti all'evento doublePush dal pulsante blu button1
"http://192.168.1.35/roller/0?go=close"
],
triplePush: [ //url che verranno eseguiti all'evento triplePush dal pulsante 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: [ //url che verranno eseguiti all'evento longPush dal pulsante blu button1
"http://192.168.1.41/rpc/Cover.Close",
"http://192.168.1.42/rpc/Cover.Close"
]
}
};
/** =============================== NON MODIFICARE OLTRE QUESTO PUNTO =============================== */
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[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 };
function getByteSize(type) {
if (type === uint8 || type === int8) return 1;
if (type === uint16 || type === int16) return 2;
if (type === uint24 || type === int24) return 3;
//impossibile poiché le pubblicità sono molto più piccole;
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) {
//i beacon potrebbero non fornire dati di servizio BTH
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;
//non può gestire dati criptati
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: tipo sconosciuto");
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("Chiamata a", data.url, "fallita");
}
else {
console.log("Chiamata a", data.url, "riuscita");
}
callsCounter--;
},
{ url: url }
);
}
}
//se ci sono più url in coda
if(urlsQueue.length > 0) {
Timer.set(
1000, //il ritardo
false,
function() {
callQueue();
}
);
}
}
let lastPacketId = 0x100;
function bleScanCallback(event, result) {
//esci se la chiamata non è per un risultato ricevuto
if (event !== BLE.Scanner.SCAN_RESULT) {
return;
}
//esci se i dati non provengono da un pulsante Shelly Blu button1 e se l'indirizzo mac non corrisponde
if ( typeof result.local_name === "undefined" ||
typeof result.addr === "undefined" ||
result.local_name.indexOf("SBBT") !== 0 ||
result.addr !== CONFIG.bluButtonAddress
) {
return;
}
let servData = result.service_data;
//esci se i dati di servizio sono nulli/dispositivo criptato
if(servData === null || typeof servData === "undefined" || typeof servData[BTHOME_SVC_ID_STR] === "undefined") {
console.log("Impossibile gestire dispositivi criptati");
return;
}
let receivedData = BTHomeDecoder.unpack(servData[BTHOME_SVC_ID_STR]);
//esci se i dati decompattati sono nulli o il dispositivo è criptato
if(receivedData === null || typeof receivedData === "undefined" || receivedData["encryption"]) {
console.log("Impossibile gestire dispositivi criptati");
return;
}
//esci se l'evento è duplicato
if (lastPacketId === receivedData.pid) {
return;
}
lastPacketId = receivedData["pid"];
//ottenere ed eseguire l'azione
let actionType = ["", "singlePush", "doublePush", "triplePush", "longPush"][receivedData["Button"]];
let actionUrls = CONFIG.actions[actionType];
//esci se l'evento non esiste nella configurazione
if(typeof actionType === "undefined") {
console.log("Tipo di evento sconosciuto nella configurazione");
return;
}
//salva tutti gli url nella coda per l'evento corrente
for(let i in actionUrls) {
urlsQueue.push(actionUrls[i]);
}
callQueue();
}
function bleScan() {
//controlla se il bluetooth è abilitato
let bleConfig = Shelly.getComponentConfig("ble");
//esci se il bluetooth non è abilitato
if(bleConfig.enable === false) {
console.log("BLE non è abilitato");
return;
}
//avvia lo scanner
let bleScanner = BLE.Scanner.Start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
active: true
});
//esci se lo scanner non può essere avviato
if(bleScanner === false) {
console.log("Errore nell'avvio dello scanner BLE");
return;
}
BLE.Scanner.Subscribe(bleScanCallback);
console.log("BLE avviato con successo");
}
function init() {
//esci se non c'è una configurazione
if(typeof CONFIG === "undefined") {
console.log("Impossibile leggere la configurazione");
return;
}
//esci se non c'è un indirizzo blu button
if(typeof CONFIG.bluButtonAddress !== "string") {
console.log("Errore con l'indirizzo del pulsante Shelly BLU button1");
return;
}
//esci se non c'è l'oggetto azioni
if(typeof CONFIG.actions === "undefined") {
console.log("Impossibile trovare l'oggetto azioni nella configurazione");
return;
}
//avvia la scansione ble
bleScan();
}
//inizializza lo script
init();