Unterstützung für Osram Lightify Socket Schnittstelle gesucht

Hallo zusammen,

ich versuche, ein Modul für das Osram Lightify Gateway zu schreiben. Meine ersten Versuche haben gezeigt, dass man über die Lightify REST Api recht bequem auf die Leuchten, Gruppen etc. zugreifen kann. Nur leider hat die Schnittstelle zwei Nachteile:

[ul]
[li]zum einen ist sie cloudbasiert, d.h. es muss eine Internetverbindung bestehen,
[/li][li]zum anderen fehlt in der API eine eindeutige Gerätekennung (z.B. MAC Adresse), um in IPS eine Zuordnung zu den gefundenen Leuchten treffen zu können.
[/li][/ul]
Daher versuche ich alternativ über die Socketschnittstelle (Port 4000) auf das Gateway zuzugreifen. Nur leider ist die Hex Welt nicht meine :smiley:

Die gute Nachricht: in GitHub gibt es ein JavaScript Projekt, das genau dieses macht. Nur leider ist auch JavaScript nicht meine Welt.

Ich bräuchte etwas Anschubhilfe, um die richtigen Wege zu finden.

Im ersten Schritt möchte ich gerne das Kommando ‚0x13‘ absetzen zum Auslesen aller Nodes.

Ich habe versucht, 0x13 in einen String zu wandeln (hexToString) und mit CSCK_SendText an die Client Socket Instanz zu schicken. Aber so einfach ist es wohl nicht.:rolleyes:

Hat jemand einen Tipp, wie ich ein Kommando richtig absetze? Leider gibt es wohl keine Dokumentation der Schnittstelle und auch eine Anfrage bei Osram blieb bislang erfolglos. Aber hier gelingt es ja…

Also muss es auch mit IPS gehen :loveips:

Gruß

Burkhard

Ich habe versucht, 0x13 in einen String zu wandeln (hexToString) und mit CSCK_SendText an die Client Socket Instanz zu schicken. Aber so einfach ist es wohl nicht.

… stimmt, ganz so einfach ist es leider doch nicht:

  • neben dem Client Socket benötigst du noch
  1. einen Timer der die Daten liest
  2. eine Register Variable Instanz
  3. ein Script, dass bei RegisterVariable ausgeführt wird

siehe
Instanzen — IP-Symcon :: Automatisierungssoftware
RegisterVariable — IP-Symcon :: Automatisierungssoftware

Nachdem ich schon länger ein Osram Gateway ungenutzt herumliegen habe, habe ich Heute Nachmittag kleines Script geschrieben, mit dem man schon einmal die Lampen auslesen und einzeln schalten kann.

Timer script


$data = chr(0x0B).chr(0x00).chr(0x00).chr(0x13).chr(0x00).chr(0x00).chr(0x00).chr(0x00).chr(0x01).chr(0x00).chr(0x00).chr(0x00).chr(0x00);
CSCK_SendText(55450, $data);

Oscam functions [script]


# Commands
# 13 all light status (returns list of light address, light status, light name)
# 1e group list (returns list of group id, and group name)
# 26 group status (returns group id, group name, and list of light addresses)
# 31 set group luminance
# 32 set group onoff
# 33 set group temp
# 36 set group colour
# 68 light status (returns light address and light status (?))


function SetBulbState($addr, $State) {
	$data = chr(0x0).chr(0x32).chr(0x0).chr(0x0).chr(0x0).chr(0x0).BulbAddressToChr($addr).chr(($State == "1") ? 1 : 0);
	CSCK_SendText(55450, $data);
}


function ReadBulbData($data, $Single = false) {
	$buffer = array();
	$addr = BulbChrToAddress(substr($data, 2, 8));
	$len = 42; // We expect 42 bytes per light, but some lights may have bigger space
	$found = false;

	for($i = 26; $i < strlen($data); $i++) {
		if ($found && $data{$i}!==chr(0)) {
			$len = $i;
			break;
		}
		if ($data{$i}==chr(0)) $found = true;
	}

	if ($Single == false) {
		$buffer['Name'] = trim(substr($data, 26, 15));
		$buffer['Firmware'] = GetBulbFirmware($data{11}).GetBulbFirmware($data{12}).GetBulbFirmware($data{13}).'xx';
	}

	$buffer['State'] = (ord($data{18})==1 ? true : false);
	$buffer['Brightness'] = ord($data{19});
	$buffer['Temperature'] = hexdec(dechex(ord($data{21})).dechex(ord($data{20})));
	$buffer['Red'] = ord($data{22});
	$buffer['Green'] = ord($data{23});
	$buffer['Blue'] = ord($data{24});

	return array('length' => $len, 'addr' => $addr, 'data' => $buffer);
}


function GetBulbFirmware($data) {
	$String = ord($data);
	return ((strlen($String) == 1) ? "0".$String : $String);
}


function BulbAddressToChr($addr) {
	$addr = explode(":", $addr);
	$Result = "";

	foreach($addr as $value) {
		$Result = chr(hexdec($value)).$Result;
	}

	return $Result; 
}


function BulbChrToAddress($chr) {
	$Result = array();

	for($i = 0; $i < 8; $i ++) {
		$hex = dechex(ord(substr($chr, $i, 1)));
		if (strlen($hex)==1) $hex = "0".$hex;
		$Result[] = $hex;
	}

	return implode(":", array_reverse($Result));
}

Osram [decode]


//Global includes
include(IPS_GetScriptFile(50035)); //Osram fucntions [script]

if ($_IPS['SENDER'] == "RegisterVariable") {
	$data= $_IPS['VALUE'];

	if (strlen($data) > 11) {
		$cnt = ord($data{9});
		$data = substr($data, 11);

		for ($i = 0; $i < $cnt; $i++) {
      $buffer = ReadBulbData(substr($data, 0, 60)); 
      $data = substr($data, $buffer['length']); // but cut out actual data size 
      $Bulb[$buffer['addr']] = $buffer['data'];
		}
	}

	print_r($Bulb);
}

Ist zwar quick & dirty und ausbaufähig, jedoch zum Testen schon einmal ein erster Anfang.

ein wenig überarbeitet:
Im Scripts die defines entsprechend euren Werten adaptieren.

  • Client Socket Instanz verworfen, die Daten werden aktuell direkt eingelesen
  • folgende Funktionen stehen zur Verfügung:
    GetBulbList () -> liest den Werte aller Lampen und erstellt entsprechende Variablen unter OSR_CATEGORYLIGHT
    SetBulbState($MAC, true/false) -> Schaltet die Lampe mit der $MAC ein. bzw. aus
    $State = GetBulbInfo($MAC) -> Liest die Werte der Lampe mit der $MAC ein und liefert den ein/aus Status der Lampe
    SetBulbBrightness($MAC, $Value) -> Dimmt die Lampe $MAC aud $Value (0 - 100, wobei 0 die Lampe ausschaltet)

Osram Funktionen


<?

 //Start writing your scripts between the brackets

# Commands
# 13 all light status (returns list of light address, light status, light name)
# 1E group list (returns list of group id, and group name)
# 26 group status (returns group id, group name, and list of light addresses)
# 31 set group brightness
# 32 set group on/off
# 33 set group temp
# 36 set group colour
# 68 light status (returns light address and light status)

//Local definitions
define('OSR_GATEWAYIP', "192.168.1.1");
define('OSR_CATEGORYLIGHT', 12842);
define('OSR_CATEGORYGROUP', 32768);

define('OSR_GETLIGHTLIST', 19);
define('OSR_GETGROUPLIST', 0x1E);
define('OSR_GETLIGHTINFO', 104);
define('OSR_SETLIGHTTOGGLE', 50);
define('OSR_TRANSITION', 0x5);


function OpenSocket($ip) {
	$socket = fsockopen($ip, 4000, $errno, $errstr, 5);

	if(!$socket) {
		throw new Exception($errno .": ".$errstr);
		return false;
	}
	stream_set_timeout($socket, 1);

	return $socket;
}


function CloseSocket($socket) {
	fclose($socket);
}


function SendData($socket, $data, $size) {
	fwrite($socket, chr(strlen($data)).chr(0x00).$data);
	return fread($socket, $size);
}


function GetBulbInfo($MAC) {
	$socket = OpenSocket(OSR_GATEWAYIP);

	if ($socket != false) {
		$data = chr(0x0).chr(0x68).chr(0x0).chr(0x0).chr(0x0).chr(0x0).ConvertMACtoChr($MAC);
		$buffer = SendData($socket, $data, 1024);
		//IPS_LogMessage("length", strlen($buffer));

		if (strlen($buffer) >= 29) {
			$State = GetBulbData($buffer, false);
		}

		CloseSocket($socket);
		return (isset($State) ? $State : -1);
	}
}


function GetBulbList() {
	$socket = OpenSocket(OSR_GATEWAYIP);

	if ($socket != false) {
		$data = chr(0x00).chr(0x13).chr(0x00).chr(0x00).chr(0x00).chr(0x00).chr(0x01).chr(0x00).chr(0x00).chr(0x00).chr(0x00);
		$buffer = SendData($socket, $data, 4096);
		//fwrite($socket, $data);
		//$buffer = fread($socket, 4096);
		//IPS_LogMessage("length", strlen($buffer));

		if (strlen($buffer) >= 11) {
			$cnt = ord($buffer{9});
			$buffer = substr($buffer, 11);

			for ($i = 0; $i < $cnt; $i++) {
				$len = GetBulbData(substr($buffer, 0, 60), true);
				$buffer = substr($buffer, $len);
			}
		}

		CloseSocket($socket);
		return true;
	}

	return false;
}


function SetBulbState($MAC, $State) {
	$socket = OpenSocket(OSR_GATEWAYIP);

	if ($socket != false) {
		$data =chr(0x00).chr(0x32).chr(0x00).chr(0x00).chr(0x00).chr(0x00).ConvertMACtoChr($MAC).chr(($State == "1") ? 1 : 0);
		$buffer = SendData($socket, $data, 1024);

		CloseSocket($socket);
		return true;
	}

	return false;
}


function SetBulbBrightness($MAC, $Value) {
	$socket = OpenSocket(OSR_GATEWAYIP);

	if ($socket != false) {
		$data = chr(0x0).chr(0x31).chr(0x0).chr(0x0).chr(0x0).chr(0x0).ConvertMACtoChr($MAC).chr($Value).chr(OSR_TRANSITION).chr(0x0);
		$buffer = SendData($socket, $data, 1024);

		CloseSocket($socket);
		return true;
	}

	return false;
}


function GetBulbData($data, $list) {
	$MAC = ConvertChrToMAC(($list) ? substr($data, 2, 8) : substr($data, 11, 8));
	//IPS_LogMessage("MAC", $MAC);
	$State =-1;
	$eob = false;

	if ($list) {
		$len = 42; //We expect 42 bytes per light, but some lights may have bigger space

		for($i = 26; $i < strlen($data); $i++) {
			if ($eob && $data{$i} !== chr(0)) {
				$len = $i;
				break;
			}
			if ($data{$i} == chr(0)) $eob = true;
		}

		$id = CreateBulbVariables(OSR_CATEGORYLIGHT, (string)trim(substr($data, 26, 15)), $MAC, null, 0);
		CreateBulbVariables($id, "Firmware", (string)(GetBulbFirmware($data{11}).GetBulbFirmware($data{12}).GetBulbFirmware($data{13})."xx"), null, 6);
	} else {
		$Child = IPS_GetChildrenIDs(OSR_CATEGORYLIGHT);

		foreach($Child as $key => $value) {
			if (GetValue($value) == $MAC) {
				$data = substr($data, 3);
				$id = $value;
				break;
			}
		}
	}

	if (isset($id)) {
		$State = (bool)((ord($data{18}) == 1) ? true : false);
		CreateBulbVariables($id, "State",$State, "~Switch", 0);
		CreateBulbVariables($id, "Color Temperature", (int)hexdec(dechex(ord($data{21})).dechex(ord($data{20}))), null, 1);
		CreateBulbVariables($id, "Color Brightness", (int)ord($data{19}), "~Intensity.100", 2);
		CreateBulbVariables($id, "Red", (int)ord($data{22}), null, 3);
		CreateBulbVariables($id, "Green", (int)ord($data{23}), null, 4);
		CreateBulbVariables($id, "Blue", (int)ord($data{24}), null, 5);
	}

	return (($list) ? $len : $State);
}


function GetBulbFirmware($data) {
	$String = ord($data);
	return (strlen($String) == 1) ? "0".$String : $String;
}


function CreateBulbVariables($ParentID, $Name, $Value, $Profile, $pos) {
	$id = @IPS_GetVariableIDByName($Name, $ParentID);
	
	if ($id === false) {
		$id = IPS_CreateVariable(ConvertPHPTypeToIPS(gettype($Value)));
		IPS_SetName($id, $Name);
		IPS_SetParent($id, $ParentID);
		IPS_SetPosition($id, $pos);
		if ($Profile) IPS_SetVariableCustomProfile($id, $Profile);
	}

	SetValue($id, $Value);
	return $id;
}


function ConvertMACtoChr($MAC) {
	$MAC = explode(":", $MAC);
	$Result = "";

	foreach($MAC as $value) {
		$Result = chr(hexdec($value)).$Result;
	}

	return $Result; 
}


function ConvertChrToMAC($chr) {
	$Result = array();

	for($i = 0; $i < 8; $i++) {
		$hex = dechex(ord(substr($chr, $i, 1)));
		if (strlen($hex) == 1) $hex = "0".$hex;
		$Result[] = $hex;
	}

	return implode(":", array_reverse($Result));
}


function ConvertPHPTypeToIPS($type) {
	switch (strtolower($type)) {
		case "boolean": return 0;
		case "integer": return 1;
		case "float": return 2;
		case "string": return 3;
	}
}

?>

Besipiel Script


<?

//Start writing your scripts between the brackets

//Global includes
include(IPS_GetScriptFile(50035)); //Osram fucntions [script]

GetBulbList();
$MAC = GetValue(53031);
SetBulbState($MAC, true);
$State = GetBulbInfo($MAC);
var_dump($State);

?>

Hallo Whitesheep,

das ist ja der Hammer! Funktioniert auf Anhieb. Es ist genau das, was ich brauchte um weiterzukommen.

Kannst du mir noch verraten, wo man den Aufbau der Nachrichten nachlesen kann?

Die Informationen in deinem Beispiel entsprechen den Rest API Elementen

[ul]
[li]name,
[/li][li]firmwareVersion,
[/li][li]onOff,
[/li][li]temperature,
[/li][li]brightnessLevel,
[/li][li]color
[/li][/ul]

Daneben hätte ich noch gerne die übrigen Elemente aus der Rest API :):

[ul]
[li]deviceType
[/li][li]groupList
[/li][li]deviceID
[/li][li]manufacturer
[/li][li]saturation
[/li][li]modelName
[/li][li]online
[/li][li]hue
[/li][li]bmpClusters
[/li][/ul]

Ich hoffe, dass die eine oder andere Information noch enthalten ist. Es würde auch schon reichen, wenn die deviceID geliefert wird, dann könnte ich die Informationen ja über die Rest API ergänzen.

Besten Dank nochmals für deine tolle Unterstützung!

Burkhard

Hi,

da die Hue und die Lightify sich gegenseitig unterstützen, könntest du dir ja etwas abschauen, im/vom Hue-Modul. Wenn du es ,vom Autor her, darfst.

Die Kommunikation ist aber grundlegend unterschiedlich Osram nutzt eine eigene REST API und wie oben beschrieben läuft die Kommunikation hier dann über einen Clientsocket. Insofern ist das dann von der Art und Weise ein komplett eigenständiges Modul.

leider gibt es keine Dokumentation, nachdem Osram diese Infos nicht veröffentlicht, da sie derzeit nur auf Cloud / Rest API setzen. Ich habe mich durch diverse Scripts Python bzw. JavaScript gelesen.

Ich bin fast sicher, dass auch die anderen Informationen im binary protocol enthalten, leider hilft da nur den binary string step by step zu zerlegen und zu analysieren.

Gruppen gehen , muss hier noch etwas scripten, kommt dann gemeinsam mit Color/Temp. Wahrscheinlich werde ich mich bei Color auch an das HUE Modul halten.

Interessant ist Saturation. Hier hätte ich keine Möglichkeit in der App gefunden diese ein zustellen.

anbei eine neue Version:

  • Osram Geräte werden in eigene Kategorien eingelesen (Lights, Switches, Motion, Plugs, Groups) - derzeit nur Lights implementiert

    a) Client Socket Instanz anlegen mit IP und Port 4000
    b) das Script Osram socket [read] unter dieser Instanz anlegen
    c) Einen Timer unter diesem Script anlegen - ich habe 10s eingestellt (Timer inaktiv)
    d) Eine Instanz vom Typ RegisterVariable anlegen
    e) Ziel der auf Osram [decode] verweisen lassen
    f) Script Osram functions [script] anlegen
    g) Kategorien Lights, Switches, Motions, Plugs und Groups erstellen
    h) die IDs in den entsprechenden defines in Osram functions [script] eintragen
    i) Timer aktivieren

    • Name
    • MAC
    • ID
    • State (wenn nicht Online wird automatisch auf off gesetzt)
      . Online
    • Color Temperature
    • Brightness
    • RGBW
    • Firmware
  • Osram Groups werden ebenfalls eingelesen und erstellt

    • Name
    • ID
    • Lampen die dieser Gruppe zugewiesen sind, werden sofern vorhanden mit den Lampen unter der Kategorie Lights verlinkt

Osram Lightify.zip (6.63 KB)

Hey danke für das Super script.

Damit es ging musste ich noch:

include(IPS_GetScriptFile(10219 /*[Osram Lightify Socket\Osram Socket Script]*/)) 

in der Osram Socket Decode (Zeile 7) ändern.

Soweit funktionier das schon ganz gut, plant du/ihr auch die noch ein Skript um die Lampen ein/aus zuschalten bzw. die Farben zu ändern?

mfg: Acer

Wie im Eingangspost geschrieben, bemühe ich mich, ein Lightify Modul zu erstellen:D
Dank der Hilfe hier im Forum komme ich inzwischen voran.

Das Modul nimmt bereits Formen an (Lampen werden bereits unterstützt) und wird zu Zeit getestet. Ziel ist es, auch Gruppen und evtl. noch Szenen zu unterstützen und es dann als Repository zur Verfügung zu stellen.

Aber bis dahin dauert es wohl noch etwas:)

Gruß

Burkhard

ist bereits in Arbeit inkl. ein paar kleinen Fixes und ich gehe davon aus, dass ich Morgen eine neue Version zur Verfügung stellen kann, die auch die Funktionen für On/Off und Brigthness für Lampen und Gruppen enthält. Color kommt dann etwas später.

PS: Kann jemand testen ob nun mehr als 28 Lampen eingelesen werden - ich habe leider nur 5 :smiley:

Update 0.86

  • Lampen/Gruppen ein-/ausschalten
    bool SetDeviceState(id, state)
    id: ID der Lampe bzw. Gruppe die geschalten werden soll.
    state 0: On
    state 1: Off

  • Lampen /Gruppen dimmen
    bool SetLightBrightness(GetValue(id, value, transition);
    id: ID der Lampe bzw. Gruppe die geschalten werden soll.
    value: 0-100 wobei 0 die Lampe/Gruppe ausschaltet
    transition: 1/10 sec (ist optional)

Osram Lightify.zip (7.16 KB)

Update 0.88

Neue Variablen

  • Hue
  • Saturation

Neue Funktionen

  • Lampen/Gruppen Farbtemperatur
    bool SetColorTemperature(id, value, transition);
    id: ID der Lampe bzw. Gruppe
    value: 1000-8000
    transition: 1/10 sec (ist optional)

Osram Lightify 0.88.zip (8.74 KB)

Update 0.90

  • Bugfixes (Hue, Saturation, SetColorTemperature)

  • SetLightBrightness Funktion umbenannt --> bool SetBulbBrightness(id, Value, Transition);

  • NEU

  • Verwendung von Variable Profilen:
    Hue --> OSR.Hue 0° - 360°
    ColorTemperature --> OSR.ColorTemperature 2000K - 8000K
    Color --> ~HexColor
    Brightness, Saturation --> ~Intensity.100

  • Unterscheidung (Beta) zwischen Lampentyp TW und RGBW. Somit werden auch nur die Variablen angelegt die relevant sind

  • Neue Funktion bool SetBulbColor(id, Value, Transition);
    id: ID der Lampe bzw. Gruppe dessen Farbe gesetzt werden soll
    value: RGB als Integer
    transition: 1/10 sec (optional, default = 0.0 sec)

PS: sämtliche Lampen bzw. Gruppen löschen und neu einlesen lassen.
PPS: Adaptierung der defines bzw. includes nicht vergessen!

Osram Lightify 0.90.zip (8.3 KB)

Hey danke für deine mühe, ich werde dein Skript mal am Wochende mal testen. Und hier berichten.

Update 0.90c

  • Bugfixes

Osram Lightify 0.90c.zip (8.35 KB)

Whitesheep
Update 0.91b

  • Neue Funktionen
    bool SetAllBulbState(Sate)
    Schaltet alle Lampen ein. bzw aus
    State: 1=on; 0=off

bool BulbColorCycle(id, cycle, Agility)
Endlos Durchlauf Farben Spektrum der Lampe
id: ID der Lampe
cycle: true=startet; false=stoppt die Schleife
Agility: 5-65535 (sec). Je kleiner der Wert umso Schneller der Durchlauf

Osram Lightify 0.91b.zip (8.75 KB)

Habe noch ein kleines Problem.

18.03.2017 13:10:40*| Register Variable*| <br />
<b>Notice</b>:  fwrite(): send of 16 bytes failed with errno=10057 Eine Anforderung zum Senden oder Empfangen von Daten wurde verhindert, da der Socket nicht verbunden ist und (beim Senden ?ber einen Datagrammsocket mit einem sendto-Aufruf) keine Adresse angegeben wurde.
 in <b>C:\IP-Symcon\scripts\10219.ips.php</b> on line <b>77</b><br />

Empfangen von Daten geht bloß das Senden egal welche Befehl klappt nicht, was haben Sie noch geändert damit es klappt?

mfg: Acer

die IP vom Gateway in Osram functions korrekt eingetragen ?

//Local definitions
define(‚OSR_GATEWAYIP‘, „192.168.1.1“);

Habe ich kontrolliert, bei mir steht die Corrente IP Adresse im „Osram Skript“.

Habe festgestellt das ab und zu auch mal ein Befehl ankommt.
Desweitern ist mir aufgefallen, dass er sich beim ersten mal die Richtigen Daten und Status holt aber diese nicht mehr updatet. Das Skript „Osram Read“ wird alle 10 Sec ausgeführt.