Orbit B-Hyve Bewässerungsventile ansteuern

Hallo Gemeinde,

ich verwende Orbit B-Hyve Bewässerungsventile, da diese sich als recht zuverlässig erwiesen haben und von den Schraubanschlüssen wertiger sind als andere Hersteller. Dafür gibt es keine offene API, aber eine brauchbare Doku der GET-Befehle zum Auslesen des aktuellen Status unter: Integration with Orbit B-Hyve Irrigation System - Feature Requests - Home Assistant Community (zu finden in dem Post von aydh im Februar 2019)

Das funktioniert ganz gut um Auszulesen, aber Steuerung geht nur über einen Websocket. Und hier stecke ich fest: ich versuche mit Nall-chan´s IPS-Network – Library eine Websocket-Verbindung als Client per IPS aufbauen. Hat das zufällig jemand dafür schon mal durchexerziert und zum Laufen gebracht ?

Konkret habe ich einen Websocket-Client erstellt mit zugehörigen Client-Socket (erstellt sich selbst). Daten scheinen transportiert zu werden, obwohl ich stetig einen Fehler im Handshake angezeigt bekomme. Die Befehle sind in JSON z.B.:

Manual run of Station 2 for 10 minutes:
{"event":"change_mode","device_id":"blahblahblah","timestamp":"2019-02-24T13:47:30.983Z","mode":"manual","stations":[{"station":2,"run_time":10}]}

5.JPG

Wenn ich das richtig verstehe muss ich eine Registervariable darunter hängen, die das codiert/decodiert ? Ich habe mal vor einiger Zeit mit einer Websocket-Verbindung zu einem Robomow mit mitfahrenden Handy experimentiert und dabei hybi10Encode/Decode für den Encode und Decode der Websocket - Messages entdeckt.

<?

/*//////////////////////////////////////////////////////////////////////////////
Robomow Websocket Decode | Encode
22.06.2016
--------------------------------------------------------------------------------
Funktionen zum Encode und Decode der Websocket - Messages
/*//////////////////////////////////////////////////////////////////////////////

function hybi10Encode($payload, $type = 'text', $masked = true)
{
  $frameHead = array();
	$frame = '';
	$payloadLength = strlen($payload);

	switch($type)
	{
		case 'text':
			// first byte indicates FIN, Text-Frame (10000001):
			$frameHead[0] = 129;
		break;

		case 'close':
			// first byte indicates FIN, Close Frame(10001000):
			$frameHead[0] = 136;
		break;

		case 'ping':
			// first byte indicates FIN, Ping frame (10001001):
			$frameHead[0] = 137;
		break;

		case 'pong':
			// first byte indicates FIN, Pong frame (10001010):
			$frameHead[0] = 138;
		break;
	}

	// set mask and payload length (using 1, 3 or 9 bytes)
	if($payloadLength > 65535)
	{
		$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
		$frameHead[1] = ($masked === true) ? 255 : 127;
		for($i = 0; $i < 8; $i++)
		{
			$frameHead[$i+2] = bindec($payloadLengthBin[$i]);
		}
		// most significant bit MUST be 0 (close connection if frame too big)
		if($frameHead[2] > 127)
		{
			$this->close(1004);
			return false;
		}
	}
	elseif($payloadLength > 125)
	{
		$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
		$frameHead[1] = ($masked === true) ? 254 : 126;
		$frameHead[2] = bindec($payloadLengthBin[0]);
		$frameHead[3] = bindec($payloadLengthBin[1]);
	}
	else
	{
		$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
	}

	// convert frame-head to string:
	foreach(array_keys($frameHead) as $i)
	{
		$frameHead[$i] = chr($frameHead[$i]);
	}
	if($masked === true)
	{
		// generate a random mask:
		$mask = array();
		for($i = 0; $i < 4; $i++)
		{
			$mask[$i] = chr(rand(0, 255));
		}

		$frameHead = array_merge($frameHead, $mask);
	}
	$frame = implode('', $frameHead);

	// append payload to frame:
	$framePayload = array();
	for($i = 0; $i < $payloadLength; $i++)
	{
		$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
	}

	return $frame;
}

function hybi10Decode($data)
{
	$payloadLength = '';
	$mask = '';
	$unmaskedPayload = '';
	$decodedData = array();

	// estimate frame type:
	$firstByteBinary = sprintf('%08b', ord($data[0]));
	$secondByteBinary = sprintf('%08b', ord($data[1]));
	$opcode = bindec(substr($firstByteBinary, 4, 4));
	$isMasked = ($secondByteBinary[0] == '1') ? true : false;
	$payloadLength = ord($data[1]) & 127;

	// close connection if unmasked frame is received:
	if($isMasked === false)
	{
		$this->close(1002);
	}

	switch($opcode)
	{
		// text frame:
		case 1:
			$decodedData['type'] = 'text';
		break;

		case 2:
			$decodedData['type'] = 'binary';
		break;

		// connection close frame:
		case 8:
			$decodedData['type'] = 'close';
		break;

		// ping frame:
		case 9:
			$decodedData['type'] = 'ping';
		break;

		// pong frame:
		case 10:
			$decodedData['type'] = 'pong';
		break;

		default:
			// Close connection on unknown opcode:
			$this->close(1003);
		break;
	}

	if($payloadLength === 126)
	{
	   $mask = substr($data, 4, 4);
	   $payloadOffset = 8;
	   $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset;
	}
	elseif($payloadLength === 127)
	{
		$mask = substr($data, 10, 4);
		$payloadOffset = 14;
		$tmp = '';
		for($i = 0; $i < 8; $i++)
		{
			$tmp .= sprintf('%08b', ord($data[$i+2]));
		}
		$dataLength = bindec($tmp) + $payloadOffset;
		unset($tmp);
	}
	else
	{
		$mask = substr($data, 2, 4);
		$payloadOffset = 6;
		$dataLength = $payloadLength + $payloadOffset;
	}

	/**
	 * We have to check for large frames here. socket_recv cuts at 1024 bytes
	 * so if websocket-frame is > 1024 bytes we have to wait until whole
	 * data is transferd.
	 */
	if(strlen($data) < $dataLength)
	{
		return false;
	}

	if($isMasked === true)
	{
		for($i = $payloadOffset; $i < $dataLength; $i++)
		{
			$j = $i - $payloadOffset;
			if(isset($data[$i]))
			{
				$unmaskedPayload .= $data[$i] ^ $mask[$j % 4];
			}
		}
		$decodedData['payload'] = $unmaskedPayload;
	}
	else
	{
		$payloadOffset = $payloadOffset - 4;
		$decodedData['payload'] = substr($data, $payloadOffset);
	}

	return $decodedData;
}

?>

Kann mir an der Stelle jemand einen Tipp geben, wie ich weiter komme oder eine andere Idee ?

Besten Gruß

André

Dieses Script brauchst du nicht, da du in der regvar nur die Daten (Payload) ohne das ganzen Websocket Protokoll bekommst.
Aber so weit sind wir gar nicht.

Die Verbindung wird nicht aufgebaut.
Der TLS Handshake klappt schon nicht.
Modul ist aktuell?
Welche Symcon Version?
Michael

IPS 5.3
Modul vor 4 Tagen installiert

Brauche ich vielleicht den Connect Service ? Der ist abgeschaltet, da ich auf meinem Testsystem bin.

Gruß

André

Gesendet von meinem SM-N960F mit Tapatalk

Danke übrigens für die ultraschnelle Antwort :slight_smile:

Gesendet von meinem SM-N960F mit Tapatalk

Connect hat damit nix zu tun.
Die Frage ist, warum der TLS Handshake nicht funktioniert.
Das kommt noch vor dem Login oder dem websocket Handshake.
Fehlermeldung im Symcon Log tauchen dabei nicht auf?

Kannst du mit den dump vom Clientsocket bitte zukommen lassen.
Da reichen die beiden Receive Zeilen nach dem Transmit.
Irgendwie erkennt mein Modul dort nicht alle Daten.
Michael

Ich habe auch so ein Orbit Gerät und klinke mich mal mit rein.
Ich hänge mal meine Debugs mit ran.

Gruß,
Loerdy

dump.txt (1.9 KB)

dump_client_socket.txt (29.4 KB)

Hallo Nall-chan,

anbei meine Dump des 1.und 2.Receive. Merkwürdig, heute ging erst garnichts (Debug blieb leer) und erst nach aufsetzen eines neuen Socket wollte es. Wie bekomme ich im Objektbaum eigentlich den Websocket Client direkt unter den Client Socket ?

Gruß André

dump (1. und 2. Receive).txt (12.1 KB)

Eigentlich gar nicht, weil diese Instanzen in spezielle Kategorie einsortiert werden.
Es hindert dich aber keiner in der Schnellausführung IPS_SetParent zu benutzen :smiley:
Michael

Fix ist online.
Verbindung mit dem Dienst wird hergestellt.
Mehr konnte ich nicht testen (z.B. Authentifizierung).
Michael

Hallo Nall-chan,

ich bestätige, der Handshake funktioniert.
Jetzt muss ich herausfinden, was zu senden ist.

Gruß André

Gesendet von meinem SM-N960F mit Tapatalk

Vermutlich zuerst das hier mit app_connection.
Integration with Orbit B-Hyve Irrigation System - Feature Requests - Home Assistant Community
Michael

…ich klinke mich mal hier ein, da ich ebenfalls mein Orbit B-Hyve über Symcon steuern möchte.

… hab mir das jetzt etwas näher angeschaut, mir hat sich noch nicht erschlosen wie ich das in symcon umsetzen mus :frowning:

Hallo zusammen,

habe jetzt auch so ein Teil :cool:

Dank der Vorarbeit von Andrej habe ich auch schon den Login erfolgreich zum laufen bekommen!

Muss jetzt erstmal alles richtig anschließen und einrichten dann schau ich mir die Sache mit dem Websocket an - wahrscheinlich am WE!

Bis dann
Heiko

Sehr gut. Viel Spaß mit dem Websocket…

Gruß André

Gesendet von meinem SM-N960F mit Tapatalk

… ist hier jemand schon weiter gekommen ?
Hab mich am letzten WE nochmal daran versucht, ohne wirklichen Erfolg…:banghead:

Ich bin dran, brauche aber noch etwas Zeit!

Hoffe diese Woche paar Sachen vorstellen zu können!

Gruß Heiko

PS: dauert etwas länger als gedacht, aber versuche recht viel Daten auszulesen/visualisieren und schon in Richtung Modul vorzubereiten!

wow, super!!! :slight_smile:
Bin schon gespannt :slight_smile:

Kurzes Update - leider mache ich nur kleine Schritte (Faktor Zeit)

Mit der HTTP API bin ich jetzt durch und alles in Funktionen weggekapselt - Check!

Bin jetzt gerade dabei - die für mich - interessantesten Informationen auszulesen und zu visualisieren (siehe Screenshot).

Welche Daten als Variablen angelegt werden kann dann jeder später über eine Art Konfiguration festlegen (läuft auch schon).

Hoffe morgen dann damit durch zu sein!

Bis dann
Heiko

das sieht schon mal sehr vielverspechend aus :slight_smile: