├── .gitignore
├── src
└── Paxx
│ └── Withings
│ ├── Exception
│ ├── ApiException.php
│ └── WbsException.php
│ ├── Collection
│ ├── SubscriptionCollection.php
│ ├── MeasureCollection.php
│ └── ActivityCollection.php
│ ├── Provider
│ └── Withings.php
│ ├── Entity
│ ├── Entity.php
│ ├── Subscription.php
│ ├── Activity.php
│ ├── User.php
│ └── Measure.php
│ ├── WithingsServiceProvider.php
│ ├── Server
│ └── Withings.php
│ └── Api.php
├── README.md
├── composer.json
└── examples
└── basic.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor
3 | composer.phar
4 | composer.lock
5 | .DS_Store
--------------------------------------------------------------------------------
/src/Paxx/Withings/Exception/ApiException.php:
--------------------------------------------------------------------------------
1 | updatedAt = Carbon::createFromTimestamp($params['updatetime'], $params['timezone']);
16 |
17 | foreach ($params['measuregrps'] as &$group) {
18 | $group = new Measure($group);
19 | }
20 |
21 | parent::__construct($params['measuregrps']);
22 | unset($params);
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/src/Paxx/Withings/Collection/ActivityCollection.php:
--------------------------------------------------------------------------------
1 | hasNecessaryVerifier()) {
14 | throw new \InvalidArgumentException('Invalid request. Missing OAuth verifier.');
15 | }
16 |
17 | $user = $this->server->getUserDetails($token = $this->getToken());
18 |
19 | return (new User())->setRaw((array)$user)->map([
20 | 'id' => $user->uid,
21 | 'nickname' => null,
22 | 'name' => $user->name,
23 | 'email' => null,
24 | 'avatar' => null,
25 | ])->setToken($token->getIdentifier(), $token->getSecret());
26 | }
27 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Withings Body Metric Service API
2 | ============
3 |
4 | PSR-FIG Compatible Withings Body metrics Services API written in PHP.
5 |
6 | I could not find a good API for Withings written in PHP that's up to par with their recent changes.
7 |
8 | This one should fill all your needs.
9 |
10 | Complete with oAuth1 integration. Also integrates nicely into Laravel's Socialite.
11 |
12 | Input parameters can be found here: http://www.withings.com/api (and very often phpdoc'ed)
13 |
14 | ### Todo
15 |
16 | * Implement Sleep Measures
17 | * Implement Sleep Summary
18 | * Implement Intraday Activity
19 | * Implement Workouts
20 | * Write tests
21 |
22 | ### Note
23 | I only own the Withings Body Scale, so I can't input any data for their other products. If you do, and would like to extend this library, please send a PR with a fix
24 |
25 | ### Credits
26 | [A big thanks to all the contributors](https://github.com/Zn4rK/php-withings/graphs/contributors)
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "paxx/withings",
3 | "description": "Withings API - Withings Body metrics Services API http:\/\/www.withings.com\/api",
4 | "license" : "MIT",
5 | "keywords" : ["withings", "wbs", "wbsapi", "wbsapi2", "laravel", "socialite", "socialiteprovider", "oauth1"],
6 | "authors": [
7 | {
8 | "name": "Alexander Liljengård",
9 | "email": "alexander@paxxmedia.se",
10 | "homepage": "http://paxxmedia.se"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=5.3.0",
15 | "league/oauth1-client": "~1.0",
16 | "illuminate/support": "~5.0",
17 | "guzzlehttp/guzzle": "~6.0",
18 | "guzzlehttp/oauth-subscriber": "~0.3",
19 | "laravel/socialite": "^2.0"
20 | },
21 | "autoload": {
22 | "psr-4": {
23 | "Paxx\\Withings\\": "src/Paxx/Withings"
24 | }
25 | },
26 | "minimum-stability": "dev"
27 | }
28 |
--------------------------------------------------------------------------------
/src/Paxx/Withings/Entity/Entity.php:
--------------------------------------------------------------------------------
1 | 2.20462, // lbs
16 | 'm' => 0.393701, // ft
17 | 'mmHg' => 0.0193367747 // psi
18 | );
19 |
20 | if($this->imperial === false) {
21 | return $value;
22 | }
23 |
24 | // Reset the imperial bool
25 | $this->imperial = false;
26 |
27 | // returning and rounding to two decimals, so we're consistant with the metric
28 | return round($value * $conversion[$unit], 2);
29 | }
30 |
31 | /**
32 | * @return $this
33 | */
34 | public function imperial()
35 | {
36 | $this->imperial = true;
37 | return $this;
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/src/Paxx/Withings/WithingsServiceProvider.php:
--------------------------------------------------------------------------------
1 | package('paxx/withings');
25 |
26 | $loader = AliasLoader::getInstance();
27 |
28 | $loader->alias('WithingsApi', 'Paxx\Withings\Api');
29 | $loader->alias('WithingsAuth', 'Paxx\Withings\Server\Withings');
30 | }
31 |
32 | /**
33 | * Register the service provider.
34 | *
35 | * @return void
36 | */
37 | public function register()
38 | {
39 | $this->app['withingsapi'] = $this->app->share(function ($app) {
40 | return new Api;
41 | });
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Paxx/Withings/Entity/Subscription.php:
--------------------------------------------------------------------------------
1 | expiresAt = Carbon::createFromTimestamp($params['expires']);
30 | $this->comment = $params['comment'];
31 | $this->callback = $params['callbackurl'];
32 | $this->appli = $params['appli'];
33 | }
34 |
35 | /**
36 | * Date at which the notification configuration will expire.
37 | *
38 | * @return Carbon
39 | */
40 | public function getExpiresAt()
41 | {
42 | return $this->expiresAt;
43 | }
44 |
45 | /**
46 | * Comment entered when creating the notification configuration.
47 | *
48 | * @return String
49 | */
50 | public function getComment()
51 | {
52 | return $this->comment;
53 | }
54 |
55 | /**
56 | * Callback entered when creating the notification configuration.
57 | *
58 | * @return String
59 | */
60 | public function getCallback()
61 | {
62 | return $this->callback;
63 | }
64 |
65 | /**
66 | * Appli entered when creating the notification configuration.
67 | *
68 | * @return Integer
69 | */
70 | public function getAppli()
71 | {
72 | return $this->appli;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/examples/basic.php:
--------------------------------------------------------------------------------
1 | 'your-key',
11 | 'secret' => 'your-secret',
12 | 'callback_uri' => 'http://your-callback.tld'
13 | );
14 |
15 | $server = new WithingsAuth($config);
16 |
17 | if (isset($_GET['oauth_token'])) {
18 | // Step 2
19 |
20 | // Retrieve the temporary credentials from step 2
21 | $temporaryCredentials = unserialize($_SESSION['temporary_credentials']);
22 |
23 | // Retrieve token credentials - you can save these for permanent usage
24 | $tokenCredentials = $server->getTokenCredentials($temporaryCredentials, $_GET['oauth_token'], $_GET['oauth_verifier']);
25 |
26 | // Also save the userId
27 | $userId = $_GET['userid'];
28 | } else {
29 | // Step 1
30 |
31 | // These identify you as a client to the server.
32 | $temporaryCredentials = $server->getTemporaryCredentials();
33 |
34 | // Store the credentials in the session.
35 | $_SESSION['temporary_credentials'] = serialize($temporaryCredentials);
36 |
37 | // Redirect the resource owner to the login screen on Withings.
38 | $server->authorize($temporaryCredentials);
39 | }
40 |
41 | $config = $config + array(
42 | 'access_token' => $tokenCredentials->getIdentifier(),
43 | 'token_secret' => $tokenCredentials->getSecret(),
44 | 'user_id' => $userId
45 | );
46 |
47 | $api = new WithingsApi($config);
48 | $user = $api->getUser();
49 |
50 | echo 'Hello ' . $user->getFirstName() . '!
';
51 | echo 'Here are your measures:
';
52 |
53 | /**
54 | * @var \Paxx\Withings\Entity\Measure $measure
55 | */
56 | foreach($user->getMeasures() as $measure) {
57 | echo $measure->getCreatedAt() . ':
';
58 | echo 'In metric: ' . $measure->getWeight() . ' kg
';
59 | echo 'In imperial: ' . $measure->imperial()->getWeight() . ' lbs';
60 | echo '
';
61 | }
--------------------------------------------------------------------------------
/src/Paxx/Withings/Entity/Activity.php:
--------------------------------------------------------------------------------
1 | createdAt = Carbon::createFromFormat('Y-m-d', $params['date'], $params['timezone']);
50 | $this->steps = $params['steps'];
51 | $this->distance = $params['distance'];
52 | $this->calories = $params['calories'];
53 | $this->elevation = $params['elevation'];
54 | $this->soft = $params['soft'];
55 | $this->moderate = $params['moderate'];
56 | $this->intense = $params['intense'];
57 | }
58 |
59 | /**
60 | * @return Carbon
61 | */
62 | public function getCreatedAt() {
63 | return $this->createdAt;
64 | }
65 |
66 | /**
67 | * Number of steps for the day.
68 | *
69 | * @return int
70 | */
71 | public function getSteps() {
72 | return $this->steps;
73 | }
74 |
75 | /**
76 | * Distance travelled for the day (in meters)
77 | *
78 | * Imperial conversion via $this->imperial()->getDistance()
79 | *
80 | * @return float
81 | */
82 | public function getDistance() {
83 | return $this->convert($this->distance, 'm');
84 | }
85 |
86 | /**
87 | * Active Calories burned in the day (in kcal).
88 | *
89 | * @return float
90 | */
91 | public function getCalories() {
92 | return $this->calories;
93 | }
94 |
95 | /**
96 | * Elevation climbed during the day (in meters)
97 | *
98 | * Imperial conversion via $this->imperial()->getElevation()
99 | *
100 | * @return float
101 | */
102 | public function getElevation() {
103 | return $this->convert($this->elevation, 'm');
104 | }
105 |
106 | /**
107 | * Duration of soft activities (in seconds).
108 | *
109 | * @return int
110 | */
111 | public function getSoft() {
112 | return $this->soft;
113 | }
114 |
115 | /**
116 | * Duration of moderate activities (in seconds).
117 | *
118 | * @return int
119 | */
120 | public function getModerate() {
121 | return $this->moderate;
122 | }
123 |
124 | /**
125 | * Duration of intense activities (in seconds).
126 | *
127 | * @return int
128 | */
129 | public function getIntense() {
130 | return $this->intense;
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/Paxx/Withings/Entity/User.php:
--------------------------------------------------------------------------------
1 | api = $api;
62 | $this->id = $params['id'];
63 | $this->firstName = $params['firstname'];
64 | $this->lastName = $params['lastname'];
65 | $this->shortName = $params['shortname'];
66 | $this->gender = $params['gender'];
67 | $this->fatMethod = $params['fatmethod'];
68 | $this->birthDate = Carbon::createFromTimestamp($params['birthdate']);
69 | $this->isPublic = !!$params['ispublic'];
70 |
71 | // Unset params
72 | unset($params);
73 | }
74 |
75 | /**
76 | * Get Withings User id
77 | *
78 | * @return int
79 | */
80 | public function getId()
81 | {
82 | return $this->id;
83 | }
84 |
85 | /**
86 | * Get the first name
87 | *
88 | * @return String
89 | */
90 | public function getFirstName()
91 | {
92 | return $this->firstName;
93 | }
94 |
95 | /**
96 | * Get the last name
97 | *
98 | * @return String
99 | */
100 | public function getLastName()
101 | {
102 | return $this->lastName;
103 | }
104 |
105 | /**
106 | * Get the short name
107 | *
108 | * @return String
109 | */
110 | public function getShortName()
111 | {
112 | return $this->shortName;
113 | }
114 |
115 | /**
116 | * Get the gender
117 | *
118 | * @return string
119 | */
120 | public function getGender()
121 | {
122 | return $this->gender == 0 ? 'male' : 'female';
123 | }
124 |
125 | /**
126 | * Get the raw gender (0 for male, and 1 for female)
127 | *
128 | * @return int
129 | */
130 | public function getGenderRaw()
131 | {
132 | return $this->gender;
133 | }
134 |
135 | /**
136 | * Get the fat method
137 | *
138 | * @return int
139 | */
140 | public function getFatMethod()
141 | {
142 | return $this->fatMethod;
143 | }
144 |
145 | /**
146 | * Get the birthdate
147 | *
148 | * @return Carbon
149 | */
150 | public function getBirthDate()
151 | {
152 | return $this->birthDate;
153 | }
154 |
155 | /**
156 | * Is this user public or not
157 | *
158 | * @return bool
159 | */
160 | public function isPublic()
161 | {
162 | return $this->isPublic;
163 | }
164 |
165 | /**
166 | * Shortcut to the API for measures
167 | *
168 | * @return \Paxx\Withings\Collection\MeasureCollection
169 | */
170 | public function getMeasures()
171 | {
172 | return $this->api->getMeasures();
173 | }
174 |
175 | /**
176 | * Shortcut to the API for activites
177 | *
178 | * @return Activity
179 | */
180 | public function getActivites()
181 | {
182 | return $this->api->getActivity();
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/Paxx/Withings/Server/Withings.php:
--------------------------------------------------------------------------------
1 | urlUserDetails;
44 | }
45 |
46 | /**
47 | * {@inheritDoc}
48 | */
49 | public function userDetails($data, TokenCredentials $tokenCredentials)
50 | {
51 | if(isset($data['body']['users'][0])) {
52 | $data = $data['body']['users'][0];
53 | }
54 |
55 | $user = new User();
56 |
57 | $user->uid = $data['id'];
58 | $user->nickname = null;
59 | $user->name = $data['firstname'] . ' ' . $data['lastname'];
60 | $user->firstName = $data['firstname'];
61 | $user->lastName = $data['lastname'];
62 |
63 | // Save all extra data
64 | $user->extra = array(
65 | 'gender' => $data['gender'],
66 | 'fatmethod' => $data['fatmethod'],
67 | 'birthdate' => $data['birthdate'],
68 | 'shortname' => $data['shortname'],
69 | 'ispublic' => $data['ispublic']
70 | );
71 |
72 | return $user;
73 | }
74 |
75 | /**
76 | * Take the decoded data from the user details URL and extract
77 | * the user's UID.
78 | *
79 | * @param mixed $data
80 | * @param TokenCredentials $tokenCredentials
81 | *
82 | * @return string|int
83 | */
84 | public function userUid($data, TokenCredentials $tokenCredentials)
85 | {
86 | if(isset($data['body']['users'][0])) {
87 | $data = $data['body']['users'][0];
88 | }
89 |
90 | return $data['id'];
91 | }
92 |
93 | /**
94 | * {@inheritDoc}
95 | */
96 | public function userEmail($data, TokenCredentials $tokenCredentials)
97 | {
98 | return;
99 | }
100 |
101 | /**
102 | * {@inheritDoc}
103 | */
104 | public function userScreenName($data, TokenCredentials $tokenCredentials)
105 | {
106 | return;
107 | }
108 |
109 | /**
110 | * Creates temporary credentials from the body response.
111 | *
112 | * @param string $body
113 | * @return TemporaryCredentials
114 | * @throws CredentialsException
115 | */
116 | protected function createTemporaryCredentials($body)
117 | {
118 | parse_str($body, $data);
119 |
120 | if (!$data || !is_array($data)) {
121 | throw new CredentialsException('Unable to parse temporary credentials response.');
122 | }
123 |
124 | $temporaryCredentials = new TemporaryCredentials();
125 | $temporaryCredentials->setIdentifier($data['oauth_token']);
126 | $temporaryCredentials->setSecret($data['oauth_token_secret']);
127 |
128 | return $temporaryCredentials;
129 | }
130 |
131 | /**
132 | * Since Withings has their own unique implementation of oAuth, we need to override
133 | * the fetchUserDetails-method and add the oauth headers as querystrings.
134 | *
135 | * {@inheritDoc}
136 | */
137 | protected function fetchUserDetails(TokenCredentials $tokenCredentials, $force=true) {
138 | if (!$this->cachedUserDetailsResponse || $force) {
139 |
140 | // The user-endpoint
141 | $endpoint = 'http://wbsapi.withings.net/user';
142 |
143 | // Parse the parameters
144 | $parameters = $this->getOauthParameters($endpoint, $tokenCredentials, array(
145 | 'action' => 'getbyuserid'
146 | ));
147 |
148 | // Set the urlUserDetails so the parent method can call it via $this->urlUserDetails();
149 | $this->urlUserDetails = $endpoint . '?' . http_build_query($parameters);
150 | }
151 |
152 | // Call the parent when we're done
153 | return parent::fetchUserDetails($tokenCredentials, $force);
154 | }
155 |
156 | /**
157 | * Since Withings has their own unique implementation of oAuth1 we need to extract the oAuthParameters
158 | * and append them to the endpoint as a querystring.
159 | *
160 | * This is an extraction of $this->protocolHeader()
161 | *
162 | * :(
163 | *
164 | * @param $url
165 | * @param TokenCredentials $tokenCredentials
166 | * @param array $extraParams
167 | * @return array
168 | */
169 | private function getOauthParameters($url, TokenCredentials $tokenCredentials, $extraParams = array()) {
170 | $parameters = array_merge(
171 | $this->baseProtocolParameters(),
172 | $this->additionalProtocolParameters(),
173 | $extraParams,
174 | array(
175 | 'oauth_token' => $tokenCredentials->getIdentifier(),
176 | )
177 | );
178 |
179 | $this->signature->setCredentials($tokenCredentials);
180 |
181 | $parameters['oauth_signature'] = $this->signature->sign($url, $parameters, 'GET');
182 |
183 | return $parameters;
184 | }
185 |
186 | }
--------------------------------------------------------------------------------
/src/Paxx/Withings/Entity/Measure.php:
--------------------------------------------------------------------------------
1 | 'weight',
14 | 4 => 'height',
15 | 5 => 'fatFreeMass',
16 | 6 => 'fatRatio',
17 | 8 => 'fatMassWeight',
18 | 9 => 'diastolicBloodPressure',
19 | 10 => 'systolicBloodPressure',
20 | 11 => 'heartPulse',
21 | 54 => 'sp02',
22 | 76 => 'muscleMass',
23 | 77 => 'hydration',
24 | 88 => 'boneMass'
25 | );
26 |
27 | /**
28 | * @var array
29 | */
30 | private $categories = array(
31 | 1 => 'Measure',
32 | 2 => 'Target',
33 | );
34 |
35 | /**
36 | * @var Integer
37 | */
38 | protected $attrib;
39 |
40 | /**
41 | * @var Carbon
42 | */
43 | protected $createdAt;
44 |
45 | /**
46 | * @var Integer
47 | */
48 | protected $category;
49 |
50 | /**
51 | * @var Integer
52 | */
53 | protected $groupId;
54 |
55 | /**
56 | * @var float
57 | */
58 | protected $weight;
59 |
60 | /**
61 | * @var float
62 | */
63 | protected $height;
64 |
65 | /**
66 | * @var float
67 | */
68 | protected $fatFreeMass;
69 |
70 | /**
71 | * @var float
72 | */
73 | protected $fatRatio;
74 |
75 | /**
76 | * @var float
77 | */
78 | protected $hydration;
79 |
80 | /**
81 | * @var float
82 | */
83 | protected $muscleMass;
84 |
85 | /**
86 | * @var float
87 | */
88 | protected $boneMass;
89 |
90 | /**
91 | * @var float
92 | */
93 | protected $fatMassWeight;
94 |
95 | /**
96 | * @var float
97 | */
98 | protected $diastolicBloodPressure;
99 |
100 | /**
101 | * @var float
102 | */
103 | protected $systolicBloodPressure;
104 |
105 | /**
106 | * @var float
107 | */
108 | protected $heartPulse;
109 |
110 | /**
111 | * @var float
112 | */
113 | protected $sp02;
114 |
115 | /**
116 | * @param array $params
117 | */
118 | public function __construct(array $params = array())
119 | {
120 | $this->attrib = $params['attrib'];
121 | $this->createdAt = Carbon::createFromTimestamp($params['date']);
122 | $this->groupId = $params['grpid'];
123 | $this->category = $params['category'];
124 |
125 | foreach ($params['measures'] as $measure) {
126 | if (isset($this->types[$measure['type']])) {
127 | $this->{$this->types[$measure['type']]} = ($measure['value'] * pow(10, $measure['unit']));
128 | }
129 | }
130 | }
131 |
132 | /**
133 | * Get the created at date
134 | *
135 | * @return Carbon
136 | */
137 | public function getCreatedAt() {
138 | return $this->createdAt;
139 | }
140 |
141 | /**
142 | * Weight (kg)
143 | *
144 | * Imperial conversion via $this->imperial()->getWeight()
145 | *
146 | * @return float
147 | */
148 | public function getWeight()
149 | {
150 | return $this->convert($this->weight, 'kg');
151 | }
152 |
153 | /**
154 | * Height (meter)
155 | *
156 | * Imperial conversion via $this->imperial()->getHeight()
157 | *
158 | * @return float
159 | */
160 | public function getHeight()
161 | {
162 | return $this->convert($this->height, 'm');
163 | }
164 |
165 | /**
166 | * Free Mass (kg)
167 | *
168 | * Imperial conversion via $this->imperial()->getFatFreeMass()
169 | *
170 | * @return float
171 | */
172 | public function getFatFreeMass() {
173 | return $this->convert($this->fatFreeMass, 'kg');
174 | }
175 |
176 | /**
177 | * Fat Ratio (%)
178 | *
179 | * @return float
180 | */
181 | public function getFatRatio()
182 | {
183 | return $this->fatRatio;
184 | }
185 |
186 | /**
187 | * Fat Mass Weight (kg)
188 | *
189 | * Imperial conversion via $this->imperial()->getFatMassWeight()
190 | *
191 | * @return float
192 | */
193 | public function getFatMassWeight()
194 | {
195 | return $this->convert($this->fatMassWeight, 'kg');
196 | }
197 |
198 | /**
199 | * Diastolic Blood Pressure (mmHg)
200 | *
201 | * Imperial conversion via $this->imperial()->getDiastolicBloodPressure()
202 | *
203 | * @return float
204 | */
205 | public function getDiastolicBloodPressure()
206 | {
207 | return $this->convert($this->diastolicBloodPressure, 'mmHg');
208 | }
209 |
210 | /**
211 | * Systolic Blood Pressure (mmHg)
212 | *
213 | * Imperial conversion via $this->imperial()->getSystolicBloodPressure()
214 | *
215 | * @return float
216 | */
217 | public function getSystolicBloodPressure()
218 | {
219 | return $this->convert($this->systolicBloodPressure, 'mmHg');
220 | }
221 |
222 | /**
223 | * Heart Pulse
224 | *
225 | * @return float
226 | */
227 | public function getHeartPulse()
228 | {
229 | return $this->heartPulse;
230 | }
231 |
232 | /**
233 | * Sp02
234 | *
235 | * @return float
236 | */
237 | public function getSp02()
238 | {
239 | return $this->sp02;
240 | }
241 |
242 | /**
243 | * Is ambiguous
244 | *
245 | * @return bool
246 | */
247 | public function isAmbiguous()
248 | {
249 | return ($this->attrib == 1 || $this->attrib == 4);
250 | }
251 |
252 | /**
253 | * Is it a measure
254 | *
255 | * @return bool
256 | */
257 | public function isMeasure()
258 | {
259 | return ($this->category == 1);
260 | }
261 |
262 | /**
263 | * Is it the target measure
264 | *
265 | * @return bool
266 | */
267 | public function isTarget()
268 | {
269 | return ($this->category == 2);
270 | }
271 |
272 | /**
273 | * Get the category name
274 | *
275 | * @return String
276 | */
277 | public function getCategoryName()
278 | {
279 | return $this->categories[$this->category];
280 | }
281 |
282 | /**
283 | * Get the category raw value (1 for "measure", 2 for "target")
284 | *
285 | * @return int
286 | */
287 | public function getCategory()
288 | {
289 | return $this->category;
290 | }
291 | /**
292 | * bone ratio
293 | *
294 | * @return float
295 | */
296 | public function getBoneRatio()
297 | {
298 | return $this->convert($this->boneMass, 'kg')/$this->convert($this->weight, 'kg')*100;
299 | }
300 |
301 | /**
302 | * fat free ratio
303 | *
304 | * @return float
305 | */
306 | public function getFatFreeRatio()
307 | {
308 | return $this->convert($this->fatFreeMass, 'kg')/$this->convert($this->weight, 'kg')*100;
309 | }
310 |
311 | /**
312 | * muscle ratio
313 | *
314 | * @return float
315 | */
316 | public function getMuscleRatio()
317 | {
318 | return $this->convert($this->muscleMass, 'kg')/$this->convert($this->weight, 'kg')*100;
319 | }
320 |
321 | /**
322 | * hydration ratio
323 | *
324 | * @return float
325 | */
326 | public function getHydrationRatio()
327 | {
328 | return $this->convert($this->hydration, 'kg')/$this->convert($this->weight, 'kg')*100;
329 | }
330 | }
331 |
--------------------------------------------------------------------------------
/src/Paxx/Withings/Api.php:
--------------------------------------------------------------------------------
1 | 'Operation was successful',
33 | 247 => 'The user_id provided is absent, or incorrect',
34 | 250 => 'The provided user_id and/or Oauth credentials do not match',
35 | 286 => 'No such subscription was found',
36 | 293 => 'The callback URL is either absent or incorrect (Note: Withings only sends notifications to valid post-requests)',
37 | 294 => 'No such subscription could be deleted',
38 | 304 => 'The comment is either absent or incorrect',
39 | 305 => 'Too many notifications are already set',
40 | 342 => 'The signature (using Oauth) is invalid',
41 | 343 => 'Wrong Notification Callback Url doesn\'t exist',
42 | 601 => 'Too Many Requests',
43 | 2554 => 'Unspecified unknown error occurred',
44 | 2555 => 'An unknown error occurred'
45 | );
46 |
47 | public function __construct(array $params = array())
48 | {
49 | $this->hydrateParams($params);
50 |
51 | $config = array(
52 | 'consumer_key' => $this->identifier,
53 | 'consumer_secret' => $this->secret,
54 | 'token' => $this->access_token,
55 | 'token_secret' => $this->token_secret,
56 | 'request_method' => 'query'
57 | );
58 |
59 | // Create a stack so we can add the oauth-subscriber
60 | $stack = HandlerStack::create();
61 |
62 | $stack->push(new Oauth1($config));
63 |
64 | $this->client = new Client([
65 | 'base_uri' => static::ENDPOINT,
66 | 'handler' => $stack,
67 | 'auth' => 'oauth'
68 | ]);
69 | }
70 |
71 | /**
72 | * Validate that the required parameters were passed into object constructor
73 | *
74 | * @param array $params
75 | * @throws ApiException
76 | */
77 | private function validateParams(array $params)
78 | {
79 | foreach ($this->required_params as $param) {
80 | if (! isset($params{$param})) {
81 | throw new ApiException('Missing parameters');
82 | }
83 | }
84 | }
85 |
86 | /**
87 | * Hydrate object from passed parameters
88 | *
89 | * @param array $params
90 | * @throws ApiException
91 | */
92 | private function hydrateParams(array $params)
93 | {
94 | $this->validateParams($params);
95 |
96 | foreach ($this->required_params as $param) {
97 | $this->{$param} = $params[$param];
98 | }
99 | }
100 |
101 | /**
102 | * Make a request to the API
103 | *
104 | * @param string $path Path to the service
105 | * @param string $action Action query string
106 | * @param array $params Parameters
107 | * @return bool
108 | * @throws WbsException
109 | */
110 | private function request($path = '', $action = '', $params = array())
111 | {
112 | $params['userid'] = $this->user_id;
113 |
114 | if (!empty($action)) {
115 | $params['action'] = $action;
116 | }
117 |
118 | // Build a request
119 | $request = $this->client->get($path, array('query' => $params));
120 |
121 | // Decode the response
122 | $response = json_decode($request->getBody()->getContents(), true);
123 |
124 | if ($response['status'] !== 0) {
125 | if (isset($this->errors[$response['status']])) {
126 | throw new WbsException($this->errors[$response['status']], $response['status']);
127 | } else {
128 | throw new WbsException($response['error']);
129 | }
130 | }
131 |
132 | // Check
133 | if (isset($response['body'])) {
134 | return $response['body'];
135 | }
136 |
137 | // We'll return true if nothing else has happened...
138 | return true;
139 | }
140 |
141 | /**
142 | * @return Entity\User
143 | * @throws WbsException
144 | */
145 | public function getUser()
146 | {
147 | $user = $this->request('user', 'getbyuserid');
148 |
149 | // Pluck single record
150 | $user = end($user['users']);
151 |
152 | return new Entity\User($this, $user);
153 | }
154 |
155 | /**
156 | * Get user's activity
157 | *
158 | * Omit both parameters to get all
159 | *
160 | * @param string $start
161 | * @param string $end
162 | * @return Collection\ActivityCollection
163 | * @throws WbsException
164 | */
165 | public function getActivity($start='', $end='')
166 | {
167 | $params = array();
168 |
169 | // Check if we have a single day
170 | if(!empty($start) && empty($end)) {
171 | $params['date'] = $start;
172 | // Or if we have a range
173 | } elseif(!empty($start) && !empty($end)) {
174 | $params['startdateymd'] = $start;
175 | $params['enddateymd'] = $end;
176 | }
177 |
178 | $activity = $this->request('v2/measure', 'getactivity', $params);
179 |
180 | return new Collection\ActivityCollection($activity);
181 | }
182 |
183 | /**
184 | * Get user's measurements
185 | *
186 | * @param array $params
187 | * @return Collection\MeasureCollection
188 | * @throws WbsException If an error is returned from the API
189 | */
190 | public function getMeasures(array $params = array())
191 | {
192 | $measure = $this->request('measure', 'getmeas', $params);
193 | return new Collection\MeasureCollection($measure);
194 | }
195 |
196 | /**
197 | * Note: From Withings API FAQ:
198 | * Make sure you specify the right Appli parameters when creating your subscriptions
199 | * We test your callback URL when you subscribe for notifications. It must be reachable for POST requests, otherwise the subscription will fail.
200 | *
201 | * @param string $callback
202 | * @param string $comment
203 | * @param int $appli
204 | * @return bool
205 | * @throws ApiException
206 | * @throws WbsException
207 | */
208 | public function subscribe($callback = '', $comment = '', $appli = 1)
209 | {
210 | if (empty($callback)) {
211 | throw new ApiException('First parameter "callback" can\'t be empty');
212 | }
213 |
214 | if (empty($comment)) {
215 | throw new ApiException('Second parameter "comment" can\'t be empty');
216 | }
217 |
218 | $params = array(
219 | 'callbackurl' => $callback,
220 | 'comment' => $comment,
221 | 'appli' => $appli
222 | );
223 |
224 | // Add a subscription
225 | $subscribe = $this->request('notify', 'subscribe', $params);
226 | return $subscribe;
227 | }
228 |
229 | /**
230 | * @param string $callback
231 | * @param int $appli
232 | * @return bool
233 | * @throws ApiException
234 | * @throws WbsException
235 | */
236 | public function unsubscribe($callback = '', $appli = 1)
237 | {
238 | if (empty($callback)) {
239 | throw new ApiException('First parameter "callback" can\'t be empty');
240 | }
241 |
242 | $params = array(
243 | 'callbackurl' => $callback,
244 | 'appli' => $appli
245 | );
246 |
247 | // Revoke subscription
248 | $unsubscribe = $this->request('notify', 'revoke', $params);
249 | return $unsubscribe;
250 | }
251 |
252 | /**
253 | * @param int $appli
254 | * @return Collection\SubscriptionCollection
255 | * @throws WbsException
256 | */
257 | public function listSubscriptions($appli = 1)
258 | {
259 | $list = $this->request('notify', 'list', array('appli' => $appli));
260 | return new Collection\SubscriptionCollection($list);
261 | }
262 |
263 | /**
264 | * @param string $callback
265 | * @param int $appli
266 | * @return Entity\Subscription
267 | * @throws ApiException
268 | * @throws WbsException
269 | */
270 | public function isSubscribed($callback = '', $appli = 1)
271 | {
272 | if (empty($callback)) {
273 | throw new ApiException('First parameter "callback" can\'t be empty');
274 | }
275 |
276 | $params = array(
277 | 'callbackurl' => $callback,
278 | 'appli' => $appli
279 | );
280 |
281 | $isSubscribed = $this->request('notify', 'get', $params);
282 | return new Entity\Subscription($isSubscribed);
283 | }
284 | }
285 |
--------------------------------------------------------------------------------