├── .gitignore ├── data └── .gitignore ├── scripts └── download_multiple_days.sh ├── LICENSE.md ├── README.md ├── basisdataexport.php └── BasisExport.class.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | cookie.txt 3 | 4 | -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /scripts/download_multiple_days.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | now=`date +"%Y-%m-%d" -d $1` 4 | end=`date +"%Y-%m-%d" -d $2` 5 | 6 | while [ "$now" != "$end" ] ; 7 | do 8 | echo "Downloading: "$now 9 | php ../basisdataexport.php -u[username] -p[password] -d$now -f[format] 10 | now=`date +"%Y-%m-%d" -d "$now + 1 day"`; 11 | done 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 Bob Troia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basis Data Export 2 | 3 | Utility that exports and saves your Basis Health Tracker (B1/Peak) uploaded sensor data, as described at [https://www.quantifiedbob.com/2013/04/liberating-your-data-from-the-basis-b1-band/](https://www.quantifiedbob.com/2013/04/liberating-your-data-from-the-basis-b1-band/) 4 | You can learn more about Basis at [http://www.mybasis.com/](http://www.mybasis.com/) 5 | 6 | > UPDATE JANUARY 1, 2017 - 7 | 8 | > RIP Basis - Intel has officially shut down Basis, including access to any historical data (users had until December 31, 2016 to export data). 9 | 10 | > UPDATE AUGUST 3, 2016 - 11 | 12 | > Intel (who acquired Basis) has notified users that they will be discontinuing all Basis devices and services on December 31, 2016. Make sure to download all of your data before then! See [http://www.mybasis.com/safety/](http://www.mybasis.com/safety/) to learn more. 13 | 14 | ## Instructions 15 | 16 | In order to use this script, you must already have a Basis account (and a Basis B1 band). 17 | 18 | ### Usage: 19 | This script can be run several ways. You can (and should probably) first edit the `BASIS_USERNAME`, `BASIS_PASSWORD`, and `BASIS_EXPORT_FORMAT` values under "Settings" in the file `basisdataexport.php` so you don't have to specify those values every time the script is run. Make sure the `data/` folder is writable! 20 | 21 | ```php 22 | /////////////////////////////////////////////////////// 23 | // Settings 24 | /////////////////////////////////////////////////////// 25 | 26 | // Specify your Basis username, password, and default export format. Leaving blank 27 | // will require inputting these values manually each time the script is run. 28 | define('BASIS_USERNAME', '[YOUR BASIS USERNAME]'); 29 | define('BASIS_PASSWORD', '[YOUR BASIS PASSWORD'); 30 | define('BASIS_EXPORT_FORMAT', 'json'); 31 | 32 | // Enable/disable debug mode 33 | define('DEBUG', false); 34 | ``` 35 | 36 | ### Method 1 - Interactive Mode 37 | 38 | ``` 39 | $ php basisdataexport.php 40 | ------------------------- 41 | Basis data export script. 42 | ------------------------- 43 | Enter Basis username [me@somewhere.com]: me@somewhere.com 44 | Enter Basis password [********]: 45 | Enter data export date (YYYY-MM-DD) [2016-08-22] : 46 | Enter export format (json|csv|html) [json] : 47 | ``` 48 | 49 | 1. Open a terminal window and cd to this script's directory 50 | 2. Type `php basisdataexport.php` 51 | 3. Follow the prompts (hit ENTER to use default values) 52 | 4. Your data will be saved to `/data/basis-data-[YYYY-MM-DD].[format]` 53 | 54 | 55 | ### Method 2 - Via command-line arguments (useful for crons) 56 | 57 | ``` 58 | $ php basisdataexport.php -ume@somewhere.com -pMyBasisPassword -d2016-08-22 -fcsv 59 | ``` 60 | 61 | Usage `php basisdataexport.php -h -u[username] -p[pass] -d[YYYY-MM-DD] -f[json|csv|html]` 62 | ``` 63 | Options: 64 | -u Basis username (if not used, defaults to BASIS_USERNAME) 65 | -p Basis password (if not used, defaults to BASIS_PASSWORD) 66 | -d Data export date (YYYY-MM-DD) (if not used, defaults to current date) 67 | -f Data export format (json|csv|html) (if not used, defaults to json) 68 | -h Show this help text 69 | ``` 70 | Make sure there are no spaces between any flags and values! 71 | 72 | ### Method 3 - Via web browser 73 | This requires that the scripts are in a location that is executable via a web server. 74 | 75 | `http://localhost/basis-data-export/basisdataexport.php?u=[basis_username]&p=[basis_password]&d=[YYYY-MM-DD]&f=[format]` 76 | 77 | ## Saving Your Data 78 | If the script runs successfully, your data will be saved in the `data/` folder. Files are saved in the format `basis-data-[YYYY-MM-DD].[format]` (i.e., `basis-data-2014-04-04.json`). 79 | 80 | That's it! (for now). 81 | 82 | 83 | ## Biometric Data 84 | 85 | Basis currently returns the following biometric data points. They each represent an average (i.e., for heart rate, GSR, skin/air temperature) or sum (i.e., steps, calories) over the previous 1-minute period: 86 | 87 | - Time - time reading was taken 88 | - Heart Rate - beats per minute 89 | - Steps - number of steps taken 90 | - Calories - number of calories burned 91 | - GSR - Galvanic skin response (i.e., sweat/skin conductivity. Learn more about GSR here - [http://en.wikipedia.org/wiki/Skin_conductance](http://en.wikipedia.org/wiki/Skin_conductance)) 92 | - Skin Temperature - skin temperature (degrees F) 93 | - Air Temperature - air temperatute (degrees F) 94 | 95 | There are some other aggregate metrics included in the reponse such as min/max/average/standard deviation metrics for each set of data. 96 | 97 | ## Sleep Data 98 | 99 | Basis currently returns the following sleep data points (it's in a different format than the metrics data because it's a newer version of their API): 100 | 101 | - Start Time - local timestamp in MM/DD/YY HH:MM format 102 | - Start Time ISO - user timestamp in ISO format 103 | - Start Time Time Zone - user time zone, i.e., America/New_York 104 | - Start Time Offset - minutes offset from GMT (i.e., -240 for America/New_York) 105 | - End Time - user timestamp in MM/DD/YY HH:MM format 106 | - End Time ISO - user timestamp in ISO format 107 | - End Time Time Zone - user time zone, i.e., America/New_York 108 | - End Time Offset - minutes offset from GMT (i.e., -240 for America/New_York) 109 | - Light Minutes - number of minutes in "light sleep" stage 110 | - Deep Minutes - number of minutes in "deep sleep" stage 111 | - REM Minutes - number of minutes in "REM sleep" stage 112 | - Interruption Minutes - number of minutes interrupted (i.e., woke up, went to bathroom, etc.) 113 | - Unknown Minutes - number of minutes unknown (i.e., device wasn't able to take readings) 114 | - Tosses and Turns - number of tosses and turns that occurred 115 | - Type - always returns 'sleep' 116 | - Actual Seconds - total sleep time recorded, in seconds 117 | - Calories - number of calories burned 118 | - Average Heart Rate - beats per minute 119 | - Minimum Heart Rate - beats per minute (currently this always returns null) 120 | - Maximum Heart Rate - beats per minute (currently this always returns null) 121 | - State - returns 'complete' if event was completed at time of export 122 | - Version - API version used (i.e., 2) 123 | - ID - internal id for given sleep event 124 | 125 | Sleep data is often broken into multiple segments - Basis treats each sleep activity as a separate 'event' if there is a 15-minute gap in readings (i.e., sensors couldn't detect anything). 126 | 127 | ## Activity Data 128 | 129 | Similar to sleep data, Basis currently returns the following activity data points (walking, biking, running): 130 | 131 | - Start Time - local timestamp in MM/DD/YY HH:MM format 132 | - Start Time ISO - user timestamp in ISO format 133 | - Start Time Time Zone - user time zone, i.e., America/New_York 134 | - Start Time Offset - minutes offset from GMT (i.e., -240 for America/New_York) 135 | - End Time - user timestamp in MM/DD/YY HH:MM format 136 | - End Time ISO - user timestamp in ISO format 137 | - End Time Time Zone - user time zone, i.e., America/New_York 138 | - End Time Offset - minutes offset from GMT (i.e., -240 for America/New_York) 139 | - Type - activity type (i.e., walk, run, bike) 140 | - Actual Seconds - total activity time, in seconds 141 | - Steps - number of steps taken 142 | - Calories - number of calories burned 143 | - Minutes - actual number of minutes activity was performed (excludes time not moving?) 144 | - Average Heart Rate - beats per minute (currently this always returns null) 145 | - Minimum Heart Rate - beats per minute (currently this always returns null) 146 | - Maximum Heart Rate - beats per minute (currently this always returns null) 147 | - State - returns 'complete' if event was completed at time of export 148 | - Version - API version used (i.e., 2) 149 | - ID - internal id for given sleep event 150 | 151 | ### Tips 152 | - You can set up a cron to run once per day to automatically grab your previous day's data (assuming you are syncing your device each day) 153 | - If you want to archive data across a date range you can use curl's [ ] syntax to do it easily (thanks to [@Edrabbit](http://twitter.com/edrabbit) for the tip!). For example, to get all of May cached in /data: 154 | 155 | `curl http://localhost/basis-data-export/basisdataexport.php?d=2013-05-[01-31]` 156 | 157 | ### Special Tip for Windows Users and Setting Up SSL/cURL 158 | (Thanks to [@joshuagarity](https://github.com/joshuagarity) for the tip!) 159 | If you haven't already, you will need to install an SSL certificate on your system in order for the script to work (because the user authentication only works over SSL, and is required by cURL). Here's how: 160 | 161 | - Download a certificate file at http://curl.haxx.se/ca/cacert.pem and save it somewhere on your computer 162 | - Run `update php.ini -- add curl.cainfo = "PATH_TO/cacert.pem"` where `"PATH_TO/cacert.pem"` is the location of the certificate file you just downloaded. If you are running [XAMPP](https://www.apachefriends.org/index.html), you would save the `cacert.pem` to `c:\xampp\cacert.pem`. Then, open XAMPP, click on Config, then selected `PHP.ini`. On the second line of the file paste the following: 163 | 164 | `curl.cainfo = "C:\xampp\cacert.pem"` 165 | 166 | - Lastly, save the file and you should be able to run the script. 167 | 168 | 169 | -------------------------------------------------------------------------------- /basisdataexport.php: -------------------------------------------------------------------------------- 1 | 10 | * @link http://www.quantifiedbob.com 11 | * @license MIT License (see LICENSE.md) 12 | * 13 | * Usage: 14 | * This script can be run several ways. You can edit the BASIS_USERNAME, BASIS_PASSWORD, 15 | * and BASIS_EXPORT_FORMAT values under "Settings" below so you don't have to specify 16 | * them every time the script is run. Make sure the data/ folder is writeable! 17 | * 18 | * [Method 1] Via interactive mode 19 | * a. Open a terminal window and cd to this script's directory. 20 | * b. Type php basisdataexport.php 21 | * c. Follow the prompts (hit ENTER to use default values) 22 | * d. Your data will be save to /data/basis-data-[YYYY-MM-DD].[format]'; 23 | * 24 | * [Method 2] Via command-line arguments (useful for crons) 25 | * php basisdataexport.php -h -u[username] -p[pass] -d[YYYY-MM-DD] -f[json|csv|html] 26 | * 27 | * Options: 28 | * -u Basis username (if not used, defaults to BASIS_USERNAME) 29 | * -p Basis password (if not used, defaults to BASIS_PASSWORD) 30 | * -d Data export date (YYYY-MM-DD) (if not used, defaults to current date) 31 | * -f Data export format (json|csv|html) (if not used, defaults to json) 32 | * -h Show this help text 33 | * 34 | * [Method 3] Via web browser 35 | * This assumes your script is in a location that is executable via a web server, 36 | * i.e., http://localhost/basis-data-export/basisdataexport.php?u=[basis_username]&p=[basis_password]&d=[YYYY-MM-DD]&f=[format] 37 | * 38 | */ 39 | require_once(dirname(__FILE__) . '/BasisExport.class.php'); 40 | 41 | /////////////////////////////////////////////////////// 42 | // Settings 43 | /////////////////////////////////////////////////////// 44 | 45 | // Specify your Basis username, password, and default export format. Leaving blank 46 | // will require inputting these values manually each time the script is run. 47 | define('BASIS_USERNAME', ''); 48 | define('BASIS_PASSWORD', ''); 49 | define('BASIS_EXPORT_FORMAT', 'json'); 50 | 51 | // Enable/disable debug mode 52 | define('DEBUG', false); 53 | 54 | /////////////////////////////////////////////////////// 55 | // You shouldn't need to edit anything below this line! 56 | /////////////////////////////////////////////////////// 57 | 58 | // See if we are running in command-line mode 59 | if (php_sapi_name() == "cli") { 60 | 61 | // Check for command-line arguments, otherwise enter interactive mode. 62 | if($argc > 1) { 63 | $settings = runCommandLine(); 64 | } else { 65 | // Enter interactive mode 66 | $settings = runInteractive(); 67 | } 68 | 69 | } else { 70 | 71 | // Request has been make via web browser 72 | $settings = runHttp(); 73 | } 74 | 75 | // Create instance of BasisExport class 76 | $basis = new BasisExport($settings['basis_username'], $settings['basis_password']); 77 | $basis->debug = DEBUG; 78 | 79 | // Query Basis API for biometric data 80 | try { 81 | $basis->getMetrics($settings['basis_export_date'], $settings['basis_export_format']); 82 | } catch (Exception $e) { 83 | echo 'Exception: ', $e->getMessage(), "\n"; 84 | } 85 | 86 | // Query Basis API for sleep data 87 | try { 88 | $basis->getSleep($settings['basis_export_date'], $settings['basis_export_format']); 89 | } catch (Exception $e) { 90 | echo 'Exception: ', $e->getMessage(), "\n"; 91 | } 92 | 93 | // Query Basis API for activity data 94 | try { 95 | $basis->getActivities($settings['basis_export_date'], $settings['basis_export_format']); 96 | } catch (Exception $e) { 97 | echo 'Exception: ', $e->getMessage(), "\n"; 98 | } 99 | 100 | /** 101 | * Take parameters via command-line args 102 | **/ 103 | function runCommandLine() 104 | { 105 | $options = getopt("h::u::p::d::f::"); 106 | $settings = array(); 107 | $settings['basis_username'] = (!defined('BASIS_USERNAME')) ? '' : BASIS_USERNAME; 108 | $settings['basis_password'] = (!defined('BASIS_PASSWORD')) ? '' : BASIS_PASSWORD; 109 | $settings['basis_export_date'] = date('Y-m-d', strtotime('now', time())); 110 | $settings['basis_export_format'] = (!defined('BASIS_EXPORT_FORMAT')) ? 'json' : BASIS_EXPORT_FORMAT; 111 | 112 | while (list($key, $value) = each($options)) { 113 | if ($key == 'h') { 114 | echo "-------------------------\n"; 115 | echo "Basis data export script.\n"; 116 | echo "-------------------------\n"; 117 | echo "Usage:\n"; 118 | echo "php basisdataexport.php -u[username] -p[pass] -d[YYYY-MM-DD] -f[json|csv|html]\n\n"; 119 | echo "options:\n"; 120 | echo "-u Basis username\n"; 121 | echo "-p Basis password\n"; 122 | echo "-d Data export date (YYYY-MM-DD). If blank will use today's date\n"; 123 | echo "-f Data export format (json|csv|html)\n"; 124 | echo "-h Show this help text\n"; 125 | echo "-------------------------\n"; 126 | exit(); 127 | } 128 | if ($key == 'u') { 129 | if (empty($value)) { 130 | die ("No username specified!\n"); 131 | } else { 132 | $settings['basis_username'] = trim($value); 133 | } 134 | } 135 | if ($key == 'p') { 136 | if (empty($value)) { 137 | die ("No password specified!\n"); 138 | } else { 139 | $settings['basis_password'] = trim($value); 140 | } 141 | } 142 | if ($key == 'd') { 143 | if (empty($value)) { 144 | die ("No date specified!\n"); 145 | } else { 146 | $settings['basis_export_date'] = trim($value); 147 | } 148 | } 149 | if ($key == 'f') { 150 | if (empty($value)) { 151 | die ("No format specified!\n"); 152 | } else { 153 | $settings['basis_export_format'] = trim($value); 154 | } 155 | } 156 | 157 | } 158 | return $settings; 159 | } 160 | 161 | /** 162 | * Take parameters via interactive shell 163 | **/ 164 | function runInteractive() 165 | { 166 | $basis_username = (!defined('BASIS_USERNAME')) ? '' : BASIS_USERNAME; 167 | $basis_password = (!defined('BASIS_PASSWORD')) ? '' : BASIS_PASSWORD; 168 | $basis_password_mask = (!defined('BASIS_PASSWORD')) ? '' : '********'; 169 | 170 | // $basis_password_mask = (!) 171 | 172 | $basis_export_date = date('Y-m-d', strtotime('now', time())); 173 | $basis_export_format = (!defined('BASIS_EXPORT_FORMAT')) ? 'json' : BASIS_EXPORT_FORMAT; 174 | $settings = array(); 175 | 176 | echo "-------------------------\n"; 177 | echo "Basis data export script.\n"; 178 | echo "-------------------------\n"; 179 | $handle = fopen ("php://stdin","r"); 180 | echo "Enter Basis username [$basis_username]: "; 181 | $input_username = trim(fgets($handle)); 182 | $settings['basis_username'] = (empty($input_username) ? $basis_username : $input_username); 183 | echo "Enter Basis password [$basis_password_mask]: "; 184 | $input_password = trim(fgets($handle)); 185 | $settings['basis_password'] = (empty($input_password) ? $basis_password : $input_password); 186 | echo "Enter data export date (YYYY-MM-DD) [$basis_export_date] : "; 187 | $input_export_date = trim(fgets($handle)); 188 | $settings['basis_export_date'] = (empty($input_export_date) ? $basis_export_date : $input_export_date); 189 | echo "Enter export format (json|csv|html) [$basis_export_format] : "; 190 | $input_export_format = trim(fgets($handle)); 191 | $settings['basis_export_format'] = (empty($input_export_format) ? $basis_export_format : $input_export_format); 192 | fclose($handle); 193 | 194 | if (DEBUG ) { 195 | echo "-----------------------------\n"; 196 | echo "Using the following settings:\n"; 197 | echo "-----------------------------\n"; 198 | echo 'Username: ' . $settings['basis_username'] . "\n"; 199 | echo 'Password: ' . $settings['basis_password'] . "\n"; 200 | echo 'Date: ' . $settings['basis_export_date'] . "\n"; 201 | echo 'Format: ' . $settings['basis_export_format'] . "\n"; 202 | echo "-----------------------------\n"; 203 | } 204 | 205 | return ($settings); 206 | } 207 | 208 | /** 209 | * Take parameters via http $_GET args 210 | **/ 211 | function runHttp() 212 | { 213 | $basis_username = (!defined('BASIS_USERNAME')) ? '' : BASIS_USERNAME; 214 | $basis_password = (!defined('BASIS_PASSWORD')) ? '' : BASIS_PASSWORD; 215 | $basis_export_date = date('Y-m-d', strtotime('now', time())); 216 | $basis_export_format = (!defined('BASIS_EXPORT_FORMAT')) ? 'json' : BASIS_EXPORT_FORMAT; 217 | $settings = array(); 218 | 219 | $settings['basis_username'] = (empty($_GET['u']) ? $basis_username : strip_tags($_GET['u'])); 220 | $settings['basis_password'] = (empty($_GET['p']) ? $basis_password : strip_tags($_GET['p'])); 221 | $settings['basis_export_date'] = (empty($_GET['d']) ? $basis_export_date : strip_tags($_GET['d'])); 222 | $settings['basis_export_format'] = (empty($_GET['f']) ? $basis_export_format : strip_tags($_GET['f'])); 223 | 224 | return ($settings); 225 | } 226 | 227 | ?> 228 | -------------------------------------------------------------------------------- /BasisExport.class.php: -------------------------------------------------------------------------------- 1 | 10 | * @link http://www.quantifiedbob.com 11 | * @license MIT License (see LICENSE.md) 12 | * 13 | */ 14 | 15 | class BasisExport 16 | { 17 | // Basis login details 18 | private $username; 19 | private $password; 20 | 21 | // Enable/disable debugging 22 | public $debug = false; 23 | 24 | // Access token and refresh token 25 | private $access_token; 26 | private $refresh_token; 27 | 28 | // Data export date 29 | public $export_date; 30 | 31 | // Acceptable export formats 32 | private $export_formats = array('json', 'csv', 'html'); 33 | 34 | // These settings should be left as-is 35 | private $export_offset = 0; // don't pad export start/end times 36 | private $export_interval = 60; // data granularity (60 = 1 reading per minute) 37 | 38 | // Used for cURL cookie storage (needed for api access) 39 | private $cookie_jar; 40 | 41 | public function __construct($username, $password) 42 | { 43 | $this->username = $username; 44 | $this->password = $password; 45 | 46 | // Location to store cURL's CURLOPT_COOKIEJAR (for access_token and refresh_token cookies) 47 | $this->cookie_jar = dirname(__FILE__) . '/cookie.txt'; 48 | } 49 | 50 | /** 51 | * Attempts to login/authenticate to Basis server 52 | * @return bool 53 | * @throws Exception 54 | */ 55 | function doLogin() 56 | { 57 | $login_data = array( 58 | 'username' => $this->username, 59 | 'password' => $this->password, 60 | ); 61 | 62 | // Test configuration 63 | if ($this->debug) { 64 | $this->testConfig(); 65 | } 66 | 67 | // Initialize the cURL resource and make login request 68 | $ch = curl_init(); 69 | curl_setopt_array($ch, array( 70 | CURLOPT_URL => 'https://app.mybasis.com/login', 71 | CURLOPT_RETURNTRANSFER => true, 72 | CURLOPT_POST => true, 73 | CURLOPT_POSTFIELDS => $login_data, 74 | CURLOPT_FOLLOWLOCATION => true, 75 | CURLOPT_HEADER => 1, 76 | CURLOPT_COOKIESESSION => true, 77 | CURLOPT_COOKIEJAR => $this->cookie_jar 78 | )); 79 | $result = curl_exec($ch); 80 | 81 | if($result === false) { 82 | // A cURL error occurred 83 | throw new Exception('ERROR: cURL error - ' . curl_error($ch) . "\n"); 84 | return false; 85 | } 86 | curl_close($ch); 87 | 88 | // Make sure login was successful then save access_token and refresh_token cookies 89 | // for api requests. Basis returns the following cookie headers: 90 | // 91 | // Set-Cookie: scope=login; Domain=.mybasis.com; Expires=Sun, 01-Jan-2040 00:00:00 GMT; Secure; Path=/ 92 | // Set-Cookie: access_token=[ACCESS TOKEN STRING]; Domain=.mybasis.com; Expires=Sun, 01-Jan-2040 00:00:00 GMT; Secure; Path=/ 93 | // Set-Cookie: refresh_token=[REFRESH TOKEN STRING]; Domain=.mybasis.com; Expires=Sun, 01-Jan-2040 00:00:00 GMT; Secure; Path=/ 94 | 95 | // Regular expression used to parse cookies 96 | preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $matches); 97 | 98 | if (empty($matches)) { 99 | throw new Exception('ERROR: Unable to login! Check your username and password.'); 100 | return false; 101 | } else { 102 | // extract access_token and refresh_token cookies and set to variables 103 | parse_str($matches[1][1], $access_token_cookie); 104 | parse_str($matches[1][2], $refresh_token_cookie); 105 | 106 | if (empty($access_token_cookie['access_token'])) { 107 | throw new Exception('ERROR: Unable to get an access token!'); 108 | return false; 109 | } elseif (empty($refresh_token_cookie['refresh_token'])) { 110 | throw new Exception('ERROR: Unable to get a refresh token!'); 111 | return false; 112 | } else { 113 | $this->access_token = (string)$access_token_cookie['access_token']; 114 | $this->refresh_token = (string)$refresh_token_cookie['refresh_token']; 115 | if ($this->debug) { 116 | echo 'access_token cookie: ' . $this->access_token . "\n"; 117 | echo 'refresh_token cookie: ' . $this->refresh_token . "\n"; 118 | } 119 | } 120 | } 121 | } // doLogin() 122 | 123 | /** 124 | * Retreive user's biometric readings for given date and save to file 125 | * @param string $export_date Date in YYYY-MM-DD format 126 | * @param string $export_format Export type (json,csv,html) 127 | * @return bool 128 | * @throws Exception 129 | */ 130 | function getMetrics($export_date = '', $export_format = 'json') 131 | { 132 | // Check for YYYY-MM-DD date format, else throw error 133 | if (!isset($export_date)) { 134 | // default to yesterday 135 | $export_date = date('Y-m-d', strtotime('-1 day', time())); 136 | } else { 137 | $export_date = preg_replace('/[^-a-zA-Z0-9_]/', '', $export_date); 138 | } 139 | if (!$this->isValidDate($export_date)) { 140 | throw new Exception('ERROR: Invalid date - ' . $export_date . "\n"); 141 | return false; 142 | } 143 | 144 | // Make sure export format is valid 145 | if (!in_array($export_format, $this->export_formats)) { 146 | throw new Exception('ERROR: Invalid export format - ' . $export_format . "\n"); 147 | return false; 148 | } 149 | 150 | // Log into Basis account to authorize access. 151 | if (empty($this->access_token) || empty($this->refresh_token)) { 152 | try { 153 | $this->doLogin(); 154 | } catch (Exception $e) { 155 | echo 'Caught exception: ', $e->getMessage(), "\n"; 156 | return false; 157 | } 158 | } 159 | 160 | // Request data from Basis for selected date. Note we're requesting all available data. 161 | $metrics_url = 'https://app.mybasis.com/api/v1/metricsday/me?' 162 | . 'day=' . $export_date 163 | . '&padding=' . $this->export_offset 164 | . '&heartrate=true' 165 | . '&steps=true' 166 | . '&calories=true' 167 | . '&gsr=true' 168 | . '&skin_temp=true' 169 | . '&air_temp=true'; 170 | 171 | // Initialize the cURL resource and make api request 172 | $ch = curl_init(); 173 | curl_setopt_array($ch, array( 174 | CURLOPT_URL => $metrics_url, 175 | CURLOPT_RETURNTRANSFER => true, 176 | CURLOPT_COOKIEFILE => $this->cookie_jar 177 | )); 178 | $result = curl_exec($ch); 179 | $response_code = curl_getinfo ($ch, CURLINFO_HTTP_CODE); 180 | 181 | if ($response_code == '401') { 182 | throw new Exception("ERROR: Unauthorized!\n"); 183 | return false; 184 | } 185 | 186 | curl_close($ch); 187 | 188 | // Parse data from JSON response 189 | $json = json_decode($result, true); 190 | $report_date = $json['starttime']; // report date, as UNIX timestamp 191 | $heartrates = $json['metrics']['heartrate']['values']; 192 | $steps = $json['metrics']['steps']['values']; 193 | $calories = $json['metrics']['calories']['values']; 194 | $gsrs = $json['metrics']['gsr']['values']; 195 | $skintemps = $json['metrics']['skin_temp']['values']; 196 | $airtemps = $json['metrics']['air_temp']['values']; 197 | 198 | if ($export_format == 'html') { 199 | // Save results as .html file 200 | $file = dirname(__FILE__) . '/data/basis-data-' . $export_date . '-metrics.html'; 201 | $html = $this->metricsToHTML($json); 202 | if (!file_put_contents($file, $html)) { 203 | throw new Exception("ERROR: Could not save data to file $file!"); 204 | return false; 205 | } 206 | 207 | } else if ($export_format == 'csv') { 208 | // Save results as .csv file 209 | $file = dirname(__FILE__) . '/data/basis-data-' . $export_date . '-metrics.csv'; 210 | 211 | $fp = fopen($file, 'w'); 212 | if(!$fp) { 213 | throw new Exception("ERROR: Could not save data to file $file!"); 214 | return false; 215 | } 216 | fputcsv($fp, array('timestamp', 'heartrate', 'steps', 'calories', 'gsr', 'skintemp', 'airtemp')); 217 | for ($i=0; $iexport_interval, date("n", $report_date), date("j", $report_date), date("Y", $report_date))); 220 | $row = array($timestamp, $heartrates[$i], $steps[$i], $calories[$i], $gsrs[$i], $skintemps[$i], $airtemps[$i]); 221 | 222 | // Add row to csv file 223 | fputcsv($fp, $row); 224 | } 225 | fclose($fp); 226 | 227 | } else { 228 | // Save results as .json file 229 | $file = dirname(__FILE__) . '/data/basis-data-' . $export_date . '-metrics.json'; 230 | if (!file_put_contents($file, $result)) { 231 | throw new Exception("ERROR: Could not save data to file $file!"); 232 | return false; 233 | } 234 | } 235 | } 236 | 237 | /** 238 | * Retreive user's sleep data for given date and save to file 239 | * @param string $export_date Date in YYYY-MM-DD format 240 | * @param string $export_format Export type (json,csv,html) 241 | * @return bool 242 | * @throws Exception 243 | */ 244 | function getSleep($export_date = '', $export_format = 'json') 245 | { 246 | // Check for YYYY-MM-DD date format, else throw error 247 | if (!isset($export_date)) { 248 | // default to yesterday 249 | $export_date = date('Y-m-d', strtotime('-1 day', time())); 250 | } else { 251 | $export_date = preg_replace('/[^-a-zA-Z0-9_]/', '', $export_date); 252 | } 253 | if (!$this->isValidDate($export_date)) { 254 | throw new Exception('ERROR: Invalid date - ' . $export_date . "\n"); 255 | return false; 256 | } 257 | 258 | // Make sure export format is valid 259 | if (!in_array($export_format, $this->export_formats)) { 260 | throw new Exception('ERROR: Invalid export format - ' . $export_format . "\n"); 261 | return false; 262 | } 263 | 264 | // Log into Basis account to authorize access. 265 | if (empty($this->access_token)) { 266 | try { 267 | $this->doLogin(); 268 | } catch (Exception $e) { 269 | echo 'Caught exception: ', $e->getMessage(), "\n"; 270 | return false; 271 | } 272 | } 273 | 274 | // Request sleep data from Basis for selected date. Note we're requesting all available data. 275 | $sleep_url = 'https://app.mybasis.com/api/v2/users/me/days/' . $export_date . '/activities?' 276 | . 'type=sleep' 277 | . '&expand=activities.stages,activities.events'; 278 | 279 | // Initialize the cURL resource and make api request 280 | $ch = curl_init(); 281 | curl_setopt_array($ch, array( 282 | CURLOPT_URL => $sleep_url, 283 | CURLOPT_RETURNTRANSFER => true, 284 | CURLOPT_COOKIEFILE => $this->cookie_jar 285 | )); 286 | $result = curl_exec($ch); 287 | $response_code = curl_getinfo ($ch, CURLINFO_HTTP_CODE); 288 | 289 | if ($response_code == '401') { 290 | throw new Exception("ERROR: Unauthorized!\n"); 291 | return false; 292 | } 293 | 294 | curl_close($ch); 295 | 296 | // Parse data from JSON response 297 | $json = json_decode($result, true); 298 | 299 | // Create an array of sleep activities. Basis breaks up sleep into individual 300 | // events if there is an interruption longer than 15 minutes. 301 | $sleep = array(); 302 | $sleep_activities = $json['content']['activities']; 303 | foreach ($sleep_activities as $sleep_activity) { 304 | // Add sleep event to array 305 | $sleep[] = array( 306 | 'start_time' => isset($sleep_activity['start_time']['timestamp']) ? $sleep_activity['start_time']['timestamp'] : '', 307 | 'start_time_iso' => isset($sleep_activity['start_time']['iso']) ? $sleep_activity['start_time']['iso'] : '', 308 | 'start_time_timezone' => isset($sleep_activity['start_time']['time_zone']['name']) ? $sleep_activity['start_time']['time_zone']['name'] : '', 309 | 'start_time_offset' => isset($sleep_activity['start_time']['time_zone']['offset']) ? $sleep_activity['start_time']['time_zone']['offset'] : '', 310 | 'end_time' => isset($sleep_activity['end_time']['timestamp']) ? $sleep_activity['end_time']['timestamp'] : '', 311 | 'end_time_iso' => isset($sleep_activity['end_time']['iso']) ? $sleep_activity['end_time']['iso'] : '', 312 | 'end_time_timezone' => isset($sleep_activity['end_time']['time_zone']['name']) ? $sleep_activity['end_time']['time_zone']['name'] : '', 313 | 'end_time_offset' => isset($sleep_activity['end_time']['time_zone']['offset']) ? $sleep_activity['end_time']['time_zone']['offset'] : '', 314 | 'heart_rate_avg' => isset($sleep_activity['heart_rate']['avg']) ? $sleep_activity['heart_rate']['avg'] : '', 315 | 'heart_rate_min' => isset($sleep_activity['heart_rate']['min']) ? $sleep_activity['heart_rate']['min'] : '', 316 | 'heart_rate_max' => isset($sleep_activity['heart_rate']['max']) ? $sleep_activity['heart_rate']['max'] : '', 317 | 'actual_seconds' => isset($sleep_activity['actual_seconds']) ? $sleep_activity['actual_seconds'] : '', 318 | 'calories' => isset($sleep_activity['calories']) ? $sleep_activity['calories'] : '', 319 | 'light_minutes' => isset($sleep_activity['sleep']['light_minutes']) ? $sleep_activity['sleep']['light_minutes'] : '', 320 | 'deep_minutes' => isset($sleep_activity['sleep']['deep_minutes']) ? $sleep_activity['sleep']['deep_minutes'] : '', 321 | 'rem_minutes' => isset($sleep_activity['sleep']['rem_minutes']) ? $sleep_activity['sleep']['rem_minutes'] : '', 322 | 'interruption_minutes' => isset($sleep_activity['sleep']['interruption_minutes']) ? $sleep_activity['sleep']['interruption_minutes'] : '', 323 | 'unknown_minutes' => isset($sleep_activity['sleep']['unknown_minutes']) ? $sleep_activity['sleep']['unknown_minutes'] : '', 324 | 'interruptions' => isset($sleep_activity['sleep']['interruptions']) ? $sleep_activity['sleep']['interruptions'] : '', 325 | 'toss_and_turn' => isset($sleep_activity['sleep']['toss_and_turn']) ? $sleep_activity['sleep']['toss_and_turn'] : '', 326 | 'events' => isset($sleep_activity['events']) ? $sleep_activity['events'] : '', 327 | 'type' => isset($sleep_activity['type']) ? $sleep_activity['type'] : '', 328 | 'state' => isset($sleep_activity['state']) ? $sleep_activity['state'] : '', 329 | 'version' => isset($sleep_activity['version']) ? $sleep_activity['version'] : '', 330 | 'id' => isset($sleep_activity['id']) ? $sleep_activity['id'] : '' 331 | ); 332 | } 333 | 334 | if ($export_format == 'html') { 335 | // Save results as .html file 336 | $file = dirname(__FILE__) . '/data/basis-data-' . $export_date . '-sleep.html'; 337 | $html = $this->sleepToHTML($json); 338 | if (!file_put_contents($file, $html)) { 339 | throw new Exception("ERROR: Could not save data to file $file!"); 340 | return false; 341 | } 342 | 343 | } else if ($export_format == 'csv') { 344 | // Save results as .csv file 345 | $file = dirname(__FILE__) . '/data/basis-data-' . $export_date . '-sleep.csv'; 346 | 347 | $fp = fopen($file, 'w'); 348 | if(!$fp) { 349 | throw new Exception("ERROR: Could not save data to file $file!"); 350 | return false; 351 | } 352 | fputcsv($fp, array( 353 | 'start time', 'start time ISO', 'start time timezone', 'start time offset', 354 | 'end time', 'end time ISO', 'end time timezone', 'end time offset', 355 | 'light mins', 'deep mins', 'rem mins', 'interruption mins', 'unknown mins', 'interruptions', 356 | 'toss turns', 'type', 'actual seconds', 'calories', 'heart rate avg', 'heart rate min', 357 | 'heart rate max', 'state', 'version', 'id' 358 | ) 359 | ); 360 | 361 | for ($i=0; $iisValidDate($export_date)) { 411 | throw new Exception('ERROR: Invalid date - ' . $export_date . "\n"); 412 | return false; 413 | } 414 | 415 | // Make sure export format is valid 416 | if (!in_array($export_format, $this->export_formats)) { 417 | throw new Exception('ERROR: Invalid export format - ' . $export_format . "\n"); 418 | return false; 419 | } 420 | 421 | // Log into Basis account to authorize access. 422 | if (empty($this->access_token)) { 423 | try { 424 | $this->doLogin(); 425 | } catch (Exception $e) { 426 | echo 'Caught exception: ', $e->getMessage(), "\n"; 427 | return false; 428 | } 429 | } 430 | 431 | // Request activities data from Basis for selected date. Note we're requesting all available data. 432 | $activities_url = 'https://app.mybasis.com/api/v2/users/me/days/' . $export_date . '/activities?' 433 | . 'type=run,walk,bike' 434 | . '&expand=activities'; 435 | 436 | // Initialize the cURL resource and make api request 437 | $ch = curl_init(); 438 | curl_setopt_array($ch, array( 439 | CURLOPT_URL => $activities_url, 440 | CURLOPT_RETURNTRANSFER => true, 441 | CURLOPT_COOKIEFILE => $this->cookie_jar 442 | )); 443 | $result = curl_exec($ch); 444 | $response_code = curl_getinfo ($ch, CURLINFO_HTTP_CODE); 445 | 446 | if ($response_code == '401') { 447 | throw new Exception("ERROR: Unauthorized!\n"); 448 | return false; 449 | } 450 | 451 | curl_close($ch); 452 | 453 | // Parse data from JSON response 454 | $json = json_decode($result, true); 455 | 456 | // Create an array of activities. 457 | $activities = array(); 458 | $activity_items = $json['content']['activities']; 459 | foreach ($activity_items as $activity_item) { 460 | // Add activity to array 461 | $activities[] = array( 462 | 'start_time' => isset($activity_item['start_time']['timestamp']) ? $activity_item['start_time']['timestamp'] : '', 463 | 'start_time_iso' => isset($activity_item['start_time']['iso']) ? $activity_item['start_time']['iso'] : '', 464 | 'start_time_timezone' => isset($activity_item['start_time']['time_zone']['name']) ? $activity_item['start_time']['time_zone']['name'] : '', 465 | 'start_time_offset' => isset($activity_item['start_time']['time_zone']['offset']) ? $activity_item['start_time']['time_zone']['offset'] : '', 466 | 'end_time' => isset($activity_item['end_time']['timestamp']) ? $activity_item['end_time']['timestamp'] : '', 467 | 'end_time_iso' => isset($activity_item['end_time']['iso']) ? $activity_item['end_time']['iso'] : '', 468 | 'end_time_timezone' => isset($activity_item['end_time']['time_zone']['name']) ? $activity_item['end_time']['time_zone']['name'] : '', 469 | 'end_time_offset' => isset($activity_item['end_time']['time_zone']['offset']) ? $activity_item['end_time']['time_zone']['offset'] : '', 470 | 'heart_rate_avg' => isset($activity_item['heart_rate']['avg']) ? $activity_item['heart_rate']['avg'] : '', 471 | 'heart_rate_min' => isset($activity_item['heart_rate']['min']) ? $activity_item['heart_rate']['min'] : '', 472 | 'heart_rate_max' => isset($activity_item['heart_rate']['max']) ? $activity_item['heart_rate']['max'] : '', 473 | 'actual_seconds' => isset($activity_item['actual_seconds']) ? $activity_item['actual_seconds'] : '', 474 | 'calories' => isset($activity_item['calories']) ? $activity_item['calories'] : '', 475 | 'steps' => isset($activity_item['steps']) ? $activity_item['steps'] : '', 476 | 'minutes' => isset($activity_item['minutes']) ? $activity_item['minutes'] : '', 477 | 'type' => isset($activity_item['type']) ? $activity_item['type'] : '', 478 | 'state' => isset($activity_item['state']) ? $activity_item['state'] : '', 479 | 'version' => isset($activity_item['version']) ? $activity_item['version'] : '', 480 | 'id' => isset($activity_item['id']) ? $activity_item['id'] : '' 481 | ); 482 | } 483 | 484 | if ($export_format == 'html') { 485 | // Save results as .html file 486 | $file = dirname(__FILE__) . '/data/basis-data-' . $export_date . '-activities.html'; 487 | $html = $this->activitiesToHTML($json); 488 | if (!file_put_contents($file, $html)) { 489 | throw new Exception("ERROR: Could not save data to file $file!"); 490 | return false; 491 | } 492 | 493 | } else if ($export_format == 'csv') { 494 | // Save results as .csv file 495 | $file = dirname(__FILE__) . '/data/basis-data-' . $export_date . '-activities.csv'; 496 | 497 | $fp = fopen($file, 'w'); 498 | if(!$fp) { 499 | throw new Exception("ERROR: Could not save data to file $file!"); 500 | return false; 501 | } 502 | fputcsv($fp, array( 503 | 'start time', 'start time ISO', 'start time timezone', 'start time offset', 504 | 'end time', 'end time ISO', 'end time timezone', 'end time offset', 505 | 'type', 'actual seconds', 'steps', 'calories', 'minutes', 'heart rate avg', 'heart rate min', 'heart rate max', 506 | 'state', 'version', 'id' 507 | ) 508 | ); 509 | for ($i=0; $i 593 | My Metrics Data for {$report_date} 594 | 623 | 624 | 625 | 626 |

My Metrics Data for {$report_date}

627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | HTML; 642 | // Format and echo data to browser 643 | for ($i=0; $iexport_interval, date("n", $report_timestamp), date("j", $report_timestamp), date("Y", $report_timestamp))); 646 | 647 | $html .= ''; 648 | $html .= ''; 649 | $html .= ''; 650 | $html .= ''; 651 | $html .= ''; 652 | $html .= ''; 653 | $html .= ''; 654 | $html .= ''; 655 | $html .= ''; 656 | } 657 | $html .= << 659 |
ReadingHeartrateStepsCaloriesGSRSkin TempAir Temp
' . $timestamp . '' . ($heartrates[$i] == '' ? 'null' : $heartrates[$i]) . '' . ($steps[$i] == '' ? 'null' : $steps[$i]) . '' . ($calories[$i] == '' ? 'null' : $calories[$i]) . '' . ($gsrs[$i] == '' ? 'null' : $gsrs[$i]) . '' . ($skintemps[$i] == '' ? 'null' : $skintemps[$i]) . '' . ($airtemps[$i] == '' ? 'null' : $airtemps[$i]) . '
660 | 661 | 662 | HTML; 663 | return $html; 664 | 665 | } // end metricsToHTML 666 | 667 | /** 668 | * Generates an HTML summary of sleep data from json response. 669 | * Yes, this function is *very* ugly. It's only included for legacy purposes 670 | * and most likely you would never use this unless you want to open up 671 | * something nicely formatted in a web browser. 672 | * @param string $json JSON response from server 673 | * @return string Formatted HTML summary 674 | */ 675 | function sleepToHTML($json) 676 | { 677 | $report_date = strftime("%Y-%m-%d", $json['content']['activities'][0]['start_time']['timestamp']); 678 | $html = << 680 | My Sleep Data for {$report_date} 681 | 710 | 711 | 712 |

My Sleep Data for {$report_date}

713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | HTML; 734 | 735 | $sleep = $json['content']['activities']; 736 | // Format and echo data to browser 737 | for ($i=0; $i'; 742 | $html .= ''; 743 | $html .= ''; 744 | $html .= ''; 745 | $html .= ''; 746 | $html .= ''; 747 | $html .= ''; 748 | $html .= ''; 749 | $html .= ''; 750 | $html .= ''; 751 | $html .= ''; 752 | $html .= ''; 753 | $html .= ''; 754 | $html .= ''; 755 | $html .= ''; 756 | } 757 | $html .= << 759 |
Start TimeEnd TimeHeart Rate AvgHeart Rate MinHeart Rate MaxCaloriesActual SecsLight MinsDeep MinsREM MinsInterruption MinsUnknown MinsInterruptionsToss Turns
' . $end_time . '' . (isset($sleep[$i]['heart_rate']['avg']) ? $sleep[$i]['heart_rate']['avg'] : '-') . '' . (isset($sleep[$i]['heart_rate']['min']) ? $sleep[$i]['heart_rate']['min'] : '-') . '' . (isset($sleep[$i]['heart_rate']['max']) ? $sleep[$i]['heart_rate']['max'] : '-') . '' . (isset($sleep[$i]['calories']) ? $sleep[$i]['calories'] : '0') . '' . (isset($sleep[$i]['actual_seconds']) ? $sleep[$i]['actual_seconds'] : '0') . '' . (isset($sleep[$i]['sleep']['light_minutes']) ? $sleep[$i]['sleep']['light_minutes'] : '0') . '' . (isset($sleep[$i]['sleep']['deep_minutes']) ? $sleep[$i]['sleep']['deep_minutes'] : '0') . '' . (isset($sleep[$i]['sleep']['rem_minutes']) ? $sleep[$i]['sleep']['rem_minutes'] : '0') . '' . (isset($sleep[$i]['sleep']['interruption_minutes']) ? $sleep[$i]['sleep']['interruption_minutes'] : '0') . '' . (isset($sleep[$i]['sleep']['unknown_minutes']) ? $sleep[$i]['sleep']['unknown_minutes'] : '0') . '' . (isset($sleep[$i]['sleep']['interruptions']) ? $sleep[$i]['sleep']['interruptions'] : '0') . '' . (isset($sleep[$i]['sleep']['toss_and_turn']) ? $sleep[$i]['sleep']['toss_and_turn'] : '0') . '
760 | 761 | 762 | HTML; 763 | return $html; 764 | 765 | } // end sleepToHTML 766 | 767 | /** 768 | * Generates an HTML summary of activities data from json response. 769 | * Yes, this function is *very* ugly. It's only included for legacy purposes 770 | * and most likely you would never use this unless you want to open up 771 | * something nicely formatted in a web browser. 772 | * @param string $json JSON response from server 773 | * @return string Formatted HTML summary 774 | */ 775 | function activitiesToHTML($json) 776 | { 777 | $report_date = strftime("%Y-%m-%d", $json['content']['activities'][0]['start_time']['timestamp']); 778 | 779 | $html = << 781 | My Activity Data for {$report_date} 782 | 811 | 812 | 813 |

My Activity Data for {$report_date}

814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | HTML; 831 | $activities = $json['content']['activities']; 832 | 833 | // Format and echo data to browser 834 | for ($i=0; $i'; 839 | $html .= ''; 840 | $html .= ''; 841 | $html .= ''; 842 | $html .= ''; 843 | $html .= ''; 844 | $html .= ''; 845 | $html .= ''; 846 | $html .= ''; 847 | $html .= ''; 848 | $html .= ''; 849 | } 850 | $html .= << 852 |
Start TimeEnd TimeHeart Rate AvgHeart Rate MinHeart Rate MaxActual SecsCaloriesStepsMinutesType
' . $end_time . '' . (isset($activities[$i]['heart_rate']['avg']) ? $activities[$i]['heart_rate']['avg'] : '-') . '' . (isset($activities[$i]['heart_rate']['min']) ? $activities[$i]['heart_rate']['min'] : '-') . '' . (isset($activities[$i]['heart_rate']['max']) ? $activities[$i]['heart_rate']['max'] : '-') . '' . (isset($activities[$i]['actual_seconds']) ? $activities[$i]['actual_seconds'] : '0') . '' . (isset($activities[$i]['calories']) ? $activities[$i]['calories'] : '0') . '' . (isset($activities[$i]['steps']) ? $activities[$i]['steps'] : '0') . '' . (isset($activities[$i]['minutes']) ? $activities[$i]['minutes'] : '0') . '' . (isset($activities[$i]['type']) ? $activities[$i]['type'] : '') . '
853 | 854 | 855 | HTML; 856 | return $html; 857 | 858 | } // end activitiesToHTML 859 | 860 | 861 | } // end class BasisExport 862 | ?> 863 | --------------------------------------------------------------------------------