/fitbitphp.php

https://github.com/clyons/fitbitphp · PHP · 2346 lines · 1532 code · 247 blank · 567 comment · 241 complexity · 2b025815e76b06b666885d4274e6ed57 MD5 · raw file

  1. <?php
  2. /**
  3. * FitbitPHP v.0.70. Basic Fitbit API wrapper for PHP using OAuth
  4. *
  5. * Note: Library is in beta and provided as-is. We hope to add features as API grows, however
  6. * feel free to fork, extend and send pull requests to us.
  7. *
  8. * - https://github.com/heyitspavel/fitbitphp
  9. *
  10. *
  11. * Date: 2011/12/09
  12. * Requires OAuth 1.0.0, SimpleXML
  13. * @version 0.70 ($Id$)
  14. */
  15. class FitBitPHP
  16. {
  17. /**
  18. * API Constants
  19. *
  20. */
  21. private $authHost = 'www.fitbit.com';
  22. private $apiHost = 'api.fitbit.com';
  23. private $baseApiUrl;
  24. private $authUrl;
  25. private $requestTokenUrl;
  26. private $accessTokenUrl;
  27. /**
  28. * Class Variables
  29. *
  30. */
  31. protected $oauth;
  32. protected $oauth_Token, $oauth_Secret;
  33. protected $userId = '-';
  34. protected $metric = 0;
  35. protected $userAgent = 'FitbitPHP 0.70';
  36. protected $debug;
  37. protected $clientDebug;
  38. /**
  39. * @param string $consumer_key Application consumer key for Fitbit API
  40. * @param string $consumer_secret Application secret
  41. * @param int $debug Debug mode (0/1) enables OAuth internal debug
  42. * @param string $userAgent User-agent to use in API calls
  43. */
  44. public function __construct($consumer_key, $consumer_secret, $debug = 1, $userAgent = null)
  45. {
  46. $this->initUrls();
  47. $this->consumer_key = $consumer_key;
  48. $this->consumer_secret = $consumer_secret;
  49. $this->oauth = new OAuth($consumer_key, $consumer_secret, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_AUTHORIZATION);
  50. $this->debug = $debug;
  51. if (isset($userAgent))
  52. $this->userAgent = $userAgent;
  53. if ($debug)
  54. $this->oauth->enableDebug();
  55. }
  56. /**
  57. * @param string $consumer_key Application consumer key for Fitbit API
  58. * @param string $consumer_secret Application secret
  59. */
  60. public function reinit($consumer_key, $consumer_secret)
  61. {
  62. $this->consumer_key = $consumer_key;
  63. $this->consumer_secret = $consumer_secret;
  64. $this->oauth = new OAuth($consumer_key, $consumer_secret, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_AUTHORIZATION);
  65. if ($debug)
  66. $this->oauth->enableDebug();
  67. }
  68. /**
  69. * @param string $apiHost API host, i.e. api.fitbit.com (do you know any others?)
  70. * @param string $authHost Auth host, i.e. www.fitbit.com
  71. */
  72. public function setEndpointBase($apiHost, $authHost, $https = true, $httpsApi = false)
  73. {
  74. $this->apiHost = $apiHost;
  75. $this->authHost = $authHost;
  76. $this->initUrls($https, $httpsApi);
  77. }
  78. private function initUrls($https = true, $httpsApi = false)
  79. {
  80. if ($httpsApi)
  81. $this->baseApiUrl = 'https://' . $this->apiHost . '/1/';
  82. else
  83. $this->baseApiUrl = 'http://' . $this->apiHost . '/1/';
  84. if ($https) {
  85. $this->authUrl = 'https://' . $this->authHost . '/oauth/authorize';
  86. $this->requestTokenUrl = 'https://' . $this->apiHost . '/oauth/request_token';
  87. $this->accessTokenUrl = 'https://' . $this->apiHost . '/oauth/access_token';
  88. } else {
  89. $this->authUrl = 'http://' . $this->authHost . '/oauth/authorize';
  90. $this->requestTokenUrl = 'http://' . $this->apiHost . '/oauth/request_token';
  91. $this->accessTokenUrl = 'http://' . $this->apiHost . '/oauth/access_token';
  92. }
  93. }
  94. /**
  95. * @return OAuth debugInfo object for previous call. Debug should be enabled in __construct
  96. */
  97. public function oauthDebug()
  98. {
  99. return $this->oauth->debugInfo;
  100. }
  101. /**
  102. * @return OAuth debugInfo object for previous client_customCall. Debug should be enabled in __construct
  103. */
  104. public function client_oauthDebug()
  105. {
  106. return $this->clientDebug;
  107. }
  108. /**
  109. * Returns Fitbit session status for frontend (i.e. 'Sign in with Fitbit' implementations)
  110. *
  111. * @return int (0 - no session, 1 - just after successful authorization, 2 - session exist)
  112. */
  113. public static function sessionStatus()
  114. {
  115. $session = session_id();
  116. if (empty($session)) {
  117. session_start();
  118. }
  119. if (empty($_SESSION['fitbit_Session']))
  120. $_SESSION['fitbit_Session'] = 0;
  121. return (int)$_SESSION['fitbit_Session'];
  122. }
  123. /**
  124. * Initialize session. Inits OAuth session, handles redirects to Fitbit login/authorization if needed
  125. *
  126. * @param $callbackUrl Callback for 'Sign in with Fitbit'
  127. * @param $cookie Use persistent cookie for authorization, or session cookie only
  128. * @return int (1 - just after successful authorization, 2 - if session already exist)
  129. */
  130. public function initSession($callbackUrl, $cookie = true)
  131. {
  132. $session = session_id();
  133. if (empty($session)) {
  134. session_start();
  135. }
  136. if (empty($_SESSION['fitbit_Session']))
  137. $_SESSION['fitbit_Session'] = 0;
  138. if (!isset($_GET['oauth_token']) && $_SESSION['fitbit_Session'] == 1)
  139. $_SESSION['fitbit_Session'] = 0;
  140. if ($_SESSION['fitbit_Session'] == 0) {
  141. $request_token_info = $this->oauth->getRequestToken($this->requestTokenUrl, $callbackUrl);
  142. $_SESSION['fitbit_Secret'] = $request_token_info['oauth_token_secret'];
  143. $_SESSION['fitbit_Session'] = 1;
  144. header('Location: ' . $this->authUrl . '?oauth_token=' . $request_token_info['oauth_token']);
  145. exit;
  146. } else if ($_SESSION['fitbit_Session'] == 1) {
  147. $this->oauth->setToken($_GET['oauth_token'], $_SESSION['fitbit_Secret']);
  148. $access_token_info = $this->oauth->getAccessToken($this->accessTokenUrl);
  149. $_SESSION['fitbit_Session'] = 2;
  150. $_SESSION['fitbit_Token'] = $access_token_info['oauth_token'];
  151. $_SESSION['fitbit_Secret'] = $access_token_info['oauth_token_secret'];
  152. $this->setOAuthDetails($_SESSION['fitbit_Token'], $_SESSION['fitbit_Secret']);
  153. return 1;
  154. } else if ($_SESSION['fitbit_Session'] == 2) {
  155. $this->setOAuthDetails($_SESSION['fitbit_Token'], $_SESSION['fitbit_Secret']);
  156. return 2;
  157. }
  158. }
  159. /**
  160. * Reset session
  161. *
  162. * @return void
  163. */
  164. public function resetSession()
  165. {
  166. $_SESSION['fitbit_Session'] = 0;
  167. }
  168. /**
  169. * Sets OAuth token/secret. Use if library used in internal calls without session handling
  170. *
  171. * @param $token
  172. * @param $secret
  173. * @return void
  174. */
  175. public function setOAuthDetails($token, $secret)
  176. {
  177. $this->oauth_Token = $token;
  178. $this->oauth_Secret = $secret;
  179. $this->oauth->setToken($this->oauth_Token, $this->oauth_Secret);
  180. }
  181. /**
  182. * Get OAuth token
  183. *
  184. * @return string
  185. */
  186. public function getOAuthToken()
  187. {
  188. return $this->oauth_Token;
  189. }
  190. /**
  191. * Get OAuth secret
  192. *
  193. * @return string
  194. */
  195. public function getOAuthSecret()
  196. {
  197. return $this->oauth_Secret;
  198. }
  199. /**
  200. * Set Fitbit userId for future API calls
  201. *
  202. * @param $userId 'XXXXX'
  203. * @return void
  204. */
  205. public function setUser($userId)
  206. {
  207. $this->userId = $userId;
  208. }
  209. /**
  210. * Set Unit System for all future calls (see http://wiki.fitbit.com/display/API/API-Unit-System)
  211. * 0 (Metric), 1 (en_US), 2 (en_GB)
  212. *
  213. * @param int $metric
  214. * @return void
  215. */
  216. public function setMetric($metric)
  217. {
  218. $this->metric = $metric;
  219. }
  220. /**
  221. * API wrappers
  222. *
  223. */
  224. /**
  225. * Get user profile
  226. *
  227. * @throws FitBitException
  228. * @param string $userId UserId of public profile, if none using set with setUser or '-' by default
  229. * @return SimpleXMLElement
  230. */
  231. public function getProfile()
  232. {
  233. $headers = $this->getHeaders();
  234. try {
  235. $this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/profile.xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  236. } catch (Exception $E) {
  237. }
  238. $response = $this->oauth->getLastResponse();
  239. $responseInfo = $this->oauth->getLastResponseInfo();
  240. if (!strcmp($responseInfo['http_code'], '200')) {
  241. $xml = simplexml_load_string($response);
  242. if ($xml)
  243. return $xml;
  244. else
  245. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  246. } else {
  247. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  248. }
  249. }
  250. /**
  251. * Update user profile
  252. *
  253. * @throws FitBitException
  254. * @param string $gender 'FEMALE', 'MALE' or 'NA'
  255. * @param DateTime $birthday Date of birth
  256. * @param string $height Height in cm/inches (as set with setMetric)
  257. * @param string $nickname Nickname
  258. * @param string $fullName Full name
  259. * @param string $timezone Timezone in the format 'America/Los_Angeles'
  260. * @return SimpleXMLElement
  261. */
  262. public function updateProfile($gender = null, $birthday = null, $height = null, $nickname = null, $fullName = null, $timezone = null)
  263. {
  264. $headers = $this->getHeaders();
  265. $parameters = array();
  266. if (isset($gender))
  267. $parameters['gender'] = $gender;
  268. if (isset($birthday))
  269. $parameters['birthday'] = $birthday->format('Y-m-d');
  270. if (isset($height))
  271. $parameters['height'] = $height;
  272. if (isset($nickname))
  273. $parameters['nickname'] = $nickname;
  274. if (isset($fullName))
  275. $parameters['fullName'] = $fullName;
  276. if (isset($timezone))
  277. $parameters['timezone'] = $timezone;
  278. try {
  279. $this->oauth->fetch($this->baseApiUrl . "user/-/profile.xml",
  280. $parameters, OAUTH_HTTP_METHOD_POST, $headers);
  281. } catch (Exception $E) {
  282. }
  283. $response = $this->oauth->getLastResponse();
  284. $responseInfo = $this->oauth->getLastResponseInfo();
  285. if (!strcmp($responseInfo['http_code'], '201')) {
  286. $xml = simplexml_load_string($response);
  287. if ($xml)
  288. return $xml;
  289. else
  290. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  291. } else {
  292. $xml = simplexml_load_string($response);
  293. if (!$xml)
  294. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  295. else
  296. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  297. }
  298. }
  299. /**
  300. * Get user activities for specific date
  301. *
  302. * @throws FitBitException
  303. * @param DateTime $date
  304. * @param String $dateStr
  305. * @return SimpleXMLElement
  306. */
  307. public function getActivities($date, $dateStr = null)
  308. {
  309. $headers = $this->getHeaders();
  310. if (!isset($dateStr)) {
  311. $dateStr = $date->format('Y-m-d');
  312. }
  313. try {
  314. $this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/activities/date/" . $dateStr . ".xml",
  315. null, OAUTH_HTTP_METHOD_GET, $headers);
  316. } catch (Exception $E) {
  317. }
  318. $response = $this->oauth->getLastResponse();
  319. $responseInfo = $this->oauth->getLastResponseInfo();
  320. if (!strcmp($responseInfo['http_code'], '200')) {
  321. $xml = simplexml_load_string($response);
  322. if ($xml)
  323. return $xml;
  324. else
  325. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  326. } else {
  327. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  328. }
  329. }
  330. /**
  331. * Get user recent activities
  332. *
  333. * @throws FitBitException
  334. * @return SimpleXMLElement
  335. */
  336. public function getRecentActivities()
  337. {
  338. $headers = $this->getHeaders();
  339. try {
  340. $this->oauth->fetch($this->baseApiUrl . "user/-/activities/recent.xml", null,
  341. OAUTH_HTTP_METHOD_GET, $headers);
  342. } catch (Exception $E) {
  343. }
  344. $response = $this->oauth->getLastResponse();
  345. $responseInfo = $this->oauth->getLastResponseInfo();
  346. if (!strcmp($responseInfo['http_code'], '200')) {
  347. $xml = simplexml_load_string($response);
  348. if ($xml)
  349. return $xml;
  350. else
  351. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  352. } else {
  353. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  354. }
  355. }
  356. /**
  357. * Get user frequent activities
  358. *
  359. * @throws FitBitException
  360. * @return SimpleXMLElement
  361. */
  362. public function getFrequentActivities()
  363. {
  364. $headers = $this->getHeaders();
  365. try {
  366. $this->oauth->fetch($this->baseApiUrl . "user/-/activities/frequent.xml", null,
  367. OAUTH_HTTP_METHOD_GET, $headers);
  368. } catch (Exception $E) {
  369. }
  370. $response = $this->oauth->getLastResponse();
  371. $responseInfo = $this->oauth->getLastResponseInfo();
  372. if (!strcmp($responseInfo['http_code'], '200')) {
  373. $xml = simplexml_load_string($response);
  374. if ($xml)
  375. return $xml;
  376. else
  377. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  378. } else {
  379. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  380. }
  381. }
  382. /**
  383. * Get user favorite activities
  384. *
  385. * @throws FitBitException
  386. * @return SimpleXMLElement
  387. */
  388. public function getFavoriteActivities()
  389. {
  390. $headers = $this->getHeaders();
  391. try {
  392. $this->oauth->fetch($this->baseApiUrl . "user/-/activities/favorite.xml", null,
  393. OAUTH_HTTP_METHOD_GET, $headers);
  394. } catch (Exception $E) {
  395. }
  396. $response = $this->oauth->getLastResponse();
  397. $responseInfo = $this->oauth->getLastResponseInfo();
  398. if (!strcmp($responseInfo['http_code'], '200')) {
  399. $xml = simplexml_load_string($response);
  400. if ($xml)
  401. return $xml;
  402. else
  403. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  404. } else {
  405. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  406. }
  407. }
  408. /**
  409. * Log user activity
  410. *
  411. * @throws FitBitException
  412. * @param DateTime $date Activity date and time (set proper timezone, which could be fetched via getProfile)
  413. * @param string $activityId Activity Id (or Intensity Level Id) from activities database,
  414. * see http://wiki.fitbit.com/display/API/API-Log-Activity
  415. * @param string $duration Duration millis
  416. * @param string $calories Manual calories to override Fitbit estimate
  417. * @param string $distance Distance in km/miles (as set with setMetric)
  418. * @param string $distanceUnit Distance unit string (see http://wiki.fitbit.com/display/API/API-Distance-Unit)
  419. * @return SimpleXMLElement
  420. */
  421. public function logActivity($date, $activityId, $duration, $calories = null, $distance = null, $distanceUnit = null, $activityName = null)
  422. {
  423. $distanceUnits = array('Centimeter', 'Foot', 'Inch', 'Kilometer', 'Meter', 'Mile', 'Millimeter', 'Steps', 'Yards');
  424. $headers = $this->getHeaders();
  425. $parameters = array();
  426. $parameters['date'] = $date->format('Y-m-d');
  427. $parameters['startTime'] = $date->format('H:i');
  428. if (isset($activityName)) {
  429. $parameters['activityName'] = $activityName;
  430. $parameters['manualCalories'] = $calories;
  431. } else {
  432. $parameters['activityId'] = $activityId;
  433. if (isset($calories))
  434. $parameters['manualCalories'] = $calories;
  435. }
  436. $parameters['durationMillis'] = $duration;
  437. if (isset($distance))
  438. $parameters['distance'] = $distance;
  439. if (isset($distanceUnit) && in_array($distanceUnit, $distanceUnits))
  440. $parameters['distanceUnit'] = $distanceUnit;
  441. try {
  442. $this->oauth->fetch($this->baseApiUrl . "user/-/activities.xml", $parameters,
  443. OAUTH_HTTP_METHOD_POST, $headers);
  444. } catch (Exception $E) {
  445. }
  446. $response = $this->oauth->getLastResponse();
  447. $responseInfo = $this->oauth->getLastResponseInfo();
  448. if (!strcmp($responseInfo['http_code'], '201')) {
  449. $xml = simplexml_load_string($response);
  450. if ($xml)
  451. return $xml;
  452. else
  453. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  454. } else {
  455. $xml = simplexml_load_string($response);
  456. if (!$xml)
  457. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  458. else
  459. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  460. }
  461. }
  462. /**
  463. * Delete user activity
  464. *
  465. * @throws FitBitException
  466. * @param string $id Activity log id
  467. * @return bool
  468. */
  469. public function deleteActivity($id)
  470. {
  471. $headers = $this->getHeaders();
  472. try {
  473. $this->oauth->fetch($this->baseApiUrl . "user/-/activities/" . $id . ".xml", null,
  474. OAUTH_HTTP_METHOD_DELETE, $headers);
  475. } catch (Exception $E) {
  476. }
  477. $responseInfo = $this->oauth->getLastResponseInfo();
  478. if (!strcmp($responseInfo['http_code'], '204')) {
  479. return true;
  480. } else {
  481. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  482. }
  483. }
  484. /**
  485. * Add user favorite activity
  486. *
  487. * @throws FitBitException
  488. * @param string $id Activity log id
  489. * @return bool
  490. */
  491. public function addFavoriteActivity($id)
  492. {
  493. $headers = $this->getHeaders();
  494. try {
  495. $this->oauth->fetch($this->baseApiUrl . "user/-/activities/log/favorite/" . $id . ".xml",
  496. null, OAUTH_HTTP_METHOD_POST, $headers);
  497. } catch (Exception $E) {
  498. }
  499. $responseInfo = $this->oauth->getLastResponseInfo();
  500. if (!strcmp($responseInfo['http_code'], '201')) {
  501. return true;
  502. } else {
  503. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  504. }
  505. }
  506. /**
  507. * Delete user favorite activity
  508. *
  509. * @throws FitBitException
  510. * @param string $id Activity log id
  511. * @return bool
  512. */
  513. public function deleteFavoriteActivity($id)
  514. {
  515. $headers = $this->getHeaders();
  516. try {
  517. $this->oauth->fetch($this->baseApiUrl . "user/-/activities/log/favorite/" . $id . ".xml",
  518. null, OAUTH_HTTP_METHOD_DELETE, $headers);
  519. } catch (Exception $E) {
  520. }
  521. $responseInfo = $this->oauth->getLastResponseInfo();
  522. if (!strcmp($responseInfo['http_code'], '204')) {
  523. return true;
  524. } else {
  525. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  526. }
  527. }
  528. /**
  529. * Get full description of specific activity
  530. *
  531. * @throws FitBitException
  532. * @param string $id Activity log Id
  533. * @return SimpleXMLElement
  534. */
  535. public function getActivity($id)
  536. {
  537. $headers = $this->getHeaders();
  538. try {
  539. $this->oauth->fetch($this->baseApiUrl . "activities/" . $id . ".xml", null,
  540. OAUTH_HTTP_METHOD_GET, $headers);
  541. } catch (Exception $E) {
  542. }
  543. $response = $this->oauth->getLastResponse();
  544. $responseInfo = $this->oauth->getLastResponseInfo();
  545. if (!strcmp($responseInfo['http_code'], '200')) {
  546. $xml = simplexml_load_string($response);
  547. if ($xml)
  548. return $xml;
  549. else
  550. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  551. } else {
  552. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  553. }
  554. }
  555. /**
  556. * Get a tree of all valid Fitbit public activities as well as private custom activities the user createds
  557. *
  558. * @throws FitBitException
  559. * @return SimpleXMLElement
  560. */
  561. public function browseActivities()
  562. {
  563. $headers = $this->getHeaders();
  564. try {
  565. $this->oauth->fetch($this->baseApiUrl . "activities.xml", null,
  566. OAUTH_HTTP_METHOD_GET, $headers);
  567. } catch (Exception $E) {
  568. }
  569. $response = $this->oauth->getLastResponse();
  570. $responseInfo = $this->oauth->getLastResponseInfo();
  571. if (!strcmp($responseInfo['http_code'], '200')) {
  572. $xml = simplexml_load_string($response);
  573. if ($xml)
  574. return $xml;
  575. else
  576. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  577. } else {
  578. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  579. }
  580. }
  581. /**
  582. * Get user foods for specific date
  583. *
  584. * @throws FitBitException
  585. * @param DateTime $date
  586. * @param String $dateStr
  587. * @return SimpleXMLElement
  588. */
  589. public function getFoods($date, $dateStr = null)
  590. {
  591. $headers = $this->getHeaders();
  592. if (!isset($dateStr)) {
  593. $dateStr = $date->format('Y-m-d');
  594. }
  595. try {
  596. $this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/foods/log/date/" . $dateStr . ".xml",
  597. null, OAUTH_HTTP_METHOD_GET, $headers);
  598. } catch (Exception $E) {
  599. }
  600. $response = $this->oauth->getLastResponse();
  601. $responseInfo = $this->oauth->getLastResponseInfo();
  602. if (!strcmp($responseInfo['http_code'], '200')) {
  603. $xml = simplexml_load_string($response);
  604. if ($xml)
  605. return $xml;
  606. else
  607. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  608. } else {
  609. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  610. }
  611. }
  612. /**
  613. * Get user recent foods
  614. *
  615. * @throws FitBitException
  616. * @return SimpleXMLElement
  617. */
  618. public function getRecentFoods()
  619. {
  620. $headers = $this->getHeaders();
  621. try {
  622. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/recent.xml", null,
  623. OAUTH_HTTP_METHOD_GET, $headers);
  624. } catch (Exception $E) {
  625. }
  626. $response = $this->oauth->getLastResponse();
  627. $responseInfo = $this->oauth->getLastResponseInfo();
  628. if (!strcmp($responseInfo['http_code'], '200')) {
  629. $xml = simplexml_load_string($response);
  630. if ($xml)
  631. return $xml;
  632. else
  633. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  634. } else {
  635. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  636. }
  637. }
  638. /**
  639. * Get user frequent foods
  640. *
  641. * @throws FitBitException
  642. * @return SimpleXMLElement
  643. */
  644. public function getFrequentFoods()
  645. {
  646. $headers = $this->getHeaders();
  647. try {
  648. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/frequent.xml", null,
  649. OAUTH_HTTP_METHOD_GET, $headers);
  650. } catch (Exception $E) {
  651. }
  652. $response = $this->oauth->getLastResponse();
  653. $responseInfo = $this->oauth->getLastResponseInfo();
  654. if (!strcmp($responseInfo['http_code'], '200')) {
  655. $xml = simplexml_load_string($response);
  656. if ($xml)
  657. return $xml;
  658. else
  659. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  660. } else {
  661. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  662. }
  663. }
  664. /**
  665. * Get user favorite foods
  666. *
  667. * @throws FitBitException
  668. * @return SimpleXMLElement
  669. */
  670. public function getFavoriteFoods()
  671. {
  672. $headers = $this->getHeaders();
  673. try {
  674. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/favorite.xml", null,
  675. OAUTH_HTTP_METHOD_GET, $headers);
  676. } catch (Exception $E) {
  677. }
  678. $response = $this->oauth->getLastResponse();
  679. $responseInfo = $this->oauth->getLastResponseInfo();
  680. if (!strcmp($responseInfo['http_code'], '200')) {
  681. $xml = simplexml_load_string($response);
  682. if ($xml)
  683. return $xml;
  684. else
  685. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  686. } else {
  687. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  688. }
  689. }
  690. /**
  691. * Log user food
  692. *
  693. * @throws FitBitException
  694. * @param DateTime $date Food log date
  695. * @param string $foodId Food Id from foods database (see searchFoods)
  696. * @param string $mealTypeId Meal Type Id from foods database (see searchFoods)
  697. * @param string $unitId Unit Id, should be allowed for this food (see getFoodUnits and searchFoods)
  698. * @param string $amount Amount in specified units
  699. * @return SimpleXMLElement
  700. */
  701. public function logFood($date, $foodId, $mealTypeId, $unitId, $amount, $foodName = null, $calories = null, $brandName = null, $nutrition = null)
  702. {
  703. $headers = $this->getHeaders();
  704. $parameters = array();
  705. $parameters['date'] = $date->format('Y-m-d');
  706. if (isset($foodName)) {
  707. $parameters['foodName'] = $foodName;
  708. $parameters['calories'] = $calories;
  709. if (isset($brandName))
  710. $parameters['brandName'] = $brandName;
  711. if (isset($nutrition)) {
  712. foreach ($nutrition as $i => $value) {
  713. $parameters[$i] = $nutrition[$i];
  714. }
  715. }
  716. } else {
  717. $parameters['foodId'] = $foodId;
  718. }
  719. $parameters['mealTypeId'] = $mealTypeId;
  720. $parameters['unitId'] = $unitId;
  721. $parameters['amount'] = $amount;
  722. try {
  723. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log.xml", $parameters,
  724. OAUTH_HTTP_METHOD_POST, $headers);
  725. } catch (Exception $E) {
  726. }
  727. $response = $this->oauth->getLastResponse();
  728. $responseInfo = $this->oauth->getLastResponseInfo();
  729. if (!strcmp($responseInfo['http_code'], '201')) {
  730. $xml = simplexml_load_string($response);
  731. if ($xml)
  732. return $xml;
  733. else
  734. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  735. } else {
  736. $xml = simplexml_load_string($response);
  737. if (!$xml)
  738. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  739. else
  740. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  741. }
  742. }
  743. /**
  744. * Delete user food
  745. *
  746. * @throws FitBitException
  747. * @param string $id Food log id
  748. * @return bool
  749. */
  750. public function deleteFood($id)
  751. {
  752. $headers = $this->getHeaders();
  753. try {
  754. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/" . $id . ".xml", null,
  755. OAUTH_HTTP_METHOD_DELETE, $headers);
  756. } catch (Exception $E) {
  757. }
  758. $responseInfo = $this->oauth->getLastResponseInfo();
  759. if (!strcmp($responseInfo['http_code'], '204')) {
  760. return true;
  761. } else {
  762. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  763. }
  764. }
  765. /**
  766. * Add user favorite food
  767. *
  768. * @throws FitBitException
  769. * @param string $id Food log id
  770. * @return bool
  771. */
  772. public function addFavoriteFood($id)
  773. {
  774. $headers = $this->getHeaders();
  775. try {
  776. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/favorite/" . $id . ".xml", null,
  777. OAUTH_HTTP_METHOD_POST, $headers);
  778. } catch (Exception $E) {
  779. }
  780. $responseInfo = $this->oauth->getLastResponseInfo();
  781. if (!strcmp($responseInfo['http_code'], '201')) {
  782. return true;
  783. } else {
  784. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  785. }
  786. }
  787. /**
  788. * Delete user favorite food
  789. *
  790. * @throws FitBitException
  791. * @param string $id Food log id
  792. * @return bool
  793. */
  794. public function deleteFavoriteFood($id)
  795. {
  796. $headers = $this->getHeaders();
  797. try {
  798. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/favorite/" . $id . ".xml",
  799. null, OAUTH_HTTP_METHOD_DELETE, $headers);
  800. } catch (Exception $E) {
  801. }
  802. $responseInfo = $this->oauth->getLastResponseInfo();
  803. if (!strcmp($responseInfo['http_code'], '204')) {
  804. return true;
  805. } else {
  806. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  807. }
  808. }
  809. /**
  810. * Get user meal sets
  811. *
  812. * @throws FitBitException
  813. * @return SimpleXMLElement
  814. */
  815. public function getMeals()
  816. {
  817. $headers = $this->getHeaders();
  818. try {
  819. $this->oauth->fetch($this->baseApiUrl . "user/-/meals.xml",
  820. null, OAUTH_HTTP_METHOD_GET, $headers);
  821. } catch (Exception $E) {
  822. }
  823. $response = $this->oauth->getLastResponse();
  824. $responseInfo = $this->oauth->getLastResponseInfo();
  825. if (!strcmp($responseInfo['http_code'], '200')) {
  826. $xml = simplexml_load_string($response);
  827. if ($xml)
  828. return $xml;
  829. else
  830. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  831. } else {
  832. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  833. }
  834. }
  835. /**
  836. * Get food units library
  837. *
  838. * @throws FitBitException
  839. * @return SimpleXMLElement
  840. */
  841. public function getFoodUnits()
  842. {
  843. $headers = $this->getHeaders();
  844. try {
  845. $this->oauth->fetch($this->baseApiUrl . "foods/units.xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  846. } catch (Exception $E) {
  847. }
  848. $response = $this->oauth->getLastResponse();
  849. $responseInfo = $this->oauth->getLastResponseInfo();
  850. if (!strcmp($responseInfo['http_code'], '200')) {
  851. $xml = simplexml_load_string($response);
  852. if ($xml)
  853. return $xml;
  854. else
  855. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  856. } else {
  857. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  858. }
  859. }
  860. /**
  861. * Search for foods in foods database
  862. *
  863. * @throws FitBitException
  864. * @param string $query Search query
  865. * @return SimpleXMLElement
  866. */
  867. public function searchFoods($query)
  868. {
  869. $headers = $this->getHeaders();
  870. try {
  871. $this->oauth->fetch($this->baseApiUrl . "foods/search.xml?query=" . rawurlencode($query), null, OAUTH_HTTP_METHOD_GET, $headers);
  872. } catch (Exception $E) {
  873. }
  874. $response = $this->oauth->getLastResponse();
  875. $responseInfo = $this->oauth->getLastResponseInfo();
  876. if (!strcmp($responseInfo['http_code'], '200')) {
  877. $xml = simplexml_load_string($response);
  878. if ($xml)
  879. return $xml;
  880. else
  881. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  882. } else {
  883. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  884. }
  885. }
  886. /**
  887. * Get description of specific food from food db (or private for the user)
  888. *
  889. * @throws FitBitException
  890. * @param string $id Food Id
  891. * @return SimpleXMLElement
  892. */
  893. public function getFood($id)
  894. {
  895. $headers = $this->getHeaders();
  896. try {
  897. $this->oauth->fetch($this->baseApiUrl . "foods/" . $id . ".xml", null,
  898. OAUTH_HTTP_METHOD_GET, $headers);
  899. } catch (Exception $E) {
  900. }
  901. $response = $this->oauth->getLastResponse();
  902. $responseInfo = $this->oauth->getLastResponseInfo();
  903. if (!strcmp($responseInfo['http_code'], '200')) {
  904. $xml = simplexml_load_string($response);
  905. if ($xml)
  906. return $xml;
  907. else
  908. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  909. } else {
  910. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  911. }
  912. }
  913. /**
  914. * Create private foods for a user
  915. *
  916. * @throws FitBitException
  917. * @param string $name Food name
  918. * @param string $defaultFoodMeasurementUnitId Unit id of the default measurement unit
  919. * @param string $defaultServingSize Default serving size in measurement units
  920. * @param string $calories Calories in default serving
  921. * @param string $description
  922. * @param string $formType ("LIQUID" or "DRY)
  923. * @param string $nutrition Array of nutritional values, see http://wiki.fitbit.com/display/API/API-Create-Food
  924. * @return SimpleXMLElement
  925. */
  926. public function createFood($name, $defaultFoodMeasurementUnitId, $defaultServingSize, $calories, $description = null, $formType = null, $nutrition = null)
  927. {
  928. $headers = $this->getHeaders();
  929. $parameters = array();
  930. $parameters['name'] = $name;
  931. $parameters['defaultFoodMeasurementUnitId'] = $defaultFoodMeasurementUnitId;
  932. $parameters['defaultServingSize'] = $defaultServingSize;
  933. $parameters['calories'] = $calories;
  934. if (isset($description))
  935. $parameters['description'] = $description;
  936. if (isset($formType))
  937. $parameters['formType'] = $formType;
  938. if (isset($nutrition)) {
  939. foreach ($nutrition as $i => $value) {
  940. $parameters[$i] = $nutrition[$i];
  941. }
  942. }
  943. try {
  944. $this->oauth->fetch($this->baseApiUrl . "foods.xml", $parameters, OAUTH_HTTP_METHOD_POST, $headers);
  945. } catch (Exception $E) {
  946. }
  947. $response = $this->oauth->getLastResponse();
  948. $responseInfo = $this->oauth->getLastResponseInfo();
  949. if (!strcmp($responseInfo['http_code'], '201')) {
  950. $xml = simplexml_load_string($response);
  951. if ($xml)
  952. return $xml;
  953. else
  954. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  955. } else {
  956. $xml = simplexml_load_string($response);
  957. if (!$xml)
  958. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  959. else
  960. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  961. }
  962. }
  963. /**
  964. * Get user water log entries for specific date
  965. *
  966. * @throws FitBitException
  967. * @param DateTime $date
  968. * @param String $dateStr
  969. * @return SimpleXMLElement
  970. */
  971. public function getWater($date, $dateStr)
  972. {
  973. $headers = $this->getHeaders();
  974. if (!isset($dateStr)) {
  975. $dateStr = $date->format('Y-m-d');
  976. }
  977. try {
  978. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/water/date/" . $dateStr . ".xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  979. } catch (Exception $E) {
  980. }
  981. $response = $this->oauth->getLastResponse();
  982. $responseInfo = $this->oauth->getLastResponseInfo();
  983. if (!strcmp($responseInfo['http_code'], '200')) {
  984. $xml = simplexml_load_string($response);
  985. if ($xml)
  986. return $xml;
  987. else
  988. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  989. } else {
  990. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  991. }
  992. }
  993. /**
  994. * Log user water
  995. *
  996. * @throws FitBitException
  997. * @param DateTime $date Log entry date (set proper timezone, which could be fetched via getProfile)
  998. * @param string $amount Amount in ml/fl oz (as set with setMetric) or waterUnit
  999. * @param string $waterUnit Water Unit ("ml", "fl oz" or "cup")
  1000. * @return SimpleXMLElement
  1001. */
  1002. public function logWater($date, $amount, $waterUnit = null)
  1003. {
  1004. $waterUnits = array('ml', 'fl oz', 'cup');
  1005. $headers = $this->getHeaders();
  1006. $parameters = array();
  1007. $parameters['date'] = $date->format('Y-m-d');
  1008. $parameters['amount'] = $amount;
  1009. if (isset($waterUnit) && in_array($waterUnit, $waterUnits))
  1010. $parameters['unit'] = $waterUnit;
  1011. try {
  1012. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/water.xml", $parameters,
  1013. OAUTH_HTTP_METHOD_POST, $headers);
  1014. } catch (Exception $E) {
  1015. }
  1016. $response = $this->oauth->getLastResponse();
  1017. $responseInfo = $this->oauth->getLastResponseInfo();
  1018. if (!strcmp($responseInfo['http_code'], '201')) {
  1019. $xml = simplexml_load_string($response);
  1020. if ($xml)
  1021. return $xml;
  1022. else
  1023. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1024. } else {
  1025. $xml = simplexml_load_string($response);
  1026. if (!$xml)
  1027. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1028. else
  1029. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1030. }
  1031. }
  1032. /**
  1033. * Delete user water record
  1034. *
  1035. * @throws FitBitException
  1036. * @param string $id Water log id
  1037. * @return bool
  1038. */
  1039. public function deleteWater($id)
  1040. {
  1041. $headers = $this->getHeaders();
  1042. try {
  1043. $this->oauth->fetch($this->baseApiUrl . "user/-/foods/log/water/" . $id . ".xml", null,
  1044. OAUTH_HTTP_METHOD_DELETE, $headers);
  1045. } catch (Exception $E) {
  1046. }
  1047. $responseInfo = $this->oauth->getLastResponseInfo();
  1048. if (!strcmp($responseInfo['http_code'], '204')) {
  1049. return true;
  1050. } else {
  1051. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1052. }
  1053. }
  1054. /**
  1055. * Get user sleep log entries for specific date
  1056. *
  1057. * @throws FitBitException
  1058. * @param DateTime $date
  1059. * @param String $dateStr
  1060. * @return SimpleXMLElement
  1061. */
  1062. public function getSleep($date, $dateStr = null)
  1063. {
  1064. $headers = $this->getHeaders();
  1065. if (!isset($dateStr)) {
  1066. $dateStr = $date->format('Y-m-d');
  1067. }
  1068. try {
  1069. $this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/sleep/date/" . $dateStr . ".xml",
  1070. null, OAUTH_HTTP_METHOD_GET, $headers);
  1071. } catch (Exception $E) {
  1072. }
  1073. $response = $this->oauth->getLastResponse();
  1074. $responseInfo = $this->oauth->getLastResponseInfo();
  1075. if (!strcmp($responseInfo['http_code'], '200')) {
  1076. $xml = simplexml_load_string($response);
  1077. if ($xml)
  1078. return $xml;
  1079. else
  1080. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1081. } else {
  1082. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1083. }
  1084. }
  1085. /**
  1086. * Log user sleep
  1087. *
  1088. * @throws FitBitException
  1089. * @param DateTime $date Sleep date and time (set proper timezone, which could be fetched via getProfile)
  1090. * @param string $duration Duration millis
  1091. * @return SimpleXMLElement
  1092. */
  1093. public function logSleep($date, $duration)
  1094. {
  1095. $headers = $this->getHeaders();
  1096. $parameters = array();
  1097. $parameters['date'] = $date->format('Y-m-d');
  1098. $parameters['startTime'] = $date->format('H:i');
  1099. $parameters['duration'] = $duration;
  1100. try {
  1101. $this->oauth->fetch($this->baseApiUrl . "user/-/sleep.xml", $parameters,
  1102. OAUTH_HTTP_METHOD_POST, $headers);
  1103. } catch (Exception $E) {
  1104. }
  1105. $response = $this->oauth->getLastResponse();
  1106. $responseInfo = $this->oauth->getLastResponseInfo();
  1107. if (!strcmp($responseInfo['http_code'], '201')) {
  1108. $xml = simplexml_load_string($response);
  1109. if ($xml)
  1110. return $xml;
  1111. else
  1112. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1113. } else {
  1114. $xml = simplexml_load_string($response);
  1115. if (!$xml)
  1116. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1117. else
  1118. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1119. }
  1120. }
  1121. /**
  1122. * Delete user sleep record
  1123. *
  1124. * @throws FitBitException
  1125. * @param string $id Activity log id
  1126. * @return bool
  1127. */
  1128. public function deleteSleep($id)
  1129. {
  1130. $headers = $this->getHeaders();
  1131. try {
  1132. $this->oauth->fetch($this->baseApiUrl . "user/-/sleep/" . $id . ".xml", null,
  1133. OAUTH_HTTP_METHOD_DELETE, $headers);
  1134. } catch (Exception $E) {
  1135. }
  1136. $responseInfo = $this->oauth->getLastResponseInfo();
  1137. if (!strcmp($responseInfo['http_code'], '204')) {
  1138. return true;
  1139. } else {
  1140. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1141. }
  1142. }
  1143. /**
  1144. * Get user body measurements
  1145. *
  1146. * @throws FitBitException
  1147. * @param DateTime $date
  1148. * @param String $dateStr
  1149. * @return SimpleXMLElement
  1150. */
  1151. public function getBody($date, $dateStr = null)
  1152. {
  1153. $headers = $this->getHeaders();
  1154. if (!isset($dateStr)) {
  1155. $dateStr = $date->format('Y-m-d');
  1156. }
  1157. try {
  1158. $this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/body/date/" . $dateStr . ".xml",
  1159. null, OAUTH_HTTP_METHOD_GET, $headers);
  1160. } catch (Exception $E) {
  1161. }
  1162. $response = $this->oauth->getLastResponse();
  1163. $responseInfo = $this->oauth->getLastResponseInfo();
  1164. if (!strcmp($responseInfo['http_code'], '200')) {
  1165. $xml = simplexml_load_string($response);
  1166. if ($xml)
  1167. return $xml;
  1168. else
  1169. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1170. } else {
  1171. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1172. }
  1173. }
  1174. /**
  1175. * Log user body measurements
  1176. *
  1177. * @throws FitBitException
  1178. * @param string $weight Float number. For en_GB units, provide floating number of stones (i.e. 11 st. 4 lbs = 11.2857143)
  1179. * @param string $fat Float number
  1180. * @param string $bicep Float number
  1181. * @param string $calf Float number
  1182. * @param string $chest Float number
  1183. * @param string $forearm Float number
  1184. * @param string $hips Float number
  1185. * @param string $neck Float number
  1186. * @param string $thigh Float number
  1187. * @param string $waist Float number
  1188. * @param DateTime $date Date Log entry date (set proper timezone, which could be fetched via getProfile)
  1189. * @return SimpleXMLElement
  1190. */
  1191. public function logBody($date, $weight = null, $fat = null, $bicep = null, $calf = null, $chest = null, $forearm = null, $hips = null, $neck = null, $thigh = null, $waist = null)
  1192. {
  1193. $headers = $this->getHeaders();
  1194. $parameters = array();
  1195. $parameters['date'] = $date->format('Y-m-d');
  1196. if (isset($weight))
  1197. $parameters['weight'] = $weight;
  1198. if (isset($fat))
  1199. $parameters['fat'] = $fat;
  1200. if (isset($bicep))
  1201. $parameters['bicep'] = $bicep;
  1202. if (isset($calf))
  1203. $parameters['calf'] = $calf;
  1204. if (isset($chest))
  1205. $parameters['chest'] = $chest;
  1206. if (isset($forearm))
  1207. $parameters['forearm'] = $forearm;
  1208. if (isset($hips))
  1209. $parameters['hips'] = $hips;
  1210. if (isset($neck))
  1211. $parameters['neck'] = $neck;
  1212. if (isset($thigh))
  1213. $parameters['thigh'] = $thigh;
  1214. if (isset($waist))
  1215. $parameters['waist'] = $waist;
  1216. try {
  1217. $this->oauth->fetch($this->baseApiUrl . "user/-/body.xml",
  1218. $parameters, OAUTH_HTTP_METHOD_POST, $headers);
  1219. } catch (Exception $E) {
  1220. }
  1221. $response = $this->oauth->getLastResponse();
  1222. $responseInfo = $this->oauth->getLastResponseInfo();
  1223. if (!strcmp($responseInfo['http_code'], '201')) {
  1224. $xml = simplexml_load_string($response);
  1225. if ($xml)
  1226. return $xml;
  1227. else
  1228. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1229. } else {
  1230. $xml = simplexml_load_string($response);
  1231. if (!$xml)
  1232. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1233. else
  1234. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1235. }
  1236. }
  1237. /**
  1238. * Log user weight
  1239. *
  1240. * @throws FitBitException
  1241. * @param string $weight Float number. For en_GB units, provide floating number of stones (i.e. 11 st. 4 lbs = 11.2857143)
  1242. * @param DateTime $date If present, log entry date, now by default (set proper timezone, which could be fetched via getProfile)
  1243. * @return bool
  1244. */
  1245. public function logWeight($weight, $date = null)
  1246. {
  1247. $headers = $this->getHeaders();
  1248. $parameters = array();
  1249. $parameters['weight'] = $weight;
  1250. if (isset($date))
  1251. $parameters['date'] = $date->format('Y-m-d');
  1252. try {
  1253. $this->oauth->fetch($this->baseApiUrl . "user/-/body/weight.xml",
  1254. $parameters, OAUTH_HTTP_METHOD_POST, $headers);
  1255. } catch (Exception $E) {
  1256. }
  1257. $response = $this->oauth->getLastResponse();
  1258. $responseInfo = $this->oauth->getLastResponseInfo();
  1259. if (!strcmp($responseInfo['http_code'], '201')) {
  1260. return true;
  1261. } else {
  1262. $xml = simplexml_load_string($response);
  1263. if (!$xml)
  1264. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1265. else
  1266. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1267. }
  1268. }
  1269. /**
  1270. * Get user blood pressure log entries for specific date
  1271. *
  1272. * @throws FitBitException
  1273. * @param DateTime $date
  1274. * @param String $dateStr
  1275. * @return SimpleXMLElement
  1276. */
  1277. public function getBloodPressure($date, $dateStr)
  1278. {
  1279. $headers = $this->getHeaders();
  1280. if (!isset($dateStr)) {
  1281. $dateStr = $date->format('Y-m-d');
  1282. }
  1283. try {
  1284. $this->oauth->fetch($this->baseApiUrl . "user/-/bp/date/" . $dateStr . ".xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1285. } catch (Exception $E) {
  1286. }
  1287. $response = $this->oauth->getLastResponse();
  1288. $responseInfo = $this->oauth->getLastResponseInfo();
  1289. if (!strcmp($responseInfo['http_code'], '200')) {
  1290. $xml = simplexml_load_string($response);
  1291. if ($xml)
  1292. return $xml;
  1293. else
  1294. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1295. } else {
  1296. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1297. }
  1298. }
  1299. /**
  1300. * Log user blood pressure
  1301. *
  1302. * @throws FitBitException
  1303. * @param DateTime $date Log entry date (set proper timezone, which could be fetched via getProfile)
  1304. * @param string $systolic Systolic measurement
  1305. * @param string $diastolic Diastolic measurement
  1306. * @param DateTime $time Time of the measurement (set proper timezone, which could be fetched via getProfile)
  1307. * @return SimpleXMLElement
  1308. */
  1309. public function logBloodPressure($date, $systolic, $diastolic, $time = null)
  1310. {
  1311. $headers = $this->getHeaders();
  1312. $parameters = array();
  1313. $parameters['date'] = $date->format('Y-m-d');
  1314. $parameters['systolic'] = $systolic;
  1315. $parameters['diastolic'] = $diastolic;
  1316. if (isset($time))
  1317. $parameters['time'] = $time->format('H:i');
  1318. try {
  1319. $this->oauth->fetch($this->baseApiUrl . "user/-/bp.xml", $parameters,
  1320. OAUTH_HTTP_METHOD_POST, $headers);
  1321. } catch (Exception $E) {
  1322. }
  1323. $response = $this->oauth->getLastResponse();
  1324. $responseInfo = $this->oauth->getLastResponseInfo();
  1325. if (!strcmp($responseInfo['http_code'], '201')) {
  1326. $xml = simplexml_load_string($response);
  1327. if ($xml)
  1328. return $xml;
  1329. else
  1330. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1331. } else {
  1332. $xml = simplexml_load_string($response);
  1333. if (!$xml)
  1334. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1335. else
  1336. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1337. }
  1338. }
  1339. /**
  1340. * Delete user blood pressure record
  1341. *
  1342. * @throws FitBitException
  1343. * @param string $id Blood pressure log id
  1344. * @return bool
  1345. */
  1346. public function deleteBloodPressure($id)
  1347. {
  1348. $headers = $this->getHeaders();
  1349. try {
  1350. $this->oauth->fetch($this->baseApiUrl . "user/-/bp/" . $id . ".xml", null,
  1351. OAUTH_HTTP_METHOD_DELETE, $headers);
  1352. } catch (Exception $E) {
  1353. }
  1354. $responseInfo = $this->oauth->getLastResponseInfo();
  1355. if (!strcmp($responseInfo['http_code'], '204')) {
  1356. return true;
  1357. } else {
  1358. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1359. }
  1360. }
  1361. /**
  1362. * Get user glucose log entries for specific date
  1363. *
  1364. * @throws FitBitException
  1365. * @param DateTime $date
  1366. * @param String $dateStr
  1367. * @return SimpleXMLElement
  1368. */
  1369. public function getGlucose($date, $dateStr)
  1370. {
  1371. $headers = $this->getHeaders();
  1372. if (!isset($dateStr)) {
  1373. $dateStr = $date->format('Y-m-d');
  1374. }
  1375. try {
  1376. $this->oauth->fetch($this->baseApiUrl . "user/-/glucose/date/" . $dateStr . ".xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1377. } catch (Exception $E) {
  1378. }
  1379. $response = $this->oauth->getLastResponse();
  1380. $responseInfo = $this->oauth->getLastResponseInfo();
  1381. if (!strcmp($responseInfo['http_code'], '200')) {
  1382. $xml = simplexml_load_string($response);
  1383. if ($xml)
  1384. return $xml;
  1385. else
  1386. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1387. } else {
  1388. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1389. }
  1390. }
  1391. /**
  1392. * Log user glucose and HbA1c
  1393. *
  1394. * @throws FitBitException
  1395. * @param DateTime $date Log entry date (set proper timezone, which could be fetched via getProfile)
  1396. * @param string $tracker Name of the glucose tracker
  1397. * @param string $glucose Glucose measurement
  1398. * @param string $hba1c Glucose measurement
  1399. * @param DateTime $time Time of the measurement (set proper timezone, which could be fetched via getProfile)
  1400. * @return SimpleXMLElement
  1401. */
  1402. public function logGlucose($date, $tracker, $glucose, $hba1c = null, $time = null)
  1403. {
  1404. $headers = $this->getHeaders();
  1405. $parameters = array();
  1406. $parameters['date'] = $date->format('Y-m-d');
  1407. $parameters['tracker'] = $tracker;
  1408. $parameters['glucose'] = $glucose;
  1409. if (isset($hba1c))
  1410. $parameters['hba1c'] = $hba1c;
  1411. if (isset($time))
  1412. $parameters['time'] = $time->format('H:i');
  1413. try {
  1414. $this->oauth->fetch($this->baseApiUrl . "user/-/glucose.xml", $parameters,
  1415. OAUTH_HTTP_METHOD_POST, $headers);
  1416. } catch (Exception $E) {
  1417. }
  1418. $response = $this->oauth->getLastResponse();
  1419. $responseInfo = $this->oauth->getLastResponseInfo();
  1420. if (!strcmp($responseInfo['http_code'], '201')) {
  1421. $xml = simplexml_load_string($response);
  1422. if ($xml)
  1423. return $xml;
  1424. else
  1425. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1426. } else {
  1427. $xml = simplexml_load_string($response);
  1428. if (!$xml)
  1429. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1430. else
  1431. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1432. }
  1433. }
  1434. /**
  1435. * Get user heart rate log entries for specific date
  1436. *
  1437. * @throws FitBitException
  1438. * @param DateTime $date
  1439. * @param String $dateStr
  1440. * @return SimpleXMLElement
  1441. */
  1442. public function getHeartRate($date, $dateStr)
  1443. {
  1444. $headers = $this->getHeaders();
  1445. if (!isset($dateStr)) {
  1446. $dateStr = $date->format('Y-m-d');
  1447. }
  1448. try {
  1449. $this->oauth->fetch($this->baseApiUrl . "user/-/heart/date/" . $dateStr . ".xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1450. } catch (Exception $E) {
  1451. }
  1452. $response = $this->oauth->getLastResponse();
  1453. $responseInfo = $this->oauth->getLastResponseInfo();
  1454. if (!strcmp($responseInfo['http_code'], '200')) {
  1455. $xml = simplexml_load_string($response);
  1456. if ($xml)
  1457. return $xml;
  1458. else
  1459. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1460. } else {
  1461. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1462. }
  1463. }
  1464. /**
  1465. * Log user heart rate
  1466. *
  1467. * @throws FitBitException
  1468. * @param DateTime $date Log entry date (set proper timezone, which could be fetched via getProfile)
  1469. * @param string $tracker Name of the glucose tracker
  1470. * @param string $heartRate Heart rate measurement
  1471. * @param DateTime $time Time of the measurement (set proper timezone, which could be fetched via getProfile)
  1472. * @return SimpleXMLElement
  1473. */
  1474. public function logHeartRate($date, $tracker, $heartRate, $time = null)
  1475. {
  1476. $headers = $this->getHeaders();
  1477. $parameters = array();
  1478. $parameters['date'] = $date->format('Y-m-d');
  1479. $parameters['tracker'] = $tracker;
  1480. $parameters['heartRate'] = $heartRate;
  1481. if (isset($time))
  1482. $parameters['time'] = $time->format('H:i');
  1483. try {
  1484. $this->oauth->fetch($this->baseApiUrl . "user/-/heart.xml", $parameters,
  1485. OAUTH_HTTP_METHOD_POST, $headers);
  1486. } catch (Exception $E) {
  1487. }
  1488. $response = $this->oauth->getLastResponse();
  1489. $responseInfo = $this->oauth->getLastResponseInfo();
  1490. if (!strcmp($responseInfo['http_code'], '201')) {
  1491. $xml = simplexml_load_string($response);
  1492. if ($xml)
  1493. return $xml;
  1494. else
  1495. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1496. } else {
  1497. $xml = simplexml_load_string($response);
  1498. if (!$xml)
  1499. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1500. else
  1501. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1502. }
  1503. }
  1504. /**
  1505. * Delete user heart rate record
  1506. *
  1507. * @throws FitBitException
  1508. * @param string $id Heart rate log id
  1509. * @return bool
  1510. */
  1511. public function deleteHeartRate($id)
  1512. {
  1513. $headers = $this->getHeaders();
  1514. try {
  1515. $this->oauth->fetch($this->baseApiUrl . "user/-/heart/" . $id . ".xml", null,
  1516. OAUTH_HTTP_METHOD_DELETE, $headers);
  1517. } catch (Exception $E) {
  1518. }
  1519. $responseInfo = $this->oauth->getLastResponseInfo();
  1520. if (!strcmp($responseInfo['http_code'], '204')) {
  1521. return true;
  1522. } else {
  1523. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1524. }
  1525. }
  1526. /**
  1527. * Launch TimeSeries requests
  1528. *
  1529. * Allowed types are:
  1530. * 'caloriesIn', 'water'
  1531. *
  1532. * 'caloriesOut', 'steps', 'distance', 'floors', 'elevation'
  1533. * 'minutesSedentary', 'minutesLightlyActive', 'minutesFairlyActive', 'minutesVeryActive',
  1534. * 'activeScore', 'activityCalories',
  1535. *
  1536. * 'tracker_caloriesOut', 'tracker_steps', 'tracker_distance', 'tracker_floors', 'tracker_elevation'
  1537. * 'tracker_activeScore'
  1538. *
  1539. * 'startTime', 'timeInBed', 'minutesAsleep', 'minutesAwake', 'awakeningsCount',
  1540. * 'minutesToFallAsleep', 'minutesAfterWakeup',
  1541. * 'efficiency'
  1542. *
  1543. * 'weight', 'bmi', 'fat'
  1544. *
  1545. * @throws FitBitException
  1546. * @param string $type
  1547. * @param $basedate DateTime or 'today', to_period
  1548. * @param $to_period DateTime or '1d, 7d, 30d, 1w, 1m, 3m, 6m, 1y, max'
  1549. * @return array
  1550. */
  1551. public function getTimeSeries($type, $basedate, $to_period)
  1552. {
  1553. switch ($type) {
  1554. case 'caloriesIn':
  1555. $path = '/foods/log/caloriesIn';
  1556. break;
  1557. case 'water':
  1558. $path = '/foods/log/water';
  1559. break;
  1560. case 'caloriesOut':
  1561. $path = '/activities/log/calories';
  1562. break;
  1563. case 'steps':
  1564. $path = '/activities/log/steps';
  1565. break;
  1566. case 'distance':
  1567. $path = '/activities/log/distance';
  1568. break;
  1569. case 'floors':
  1570. $path = '/activities/log/floors';
  1571. break;
  1572. case 'elevation':
  1573. $path = '/activities/log/elevation';
  1574. break;
  1575. case 'minutesSedentary':
  1576. $path = '/activities/log/minutesSedentary';
  1577. break;
  1578. case 'minutesLightlyActive':
  1579. $path = '/activities/log/minutesLightlyActive';
  1580. break;
  1581. case 'minutesFairlyActive':
  1582. $path = '/activities/log/minutesFairlyActive';
  1583. break;
  1584. case 'minutesVeryActive':
  1585. $path = '/activities/log/minutesVeryActive';
  1586. break;
  1587. case 'activeScore':
  1588. $path = '/activities/log/activeScore';
  1589. break;
  1590. case 'activityCalories':
  1591. $path = '/activities/log/activityCalories';
  1592. break;
  1593. case 'tracker_caloriesOut':
  1594. $path = '/activities/log/tracker/calories';
  1595. break;
  1596. case 'tracker_steps':
  1597. $path = '/activities/log/tracker/steps';
  1598. break;
  1599. case 'tracker_distance':
  1600. $path = '/activities/log/tracker/distance';
  1601. break;
  1602. case 'tracker_floors':
  1603. $path = '/activities/log/tracker/floors';
  1604. break;
  1605. case 'tracker_elevation':
  1606. $path = '/activities/log/tracker/elevation';
  1607. break;
  1608. case 'tracker_activeScore':
  1609. $path = '/activities/log/tracker/activeScore';
  1610. break;
  1611. case 'startTime':
  1612. $path = '/sleep/startTime';
  1613. break;
  1614. case 'timeInBed':
  1615. $path = '/sleep/timeInBed';
  1616. break;
  1617. case 'minutesAsleep':
  1618. $path = '/sleep/minutesAsleep';
  1619. break;
  1620. case 'awakeningsCount':
  1621. $path = '/sleep/awakeningsCount';
  1622. break;
  1623. case 'minutesAwake':
  1624. $path = '/sleep/minutesAwake';
  1625. break;
  1626. case 'minutesToFallAsleep':
  1627. $path = '/sleep/minutesToFallAsleep';
  1628. break;
  1629. case 'minutesAfterWakeup':
  1630. $path = '/sleep/minutesAfterWakeup';
  1631. break;
  1632. case 'efficiency':
  1633. $path = '/sleep/efficiency';
  1634. break;
  1635. case 'weight':
  1636. $path = '/body/weight';
  1637. break;
  1638. case 'bmi':
  1639. $path = '/body/bmi';
  1640. break;
  1641. case 'fat':
  1642. $path = '/body/fat';
  1643. break;
  1644. default:
  1645. return false;
  1646. }
  1647. $headers = $this->getHeaders();
  1648. try {
  1649. $this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . $path . '/date/' . (is_string($basedate) ? $basedate : $basedate->format('Y-m-d')) . "/" . (is_string($to_period) ? $to_period : $to_period->format('Y-m-d')) . ".json", null, OAUTH_HTTP_METHOD_GET, $headers);
  1650. } catch (Exception $E) {
  1651. }
  1652. $response = $this->oauth->getLastResponse();
  1653. $responseInfo = $this->oauth->getLastResponseInfo();
  1654. if (!strcmp($responseInfo['http_code'], '200')) {
  1655. $json = json_decode($response);
  1656. $path = str_replace('/', '-', substr($path, 1));
  1657. return $json->$path;
  1658. } else {
  1659. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1660. }
  1661. }
  1662. /**
  1663. * Get user's activity statistics (lifetime statistics from the tracker device and total numbers including the manual activity log entries)
  1664. *
  1665. * @throws FitBitException
  1666. * @return SimpleXMLElement
  1667. */
  1668. public function getActivityStats()
  1669. {
  1670. $headers = $this->getHeaders();
  1671. try {
  1672. $this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/activities.xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1673. } catch (Exception $E) {
  1674. }
  1675. $response = $this->oauth->getLastResponse();
  1676. $responseInfo = $this->oauth->getLastResponseInfo();
  1677. if (!strcmp($responseInfo['http_code'], '200')) {
  1678. $xml = simplexml_load_string($response);
  1679. if ($xml)
  1680. return $xml;
  1681. else
  1682. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1683. } else {
  1684. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1685. }
  1686. }
  1687. /**
  1688. * Get list of devices and their properties
  1689. *
  1690. * @throws FitBitException
  1691. * @return SimpleXMLElement
  1692. */
  1693. public function getDevices()
  1694. {
  1695. $headers = $this->getHeaders();
  1696. try {
  1697. $this->oauth->fetch($this->baseApiUrl . "user/-/devices.xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1698. } catch (Exception $E) {
  1699. }
  1700. $response = $this->oauth->getLastResponse();
  1701. $responseInfo = $this->oauth->getLastResponseInfo();
  1702. if (!strcmp($responseInfo['http_code'], '200')) {
  1703. $xml = simplexml_load_string($response);
  1704. if ($xml)
  1705. return $xml;
  1706. else
  1707. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1708. } else {
  1709. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1710. }
  1711. }
  1712. /**
  1713. * Get user friends
  1714. *
  1715. * @throws FitBitException
  1716. * @return SimpleXMLElement
  1717. */
  1718. public function getFriends()
  1719. {
  1720. $headers = $this->getHeaders();
  1721. try {
  1722. $this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/friends.xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1723. } catch (Exception $E) {
  1724. }
  1725. $response = $this->oauth->getLastResponse();
  1726. $responseInfo = $this->oauth->getLastResponseInfo();
  1727. if (!strcmp($responseInfo['http_code'], '200')) {
  1728. $xml = simplexml_load_string($response);
  1729. if ($xml)
  1730. return $xml;
  1731. else
  1732. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1733. } else {
  1734. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1735. }
  1736. }
  1737. /**
  1738. * Get user's friends leaderboard
  1739. *
  1740. * @throws FitBitException
  1741. * @param string $period Depth ('7d' or '30d')
  1742. * @return SimpleXMLElement
  1743. */
  1744. public function getFriendsLeaderboard($period = '7d')
  1745. {
  1746. $headers = $this->getHeaders();
  1747. try {
  1748. $this->oauth->fetch($this->baseApiUrl . "user/-/friends/leaders/" . $period . ".xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1749. } catch (Exception $E) {
  1750. }
  1751. $response = $this->oauth->getLastResponse();
  1752. $responseInfo = $this->oauth->getLastResponseInfo();
  1753. if (!strcmp($responseInfo['http_code'], '200')) {
  1754. $xml = simplexml_load_string($response);
  1755. if ($xml)
  1756. return $xml;
  1757. else
  1758. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1759. } else {
  1760. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1761. }
  1762. }
  1763. /**
  1764. * Invite user to become friends
  1765. *
  1766. * @throws FitBitException
  1767. * @param string $userId Invite user by id
  1768. * @param string $email Invite user by email address (could be already Fitbit member or not)
  1769. * @return bool
  1770. */
  1771. public function inviteFriend($userId = null, $email = null)
  1772. {
  1773. $headers = $this->getHeaders();
  1774. $parameters = array();
  1775. if (isset($userId))
  1776. $parameters['invitedUserId'] = $userId;
  1777. if (isset($email))
  1778. $parameters['invitedUserEmail'] = $email;
  1779. try {
  1780. $this->oauth->fetch($this->baseApiUrl . "user/-/friends/invitations.xml", $parameters, OAUTH_HTTP_METHOD_POST, $headers);
  1781. } catch (Exception $E) {
  1782. }
  1783. $response = $this->oauth->getLastResponse();
  1784. $responseInfo = $this->oauth->getLastResponseInfo();
  1785. if (!strcmp($responseInfo['http_code'], '201')) {
  1786. return true;
  1787. } else {
  1788. $xml = simplexml_load_string($response);
  1789. if (!$xml)
  1790. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1791. else
  1792. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1793. }
  1794. }
  1795. /**
  1796. * Accept invite to become friends from user
  1797. *
  1798. * @throws FitBitException
  1799. * @param string $userId Id of the inviting user
  1800. * @return bool
  1801. */
  1802. public function acceptFriend($userId)
  1803. {
  1804. $headers = $this->getHeaders();
  1805. $parameters = array();
  1806. $parameters['accept'] = 'true';
  1807. try {
  1808. $this->oauth->fetch($this->baseApiUrl . "user/-/friends/invitations/" . $userId . ".xml", $parameters, OAUTH_HTTP_METHOD_POST, $headers);
  1809. } catch (Exception $E) {
  1810. }
  1811. $response = $this->oauth->getLastResponse();
  1812. $responseInfo = $this->oauth->getLastResponseInfo();
  1813. if (!strcmp($responseInfo['http_code'], '204')) {
  1814. return true;
  1815. } else {
  1816. $xml = simplexml_load_string($response);
  1817. if (!$xml)
  1818. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1819. else
  1820. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1821. }
  1822. }
  1823. /**
  1824. * Accept invite to become friends from user
  1825. *
  1826. * @throws FitBitException
  1827. * @param string $userId Id of the inviting user
  1828. * @return bool
  1829. */
  1830. public function rejectFriend($userId)
  1831. {
  1832. $headers = $this->getHeaders();
  1833. $parameters = array();
  1834. $parameters['accept'] = 'true';
  1835. try {
  1836. $this->oauth->fetch($this->baseApiUrl . "user/-/friends/invitations/" . $userId . ".xml", $parameters, OAUTH_HTTP_METHOD_POST, $headers);
  1837. } catch (Exception $E) {
  1838. }
  1839. $response = $this->oauth->getLastResponse();
  1840. $responseInfo = $this->oauth->getLastResponseInfo();
  1841. if (!strcmp($responseInfo['http_code'], '204')) {
  1842. return true;
  1843. } else {
  1844. $xml = simplexml_load_string($response);
  1845. if (!$xml)
  1846. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1847. else
  1848. throw new FitBitException($responseInfo['http_code'], $xml->errors->apiError->message, 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1849. }
  1850. }
  1851. /**
  1852. * Add subscription
  1853. *
  1854. * @throws FitBitException
  1855. * @param string $id Subscription Id
  1856. * @param string $path Subscription resource path (beginning with slash). Omit to subscribe to all user updates.
  1857. * @return
  1858. */
  1859. public function addSubscription($id, $path = null, $subscriberId = null)
  1860. {
  1861. $headers = $this->getHeaders();
  1862. $userHeaders = array();
  1863. if ($subscriberId)
  1864. $userHeaders['X-Fitbit-Subscriber-Id'] = $subscriberId;
  1865. $headers = array_merge($headers, $userHeaders);
  1866. if (isset($path))
  1867. $path = '/' . $path;
  1868. else
  1869. $path = '';
  1870. try {
  1871. $this->oauth->fetch($this->baseApiUrl . "user/-" . $path . "/apiSubscriptions/" . $id . ".xml", null, OAUTH_HTTP_METHOD_POST, $headers);
  1872. } catch (Exception $E) {
  1873. }
  1874. $response = $this->oauth->getLastResponse();
  1875. $responseInfo = $this->oauth->getLastResponseInfo();
  1876. if (!strcmp($responseInfo['http_code'], '200') || !strcmp($responseInfo['http_code'], '201')) {
  1877. $xml = simplexml_load_string($response);
  1878. if ($xml)
  1879. return $xml;
  1880. else
  1881. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1882. } else {
  1883. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1884. }
  1885. }
  1886. /**
  1887. * Delete user subscription
  1888. *
  1889. * @throws FitBitException
  1890. * @param string $id Subscription Id
  1891. * @param string $path Subscription resource path (beginning with slash)
  1892. * @return bool
  1893. */
  1894. public function deleteSubscription($id, $path = null)
  1895. {
  1896. $headers = $this->getHeaders();
  1897. if (isset($path))
  1898. $path = '/' . $path;
  1899. else
  1900. $path = '';
  1901. try {
  1902. $this->oauth->fetch($this->baseApiUrl . "user/-" . $path . "/apiSubscriptions/" . $id . ".xml", null, OAUTH_HTTP_METHOD_DELETE, $headers);
  1903. } catch (Exception $E) {
  1904. }
  1905. $responseInfo = $this->oauth->getLastResponseInfo();
  1906. if (!strcmp($responseInfo['http_code'], '204')) {
  1907. return true;
  1908. } else {
  1909. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1910. }
  1911. }
  1912. /**
  1913. * Get list of user's subscriptions for this application
  1914. *
  1915. * @throws FitBitException
  1916. * @return
  1917. */
  1918. public function getSubscriptions()
  1919. {
  1920. $headers = $this->getHeaders();
  1921. try {
  1922. $this->oauth->fetch($this->baseApiUrl . "user/-/apiSubscriptions.xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1923. } catch (Exception $E) {
  1924. }
  1925. $response = $this->oauth->getLastResponse();
  1926. $responseInfo = $this->oauth->getLastResponseInfo();
  1927. if (!strcmp($responseInfo['http_code'], '200')) {
  1928. $xml = simplexml_load_string($response);
  1929. if ($xml)
  1930. return $xml;
  1931. else
  1932. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1933. } else {
  1934. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1935. }
  1936. }
  1937. /**
  1938. * Get CLIENT+VIEWER and CLIENT rate limiting quota status
  1939. *
  1940. * @throws FitBitException
  1941. * @return FitBitRateLimiting
  1942. */
  1943. public function getRateLimit()
  1944. {
  1945. $headers = $this->getHeaders();
  1946. try {
  1947. $this->oauth->fetch($this->baseApiUrl . "account/clientAndViewerRateLimitStatus.xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1948. } catch (Exception $E) {
  1949. }
  1950. $response = $this->oauth->getLastResponse();
  1951. $responseInfo = $this->oauth->getLastResponseInfo();
  1952. if (!strcmp($responseInfo['http_code'], '200')) {
  1953. $xmlClientAndUser = simplexml_load_string($response);
  1954. } else {
  1955. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1956. }
  1957. try {
  1958. $this->oauth->fetch($this->baseApiUrl . "account/clientRateLimitStatus.xml", null, OAUTH_HTTP_METHOD_GET, $headers);
  1959. } catch (Exception $E) {
  1960. }
  1961. $response = $this->oauth->getLastResponse();
  1962. $responseInfo = $this->oauth->getLastResponseInfo();
  1963. if (!strcmp($responseInfo['http_code'], '200')) {
  1964. $xmlClient = simplexml_load_string($response);
  1965. } else {
  1966. throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
  1967. }
  1968. return new FitBitRateLimiting(
  1969. $xmlClientAndUser->rateLimitStatus->remainingHits,
  1970. $xmlClient->rateLimitStatus->remainingHits,
  1971. $xmlClientAndUser->rateLimitStatus->resetTime,
  1972. $xmlClient->rateLimitStatus->resetTime,
  1973. $xmlClientAndUser->rateLimitStatus->hourlyLimit,
  1974. $xmlClient->rateLimitStatus->hourlyLimit
  1975. );
  1976. }
  1977. /**
  1978. * Make custom call to any API endpoint
  1979. *
  1980. * @param string $url Endpoint url after '.../1/'
  1981. * @param array $parameters Request parameters
  1982. * @param string $method (OAUTH_HTTP_METHOD_GET, OAUTH_HTTP_METHOD_POST, OAUTH_HTTP_METHOD_PUT, OAUTH_HTTP_METHOD_DELETE)
  1983. * @param array $userHeaders Additional custom headers
  1984. * @return FitBitResponse
  1985. */
  1986. public function customCall($url, $parameters, $method, $userHeaders = array())
  1987. {
  1988. $headers = $this->getHeaders();
  1989. $headers = array_merge($headers, $userHeaders);
  1990. try {
  1991. $this->oauth->fetch($this->baseApiUrl . $url, $parameters, $method, $headers);
  1992. } catch (Exception $E) {
  1993. }
  1994. $response = $this->oauth->getLastResponse();
  1995. $responseInfo = $this->oauth->getLastResponseInfo();
  1996. return new FitBitResponse($response, $responseInfo['http_code']);
  1997. }
  1998. /**
  1999. * Make custom call to any API endpoint, signed with consumer_key only (on behalf of CLIENT)
  2000. *
  2001. * @param string $url Endpoint url after '.../1/'
  2002. * @param array $parameters Request parameters
  2003. * @param string $method (OAUTH_HTTP_METHOD_GET, OAUTH_HTTP_METHOD_POST, OAUTH_HTTP_METHOD_PUT, OAUTH_HTTP_METHOD_DELETE)
  2004. * @param array $userHeaders Additional custom headers
  2005. * @return FitBitResponse
  2006. */
  2007. public function client_customCall($url, $parameters, $method, $userHeaders = array())
  2008. {
  2009. $OAuthConsumer = new OAuth($this->consumer_key, $this->consumer_secret, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_AUTHORIZATION);
  2010. if ($debug)
  2011. $OAuthConsumer->enableDebug();
  2012. $headers = $this->getHeaders();
  2013. $headers = array_merge($headers, $userHeaders);
  2014. try {
  2015. $OAuthConsumer->fetch($this->baseApiUrl . $url, $parameters, $method, $headers);
  2016. } catch (Exception $E) {
  2017. }
  2018. $response = $OAuthConsumer->getLastResponse();
  2019. $responseInfo = $OAuthConsumer->getLastResponseInfo();
  2020. $this->clientDebug = print_r($OAuthConsumer->debugInfo, true);
  2021. return new FitBitResponse($response, $responseInfo['http_code']);
  2022. }
  2023. /**
  2024. * @return array
  2025. */
  2026. private function getHeaders()
  2027. {
  2028. $headers = array();
  2029. $headers['User-Agent'] = $this->userAgent;
  2030. if ($this->metric == 1) {
  2031. $headers['Accept-Language'] = 'en_US';
  2032. } else if ($this->metric == 2) {
  2033. $headers['Accept-Language'] = 'en_GB';
  2034. }
  2035. return $headers;
  2036. }
  2037. }
  2038. /**
  2039. * Fitbit API communication exception
  2040. *
  2041. */
  2042. class FitBitException extends Exception
  2043. {
  2044. public $fbMessage = '';
  2045. public $httpcode;
  2046. public function __construct($code, $fbMessage = null, $message = null)
  2047. {
  2048. $this->fbMessage = $fbMessage;
  2049. $this->httpcode = $code;
  2050. if (isset($fbMessage) && !isset($message))
  2051. $message = $fbMessage;
  2052. try {
  2053. $code = (int)$code;
  2054. } catch (Exception $E) {
  2055. $code = 0;
  2056. }
  2057. parent::__construct($message, $code);
  2058. }
  2059. }
  2060. /**
  2061. * Basic response wrapper for customCall
  2062. *
  2063. */
  2064. class FitBitResponse
  2065. {
  2066. public $response;
  2067. public $code;
  2068. /**
  2069. * @param $response string
  2070. * @param $code string
  2071. */
  2072. public function __construct($response, $code)
  2073. {
  2074. $this->response = $response;
  2075. $this->code = $code;
  2076. }
  2077. }
  2078. /**
  2079. * Wrapper for rate limiting quota
  2080. *
  2081. */
  2082. class FitBitRateLimiting
  2083. {
  2084. public $viewer;
  2085. public $viewerReset;
  2086. public $viewerQuota;
  2087. public $client;
  2088. public $clientReset;
  2089. public $clientQuota;
  2090. public function __construct($viewer, $client, $viewerReset = null, $clientReset = null, $viewerQuota = null, $clientQuota = null)
  2091. {
  2092. $this->viewer = $viewer;
  2093. $this->viewerReset = $viewerReset;
  2094. $this->viewerQuota = $viewerQuota;
  2095. $this->client = $client;
  2096. $this->clientReset = $clientReset;
  2097. $this->clientQuota = $clientQuota;
  2098. }
  2099. }