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.