├── .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 | --------------------------------------------------------------------------------