def unsubscribe_notifications(request): logger.debug("[medical-compliance] Notifications unsubscribe was called.") withings_user = None try: withings_user = get_withings_user(settings.WITHINGS_USER_ID) except Exception as e: logger.error( "[medical-compliance] Unable to retrieve withings user by userid in the settings file: %s" % (e)) return HttpResponseServerError("User with id %s could not be found." % (settings.WITHINGS_USER_ID)) credentials = WithingsCredentials( access_token=withings_user.oauth_token, access_token_secret=withings_user.oauth_token_secret, consumer_key=settings.WITHINGS_CONSUMER_KEY, consumer_secret=settings.WITHINGS_CONSUMER_SECRET, user_id=withings_user.userid) client = WithingsApi(credentials) response_data = client.unsubscribe(get_full_callback_url(request), appli=1) logger.debug( "[medical-compliance] Notifications unsubscribe response: %s." % (response_data)) return HttpResponse(json.dumps(response_data), content_type="application/json")
def subscribe_notifications(request): logger.debug("[medical-compliance] Notifications subscribe was called.") withings_user = None try: withings_user = get_withings_user(settings.WITHINGS_USER_ID) except Exception as e: logger.error("[medical-compliance] Unable to retrieve withings user by userid in the settings file: %s" % (e)) return HttpResponseServerError("User with id %s could not be found." % (settings.WITHINGS_USER_ID)) credentials = WithingsCredentials(access_token=withings_user.oauth_token, access_token_secret=withings_user.oauth_token_secret, consumer_key=settings.WITHINGS_CONSUMER_KEY, consumer_secret=settings.WITHINGS_CONSUMER_SECRET, user_id=withings_user.userid) client = WithingsApi(credentials) callback_uri = get_full_callback_url(request) logger.debug("[medical-compliance] Subscribing to Withings notifications for user_id=%s with URI: %s." % ( withings_user.userid, callback_uri)) response_data = client.subscribe(callback_uri, "Subscribe for weight measurement notifications.", appli=1) logger.debug("[medical-compliance] Notifications subscribe response: %s." % (response_data)) return HttpResponse(json.dumps(response_data), content_type="application/json")
def open_token(self, auth): try: with open(withing_config_file, 'r') as withing_token_file: self._log.debug(u"Opening File") self.creds = pickle.load(withing_token_file) self._log.debug(u"Getting user") self.client = WithingsApi(self.creds) self.user = self.client.get_user() if user == None : self.auth = WithingsAuth(self.api_key, self.api_secret) self.authorize_url = self.auth.get_authorize_url() print("Go to %s allow the app and copy your oauth_verifier" %self.authorize_url) self.oauth_verifier = raw_input('Please enter your oauth_verifier: ') self.creds = auth.get_credentials(self.oauth_verifier) self.client = WithingsApi(creds) self.user = self.client.get_user() if user == None : self._log.error(u"Error getting user, from code") self._log.error(error) sys.exit("refreshing token failed from refresh_token") #TODO stop plugin else : self._log.warning(u"Token succesfully refresh with token_refresh from file") with open(withing_config_file, 'w') as withing_token_file: pickle.dump(self.creds, withing_token_file) except: self._log.error(u"Error with file saved or no file saved") self._log.error(u"Go to Advanced page to generate a new token file")
def __init__(self, log, api_id, api_secret, period, dataPath): try: """ Create a withing instance, allowing to use withing api """ self._log = log self.api_id = api_id self.api_secret = api_secret self.period = period self._sensors = [] self._dataPath = dataPath if not os.path.exists(self._dataPath) : self._log.info(u"Directory data not exist, trying create : %s" , self._dataPath) try : os.mkdir(self._dataPath) self._log.info(u"Withings data directory created : %s" %self._dataPath) except Exception as e: self._log.error(e.message) raise withingException ("Withings data directory not exist : %s" % self._dataPath) if not os.access(self._dataPath, os.W_OK) : self._log.error("User %s haven't write access on data directory : %s" %(user, self._dataPath)) raise withingException ("User %s haven't write access on data directory : %s" %(user, self._dataPath)) self.withing_config_file = os.path.join(os.path.dirname(__file__), '../data/withings.json') self.auth = WithingsAuth(self.api_id, self.api_secret) # self.open_token(self.auth) try: with open(self.withing_config_file, 'r') as withing_token_file: self._log.debug(u"Opening File") self.creds = pickle.load(withing_token_file) self._log.debug(u"Getting user") self.client = WithingsApi(self.creds) self.user = self.client.get_user() if self.user == None : self.auth = WithingsAuth(self.api_key, self.api_secret) self.authorize_url = self.auth.get_authorize_url() print("Go to %s allow the app and copy your oauth_verifier" %self.authorize_url) self.oauth_verifier = raw_input('Please enter your oauth_verifier: ') self.creds = auth.get_credentials(self.oauth_verifier) self.client = WithingsApi(creds) self.user = self.client.get_user() if user == None : self._log.error(u"Error getting user, from code") self._log.error(error) sys.exit("refreshing token failed from refresh_token") #TODO stop plugin else : self._log.warning(u"Token succesfully refresh with token_refresh from file") with open(self.withing_config_file, 'w') as withing_token_file: pickle.dump(self.creds, withing_token_file) except ValueError: self._log.error(u"error reading Withing.") return except ValueError: self._log.error(u"error reading Withing.")
def store_measures(creds): client = WithingsApi(creds) # lastupdate = int(time.time()) measures = client.get_measures() with db_connection() as db_conn: for m in measures: with db_conn.cursor() as c: c.execute('SELECT 1 FROM withings_measures WHERE grpid = %s', (m.grpid,)) if c.fetchone(): continue # grpid, wuserid, weight, height, fat_free_mass, fat_ratio, fat_mass_weight, diastolic_blood_pressure, systolic_blood_pressure, heart_pulse, created_at c.execute('INSERT INTO withings_measures (grpid, wuserid, weight, height, fat_free_mass, fat_ratio, fat_mass_weight, diastolic_blood_pressure, systolic_blood_pressure, heart_pulse, wdate) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', (m.grpid, creds.user_id, m.weight, m.height, m.fat_free_mass, m.fat_ratio, m.fat_mass_weight, m.diastolic_blood_pressure, m.systolic_blood_pressure, m.heart_pulse, m.date))
def withings_get_data(healthdevice): height = None weight = None daily_exercise = None creds = WithingsCredentials( access_token=healthdevice.access_token, access_token_secret=healthdevice.meta['access_token_secret'], consumer_key=WITHINGS_SETTINGS['CONSUMER_KEY'], consumer_secret=WITHINGS_SETTINGS['CONSUMER_SECRET'], user_id=healthdevice.meta['user_id']) client = WithingsApi(creds) try: measures = client.get_measures(limit=1, meastype='1') # measure type: weight weight = measures[0].weight except: pass try: measures = client.get_measures(limit=1, meastype='4') # mesuare type: height height = measures[0].height * 100 except: pass try: workouts_params = { 'userid': healthdevice.meta['user_id'], 'startdateymd': (date.today() - timedelta(days=PERIOD)).isoformat(), 'enddateymd': date.today().isoformat() } workouts = client.request('v2/measure', 'getworkouts', params=workouts_params) if 'series' in workouts: total_seconds = reduce( lambda acc, item: acc + withings_get_item_duration(item), workouts['series'], 0) daily_exercise = round(total_seconds / (PERIOD * 60), 0) except: pass if height is None and weight is None and daily_exercise is None: return None else: return { 'height': height, 'weight': weight, 'daily_exercise': daily_exercise }
def generate_token_file(oauth_verifier): with open(withing_temp_file, 'r') as withing_auth_file: auth = pickle.load(withing_auth_file) creds = auth.get_credentials(oauth_verifier) client = WithingsApi(creds) user = client.get_user() if user == None : flash(gettext(u"Error while getting token from Withign code check you client id/secret redirect url or oauth_verifier"),"error") else: withing_user_file = os.path.join(os.path.dirname(__file__), '../data/'+creds.user_id+'-withings.json') with open(withing_user_file, 'w') as withing_token_file: pickle.dump(creds, withing_token_file) flash(gettext(u"Successfully generate token. Please restart the plugin."), "success")
def setUp(self): self.mock_api = True if self.mock_api: self.creds = WithingsCredentials() else: config = ConfigParser.ConfigParser() config.read('withings.conf') self.creds = WithingsCredentials( consumer_key=config.get('withings', 'consumer_key'), consumer_secret=config.get('withings', 'consumer_secret'), access_token=config.get('withings', 'access_token'), access_token_secret=config.get('withings', 'access_token_secret'), user_id=config.get('withings', 'user_id')) self.api = WithingsApi(self.creds)
def test_attributes(self): """ Make sure the WithingsApi objects have the right attributes """ assert hasattr(WithingsApi, 'URL') creds = WithingsCredentials(user_id='FAKEID') api = WithingsApi(creds) assert hasattr(api, 'credentials') assert hasattr(api, 'oauth') assert hasattr(api, 'client')
def getWithingsClient(): auth = WithingsAuth(CONSUMER_KEY, CONSUMER_SECRET) authorize_url = auth.get_authorize_url() print "Go to %s allow the app and copy your oauth_verifier" % authorize_url oauth_verifier = raw_input('Please enter your oauth_verifier: ') creds = auth.get_credentials(oauth_verifier) client = WithingsApi(creds) return client
def test_attribute_defaults(self): """ Make sure WithingsApi object attributes have the correct defaults """ self.assertEqual(WithingsApi.URL, 'http://wbsapi.withings.net') creds = WithingsCredentials(user_id='FAKEID') api = WithingsApi(creds) self.assertEqual(api.credentials, creds) self.assertEqual(api.client.auth, api.oauth) self.assertEqual(api.client.params, {'userid': creds.user_id})
def refresh_weight(cfg_file, engine, db_df): print("REFRESHING WEIGHT...") parser = configparser.ConfigParser() parser.read(cfg_file) consumer_key = parser.get('withings', 'consumer_key') consumer_secret = parser.get('withings', 'consumer_secret') access_token = parser.get('withings', 'access_token') access_token_secret = parser.get('withings', 'access_token_secret') user_id = parser.get('withings', 'user_id') credentials = WithingsCredentials(access_token=access_token, access_token_secret=access_token_secret, consumer_key=consumer_key, consumer_secret=consumer_secret, user_id=user_id) client = WithingsApi(credentials) [date_start, date_end] = get_target_date_endpoints('Weight', db_df) date_query = date_start date_diff = date_end - date_query days = date_diff.days + 2 measures = client.get_measures(meastype=1, limit=days) measures.pop(0) weight_json = [{ 'weight': (float("{:.1f}".format(x.weight * 2.20462))), 'date': x.date.strftime('%Y-%m-%d') } for x in measures] date_values = [[pd.tseries.offsets.to_datetime(x['date']), x['weight']] for x in weight_json] updated_df = insert_values(date_values, 'Weight', db_df) with engine.connect() as conn, conn.begin(): updated_df.to_sql('fitness', conn, if_exists='replace') return updated_df
def _fetch_withings(): results = [] creds = WithingsCredentials() creds.access_token = ACCESS_TOKEN creds.access_token_secret = ACCESS_TOKEN_SECRET creds.user_id = USER_ID creds.consumer_key = CONSUMER_KEY creds.consumer_secret = CONSUMER_SECRET client = WithingsApi(creds) readings = { # predictions 'future': { 'x': '', 'diastolic': '', 'systolic': '', 'pulse': '', 'simple_moving_average': { 'diastolic': '', 'pulse': '', 'systolic': '', } }, # analysis of past readings 'past': { 'x': '', 'diastolic': '', 'systolic': '', 'pulse': '', 'simple_moving_average': { 'diastolic': '', 'pulse': '', 'systolic': '', } } } measures = client.get_measures() # make sure the graph goes left to right measures.reverse() last_reading_date = measures[-1].date counter = 1 raw_readings = { 'systolic': [], 'diastolic': [], 'pulse': [], } for measure in measures: if measure.systolic_blood_pressure\ and measure.diastolic_blood_pressure: next_date = last_reading_date + timedelta(days=counter) # sort out date times readings['past']['x'] += '"' + measure.date.strftime( '%Y-%m-%d %H:%M:%S') + '",' readings['future']['x'] += '"' + next_date.strftime( '%Y-%m-%d %H:%M:%S') + '",' readings['past']['systolic'] += str( measure.systolic_blood_pressure) + ',' readings['past']['diastolic'] += str( measure.diastolic_blood_pressure) + ',' # keep ints for for sending to ALGORITHMIA # should really rename it... raw_readings['systolic'].append(measure.systolic_blood_pressure) raw_readings['diastolic'].append(measure.diastolic_blood_pressure) if measure.heart_pulse and measure.heart_pulse > 30: raw_readings['pulse'].append(measure.heart_pulse) readings['past']['pulse'] += str(measure.heart_pulse) + ',' counter += 1 if measure.weight: pass # trim that last , readings['past']['diastolic'] = readings['past']['diastolic'][:-1] readings['past']['systolic'] = readings['past']['systolic'][:-1] readings['past']['pulse'] = readings['past']['pulse'][:-1] # simple moving average of existing data readings['past']['simple_moving_average'][ 'diastolic'], average_diastolic = _get_simple_moving_average( raw_readings['diastolic']) readings['past']['simple_moving_average'][ 'systolic'], average_systolic = _get_simple_moving_average( raw_readings['systolic']) readings['past']['simple_moving_average'][ 'pulse'], average_pulse = _get_simple_moving_average( raw_readings['pulse']) global FORECAST_ON_AVERAGE if FORECAST_ON_AVERAGE: readings['future']['diastolic'], future_diastolic = _get_forecast( average_diastolic) readings['future']['systolic'], future_systolic = _get_forecast( average_systolic) readings['future']['pulse'], future_pulse = _get_forecast( average_pulse) else: # populate the standard graphs and get the raw data to feed into thenext algorithm readings['future']['diastolic'], future_diastolic = _get_forecast( raw_readings['diastolic']) readings['future']['systolic'], future_systolic = _get_forecast( raw_readings['systolic']) readings['future']['pulse'], future_pulse = _get_forecast( raw_readings['pulse']) # simple moving average of future data readings['future']['simple_moving_average'][ 'diastolic'], average_diastolic = _get_simple_moving_average( future_diastolic) readings['future']['simple_moving_average'][ 'systolic'], average_systolic = _get_simple_moving_average( future_systolic) readings['future']['simple_moving_average'][ 'pulse'], average_pulse = _get_simple_moving_average(future_pulse) return readings
def retrieve_and_save_withings_measurements(withings_userid, device_id, start_ts, end_ts, measurement_type_id): logger.debug( "[medical-compliance] Query Withings API to retrieve measurement: %s" % (locals())) ## retrieve the DeviceUsage instance associated with the Withings `withings_userid` and `device_id` device_usage_data = store_utils.get_device_usage( store_utils.STORE_ENDPOINT_URI, device=device_id, access_info={"withings_userid": str(withings_userid)}) if not device_usage_data: logger.error( "[medical-compliance] Cannot find any user - device combination with access config for " "withings_userid : %s and device_id : %s" % (str(withings_userid), str(device_id))) return logger.debug( "[medical-compliance] DeviceUsage data for Withings weight measurement: %s" % str(device_usage_data)) ## get access_info, user and device information from device_usage object access_info = device_usage_data['access_info'] cami_user_id = device_usage_data['user_id'] credentials = WithingsCredentials( access_token=access_info['withings_oauth_token'], access_token_secret=access_info['withings_oauth_token_secret'], consumer_key=access_info['withings_consumer_key'], consumer_secret=access_info['withings_consumer_secret'], user_id=int(access_info['withings_userid'])) client = WithingsApi(credentials) req_params = { 'startdate': start_ts, 'enddate': end_ts, 'meastype': int(measurement_type_id) } response = client.request('measure', 'getmeas', req_params) logger.debug( "[medical-compliance] Got the following Withings response for user_id %s and req params %s: %s" % (withings_userid, req_params, response)) measures = WithingsMeasures(response) timezoneStr = response['timezone'] measurement_type = WithingsMeasurement.get_measure_type_by_id( int(measurement_type_id)) for m in measures: ## Store WithingsMeasurement for error inspection meas = WithingsMeasurement(withings_user_id=int(withings_userid), type=measurement_type_id, retrieval_type=m.attrib, measurement_unit=WithingsMeasurement. MEASUREMENT_SI_UNIT[measurement_type], timestamp=m.data['date'], timezone=timezoneStr, value=m.__getattribute__(measurement_type)) logger.debug( "[medical-compliance] Saving Withings measurement in cami DB: %s" % (meas)) meas.save() logger.debug( "[medical-compliance] Sending Withings weight measurement for processing: %s" % (meas)) process_weight_measurement.delay( cami_user_id, # end user id device_id, # device id "weight", # measurement_type meas.measurement_unit, meas.timestamp, meas.timezone, meas.value)
class TestWithingsApi(unittest.TestCase): def setUp(self): self.mock_api = True if self.mock_api: self.creds = WithingsCredentials() else: config = ConfigParser.ConfigParser() config.read('withings.conf') self.creds = WithingsCredentials( consumer_key=config.get('withings', 'consumer_key'), consumer_secret=config.get('withings', 'consumer_secret'), access_token=config.get('withings', 'access_token'), access_token_secret=config.get('withings', 'access_token_secret'), user_id=config.get('withings', 'user_id')) self.api = WithingsApi(self.creds) def test_attributes(self): """ Make sure the WithingsApi objects have the right attributes """ assert hasattr(WithingsApi, 'URL') creds = WithingsCredentials(user_id='FAKEID') api = WithingsApi(creds) assert hasattr(api, 'credentials') assert hasattr(api, 'oauth') assert hasattr(api, 'client') def test_attribute_defaults(self): """ Make sure WithingsApi object attributes have the correct defaults """ self.assertEqual(WithingsApi.URL, 'http://wbsapi.withings.net') creds = WithingsCredentials(user_id='FAKEID') api = WithingsApi(creds) self.assertEqual(api.credentials, creds) self.assertEqual(api.client.auth, api.oauth) self.assertEqual(api.client.params, {'userid': creds.user_id}) def test_request(self): """ Make sure the request method builds the proper URI and returns the request body as a python dict. """ self.mock_request({}) resp = self.api.request('fake_service', 'fake_action') Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/fake_service', params={'action': 'fake_action'}) self.assertEqual(resp, {}) def test_request_params(self): """ Check that the request method passes along extra params and works with different HTTP methods """ self.mock_request({}) resp = self.api.request('user', 'getbyuserid', params={'p2': 'p2'}, method='POST') Session.request.assert_called_once_with( 'POST', 'http://wbsapi.withings.net/user', params={'p2': 'p2', 'action': 'getbyuserid'}) self.assertEqual(resp, {}) def test_request_error(self): """ Check that requests raises an exception when there is an error """ self.mock_request('', status=1) self.assertRaises(Exception, self.api.request, ('user', 'getbyuserid')) def test_get_user(self): """ Check that the get_user method fetches the right URL """ self.mock_request({ 'users': [ {'id': 1111111, 'birthdate': 364305600, 'lastname': 'Baggins', 'ispublic': 255, 'firstname': 'Frodo', 'fatmethod': 131, 'gender': 0, 'shortname': 'FRO'} ] }) resp = self.api.get_user() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/user', params={'action': 'getbyuserid'}) self.assertEqual(type(resp), dict) assert 'users' in resp self.assertEqual(type(resp['users']), list) self.assertEqual(len(resp['users']), 1) self.assertEqual(resp['users'][0]['firstname'], 'Frodo') self.assertEqual(resp['users'][0]['lastname'], 'Baggins') def test_get_sleep(self): """ Check that get_sleep fetches the appropriate URL, the response looks correct, and the return value is a WithingsSleep object with the correct attributes """ body = { "series": [{ "startdate": 1387235398, "state": 0, "enddate": 1387235758 }, { "startdate": 1387243618, "state": 1, "enddate": 1387244518 }], "model": 16 } self.mock_request(body) resp = self.api.get_sleep() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/v2/sleep', params={'action': 'get'}) self.assertEqual(type(resp), WithingsSleep) self.assertEqual(resp.model, body['model']) self.assertEqual(type(resp.series), list) self.assertEqual(len(resp.series), 2) self.assertEqual(type(resp.series[0]), WithingsSleepSeries) self.assertEqual(resp.series[0].startdate.timestamp, body['series'][0]['startdate']) self.assertEqual(resp.series[0].enddate.timestamp, body['series'][0]['enddate']) self.assertEqual(resp.series[1].state, 1) def test_get_activities(self): """ Check that get_activities fetches the appropriate URL, the response looks correct, and the return value is a list of WithingsActivity objects """ body = { "date": "2013-04-10", "steps": 6523, "distance": 4600, "calories": 408.52, "elevation": 18.2, "soft": 5880, "moderate": 1080, "intense": 540, "timezone": "Europe/Berlin" } self.mock_request(body) resp = self.api.get_activities() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/v2/measure', params={'action': 'getactivity'}) self.assertEqual(type(resp), list) self.assertEqual(len(resp), 1) self.assertEqual(type(resp[0]), WithingsActivity) # No need to assert all attributes, that happens elsewhere self.assertEqual(resp[0].data, body) # Test multiple activities new_body = { 'activities': [ body, { "date": "2013-04-11", "steps": 223, "distance": 400, "calories": 108.52, "elevation": 1.2, "soft": 160, "moderate": 42, "intense": 21, "timezone": "Europe/Berlin" } ] } self.mock_request(new_body) resp = self.api.get_activities() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/v2/measure', params={'action': 'getactivity'}) self.assertEqual(type(resp), list) self.assertEqual(len(resp), 2) self.assertEqual(type(resp[0]), WithingsActivity) self.assertEqual(type(resp[1]), WithingsActivity) self.assertEqual(resp[0].data, new_body['activities'][0]) self.assertEqual(resp[1].data, new_body['activities'][1]) def test_get_measures(self): """ Check that get_measures fetches the appriate URL, the response looks correct, and the return value is a WithingsMeasures object """ body = { 'updatetime': 1409596058, 'measuregrps': [ {'attrib': 2, 'measures': [ {'unit': -1, 'type': 1, 'value': 860} ], 'date': 1409361740, 'category': 1, 'grpid': 111111111}, {'attrib': 2, 'measures': [ {'unit': -2, 'type': 4, 'value': 185} ], 'date': 1409361740, 'category': 1, 'grpid': 111111112} ] } self.mock_request(body) resp = self.api.get_measures() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/measure', params={'action': 'getmeas'}) self.assertEqual(type(resp), WithingsMeasures) self.assertEqual(len(resp), 2) self.assertEqual(type(resp[0]), WithingsMeasureGroup) self.assertEqual(resp[0].weight, 86.0) self.assertEqual(resp[1].height, 1.85) # Test limit=1 body['measuregrps'].pop() self.mock_request(body) resp = self.api.get_measures(limit=1) Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/measure', params={'action': 'getmeas', 'limit': 1}) self.assertEqual(len(resp), 1) self.assertEqual(resp[0].weight, 86.0) def test_subscribe(self): """ Check that subscribe fetches the right URL and returns the expected results """ # Unspecified appli self.mock_request(None) resp = self.api.subscribe('http://www.example.com/', 'fake_comment') Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'subscribe', 'comment': 'fake_comment', 'callbackurl': 'http://www.example.com/'}) self.assertEqual(resp, None) # appli=1 self.mock_request(None) resp = self.api.subscribe('http://www.example.com/', 'fake_comment', appli=1) Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'subscribe', 'appli': 1, 'comment': 'fake_comment', 'callbackurl': 'http://www.example.com/'}) self.assertEqual(resp, None) def test_unsubscribe(self): """ Check that unsubscribe fetches the right URL and returns the expected results """ # Unspecified appli self.mock_request(None) resp = self.api.unsubscribe('http://www.example.com/') Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'revoke', 'callbackurl': 'http://www.example.com/'}) self.assertEqual(resp, None) # appli=1 self.mock_request(None) resp = self.api.unsubscribe('http://www.example.com/', appli=1) Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'revoke', 'appli': 1, 'callbackurl': 'http://www.example.com/'}) self.assertEqual(resp, None) def test_is_subscribed(self): """ Check that is_subscribed fetches the right URL and returns the expected results """ url = 'http://wbsapi.withings.net/notify' params = { 'callbackurl': 'http://www.example.com/', 'action': 'get', 'appli': 1 } self.mock_request({'expires': 2147483647, 'comment': 'fake_comment'}) resp = self.api.is_subscribed('http://www.example.com/') Session.request.assert_called_once_with('GET', url, params=params) self.assertEquals(resp, True) # Not subscribed self.mock_request(None, status=343) resp = self.api.is_subscribed('http://www.example.com/') Session.request.assert_called_once_with('GET', url, params=params) self.assertEquals(resp, False) def test_list_subscriptions(self): """ Check that list_subscriptions fetches the right URL and returns the expected results """ self.mock_request({'profiles': [ {'comment': 'fake_comment', 'expires': 2147483647} ]}) resp = self.api.list_subscriptions() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'list', 'appli': 1}) self.assertEqual(type(resp), list) self.assertEqual(len(resp), 1) self.assertEqual(resp[0]['comment'], 'fake_comment') self.assertEqual(resp[0]['expires'], 2147483647) # No subscriptions self.mock_request({'profiles': []}) resp = self.api.list_subscriptions() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'list', 'appli': 1}) self.assertEqual(type(resp), list) self.assertEqual(len(resp), 0) def mock_request(self, body, status=0): if self.mock_api: json_content = {'status': status} if body != None: json_content['body'] = body response = MagicMock() response.content = json.dumps(json_content).encode('utf8') Session.request = MagicMock(return_value=response)
# copied from https://github.com/maximebf/python-withings/blob/master/README.md from withings import WithingsAuth, WithingsApi from settings import CONSUMER_KEY, CONSUMER_SECRET auth = WithingsAuth(CONSUMER_KEY, CONSUMER_SECRET) authorize_url = auth.get_authorize_url() print "Go to %s allow the app and copy your oauth_verifier" % authorize_url oauth_verifier = raw_input('Please enter your oauth_verifier: ') creds = auth.get_credentials(oauth_verifier) client = WithingsApi(creds) measures = client.get_measures(limit=1) print "Your last measured weight: %skg" % measures[0].weight
def retrieve_and_save_withings_measurements(withings_userid, device_id, start_ts, end_ts, measurement_type_id): logger.debug("[medical-compliance] Query Withings API to retrieve measurement: %s" % (locals())) ## retrieve the DeviceUsage instance associated with the Withings `withings_userid` and `device_id` device_usage_data = store_utils.get_device_usage(store_utils.STORE_ENDPOINT_URI, device = device_id, access_info = {"withings_userid" : str(withings_userid)}) if not device_usage_data: logger.error("[medical-compliance] Cannot find any user - device combination with access config for " "withings_userid : %s and device_id : %s" % (str(withings_userid), str(device_id))) return logger.debug("[medical-compliance] DeviceUsage data for Withings weight measurement: %s" % str(device_usage_data)) ## get access_info, user and device information from device_usage object access_info = device_usage_data['access_info'] cami_user_id = device_usage_data['user_id'] credentials = WithingsCredentials(access_token=access_info['withings_oauth_token'], access_token_secret=access_info['withings_oauth_token_secret'], consumer_key=access_info['withings_consumer_key'], consumer_secret=access_info['withings_consumer_secret'], user_id=int(access_info['withings_userid'])) client = WithingsApi(credentials) req_params = { 'startdate': start_ts, 'enddate': end_ts, 'meastype': int(measurement_type_id) } response = client.request('measure', 'getmeas', req_params) logger.debug( "[medical-compliance] Got the following Withings response for user_id %s and req params %s: %s" % (withings_userid, req_params, response) ) measures = WithingsMeasures(response) timezoneStr = response['timezone'] measurement_type = WithingsMeasurement.get_measure_type_by_id(int(measurement_type_id)) for m in measures: ## Store WithingsMeasurement for error inspection meas = WithingsMeasurement( withings_user_id = int(withings_userid), type=measurement_type_id, retrieval_type=m.attrib, measurement_unit=WithingsMeasurement.MEASUREMENT_SI_UNIT[measurement_type], timestamp=m.data['date'], timezone=timezoneStr, value=m.__getattribute__(measurement_type)) logger.debug("[medical-compliance] Saving Withings measurement in cami DB: %s" % (meas)) meas.save() logger.debug("[medical-compliance] Sending Withings weight measurement for processing: %s" % (meas)) process_weight_measurement.delay(cami_user_id, # end user id device_id, # device id "weight", # measurement_type meas.measurement_unit, meas.timestamp, meas.timezone, meas.value)
class TestWithingsApi(unittest.TestCase): def setUp(self): self.mock_api = True if self.mock_api: self.creds = WithingsCredentials() else: config = ConfigParser.ConfigParser() config.read('withings.conf') self.creds = WithingsCredentials( consumer_key=config.get('withings', 'consumer_key'), consumer_secret=config.get('withings', 'consumer_secret'), access_token=config.get('withings', 'access_token'), access_token_secret=config.get('withings', 'access_token_secret'), user_id=config.get('withings', 'user_id')) self.api = WithingsApi(self.creds) def test_attributes(self): """ Make sure the WithingsApi objects have the right attributes """ assert hasattr(WithingsApi, 'URL') creds = WithingsCredentials(user_id='FAKEID') api = WithingsApi(creds) assert hasattr(api, 'credentials') assert hasattr(api, 'oauth') assert hasattr(api, 'client') def test_attribute_defaults(self): """ Make sure WithingsApi object attributes have the correct defaults """ self.assertEqual(WithingsApi.URL, 'http://wbsapi.withings.net') creds = WithingsCredentials(user_id='FAKEID') api = WithingsApi(creds) self.assertEqual(api.credentials, creds) self.assertEqual(api.client.auth, api.oauth) self.assertEqual(api.client.params, {'userid': creds.user_id}) def test_request(self): """ Make sure the request method builds the proper URI and returns the request body as a python dict. """ self.mock_request({}) resp = self.api.request('fake_service', 'fake_action') Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/fake_service', params={'action': 'fake_action'}) self.assertEqual(resp, {}) def test_request_params(self): """ Check that the request method passes along extra params and works with different HTTP methods """ self.mock_request({}) resp = self.api.request('user', 'getbyuserid', params={'p2': 'p2'}, method='POST') Session.request.assert_called_once_with( 'POST', 'http://wbsapi.withings.net/user', params={'p2': 'p2', 'action': 'getbyuserid'}) self.assertEqual(resp, {}) def test_request_error(self): """ Check that requests raises an exception when there is an error """ self.mock_request('', status=1) self.assertRaises(Exception, self.api.request, ('user', 'getbyuserid')) def test_get_user(self): """ Check that the get_user method fetches the right URL """ self.mock_request({ 'users': [ {'id': 1111111, 'birthdate': 364305600, 'lastname': 'Baggins', 'ispublic': 255, 'firstname': 'Frodo', 'fatmethod': 131, 'gender': 0, 'shortname': 'FRO'} ] }) resp = self.api.get_user() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/user', params={'action': 'getbyuserid'}) self.assertEqual(type(resp), dict) assert 'users' in resp self.assertEqual(type(resp['users']), list) self.assertEqual(len(resp['users']), 1) self.assertEqual(resp['users'][0]['firstname'], 'Frodo') self.assertEqual(resp['users'][0]['lastname'], 'Baggins') def test_get_sleep(self): """ Check that get_sleep fetches the appropriate URL, the response looks correct, and the return value is a WithingsSleep object with the correct attributes """ body = { "series": [{ "startdate": 1387235398, "state": 0, "enddate": 1387235758 }, { "startdate": 1387243618, "state": 1, "enddate": 1387244518 }], "model": 16 } self.mock_request(body) resp = self.api.get_sleep() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/v2/sleep', params={'action': 'get'}) self.assertEqual(type(resp), WithingsSleep) self.assertEqual(resp.model, body['model']) self.assertEqual(type(resp.series), list) self.assertEqual(len(resp.series), 2) self.assertEqual(type(resp.series[0]), WithingsSleepSeries) self.assertEqual(time.mktime(resp.series[0].startdate.timetuple()), body['series'][0]['startdate']) self.assertEqual(time.mktime(resp.series[0].enddate.timetuple()), body['series'][0]['enddate']) self.assertEqual(resp.series[1].state, 1) def test_get_activities(self): """ Check that get_activities fetches the appropriate URL, the response looks correct, and the return value is a list of WithingsActivity objects """ body = { "date": "2013-04-10", "steps": 6523, "distance": 4600, "calories": 408.52, "elevation": 18.2, "soft": 5880, "moderate": 1080, "intense": 540, "timezone": "Europe/Berlin" } self.mock_request(body) resp = self.api.get_activities() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/v2/measure', params={'action': 'getactivity'}) self.assertEqual(type(resp), list) self.assertEqual(len(resp), 1) self.assertEqual(type(resp[0]), WithingsActivity) # No need to assert all attributes, that happens elsewhere self.assertEqual(resp[0].data, body) # Test multiple activities new_body = { 'activities': [ body, { "date": "2013-04-11", "steps": 223, "distance": 400, "calories": 108.52, "elevation": 1.2, "soft": 160, "moderate": 42, "intense": 21, "timezone": "Europe/Berlin" } ] } self.mock_request(new_body) resp = self.api.get_activities() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/v2/measure', params={'action': 'getactivity'}) self.assertEqual(type(resp), list) self.assertEqual(len(resp), 2) self.assertEqual(type(resp[0]), WithingsActivity) self.assertEqual(type(resp[1]), WithingsActivity) self.assertEqual(resp[0].data, new_body['activities'][0]) self.assertEqual(resp[1].data, new_body['activities'][1]) def test_get_measures(self): """ Check that get_measures fetches the appriate URL, the response looks correct, and the return value is a WithingsMeasures object """ body = { 'updatetime': 1409596058, 'measuregrps': [ {'attrib': 2, 'measures': [ {'unit': -1, 'type': 1, 'value': 860} ], 'date': 1409361740, 'category': 1, 'grpid': 111111111}, {'attrib': 2, 'measures': [ {'unit': -2, 'type': 4, 'value': 185} ], 'date': 1409361740, 'category': 1, 'grpid': 111111112} ] } self.mock_request(body) resp = self.api.get_measures() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/measure', params={'action': 'getmeas'}) self.assertEqual(type(resp), WithingsMeasures) self.assertEqual(len(resp), 2) self.assertEqual(type(resp[0]), WithingsMeasureGroup) self.assertEqual(resp[0].weight, 86.0) self.assertEqual(resp[1].height, 1.85) # Test limit=1 body['measuregrps'].pop() self.mock_request(body) resp = self.api.get_measures(limit=1) Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/measure', params={'action': 'getmeas', 'limit': 1}) self.assertEqual(len(resp), 1) self.assertEqual(resp[0].weight, 86.0) def test_subscribe(self): """ Check that subscribe fetches the right URL and returns the expected results """ self.mock_request(None) resp = self.api.subscribe('http://www.example.com/', 'fake_comment') Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'subscribe', 'appli': 1, 'comment': 'fake_comment', 'callbackurl': 'http://www.example.com/'}) self.assertEqual(resp, None) def test_unsubscribe(self): """ Check that unsubscribe fetches the right URL and returns the expected results """ self.mock_request(None) resp = self.api.unsubscribe('http://www.example.com/') Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'revoke', 'appli': 1, 'callbackurl': 'http://www.example.com/'}) self.assertEqual(resp, None) def test_is_subscribed(self): """ Check that is_subscribed fetches the right URL and returns the expected results """ url = 'http://wbsapi.withings.net/notify' params = { 'callbackurl': 'http://www.example.com/', 'action': 'get', 'appli': 1 } self.mock_request({'expires': 2147483647, 'comment': 'fake_comment'}) resp = self.api.is_subscribed('http://www.example.com/') Session.request.assert_called_once_with('GET', url, params=params) self.assertEquals(resp, True) # Not subscribed self.mock_request(None, status=343) resp = self.api.is_subscribed('http://www.example.com/') Session.request.assert_called_once_with('GET', url, params=params) self.assertEquals(resp, False) def test_list_subscriptions(self): """ Check that list_subscriptions fetches the right URL and returns the expected results """ self.mock_request({'profiles': [ {'comment': 'fake_comment', 'expires': 2147483647} ]}) resp = self.api.list_subscriptions() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'list', 'appli': 1}) self.assertEqual(type(resp), list) self.assertEqual(len(resp), 1) self.assertEqual(resp[0]['comment'], 'fake_comment') self.assertEqual(resp[0]['expires'], 2147483647) # No subscriptions self.mock_request({'profiles': []}) resp = self.api.list_subscriptions() Session.request.assert_called_once_with( 'GET', 'http://wbsapi.withings.net/notify', params={'action': 'list', 'appli': 1}) self.assertEqual(type(resp), list) self.assertEqual(len(resp), 0) def mock_request(self, body, status=0): if self.mock_api: json_content = {'status': status} if body != None: json_content['body'] = body response = MagicMock() response.content = json.dumps(json_content).encode('utf8') Session.request = MagicMock(return_value=response)
def _fetch_withings(): results = [] creds = WithingsCredentials() creds.access_token = ACCESS_TOKEN creds.access_token_secret = ACCESS_TOKEN_SECRET creds.user_id = USER_ID creds.consumer_key = CONSUMER_KEY creds.consumer_secret = CONSUMER_SECRET client = WithingsApi(creds) readings = { # predictions 'future' : { 'x': '', 'diastolic': '', 'systolic': '', 'pulse': '', 'simple_moving_average' : { 'diastolic': '', 'pulse': '', 'systolic': '', } }, # analysis of past readings 'past': { 'x': '', 'diastolic': '', 'systolic': '', 'pulse': '', 'simple_moving_average' : { 'diastolic': '', 'pulse': '', 'systolic': '', } } } measures = client.get_measures() # make sure the graph goes left to right measures.reverse() last_reading_date = measures[-1].date counter = 1 raw_readings = { 'systolic': [], 'diastolic': [], 'pulse': [], } for measure in measures: if measure.systolic_blood_pressure\ and measure.diastolic_blood_pressure: next_date = last_reading_date + timedelta(days=counter) # sort out date times readings['past']['x'] += '"' + measure.date.strftime('%Y-%m-%d %H:%M:%S') + '",' readings['future']['x'] += '"' + next_date.strftime('%Y-%m-%d %H:%M:%S') + '",' readings['past']['systolic'] += str(measure.systolic_blood_pressure) + ',' readings['past']['diastolic'] += str(measure.diastolic_blood_pressure) + ',' # keep ints for for sending to ALGORITHMIA # should really rename it... raw_readings['systolic'].append(measure.systolic_blood_pressure) raw_readings['diastolic'].append(measure.diastolic_blood_pressure) if measure.heart_pulse and measure.heart_pulse > 30: raw_readings['pulse'].append(measure.heart_pulse) readings['past']['pulse'] += str(measure.heart_pulse) + ',' counter += 1 if measure.weight: pass # trim that last , readings['past']['diastolic'] = readings['past']['diastolic'][:-1] readings['past']['systolic'] = readings['past']['systolic'][:-1] readings['past']['pulse'] = readings['past']['pulse'][:-1] # simple moving average of existing data readings['past']['simple_moving_average']['diastolic'], average_diastolic = _get_simple_moving_average(raw_readings['diastolic']) readings['past']['simple_moving_average']['systolic'], average_systolic = _get_simple_moving_average(raw_readings['systolic']) readings['past']['simple_moving_average']['pulse'], average_pulse = _get_simple_moving_average(raw_readings['pulse']) global FORECAST_ON_AVERAGE if FORECAST_ON_AVERAGE: readings['future']['diastolic'], future_diastolic = _get_forecast(average_diastolic) readings['future']['systolic'], future_systolic = _get_forecast(average_systolic) readings['future']['pulse'], future_pulse = _get_forecast(average_pulse) else: # populate the standard graphs and get the raw data to feed into thenext algorithm readings['future']['diastolic'], future_diastolic = _get_forecast(raw_readings['diastolic']) readings['future']['systolic'], future_systolic = _get_forecast(raw_readings['systolic']) readings['future']['pulse'], future_pulse = _get_forecast(raw_readings['pulse']) # simple moving average of future data readings['future']['simple_moving_average']['diastolic'], average_diastolic = _get_simple_moving_average(future_diastolic) readings['future']['simple_moving_average']['systolic'], average_systolic = _get_simple_moving_average(future_systolic) readings['future']['simple_moving_average']['pulse'], average_pulse = _get_simple_moving_average(future_pulse) return readings
class WITHINGclass: """ Get informations about withing """ # ------------------------------------------------------------------------------------------------- def __init__(self, log, api_id, api_secret, period, dataPath): try: """ Create a withing instance, allowing to use withing api """ self._log = log self.api_id = api_id self.api_secret = api_secret self.period = period self._sensors = [] self._dataPath = dataPath if not os.path.exists(self._dataPath) : self._log.info(u"Directory data not exist, trying create : %s" , self._dataPath) try : os.mkdir(self._dataPath) self._log.info(u"Withings data directory created : %s" %self._dataPath) except Exception as e: self._log.error(e.message) raise withingException ("Withings data directory not exist : %s" % self._dataPath) if not os.access(self._dataPath, os.W_OK) : self._log.error("User %s haven't write access on data directory : %s" %(user, self._dataPath)) raise withingException ("User %s haven't write access on data directory : %s" %(user, self._dataPath)) self.withing_config_file = os.path.join(os.path.dirname(__file__), '../data/withings.json') self.auth = WithingsAuth(self.api_id, self.api_secret) # self.open_token(self.auth) try: with open(self.withing_config_file, 'r') as withing_token_file: self._log.debug(u"Opening File") self.creds = pickle.load(withing_token_file) self._log.debug(u"Getting user") self.client = WithingsApi(self.creds) self.user = self.client.get_user() if self.user == None : self.auth = WithingsAuth(self.api_key, self.api_secret) self.authorize_url = self.auth.get_authorize_url() print("Go to %s allow the app and copy your oauth_verifier" %self.authorize_url) self.oauth_verifier = raw_input('Please enter your oauth_verifier: ') self.creds = auth.get_credentials(self.oauth_verifier) self.client = WithingsApi(creds) self.user = self.client.get_user() if user == None : self._log.error(u"Error getting user, from code") self._log.error(error) sys.exit("refreshing token failed from refresh_token") #TODO stop plugin else : self._log.warning(u"Token succesfully refresh with token_refresh from file") with open(self.withing_config_file, 'w') as withing_token_file: pickle.dump(self.creds, withing_token_file) except ValueError: self._log.error(u"error reading Withing.") return except ValueError: self._log.error(u"error reading Withing.") def open_token(self, auth): try: with open(withing_config_file, 'r') as withing_token_file: self._log.debug(u"Opening File") self.creds = pickle.load(withing_token_file) self._log.debug(u"Getting user") self.client = WithingsApi(self.creds) self.user = self.client.get_user() if user == None : self.auth = WithingsAuth(self.api_key, self.api_secret) self.authorize_url = self.auth.get_authorize_url() print("Go to %s allow the app and copy your oauth_verifier" %self.authorize_url) self.oauth_verifier = raw_input('Please enter your oauth_verifier: ') self.creds = auth.get_credentials(self.oauth_verifier) self.client = WithingsApi(creds) self.user = self.client.get_user() if user == None : self._log.error(u"Error getting user, from code") self._log.error(error) sys.exit("refreshing token failed from refresh_token") #TODO stop plugin else : self._log.warning(u"Token succesfully refresh with token_refresh from file") with open(withing_config_file, 'w') as withing_token_file: pickle.dump(self.creds, withing_token_file) except: self._log.error(u"Error with file saved or no file saved") self._log.error(u"Go to Advanced page to generate a new token file") #TODO stop plugin # ------------------------------------------------------------------------------------------------- def add_sensor(self, device_id, device_name, device_type, user_id): """ Add a sensor to sensors list. """ self._sensors.append({'device_id': device_id, 'device_name': device_name, 'device_type': device_type, 'user_id': user_id}) # ------------------------------------------------------------------------------------------------- def readWithingApi(self, userid): """ read the withing api for user information """ try: user = self.client.get_user() self._log.debug(user) return user except AttributeError: self._log.error(u"### USERid '%s', ERROR while reading client value." % userid) return "failed" # ------------------------------------------------------------------------------------------------- def readWithingMeasureApi(self, userid): """ read the withing measure api information """ try: measures = self.client.get_measures() return measures[0].data except AttributeError: self._log.error(u"### USERid '%s', ERROR while reading measure value." % userid) return "failed" # ------------------------------------------------------------------------------------------------- def loop_read_sensor(self, send, send_sensor, stop): """ """ while not stop.isSet(): for sensor in self._sensors: self._log.debug(sensor) if sensor['device_type'] == "withing.user": val = self.readWithingApi(sensor['user_id']) self._log.debug(val) if val != "failed": self._log.debug(val) self._log.debug(sensor) send(sensor['device_id'], {'firstname': val['users'][0]['firstname'], 'lastname': val['users'][0]['lastname'], 'id': val['users'][0]['id']}) elif sensor['device_type'] == "withing.measure": val = self.readWithingMeasureApi(sensor['user_id']) self._log.debug(val) if val != "failed": timestamp = val['date'] for measure in val['measures']: sensor_name = u'' self._log.debug(measure) if measure['type'] == 1: sensor_name = "weight" elif measure['type'] == 4: sensor_name = "height" elif measure['type'] == 5: sensor_name = "fat_free_mass" elif measure['type'] == 6: sensor_name = "fat_ratio" elif measure['type'] == 8: sensor_name = "fat_free_mass" elif measure['type'] == 9: sensor_name = "diastolic_blood_pressure" elif measure['type'] == 10: sensor_name = "systolic_blood_pressure" elif measure['type'] == 11: sensor_name = "heart_pulse" if sensor_name != u'': #timestamp = calendar.timegm(sensors.date.timetuple()) #timestamp=time.time() self._log.debug("Sending value to binary") value=float(measure['value'])/pow(10, abs(measure['unit'])) send_sensor(sensor['device_id'], sensor_name, value, timestamp) self._log.debug(u"=> '{0}' : wait for {1} seconds".format(sensor['device_name'], self.period)) stop.wait(self.period)