def test_authorize() -> None: """Test function.""" client_id = "fake_client_id" consumer_secret = "fake_consumer_secret" callback_uri = "http://127.0.0.1:8080" arrow.utcnow = MagicMock(return_value=arrow.get(100000000)) responses.add( method=responses.POST, url="https://account.withings.com/oauth2/token", json=_FETCH_TOKEN_RESPONSE_BODY, status=200, ) auth = WithingsAuth( client_id, consumer_secret, callback_uri=callback_uri, scope=(AuthScope.USER_METRICS, AuthScope.USER_ACTIVITY), ) url = auth.get_authorize_url() assert url.startswith( "https://account.withings.com/oauth2_user/authorize2") assert_url_query_equals( url, { "response_type": "code", "client_id": "fake_client_id", "redirect_uri": "http://127.0.0.1:8080", "scope": "user.metrics,user.activity", }, ) params = dict(parse.parse_qsl(parse.urlsplit(url).query)) assert "scope" in params assert len(params["scope"]) > 0 creds = auth.get_credentials("FAKE_CODE") assert creds == Credentials( access_token="my_access_token", token_expiry=100000011, token_type="Bearer", refresh_token="my_refresh_token", userid=_USERID, client_id=client_id, consumer_secret=consumer_secret, )
def index(self, reload=None, state=None, code=None, error=None): """ Build index.html for cherrypy Render the template and return the html file to be delivered to the browser :return: contents of the template after beeing rendered """ if self._auth is None: self._auth = WithingsAuth( client_id=self.plugin.get_client_id(), consumer_secret=self.plugin.get_consumer_secret(), callback_uri=self.plugin.get_callback_url(), scope=(AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS,) ) if not reload and code: self.logger.debug("Got code as callback: {}".format(self.plugin.get_fullname(), code)) credentials = None try: credentials = self._auth.get_credentials(code) except Exception as e: self.logger.error( "An error occurred, perhaps code parameter is invalid or too old? Message: {}".format( str(e))) if credentials is not None: self._creds = credentials self.logger.debug( "New credentials are: access_token {}, token_expiry {}, token_type {}, refresh_token {}". format(self.plugin.get_fullname(), self._creds.access_token, self._creds.token_expiry, self._creds.token_type, self._creds.refresh_token)) self.plugin.get_item('access_token')(self._creds.access_token) self.plugin.get_item('token_expiry')(self._creds.token_expiry) self.plugin.get_item('token_type')(self._creds.token_type) self.plugin.get_item('refresh_token')(self._creds.refresh_token) self.plugin._client = None tmpl = self.tplenv.get_template('index.html') return tmpl.render(plugin_shortname=self.plugin.get_shortname(), plugin_version=self.plugin.get_version(), interface=None, item_count=len(self.plugin.get_items()), plugin_info=self.plugin.get_info(), tabcount=2, callback_url=self.plugin.get_callback_url(), tab1title="Withings Health Items (%s)" % len(self.plugin.get_items()), tab2title="OAuth2 Data", authorize_url=self._auth.get_authorize_url(), p=self.plugin, token_expiry=datetime.datetime.fromtimestamp(self.plugin.get_item( 'token_expiry')(), tz=self.plugin.shtime.tzinfo()), now=self.plugin.shtime.now(), code=code, state=state, reload=reload, language=self.plugin.get_sh().get_defaultlanguage())
def __init__(self): self.api = None self.scale = Settings.objects.first().default_scale self.auth = WithingsAuth( client_id=settings.WITHINGS_API_CLIENT_ID, consumer_secret=settings.WITHINGS_API_CONSUMER_SECRET, callback_uri=settings.WITHINGS_API_CALLBACK_URI, scope=( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, ), )
def test_get_authorize_url() -> None: """Test function.""" auth1: Final = WithingsAuth( client_id="fake_client_id", consumer_secret="fake_consumer_secret", callback_uri="http://localhost", ) auth2: Final = WithingsAuth( client_id="fake_client_id", consumer_secret="fake_consumer_secret", callback_uri="http://localhost", mode="MY_MODE", ) assert "&mode=MY_MODE" not in auth1.get_authorize_url() assert "&mode=MY_MODE" in auth2.get_authorize_url()
def __init__(self): scope = ( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, ) client = WithingsAuth( client_id=config.withings_client_id, consumer_secret=config.withings_consumer_secret, callback_uri=config.withings_redirect_uri, scope=scope, ) self.client: WithingsAuth = client
def auth_client(): scope = ( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, ) client = WithingsAuth( client_id=config.withings_client_id, consumer_secret=config.withings_consumer_secret, callback_uri=config.withings_redirect_uri, scope=scope, ) return client
def test_authorize() -> None: """Test function.""" client_id: Final = "fake_client_id" consumer_secret: Final = "fake_consumer_secret" callback_uri: Final = "http://127.0.0.1:8080" responses.add( method=responses.POST, url="https://account.withings.com/oauth2/token", json=_FETCH_TOKEN_RESPONSE_BODY, status=200, ) auth: Final = WithingsAuth( client_id, consumer_secret, callback_uri=callback_uri, scope=(AuthScope.USER_METRICS, AuthScope.USER_ACTIVITY), ) url: Final = auth.get_authorize_url() assert url.startswith( "https://account.withings.com/oauth2_user/authorize2") assert_url_query_equals( url, { "response_type": "code", "client_id": "fake_client_id", "redirect_uri": "http://127.0.0.1:8080", "scope": "user.metrics,user.activity", }, ) params: Final = dict(parse.parse_qsl(parse.urlsplit(url).query)) assert "scope" in params assert len(params["scope"]) > 0 creds: Final = auth.get_credentials("FAKE_CODE") assert creds.access_token == "my_access_token" assert creds.token_type == "Bearer" assert creds.refresh_token == "my_refresh_token" assert creds.userid == _USERID assert creds.client_id == client_id assert creds.consumer_secret == consumer_secret assert creds.expires_in == 11 assert creds.token_expiry == arrow.utcnow().int_timestamp + 11
def main() -> None: """Run main function.""" parser: Final = argparse.ArgumentParser( description="Process some integers.") parser.add_argument( "--client-id", dest="client_id", help="Client id provided by withings.", required=True, ) parser.add_argument( "--consumer-secret", dest="consumer_secret", help="Consumer secret provided by withings.", required=True, ) parser.add_argument( "--callback-uri", dest="callback_uri", help="Callback URI configured for withings application.", required=True, ) parser.add_argument( "--live-data", dest="live_data", action="store_true", help= "Should we run against live data? (Removal of .credentials file is required before running)", ) args: Final = parser.parse_args() if path.isfile(CREDENTIALS_FILE): print("Attempting to load credentials from:", CREDENTIALS_FILE) api = WithingsApi(load_credentials(), refresh_cb=save_credentials) try: api.user_get_device() except MissingTokenError: os.remove(CREDENTIALS_FILE) print( "Credentials in file are expired. Re-starting auth procedure..." ) if not path.isfile(CREDENTIALS_FILE): print("Attempting to get credentials...") auth: Final = WithingsAuth( client_id=args.client_id, consumer_secret=args.consumer_secret, callback_uri=args.callback_uri, mode=None if args.live_data else "demo", scope=( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, ), ) authorize_url: Final = auth.get_authorize_url() print("Goto this URL in your browser and authorize:", authorize_url) print("Once you are redirected, copy and paste the whole url" "(including code) here.") redirected_uri: Final = input("Provide the entire redirect uri: ") redirected_uri_params: Final = dict( parse.parse_qsl(parse.urlsplit(redirected_uri).query)) auth_code: Final = redirected_uri_params["code"] print("Getting credentials with auth code", auth_code) save_credentials(auth.get_credentials(auth_code)) api = WithingsApi(load_credentials(), refresh_cb=save_credentials) # This only tests the refresh token. Token refresh is handled automatically by the api so you should not # need to use this method so long as your code regularly (every 3 hours or so) requests data from withings. orig_access_token = api.get_credentials().access_token print("Refreshing token...") api.refresh_token() assert orig_access_token != api.get_credentials().access_token print("Getting devices...") assert api.user_get_device() is not None print("Getting measures...") assert (api.measure_get_meas(startdate=arrow.utcnow().shift(days=-21), enddate=arrow.utcnow()) is not None) print("Getting activity...") assert (api.measure_get_activity( startdateymd=arrow.utcnow().shift(days=-21), enddateymd=arrow.utcnow()) is not None) print("Getting sleep...") assert (api.sleep_get( data_fields=GetSleepField, startdate=arrow.utcnow().shift(days=-2), enddate=arrow.utcnow(), ) is not None) print("Getting sleep summary...") assert (api.sleep_get_summary( data_fields=GetSleepSummaryField, startdateymd=arrow.utcnow().shift(days=-2), enddateymd=arrow.utcnow(), ) is not None) print("Getting subscriptions...") assert api.notify_list() is not None print("Successfully finished.")
def main() -> None: """Run main function.""" parser = argparse.ArgumentParser(description="Process some integers.") parser.add_argument( "--client-id", dest="client_id", help="Client id provided by withings.", required=True, ) parser.add_argument( "--consumer-secret", dest="consumer_secret", help="Consumer secret provided by withings.", required=True, ) parser.add_argument( "--callback-uri", dest="callback_uri", help="Callback URI configured for withings application.", required=True, ) args = parser.parse_args() if path.isfile(CREDENTIALS_FILE): print("Using credentials saved in:", CREDENTIALS_FILE) with open(CREDENTIALS_FILE, "rb") as file_handle: credentials = pickle.load(file_handle) else: print("Attempting to get credentials...") auth = WithingsAuth( client_id=args.client_id, consumer_secret=args.consumer_secret, callback_uri=args.callback_uri, mode="demo", scope=( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, ), ) authorize_url = auth.get_authorize_url() print("Goto this URL in your browser and authorize:", authorize_url) print("Once you are redirected, copy and paste the whole url" "(including code) here.") redirected_uri = input("Provide the entire redirect uri: ") redirected_uri_params = dict( parse.parse_qsl(parse.urlsplit(redirected_uri).query)) auth_code = redirected_uri_params["code"] print("Getting credentials with auth code", auth_code) credentials = auth.get_credentials(auth_code) with open(CREDENTIALS_FILE, "wb") as file_handle: pickle.dump(credentials, file_handle) refresh_cb = MagicMock() api = WithingsApi(credentials, refresh_cb=refresh_cb) print("Getting devices...") assert api.measure_get_meas() is not None print("Refreshing token...") refresh_cb.reset_mock() api.refresh_token() refresh_cb.assert_called_once() print("Getting measures...") assert (api.measure_get_meas(startdate=arrow.utcnow().shift(days=-21), enddate=arrow.utcnow()) is not None) print("Getting activity...") assert (api.measure_get_activity( startdateymd=arrow.utcnow().shift(days=-21), enddateymd=arrow.utcnow()) is not None) print("Getting sleep...") assert (api.sleep_get(startdate=arrow.utcnow().shift(days=-2), enddate=arrow.utcnow()) is not None) print("Getting sleep summary...") assert (api.sleep_get_summary(startdateymd=arrow.utcnow().shift(days=-2), enddateymd=arrow.utcnow()) is not None) print("Getting subscriptions...") assert api.notify_list() is not None print("Successfully finished.")
from withings_api import WithingsAuth, WithingsApi, AuthScope from withings_api.common import get_measure_value, MeasureType, NotifyAppli from enum import Enum, IntEnum auth = WithingsAuth( client_id='bd7fa1e13e826e59cf239e309f53b765b706e90bdcd4e7f87f9b6378481605af', consumer_secret='1739c24a9971681ea82abc4b10a44882584eeaf6334eaebdbfd54df7396270fc', callback_uri='https://asia-southeast2-eric-han.cloudfunctions.net/nus-withings-bridge', scope=( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, ) ) authorize_url = auth.get_authorize_url() # Have the user goto authorize_url and authorize the app. They will be redirected back to your redirect_uri. print(authorize_url) code = input("Enter Code: ") credentials = auth.get_credentials(code) # Now you are ready to make calls for data. api = WithingsApi(credentials) ll = api.notify_list() print(ll)
def main() -> None: """Run main function.""" parser: Final = argparse.ArgumentParser( description="Process some integers.") parser.add_argument( "--client-id", dest="client_id", help="Client id provided by withings.", required=True, ) parser.add_argument( "--consumer-secret", dest="consumer_secret", help="Consumer secret provided by withings.", required=True, ) parser.add_argument( "--callback-uri", dest="callback_uri", help="Callback URI configured for withings application.", required=True, ) parser.add_argument( "--live-data", dest="live_data", action="store_true", help= "Should we run against live data? (Removal of .credentials file is required before running)", ) args: Final = parser.parse_args() if not path.isfile(CREDENTIALS_FILE): print("Attempting to get credentials...") auth: Final = WithingsAuth( client_id=args.client_id, consumer_secret=args.consumer_secret, callback_uri=args.callback_uri, mode=None if args.live_data else "demo", scope=( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, ), ) authorize_url: Final = auth.get_authorize_url() print("Goto this URL in your browser and authorize:", authorize_url) print("Once you are redirected, copy and paste the whole url" "(including code) here.") redirected_uri: Final = input("Provide the entire redirect uri: ") redirected_uri_params: Final = dict( parse.parse_qsl(parse.urlsplit(redirected_uri).query)) auth_code: Final = redirected_uri_params["code"] print("Getting credentials with auth code", auth_code) save_credentials(auth.get_credentials(auth_code)) api: Final = WithingsApi(load_credentials(), refresh_cb=save_credentials) print("Getting devices...") assert api.user_get_device() is not None print("Getting measures...") assert (api.measure_get_meas(startdate=arrow.utcnow().shift(days=-21), enddate=arrow.utcnow()) is not None) print("Getting activity...") assert (api.measure_get_activity( startdateymd=arrow.utcnow().shift(days=-21), enddateymd=arrow.utcnow()) is not None) print("Getting sleep...") assert (api.sleep_get(startdate=arrow.utcnow().shift(days=-2), enddate=arrow.utcnow()) is not None) print("Getting sleep summary...") assert (api.sleep_get_summary(startdateymd=arrow.utcnow().shift(days=-2), enddateymd=arrow.utcnow()) is not None) print("Getting subscriptions...") assert api.notify_list() is not None print("Successfully finished.")
class CommWithings: class NotAuthorized(Exception): pass def __init__(self): self.api = None self.scale = Settings.objects.first().default_scale self.auth = WithingsAuth( client_id=settings.WITHINGS_API_CLIENT_ID, consumer_secret=settings.WITHINGS_API_CONSUMER_SECRET, callback_uri=settings.WITHINGS_API_CALLBACK_URI, scope=( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, ), ) 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 authorize_request(self): logger.info('Attempting to get credentials...') authorize_url = self.get_authorize_url() subject = 'Withings API authorization request' html_message = ( f'<html><body><div>' f'The application needs to be authorized with Withings API.</div>' f'<div>Go to this <a href="{authorize_url}">link</a>' f' and authorize.</div>' f'</body></html>' ) plain_message = strip_tags(html_message) mail_admins(subject, plain_message, html_message=html_message) def get_authorize_url(self): return self.auth.get_authorize_url() def save_credentials(self, auth_code): self._save_credentials(self.auth.get_credentials(auth_code)) @classmethod def _save_credentials(cls, credentials: CredentialsType) -> None: """Save credentials to a file.""" logger.info(f'Saving credentials in database') cls._log_credentials(credentials) withings_settings = Settings.objects.first() withings_settings.token = pickle.dumps(credentials) withings_settings.save() @classmethod def _load_credentials(cls) -> CredentialsType: """Load credentials from a file.""" logger.info(f'Using credentials from database') credentials = cast(CredentialsType, pickle.loads(Settings.objects.first().token)) cls._log_credentials(credentials) @staticmethod def _log_credentials(credentials: CredentialsType): logger.info( f'Credential properties: ' f' Token: {credentials.access_token}' f' Refresh Token: {credentials.refresh_token}' f' Client ID: {credentials.client_id}' f' Expiry: {credentials.token_expiry}' ) def import_data(self, date_from, date_to): assert self.api is not None assert self.api.user_get_device() is not None meas_result = self.api.measure_get_meas( startdate=date_from, enddate=date_to, # lastupdate=1601478000, lastupdate=None, category=MeasureGetMeasGroupCategory.REAL) api_to_django = { MeasureType.WEIGHT: 'weight', MeasureType.FAT_RATIO: 'fat_pct', } dict_meas = [ { **{ api_to_django[measure.type]: float( measure.value * pow(10, measure.unit)) for measure in grp.measures }, 'measure_date': grp.date.datetime } for grp in query_measure_groups( meas_result, with_measure_type=cast(Tuple, api_to_django.keys()) ) ] logger.debug(f'Values from Withings API: {dict_meas}') db_values = list(Measurement.objects.filter( measure_date__range=[date_from.datetime, date_to.datetime] ).values('measure_date', *api_to_django.values())) logger.debug(f'Values from Django DB: {db_values}') results = {'add': 0, 'del': 0} for i in dict_meas + db_values: if i not in db_values: logger.info(f'Add to DB: {i}') Measurement.objects.create( scale=self.scale, **i ) results['add'] += 1 if i not in dict_meas: logger.info(f'Delete from DB: {i}') Measurement.objects.get(**i).delete() results['del'] += 1 logger.info(f'Import results: {results["add"]} addition,' f' {results["del"]} deletion')
from urllib import parse import requests from flask import Flask, request, render_template, jsonify import json app = Flask(__name__) clientid = "7f5486f78731e761c99878e8e37d540f5868a99c12a16089be227b372a366c36" consumer_secrete = "efa34f8ae8542f4de35ce0a4b7108421e3d32a6bd9ae8eecc0d8efcb6ff29d7a" calbackuri = "https://vocobot.herokuapp.com/" auth = WithingsAuth(client_id=clientid, consumer_secret=consumer_secrete, callback_uri=calbackuri, scope=( AuthScope.USER_ACTIVITY, AuthScope.USER_METRICS, AuthScope.USER_INFO, AuthScope.USER_SLEEP_EVENTS, )) @app.route("/", methods=["GET", "POST"]) def index(): authorize_url = auth.get_authorize_url() return render_template("index.html", url=authorize_url) @app.route("/authorize", methods=["POST", "GET"]) def authorize(): if request.method == "POST":