From 93187da38425041f761faae83cc8049d2143ded3 Mon Sep 17 00:00:00 2001 From: Enzo Macri Date: Fri, 18 Sep 2015 18:14:44 +0200 Subject: [PATCH] Introduction of dedicated SDK for each Netatmo Product --- AppliCommonPublic.php | 138 ------- CLI_ClientCredential_Example.php | 48 --- CLI_PARTNER_Example.php | 74 ---- CLI_THERM_ClientCredential_Example.php | 96 ----- Config.php | 6 - Examples/CLI_Therm_API_Example.php | 247 ++++++++++++ Examples/CLI_WS_API_Example.php | 149 +++++++ Examples/CLI_Welcome_API_Example.php | 114 ++++++ Examples/Config.php | 6 + Examples/Utils.php | 312 +++++++++++++++ Examples/WEB_AuthorizationGrant_Example.php | 249 ++++++++++++ README.md | 358 ++++++++++++++++- WEB_AuthorisationCode_Example.php | 143 ------- .../Clients/NAApiClient.php | 88 +---- src/Clients/NAThermApiClient.php | 229 +++++++++++ src/Clients/NAWSApiClient.php | 79 ++++ src/Clients/NAWelcomeApiClient.php | 100 +++++ src/Constants/AppliCommonPublic.php | 374 ++++++++++++++++++ src/Exceptions/NAClientException.php | 79 ++++ src/Exceptions/NASDKException.php | 21 + src/Handlers/NAResponseHandler.php | 167 ++++++++ src/Objects/NACamera.php | 75 ++++ src/Objects/NAEvent.php | 137 +++++++ src/Objects/NAHome.php | 191 +++++++++ src/Objects/NAObject.php | 92 +++++ src/Objects/NAPerson.php | 64 +++ 26 files changed, 3052 insertions(+), 584 deletions(-) delete mode 100644 AppliCommonPublic.php delete mode 100755 CLI_ClientCredential_Example.php delete mode 100644 CLI_PARTNER_Example.php delete mode 100755 CLI_THERM_ClientCredential_Example.php delete mode 100644 Config.php create mode 100755 Examples/CLI_Therm_API_Example.php create mode 100755 Examples/CLI_WS_API_Example.php create mode 100755 Examples/CLI_Welcome_API_Example.php create mode 100644 Examples/Config.php create mode 100644 Examples/Utils.php create mode 100644 Examples/WEB_AuthorizationGrant_Example.php delete mode 100644 WEB_AuthorisationCode_Example.php rename NAApiClient.php => src/Clients/NAApiClient.php (94%) create mode 100644 src/Clients/NAThermApiClient.php create mode 100644 src/Clients/NAWSApiClient.php create mode 100644 src/Clients/NAWelcomeApiClient.php create mode 100644 src/Constants/AppliCommonPublic.php create mode 100644 src/Exceptions/NAClientException.php create mode 100644 src/Exceptions/NASDKException.php create mode 100644 src/Handlers/NAResponseHandler.php create mode 100644 src/Objects/NACamera.php create mode 100644 src/Objects/NAEvent.php create mode 100644 src/Objects/NAHome.php create mode 100644 src/Objects/NAObject.php create mode 100644 src/Objects/NAPerson.php diff --git a/AppliCommonPublic.php b/AppliCommonPublic.php deleted file mode 100644 index 5fc7bdb..0000000 --- a/AppliCommonPublic.php +++ /dev/null @@ -1,138 +0,0 @@ - diff --git a/CLI_ClientCredential_Example.php b/CLI_ClientCredential_Example.php deleted file mode 100755 index 40f9241..0000000 --- a/CLI_ClientCredential_Example.php +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/php - $client_id, "client_secret" => $client_secret, "username" => $test_username, "password" => $test_password, "scope" => $scope)); -$helper = new NAApiHelper($client); - -try { - $tokens = $client->getAccessToken(); - -} catch(NAClientException $ex) { - echo "An error happend while trying to retrieve your tokens\n"; - exit(-1); -} - -// Retrieve User Info : -$user = $helper->api("getuser", "POST"); -echo ("-------------\n"); -echo ("- User Info -\n"); -echo ("-------------\n"); -//print_r($user); -echo ("OK\n"); -echo ("---------------\n"); -echo ("- Device List -\n"); -echo ("---------------\n"); -$devicelist = $helper->simplifyDeviceList(); -echo ("OK\n"); -echo ("-----------------\n"); -echo ("- Last Measures -\n"); -echo ("-----------------\n"); -$mesures = $helper->getLastMeasures(); -print_r($mesures); -echo ("OK\n"); -echo ("---------------------\n"); -echo ("- Last Day Measures -\n"); -echo ("---------------------\n"); -$mesures = $helper->getAllMeasures(mktime() - 86400); -print_r($mesures); -echo ("OK\n"); - - -?> diff --git a/CLI_PARTNER_Example.php b/CLI_PARTNER_Example.php deleted file mode 100644 index 406ff41..0000000 --- a/CLI_PARTNER_Example.php +++ /dev/null @@ -1,74 +0,0 @@ - $client_id, "client_secret" => $client_secret, "username" => $test_username, "password" => $test_password, "scope" => $scope)); - -/*Retrieve user access_token*/ -/* This user is the user created to access your partner application */ -try { - $tokens = $client->getAccessToken(); -} -catch(NAClientException $ex) { - echo "An error happend while trying to retrieve your tokens\n"; - echo $ex->getMessage()."\n"; - exit(-1); -} - -try{ - //Retrieve all your partner devices - $devicelist = $client->api("partnerdevices", "POST"); - foreach($devicelist as $device_id){ - //retrieve device information from api - $devices = $client->api("devicelist", "POST", array("app_type" => "app_thermostat", "device_id" => $device_id)); - if(isset($devices["devices"][0])){ - $device = $devices["devices"][0]; - if(isset($device["modules"]) && isset($device["modules"][0])){ - - //Retrieve getthermstate - $thermostat_id = $device["modules"][0]; - echo "Retrieving thermstate for $thermostat_id/$device_id\n"; - $thermstate = $client->api("getthermstate", "POST", array("device_id" => $device_id, "module_id" => $thermostat_id)); - if(isset($thermstate["measured"])){ - echo ("-----------------\n"); - echo ("- Last measures -\n"); - echo ("-----------------\n"); - print_r($thermstate["measured"]); - } - if(isset($thermstate["setpoint_order"])){ - echo ("--------------------------\n"); - echo ("- Pending setpoint_order -\n"); - echo ("--------------------------\n"); - print_r($thermstate["setpoint_order"]); - } - else if(isset($thermstate["setpoint"])){ - echo ("--------------------\n"); - echo ("- Current setpoint -\n"); - echo ("--------------------\n"); - print_r($thermstate["setpoint"]); - } - - //now set an froze-guard setpoint to every devices - $res = $client->api("setthermpoint", "POST", array("device_id" => $device_id, "module_id" => $thermostat_id, "setpoint_mode" => "hg")); - echo "$thermostat_id/$device_id set in froze-guard mode\n"; - } - } - } -} -catch(NAClientException $ex){ - echo "An error happend during process\n"; - echo $ex->getMessage()."\n"; -} - -?> diff --git a/CLI_THERM_ClientCredential_Example.php b/CLI_THERM_ClientCredential_Example.php deleted file mode 100755 index 0b988e9..0000000 --- a/CLI_THERM_ClientCredential_Example.php +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/php - $client_id, "client_secret" => $client_secret, "username" => $test_username, "password" => $test_password, "scope" => $scope)); - -try { - $tokens = $client->getAccessToken(); - -} -catch(NAClientException $ex) { - echo "An error happend while trying to retrieve your tokens\n"; - exit(-1); -} - -// Retrieve User Info : -$user = $client->api("getuser", "POST"); -echo ("-------------\n"); -echo ("- User Info -\n"); -echo ("-------------\n"); -//print_r($user); -echo ("OK\n"); -echo ("---------------\n"); -echo ("- Device List -\n"); -echo ("---------------\n"); -$devicelist = $client->api("devicelist", "POST", array("app_type" => "app_thermostat")); -echo ("OK\n"); -if(isset($devicelist["devices"]) && isset($devicelist["devices"][0])){ - $device = $devicelist["devices"][0]; - if(isset($device["modules"]) && isset($device["modules"][0])){ - $module = $device["modules"][0]; - echo ("-----------------\n"); - echo ("- Therm State -\n"); - echo ("-----------------\n"); - $thermstate = $client->api("getthermstate", "POST", array("device_id" => $device["_id"], "module_id" => $module)); - if(isset($thermstate["measured"])){ - echo ("-----------------\n"); - echo ("- Last measures -\n"); - echo ("-----------------\n"); - print_r($thermstate["measured"]); - } - if(isset($thermstate["setpoint_order"])){ - echo ("--------------------------\n"); - echo ("- Pending setpoint_order -\n"); - echo ("--------------------------\n"); - print_r($thermstate["setpoint_order"]); - } - else if(isset($thermstate["setpoint"])){ - echo ("--------------------\n"); - echo ("- Current setpoint -\n"); - echo ("--------------------\n"); - print_r($thermstate["setpoint"]); - } - $program = null; - if(isset($thermstate["therm_program_order"])){ - echo ("-------------------------\n"); - echo ("- Pending program_order -\n"); - echo ("-------------------------\n"); - $program = $thermstate["therm_program_order"]; - print_r($thermstate["therm_program_order"]); - } - else if(isset($thermstate["therm_program"])){ - echo ("-------------------\n"); - echo ("- Current program -\n"); - echo ("------------------\n"); - $program = $thermstate["therm_program"]; - print_r($thermstate["therm_program"]); - } - echo ("OK\n"); - - //lets syncschedule - if($program){ - $res = $client->api("syncschedule", "POST", array("device_id" => $device["_id"], "module_id" => $module, "zones" => $program["zones"], "timetable" => $program["timetable"])); - echo ("---------------------\n"); - echo ("- New program synced-\n"); - echo ("---------------------\n"); - echo ("OK\n"); - } - - //lets order a manual setpoint @19 for 2 hours - $client->api("setthermpoint", "POST", array("device_id" => $device["_id"], "module_id" => $module, "setpoint_mode" => "manual", "setpoint_endtime" => mktime() + 2*3600, "setpoint_temp" => 19)); - echo ("------------------\n"); - echo ("- bypass ordered -\n"); - echo ("------------------\n"); - echo ("OK\n"); - } -} - - -?> diff --git a/Config.php b/Config.php deleted file mode 100644 index 320c48a..0000000 --- a/Config.php +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/Examples/CLI_Therm_API_Example.php b/Examples/CLI_Therm_API_Example.php new file mode 100755 index 0000000..57f0804 --- /dev/null +++ b/Examples/CLI_Therm_API_Example.php @@ -0,0 +1,247 @@ +#!/usr/bin/php + $client_id, + "client_secret" => $client_secret, + "username" => $test_username, + "password" => $test_password, + "scope" => $scope); +$client = new NAThermApiClient($conf); + +//Retrieve access token +try +{ + $tokens = $client->getAccessToken(); +} +catch(NAClientException $ex) +{ + $error_msg = "An error happened while trying to retrieve your tokens \n" . $ex->getMessage() . "\n"; + handleError($error_msg, TRUE); +} + +//Retrieve User's thermostats info: +try +{ + $thermData = $client->getData(); +} +catch(NAClientException $ex) +{ + handleError("An error occured while retrieve thermostat data: " . $ex->getMessage() . "\n", TRUE); +} + +if(count($thermData['devices']) === 0) + echo ("You don't have any thermostats linked to your account. \n"); +else +{ + printMessageWithBorder("Thermostats Basic Information"); + + //We'll use the first device later + $device = $thermData['devices'][0]; + $module_id = $device['modules'][0]['_id']; + // keep record of current program and mode to restore it + $currentProgram = getCurrentProgram($device["modules"][0]); + list($initialMode, $initialTemp, $initialEndtime) = getCurrentMode($device["modules"][0]); + + //first print devices information + foreach($thermData['devices'] as $dev) + { + printThermBasicInfo($dev); + } + + + + //Create a new schedule + $scheduleName = "testSchedule"; + //build schedule's zones & timetable + $zones = array( + array("type" => NAThermZone::THERMOSTAT_SCHEDULE_SLOT_DAY, + "id" => 0, + "temp" => 19), + array("type" => NAThermZone::THERMOSTAT_SCHEDULE_SLOT_NIGHT, + "id" => 1, + "temp" => 17), + array("type" => NAThermZone::THERMOSTAT_SCHEDULE_SLOT_AWAY, + "id" => 2, + "temp" => 12), + array("type" => NAThermZone::THERMOSTAT_SCHEDULE_SLOT_HG, + "id" => 3, + "temp" => 7), + array("type" => NAThermZone::THERMOSTAT_SCHEDULE_SLOT_ECO, + "id" => 4, + "temp" => 16), + ); + + // weekly timetable = When to use which zone (offset in min + if of the zone) + $timetable = array( + // monday + array("m_offset" => 0, "id" => 1), + array("m_offset" => 420, "id" => 0), + array("m_offset" => 480, "id" => 4), + array("m_offset" => 1140, "id" => 0), + array("m_offset" => 1320, "id" => 1), + // tuesday + array("m_offset" => 1860, "id" => 0), + array("m_offset" => 1920, "id" => 4), + array("m_offset" => 2580, "id" => 0), + array("m_offset" => 2760, "id" => 1), + // Wednesday + array("m_offset" => 3300, "id" => 0), + array("m_offset" => 3360, "id" => 4), + array("m_offset" => 4020, "id" => 0), + array("m_offset" => 4200, "id" => 1), + // Thursday + array("m_offset" => 4740, "id" => 0), + array("m_offset" => 4800, "id" => 4), + array("m_offset" => 5460, "id" => 0), + array("m_offset" => 5640, "id" => 1), + // Friday + array("m_offset" => 6180, "id" => 0), + array("m_offset" => 6240, "id" => 4), + array("m_offset" => 6900, "id" => 0), + array("m_offset" => 7080, "id" => 1), + // Saturday + array("m_offset" => 7620, "id" => 0), + array("m_offset" => 8520, "id" => 1), + // Sunday + array("m_offset" => 9060, "id" => 0), + array("m_offset" => 9960, "id" => 1) + ); + try + { + $res = $client->createSchedule($device['_id'], $module_id, $zones, $timetable, $scheduleName); + $schedule_id = $res['schedule_id']; + printMessageWithBorder("New test Schedule created"); + } + catch(NAClientException $ex) + { + handleError("An error occured while creating a new schedule: " . $ex->getMessage() . "\n", TRUE); + } + + //Switch to our new schedule + if(!is_null($schedule_id)) + { + try + { + $client->switchSchedule($device['_id'], $module_id, $schedule_id); + } + catch(NAClientException $ex) + { + handleError("An error occured while changing the device schedule: " . $ex->getMessage(). "\n", TRUE); + } + printMessageWithBorder("Schedule changed to testSchedule"); + } + + //Let's set this thermostat to away mode + try + { + $client->setToAwayMode($device['_id'], $module_id); + printMessageWithBorder($device['station_name'] . " is now in Away Mode"); + + } + catch(NAClientException $ex) + { + handleError("An error occured while setting the thermostat to away mode: ".$ex->getMessage()."\n"); + } + + //Get daily measurements of the last 30 days + $type = "temperature,max_temp,min_temp"; + try + { + $measurements = $client->getMeasure($thermData['devices'][1]['_id'], $thermData['devices'][1]['modules'][0]['_id'], "1day", $type, time()-3600*24*30, time(), 30, FALSE, FALSE); + printMeasure($measurements, $type, $device['place']['timezone'], "Daily Measurements of the last 30 days"); + + } + catch(NAClientException $ex) + { + handleError("An error occured while retrieving measures: " . $ex->getMessage(). "\n"); + } + + + //Rename test schedule + try + { + $client->renameSchedule($device['_id'], $module_id, $schedule_id, "To be deleted"); + printMessageWithBorder("Schedule renamed"); + } + catch(NAClientException $ex) + { + handleError("An error occured while renaming schedule $schedule_id: " . $ex->getMessage() ."\n"); + } + + //switch back to previous program + if(!is_null($currentProgram)) + { + try + { + $client->switchSchedule($device['_id'], $module_id, $currentProgram); + printMessageWithBorder("Switching back to the original schedule"); + sleep(30); //wait for thermostat to reconnect so that the change will be effective and initial schedule will be set back + } + catch(NAClientException $ex) + { + handleError("An error occured while switching back to original schedule: ". $ex->getMessage() ."\n"); + } + } + // let's remove test schedule + try + { + $client->deleteSchedule($device['_id'], $module_id, $schedule_id); + printMessageWithBorder("test Schedule deleted"); + sleep(20); + } + catch(NAClientException $ex) + { + handleError("An error occured while removing schedule $schedule_id " . $ex->getMessage() ."\n"); + } + + //let's change to a manual set point : 19°C for 10 min + try + { + $client->setToManualMode($device["_id"], $module_id, 19, time() + 10 * 60); + printMessageWithBorder("Manual setpoint set"); + + } + catch(NAClientException $ex) + { + handleError("An error occured while setting a manual setpoint ". $ex->getMessage() . "\n"); + } + + //Finally set back to the intial mode + try + { + switch($initialMode) + { + case "away" : $client->setToAwayMode($device["_id"], $module_id, $initialEndtime); + break; + case "manual" : $client->setToManualMode($device["_id"], $module_id, $initialTemp, $initialEndtime); + break; + case "program": $client->setToProgramMode($device["_id"], $module_id); + break; + case "frost_guard": $client->setToFrostGuardMode($device["_id"], $module_id, $initialEndtime); + break; + case "off": $client->turnOff($device["_id"], $module_id); + break; + case "max": $client->setToMaxMode($device["_id"], $module_id, $initialEndtime); + break; + } + printMessageWithBorder("Back to original mode: " . $initialMode); + sleep(20); //wait for thermostat synchronization to make sure, it's back in its initial mode + } + catch(NAClientException $ex) + { + handleError("An error occured while setting back the initial mode: ". $ex->getMessage() . " \n"); + } +} +?> diff --git a/Examples/CLI_WS_API_Example.php b/Examples/CLI_WS_API_Example.php new file mode 100755 index 0000000..0905b6d --- /dev/null +++ b/Examples/CLI_WS_API_Example.php @@ -0,0 +1,149 @@ +#!/usr/bin/php + $client_id, + "client_secret" => $client_secret, + "username" => $test_username, + "password" => $test_password); + +$client = new NAWSApiClient($config); + +//Authentication with Netatmo server (OAuth2) +try +{ + $tokens = $client->getAccessToken(); +} +catch(NAClientException $ex) +{ + handleError("An error happened while trying to retrieve your tokens: " .$ex->getMessage()."\n", TRUE); +} + +//Retrieve user's Weather Stations Information + +try +{ + //retrieve all stations belonging to the user, and also his favorite ones + $data = $client->getData(NULL, TRUE); + printMessageWithBorder("Weather Stations Basic Information"); +} +catch(NAClientException $ex) +{ + handleError("An error occured while retrieving data: ". $ex->getMessage()."\n", TRUE); +} + +if(empty($data['devices'])) +{ + echo 'No devices affiliated to user'; +} +else +{ + + $users = array(); + $friends = array(); + $fav = array(); + $device = $data['devices'][0]; + $tz = isset($device['place']['timezone']) ? $device['place']['timezone'] : "GMT"; + + //devices are already sorted in the following way: first weather stations owned by user, then "friend" WS, and finally favorites stations. Still let's store them in different arrays according to their type + foreach($data['devices'] as $device) + { + + //favorites have both "favorite" and "read_only" flag set to true, whereas friends only have read_only + if(isset($device['favorite']) && $device['favorite']) + $fav[] = $device; + else if(isset($device['read_only']) && $device['read_only']) + $friends[] = $device; + else $users[] = $device; + } + + //print first User's device Then friends, then favorite + printDevices($users, "User's weather stations"); + printDevices($friends, "User's friends weather stations"); + printDevices($fav, "User's favorite weather stations"); + + // now get some daily measurements for the last 30 days + $type = "temperature,Co2,humidity,noise,pressure"; + + //first for the main device + try + { + $measure = $client->getMeasure($device['_id'], NULL, "1day" , $type, time() - 24*3600*30, time(), 30, FALSE, FALSE); + printMeasure($measure, $type, $tz, $device['_id'] ."'s daily measurements of the last 30 days"); + } + catch(NAClientException $ex) + { + handleError("An error occured while retrieving main device's daily measurements: " . $ex->getMessage() . "\n"); + } + + //Then for its modules + foreach($device['modules'] as $module) + { + //requested data type depends on the module's type + switch($module['type']) + { + case "NAModule3": $type = "sum_rain"; + break; + case "NAModule2": $type = "WindStrength,WindAngle,GustStrength,GustAngle,date_max_gust"; + break; + case "NAModule1" : $type = "temperature,humidity"; + break; + default : $type = "temperature,Co2,humidity,noise,pressure"; + } + try + { + $measure = $client->getMeasure($device['_id'], $module['_id'], "1day" , $type, time()-24*3600*30 , time(), 30, FALSE, FALSE); + printMeasure($measure, $type, $tz, $module['_id']. "'s daily measurements of the last 30 days "); + } + catch(NAClientException $ex) + { + handleError("An error occured while retrieving main device's daily measurements: " . $ex->getMessage() . "\n"); + } + + } + + //Finally, retrieve general info about last month for main device + $type = "max_temp,date_max_temp,min_temp,date_min_temp,max_hum,date_max_hum,min_hum,date_min_hum,max_pressure,date_max_pressure,min_pressure,date_min_pressure,max_noise,date_max_noise,min_noise,date_min_noise,max_co2,date_max_co2,min_co2,date_min_co2"; + try + { + $measures = $client->getMeasure($device['_id'], NULL, "1month", $type, NULL, "last", 1, FALSE, FALSE); + printMeasure($measures, $type, $tz, "Last month information of " .$device['_id'], TRUE); + } + catch(NAClientException $ex) + { + handleError("An error occcured while retrieving last month info: ".$ex->getMessage() . " \n"); + } +} +?> diff --git a/Examples/CLI_Welcome_API_Example.php b/Examples/CLI_Welcome_API_Example.php new file mode 100755 index 0000000..9003dc1 --- /dev/null +++ b/Examples/CLI_Welcome_API_Example.php @@ -0,0 +1,114 @@ +#!/usr/bin/php + $client_id, + "client_secret" => $client_secret, + "username" => $test_username, + "password" => $test_password, + "scope" => $scope); +$client = new NAWelcomeApiClient($conf); + +//Retrieve access token +try +{ + $tokens = $client->getAccessToken(); +} +catch(NAClientException $ex) +{ + $error_msg = "An error happened while trying to retrieve your tokens \n" . $ex->getMessage() . "\n"; + handleError($error_msg, TRUE); +} + +//Try to retrieve user's Welcome information +try +{ + //retrieve every user's homes and their last 10 events + $response = $client->getData(NULL, 10); + $homes = $response->getData(); +} +catch(NASDKException $ex) +{ + handleError("An error happened while trying to retrieve home information: ".$ex->getMessage() ."\n", TRUE); +} + +if(is_null($homes) || empty($homes)) +{ + handleError("No home found for this user...", TRUE); +} +printMessageWithBorder("User's Homes"); +foreach($homes as $home) +{ + printHomeInformation($home); +} + +$home = $homes[0]; +$tz = $home->getTimezone(); +if(!empty($home->getPersons())) +{ + $known = $home->getKnownPersons(); + $person = $known[0]; + //retrieve last every events that happened after the last time person has been seen + try + { + $response = $client->getLastEventOf($home->getId(), $person->getId()); + $eventList = $response->getData(); + } + catch(NASDKException $ex) + { + handleError("An error occured while retrieving last event of ".$person->getPseudo() . "\n"); + } + if(!empty($eventList)) + { + printMessageWithBorder("Events until last time ".$person->getPseudo(). " was seen"); + + foreach($eventList as $event) + { + printEventInformation($event, $tz); + } + // let's retrieve 10 events that happens right before last event of the given person + $lastIndex = count($eventList) -1; + $lastEvent = $eventList[$lastIndex]; + $event = $eventList[0]; + try + { + $response = $client->getNextEvents($home->getId(), $lastEvent->getId(), 10); + $data = $response->getData(); + } + catch(NASDKException $ex) + { + handleError("An error occured while retrieving events: ". $ex->getMessage(). "\n"); + } + + if(!empty($data)) + { + printMessageWithBorder("The 10 events that happened right before ". $person->getPseudo()." was seen"); + foreach($data as $event) + { + printEventInformation($event, $tz); + } + } + + try + { + printMessageWithBorder("Event's snapshot"); + echo $event->getSnapshot() . "\n"; + } + catch(NASDKException $ex) + { + handleError("An error occured while retrieving event's snapshot: ". $ex->getMessage()."\n"); + } + } +} +?> diff --git a/Examples/Config.php b/Examples/Config.php new file mode 100644 index 0000000..408fca1 --- /dev/null +++ b/Examples/Config.php @@ -0,0 +1,6 @@ + diff --git a/Examples/Utils.php b/Examples/Utils.php new file mode 100644 index 0000000..b286d21 --- /dev/null +++ b/Examples/Utils.php @@ -0,0 +1,312 @@ +setTimezone($tz); + $date->setTimestamp($time); + echo $date->format($format); +} + +function printBorder($message) +{ + $size = strlen($message); + for($i = 0; $i < $size; $i++) + echo("-"); + echo("\n"); +} + +function printMessageWithBorder($message) +{ + $message = "- " . $message . " -"; + printBorder($message); + echo $message . "\n"; + printBorder($message); +} + +function printMeasure($measurements, $type, $tz, $title = NULL, $monthly = FALSE) +{ + if(!empty($measurements)) + { + if(!empty($title)) + printMessageWithBorder($title); + + if($monthly) + $dateFormat = 'F: '; + else $dateFormat = 'j F: '; + //array of requested info type, needed to map result values to what they mean + $keys = explode(",", $type); + + foreach($measurements as $timestamp => $values) + { + printTimeinTz($timestamp, $tz, $dateFormat); + echo"\n"; + foreach($values as $key => $val) + { + echo $keys[$key] . ": "; + if($keys[$key] === "time_utc" || preg_match("/^date_.*/", $keys[$key])) + echo printTimeInTz($val, $tz, "j F H:i"); + else{ + echo $val; + printUnit($keys[$key]); + } + if(count($values)-1 === $key || $monthly) + echo "\n"; + else echo ", "; + } + } + } +} + +/** + * function printing a weather station or modules basic information such as id, name, dashboard data, modules (if main device), type(if module) + * + */ +function printWSBasicInfo($device) +{ + if(isset($device['station_name'])) + echo ("- ".$device['station_name']. " -\n"); + else if($device['module_name']) + echo ("- ".$device['module_name']. " -\n"); + + echo ("id: " . $device['_id']. "\n"); + + if(isset($device['type'])) + { + echo ("type: "); + switch($device['type']) + { + // Outdoor Module + case "NAModule1": echo ("Outdoor\n"); + break; + //Wind Sensor + case "NAModule2": echo("Wind Sensor\n"); + break; + + //Rain Gauge + case "NAModule3": echo("Rain Gauge\n"); + break; + //Indoor Module + case "NAModule4": echo("Indoor\n"); + break; + case "NAMain" : echo ("Main device \n"); + break; + } + + } + + if(isset($device['place']['timezone'])) + $tz = $device['place']['timezone']; + else $tz = 'GMT'; + + if(isset($device['dashboard_data'])) + { + echo ("Last data: \n"); + foreach($device['dashboard_data'] as $key => $val) + { + if($key === 'time_utc' || preg_match("/^date_.*/", $key)) + { + echo $key .": "; + printTimeInTz($val, $tz, 'j F H:i'); + echo ("\n"); + } + else if(is_array($val)) + { + //do nothing : don't print historic + } + else { + echo ($key .": " . $val); + printUnit($key); + echo "\n"; + } + } + + if(isset($device['modules'])) + { + echo (" \n\nModules: \n"); + foreach($device['modules'] as $module) + printWSBasicInfo($module); + } + } + + echo" ---------------------- \n"; +} + +function printUnit($key) +{ + $typeUnit = array('temp' => '°C', 'hum' => '%', 'noise' => 'db', 'strength' => 'km/h', 'angle' => '°', 'rain' => 'mm', 'pressure' => 'mbar', 'co2' => 'ppm'); + foreach($typeUnit as $type => $unit) + { + if(preg_match("/.*$type.*/i", $key)) + { + echo " ".$unit; + return; + } + } +} + +/** THERM Utils function **/ +/* +* @brief print a thermostat basic information in CLI +*/ +function printThermBasicInfo($dev) +{ + //Device + echo (" -".$dev['station_name']."- \n"); + echo (" id: ".$dev['_id']." \n"); + echo ("Modules : \n"); + // Device's modules info + foreach($dev['modules'] as $module) + { + echo (" - ".$module['module_name']." -\n"); + + //module last measurements + echo (" Last Measure date : "); + printTimeInTz($module['measured']['time'], $dev['place']['timezone'], 'j F H:i'); + echo("\n"); + echo (" Last Temperature measured: ". $module['measured']['temperature']); + printUnit("temperature"); + echo("\n"); + echo (" Last Temperature setpoint: ". $module['measured']['setpoint_temp']); + printUnit('setpoint_temp'); + echo("\n"); + echo (" Program List: \n"); + + //program list + foreach($module['therm_program_list'] as $program) + { + if(isset($program['name'])) + echo (" -".$program['name']."- \n"); + else echo(" -Standard- \n"); + echo (" id: ".$program['program_id']." \n"); + if(isset($program['selected']) && $program['selected'] === TRUE) + { + echo " This is the current program \n"; + } + } + } + +} + +/** +* @brief returns the current program of a therm module +*/ +function getCurrentProgram($module) +{ + foreach($module['therm_program_list'] as $program) + { + if(isset($program['selected']) && $program['selected'] === TRUE) + return $program['program_id']; + } + //not found + return NULL; +} + +/** +* @brief returns the current setpoint of a therm module along with its setpoint temperature and endtime if defined +*/ +function getCurrentMode($module) +{ + $initialMode = $module["setpoint"]["setpoint_mode"]; + $initialTemp = isset($module["setpoint"]["setpoint_temp"]) ? $module["setpoint"]["setpoint_temp"]: NULL; + $initialEndtime = isset($module['setpoint']['setpoint_endtime']) ? $module['setpoint']['setpoint_endtime'] : NULL; + + return array($initialMode, $initialTemp, $initialEndtime); + +} + +function printHomeInformation(NAHome $home) +{ + !is_null($home->getName()) ? printMessageWithBorder($home->getName()) : printMessageWithBorder($home->getId()); + echo ("id: ". $home->getId() ."\n"); + + $tz = $home->getTimezone(); + + if(!empty($home->getPersons())) + { + printMessageWithBorder("Persons"); + //print person list + foreach($home->getPersons() as $person) + { + printPersonInformation($person, $tz); + } + } + + if((!empty($home->getEvents()))) + { + printMessageWithBorder('Timeline of Events'); + //print event list + foreach($home->getEvents() as $event) + { + printEventInformation($event, $tz); + } + } + + if(!empty($home->getCameras())) + { + printMessageWithBorder("Cameras"); + foreach($home->getCameras() as $camera) + { + printCameraInformation($camera); + } + } +} + + +function printPersonInformation(NAPerson $person, $tz) +{ + $person->isKnown() ? printMessageWithBorder($person->getPseudo()) : printMessageWithBorder("Inconnu"); + echo("id: ". $person->getId(). "\n"); + if($person->isAway()) + echo("is away from home \n" ); + else echo("is home \n"); + + echo ("Last seen on: "); + printTimeInTz($person->getLastSeen(), $tz, "j F H:i"); + echo ("\n"); +} + +function printEventInformation(NAEvent $event, $tz) +{ + printTimeInTz($event->getTime(), $tz, "j F H:i"); + $message = removeHTMLTags($event->getMessage()); + echo(": ".$message. "\n"); +} + +function printCameraInformation(NACamera $camera) +{ + !is_null($camera->getName()) ? printMessageWithBorder($camera->getName()) : printMessageWithBorder($camera->getId()); + + echo("id: ". $camera->getId() ."\n"); + echo("Monitoring status: ". $camera->getVar(NACameraInfo::CI_STATUS) ."\n"); + echo("SD card status: " .$camera->getVar(NACameraInfo::CI_SD_STATUS) . "\n"); + echo ("Power status: ". $camera->getVar(NACameraInfo::CI_ALIM_STATUS) ."\n"); + + if($camera->getGlobalStatus()) + $globalStatus = "OK"; + else $globalStatus = "NOK"; + + echo ("Global Status: ". $globalStatus ."\n"); + +} + +function removeHTMLTags($string) +{ + return preg_replace("/<.*?>/", "", $string); +} + +?> diff --git a/Examples/WEB_AuthorizationGrant_Example.php b/Examples/WEB_AuthorizationGrant_Example.php new file mode 100644 index 0000000..b09f5ad --- /dev/null +++ b/Examples/WEB_AuthorizationGrant_Example.php @@ -0,0 +1,249 @@ + $client_id, + "client_secret" => $client_secret, + "scope" => NAScopes::SCOPE_READ_STATION); +$client = new NAWSApiClient($config); + +//if code is provided in get param, it means user has accepted your app and been redirected here +if(isset($_GET["code"])) +{ + //get the tokens, you can store $tokens['refresh_token'] in order to quickly retrieve a new access_token next time + try{ + $tokens = $client->getAccessToken(); + } + catch(NAClientException $ex) + { + echo "An error occured while trying to retrieve your tokens \n"; + echo "Reason: ".$ex->getMessage()."\n"; + die(); + } + //retrieve user's weather station data + try{ + $data = $client->getData(); + } + catch(NAClientException $ex) + { + echo "An error occured while retrieving data: ". $ex->getMessage()."\n"; + die(); + } + + if(!isset($data['devices']) || !is_array($data['devices']) || count($data['devices']) < 1) + { + echo "User has no devices \n"; + die(); + } + + //In this example, we will only deal with the first weather station linked to the user account + $device = $data['devices'][0]; + + //last month report + $reports = getMonthReport($device, $client); + + //Get device timezone + if(!is_null($device) && !empty($device)) + { + if(isset($device['place']['timezone'])) + $tz = $device['place']['timezone']; + else $tz = 'GMT'; + + //print data +?> + + + +
+

+

id :

+"; + echo "

Month report

"; + foreach($reports as $report) + { + if(is_array($report)) + printDashBoardDataInHtml($report, $tz); + else echo "

".$report."

"; + } + echo "
"; + } + ?> + + + + redirect to Netatmo OAuth + else if(isset($_GET['start'])) + { + //Ok redirect to Netatmo Authorize URL + $redirect_url = $client->getAuthorizeUrl(); + header("HTTP/1.1 ". 302); + header("Location: " . $redirect_url); + die(); + } + // Homepage : start button + else + { +?> + + +
+ +
+ + + $value) + { + echo "

"; + echo $key.": "; + if($key === 'time_utc' || preg_match("/^date_.*/", $key)) + { + printTimeInTz($value, $tz, 'j F H:i'); + } + else { + echo $value; + printUnit($key); + } + echo"

"; + } +} + +/** +* @param array $module +* @param string $tz device's timezone +* @brief print a Netatmo Weather Station's module in HTML +*/ +function printModuleInHtml($module, $tz) +{ + echo "
"; + echo "

". $module['module_name']. "

"; + echo "

id: ".$module['_id']. "

"; + echo "

type: "; + switch($module['type']) + { + case "NAModule1" : echo "Outdoor"; + break; + case "NAModule2" : echo "Wind Sensor"; + break; + case "NAModule3" : echo "Rain Gauge"; + break; + case "NAModule4" : echo "Indoor"; + break; + } + echo "

"; + printDashboardDataInHtml($module['dashboard_data'], $tz); + echo "
"; +} + +/** +* @param array $device +* @param NAWSApiClient $client +* @return array $report : array with device or module ids as keys, and their data of the month as values +* @brief retrieve month data for a device and its modules +*/ +function getMonthReport($device, $client) +{ + $report = array(); + //step between two measurements + $scale = '1month'; + //type of measures wanted + $type = "Temperature,CO2,Humidity,Pressure,Noise,max_temp,date_max_temp,min_temp,date_min_temp,max_hum,date_max_hum,min_hum,date_min_hum,max_pressure,date_max_pressure,min_pressure,date_min_pressure,max_noise,date_max_noise,min_noise,date_min_noise,max_co2,date_max_co2,min_co2,date_min_co2"; + // main device + try{ + $measure = $client->getMeasure($device['_id'], NULL, $scale, $type, NULL, "last", NULL, FALSE, FALSE); + $measure = addMeasureKeys($measure, $type); + $report[$device['_id']] = $measure; + } + catch(NAClientException $ex) + { + $report[$device['_id']] = "Error retrieving measure for ".$device['_id'].": " .$ex->getMessage(); + } + + foreach($device['modules'] as $module) + { + switch($module['type']) + { + //Outdoor + case "NAModule1": $type = "temperature,humidity"; + break; + //Wind Sensor + case "NAModule2": $type = "WindStrength,WindAngle,GustStrength,GustAngle,date_max_gust"; + break; + //Rain Gauge + case "NAModule3": $type = "sum_rain"; + break; + // Indoor + case "NAModule4": $type = "temperature,Co2,humidity,noise,pressure"; + break; + } + try{ + $measure = $client->getMeasure($device['_id'], $module['_id'], $scale, $type, NULL, "last", NULL, FALSE, FALSE); + $measure = addMeasureKeys($measure, $type); + $report[$module['_id']] = $measure; + } + catch(NAClientException $ex) + { + $report[$module['_id']] = "Error retrieving measure for " .$module['_id']. ": ".$ex->getMessage(); + } + } + return $report; +} +/** +* @param array measures : measure sent back by getMeasure method +* @param string $type : types of measure requested +* @return array $measurements : array of measures mapped by type +* @brief add the type as a key for each measure +*/ +function addMeasureKeys($measures, $type) +{ + $measurements = array(); + $keys = explode(",", $type); + foreach($measures as $measure) + { + foreach($measure as $key => $val) + { + $measurements[$keys[$key]] = $val; + } + } + return $measurements; +} +?> diff --git a/README.md b/README.md index 26f794c..f1adbcc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,356 @@ -Netatmo-API-PHP -=============== +# Netatmo-API-PHP -Netatmo Weather Station API client implementation - PHP SDK +This release is a major update, and might break the compatibility with older versions. If you were using an older version of our SDK, you will have to stop using NAApiClient class and use either NAThermApiClient or NAWSApiClient instead. -Please read http://dev.netatmo.com/doc/sdk/php to have more information +As a matter of fact, those are adding an additional abstraction layer which will make it easier for you to use : API call using "api" client method are now replaced with dedicated methods along with the parameters they expect for each product API. + +You also might need to change the include path of the SDK files as they are now stored in the "src" folder (API clients are stored in "src/Clients/" folder). + +## Install and Configure + +To install the sdk, extract the downloaded files to your project directory. Then, just include NAWSApiClient.php (for Netatmo Weather Station) or NAThermApiClient.php (for Netatmo Thermostat), or NAWelcomeApiClient.php (for Netatmo Welcome) depending on what you need and instantiate a new NAWSApiClient (or NAThermApiClient) object with your application client_id and client_secret: + + + require_once('src/Clients/NAWSApiClient.php'); + + $config = array(); + $config['client_id'] = "YOUR_APP_ID"; + $config['client_secret'] = "YOUR_APP_SECRET"; + $config['scope'] = 'read_station read_thermostat write_thermostat'; + $client = new NAApiClient($config); + +## Authenticate & Authorize + +The SDK provides helper-methods to authenticate and authorize your app to access a user's data + +### Retrieving an access token through a user credential grant + + $username = "YOUR_USERNAME"; + $pwd = "YOUR_PWD"; + $client->setVariable('username', $username); + $client->seVariable('password', $pwd); + try + { + $tokens = $client->getAccessToken(); + $refresh_token = $tokens["refresh_token"]; + $access_token = $tokens["access_token"]; + } + catch(NAClientException $ex) + { + echo "An error occcured while trying to retrive your tokens \n"; + } + +### Retrieving an access token through Authorization code grant + + //test if "code" is provided in get parameters (which would mean that user has already accepted the app and has been redirected here) + if(isset($_GET['code'])) + { + try + { + $tokens = $client->getAccessToken(); + $refresh_token = $tokens['refresh_token']; + $access_token = $tokens['access_token']; + } + catch(NAClientException $ex) + { + echo " An error occured while trying to retrieve your tokens \n"; + } + } + else if(isset($_GET['error'])) + { + if($_GET['error'] === 'access_denied') + echo "You refused that this application access your Netatmo Data"; + else echo "An error occured \n"; + } + else + { + //redirect to Netatmo Authorize URL + $redirect_url = $client->getAuthorizeUrl(); + header("HTTP/1.1 ". OAUTH2_HTTTP_FOUND); + header("Location: ". $redirect_url); + die(); + } + +## Use API methods + +### Netatmo Weather Station API client implementation - PHP SDK + +If you need further information about how the Netatmo Weather Station works, please see https://dev.netatmo.com/doc/devices/weatherstation + +#### Retrieving Netatmo Weather Station's data + +Once an access token has been retrieved, it can be used to retrieved user's devices data + + + $data = $client->getData(NULL, TRUE); + foreach($data['devices'] as $device) + { + echo device['station_name'] . "\n"; + print_r($device['dashboard_data']); + foreach($device['modules'] as $module) + { + echo $module['module_name']; + print_r($module['dashboard_data']); + } + } + +$data will be an array containing a list of devices along with their modules and their last data stored: First the devices belonging to the user, then user's friends devices, and finally the user's favorites devices. + +To retrieve a specific device's data, you have to provide its id: + + $data = $client->getData("YOUR_DEVICE_ID"); + +If it is one of the user's favorite device, you'll have to set the "get_favorites" parameter to TRUE in order to retrieve it: + + $data = $client->getData("YOUR_FAV_DEVICE_ID", TRUE); + +#### Retrieving Netatmo Weather Station's measurements + +You can also retrieve specific measurements of a given period for a given device: + + $device = $data['devices'][0]; + $module = $device['modules'][0]; + $scale = "1day"; + $type = $module['data_type']; + $date_begin = time() -24*3600*7; //1 week ago + $date_end = time() //now + $optimized = FALSE; + $real_time = FALSE; + $measurements = $client->getMeasure($device['_id'], $module_id, $scale, $type, $date_begin, $date_end, $optimized, $real_time); + +Please note that not giving a module_id will retrieve main device measurements. +The scale parameter corresponds to the step between two measures, the type depends on the device or module data_type and the scale. For more details regarding its allowed values, please take a glance at https://dev.netatmo.com/doc/getmeasure. +Set the optimized flag to true, if you need the json response to be lighter but a little trickier to parse : you'll only have the beginning timestamp provided, and you'll have to compute the others using the scale. +Finally, the real_time flag enables to remove the timestamp offset in scales higher than 'max'. As a matter of fact, since data are aggregated timestamps are offset by + scale/2 by default. + +### Netatmo Thermostat API client implementation - PHP SDK + +If you need further information about how the Netatmo Thermostat works, please see https://dev.netatmo.com/doc/devices/thermostat + +#### Retrieving Netatmo Thermostat's data + +The Thermostat API client just works the same way as the Weather Station API Client: + + $client = new NAThermApiClient($config); + +It also has its own method to retrieve user's thermostats data which returns an array of devices along with their data + + $data = $client->getData(); + +In order to retrieve a specific thermostat, you need to provide its id + + $data = $client->getData($device_id); + +#### Retrieving Netatmo Thermostat's measurements + +Thermostat also has its getMeasure method, which works in the exact same way as the weather station one, in order to retrieve specific data of a given period: + + $device = $data['devices'][0]; + $module = $device['modules'][0]; + $scale = "1day"; + $type = "Temperature,max_temp,min_temp,date_max_temp"; + $date_begin = time() -24*3600*7; //1 week ago + $date_end = time() //now + $optimized = FALSE; + $real_time = FALSE; + $measurements = $client->getMeasure($device['_id'], $module_id, $scale, $type, $date_begin, $date_end, $optimized, $real_time); + + +#### Sending orders to the Netatmo Thermostat + +You can easily change the thermostat setpoint: + + try{ + $client->setToAwayMode($device['_id'], $module['_id']); + $client->setToFrostGuardMode($device['_id'], $module['_id']); + $client->setToProgramMode($device['_id'], $module['_id']); + } + catch(NAClientException $ex) + { + "An error occured"; + } + +And you can also give time-limited orders: + + $endtime = time() +2*3600; + $client->setToAwayMode($device['_id'], $module['_id'], $endtime); // away for 2 hours + $client->setToFrostGuardMode($device['_id'], $module['_id'], $endtime); + +Some orders require a time-limit: + + $endtime = time() + 2*3600; + $client->setToMaxMode($device['_id'], $module['_id'], $endtime); + +Finally, you have the ability to just set a Manual temperature setpoint to your thermostat: + + $endtime = time() + 2*3600; + $temp = 21.5; + $client->setToManualMode($device['_id'], $module['_id'], $temp, $endtime); + +You also have the possibility to turn off your thermostat: + + $client->turnOff($device['_id'], $module['_id']); + +(To turn it back on, set another setpoint ex : order the thermostat to follow its schedule) + +#### Dealing with Netatmo Thermostat's weekly schedules + +The Netatmo Thermostat SDK also enables you to deal with the thermostat schedule: + +You can create a new schedule: + + $res = $client->createSchedule($device['_id'], $module['_id'], $zones, $timetable, 'new schedule'); + +You'll find the id of the created schedule in the return value. + + $schedule_id = $res['schedule_id']; + +Then switch the thermostat current schedule to another existing schedule: + + $client->switchSchedule($device['_id'], $module['_id'], $schedule_id); + +And modify your current schedule: + + $client->syncSchedule($device['_id'], $module['_id'], $zones, $timetable); + +Then you can rename a schedule: + + $client->renameSchedule($device['_id'], $module['_id'], $schedule_id, 'new Name'); + +Finally, you can also delete a schedule: + + $client->deleteSchedule($device['_id'], $module['_id'], $schedule_id); + +### Netatmo Welcome API client implementation - PHP SDK + +If you need more information regarding how the Netatmo Welcome works, please see https://dev.netatmo.com/doc/devices/camera + +### Retrieving Netatmo Welcome data + +The Netatmo Welcome SDK works in a slightly different way than Netatmo Weather Stations and Netatmo Thermostat SDK do. +First, instantiate the client: + + $client = new NAWelcomeApiClient($config); + +The Netatmo Welcome SDK enables you to retrieve the data (persons, cameras, events) of every home belonging to an user: + + $res = $client->getData(): + +If you only want to retrieve information about a specific user's home, just specify its id: + + $res = client->getData($home_id); + +Please note that you can limit the number of events requested per home, using the $size parameter (default value is 30): + + $res = client->getData(NULL, 10); // will retrieve every home belonging to the user and only the 10 last associated events + +The difference with Weather Station and Thermostats SDK is that the client returns NAResponseHandler objects. Those enable you to either get the raw data returned by the Netatmo API as PHP arrays or to get it as array of objects (NAHome or NAEvent): + + $array = $res->getDecodedBody(); + $objects = $res->getData(); + + +### Retrieving specific events from a given home + +In addition of retrieving all information for a home, you can also get only events you're interested in. In that purpose, you can use the three following methods: + +- First, you can retrieve every event until the last one of a specific person + + $res = $client->getLastEventOf($home_id, $person_id); + $events = $res->getData(); + + This function also accepts an offset parameter, which gives the abitility to retrieve a certain number of events older than the requested one. Default value is 0. + + $res = $client->getLastEventOf($home_id, $event_id, 5); + $events = $res->getData(); + +- Then, you also have the ability to retrieve every events until a specific one + + $res = $client->getEventsUntil($home_id, $event_id); + $events = $res->getData(); + +- Finally, you can also get events that happened before a specific one. In that purpose, you should specify how many events you want to retrieve using the $size parameter. Default value is 30. + + $res = $client->getNextEvents($home_id, $event_id, 10); // get the 10 events that happened right before event_id + $events = $res->getData(); + +### Retrieving a picture linked to an event or a person face + +After retrieving event's or person's information using one of the previous methods, you might want to be able to get the picture corresponding to an event snapshot or a person face. In order to do so, you will need the snapshot or face information of that event if you're dealing with raw data in PHP array: + + $img = $client->getCameraPicture($event['snapshot']); + +If you are working with the objects provided by the SDK, just call the object getter on the picture, to retrieve its URL: + + $pictureURL = $event->getSnapshot(); + $faceURL = $person->getFace(); + +### Quick Example + + $scope = NAScopes::SCOPE_READ_CAMERA; + + //Client configuration from Config.php + $conf = array("client_id" => $client_id, + "client_secret" => $client_secret, + "username" => $test_username, + "password" => $test_password, + "scope" => $scope); + $client = new NAWelcomeApiClient($conf); + + //Retrieve access token + try + { + $tokens = $client->getAccessToken(); + } + catch(NAClientException $ex) + { + echo "An error happened while trying to retrieve your tokens \n" . $ex->getMessage() . "\n"; + } + + //Try to retrieve user's Welcome information + try + { + //retrieve every user's homes and their last 10 events + $response = $client->getData(NULL, 10); + $homes = $response->getData(); + } + catch(NASDKException $ex) + { + echo "An error happened while trying to retrieve home information: ".$ex->getMessage() ."\n"; + } + + //print homes names + foreach($homes as $home) + { + echo $home->getName() . "\n"; + } + +## Error handling + +###SDK Errors +The SDK throws NASDKException when an error happens. It encapsulates Networking errors, API errors and SDK objects error for Netatmo Welcome SDK. + + try{ + $client->getData(); + } + catch(NASDKException $ex) + { + //Handle error here + echo $ex->getMessage(); + } + + +### API Client errors +The client throws NAClientException if it encounters an error dealing with the API or Networking protocol: + + try{ + $client->getData(); + } + catch(NAClientException $ex) + { + //Handle error here + echo $ex->getMessage(); + } + +## Details +Please read https://dev.netatmo.com/doc for further information. diff --git a/WEB_AuthorisationCode_Example.php b/WEB_AuthorisationCode_Example.php deleted file mode 100644 index aa7959e..0000000 --- a/WEB_AuthorisationCode_Example.php +++ /dev/null @@ -1,143 +0,0 @@ - $client_id, "client_secret" => $client_secret, "scope" => NAScopes::SCOPE_READ_STATION)); - -//Test if code is provided in get parameters (that means user has already accepted the app and has been redirected here) -if(isset($_GET["code"])) -{ - try - { - // Get the token for later usage.(you can store $tokens["refresh_token"] for retrieving a new access_token next time) - $tokens = $client->getAccessToken(); - } - catch(NAClientException $ex) - { - echo "An error happend while trying to retrieve your tokens\n"; - echo "Reason : ".$ex->getMessage()."\n"; - die(); - } - try - { - $helper = new NAApiHelper($client); - - $user = $helper->api("getuser", "POST"); - $devicelist = $helper->simplifyDeviceList(); - $mesures = $helper->getLastMeasures($client,$devicelist); -?> -

-
-        
-getMessage()."\n"; - } -} -else -{ - if(isset($_GET["error"])) - { - if($_GET["error"] == "access_denied") - echo "You refused to let application access your netatmo data\n"; - else - echo "An error happend\n"; - } - else if(isset($_GET["start"])) - { - //Ok redirect to Netatmo Authorize URL - $redirect_url = $client->getAuthorizeUrl(); - header("HTTP/1.1 ". 302); - header("Location: " . $redirect_url); - die(); - } - else - { -?> - - -
- -
- - - 0 && $json[$c-1] != '\\') { - $in_string = !$in_string; - } - default: - $new_json .= $char; - break; - } - } - return $new_json; -} - -?> diff --git a/NAApiClient.php b/src/Clients/NAApiClient.php similarity index 94% rename from NAApiClient.php rename to src/Clients/NAApiClient.php index 51ad2b3..1390188 100644 --- a/NAApiClient.php +++ b/src/Clients/NAApiClient.php @@ -1,5 +1,7 @@ . - */ -class NAClientException extends Exception -{ - public $error_type; - /** - * Make a new API Exception with the given result. - * - * @param $result - * The result from the API server. - */ - public function __construct($code, $message, $error_type) - { - $this->error_type = $error_type; - parent::__construct($message, $code); - } -} -class NAApiErrorType extends NAClientException -{ - public $http_code; - public $http_message; - public $result; - function __construct($code, $message, $result) - { - $this->http_code = $code; - $this->http_message = $message; - $this->result = $result; - if(isset($result["error"]) && is_array($result["error"]) && isset($result["error"]["code"])) - { - parent::__construct($result["error"]["code"], $result["error"]["message"], API_ERROR_TYPE); - } - else - { - parent::__construct($code, $message, API_ERROR_TYPE); - } - } -} - -class NACurlErrorType extends NAClientException -{ - function __construct($code, $message) - { - parent::__construct($code, $message, CURL_ERROR_TYPE); - } -} - -class NAJsonErrorType extends NAClientException -{ - function __construct($code, $message) - { - parent::__construct($code, $message, JSON_ERROR_TYPE); - } -} - -class NAInternalErrorType extends NAClientException -{ - function __construct($message) - { - parent::__construct(0, $message, INTERNAL_ERROR_TYPE); - } -} - -class NANotLoggedErrorType extends NAClientException -{ - function __construct($code, $message) - { - parent::__construct($code, $message, NOT_LOGGED_ERROR_TYPE); - } -} - /** * OAuth2.0 Netatmo client-side implementation. * @@ -255,6 +185,7 @@ public function __construct($config = array()) CURLOPT_HTTPHEADER => array("Accept: application/json"), ); + /** * Makes an HTTP request. * @@ -811,6 +742,11 @@ protected function getUri($path = '', $params = array(), $secure = false) return $url; } + + public function getPartnerDevices() + { + return $this->api("partnerdevices", "POST"); + } } /** * API Helpers @@ -940,8 +876,10 @@ public function getAllMeasures($date_begin) } $results[] = $result; } - return($results); + return($results); } } + + ?> diff --git a/src/Clients/NAThermApiClient.php b/src/Clients/NAThermApiClient.php new file mode 100644 index 0000000..2ae4e64 --- /dev/null +++ b/src/Clients/NAThermApiClient.php @@ -0,0 +1,229 @@ + + */ +class NAThermApiClient extends NAApiClient +{ + + private function setThermPoint($device_id, $module_id, $mode, $endtime = NULL, $temp = NULL) + { + $params = array('device_id' => $device_id, + 'module_id' => $module_id, + 'setpoint_mode' => $mode); + + if(!is_null($endtime)) $params['setpoint_endtime'] = $endtime; + if(!is_null($temp)) $params['setpoint_temp'] = $temp; + + return $this->api('setthermpoint', 'POST', $params); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @return array of devices + * @brief Method used to retrieve data for the given Thermostat or all the thermostats belonging to the user + */ + public function getData($device_id = NULL) + { + $params = array(); + if(!is_null($device_id)) $params['device_id'] = $device_id; + + return $this->api('getthermostatsdata', 'GET', $params); + } + + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @param timestamp (utc) $endtime (optional) + * @brief Sets the given thermostat to away mode until the given date or until change. + */ + public function setToAwayMode($device_id, $module_id, $endtime = NULL) + { + return $this->setThermPoint($device_id, $module_id, 'away', $endtime); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @param timestamp (utc) $endtime (optional) + * @brief Sets the given thermostat to frost-guard mode until the given date or until change + */ + public function setToFrostGuardMode($device_id, $module_id, $endtime = NULL) + { + return $this->setThermPoint($device_id, $module_id, 'hg', $endtime); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @brief Turn off the given thermostat + */ + public function turnOff($device_id, $module_id) + { + return $this->setThermPoint($device_id, $module_id, 'off'); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @brief Order the given thermostat to follow its schedule + */ + public function setToProgramMode($device_id, $module_id) + { + return $this->setThermPoint($device_id, $module_id, 'program'); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @param float $temp + * @param timestamp (utc) $endtime (optional) + * @brief Sets a manual temperature to the given thermostat for a specified amount of time + */ + public function setToManualMode($device_id, $module_id, $temp, $endtime = NULL) + { + return $this->setThermPoint($device_id, $module_id, 'manual', $endtime, $temp); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @param timestamp(utc) $endtime + * @brief Order the given thermostat to heat to its max temperature + */ + public function setToMaxMode($device_id, $module_id, $endtime) + { + return $this->setThermPoint($device_id, $module_id, 'max', $endtime); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @param array $zones + * @param array $timetable + * @param string $name + * @brief Create a new heating schedule for the given thermostat + */ + public function createSchedule($device_id, $module_id, $zones, $timetable, $name) + { + $params = array('device_id' => $device_id, + 'module_id' => $module_id, + 'zones' => $zones, + 'timetable' => $timetable, + 'name' => $name); + return $this->api('createnewschedule', 'POST', $params); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @param string $schedule_id + * @brief switch to the given existing heating schedule + */ + public function switchSchedule($device_id, $module_id, $schedule_id) + { + $params = array('device_id' => $device_id, + 'module_id' => $module_id, + 'schedule_id' => $schedule_id); + return $this->api('switchschedule', 'POST', $params); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $device_id + * @param string $schedule_id + * @param string $name + * @brief Rename an existing heating schedule + */ + public function renameSchedule($device_id, $module_id, $schedule_id, $name) + { + $params= array('device_id' => $device_id, + 'module_id' => $module_id, + 'schedule_id' => $schedule_id, + 'name' => $name); + return $this->api('renameschedule', 'POST', $params); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @param string $schedule_id + * @brief Delete the given heating schedule. Beware, there should always be at least one schedule left for the device. + */ + public function deleteSchedule($device_id, $module_id, $schedule_id) + { + $params = array('device_id' => $device_id, + 'module_id' => $module_id, + 'schedule_id' => $schedule_id); + return $this->api('deleteschedule', 'POST', $params); + } + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id + * @param array $zones + * @param array $timetable + * @brief change the thermostat's heating schedule the given one + */ + public function syncSchedule($device_id, $module_id, $zones, $timetable) + { + $params = array('device_id' => $device_id, + 'module_id' => $module_id, + 'zones' => $zones, + 'timetable' => $timetable); + return $this->api('syncschedule', 'POST', $params); + } + + /* + * @type PUBLIC, PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id (optional) if specified will retrieve the module's measurements, else it will retrieve the main device's measurements + * @param string scale : interval of time between two measurements. Allowed values : max, 30min, 1hour, 3hours, 1day, 1week, 1month + * @param string type : type of measurements you wanna retrieve. Ex : "Sp_Temperature, Temperature". + * @param timestamp (utc) $start (optional) : starting timestamp of requested measurements + * @param timestamp (utc) $end (optional) : ending timestamp of requested measurements. + * @param int $limit (optional) : limits numbers of measurements returned (default & max : 1024) + * @param bool $optimize (optional) : optimize the bandwith usage if true. Optimize = FALSE enables an easier result parsing + * @param bool $realtime (optional) : Remove time offset (+scale/2) for scale bigger than max + * @return array of measures and timestamp + * @brief Method used to retrieve specifig measures of the given weather station + */ + + public function getMeasure($device_id, $module_id = NULL, $scale, $type, $start = NULL, $end = NULL, $limit = NULL, $optimize = NULL, $realtime = NULL) + { + $params = array('device_id' => $device_id, + 'scale' => $scale, + 'type' => $type); + + $optionals = array('module_id' => $module_id, + 'date_begin' => $start, + 'date_end' => $end, + 'limit' => $limit, + 'optimize' => $optimize, + 'real_time' => $realtime); + foreach($optionals as $key => $value) + { + if(!is_null($value)) $params[$key] = $value; + } + + return $this->api('getmeasure', 'GET', $params); + } +} +?> diff --git a/src/Clients/NAWSApiClient.php b/src/Clients/NAWSApiClient.php new file mode 100644 index 0000000..db8a4d3 --- /dev/null +++ b/src/Clients/NAWSApiClient.php @@ -0,0 +1,79 @@ + + */ +class NAWSApiClient extends NAApiClient +{ + + /* + * @type PRIVATE & PARTNER API + * @param string $device_id + * @param bool $get_favorites : used to retrieve (or not) user's favorite public weather stations + * @return array of devices + * @brief Method used to retrieve data for the given weather station or all weather station linked to the user + */ + public function getData($device_id = NULL, $get_favorites = TRUE) + { + return $this->api('getstationsdata', 'GET', array($device_id, $get_favorites)); + } + + /* + * @type PUBLIC, PRIVATE & PARTNER API + * @param string $device_id + * @param string $module_id (optional) if specified will retrieve the module's measurements, else it will retrieve the main device's measurements + * @param string $scale : interval of time between two measurements. Allowed values : max, 30min, 1hour, 3hours, 1day, 1week, 1month + * @param string $type : type of measurements you wanna retrieve. Ex : "Temperature, CO2, Humidity". + * @param timestamp (utc) $start (optional) : starting timestamp of requested measurements + * @param timestamp (utc) $end (optional) : ending timestamp of requested measurements. + * @param int $limit (optional) : limits numbers of measurements returned (default & max : 1024) + * @param bool $optimize (optional) : optimize the bandwith usage if true. Optimize = FALSE enables an easier result parsing + * @param bool $realtime (optional) : Remove time offset (+scale/2) for scale bigger than max + * @return array of measures and timestamp + * @brief Method used to retrieve specifig measures of the given weather station + */ + public function getMeasure($device_id, $module_id, $scale, $type, $start = NULL, $end = NULL, $limit = NULL, $optimize = NULL, $realtime = NULL) + { + $params = array('device_id' => $device_id, + 'scale' => $scale, + 'type' => $type); + + $optionals = array('module_id' => $module_id, + 'date_begin' => $start, + 'date_end' => $end, + 'limit' => $limit, + 'optimize' => $optimize, + 'real_time' => $realtime); + foreach($optionals as $key => $value) + { + if(!is_null($value)) $params[$key] = $value; + } + + return $this->api('getmeasure', 'GET', $params); + } + + public function getRainMeasure($device_id, $rainGauge_id, $scale, $start = NULL, $end = NULL, $limit = NULL, $optimize = NULL, $realtime = NULL) + { + if($scale === "max") + { + $type = "Rain"; + } + else $type = "sum_rain"; + + return $this->getMeasure($device_id, $rainGauge_id, $scale, $type, $start, $end, $limit, $optimize, $realtime); + } + + public function getWindMeasure($device_id, $windSensor_id, $scale, $start = NULL, $end = NULL, $limit = NULL, $optimize = NULL, $realtime = NULL) + { + $type = "WindStrength,WindAngle,GustStrength,GustAngle,date_max_gust"; + return $this->getMeasure($device_id, $windSensor_id, $scale, $type, $start, $end, $limit, $optimize, $realtime); + } + +} + +?> diff --git a/src/Clients/NAWelcomeApiClient.php b/src/Clients/NAWelcomeApiClient.php new file mode 100644 index 0000000..1a1608e --- /dev/null +++ b/src/Clients/NAWelcomeApiClient.php @@ -0,0 +1,100 @@ + + */ +class NAWelcomeApiClient extends NAApiClient +{ + + /* + * @type PRIVATE API + * @param string $home_id + * @param integer size (optional): number of events requested per home + * @return NAResponseHandler + * @brief Method use to retrieve data for the given home, or all the home belonging to the user + */ + public function getData($home_id = NULL, $size = 30) + { + $params = array('size' => $size); + if(!is_null($home_id)) $params['home_id'] = $home_id; + + return new NAResponseHandler($this->api('gethomedata', $params)); + } + + /* + * @type PRIVATE API + * @param string $home_id + * @param string $person_id + * @param integer $offset (optional): number of events you want to retrieve further than the last event of the given person. Default 0. + * @return NAResponseHandler + * @brief Method used to retrieve every events until the last one of the given person (plus eventually the given offset) + */ + public function getLastEventOf($home_id, $person_id, $offset = 0) + { + $params = array("home_id" => $home_id, + "person_id" => $person_id, + "offset" => $offset); + + return new NAResponseHandler($this->api("getlasteventof", $params)); + } + + /* + * @type PRIVATE API + * @param string $home_id + * @param string $event_id + * @return NAResponseHandler + * @brief Method used to retrieve every events until the given one + */ + public function getEventsUntil($home_id, $event_id) + { + $params = array("home_id" => $home_id, + "event_id" => $event_id); + + return new NAResponseHandler($this->api("geteventsuntil", $params)); + } + + /* + * @type PRIVATE API + * @param string $home_id + * @param string $event_id + * @param size (optional) Number of events you want to retrieve. Default 30 + * @return NAResponseHandler + * @brief Method used to retrieve events older than the given one + */ + public function getNextEvents($home_id, $event_id, $size = 30) + { + $params = array("home_id" => $home_id, + "event_id" => $event_id, + "size" => $size); + + return new NAResponseHandler($this->api("getnextevents", $params)); + } + + /* + * @type PRIVATE API + * @param array $picture: contains picture information such as id, version & key. Correspond to events snapshot or persons face + * @return picture's URL + * @brief Method used to retrieve an event snapshot or a person face + */ + public function getCameraPicture($picture) + { + + if(isset($picture['id']) && isset($picture['key'])) + { + $src = $this->getVariable('services_uri'); + $src.= "/getcamerapicture?image_id=".$picture['id']."&key=".$picture['key']; + return $src; + } + else throw new NAApiErrorType(NARestErrorCode::MISSING_ARGS, "Missing args", NULL); + + } +} + + +?> diff --git a/src/Constants/AppliCommonPublic.php b/src/Constants/AppliCommonPublic.php new file mode 100644 index 0000000..0ee897a --- /dev/null +++ b/src/Constants/AppliCommonPublic.php @@ -0,0 +1,374 @@ + 8, don't use this value +} + +class NAPluvioCalibration +{ + const RAIN_SCALE_MIN = 0.01; + const RAIN_SCALE_MAX = 0.25; + const RAIN_SCALE_ML_MIN = 0; + const RAIN_SCALE_ML_MAX = 3; +} + +//CAMERA SPECIFIC DATA +class NACameraEventType +{ + const CET_PERSON = "person"; + const CET_PERSON_AWAY = "person_away"; + const CET_MODEL_IMPROVED = "model_improved"; + const CET_MOVEMENT = "movement"; + const CET_CONNECTION = "connection"; + const CET_DISCONNECTION = "disconnection"; + const CET_ON = "on"; + const CET_OFF = "off"; + const CET_END_RECORDING = "end_recording"; + const CET_LIVE = "live_rec"; + const CET_BOOT = "boot"; + const CET_SD = "sd"; + const CET_ALIM = "alim"; +} + +class NACameraEventInfo +{ + const CEI_ID = "id"; + const CEI_TYPE = "type"; + const CEI_TIME = "time"; + const CEI_PERSON_ID = "person_id"; + const CEI_SNAPSHOT = "snapshot"; + const CEI_VIDEO_ID = "video_id"; + const CEI_VIDEO_STATUS = "video_status"; + const CEI_CAMERA_ID = "camera_id"; + const CEI_MESSAGE = "message"; + const CEI_SUB_TYPE = "sub_type"; + const CEI_IS_ARRIVAL = "is_arrival"; + const CEI_ALARM_ID = "alarm_id"; + const CEI_ALARM_TYPE = "alarm_type"; +} + +class NACameraPersonInfo +{ + const CPI_ID = "id"; + const CPI_LAST_SEEN = "last_seen"; + const CPI_FACE = "face"; + const CPI_OUT_OF_SIGHT = "out_of_sight"; + const CPI_PSEUDO = "pseudo"; + const CPI_IS_CURRENT_USER = "is_current_user"; +} + +class NACameraImageInfo +{ + const CII_ID = "id"; + const CII_VERSION = "version"; + const CII_KEY = "key"; +} + +class NACameraHomeInfo +{ + const CHI_ID = "id"; + const CHI_NAME = "name"; + const CHI_PLACE = "place"; + const CHI_PERSONS = "persons"; + const CHI_EVENTS = "events"; + const CHI_CAMERAS = "cameras"; +} + +class NACameraInfo +{ + const CI_ID = "id"; + const CI_NAME = "name"; + const CI_LIVE_URL = "live_url"; + const CI_STATUS = "status"; + const CI_SD_STATUS = "sd_status"; + const CI_ALIM_STATUS = "alim_status"; + const CI_IS_LOCAL = "is_local"; + const CI_VPN_URL = "vpn_url"; +} + +class NACameraStatus +{ + const CS_ON = "on"; + const CS_OFF = "off" ; + const CS_DISCONNECTED = "disconnected"; +} + +class APIResponseFields +{ + const APIRF_SYNC_ORDER = "sync"; + const APIRF_KEEP_RECORD_ORDER = "keep_record"; + const APIRF_VIDEO_ID = "video_id"; + const APIRF_SYNC_ORDER_LIST = "sync_order_list"; + const APIRF_EVENTS_LIST = "events_list"; + const APIRF_PERSONS_LIST = "persons_list"; + const APIRF_HOMES = "homes"; + const APIRF_USER = "user"; + const APIRF_GLOBAL_INFO = "global_info"; +} + + +class NACameraVideoStatus +{ + const CVS_RECORDING = "recording"; + const CVS_DELETED = "deleted"; + const CVS_AVAILABLE = "available"; + const CVS_ERROR = "error"; +} + +class NACameraSDEvent +{ + const CSDE_ABSENT = 1; + const CSDE_INSERTED = 2; + const CSDE_FORMATED = 3; + const CSDE_OK = 4; + const CSDE_DEFECT = 5; + const CSDE_INCOMPATIBLE = 6; + const CSDE_TOO_SMALL = 7; + + static $issueEvents = array(NACameraSDEvent::CSDE_ABSENT, NACameraSDEvent::CSDE_DEFECT, NACameraSDEvent::CSDE_INCOMPATIBLE, NACameraSDEvent::CSDE_TOO_SMALL); +} + +class NACameraAlimSubStatus +{ + const CASS_DEFECT = 1; + const CASS_OK = 2; +} + +class NAThermZone { +const THERMOSTAT_SCHEDULE_SLOT_DAY = 0x00; +const THERMOSTAT_SCHEDULE_SLOT_NIGHT = 0x01; +const THERMOSTAT_SCHEDULE_SLOT_AWAY = 0x02; +const THERMOSTAT_SCHEDULE_SLOT_HG = 0x03; +const THERMOSTAT_SCHEDULE_SLOT_PERSO = 0x04; +const THERMOSTAT_SCHEDULE_SLOT_ECO = 0x05; +const THERMOSTAT_SCHEDULE_HOT_WATER_ON = 0x06; +const THERMOSTAT_SCHEDULE_HOT_WATER_OFF = 0x07; +} + +?> diff --git a/src/Exceptions/NAClientException.php b/src/Exceptions/NAClientException.php new file mode 100644 index 0000000..31b6ce4 --- /dev/null +++ b/src/Exceptions/NAClientException.php @@ -0,0 +1,79 @@ +. + */ +class NAClientException extends NASDKException +{ + public $error_type; + /** + * Make a new API Exception with the given result. + * + * @param $result + * The result from the API server. + */ + public function __construct($code, $message, $error_type) + { + $this->error_type = $error_type; + parent::__construct($message, $code); + } +} + + +class NAApiErrorType extends NAClientException +{ + public $http_code; + public $http_message; + public $result; + function __construct($code, $message, $result) + { + $this->http_code = $code; + $this->http_message = $message; + $this->result = $result; + if(isset($result["error"]) && is_array($result["error"]) && isset($result["error"]["code"])) + { + parent::__construct($result["error"]["code"], $result["error"]["message"], API_ERROR_TYPE); + } + else + { + parent::__construct($code, $message, API_ERROR_TYPE); + } + } +} + +class NACurlErrorType extends NAClientException +{ + function __construct($code, $message) + { + parent::__construct($code, $message, CURL_ERROR_TYPE); + } +} + +class NAJsonErrorType extends NAClientException +{ + function __construct($code, $message) + { + parent::__construct($code, $message, JSON_ERROR_TYPE); + } +} + +class NAInternalErrorType extends NAClientException +{ + function __construct($message) + { + parent::__construct(0, $message, INTERNAL_ERROR_TYPE); + } +} + +class NANotLoggedErrorType extends NAClientException +{ + function __construct($code, $message) + { + parent::__construct($code, $message, NOT_LOGGED_ERROR_TYPE); + } +} +?> diff --git a/src/Exceptions/NASDKException.php b/src/Exceptions/NASDKException.php new file mode 100644 index 0000000..5ff1bfe --- /dev/null +++ b/src/Exceptions/NASDKException.php @@ -0,0 +1,21 @@ + diff --git a/src/Handlers/NAResponseHandler.php b/src/Handlers/NAResponseHandler.php new file mode 100644 index 0000000..d4527d7 --- /dev/null +++ b/src/Handlers/NAResponseHandler.php @@ -0,0 +1,167 @@ +decodedBody = $responseBody; + } + + /** + * @return array $decodedBody + * @brief return raw data retrieved from Netatmo API + */ + public function getDecodedBody() + { + return $this->decodedBody; + } + + /** + * return array $dataCollection : array of home or event objects + * @brief return data as collection objects + * @throw NASDKException + */ + public function getData() + { + if(!is_null($this->decodedBody) && !empty($this->decodedBody)) + { + if(is_null($this->dataCollection) || empty($this->dataCollection)) + { + if(isset($this->decodedBody['homes'])) + { + $this->buildHomeCollectionFromResponse(); + } + else if (isset($this->decodedBody['events_list'])) + { + $this->buildEventCollectionFromResponse(); + } + } + + return $this->dataCollection; + + } + + else throw new NASDKException(NASDKError::UNABLE_TO_CAST, "Empty Response."); + + } + + /** + * @brief convert raw data to home objects + * @throw NASDKException + */ + public function buildHomeCollectionFromResponse() + { + $this->validateCastToHomeCollection(); + + $homeCollection = array(); + + foreach($this->decodedBody['homes'] as $homeArray) + { + $this->validateCastToHome($homeArray); + $home = new NAHome($homeArray); + $homeCollection[] = $home; + } + + $this->dataCollection = $homeCollection; + } + + /** + * @brief check if array of data is castable to collection of home objects + * @throw NASDKException + */ + protected function validateCastToHomeCollection() + { + if(is_array($this->decodedBody) && $this->validateArrayForCast($this->decodedBody, 'homes')) + return; + + throw new NASDKException(NASDKError::UNABLE_TO_CAST, "Unable to cast data to NAHome object"); + } + + /** + * @brief check if array of data is castable to NAHome object + * @throw NASDKException + */ + protected function validateCastToHome($data) + { + if(isset($data['id']) + && $this->validateArrayForCast($data, 'persons') + && $this->validateArrayForCast($data, 'events') + && $this->validateArrayForCast($data, 'cameras') + ) + return; + + throw new NASDKException(NASDKError::UNABLE_TO_CAST, "Unable to cast data to home object"); + } + + /** + * @brief check if array is castable + */ + private function validateArrayForCast($array, $name) + { + if(isset($array[$name]) && is_array($array[$name])) + return true; + else return false; + } + + /** + * @brief convert raw data to collection of event objects + * @throw NASDKException + */ + public function buildEventCollectionFromResponse() + { + $this->validateCastToEventCollection(); + + $eventCollection = array(); + + foreach($this->decodedBody['events_list'] as $eventArray) + { + $this->validateCastToEventObject($eventArray); + $event = new NAEvent($eventArray); + $eventCollection[] = $event; + } + + $this->dataCollection = $eventCollection; + } + + /** + * @brief check if array of data is castable to collection of event objects + * @throw NASDKException + */ + protected function validateCastToEventCollection() + { + if(is_array($this->decodedBody) + && $this->validateArrayForCast($this->decodedBody, 'events_list') + ) + return; + + throw new NASDKException(NASDKError::UNABLE_TO_CAST, "Impossible to cast to NAEvent"); + } + + /** + * @brief check if array of data is castable to NAEvent object + * @throw NASDKException + */ + protected function validateCastToEventObject($data) + { + if(isset($data['id']) && isset($data['time']) && isset($data['type'])) + return; + + throw new NASDKException(NASDKError::UNABLE_TO_CAST, "Unable to cast data to event object"); + } +} +?> diff --git a/src/Objects/NACamera.php b/src/Objects/NACamera.php new file mode 100644 index 0000000..6f9f862 --- /dev/null +++ b/src/Objects/NACamera.php @@ -0,0 +1,75 @@ +getVar(NACameraInfo::CI_STATUS); + $sd = $this->getVar(NACameraInfo::CI_SD_STATUS); + $power = $this->getVar(NACameraInfo::CI_ALIM_STATUS); + + if($on_off === NACameraStatus::CS_ON + && $sd === NACameraStatus::CS_ON + && $power === NACameraStatus::CS_ON) + { + return TRUE; + } + return FALSE; + } + + /** + * @return string $name + * @brief returns the camera name + */ + public function getName() + { + return $this->getVar(NACameraInfo::CI_NAME); + } + + /** + * @return string $vpn_url + * @brief returns the vpn_url of the camera + * @throw new NASDKErrorException + */ + public function getVpnUrl() + { + if(!is_null($this->getVar(NACameraInfo::CI_VPN_URL))) + return $this->getVar(NACameraInfo::CI_VPN_URL); + else throw new NASDKErrorException(NASDKErrorCode::FORBIDDEN_OPERATION, "You don't have access to this field due to the scope of your application"); + } + + /** + * @return boolean $is_local + * @brief returns whether or not the camera shares the same public address than this application + * @throw new NASDKErrorException + */ + public function isLocal() + { + if(!is_null($this->getVar(NACameraInfo::CI_IS_LOCAL))) + return $this->getVar(NACameraInfo::CI_IS_LOCAL); + else throw new NASDKErrorException(NASDKErrorCode::FORBIDDEN_OPERATION, "You don't have access to this field due to the scope of your application"); + } + + public function getSDCardStatus() + { + return $this->getVar(NACameraInfo::CI_SD_STATUS); + } + + public function getPowerAdapterStatus() + { + return $this->getVar(NACameraInfo::CI_ALIM_STATUS); + } + + public function getMonitoringStatus() + { + return $this->getVar(NACameraInfo::CI_STATUS); + } +} + +?> diff --git a/src/Objects/NAEvent.php b/src/Objects/NAEvent.php new file mode 100644 index 0000000..ec2f07b --- /dev/null +++ b/src/Objects/NAEvent.php @@ -0,0 +1,137 @@ +getVar(NACameraEventInfo::CEI_SNAPSHOT); + return $this->getPictureURL($snapshot); + } + + /** + * @return string + * @brief returns event's description + */ + public function getMessage() + { + return $this->getVar(NACameraEventInfo::CEI_MESSAGE); + } + + /** + * @return timestamp + * @brief returns at which time the event has been triggered + */ + public function getTime() + { + return $this->getVar(NACameraEventInfo::CEI_TIME); + } + + /** + * @return string + * @brief returns the event's type + */ + public function getEventType() + { + return $this->getVar(NACameraEventInfo::CEI_TYPE); + } + + /** + * @return int + * @brief returns event's subtype for SD Card & power adapter events + * @throw NASDKException + */ + public function getEventSubType() + { + if($this->getEventType() === NACameraEventType::CET_SD + || $this->getEventType() === NACameraEventType::CET_ALIM) + { + return $this->getVar(NACameraEventInfo::CEI_SUB_TYPE); + } + else throw new NASDKException(NASDKError::INVALID_FIELD, "This field does not exist for this type of event"); + } + + /** + * @return string + * @brief returns id of the camera that triggered the event + */ + public function getCameraId() + { + return $this->getVar(NACameraEventInfo::CEI_CAMERA_ID); + } + + /** + * @return string + * @brief returns id of the person seen in the event + * @throw NASDKException + */ + public function getPersonId() + { + if($this->getEventType() === NACameraEventType::CET_PERSON + || $this->getEventType() === NACameraEventType::CET_PERSON_AWAY + ) + { + return $this->getVar(NACameraEventInfo::CEI_PERSON_ID); + } + else throw new NASDKException(NASDKError::INVALID_FIELD, "This field does not exist for this type of event"); + + } + + public function hasVideo() + { + if(in_array($this->getEventType(), $this->videoEvents)) + return TRUE; + else return FALSE; + } + + /** + * @return string + * @brief returns event's video id + * @throw NASDKException + */ + public function getVideo() + { + if($this->hasVideo()) + return $this->getVar(NACameraEventInfo::CEI_VIDEO_ID); + else throw new NASDKException(NASDKError::INVALID_FIELD, "This type of event does not have videos"); + } + + /** + * @return string + * @brief returns event's video status + * @throw NASDKException + */ + public function getVideoStatus() + { + if($this->hasVideo()) + return $this->getVar(NACameraEventInfo::CEI_VIDEO_STATUS); + else throw new NASDKException(NASDKError::INVALID_FIELD, "This type of event does not have videos"); + + } + + /** + * @return boolean + * @brief returns whether or not this event corresponds to the moment where the person arrived home + * @throw NASDKException + */ + public function isPersonArrival() + { + if($this->getEventType() === NACameraEventType::CET_PERSON) + { + return $this->getVar(NACameraEventInfo::CEI_IS_ARRIVAL); + } + else throw new NASDKException(NASDKError::INVALID_FIELD, "This field does not exist for this type of event"); + + } +} +?> diff --git a/src/Objects/NAHome.php b/src/Objects/NAHome.php new file mode 100644 index 0000000..48d0e26 --- /dev/null +++ b/src/Objects/NAHome.php @@ -0,0 +1,191 @@ +object[NACameraHomeInfo::CHI_PERSONS] = $personArray; + } + + if(isset($array[NACameraHomeInfo::CHI_EVENTS])) + { + $eventArray = array(); + foreach($array[NACameraHomeInfo::CHI_EVENTS] as $event) + { + $eventArray[] = new NAEvent($event); + } + $this->object[NACameraHomeInfo::CHI_EVENTS] = $eventArray; + } + + if(isset($array[NACameraHomeInfo::CHI_CAMERAS])) + { + $cameraArray = array(); + foreach($array[NACameraHomeInfo::CHI_CAMERAS] as $camera) + { + $cameraArray[] = new NACamera($camera); + } + $this->object[NACameraHomeInfo::CHI_CAMERAS] = $cameraArray; + } + } + + /** + * @return string + * @brief returns home's name + */ + public function getName() + { + return $this->getVar(NACameraHomeInfo::CHI_NAME); + } + + /** + * @return array of event objects + * @brief returns home timeline of event + */ + public function getEvents() + { + return $this->getVar(NACameraHomeInfo::CHI_EVENTS, array()); + } + + /** + * @return array of person objects + * @brief returns every person belonging to this home + */ + public function getPersons() + { + return $this->getVar(NACameraHomeInfo::CHI_PERSONS, array()); + } + + /** + * @return array of person objects + * @brief returns every known person belonging to this home + */ + public function getKnownPersons() + { + $knowns = array(); + foreach($this->getVar(NACameraHomeInfo::CHI_PERSONS, array()) as $person) + { + if($person->isKnown()) + $knowns[] = $person; + } + return $knowns; + } + + /** + * @return array of person objects + * @brief returns every unknown person belonging to this home + */ + public function getUnknownPersons() + { + $unknowns = array(); + foreach($this->getVar(NACameraHomeInfo::CHI_PERSONS, array()) as $person) + { + if($person->isUnknown()) + $unknowns[] = $person; + } + return $unknowns; + } + + /** + * @return array of camera objects + * @brief returns every camera belonging to this home + */ + public function getCameras() + { + return $this->getVar(NACameraHomeInfo::CHI_CAMERAS, array()); + } + + /** + * @return string + * @brief returns home's timezone + */ + public function getTimezone() + { + $place = $this->getVar(NACameraHomeInfo::CHI_PLACE); + return isset($place['timezone'])? $place['timezone'] : 'GMT'; + } + + /** + * @return NACamera + * @brief return the camera object corresponding to the id asked + * @throw NASDKErrorException + */ + public function getCamera($camera_id) + { + foreach($this->getVar(NACameraHomeInfo::CHI_CAMERAS, array()) as $camera) + { + if($camera->getId() === $camera_id) + { + return $camera; + } + } + throw new NASDKException(NASDKError::NOT_FOUND, "camera $camera_id not found in home: " . $this->getId()); + } + + /** + * @return NAPerson + * @brief returns NAPerson object corresponding to the id in parameter + * @throw NASDKErrorException + */ + public function getPerson($person_id) + { + foreach($this->getVar(NACameraHomeInfo::CHI_PERSONS, array()) as $camera) + { + if($person->getId() === $person_id) + return $person; + } + + throw new NASDKException(NASDKError::NOT_FOUND, "person $person_id not found in home: " . $this->getId()); + } + + /** + * @return array of NAPerson + * @brief returns every person that are not home + */ + public function getPersonAway() + { + $away = array(); + + foreach($this->getVar(NACameraHomeInfo::CHI_PERSONS, array()) as $person) + { + if($person->isAway()) + $away[] = $person; + } + return $away; + } + + /** + * @return array of NAPerson + * @brief returns every person that are home + */ + public function getPersonAtHome() + { + $home = array(); + + foreach($this->getVar(NACameraHomeInfo::CHI_PERSONS, array()) as $person) + { + if(!$person->isAway()) + $home[] = $person; + } + return $home; + } +} +?> diff --git a/src/Objects/NAObject.php b/src/Objects/NAObject.php new file mode 100644 index 0000000..1332035 --- /dev/null +++ b/src/Objects/NAObject.php @@ -0,0 +1,92 @@ +object = $array; + } + + /** + * @param string field : array key + * @param $default : default value in case field is not set + * @return object field or default if field is not set + * @brief returns an object's field + */ + public function getVar($field, $default = NULL) + { + if(isset($this->object[$field])) + return $this->object[$field]; + else return $default; + } + + /** + * @param string $field : field to be set + * @param $value value to set to field + * @brief set an object's field + */ + public function setVar($field, $value) + { + $this->object[$field] = $value; + } + + /** + * @return id + * @btief returns object id + */ + public function getId() + { + return $this->getVar("id"); + } + + /** + * @return array $object + * @brief return this object as an array + */ + public function toArray() + { + return $this->object; + } + + /** + * @return JSON document + * @brief returns object as a JSON document + */ + public function toJson() + { + return json_encode($this->toArray()); + } + + /** + * @return string + * @brief return string representation of object : JSON doc + */ + public function __toString() + { + return $this->toJson(); + } + +} + +abstract class NAObjectWithPicture extends NAObject +{ + public function getPictureURL($picture, $baseURI = 'https://api.netatmo.com/api/getcamerapicture') + { + if(isset($picture[NACameraImageInfo::CII_ID]) && isset($picture[NACameraImageInfo::CII_KEY])) + { + return $baseURI.'?image_id='.$picture[NACameraImageInfo::CII_ID].'&key='.$picture[NACameraImageInfo::CII_KEY]; + } + else return NULL; + + } + +} +?> diff --git a/src/Objects/NAPerson.php b/src/Objects/NAPerson.php new file mode 100644 index 0000000..08493be --- /dev/null +++ b/src/Objects/NAPerson.php @@ -0,0 +1,64 @@ +getVar(NACameraPersonInfo::CPI_PSEUDO, FALSE)) + return TRUE; + else return FALSE; + } + + /** + * @return bool + * @brief returns whether or not this person is unknown + */ + + public function isUnknown() + { + return !$this->isKnown(); + } + + /** + * @return bool + * @brief returns whether or not this person is at home + */ + public function isAway() + { + return $this->getVar(NACameraPersonInfo::CPI_OUT_OF_SIGHT); + } + + public function getFace() + { + $face = $this->getVar(NACameraPersonInfo::CPI_FACE); + return $this->getPictureURL($face); + } + + /** + * @return timestamp + * @brief returns last time this person has been seen + */ + public function getLastSeen() + { + return $this->getVar(NACameraPersonInfo::CPI_LAST_SEEN); + } + + /** + * @return string + * @brief returns this person's name + */ + public function getPseudo() + { + return $this->getVar(NACameraPersonInfo::CPI_PSEUDO); + } +} +?>