Komunikacja z platformą Dessmonitor za pomocą PHP

Komunikacja z platformą Dessmonitor za pomocą PHP Dessmonitor to popularna platforma do monitoringu i zarządzania systemami magazynowania energii oraz instalacjami fotowoltaicznymi głównie firmy EaSun. Umożliwia zdalny odczyt parametrów urządzeń, ich konfigurację oraz analizę danych historycznych.

Jak używać klasy DessMonitorClient do komunikacji z Dessmonitor

<?php

/**
 * DessMonitorClient – ujednolicony klient:
 * - read($dev, "<param_id>") => queryDeviceCtrlValue
 * - write($dev, "<param_id>", $value) => ctrlDevice z mapowaniem
 * - read($dev, "energy_flow") => webQueryDeviceEnergyFlowEs
 * - param("<param_id>")->bind($dev)->get()/set()
 */
class DessMonitorClient
{
	private static string $baseUrl = "https://api.dessmonitor.com";
	private string $token;
	private string $secret;

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	public function __construct(string $token, string $secret)
	{
		self::$baseUrl = rtrim(self::$baseUrl, "/");
		$this->token   = $token;
		$this->secret  = $secret;
	}

	// ---------------------------
	// Public API (high-level)
	// ---------------------------

	/**
	 * Czytanie:
	 * - "energy_flow" (alias) -> webQueryDeviceEnergyFlowEs
	 * - w przeciwnym razie traktuje $what jako ID parametru -> queryDeviceCtrlValue
	 */


	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	public static function login(string $usr, string $pwd, string $companyKey, int $source = 1): array {
		
		$base = rtrim(self::$baseUrl, "/") . "/public/";

		$salt = (string) round(microtime(true) * 1000);

		// wartości jak w dess_login.php / web
		$appClient  = "web";
		$appId	  = "dessmonitor.com";
		$appVersion = "1.0.0";

		// UWAGA: kolejność w podpisie ma znaczenie
		$signatureTail =
			"&action=authSource" .
			"&usr=" . $usr .
			"&company-key=" . $companyKey .
			"&source=" . $source .
			"&_app_client_=" . $appClient .
			"&_app_id_=" . $appId .
			"&_app_version_=" . $appVersion;

		$sign = sha1($salt . sha1($pwd) . $signatureTail);

		$params = [
			"sign"		  => $sign,
			"salt"		  => $salt,
			"action"		=> "authSource",
			"usr"		   => $usr,
			"company-key"   => $companyKey,
			"source"		=> (string)$source,
			"_app_client_"  => $appClient,
			"_app_id_"	  => $appId,
			"_app_version_" => $appVersion,
		];

		$url = $base . "?" . http_build_query($params);

		$ch = curl_init($url);
		curl_setopt_array($ch, [
			CURLOPT_RETURNTRANSFER => true,
			CURLOPT_TIMEOUT => 20,
			CURLOPT_CONNECTTIMEOUT => 10,

			// tak jak u Ciebie (jeśli masz poprawne CA, ustaw docelowo true)
			CURLOPT_SSL_VERIFYPEER => false,
			CURLOPT_SSL_VERIFYHOST => 0,

			CURLOPT_HTTPHEADER => [
				"Accept: application/json",
				"User-Agent: Mozilla/5.0",
			],
		]);

		$resp = curl_exec($ch);
		$http = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
		$err  = curl_error($ch);
		curl_close($ch);

		if ($resp === false || $resp === "") {
			throw new RuntimeException("Puste logowanie. HTTP=$http curl_error=$err URL=$url");
		}

		$json = json_decode($resp, true);
		if (!is_array($json)) {
			throw new RuntimeException("Nie-JSON w authSource. HTTP=$http BODY=$resp");
		}

		if (($json["err"] ?? null) !== 0) {
			$desc = $json["desc"] ?? "UNKNOWN";
			throw new RuntimeException("Błąd authSource: err=" . ($json["err"] ?? "NULL") . " desc=$desc HTTP=$http BODY=$resp");
		}

		// najczęściej "dat", czasem "data"
		$dat = $json["dat"] ?? ($json["data"] ?? null);
		if (!is_array($dat)) {
			throw new RuntimeException("authSource: brak pola dat/data w odpowiedzi. BODY=$resp");
		}

		// Zwracamy wyłącznie tablicę z logowania (bez zapisywania w obiekcie)
		return [
			"secret"  => $dat["secret"]  ?? null,
			"token"   => $dat["token"]   ?? null,
			"expire"  => $dat["expire"]  ?? null,
			"role"	=> $dat["role"]	?? null,
			"usr"	 => $dat["usr"]	 ?? null,
			"uid"	 => $dat["uid"]	 ?? null,
			"pprauth" => $dat["pprauth"] ?? null,
			// jeśli API zwraca jeszcze coś – zachowaj:
			"raw"	 => $dat,
		];
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	public function read(array $dev, string $what, array $ctx = []): array
	{
		// alias: specjalny odczyt
		if ($what === "energy_flow") {
			return $this->webQueryDeviceEnergyFlowEs($dev);
		}

		// domyślnie: traktuj $what jako ID parametru sterującego
		return $this->queryDeviceCtrlValue(
			$dev,
			$what,
			$ctx["i18n"] ?? "en_US"
		);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	/**
	 * Ustawianie – te same nazwy parametrów co read()
	 */
	public function write(array $dev, string $param, $value, array $ctx = []): array
	{
		return $this->setParam($dev, $param, $value, $ctx["i18n"] ?? "en_US");
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	/**
	 * Fluent: param("...")->bind($dev)->get()/set()
	 * Uwaga: param() nie zna $dev, więc musisz wywołać bind($dev) lub użyć paramForDev().
	 */
	public function param(string $id): DessParam
	{
		return new DessParam($this, $id);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	/**
	 * Wygodny skrót: paramForDev($dev, "id")->get()/set()
	 */
	public function paramForDev(array $dev, string $id): DessParam
	{
		return $this->param($id)->bind($dev);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	// ---------------------------
	// Fluent helper (internal)
	// ---------------------------

	public function _paramGet(array $dev, string $id, string $i18n = "en_US"): array
	{
		return $this->queryDeviceCtrlValue($dev, $id, $i18n);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	public function _paramSet(array $dev, string $id, $value, string $i18n = "en_US"): array
	{
		return $this->setParam($dev, $id, $value, $i18n);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	// ---------------------------
	// Low-level core (sign/call)
	// ---------------------------

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	private function salt(): string
	{
		return (string) round(microtime(true) * 1000);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	/**
	 * sign = sha1( salt + secret + token + "&action=...&k=v&k2=v2..." )
	 * KOLEJNOŚĆ $orderedParams jest krytyczna.
	 */
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	private function sign(string $salt, string $action, array $orderedParams): string
	{
		$tail = "&action=" . $action;
		foreach ($orderedParams as $k => $v) {
			$tail .= "&" . $k . "=" . $v;
		}
		return sha1($salt . $this->secret . $this->token . $tail);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	private function call(string $action, array $orderedParams): array
	{
		$salt = $this->salt();
		$sign = $this->sign($salt, $action, $orderedParams);

		$params = array_merge(
			["sign" => $sign, "salt" => $salt, "token" => $this->token, "action" => $action],
			$orderedParams
		);

		$url = self::$baseUrl . "/public/?" . http_build_query($params);

		$ch = curl_init($url);
		curl_setopt_array($ch, [
			CURLOPT_RETURNTRANSFER => true,
			CURLOPT_TIMEOUT => 20,
			CURLOPT_CONNECTTIMEOUT => 10,

			// Jeśli masz poprawnie CA/SSL, ustaw docelowo true.
			CURLOPT_SSL_VERIFYPEER => false,
			CURLOPT_SSL_VERIFYHOST => 0,

			CURLOPT_HTTPHEADER => [
				"Accept: application/json",
				"User-Agent: Mozilla/5.0",
			],
		]);

		$resp = curl_exec($ch);
		$http = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
		$err  = curl_error($ch);
		curl_close($ch);

		if ($resp === false || $resp === "") {
			throw new RuntimeException("Pusta odpowiedz. HTTP=$http curl_error=$err URL=$url");
		}

		$json = json_decode($resp, true);
		if (!is_array($json)) {
			throw new RuntimeException("Nie-JSON. HTTP=$http BODY=$resp");
		}

		if (($json["err"] ?? null) !== 0) {
			$desc = $json["desc"] ?? "UNKNOWN";
			throw new RuntimeException("API error: err=" . ($json["err"] ?? "NULL") . " desc=$desc BODY=$resp");
		}

		return $json;
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	// ---------------------------
	// Concrete actions
	// ---------------------------

	public function ctrlDevice(array $dev, string $id, $val, string $i18n = "en_US"): array
	{
		$ordered = [
			"source"  => "1",
			"pn"	  => $dev["pn"],
			"sn"	  => $dev["sn"],
			"devcode" => (string)$dev["devcode"],
			"devaddr" => (string)$dev["devaddr"],
			"id"	  => $id,
			"val"	 => (string)$val,
			"i18n"	=> $i18n,
		];
		return $this->call("ctrlDevice", $ordered);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	public function queryDeviceCtrlValue(array $dev, string $id, string $i18n = "en_US"): array
	{
		$ordered = [
			"source"  => "1",
			"pn"	  => $dev["pn"],
			"sn"	  => $dev["sn"],
			"devcode" => (string)$dev["devcode"],
			"devaddr" => (string)$dev["devaddr"],
			"id"	  => $id,
			"i18n"	=> $i18n,
		];
		return $this->call("queryDeviceCtrlValue", $ordered);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	public function webQueryDeviceEnergyFlowEs(array $dev): array
	{
		// kolejność jak w Twoim URL
		$ordered = [
			"source"  => "1",
			"devcode" => (string)$dev["devcode"],
			"pn"	  => $dev["pn"],
			"devaddr" => (string)$dev["devaddr"],
			"sn"	  => $dev["sn"],
		];
		return $this->call("webQueryDeviceEnergyFlowEs", $ordered);
	}

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	// ---------------------------
	// Param mapping (SET)
	// ---------------------------

	/**
	 * Tu trzymasz mapowania wartości wejściowych -> val do ctrlDevice.
	 * Dzięki temu write() i ->set() przyjmują "ludzkie" wartości, a wysyłają API value.
	 */
	public function setParam(array $dev, string $param, $value, string $i18n = "en_US"): array
	{
		switch ($param) {
			case "bat_max_total_charge_current":
			case "bat_max_utility_charge_current":
				return $this->ctrlDevice($dev, $param, (int)$value, $i18n);

			case "los_output_source_priority":
				// Twoje mapowanie: Utility => 0, inaczej => 2
				$val = ($value === "Utility" || $value === 0 || $value === "0") ? 0 : 2;
				return $this->ctrlDevice($dev, $param, $val, $i18n);

			default:
				// fallback
				return $this->ctrlDevice($dev, $param, $value, $i18n);
		}
	}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

/**
 * DessParam – obiekt parametru (fluent).
 */
class DessParam
{
	private DessMonitorClient $client;
	private string $id;
	private ?array $dev = null;
	private string $i18n = "en_US";

	public function __construct(DessMonitorClient $client, string $id)
	{
		$this->client = $client;
		$this->id	 = $id;
	}

	public function bind(array $dev): self
	{
		$this->dev = $dev;
		return $this;
	}

	public function i18n(string $i18n): self
	{
		$this->i18n = $i18n;
		return $this;
	}

	public function get(): array
	{
		$this->assertBound();
		return $this->client->_paramGet($this->dev, $this->id, $this->i18n);
	}

	public function set($value): array
	{
		$this->assertBound();
		return $this->client->_paramSet($this->dev, $this->id, $value, $this->i18n);
	}

	private function assertBound(): void
	{
		if (!is_array($this->dev)) {
			throw new InvalidArgumentException("DessParam: brak bind($dev). Użyj paramForDev() albo ->bind($dev).");
		}
	}
}


?>
skopiuj kod   |   pobierz kod
Klasa DessMonitorClient służy do komunikacji z platformą Dessmonitor – umożliwia logowanie do systemu oraz odczyt i ustawianie parametrów urządzeń (np. falowników, magazynów energii). Jej użycie jest celowo uproszczone i składa się z dwóch podstawowych etapów: logowania oraz pracy na danych.

1. Logowanie do Dessmonitor

Logowanie wykonuje się jednorazowo za pomocą statycznej metody login(). Przekazujemy login (najczęściej e-mail), zwykłe hasło (nie hash) oraz companyKey.
include_once(ROOT_DIR . 'inc/dess_monitor_client.class.php');

$usr        = ''; // login / e-mail
$pwd        = ''; // zwykłe hasło
$companyKey = '';

$LOGIN_DATA = DessMonitorClient::login($usr, $pwd, $companyKey);

Metoda login() zwraca tablicę z danymi autoryzacyjnymi, m.in.:
$LOGIN_DATA = [
    'token'  => '...',
    'secret' => '...',
    'expire' => '...',
    'role'   => '...',
    'usr'    => '...',
    'uid'    => '...'
];

Najważniejsze są pola token i secret – to one służą do dalszej komunikacji z API.

2. Utworzenie klienta (bez ponownego logowania)

Po zalogowaniu nie należy logować się ponownie przy każdym zapytaniu. Wystarczy przekazać zapisany token i secret do konstruktora klasy.
include_once(ROOT_DIR . 'inc/dess_monitor_client.class.php');

$token  = 'moj token';
$secret = 'moj secret';

$client = new DessMonitorClient($token, $secret);
Od tego momentu obiekt $client jest gotowy do pracy.

3. Odczyt danych z urządzenia

Aby odczytać parametr z urządzenia, używamy metody read():
$dev  = ''; // ID lub obiekt urządzenia
$WHAT = 'bat_max_total_charge_current';

$result = $client->read($dev, $WHAT);
Zwrócona wartość to obiekt z danymi, który można dalej przetwarzać, np.:
$value = $result->value ?? null;
lub zamienić na tablicę:
$data = (array) $result;

4. Kilka odczytów pod rząd

Klasa pozwala wygodnie odczytywać wiele parametrów:
$client->read($dev, 'los_output_source_priority');
$client->read($dev, 'bat_max_total_charge_current');
$client->read($dev, 'bat_max_utility_charge_current');
$client->read($dev, 'energy_flow');
Każde wywołanie zwraca niezależny obiekt z danymi.

5. Podsumowanie

  • Cały proces pracy z klasą DessMonitorClient jest prosty i przewidywalny:
  • Logujesz się raz przez login()
  • Zapisujesz token i secret
  • Tworzysz klienta
  • Czytasz lub ustawiasz parametry
  • Otrzymane dane przetwarzasz według własnych potrzeb
Dzięki temu klasa stanowi czystą, jednolitą warstwę pośrednią pomiędzy Twoją aplikacją a systemem Dessmonitor i nadaje się zarówno do prostych skryptów, jak i większych systemów monitoringu.
iHome
Wersja systemu: 2.10.44, aktualiacja: 2026-04-16
Polityka prywatności
Copyright net2me.pl 2018
stats pixel