Divoom Aurabox + Timebox + TimeboxEvo [Update, kommunizieren funktioniert]

Begonnen von schwatter, 25 Dezember 2017, 23:37:43

Vorheriges Thema - Nächstes Thema

schwatter

Hast du zufällig ein externes Dongle zum testen?
Ansonsten hat das bei mir super geklappt unter Stretch.
Anleitung oben. Audio + RFCOMM funktioniert top.
Es darf nur nich zur gleichen Zeit gesendet werden,
sonst hängt sich Audio auf und verzerrt.

mumpitzstuff

Ich habe einen externen Dongle dran hängen, der auch schon mit einem Lautsprecher gekoppelt ist. Deine Dinge habe ich eigentlich alle übernommen, bei mir führt es aber leider nicht zum Erfolg. Ich muss mal gucken ob ich irgendwo temporär noch einen anderen Raspberry aufsetze, damit ich mir nichts auf meinem Produktivsystem platt mache...

schwatter

#17
Ok. Was mir noch einfällt. Ich nutze bluez-alsa.
Das funktioniert sehr gut. Hatte mit Pulseaudio
+ Bluetooth nur Probleme.

edit:

apt-get install bluealsa

mumpitzstuff

Ich benutze Pulseaudio und habe damit eigentlich keine Probleme.

mumpitzstuff

Mit einem neu aufgesetzten Pi gehts mit deiner Anleitung. Die Installation von pybluez sollte aber erst vor pillow erfolgen, sonst fehlen Header, die durch die anderen Dinge erst noch installiert werden müssen.

Danke schon mal an dieser Stelle. Werde jetzt die nächsten Tage versuchen den Code nach Perl zu transferieren und ein raw send zu implementieren.

mumpitzstuff

#20
Sry hat leider länger gedauert als geplant, da ich einige Zeit gebraucht habe die Instabilitäten zu beseitigen. Irgendwie musste ich am Ende ein flush() auf das Filehandle ausführen, um die daten an die Timebox zu schicken. Alle anderen Versuche waren irgendwie instabil. Was mir auch noch nicht gelungen ist, ist das zurück lesen der Antwort. Daran muss ich noch arbeiten, im Moment gehts aber noch ohne.

Damit es funktioniert, muss folgendes vorab installiert werden:

sudo apt install libnet-bluetooth-perl

#!/usr/bin/perl
use strict;
use warnings;
use Net::Bluetooth;

my $socket;
my $TIMEBOX;

sub listDevices()
{
  print "Search for devices...\n\n"; 

  my $device_ref = get_remote_devices();
 
  foreach my $addr (keys %$device_ref)
  {
    print "Address: $addr Name: $device_ref->{$addr}\n";
  }

  print "\ndone\n";
}

sub connectTB($)
{
  my $device = shift;
  my $ret;
  my $success = 0;

  print "Create RFCOMM client ($device)...\n";

  $socket = Net::Bluetooth->newsocket("RFCOMM");
  return $success unless(defined($socket));
 
  if (0 != $socket->connect($device, 4))
  {
    $socket->close();
    return $success;
  }

  $TIMEBOX = $socket->perlfh();
  #$TIMEBOX->autoflush(1);

  sysread($TIMEBOX, $ret, 256);
  if (defined($ret))
  {
    $ret =~ s/[^[:print:]]//g;
    print "Device answer: $ret";

    if ('HELLO' eq $ret)
    {
      $success = 1;
    }
    else
    {
      close($TIMEBOX);
      $socket->close();
    }
  }
 
  print "\ndone\n";

  return $success;
}

sub disconnectTB()
{
  close($TIMEBOX);
  $socket->close();
}

sub sendRawTB($$)
{
  my $data = shift;
  my $timeout = shift;
  my $ret;
  my $retry = 0;
 
  print "Send raw command: $data\n";

  $data =~ s/((?:[0-9a-fA-F]{2})+)/pack('H*', $1)/ge;
 
  do
  {
    #print $TIMEBOX $data."\n";
    syswrite($TIMEBOX, $data);

    sysread($TIMEBOX, $ret, 256);
    if (defined ($ret))
    {
      $ret = unpack('(H2)*', $ret);
      $ret =~ s/[^[:print:]]+//g;
      print "Device answer: $ret";
    }

    $retry++;
  } while (($retry <= 3) && (!defined($ret) || '01' ne $ret));

  sleep($timeout);

  print "\ndone\n";
}

if (connectTB('11:75:58:7A:60:3B'))
{
  sendRawTB('0104004507500002', 5);
  sendRawTB('0104004503044a0002', 5);
  sendRawTB('0104004500490002', 5);
  disconnectTB();
}


Was auch immer blöd ist, ist die Tatsache, das nach dem disconnect immer das Bluetooth Zeichen rumblinkt. Das könnte man nur verhindern, wenn man innerhalb eines Moduls eine dauerhafte Verbindung aufbaut. Aber alles zu seiner Zeit...

TODO:
- Antworten der Timebox zurücklesen und eventuell Senden wiederholen
- Protokoll implementieren, so das beliebige Daten Escaped werden und die Nachricht korrekt mit Header, Länge und Checksumme zusammen gesetzt wird.
- verschiedene Kommandos der App sniffen und mit dem Script nachvollziehen zu können (setzen von Bilder, Timern, Einstellungen usw.)
- Unterschiede zwischen verschiedenen Divoom Produkten ermitteln und wenn möglich Nachrichten entsprechend anpassen oder Liste der möglichen Kommandos Gerätespezifisch ablegen

schwatter

Super! Heute leider keine Zeit mehr zum testen. Morgen komme ich aber dazu.

schwatter

#22
Ok, habe meine MAC eingetragen und bekomme

Create RFCOMM client (XX:XX:XX:XX:XX:XX)...
connect error: Host is down


Mal schauen woran es liegen könnte. Habe danach direkt den PythonCli von derHeinz gestartet.
Lief normal durch.

edit:

Ok, mein Fehler, es funktioniert. Falsche MAC von wem auch immer, deine war es nicht.


root@raspiFhem:/opt/fhem/divoom# perl divoom.pl
Create RFCOMM client (XX:XX:XX:XX:XX:XX)...
done
Send raw command: 0104004507500002
done
Send raw command: 0104004503044a0002
done
Send raw command: 0104004500490002
done
Send raw command: 0104004503064c0002
done
root@raspiFhem:/opt/fhem/divoom#


edit2:

Bei mir ist alles ok, habe kein disconnect der Box (blinkendes Bluetoothzeichen).

mumpitzstuff

Habe den Code oben noch etwas robuster gestaltet. Rückgaben werden jetzt ausgewertet und die Nachricht gegebenenfalls wiederholt. Der nächste Schritt wird sein, eine Nachricht zusammen zu setzen und beispielsweise ein Bild zu schicken. Muss aber gucken wann ich die Zeit dazu finde...

schwatter

#24
Wäre es viel Aufwand für dich, die Basics als Modul zu bauen? So das ich folgendes aus Fhem setzen könnte.

set Aurabox Blackscreen
set Aurabox Equalizer
set Aurabox .............


Damit wären schon diverse Scenen möglich.


edit:

Habe gerade das Script nochmal während der Audioausgabe getestet.  Funktioniert auch! :) Bedeutet, folgendes wäre machbar,

Box steht auf Uhr, ich schalte auf MPD (Audio spielt) und die Anzeige wechselt per DOIF z.B auf Equalizer.

schwatter

Hab vorerst ein paar Scripte erstellt und eingebunden. Tolle Sache  :)


mumpitzstuff

Da soll es ja hingehen. Ich bastel aber erst noch ein wenig low Level rum, das macht es einfacher die Basics zu testen. Das Modul kommt dann noch...

mumpitzstuff

#27
Hab wieder ein paar Minuten gefunden und folgendes hinzugefügt:

- wenn keine Antwort kommt (falsches Kommando), dann bleibt das Script nicht mehr stehen
- convertRawToPlain() entfernt von einer Raw Nachricht Prefix, Postfix und Checksumme und Unescaped die Daten (das Ergebnis kann für die folgende Funktion verwendet werden)
- sendPlainTB(): Damit kann man nur Daten übergeben und die Funktion fügt, Prefix, Postfix, Checksumme und eventuell notwendige Escape Sequenzen dazu.

Damit kann man jetzt auch mal in einer Schleife verschiedene Dinge durchprobieren lassen und sieht dann, ob das Gerät diese annimmt oder ablehnt.

#!/usr/bin/perl
use strict;
use warnings;
use Net::Bluetooth;
use IO::Select;

sub listDevices();
sub connectTB($);
sub disconnectTB();
sub sendRawTB($$);
sub sendPlainTB($$);
sub convertRawToPlain($);

my $socket;
my $TIMEBOX;

sub listDevices()
{
  print "Search for devices...\n\n"; 

  my $device_ref = get_remote_devices();
 
  foreach my $addr (keys %$device_ref)
  {
    print "Address: $addr Name: $device_ref->{$addr}\n";
  }

  print "done\n\n";
}

sub connectTB($)
{
  my $device = shift;
  my $ret;
  my $success = 0;

  print "Create RFCOMM client ($device)...\n";

  $socket = Net::Bluetooth->newsocket("RFCOMM");
  return $success unless(defined($socket));
 
  if (0 != $socket->connect($device, 4))
  {
    $socket->close();
    return $success;
  }

  $TIMEBOX = $socket->perlfh();
 
  sysread($TIMEBOX, $ret, 256);
  if (defined($ret))
  {
    $ret =~ s/[^[:print:]]//g;
    print "Device answer: $ret";

    if ('HELLO' eq $ret)
    {
      $success = 1;
    }
    else
    {
      close($TIMEBOX);
      $socket->close();
    }
  }
 
  print "\ndone\n\n";

  return $success;
}

sub disconnectTB()
{
  close($TIMEBOX);
  $socket->close();
}

sub sendRawTB($$)
{
  my $data = shift;
  my $timeout = shift;
  my $ret;
  my $retry = 0;
  my $select = IO::Select->new($TIMEBOX);
 
  print "Send raw command: $data\n";

  $data =~ s/((?:[0-9a-fA-F]{2})+)/pack('H*', $1)/ge;
 
  do
  {
    syswrite($TIMEBOX, $data);

    if ($select->can_read(0.1))
    {
      sysread($TIMEBOX, $ret, 256);
      if (defined ($ret))
      {
        $ret = unpack('(H2)*', $ret);
        $ret =~ s/[^[:print:]]+//g;
        print "Device answer: $ret";
      }
    }

    $retry++;
  } while (($retry <= 3) && (!defined($ret) || '01' ne $ret));

  if ($retry > 3)
  {
    print "Failed!";
  }
  else
  {
    sleep($timeout);
  }

  print "\ndone\n\n";
}

sub sendPlainTB($$)
{
  my $data = shift;
  my $timeout = shift;
  my $crc = 0;
  my $ret;
  my $retry = 0;

  print "Send plain command: $data\n";

  # calculate crc
  while ($data =~ /(..)/g)
  {
    $crc += hex($1);
  }

  # add crc
  $data .= sprintf("%02x", ($crc & 0xFF)).sprintf("%02x", (($crc >> 8) & 0xFF)); 

  # escape data
  $data =~ s/(01|02|03)(?{ if (0 == ($-[0] & 1)) {'030'.(3+$1)} else {$1} })/$^R/g;

  # add prefix and postfix
  $data = '01'.$data.'02';

  print "Generated raw command: $data\n";

  sendRawTB($data, $timeout);
}

sub convertRawToPlain($)
{
  my $data = shift;

  print $data."\n";

  # remove prefix and postfix
  $data = substr($data, 2, -2);

  # unescape data
  $data =~ s/(03(04|05|06))(?{ if (0 == ($-[0] & 1)) {'0'.($2-3)} else {$1} })/$^R/g;
 
  # remove checksum
  $data = substr($data, 0, -4);

  print $data."\n";

  return $data;
}

if (connectTB('11:75:58:7A:60:3B'))
{
  sendPlainTB('04004509', 5);
  sendPlainTB('04004508', 5);
  sendPlainTB('04004507', 5);
  sendPlainTB('04004506', 5);
  sendPlainTB('04004505', 5);
  sendPlainTB('04004504', 5);
  sendPlainTB('04004503', 5);
  sendPlainTB('04004502', 5);
  sendPlainTB('04004501', 5);
  sendPlainTB('04004500', 5);
  disconnectTB();
}


Falls du die von dir erstellten Sequenzen schon mal nutzen willst, kannst du folgendes machen:

Du erstellst dir mit dem Code von mir eine Datei lib.pl und entfernst unten die letzten Zeilen mit dem connect usw. so das nur noch die subs drin bleiben. Dann erstellst du dir Dateien 1.pl und 2.pl und bindest die lib oben ein:

require "lib.pl"

Danach schreibst du dir deine Sequenzen zusammen.

In Fhem kannst du dann diese Scripte non blocking so aufrufen:

{fhem("perl /opt/fhem/divoom/1.pl")}

Denk aber an die Zugriffsrechte, die müssen richtig gesetzt sein.

PS: Ich habs nicht selbst probiert, müsste aber in der Theorie funktionieren.

schwatter

#28
Super, update  :) Schaue ich mir an. Ich hab es so ähnlich laufen, nur ohne verlinken.
Einfach pro Kommando ein Script. Läuft ja auf das selbe hinaus erstmal?

Der Hauptdummy (AuraBox),greift auf diverse Dummys zu, von denen jeder ein Script startet.

AuraBox:ExampleAni {system("sudo perl /opt/fhem/divoom/divoomExampleAni.pl")}

edit:

Die Ausgabe in der Konsole gefällt mir, das wird :)


mumpitzstuff

Das Einbinden hat den Vorteil, das du nur 1 File updaten musst.