From ed6adbf96d56585457ff80bd0d540cc0f193b2b9 Mon Sep 17 00:00:00 2001 From: Pawel Janisio Date: Wed, 12 Jun 2024 22:42:50 +0200 Subject: [PATCH] 0.9.5 - modyfied postRequest method to be able to use only token on header - modyfied __constructor of devices to take json saved data - added setDeviceStatus method to update parameters of the device - updated README - create pre-release --- README.md | 38 +++++++++++++++++++------- index.php | 11 ++++++-- src/Devices.php | 66 +++++++++++++++++++++++++++++++++++++++++----- src/HttpClient.php | 41 ++++++++++++++++------------ 4 files changed, 122 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 6668cdb..d3ae034 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,18 @@ # ewelinkApiPhp -API connector for Sonoff devices using OAuth2. +API connector for Sonoff/ewelink devices using simple webapi based on OAuth2. -PHP 7.4+ required, no other dependencies. +PHP 7.4+, no other dependiencies required. + +Version **0.9.5** is currently operative and ***pre released***, but not yet tested in 100%, so feedback is appreciated **create issues / PR** if needed. + +## Current features + +- login and authorization to ewelink (with refresh token) +- get devices list with all parameters +- saving devices to .json +- search value of parameter for each device (f.e switch status, productName etc.) +- update parameter of device ## Public key and secret @@ -14,24 +24,34 @@ And put your keys and redirect Url in **Constants.php** ``` + + project_root/ │ ├── src/ -│ ├── Constants.php -│ ├── HttpClient.php -│ └── Utils.php -| └── Devices.php +│ ├── Constants.php +│ ├── HttpClient.php +│ └── Utils.php +| └── Devices.php │ └── index.php + + ``` All classes are located in src directory. -Index.php works as a gateway to API and also as a debug for all methods. +Index.php works as a gateway to API and also as a debug for all availablemethods. -.json outputs will be saved in project_root +.json files outputs will be saved in project_root ## Example -See **index.php** +Examples will follow with stable release, for now - see **index.php** + +## More inside info + +- with next stable release methods and invoking functions structure can be changed (keep this in mind) +- branch **websockets** will probably be not maintened +- there could be some incosistency in the code still, like f.e handling error messages diff --git a/index.php b/index.php index 24fb81e..0d01104 100644 --- a/index.php +++ b/index.php @@ -42,7 +42,7 @@ function getQueryParam($name) { echo '

Devices Data

'; echo '
' . print_r($devicesData, true) . '
'; - $devices = new Devices($httpClient); + $devices = new Devices(); echo '

Loaded Devices Data

'; echo '
' . print_r($devices->getDevicesData(), true) . '
'; @@ -59,9 +59,16 @@ function getQueryParam($name) { // Example usage of getDeviceParamLive $liveParam = 'switch'; // example parameter to get - $liveResult = $devices->getDeviceParamLive($deviceId, $liveParam); + $liveResult = $devices->getDeviceParamLive($httpClient, $deviceId, $liveParam); echo '

Live Device Parameter

'; echo '
' . print_r($liveResult, true) . '
'; + + // Example usage of setDeviceStatus + $params = ['switch' => 'on']; // example parameters to update + $setStatusResult = $devices->setDeviceStatus($httpClient, $deviceId, $params); + echo '

Set Device Status Result

'; + echo '
' . print_r($setStatusResult, true) . '
'; + } catch (Exception $e) { echo 'Error: ' . $e->getMessage(); } diff --git a/src/Devices.php b/src/Devices.php index 3183b75..7c0d1a6 100644 --- a/src/Devices.php +++ b/src/Devices.php @@ -2,11 +2,11 @@ class Devices { private $devicesData; - private $httpClient; - public function __construct(HttpClient $httpClient) { - $this->httpClient = $httpClient; - $this->loadDevicesData(); + public function __construct() { + if (file_exists('devices.json')) { + $this->devicesData = json_decode(file_get_contents('devices.json'), true); + } } /** @@ -20,6 +20,15 @@ private function loadDevicesData() { } } + /** + * Get the stored devices data. + * + * @return array|null The devices data or null if not set. + */ + public function getDevicesDataFromStorage() { + return $this->devicesData; + } + /** * Get the loaded devices data. * @@ -96,12 +105,13 @@ public function searchDeviceParam($searchKey, $deviceId) { /** * Get live device parameter using the API. * + * @param HttpClient $httpClient The HTTP client instance. * @param string $deviceId The ID of the device. * @param string $param The parameter to get. * @param int $type The type (default is 1). * @return mixed The specific parameter value from the API response or null if not found. */ - public function getDeviceParamLive($deviceId, $param, $type = 1) { + public function getDeviceParamLive(HttpClient $httpClient, $deviceId, $param, $type = 1) { $endpoint = '/v2/device/thing/status'; $queryParams = [ 'id' => $deviceId, @@ -109,7 +119,7 @@ public function getDeviceParamLive($deviceId, $param, $type = 1) { 'params' => urlencode($param) ]; - $response = $this->httpClient->getRequest($endpoint, $queryParams); + $response = $httpClient->getRequest($endpoint, $queryParams); if (isset($response['error']) && $response['error'] != 0) { throw new Exception('Error: ' . $response['msg']); @@ -121,4 +131,48 @@ public function getDeviceParamLive($deviceId, $param, $type = 1) { return null; } + + /** + * Set the device status by updating a parameter. + * + * @param HttpClient $httpClient The HTTP client instance. + * @param string $deviceId The ID of the device. + * @param array $params The parameters to update. + * @return string The result message. + * @throws Exception If the parameter update fails. + */ + public function setDeviceStatus(HttpClient $httpClient, $deviceId, $params) { + // Check if the parameter exists and get the current value + foreach ($params as $param => $newValue) { + $currentValue = $this->getDeviceParamLive($httpClient, $deviceId, $param); + if ($currentValue === null) { + return "Parameter $param does not exist for device $deviceId."; + } + + // Compare the current value with the new value + if ($currentValue == $newValue) { + return "Parameter $param is already set to $newValue for device $deviceId."; + } + + // Update the parameter + $data = [ + 'type' => 1, + 'id' => $deviceId, + 'params' => [$param => $newValue] + ]; + $response = $httpClient->postRequest('/v2/device/thing/status', $data, true); + + if (!isset($response['error']) || $response['error'] !== 0) { + throw new Exception('Failed to update parameter: ' . ($response['msg'] ?? 'Unknown error')); + } + + // Verify the update + $updatedValue = $this->getDeviceParamLive($httpClient, $deviceId, $param); + if ($updatedValue == $newValue) { + return "Parameter $param successfully updated to $newValue for device $deviceId."; + } else { + return "Failed to update parameter $param to $newValue for device $deviceId. Current value is $updatedValue."; + } + } + } } diff --git a/src/HttpClient.php b/src/HttpClient.php index 3b7f794..9d69437 100644 --- a/src/HttpClient.php +++ b/src/HttpClient.php @@ -17,6 +17,7 @@ public function __construct($state) { $utils = new Utils(); $this->region = Constants::REGION; // Assign region from Constants $this->loginUrl = $this->createLoginUrl($state); + list($this->code, $redirectRegion) = $utils->handleRedirect(); if ($redirectRegion) { $this->region = $redirectRegion; @@ -106,38 +107,44 @@ public function getGatewayUrl() { } /** - * Make an HTTP POST request with the given endpoint and data. + * Make a POST request. * - * @param string $endpoint The endpoint to make the request to. - * @param array $data The data to send in the request. + * @param string $endpoint The endpoint to send the request to. + * @param array $data The data to send in the request body. + * @param bool $useTokenAuthorization Whether to use token-based authorization. * @return array The response data. - * @throws Exception If the request fails. + * @throws Exception If there is an error in the request. */ - public function postRequest($endpoint, $data = []) { - $url = $this->getGatewayUrl() . $endpoint; + public function postRequest($endpoint, $data, $useTokenAuthorization = false) { $utils = new Utils(); - $nonce = $utils->generateNonce(); - $body = json_encode($data); - $authorization = 'Sign ' . $utils->sign($body, Constants::APP_SECRET); - + $url = $this->getGatewayUrl() . $endpoint; $headers = [ "Content-type: application/json; charset=utf-8", "X-CK-Appid: " . Constants::APPID, - "Authorization: " . $authorization, - "X-CK-Nonce: " . $nonce + "X-CK-Nonce: " . $utils->generateNonce() ]; + if ($useTokenAuthorization) { + $token = $this->tokenData['accessToken']; + $headers[] = "Authorization: Bearer $token"; + } else { + $authorization = $utils->sign(json_encode($data), Constants::APP_SECRET); + $headers[] = "Authorization: Sign $authorization"; + } + $options = [ 'http' => [ - 'header' => implode("\r\n", $headers) . "\r\n", - 'method' => 'POST', - 'content' => $body, + 'header' => implode("\r\n", $headers), + 'method' => 'POST', + 'content' => json_encode($data), ], ]; - $context = stream_context_create($options); + + $context = stream_context_create($options); $result = file_get_contents($url, false, $context); + if ($result === FALSE) { - throw new Exception('Error in request'); + throw new Exception('Error making POST request'); } $response = json_decode($result, true);