Solaranlage auslesen: SAMIL Solarlake SL 7000TL-PM

<?php

class GeneralData {
	public function __construct($data) {
		$this->{'va_rating'} = trim(substr($data, 21, 6));
		$this->{'model'} = trim(substr($data, 27, 14));
		$this->{'producer'} = trim(str_replace('\x00', ' ', substr($data, 41, 49)));
		$this->{'serial_number'} = trim(str_replace('\x00', ' ', substr($data, 90, 32)));
		$this->{'soft_ver_com'} = trim(str_replace('\x00', ' ', substr($data, 122, 6)));
		$this->{'soft_ver_master_cpu'} = trim(str_replace('\x00', ' ', substr($data, 128, 6)));
		$this->{'soft_ver_slave_cpu'} = trim(str_replace('\x00', ' ', substr($data, 134, 6)));
	}
}

class StatusData {
	public function __construct($data) {
		$unp = unpack('nNNnnCnnnnNNnnnnnnNNnN', substr($data, 10));
		$this->{'counter'} = unpack("n", substr($data, 10, 2))[1];
		$this->{'today_generation'} = unpack("n", substr($data, 61, 2))[1];
		$this->{'pv_ampere1'} = unpack("n", substr($data, 25, 2))[1];
		$this->{'pv_ampere2'} = unpack("n", substr($data, 27, 2))[1];
		$this->{'pv_input1'} = unpack("n", substr($data, 29, 2))[1];
		$this->{'pv_input2'} = unpack("n", substr($data, 31, 2))[1];
		$this->{'grid_voltage'} = unpack("n", substr($data, 43, 2))[1];
		$this->{'grid_frequency'} = unpack("n", substr($data, 45, 2))[1];
		$this->{'grid_current'} = unpack("n", substr($data, 47, 2))[1];
		$this->{'amb_temp'} = unpack("n", substr($data, 51, 2))[1];
		$this->{'total_generation'} = unpack("N", substr($data, 53, 4))[1];
		$this->{'running_time'} = unpack("N", substr($data, 57, 4))[1];
		$this->{'output_power'} = unpack("N", substr($data, 63, 4))[1];
	}
}

function _sum($data) {
	$res = 0;
	for($i = 0, $len=strlen($data); $i < $len; $i++) $res += ord($data[$i]);
	return $res;
}

function validate_checksum($msg) {
	$data_len = strlen($msg);
	$msg_sum = unpack("n", substr($msg, $data_len - 2, 2))[1];
	$actual_sum = _sum(substr($msg, 0, $data_len - 2));
	return $msg_sum == $actual_sum;
}


function interpret_general_data($data) {
	if(unpack("n" ,$data)[1] != 0x55aa) return "INVALID GENERAL DATA";
	if($data && validate_checksum($data)) return new GeneralData($data);
	return null;
}

function interpret_status_data($data) {
	if(unpack("n" ,$data)[1] != 0x55aa) return "INVALID STATUS DATA";
	if($data && validate_checksum($data)) return new StatusData($data);
	return null;
}

function broadcast_message($port, $msg) {
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
	socket_set_option($s, SOL_SOCKET, SO_BROADCAST, 1);
	socket_sendto($s, $msg, strlen($msg), 0, '255.255.255.255', $port);
	socket_close($s);
}

function request_general_data($conn) {
	$data=hex2bin("55AA001101000000000000000280010000007A020E");
	socket_send($conn, $data, strlen($data), 0);
	time_nanosleep(0, 250000000);
	return socket_read($conn, 1024, PHP_BINARY_READ);
}

function request_status_data($conn){
	$data=hex2bin("55AA0011010000000000000102800103E8004A02CA");
	socket_send($conn, $data, strlen($data), 0);
	time_nanosleep(0, 250000000);
	return socket_read($conn, 1024, PHP_BINARY_READ);
}


function collect_data($verbose = false){

	$TCP_PORT = 60001 /*[Objekt #60001 existiert nicht]*/;
	$UDP_PORT1 = 1300;
	$UDP_PORT2 = 60000 /*[Objekt #60000 existiert nicht]*/;

	$s = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
	socket_bind($s, '0', $TCP_PORT);
	socket_set_option($s, SOL_SOCKET, SO_RCVTIMEO, ["sec" => 5, "usec" => 0]);

	broadcast_message($UDP_PORT1, hex2bin("55aa004002000b4920414d20534552564552043a"));
   broadcast_message($UDP_PORT2, hex2bin("55aa000c010000000007000000000113"));

	if(socket_listen($s, 1)) {
		$retries = 10;
		socket_set_nonblock($s);
		if($verbose) IPS_LogMessage('SolarSystem', "waiting for connection...");
		for(;;){
			if(!$conn = socket_accept($s)) {
				if($retries == 0) {
				  break;
				}
				$retries--;
				time_nanosleep(0, 900000000);
				if($verbose) IPS_LogMessage('SolarSystem', "sleep 1 sec");
				continue;
			}
			else {
				socket_set_block($conn);
				$general_data = interpret_general_data(request_general_data($conn));
				sleep(1);
				$status_data = interpret_status_data(request_status_data($conn));

				// if everything is ok, then at this point you have $general_data and $status_data that holds your data
				// the classes are defined at the top of the script

				//var_dump($general_data);
				SetValueInteger(22892 /*[Energie\SolarSystem\Max DC Power]*/, $general_data->va_rating);
				SetValueString(47748 /*[Energie\SolarSystem\Model name]*/, $general_data->model);
				SetValueString(33669 /*[Energie\SolarSystem\Manufacturer]*/, $general_data->producer);
				SetValueString(52120 /*[Energie\SolarSystem\Serial number]*/, $general_data->serial_number);
				SetValueString(27809 /*[Energie\SolarSystem\Software version [COM]]*/, $general_data->soft_ver_com);
				SetValueString(31265 /*[Energie\SolarSystem\Software version [Master CPU]]*/, $general_data->soft_ver_master_cpu);
            SetValueString(42070 /*[Energie\SolarSystem\Software version [Slave CPU]]*/, $general_data->soft_ver_slave_cpu);

				//var_dump($status_data);
            SetValueInteger(16847 /*[Energie\SolarSystem\Running time]*/, $status_data->running_time);
            SetValueFloat(56533 /*[Energie\SolarSystem\Output Power]*/, $status_data->output_power);
            SetValueFloat(49228 /*[Energie\SolarSystem\Today generation]*/, $status_data->today_generation*0.01);

				SetValueFloat(23825 /*[Energie\SolarSystem\PV1 current]*/, $status_data->pv_ampere1 *0.1);
				SetValueFloat(20956 /*[Energie\SolarSystem\PV2 current]*/, $status_data->pv_ampere2 *0.1);
				//SetValueFloat(49541 /*[Energie\SolarSystem\PV1 voltage]*/, $status_data->pv_input1);
				//SetValueFloat(21275 /*[Energie\SolarSystem\PV2 voltage]*/, $status_data->pv_input2);
				SetValueFloat(18456 /*[Energie\SolarSystem\PV1 input power]*/, $status_data->pv_input1);
				SetValueFloat(14381 /*[Energie\SolarSystem\PV2 input power]*/, $status_data->pv_input2);
				//SetValueFloat(49228 /*[Energie\SolarSystem\Today generation]*/, $status_data->grid_voltage);
				//SetValueFloat(49228 /*[Energie\SolarSystem\Today generation]*/, $status_data->grid_frequency);
				//SetValueFloat(49228 /*[Energie\SolarSystem\Today generation]*/, $status_data->grid_current);
				//SetValueFloat(49228 /*[Energie\SolarSystem\Today generation]*/, $status_data->amb_temp);
				SetValueFloat(34316 /*[Energie\SolarSystem\Total generation]*/, $status_data->total_generation* 0.1);

				//Gesamtstrom mit Solar
				SetvalueFloat(29015 /*[Energie\Gesamtstrom mit Solar]*/, Getvalue(13659 /*[Energie\EKM-868 (Gesamtstrom)\Current]*/)-Getvalue(56533 /*[Energie\SolarSystem\Output Power]*/));

				// cleanup accepted socket
				socket_shutdown($conn, 2);
				socket_close($conn);
				break;
			}
		}
		IPS_LogMessage('SolarSystem',  "No one connected");
	}
	else IPS_LogMessage('SolarSystem', "listen returned error: ".socket_strerror(socket_getlast_error($s))."
");
	// close listening socket
	socket_close($s);
}

$DECODE_TEST = false;
if($DECODE_TEST) {
	// test data
	$TEST_STATUS_DATA = hex2bin("55AA005B010000000003000103800103E8004A01E00D7A11A7001E0008040E01820172001508CA1386001508CE1386001408D5138600007B21000005690067000004FA00010000000000000000000000000000000000000000000000000D64");
	$TEST_GENERAL_DATA = hex2bin("55AA008B01000000000300000380010000007A0032202037303030534C20202037303030544C2D504D20202020202020202020202020202020002053616D696C506F77657200202020202020202020202020202020202020202020543032313435433037310000000000000000000000000000000000000000000056322E30302056322E30302056322E3030201585");

	//echo validate_checksum($TEST_STATUS_DATA);
	//echo validate_checksum($TEST_GENERAL_DATA);

	var_dump(interpret_general_data($TEST_GENERAL_DATA));
	var_dump(interpret_status_data($TEST_STATUS_DATA));
}
else
	collect_data();

?>