def test_measure_get_meas_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_measure_get_meas() withings_api.measure_get_meas( meastype=MeasureType.BONE_MASS, category=MeasureGetMeasGroupCategory.USER_OBJECTIVES, startdate=arrow.get("2019-01-01"), enddate=100000000, offset=12, lastupdate=datetime.date(2019, 1, 2), ) assert_url_query_equals( responses.calls[0].request.url, { "meastype": "88", "category": "2", "startdate": "1546300800", "enddate": "100000000", "offset": "12", "lastupdate": "1546387200", }, ) assert_url_path(responses.calls[0].request.url, "/measure") assert_url_query_equals(responses.calls[0].request.url, {"action": "getmeas"})
def test_notify_update_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_notify_update() withings_api.notify_update( callbackurl="http://localhost/callback2", appli=NotifyAppli.CIRCULATORY, new_callbackurl="http://localhost/callback2", new_appli=NotifyAppli.SLEEP, comment="comment3", ) assert_url_query_equals( responses.calls[0].request.url, { "callbackurl": "http://localhost/callback2", "appli": str(NotifyAppli.CIRCULATORY.real), "new_callbackurl": "http://localhost/callback2", "new_appli": str(NotifyAppli.SLEEP.real), "comment": "comment3", }, ) assert_url_path(responses.calls[0].request.url, "/notify") assert_url_query_equals(responses.calls[0].request.url, {"action": "update"})
def test_measure_get_activity_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_measure_get_activity() withings_api.measure_get_activity( startdateymd="2019-01-01", enddateymd=arrow.get("2019-01-02"), offset=2, data_fields=( GetActivityField.ACTIVE, GetActivityField.CALORIES, GetActivityField.ELEVATION, ), lastupdate=10000000, ) assert_url_query_equals( responses.calls[0].request.url, { "startdateymd": "2019-01-01", "enddateymd": "2019-01-02", "offset": "2", "data_fields": "active,calories,elevation", "lastupdate": "10000000", }, ) assert_url_path(responses.calls[0].request.url, "/v2/measure") assert_url_query_equals(responses.calls[0].request.url, {"action": "getactivity"})
def test_get_sleep_summary_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_sleep_get_summary() withings_api.sleep_get_summary( startdateymd="2019-01-01", enddateymd=arrow.get("2019-01-02"), data_fields=( GetSleepSummaryField.DEEP_SLEEP_DURATION, GetSleepSummaryField.HR_AVERAGE, ), lastupdate=10000000, ) assert_url_query_equals( responses.calls[0].request.url, { "startdateymd": "2019-01-01", "enddateymd": "2019-01-02", "data_fields": "deepsleepduration,hr_average", "lastupdate": "10000000", }, ) assert_url_path(responses.calls[0].request.url, "/v2/sleep") assert_url_query_equals(responses.calls[0].request.url, {"action": "getsummary"})
def connect(self) -> bool: withings_settings = Settings.objects.first() if withings_settings.token: logger.info(f'Attempting to load credentials from database:') self.api = WithingsApi(self._load_credentials(), refresh_cb=self._save_credentials) try: self.api.user_get_device() orig_access_token = self.api.get_credentials().access_token logger.info('Refreshing token...') self.api.refresh_token() assert orig_access_token \ != self.api.get_credentials().access_token except (MissingTokenError, AuthFailedException): withings_settings.token = None withings_settings.save() self.api = None logger.info('Credentials in file are expired.') raise CommWithings.NotAuthorized else: logger.info('No credentials file found.') raise CommWithings.NotAuthorized return self.api is not None
def test_notify_list_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_notify_list() withings_api.notify_list(appli=NotifyAppli.CIRCULATORY) assert_url_query_equals(responses.calls[0].request.url, {"appli": str(NotifyAppli.CIRCULATORY.real)}) assert_url_path(responses.calls[0].request.url, "/notify") assert_url_query_equals(responses.calls[0].request.url, {"action": "list"})
def withings_connected(): try: client = WithingsApi(credentials=load_credentials(), refresh_cb=save_withings_token) measures = client.measure_get_meas() app.server.logger.debug('Withings Connected') return True except BaseException as e: app.server.logger.error('Withings not connected') app.server.logger.error(e) return False
def test_heart_get_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_heart_get(HeartWearPosition.LEFT_ARM.real) withings_api.heart_get(signalid=1234567) assert_url_query_equals( responses.calls[0].request.url, {"signalid": "1234567"}, ) assert_url_path(responses.calls[0].request.url, "/v2/heart") assert_url_query_equals(responses.calls[0].request.url, {"action": "get"})
def test_refresh_token() -> None: """Test function.""" client_id = "my_client_id" consumer_secret = "my_consumer_secret" credentials = Credentials( access_token="my_access_token,_old", token_expiry=arrow.utcnow().timestamp - 1, token_type="Bearer", refresh_token="my_refresh_token_old", userid=_USERID, client_id=client_id, consumer_secret=consumer_secret, ) responses.add( method=responses.POST, url=re.compile("https://account.withings.com/oauth2/token.*"), status=200, json=_FETCH_TOKEN_RESPONSE_BODY, ) responses.add( method=responses.POST, url=re.compile("https://account.withings.com/oauth2/token.*"), status=200, json={ "access_token": "my_access_token_refreshed", "expires_in": 11, "token_type": "Bearer", "refresh_token": "my_refresh_token_refreshed", "userid": _USERID, }, ) responses_add_measure_get_activity() refresh_callback = MagicMock() api = WithingsApi(credentials, refresh_callback) api.measure_get_activity() refresh_callback.assert_called_with(api.get_credentials()) new_credentials = api.get_credentials() assert new_credentials.access_token == "my_access_token" assert new_credentials.refresh_token == "my_refresh_token" assert new_credentials.token_expiry > credentials.token_expiry refresh_callback.reset_mock() api.refresh_token() refresh_callback.assert_called_with(api.get_credentials()) new_credentials = api.get_credentials() assert new_credentials.access_token == "my_access_token_refreshed" assert new_credentials.refresh_token == "my_refresh_token_refreshed" assert new_credentials.token_expiry > credentials.token_expiry
def test_heart_list_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_heart_list() withings_api.heart_list(startdate="2020-01-01", enddate="2020-07-23", offset=1) assert_url_query_equals( responses.calls[0].request.url, {"startdate": "1577836800", "enddate": "1595462400", "offset": "1"}, ) assert_url_path(responses.calls[0].request.url, "/v2/heart") assert_url_query_equals(responses.calls[0].request.url, {"action": "list"})
def withings_connected(): token_dict = current_token_dict() try: if token_dict: creds = withings_creds(token_dict) client = WithingsApi(credentials=creds, refresh_cb=save_withings_token) measures = client.measure_get_meas() app.server.logger.debug('Withings Connected') return True except BaseException as e: app.server.logger.error('Withings not connected') app.server.logger.error(e) return False
def get_api_handle(request): need_creds = False token = request.cookies.get('token') credential_store = get_credential_store() creds = credential_store.get(token, None) if creds: update_creds = lambda creds: credential_store.update(token, creds) try: api = WithingsApi(creds, refresh_cb=update_creds) api.measure_get_meas() # check creds are still valid return api except (MissingTokenError, AuthFailedException) as e: print("auth error", e) return None
def test_sleep_get(withings_api: WithingsApi) -> None: """Test function.""" responses_add_sleep_get() assert withings_api.sleep_get() == SleepGetResponse( model=SleepModel.TRACKER, series=( SleepGetSerie( startdate=arrow.get(1387235398), state=SleepState.AWAKE, enddate=arrow.get(1387235758), hr=(), rr=(), snoring=(), ), SleepGetSerie( startdate=arrow.get(1387243618), state=SleepState.LIGHT, enddate=arrow.get(1387244518), hr=( SleepGetTimestampValue(arrow.get(1387243618), 12), SleepGetTimestampValue(arrow.get(1387243700), 34), ), rr=( SleepGetTimestampValue(arrow.get(1387243618), 45), SleepGetTimestampValue(arrow.get(1387243700), 67), ), snoring=( SleepGetTimestampValue(arrow.get(1387243618), 78), SleepGetTimestampValue(arrow.get(1387243700), 90), ), ), ), )
def test_heart_list(withings_api: WithingsApi) -> None: """Test function.""" responses_add_heart_list() assert withings_api.heart_list() == HeartListResponse( more=False, offset=0, series=( HeartListSerie( ecg=HeartListECG(signalid=9876543, afib=AfibClassification.NEGATIVE), bloodpressure=HeartBloodPressure(diastole=80, systole=120), heart_rate=78, timestamp=arrow.get(1594911107), model=HeartModel.BPM_CORE, ), HeartListSerie( ecg=HeartListECG(signalid=7654321, afib=AfibClassification.POSITIVE), bloodpressure=HeartBloodPressure(diastole=75, systole=125), heart_rate=87, timestamp=arrow.get(1594910902), model=HeartModel.BPM_CORE, ), # the Move ECG device does not take blood pressure HeartListSerie( ecg=HeartListECG(signalid=123987, afib=AfibClassification.INCONCLUSIVE), bloodpressure=None, heart_rate=77, timestamp=arrow.get(1594921551), model=HeartModel.MOVE_ECG, ), ), )
def test_notify_get_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_notify_get() withings_api.notify_get(callbackurl="http://localhost/callback2", appli=NotifyAppli.CIRCULATORY) assert_url_query_equals( responses.calls[0].request.url, { "callbackurl": "http://localhost/callback2", "appli": str(NotifyAppli.CIRCULATORY.real), }, ) assert_url_path(responses.calls[0].request.url, "/notify") assert_url_query_equals(responses.calls[0].request.url, {"action": "get"})
def test_notify_list(withings_api: WithingsApi) -> None: """Test function.""" responses_add_notify_list() assert withings_api.notify_list() == NotifyListResponse(profiles=( NotifyListProfile( appli=NotifyAppli.WEIGHT, callbackurl="http://localhost/callback", comment="fake_comment1", expires=None, ), NotifyListProfile( appli=NotifyAppli.CIRCULATORY, callbackurl="http://localhost/callback2", comment="fake_comment2", expires=arrow.get("2019-09-02"), ), NotifyListProfile( appli=NotifyAppli.UNKNOWN, callbackurl="http://localhost/callback2", comment="fake_comment2", expires=arrow.get("2019-09-02"), ), )) assert_url_path(responses.calls[0].request.url, "/notify") assert_url_query_equals(responses.calls[0].request.url, {"action": "list"})
def test_get_sleep_params(withings_api: WithingsApi) -> None: """Test function.""" responses_add_sleep_get(SleepModel.TRACKER) withings_api.sleep_get( startdate="2019-01-01", enddate=arrow.get("2019-01-02"), data_fields=(GetSleepField.HR, GetSleepField.HR), ) assert_url_query_equals( responses.calls[0].request.url, {"startdate": "1546300800", "enddate": "1546387200", "data_fields": "hr,hr"}, ) assert_url_path(responses.calls[0].request.url, "/v2/sleep") assert_url_query_equals(responses.calls[0].request.url, {"action": "get"})
def pull_withings_data(): # UTC dates will get sampled into daily if withings_connected(): client = WithingsApi(load_credentials(), refresh_cb=save_withings_token) df = pd.DataFrame( columns=['date_utc', 'weight', 'fat_ratio', 'hydration']) meas_result = client.measure_get_meas() for x in meas_result.measuregrps: date = pd.to_datetime(str(x.date)) weight = get_measure_value(x, with_measure_type=MeasureType.WEIGHT) fat_ratio = get_measure_value( x, with_measure_type=MeasureType.FAT_RATIO) hydration = get_measure_value( x, with_measure_type=MeasureType.HYDRATION) if weight and fat_ratio: df = df.append( { 'date_utc': date, 'weight': weight, 'fat_ratio': fat_ratio, 'hydration': hydration }, ignore_index=True) df = df.set_index( df['date_utc'].apply(lambda x: x.replace(tzinfo=None))) df = df[['weight', 'fat_ratio', 'hydration']] # Convert to lbs df['weight'] *= 2.20462 # Filter to days later than what is already in db withings_max_date = app.session.query(func.max( withings.date_utc)).first()[0] withings_max_date = datetime.strptime( '1991-08-30 00:00:00', '%Y-%m-%d %H:%M:%S' ) if not withings_max_date else withings_max_date app.session.remove() df = df[(df.index > withings_max_date) & (~np.isnan(df['weight'])) & (~np.isnan(df['fat_ratio']))] if len(df) > 0: app.server.logger.info('New withings measurements found!') df.to_sql('withings', engine, if_exists='append', index=True)
def test_heart_get(withings_api: WithingsApi) -> None: """Test function.""" responses_add_heart_get() assert withings_api.heart_get(123456) == HeartGetResponse( signal=tuple([-20, 0, 20]), sampling_frequency=500, wearposition=HeartWearPosition.LEFT_ARM, )
def withings_api_fixture() -> WithingsApi: """Provide withings api.""" withings_api = WithingsApi.__new__(WithingsApi) withings_api.user_get_device = MagicMock() withings_api.measure_get_meas = MagicMock() withings_api.sleep_get = MagicMock() withings_api.sleep_get_summary = MagicMock() return withings_api
def test_measure_get_meas(withings_api: WithingsApi) -> None: """Test function.""" responses_add_measure_get_meas() assert withings_api.measure_get_meas() == MeasureGetMeasResponse( more=False, offset=0, timezone=TIMEZONE0, updatetime=arrow.get(1409596058).to(TIMEZONE0), measuregrps=( MeasureGetMeasGroup( attrib=MeasureGetMeasGroupAttrib. MANUAL_USER_DURING_ACCOUNT_CREATION, category=MeasureGetMeasGroupCategory.REAL, created=arrow.get(1111111111).to(TIMEZONE0), date=arrow.get("2019-01-01").to(TIMEZONE0), deviceid="dev1", grpid=1, measures=( MeasureGetMeasMeasure(type=MeasureType.HEIGHT, unit=110, value=110), MeasureGetMeasMeasure(type=MeasureType.WEIGHT, unit=120, value=120), ), ), MeasureGetMeasGroup( attrib=MeasureGetMeasGroupAttrib. DEVICE_ENTRY_FOR_USER_AMBIGUOUS, category=MeasureGetMeasGroupCategory.USER_OBJECTIVES, created=arrow.get(2222222222).to(TIMEZONE0), date=arrow.get("2019-01-02").to(TIMEZONE0), deviceid="dev2", grpid=2, measures=( MeasureGetMeasMeasure(type=MeasureType.BODY_TEMPERATURE, unit=210, value=210), MeasureGetMeasMeasure(type=MeasureType.BONE_MASS, unit=220, value=220), ), ), MeasureGetMeasGroup( attrib=MeasureGetMeasGroupAttrib.UNKNOWN, category=MeasureGetMeasGroupCategory.UNKNOWN, created=arrow.get(2222222222).to(TIMEZONE0), date=arrow.get("2019-01-02").to(TIMEZONE0), deviceid="dev2", grpid=2, measures=(MeasureGetMeasMeasure(type=MeasureType.UNKNOWN, unit=230, value=230), ), ), ), )
def sleep(client: WithingsApi) -> str: return client.sleep_get_summary( data_fields=[ GetSleepSummaryField.REM_SLEEP_DURATION, GetSleepSummaryField.LIGHT_SLEEP_DURATION, GetSleepSummaryField.DEEP_SLEEP_DURATION, ], startdateymd=pendulum.yesterday(), enddateymd=pendulum.today(), )
def test_notify_get_unknown(withings_api: WithingsApi) -> None: """Test function.""" responses_add_notify_get(_UNKNOWN_INT) response = withings_api.notify_get(callbackurl="http://localhost/callback") assert response == NotifyGetResponse( callbackurl="http://localhost/callback", appli=NotifyAppli.UNKNOWN, comment="comment1", )
def test_notify_get(withings_api: WithingsApi) -> None: """Test function.""" responses_add_notify_get() response = withings_api.notify_get(callbackurl="http://localhost/callback") assert response == NotifyGetResponse( callbackurl="http://localhost/callback", appli=NotifyAppli.ACTIVITY, comment="comment1", )
def test_measure_get_activity(withings_api: WithingsApi) -> None: """Test function.""" responses_add_measure_get_activity() assert withings_api.measure_get_activity() == MeasureGetActivityResponse( more=False, offset=0, activities=( MeasureGetActivityActivity( date=arrow.get("2019-01-01").to(TIMEZONE0), timezone=TIMEZONE0, is_tracker=True, deviceid="dev1", brand=100, steps=101, distance=102.1, elevation=103.1, soft=104, moderate=105, intense=106, active=107, calories=108.1, totalcalories=109.1, hr_average=110, hr_min=111, hr_max=112, hr_zone_0=113, hr_zone_1=114, hr_zone_2=115, hr_zone_3=116, ), MeasureGetActivityActivity( date=arrow.get("2019-01-02").to(TIMEZONE1), timezone=TIMEZONE1, is_tracker=False, deviceid="dev2", brand=200, steps=201, distance=202.1, elevation=203.1, soft=204, moderate=205, intense=206, active=207, calories=208.1, totalcalories=209.1, hr_average=210, hr_min=211, hr_max=212, hr_zone_0=213, hr_zone_1=214, hr_zone_2=215, hr_zone_3=216, ), ), )
def test_sleep_get_summary(withings_api: WithingsApi) -> None: """Test function.""" responses_add_sleep_get_summary() assert withings_api.sleep_get_summary() == SleepGetSummaryResponse( more=False, offset=1, series=( GetSleepSummarySerie( date=arrow.get("2018-10-30").replace(tzinfo=TIMEZONE0), enddate=arrow.get(1540897020).replace(tzinfo=TIMEZONE0), model=SleepModel.TRACKER, modified=arrow.get(1540897246).replace(tzinfo=TIMEZONE0), startdate=arrow.get(1540857420).replace(tzinfo=TIMEZONE0), timezone=TIMEZONE0, data=GetSleepSummaryData( deepsleepduration=110, durationtosleep=111, durationtowakeup=112, lightsleepduration=113, wakeupcount=114, wakeupduration=116, remsleepduration=116, hr_average=117, hr_min=118, hr_max=119, rr_average=120, rr_min=121, rr_max=122, ), ), GetSleepSummarySerie( date=arrow.get("2018-10-31").replace(tzinfo=TIMEZONE1), enddate=arrow.get(1540973400).replace(tzinfo=TIMEZONE1), model=SleepModel.TRACKER, modified=arrow.get(1541020749).replace(tzinfo=TIMEZONE1), startdate=arrow.get(1540944960).replace(tzinfo=TIMEZONE1), timezone=TIMEZONE1, data=GetSleepSummaryData( deepsleepduration=210, durationtosleep=211, durationtowakeup=212, lightsleepduration=213, wakeupcount=214, wakeupduration=216, remsleepduration=216, hr_average=217, hr_min=218, hr_max=219, rr_average=220, rr_min=221, rr_max=222, ), ), ), )
def init_client(token: dict,) -> WithingsApi: credentials = Credentials( userid=token["id"], access_token=token["access_token"], refresh_token=token["refresh_token"], token_expiry=token["expires_at"], client_id=config.withings_client_id, consumer_secret=config.withings_consumer_secret, token_type="Bearer", ) client = WithingsApi(credentials, refresh_cb=Withings.persist_token) return client
def authorize(): if request.method == "POST": data = request.form urlcode = data["url"] redirected_uri_params = dict( parse.parse_qsl(parse.urlsplit(urlcode).query)) auth_code = redirected_uri_params["code"] text_file = open("credentials.txt", "w") n = text_file.write(auth_code) text_file.close() file = open("credentials.txt", "r") aa = file.readline() file.close() credentials = auth.get_credentials(auth_code) api = WithingsApi(credentials) meas_result = api.measure_get_meas() weight_or_none = get_measure_value( meas_result, with_measure_type=MeasureType.WEIGHT) fat = get_measure_value(meas_result, with_measure_type=MeasureType.FAT_MASS_WEIGHT) musle_mass = get_measure_value( meas_result, with_measure_type=MeasureType.MUSCLE_MASS) bone_mass = get_measure_value(meas_result, with_measure_type=MeasureType.BONE_MASS) body_water = get_measure_value(meas_result, with_measure_type=MeasureType.HYDRATION) heart_rate = get_measure_value( meas_result, with_measure_type=MeasureType.HEART_RATE) pluse_vave_velicity = get_measure_value( meas_result, with_measure_type=MeasureType.PULSE_WAVE_VELOCITY) return render_template("showdata.html", w=weight_or_none, f=fat, musle_mass=musle_mass, bone_mass=bone_mass, body_water=body_water, heart_rate=heart_rate, pvv=pluse_vave_velicity) return "credentials"
def steps(client: WithingsApi) -> t.Optional[int]: result = client.measure_get_activity( data_fields=[GetActivityField.STEPS], startdateymd=pendulum.yesterday(), enddateymd=pendulum.today(), ) yesterday = next( ( act for act in result.activities if act.date.day == pendulum.yesterday().day ), None, ) return yesterday.steps if yesterday else None
def withings_api_instance() -> WithingsApi: """Test function.""" client_id: Final = "my_client_id" consumer_secret: Final = "my_consumer_secret" credentials: Final = Credentials( access_token="my_access_token", token_expiry=arrow.utcnow().timestamp + 10000, token_type="Bearer", refresh_token="my_refresh_token", userid=_USERID, client_id=client_id, consumer_secret=consumer_secret, ) return WithingsApi(credentials)