Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. JavaScript
  5. Obi Bluetooth Lichterkette in IOBroker integrieren

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.8k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    2.2k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    16
    1
    3.2k

Obi Bluetooth Lichterkette in IOBroker integrieren

Geplant Angeheftet Gesperrt Verschoben JavaScript
4 Beiträge 2 Kommentatoren 420 Aufrufe 3 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • F Offline
    F Offline
    Fabian1
    schrieb am zuletzt editiert von Fabian1
    #1

    Hallo Zusammen,
    ich bin echt am verzweifeln, habe keine Lust mehr und brauche dringend ein frisches paar Augen!
    Alles hat damit angefangen, dass meine Frau davon abgeraten hat, die 5. Twinkly Lichterkette für den Garten zu bestellen! :D
    Also bin ich zum Obi und habe mich nach Alternativen umgeguckt und bin dabei auf diese Lichterkette gestoßen! WhatsApp Image 2024-11-26 at 16.56.25.jpeg
    Die perfekte Länge und Bluetooth Steuerung per App, da dachte ich mir, perfekt, die kriege ich bestimmt irgendwie in den IOBroker integriert und kann sie dann zusammen mit den anderen Lichterketten über den Außenlichtsensor dimmen bzw. ein und ausschalten. (Außerdem liebe ich die Herausforderung :) )

    Zu diesem Zeitpunkt dachte ich noch, der schwierigste Teil wird, die funktionsweise der App reverse zu engineeren und herauszufinden, was ich senden muss um die verschiedenen Funktionen zu triggern. Das war allerdings dank eines alten Android Handys meiner Frau und Wireshark ziemlich easy. Also kannte ich jetzt die Daten die ich senden muss und die richtigen Addressen. Am Handy hat es sofort funktioniert und ich dachte, ok das war einfach, jetzt lade ich mir einfach einen IOB Adapter runter der Bluetooth Befehle senden kann (sowie die 1000 IPhone und Android Apps die es da gibt) und das war's. Leider hab ich dann gemerkt den gibt es nicht! :astonished:

    Also bin ich selbst zur tat geschritten und habe folgendes Skript programmiert und es funktioniert fast alles!
    Die Lichterkette wird verbunden, ich kann die Services auslesen aber ich KRIEG EINFACH KEINE BEFEHLE GESENDET, immer Fehler beim Schreiben des Wertes in die Characteristic!! :hankey:

    Ich bin echt mit meinem Latein am Ende! Hier das Skript:

    // Laden des dbus-next Moduls
    const dbus = require('dbus-next');
    
    // Pfad zum Datenpunkt
    const datapointPath = 'javascript.0.MeineVariablen.Lichterkette_OnOff';
    
    // Zustand 'Lichterkette_OnOff' erstellen, falls nicht vorhanden
    createState(datapointPath, false, {
       name: 'Lichterkette On/Off',
       type: 'boolean',
       role: 'switch',
       read: true,
       write: true,
       def: false
    }, () => {
       // Event Listener für Zustandsänderungen registrieren
       on({ id: datapointPath, change: 'ne' }, (obj) => {
           const action = obj.state.val ? 'on' : 'off';
           controlLight(action)
               .then(() => {
                   console.log(`Lichterkette wurde ${action === 'on' ? 'eingeschaltet' : 'ausgeschaltet'}`);
               })
               .catch((err) => {
                   console.error('Fehler bei der Steuerung der Lichterkette:', err.message || err);
               });
       });
    });
    
    // Hauptfunktion zur Steuerung der Lichterkette
    function controlLight(action) {
       return new Promise(async (resolve, reject) => {
           const bus = dbus.systemBus();
    
           const bluezServiceName = 'org.bluez';
           const adapterPath = '/org/bluez/hci0'; // Bluetooth-Adapter (hci0)
           const deviceAddress = '24:35:02:27:DE:6E'; // MAC-Adresse Lichterkette
           const devicePath = adapterPath + '/dev_' + deviceAddress.replace(/:/g, '_');
    
           try {
               // Verbindung zum BlueZ-Service herstellen
               const bluez = await bus.getProxyObject(bluezServiceName, '/');
               const manager = bluez.getInterface('org.freedesktop.DBus.ObjectManager');
    
               // Alle verwalteten Objekte abrufen
               const objects = await manager.GetManagedObjects();
    
               // Prüfen, ob das Gerät bereits bekannt ist
               let deviceFound = false;
    
               for (const [path, interfaces] of Object.entries(objects)) {
                   if (path === devicePath) {
                       deviceFound = true;
                       break;
                   }
               }
    
               // Wenn das Gerät nicht gefunden wurde, Discovery starten
               if (!deviceFound) {
                   console.log('Gerät nicht gefunden, starte Discovery...');
    
                   const adapter = await bus.getProxyObject(bluezServiceName, adapterPath);
                   const adapterInterface = adapter.getInterface('org.bluez.Adapter1');
    
                   await adapterInterface.StartDiscovery();
    
                   // Warten, bis das Gerät gefunden wird oder Timeout nach 15 Sekunden
                   await new Promise((resolveDiscovery, rejectDiscovery) => {
                       const timeout = setTimeout(() => {
                           rejectDiscovery(new Error('Gerät wurde während der Discovery nicht gefunden'));
                       }, 15000); // Timeout nach 15 Sekunden
    
                       manager.on('InterfacesAdded', (path, interfaces) => {
                           if (path === devicePath) {
                               clearTimeout(timeout);
                               resolveDiscovery();
                           }
                       });
                   });
    
                   await adapterInterface.StopDiscovery();
               }
    
               // Verbindung zum Gerät herstellen
               const device = await bus.getProxyObject(bluezServiceName, devicePath);
               const deviceInterface = device.getInterface('org.bluez.Device1');
    
               try {
                   await deviceInterface.Connect();
                   console.log('Verbunden mit der Lichterkette');
    
                   // Verzögerung erhöhen
                   await new Promise((resolveDelay) => setTimeout(resolveDelay, 5000)); // 5 Sekunden warten
    
                   // Services und Characteristics auflisten
                   await listServicesAndCharacteristics(devicePath, bus);
               } catch (error) {
                   console.error('Fehler beim Verbinden mit dem Gerät:', error.message || error);
                   return reject(error);
               }
    
               // **Characteristic für Initialisierung und Ein/Aus finden**
               const characteristicUUID = '0000fff1-0000-1000-8000-00805f9b34fb'; // Verwenden von 0000fff1-... für beide Befehle
               const characteristicPath = await findCharacteristicPath(devicePath, characteristicUUID, bus);
               if (!characteristicPath) {
                   console.error('Characteristic für Ein/Aus nicht gefunden');
                   await deviceInterface.Disconnect();
                   return reject(new Error('Characteristic für Ein/Aus nicht gefunden'));
               }
    
               // Interface der Characteristic abrufen
               const characteristic = await bus.getProxyObject(bluezServiceName, characteristicPath);
               const characteristicInterface = characteristic.getInterface('org.bluez.GattCharacteristic1');
    
               // **Ein-/Ausschaltbefehl senden**
               // Befehl basierend auf der Aktion definieren
               const value = action === 'on' ? [0x01, 0x01, 0x01, 0x01] : [0x01, 0x01, 0x01, 0x00];
    
               try {
                   await characteristicInterface.WriteValue(Buffer.from(value), { type: dbus.Variant('s', 'command') });
                   console.log(`Lichterkette wurde ${action === 'on' ? 'eingeschaltet' : 'ausgeschaltet'}`);
               } catch (error) {
                   console.error('Fehler beim Schreiben des Wertes in die Characteristic:', error.message || error);
               }
    
               // Verbindung zum Gerät trennen
               try {
                   await deviceInterface.Disconnect();
                   console.log('Verbindung zur Lichterkette getrennt');
               } catch (error) {
                   console.error('Fehler beim Trennen der Verbindung zum Gerät:', error.message || error);
               }
    
               resolve();
           } catch (err) {
               console.error('Fehler in controlLight:', err.message || err);
               reject(err);
           }
       });
    }
    
    // Hilfsfunktion, um den Pfad der Characteristic zu finden
    function findCharacteristicPath(devicePath, characteristicUUID, bus) {
       return new Promise(async (resolve) => {
           const bluezServiceName = 'org.bluez';
           const manager = await bus.getProxyObject(bluezServiceName, '/').then((obj) => obj.getInterface('org.freedesktop.DBus.ObjectManager'));
           const objects = await manager.GetManagedObjects();
    
           for (const [path, interfaces] of Object.entries(objects)) {
               if (path.startsWith(devicePath) && 'org.bluez.GattCharacteristic1' in interfaces) {
                   const uuid = interfaces['org.bluez.GattCharacteristic1'].UUID.value.toLowerCase();
                   if (uuid === characteristicUUID.toLowerCase()) {
                       return resolve(path);
                   }
               }
           }
           resolve(null);
       });
    }
    
    // Funktion zum Auflisten der verfügbaren Services und Characteristics
    function listServicesAndCharacteristics(devicePath, bus) {
       return new Promise(async (resolve) => {
           const manager = await bus.getProxyObject('org.bluez', '/').then((obj) => obj.getInterface('org.freedesktop.DBus.ObjectManager'));
           const objects = await manager.GetManagedObjects();
    
           console.log(`Anzahl der Objekte: ${Object.keys(objects).length}`);
    
           console.log('Verfügbare Services und Characteristics:');
           let found = false;
           for (const [path, interfaces] of Object.entries(objects)) {
               if (path.startsWith(devicePath)) {
                   found = true;
                   console.log(`Gefundenes Objekt: ${path}`);
                   if ('org.bluez.GattService1' in interfaces) {
                       const uuid = interfaces['org.bluez.GattService1'].UUID.value;
                       console.log(`Service: ${path}, UUID: ${uuid}`);
                   }
                   if ('org.bluez.GattCharacteristic1' in interfaces) {
                       const uuid = interfaces['org.bluez.GattCharacteristic1'].UUID.value;
                       const flags = interfaces['org.bluez.GattCharacteristic1'].Flags.value;
                       console.log(`Characteristic: ${path}, UUID: ${uuid}, Flags: ${flags}`);
                   }
               }
           }
           if (!found) {
               console.log('Keine Services oder Characteristics gefunden.');
           }
           resolve();
       });
    }
    
    
    

    In der Konsole funktioniert es übrigens:
    mit

    bluetoothctl
    power on
    scan on
    connect 24:35:02:27:DE:6E
    gatt.select-attribute 0000fff1-0000-1000-8000-00805f9b34fb
    
    Aus:
    gatt.write "0x01 0x01 0x01 0x00"
    
    Ein:
    gatt.write "0x01 0x01 0x01 0x01"
    

    Kann ich die Lichterkette aus der Konsole ein- und ausschalten, aber ich finde einfach keinen Weg, das ganze in ein Skript zu packen.

    Vielen Dank für eure Hilfe,
    Fabian

    F 1 Antwort Letzte Antwort
    0
    • F Fabian1

      Hallo Zusammen,
      ich bin echt am verzweifeln, habe keine Lust mehr und brauche dringend ein frisches paar Augen!
      Alles hat damit angefangen, dass meine Frau davon abgeraten hat, die 5. Twinkly Lichterkette für den Garten zu bestellen! :D
      Also bin ich zum Obi und habe mich nach Alternativen umgeguckt und bin dabei auf diese Lichterkette gestoßen! WhatsApp Image 2024-11-26 at 16.56.25.jpeg
      Die perfekte Länge und Bluetooth Steuerung per App, da dachte ich mir, perfekt, die kriege ich bestimmt irgendwie in den IOBroker integriert und kann sie dann zusammen mit den anderen Lichterketten über den Außenlichtsensor dimmen bzw. ein und ausschalten. (Außerdem liebe ich die Herausforderung :) )

      Zu diesem Zeitpunkt dachte ich noch, der schwierigste Teil wird, die funktionsweise der App reverse zu engineeren und herauszufinden, was ich senden muss um die verschiedenen Funktionen zu triggern. Das war allerdings dank eines alten Android Handys meiner Frau und Wireshark ziemlich easy. Also kannte ich jetzt die Daten die ich senden muss und die richtigen Addressen. Am Handy hat es sofort funktioniert und ich dachte, ok das war einfach, jetzt lade ich mir einfach einen IOB Adapter runter der Bluetooth Befehle senden kann (sowie die 1000 IPhone und Android Apps die es da gibt) und das war's. Leider hab ich dann gemerkt den gibt es nicht! :astonished:

      Also bin ich selbst zur tat geschritten und habe folgendes Skript programmiert und es funktioniert fast alles!
      Die Lichterkette wird verbunden, ich kann die Services auslesen aber ich KRIEG EINFACH KEINE BEFEHLE GESENDET, immer Fehler beim Schreiben des Wertes in die Characteristic!! :hankey:

      Ich bin echt mit meinem Latein am Ende! Hier das Skript:

      // Laden des dbus-next Moduls
      const dbus = require('dbus-next');
      
      // Pfad zum Datenpunkt
      const datapointPath = 'javascript.0.MeineVariablen.Lichterkette_OnOff';
      
      // Zustand 'Lichterkette_OnOff' erstellen, falls nicht vorhanden
      createState(datapointPath, false, {
         name: 'Lichterkette On/Off',
         type: 'boolean',
         role: 'switch',
         read: true,
         write: true,
         def: false
      }, () => {
         // Event Listener für Zustandsänderungen registrieren
         on({ id: datapointPath, change: 'ne' }, (obj) => {
             const action = obj.state.val ? 'on' : 'off';
             controlLight(action)
                 .then(() => {
                     console.log(`Lichterkette wurde ${action === 'on' ? 'eingeschaltet' : 'ausgeschaltet'}`);
                 })
                 .catch((err) => {
                     console.error('Fehler bei der Steuerung der Lichterkette:', err.message || err);
                 });
         });
      });
      
      // Hauptfunktion zur Steuerung der Lichterkette
      function controlLight(action) {
         return new Promise(async (resolve, reject) => {
             const bus = dbus.systemBus();
      
             const bluezServiceName = 'org.bluez';
             const adapterPath = '/org/bluez/hci0'; // Bluetooth-Adapter (hci0)
             const deviceAddress = '24:35:02:27:DE:6E'; // MAC-Adresse Lichterkette
             const devicePath = adapterPath + '/dev_' + deviceAddress.replace(/:/g, '_');
      
             try {
                 // Verbindung zum BlueZ-Service herstellen
                 const bluez = await bus.getProxyObject(bluezServiceName, '/');
                 const manager = bluez.getInterface('org.freedesktop.DBus.ObjectManager');
      
                 // Alle verwalteten Objekte abrufen
                 const objects = await manager.GetManagedObjects();
      
                 // Prüfen, ob das Gerät bereits bekannt ist
                 let deviceFound = false;
      
                 for (const [path, interfaces] of Object.entries(objects)) {
                     if (path === devicePath) {
                         deviceFound = true;
                         break;
                     }
                 }
      
                 // Wenn das Gerät nicht gefunden wurde, Discovery starten
                 if (!deviceFound) {
                     console.log('Gerät nicht gefunden, starte Discovery...');
      
                     const adapter = await bus.getProxyObject(bluezServiceName, adapterPath);
                     const adapterInterface = adapter.getInterface('org.bluez.Adapter1');
      
                     await adapterInterface.StartDiscovery();
      
                     // Warten, bis das Gerät gefunden wird oder Timeout nach 15 Sekunden
                     await new Promise((resolveDiscovery, rejectDiscovery) => {
                         const timeout = setTimeout(() => {
                             rejectDiscovery(new Error('Gerät wurde während der Discovery nicht gefunden'));
                         }, 15000); // Timeout nach 15 Sekunden
      
                         manager.on('InterfacesAdded', (path, interfaces) => {
                             if (path === devicePath) {
                                 clearTimeout(timeout);
                                 resolveDiscovery();
                             }
                         });
                     });
      
                     await adapterInterface.StopDiscovery();
                 }
      
                 // Verbindung zum Gerät herstellen
                 const device = await bus.getProxyObject(bluezServiceName, devicePath);
                 const deviceInterface = device.getInterface('org.bluez.Device1');
      
                 try {
                     await deviceInterface.Connect();
                     console.log('Verbunden mit der Lichterkette');
      
                     // Verzögerung erhöhen
                     await new Promise((resolveDelay) => setTimeout(resolveDelay, 5000)); // 5 Sekunden warten
      
                     // Services und Characteristics auflisten
                     await listServicesAndCharacteristics(devicePath, bus);
                 } catch (error) {
                     console.error('Fehler beim Verbinden mit dem Gerät:', error.message || error);
                     return reject(error);
                 }
      
                 // **Characteristic für Initialisierung und Ein/Aus finden**
                 const characteristicUUID = '0000fff1-0000-1000-8000-00805f9b34fb'; // Verwenden von 0000fff1-... für beide Befehle
                 const characteristicPath = await findCharacteristicPath(devicePath, characteristicUUID, bus);
                 if (!characteristicPath) {
                     console.error('Characteristic für Ein/Aus nicht gefunden');
                     await deviceInterface.Disconnect();
                     return reject(new Error('Characteristic für Ein/Aus nicht gefunden'));
                 }
      
                 // Interface der Characteristic abrufen
                 const characteristic = await bus.getProxyObject(bluezServiceName, characteristicPath);
                 const characteristicInterface = characteristic.getInterface('org.bluez.GattCharacteristic1');
      
                 // **Ein-/Ausschaltbefehl senden**
                 // Befehl basierend auf der Aktion definieren
                 const value = action === 'on' ? [0x01, 0x01, 0x01, 0x01] : [0x01, 0x01, 0x01, 0x00];
      
                 try {
                     await characteristicInterface.WriteValue(Buffer.from(value), { type: dbus.Variant('s', 'command') });
                     console.log(`Lichterkette wurde ${action === 'on' ? 'eingeschaltet' : 'ausgeschaltet'}`);
                 } catch (error) {
                     console.error('Fehler beim Schreiben des Wertes in die Characteristic:', error.message || error);
                 }
      
                 // Verbindung zum Gerät trennen
                 try {
                     await deviceInterface.Disconnect();
                     console.log('Verbindung zur Lichterkette getrennt');
                 } catch (error) {
                     console.error('Fehler beim Trennen der Verbindung zum Gerät:', error.message || error);
                 }
      
                 resolve();
             } catch (err) {
                 console.error('Fehler in controlLight:', err.message || err);
                 reject(err);
             }
         });
      }
      
      // Hilfsfunktion, um den Pfad der Characteristic zu finden
      function findCharacteristicPath(devicePath, characteristicUUID, bus) {
         return new Promise(async (resolve) => {
             const bluezServiceName = 'org.bluez';
             const manager = await bus.getProxyObject(bluezServiceName, '/').then((obj) => obj.getInterface('org.freedesktop.DBus.ObjectManager'));
             const objects = await manager.GetManagedObjects();
      
             for (const [path, interfaces] of Object.entries(objects)) {
                 if (path.startsWith(devicePath) && 'org.bluez.GattCharacteristic1' in interfaces) {
                     const uuid = interfaces['org.bluez.GattCharacteristic1'].UUID.value.toLowerCase();
                     if (uuid === characteristicUUID.toLowerCase()) {
                         return resolve(path);
                     }
                 }
             }
             resolve(null);
         });
      }
      
      // Funktion zum Auflisten der verfügbaren Services und Characteristics
      function listServicesAndCharacteristics(devicePath, bus) {
         return new Promise(async (resolve) => {
             const manager = await bus.getProxyObject('org.bluez', '/').then((obj) => obj.getInterface('org.freedesktop.DBus.ObjectManager'));
             const objects = await manager.GetManagedObjects();
      
             console.log(`Anzahl der Objekte: ${Object.keys(objects).length}`);
      
             console.log('Verfügbare Services und Characteristics:');
             let found = false;
             for (const [path, interfaces] of Object.entries(objects)) {
                 if (path.startsWith(devicePath)) {
                     found = true;
                     console.log(`Gefundenes Objekt: ${path}`);
                     if ('org.bluez.GattService1' in interfaces) {
                         const uuid = interfaces['org.bluez.GattService1'].UUID.value;
                         console.log(`Service: ${path}, UUID: ${uuid}`);
                     }
                     if ('org.bluez.GattCharacteristic1' in interfaces) {
                         const uuid = interfaces['org.bluez.GattCharacteristic1'].UUID.value;
                         const flags = interfaces['org.bluez.GattCharacteristic1'].Flags.value;
                         console.log(`Characteristic: ${path}, UUID: ${uuid}, Flags: ${flags}`);
                     }
                 }
             }
             if (!found) {
                 console.log('Keine Services oder Characteristics gefunden.');
             }
             resolve();
         });
      }
      
      
      

      In der Konsole funktioniert es übrigens:
      mit

      bluetoothctl
      power on
      scan on
      connect 24:35:02:27:DE:6E
      gatt.select-attribute 0000fff1-0000-1000-8000-00805f9b34fb
      
      Aus:
      gatt.write "0x01 0x01 0x01 0x00"
      
      Ein:
      gatt.write "0x01 0x01 0x01 0x01"
      

      Kann ich die Lichterkette aus der Konsole ein- und ausschalten, aber ich finde einfach keinen Weg, das ganze in ein Skript zu packen.

      Vielen Dank für eure Hilfe,
      Fabian

      F Offline
      F Offline
      Fabian1
      schrieb am zuletzt editiert von
      #2

      ok ich habe es hinbekommen, zwar mit exec, aber besser als garnichts!
      Ich habe jetzt also eine Obi Bluetooth Lichterkette die per Alexa steuerbar ist.
      Es funktioniert bis jetzt An und Aus und Helligkeit in %. Falls noch jemand vor einer ähnlichen Problem steht, hier die Lösung:

      const { exec } = require('child_process');
      
      // MAC-Adresse des Geräts und UUID der GATT-Characteristic
      const deviceAddress = '24:35:02:27:DE:6E';
      const characteristicUUID = '0000fff1-0000-1000-8000-00805f9b34fb';
      
      // Pfade zu den Datenpunkten
      const datapointOnOff = 'javascript.0.MeineVariablen.Lichterkette_OnOff';
      const datapointBrightness = 'javascript.0.MeineVariablen.Lichterkette_Helligkeit';
      
      // Warteschlange und Status
      const actionQueue = [];
      let isProcessing = false;
      
      // Funktion: Shell-Befehl ausführen mit Debugging und Timeout
      function executeBluetoothCommand(command, timeout = 10000) {
          console.log(`Führe aus: ${command}`);
          return new Promise((resolve, reject) => {
              const process = exec(command, (error, stdout, stderr) => {
                  if (error) {
                      console.error(`Fehler: ${stderr || error.message}`);
                      return reject(stderr || error.message);
                  }
                  console.log(`Erfolg: ${stdout}`);
                  resolve(stdout.trim());
              });
      
              // Timeout hinzufügen
              setTimeout(() => {
                  process.kill('SIGTERM');
                  reject('Befehl abgebrochen (Timeout)');
              }, timeout);
          });
      }
      
      // Funktion: Gerät ist verbunden prüfen
      async function isDeviceConnected() {
          try {
              const output = await executeBluetoothCommand(`bluetoothctl info ${deviceAddress}`);
              return output.includes('Connected: yes');
          } catch (error) {
              console.warn('Verbindungsprüfung fehlgeschlagen:', error);
              return false;
          }
      }
      
      // Funktion: Wiederholungsversuch für einen Befehl
      async function retryCommand(command, retries = 10) {
          for (let i = 0; i < retries; i++) {
              try {
                  return await executeBluetoothCommand(command);
              } catch (error) {
                  console.warn(`Fehler beim Befehl: ${command}, Versuch ${i + 1} von ${retries}`);
              }
          }
          throw new Error(`Befehl fehlgeschlagen nach ${retries} Versuchen: ${command}`);
      }
      
      // Funktion: Gerät verbinden
      async function connectToDevice() {
          try {
              console.log('Versuche Verbindung zum Gerät herzustellen...');
              if (!(await isDeviceConnected())) {
                  await retryCommand(`bluetoothctl connect ${deviceAddress}`);
              }
              console.log('Verbindung erfolgreich hergestellt.');
          } catch (error) {
              console.error('Verbindung fehlgeschlagen, versuche Fehler zu beheben...');
              await retryCommand(`bluetoothctl power on`);
              await retryCommand(`bluetoothctl connect ${deviceAddress}`);
              console.log('Verbindung nach Fehlerbehebung erfolgreich.');
          }
      }
      
      // Funktion: Gerät trennen
      async function disconnectDevice() {
          try {
              console.log('Trenne Verbindung zum Gerät...');
              if (await isDeviceConnected()) {
                  await retryCommand(`bluetoothctl disconnect ${deviceAddress}`);
              }
              console.log('Verbindung erfolgreich getrennt.');
          } catch (error) {
              console.error('Fehler beim Trennen der Verbindung:', error);
          }
      }
      
      // Funktion: GATT-Wert schreiben
      async function writeGattValue(value) {
          try {
              console.log(`Schreibe Wert: ${value} an die GATT-Characteristic...`);
              const script = `
      bluetoothctl << EOF
      menu gatt
      select-attribute ${characteristicUUID}
      write ${value}
      exit
      EOF
              `;
              await executeBluetoothCommand(script);
          } catch (error) {
              console.error('Fehler beim Schreiben auf die GATT-Characteristic:', error);
              throw new Error('GATT-Wert konnte nicht geschrieben werden.');
          }
      }
      
      // Funktion: Helligkeit berechnen
      function calculateBrightnessHex(brightnessPercent) {
          const brightnessHex = Math.round((brightnessPercent / 100) * 0x64).toString(16).padStart(2, '0');
          return `"0x03 0x01 0x01 0x${brightnessHex}"`;
      }
      
      // Funktion: Lichterkette steuern
      async function controlLight(action, brightness = null) {
          try {
              await connectToDevice();
              if (action) {
                  if (brightness !== null) {
                      const brightnessValue = calculateBrightnessHex(brightness);
                      await writeGattValue(brightnessValue);
                  } else {
                      const value = action === 'on' ? '"0x01 0x01 0x01 0x01"' : '"0x01 0x01 0x01 0x00"';
                      await writeGattValue(value);
                  }
              }
              await disconnectDevice();
          } catch (error) {
              console.error('Fehler bei der Steuerung der Lichterkette:', error);
          }
      }
      
      // Funktion: Warteschlange abarbeiten
      async function processQueue() {
          if (isProcessing) return; // Verhindert gleichzeitige Verarbeitung
      
          isProcessing = true;
          while (actionQueue.length > 0) {
              const { action, brightness } = actionQueue.shift(); // Nächsten Befehl aus der Warteschlange holen
              console.log(`Verarbeite Aktion: ${action}, Helligkeit: ${brightness}`);
              await controlLight(action, brightness);
          }
          isProcessing = false;
      }
      
      // Datenpunkte erstellen, falls nicht vorhanden
      createState(datapointOnOff, false, {
          name: 'Lichterkette On/Off',
          type: 'boolean',
          role: 'switch',
          read: true,
          write: true,
          def: false
      }, () => {
          // Event Listener für Zustandsänderungen registrieren
          on({ id: datapointOnOff, change: 'ne' }, async (obj) => {
              const action = obj.state.val ? 'on' : 'off';
              actionQueue.push({ action, brightness: null });
              processQueue();
          });
      });
      
      createState(datapointBrightness, 100, {
          name: 'Lichterkette Helligkeit',
          type: 'number',
          role: 'level.dimmer',
          read: true,
          write: true,
          def: 100,
          min: 0,
          max: 100
      }, () => {
          // Event Listener für Helligkeitsänderungen registrieren
          on({ id: datapointBrightness, change: 'ne' }, async (obj) => {
              const brightness = obj.state.val;
      
              if (brightness < 3) {
                  // Wenn Helligkeit unter 3%, Lichterkette ausschalten
                  console.log('Helligkeit unter 3% - Lichterkette wird ausgeschaltet.');
                  setState(datapointOnOff, false); // Setzt On/Off-Datenpunkt auf false
              } else {
                  // Ansonsten Helligkeit setzen
                  actionQueue.push({ action: 'on', brightness });
                  processQueue();
              }
          });
      });
      
      // Debugging: Manuelle Steuerung
      (async () => {
          try {
              console.log('Teste Steuerung: Einschalten mit 50% Helligkeit...');
              actionQueue.push({ action: 'on', brightness: 50 });
              processQueue();
      
              console.log('Teste Steuerung: Ausschalten...');
              actionQueue.push({ action: 'off', brightness: null });
              processQueue();
          } catch (error) {
              console.error('Fehler bei manueller Steuerung:', error);
          }
      })();
      
      
      OliverIOO 1 Antwort Letzte Antwort
      0
      • F Fabian1

        ok ich habe es hinbekommen, zwar mit exec, aber besser als garnichts!
        Ich habe jetzt also eine Obi Bluetooth Lichterkette die per Alexa steuerbar ist.
        Es funktioniert bis jetzt An und Aus und Helligkeit in %. Falls noch jemand vor einer ähnlichen Problem steht, hier die Lösung:

        const { exec } = require('child_process');
        
        // MAC-Adresse des Geräts und UUID der GATT-Characteristic
        const deviceAddress = '24:35:02:27:DE:6E';
        const characteristicUUID = '0000fff1-0000-1000-8000-00805f9b34fb';
        
        // Pfade zu den Datenpunkten
        const datapointOnOff = 'javascript.0.MeineVariablen.Lichterkette_OnOff';
        const datapointBrightness = 'javascript.0.MeineVariablen.Lichterkette_Helligkeit';
        
        // Warteschlange und Status
        const actionQueue = [];
        let isProcessing = false;
        
        // Funktion: Shell-Befehl ausführen mit Debugging und Timeout
        function executeBluetoothCommand(command, timeout = 10000) {
            console.log(`Führe aus: ${command}`);
            return new Promise((resolve, reject) => {
                const process = exec(command, (error, stdout, stderr) => {
                    if (error) {
                        console.error(`Fehler: ${stderr || error.message}`);
                        return reject(stderr || error.message);
                    }
                    console.log(`Erfolg: ${stdout}`);
                    resolve(stdout.trim());
                });
        
                // Timeout hinzufügen
                setTimeout(() => {
                    process.kill('SIGTERM');
                    reject('Befehl abgebrochen (Timeout)');
                }, timeout);
            });
        }
        
        // Funktion: Gerät ist verbunden prüfen
        async function isDeviceConnected() {
            try {
                const output = await executeBluetoothCommand(`bluetoothctl info ${deviceAddress}`);
                return output.includes('Connected: yes');
            } catch (error) {
                console.warn('Verbindungsprüfung fehlgeschlagen:', error);
                return false;
            }
        }
        
        // Funktion: Wiederholungsversuch für einen Befehl
        async function retryCommand(command, retries = 10) {
            for (let i = 0; i < retries; i++) {
                try {
                    return await executeBluetoothCommand(command);
                } catch (error) {
                    console.warn(`Fehler beim Befehl: ${command}, Versuch ${i + 1} von ${retries}`);
                }
            }
            throw new Error(`Befehl fehlgeschlagen nach ${retries} Versuchen: ${command}`);
        }
        
        // Funktion: Gerät verbinden
        async function connectToDevice() {
            try {
                console.log('Versuche Verbindung zum Gerät herzustellen...');
                if (!(await isDeviceConnected())) {
                    await retryCommand(`bluetoothctl connect ${deviceAddress}`);
                }
                console.log('Verbindung erfolgreich hergestellt.');
            } catch (error) {
                console.error('Verbindung fehlgeschlagen, versuche Fehler zu beheben...');
                await retryCommand(`bluetoothctl power on`);
                await retryCommand(`bluetoothctl connect ${deviceAddress}`);
                console.log('Verbindung nach Fehlerbehebung erfolgreich.');
            }
        }
        
        // Funktion: Gerät trennen
        async function disconnectDevice() {
            try {
                console.log('Trenne Verbindung zum Gerät...');
                if (await isDeviceConnected()) {
                    await retryCommand(`bluetoothctl disconnect ${deviceAddress}`);
                }
                console.log('Verbindung erfolgreich getrennt.');
            } catch (error) {
                console.error('Fehler beim Trennen der Verbindung:', error);
            }
        }
        
        // Funktion: GATT-Wert schreiben
        async function writeGattValue(value) {
            try {
                console.log(`Schreibe Wert: ${value} an die GATT-Characteristic...`);
                const script = `
        bluetoothctl << EOF
        menu gatt
        select-attribute ${characteristicUUID}
        write ${value}
        exit
        EOF
                `;
                await executeBluetoothCommand(script);
            } catch (error) {
                console.error('Fehler beim Schreiben auf die GATT-Characteristic:', error);
                throw new Error('GATT-Wert konnte nicht geschrieben werden.');
            }
        }
        
        // Funktion: Helligkeit berechnen
        function calculateBrightnessHex(brightnessPercent) {
            const brightnessHex = Math.round((brightnessPercent / 100) * 0x64).toString(16).padStart(2, '0');
            return `"0x03 0x01 0x01 0x${brightnessHex}"`;
        }
        
        // Funktion: Lichterkette steuern
        async function controlLight(action, brightness = null) {
            try {
                await connectToDevice();
                if (action) {
                    if (brightness !== null) {
                        const brightnessValue = calculateBrightnessHex(brightness);
                        await writeGattValue(brightnessValue);
                    } else {
                        const value = action === 'on' ? '"0x01 0x01 0x01 0x01"' : '"0x01 0x01 0x01 0x00"';
                        await writeGattValue(value);
                    }
                }
                await disconnectDevice();
            } catch (error) {
                console.error('Fehler bei der Steuerung der Lichterkette:', error);
            }
        }
        
        // Funktion: Warteschlange abarbeiten
        async function processQueue() {
            if (isProcessing) return; // Verhindert gleichzeitige Verarbeitung
        
            isProcessing = true;
            while (actionQueue.length > 0) {
                const { action, brightness } = actionQueue.shift(); // Nächsten Befehl aus der Warteschlange holen
                console.log(`Verarbeite Aktion: ${action}, Helligkeit: ${brightness}`);
                await controlLight(action, brightness);
            }
            isProcessing = false;
        }
        
        // Datenpunkte erstellen, falls nicht vorhanden
        createState(datapointOnOff, false, {
            name: 'Lichterkette On/Off',
            type: 'boolean',
            role: 'switch',
            read: true,
            write: true,
            def: false
        }, () => {
            // Event Listener für Zustandsänderungen registrieren
            on({ id: datapointOnOff, change: 'ne' }, async (obj) => {
                const action = obj.state.val ? 'on' : 'off';
                actionQueue.push({ action, brightness: null });
                processQueue();
            });
        });
        
        createState(datapointBrightness, 100, {
            name: 'Lichterkette Helligkeit',
            type: 'number',
            role: 'level.dimmer',
            read: true,
            write: true,
            def: 100,
            min: 0,
            max: 100
        }, () => {
            // Event Listener für Helligkeitsänderungen registrieren
            on({ id: datapointBrightness, change: 'ne' }, async (obj) => {
                const brightness = obj.state.val;
        
                if (brightness < 3) {
                    // Wenn Helligkeit unter 3%, Lichterkette ausschalten
                    console.log('Helligkeit unter 3% - Lichterkette wird ausgeschaltet.');
                    setState(datapointOnOff, false); // Setzt On/Off-Datenpunkt auf false
                } else {
                    // Ansonsten Helligkeit setzen
                    actionQueue.push({ action: 'on', brightness });
                    processQueue();
                }
            });
        });
        
        // Debugging: Manuelle Steuerung
        (async () => {
            try {
                console.log('Teste Steuerung: Einschalten mit 50% Helligkeit...');
                actionQueue.push({ action: 'on', brightness: 50 });
                processQueue();
        
                console.log('Teste Steuerung: Ausschalten...');
                actionQueue.push({ action: 'off', brightness: null });
                processQueue();
            } catch (error) {
                console.error('Fehler bei manueller Steuerung:', error);
            }
        })();
        
        
        OliverIOO Offline
        OliverIOO Offline
        OliverIO
        schrieb am zuletzt editiert von
        #3

        @fabian1 sagte in Obi Bluetooth Lichterkette in IOBroker integrieren:

        bluetoothctl

        das hier ist die javascript bibliothek die direkten zugriff auf den bluetooth stack bereitstellt.. ob das da so einfach ist wie mit bluetoothctl, weiß ich nicht.
        https://www.npmjs.com/package/node-ble

        ich hab mir mal was mit dbus events gebaut, die auf bluetooth events horchen.
        das war ein krampf. das war allerdings auch nicht in javascript sondern mit python.

        Meine Adapter und Widgets
        TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
        Links im Profil

        F 1 Antwort Letzte Antwort
        1
        • OliverIOO OliverIO

          @fabian1 sagte in Obi Bluetooth Lichterkette in IOBroker integrieren:

          bluetoothctl

          das hier ist die javascript bibliothek die direkten zugriff auf den bluetooth stack bereitstellt.. ob das da so einfach ist wie mit bluetoothctl, weiß ich nicht.
          https://www.npmjs.com/package/node-ble

          ich hab mir mal was mit dbus events gebaut, die auf bluetooth events horchen.
          das war ein krampf. das war allerdings auch nicht in javascript sondern mit python.

          F Offline
          F Offline
          Fabian1
          schrieb am zuletzt editiert von
          #4

          @oliverio node-ble und noble habe ich zuerst versucht, das hat überhaupt nicht funktioniert, darum bin ich dann auf dbus gewechselt und das hätte auch super funktioniert, wenn ich rausgefunden hätte, wie ich die Befehle im richtigen Format sende. :) Die Lichterkette erwartet nämlich strings und das habe ich einfach nicht hinbekommen und meine online suche hat gezeigt, ich bin da nicht der Einzige. :D Irgendwie ist alles was mit bluetooth zu tun hat immer ein absoluter krampf!

          Ich habe jetzt eine neue Version die 100% zuverlässig ist, falls das jemand gebrauchen kann: (diese benutzt das gatttool)

          const { exec } = require('child_process');
          
          // MAC-Adresse des Geräts und Handle der GATT-Characteristic
          const deviceAddress = '24:35:02:27:DE:6E';
          const characteristicHandle = '0x0025';
          
          // Pfade zu den Datenpunkten
          const datapointOnOff = 'javascript.0.MeineVariablen.Lichterkette_OnOff';
          const datapointBrightness = 'javascript.0.MeineVariablen.Lichterkette_Helligkeit';
          
          // Warteschlange und Status
          const actionQueue = [];
          let isProcessing = false;
          
          // Funktion: Shell-Befehl ausführen mit Debugging und Timeout
          function executeCommand(command, timeout = 10000) {
              console.log(`Führe aus: ${command}`);
              return new Promise((resolve, reject) => {
                  const process = exec(command, (error, stdout, stderr) => {
                      if (error) {
                          console.error(`Fehler: ${stderr || error.message}`);
                          return reject(stderr || error.message);
                      }
                      console.log(`Erfolg: ${stdout}`);
                      resolve(stdout.trim());
                  });
          
                  setTimeout(() => {
                      process.kill('SIGTERM');
                      reject('Befehl abgebrochen (Timeout)');
                  }, timeout);
              });
          }
          
          // Funktion: GATT-Wert schreiben mit Wiederholungslogik
          async function writeGattValueWithRetries(value, maxRetries = 10, initialDelay = 500) {
              let attempts = 0;
              let delay = initialDelay;
          
              while (attempts < maxRetries) {
                  try {
                      console.log(`Versuch ${attempts + 1}/${maxRetries}: Sende GATT-Befehl`);
                      await writeGattValue(value);
                      console.log('Befehl erfolgreich gesendet.');
                      return; // Erfolgreich, keine weiteren Versuche notwendig
                  } catch (error) {
                      attempts++;
                      console.error(`Fehler beim Senden (Versuch ${attempts}):`, error);
                      if (attempts >= maxRetries) {
                          console.error('Maximale Anzahl an Versuchen erreicht. Abbruch.');
                          throw new Error('GATT-Befehl konnte nach mehreren Versuchen nicht gesendet werden.');
                      }
                      console.log(`Warte ${delay} ms vor erneutem Versuch...`);
                      await new Promise((resolve) => setTimeout(resolve, delay));
                      delay *= 2; // Verzögerung verdoppeln
                  }
              }
          }
          
          // Funktion: GATT-Wert schreiben
          async function writeGattValue(value) {
              const command = `gatttool -b ${deviceAddress} --char-write-req --handle=${characteristicHandle} --value="${value}"`;
              console.log(`Sende GATT-Befehl: ${command}`);
              try {
                  const output = await executeCommand(command);
                  console.log(`GATT-Befehl erfolgreich: ${output}`);
              } catch (error) {
                  console.error('Fehler beim Schreiben auf die GATT-Characteristic:', error);
                  throw new Error('GATT-Wert konnte nicht geschrieben werden.');
              }
          }
          
          // Funktion: Helligkeit berechnen
          function calculateBrightnessHex(brightnessPercent) {
              const brightnessHex = Math.round((brightnessPercent / 100) * 0x64).toString(16).padStart(2, '0');
              return `030101${brightnessHex}`;
          }
          
          // Funktion: Lichterkette steuern
          async function controlLight(action, brightness = null) {
              try {
                  if (action) {
                      if (brightness !== null) {
                          const brightnessValue = calculateBrightnessHex(brightness);
                          await writeGattValueWithRetries(brightnessValue);
                      } else {
                          const value = action === 'on' ? '01010101' : '01010100';
                          await writeGattValueWithRetries(value);
                      }
                  }
              } catch (error) {
                  console.error('Fehler bei der Steuerung der Lichterkette:', error);
              }
          }
          
          // Funktion: Warteschlange abarbeiten
          async function processQueue() {
              if (isProcessing) return; // Verhindert gleichzeitige Verarbeitung
          
              isProcessing = true;
              while (actionQueue.length > 0) {
                  const { action, brightness } = actionQueue.shift(); // Nächsten Befehl aus der Warteschlange holen
                  console.log(`Verarbeite Aktion: ${action}, Helligkeit: ${brightness}`);
                  await controlLight(action, brightness);
              }
              isProcessing = false;
          }
          
          // Datenpunkte erstellen, falls nicht vorhanden
          createState(datapointOnOff, false, {
              name: 'Lichterkette On/Off',
              type: 'boolean',
              role: 'switch',
              read: true,
              write: true,
              def: false
          }, () => {
              // Event Listener für Zustandsänderungen registrieren
              on({ id: datapointOnOff, change: 'ne' }, async (obj) => {
                  const action = obj.state.val ? 'on' : 'off';
                  actionQueue.push({ action, brightness: null });
                  processQueue();
              });
          });
          
          createState(datapointBrightness, 100, {
              name: 'Lichterkette Helligkeit',
              type: 'number',
              role: 'level.dimmer',
              read: true,
              write: true,
              def: 100,
              min: 0,
              max: 100
          }, () => {
              // Event Listener für Helligkeitsänderungen registrieren
              on({ id: datapointBrightness, change: 'ne' }, async (obj) => {
                  const brightness = obj.state.val;
          
                  if (brightness < 3) {
                      // Wenn Helligkeit unter 3%, Lichterkette ausschalten
                      console.log('Helligkeit unter 3% - Lichterkette wird ausgeschaltet.');
                      setState(datapointOnOff, false); // Setzt On/Off-Datenpunkt auf false
                  } else {
                      // Ansonsten Helligkeit setzen
                      actionQueue.push({ action: 'on', brightness });
                      processQueue();
                  }
              });
          });
          
          // Debugging: Manuelle Steuerung
          /*(async () => {
              try {
                  console.log('Teste Steuerung: Einschalten mit 50% Helligkeit...');
                  actionQueue.push({ action: 'on', brightness: 50 });
                  processQueue();
          
                  console.log('Teste Steuerung: Ausschalten...');
                  actionQueue.push({ action: 'off', brightness: null });
                  processQueue();
              } catch (error) {
                  console.error('Fehler bei manueller Steuerung:', error);
              }
          })();
          */
          
          1 Antwort Letzte Antwort
          0
          Antworten
          • In einem neuen Thema antworten
          Anmelden zum Antworten
          • Älteste zuerst
          • Neuste zuerst
          • Meiste Stimmen


          Support us

          ioBroker
          Community Adapters
          Donate

          887

          Online

          32.4k

          Benutzer

          81.5k

          Themen

          1.3m

          Beiträge
          Community
          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
          ioBroker Community 2014-2025
          logo
          • Anmelden

          • Du hast noch kein Konto? Registrieren

          • Anmelden oder registrieren, um zu suchen
          • Erster Beitrag
            Letzter Beitrag
          0
          • Home
          • Aktuell
          • Tags
          • Ungelesen 0
          • Kategorien
          • Unreplied
          • Beliebt
          • GitHub
          • Docu
          • Hilfe