Blog der Heimetli Software AG

Ansteuerung von RGB-Lampen von Philips

Per Zufall bin ich auf das Hue-System von Philips gestossen. Es gibt ein Starterkit mit einer Bridge und drei Lampen dazu.

Die Bridge wird am Ethernet angeschlossen und übersetzt JSON-Kommandos in Steuerbefehle für die Lampen.

Selbstverständlich gibt es einige Apps für das Hue-System, aber ich wollte es mit eigenen Programmen bedienen.

Ein erster Versuch mit curl gelang sofort:

curl -X PUT -d '{"on":false}' http://192.168.1.121/api/XsbA-ZrmWbp42eeXu5j8otqJlPnIIX1Mxbh6rwn8/lights/2/state

Dieser Befehl bewirkt, dass die Lampe Nummer 2 ausgeschaltet wird.

Logischerweise ist die IP abhängig von der Netzwerk-Konfiguration. Bei mir wurde sie per DHCP vergeben.

Der seltsame String in der URL wird bei der Einrichtung der Bridge vergeben und dient als Authentifizierung für den User.

Genauere Informationen zur Einrichtung und zur API gibt es bei https://www.developers.meethue.com

Das ist zwar schon recht cool, aber noch lieber steuere ich solche Geräte mit einem eigenen Programm.

Es stellte sich heraus, dass das auch nicht allzu kompliziert ist, obwohl sich die Bridge nicht wirklich an das HTTP-Protokoll hält. Die Bridge gibt weder eine Content-Length an, noch benutzt sie Chunked Transfer Encoding. Sie schickt als Response mehrere TCP-Frames und schliesst am Schluss die Verbindung.

Zufällige Farbwechsel für das Hue Starterkit

Das folgende Programm wechselt alle paar Sekunden die Farbe einer Hue-Lampe. Die Zeit wird zufällig bestimmt und liegt im Bereich von 0 bis 20 Sekunden. Die Farbe wird ebenfalls zufällig bestimmt.

/******************************************************************************/
/*                                                                            */
/*                                                          FILE: huectrl.cpp */
/*                                                                            */
/*     Random light show on a Philips Hue starter kit                         */
/*     ==============================================                         */
/*                                                                            */
/*     V0.01   16-AUG-2017   Te                                               */
/*                                                                            */
/******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

// Customize these constants for your system
const int   LAMPS   =  3 ;
const int   DELAY   = 20 ;

const char *key     = "XsbA-ZrmWbp42eeXu5j8otqJlPnIIX1Mxbh6rwn8" ;
const char *ip      = "192.168.1.121" ;

// Do not touch this constant
const char *request = "PUT /api/%s/lights/%d/state HTTP/1.1\r\n"
                      "Host: %s\r\n"
                      "User-Agent: huectl/0.01\r\n"
                      "Accept: */*\r\nContent-Length: %d\r\n"
                      "Content-Type: application/x-www-form-urlencoded\r\n\r\n"
                      "%s" ;

/***********************************************************************/
 bool SendJSON( const char *host, int port, int lamp, const char *json )
/***********************************************************************/

{
    // Create a new socket
    int sock = socket( AF_INET, SOCK_STREAM, 0 ) ;

    if( sock == -1 )
    {
       printf( "Error: socket failed" ) ;
       return false ;
    }

    // Bind it to the local address
    struct sockaddr_in local ;

    local.sin_family      = AF_INET ;
    local.sin_addr.s_addr = htonl( INADDR_ANY ) ;
    local.sin_port        = htons( 0 ) ;

    if( bind(sock,(struct sockaddr *)&local,sizeof(local)) == -1 )
    {
       printf( "Error: bind failed" ) ;
       close( sock ) ;
       return false ;
    }

    // Connect to the remote address
    struct sockaddr_in remote ;

    remote.sin_family      = AF_INET ;
    remote.sin_addr.s_addr = inet_addr( host ) ;
    remote.sin_port        = htons( port ) ;

    if( connect(sock,(struct sockaddr *)&remote,sizeof(remote)) == -1 )
    {
       printf( "Error: connect failed" ) ;
       close( sock ) ;
       return false ;
    }

    char buffer[1024] ;
    sprintf( buffer, request, key, lamp, host, strlen(json), json ) ;

    if( send(sock,buffer,strlen(buffer),0) != (ssize_t)strlen(buffer) )
    {
       printf( "Error: send failed" ) ;
       close( sock ) ;
       return false ;
    }

    int length = 0 ;
    int len ;
    while( (len=recv(sock,buffer+length,sizeof(buffer)-length,0)) > 0 )
       length += len ;

    buffer[length]  = '\0' ;
    const char *ptr = strstr( buffer, "\r\n\r\n" ) ;

    if( ptr == NULL )
       printf( "%s\n", buffer ) ;
    else
       printf( "%s\n", ptr + 4 ) ;

    close( sock ) ;

    return true ;
}

/**********/
 int main()
/**********/

{
   int  delays[LAMPS] ;
   char json[64] ;
   int  lamp ;

   // Initialize the delay table
   srandom( time(NULL) ) ;
   for( int i = 0; i < LAMPS; i++ )
      delays[i] = random() % DELAY ;

   for( ;; )
   {
      // Search the minimal delay in the table
      int sec = delays[0] ;

      for( int i = 0; i < LAMPS; i++ )
      {
         if( delays[i] <= sec )
         {
            sec  = delays[i] ;
            lamp = i + 1 ;
         }
      }

      // Prepare the delay table for the next round
      for( int i = 0; i < LAMPS; i++ )
      {
         delays[i] -= sec ;

         if( delays[i] == 0 )
            delays[i] = random() % DELAY ;
      }

      sleep( sec ) ;

      // Set the hue of the selected lamp to a random value
      sprintf( json, "{\"hue\":%ld}", random() % 65535 ) ;
      SendJSON( ip, 80, lamp, json ) ;
   }

   return 0 ;
}

Oben im Programm sind ein paar Konstanten die Sie an Ihre Installation anpassen müssen.

Ganz sicher gilt das für ip und key. Mit DELAY können Sie die Geschwindigkeit bestimmen.

Wenn Sie mehr oder weniger Lampen haben, dann sollten sie auch LAMPS entsprechend setzen.

Nicht abtippen !

Sie können die Source von huectl.cpp hier herunterladen.