
    h]K                       d Z ddlZddlZddlZddlZddlmZ ddlmZmZm	Z	 ddl
mZmZ ddlmZ ddlmZ ddlZddlZddlmZmZ dd	lmZ d
dlmZ  ej2                  e      ZdZdZdZdZddhZ d&de!de!de!fdZ"	 d'de#e$z  de!de#e$z  fdZ%d'de#de!de#fdZ&d'de#de!de#fdZ'dede!fdZ( G d d      Z) G d de*      Z+ G d  d!e*      Z, G d" d#e*      Z- G d$ d%e*      Z.y)(z(Python 3 API wrapper for Garmin Connect.    N)Callable)datedatetimetimezone)Enumauto)Path)Any)GarthExceptionGarthHTTPError)	HTTPError   )FitEncoderWeight  i'  z^\d{4}-\d{2}-\d{2}$z%Y-%m-%dkglbsdate_str
param_namereturnc                 2   t        | t              st        | d      | j                         } t	        j
                  t        |       st        | d|        	 t        j                  | t               | S # t        $ r}t        d| d|       |d}~ww xY w)z'Validate date string format YYYY-MM-DD.z must be a stringz& must be in format 'YYYY-MM-DD', got: zinvalid : N)

isinstancestr
ValueErrorstripre	fullmatchDATE_FORMAT_REGEXr   strptimeDATE_FORMAT_STR)r   r   es      Q/var/www/html/python/myenv/lib/python3.12/site-packages/garminconnect/__init__.py_validate_date_formatr#      s    h$J<'89:: ~~H<<)84l@
K
 	
>(O4 O  >8J<r!56A=>s   A6 6	B?BBvaluec                     t        | t        j                        st        | d      t        | t              rt        | d      | dk  rt        | d|        | S )z#Validate that a number is positive.z must be a numberz must be a number, not boolr   z must be positive, got: )r   numbersRealr   boolr$   r   s     r"   _validate_positive_numberr*   5   se     eW\\*J<'89::%J<'BCDDzJ<'?wGHHL    c                     t        | t              rt        | t              rt        | d      | dk  rt        | d|        | S )z0Validate that a value is a non-negative integer. must be an integerr   z must be non-negative, got: r   intr(   r   r)   s     r"   _validate_non_negative_integerr0   E   sK    eS!Zt%<J<':;<<qyJ<'CE7KLLLr+   c                     t        | t              rt        | t              rt        | d      | dk  rt        | d|        | S )z,Validate that a value is a positive integer.r-   r   z" must be a positive integer, got: r.   r)   s     r"   _validate_positive_integerr2   P   sK    eS!Zt%<J<':;<<zJ<'I%QRRLr+   dtc                 J    | j                  d       j                  d      d d S )Ntzinfoz%Y-%m-%dT%H:%M:%S.%f)replacestrftime)r3   s    r"   _fmt_tsr:   Y   s&    ::T:"++,BCCRHHr+   c                   ,   e Zd ZdZ	 	 	 	 	 ddedz  dedz  dedeg ef   dz  deddfd	Zd
ededefdZ	d
ededefdZ
ddedz  deedz  edz  f   fdZdeeef   dedeeef   fdZdedz  fdZdedz  fdZdedeeef   fdZdedeeef   fdZdedeeeef      fdZdedeeef   fdZdededeeeef      fdZdedeeef   fdZdedeeef   fdZ	 dded edz  deeef   fd!Z	 	 	 	 	 	 	 	 	 	 	 dd"edz  d#ed$edz  d%edz  d&edz  d'edz  d(edz  d)edz  d*edz  d+edz  d,edz  d-edz  d.edz  deeef   fd/Z	 dd#eez  d0ed"edeeef   fd1Z	 	 	 dd#eez  d0ed2ed3edeeef   f
d4Zded edeeef   fd5Zdedeeef   fd6Z d7ededefd8Z!dded9ededz  fd:Z"	 dded edz  deeeef      fd;Z#dedeeeef      fd<Z$	 	 dd=ed>ed?ed"ed@edeeef   fdAZ%	 dded edz  deeef   fdBZ&dCededeeef   fdDZ'dedeeef   fdEZ(dFdddGdHdIedJee)z  dz  dKee)z  dz  dLedeeef   f
dMZ*	 	 ddNed"edz  dedz  deeef   fdOZ+dedeeef   fdPZ,dedeeef   fdQZ-dedeeef   fdRZ.dedeeef   fdSZ/dedeeef   fdTZ0dedeeef   fdUZ1deeef   fdVZ2deeeef      fdWZ3deeeef      fdXZ4deeeef      fdYZ5dedZedeeef   fd[Z6dedZedeeef   fd\Z7dedZedeeef   fd]Z8dedZedeeef   fd^Z9dedZedeeef   fd_Z:dedeeef   fd`Z;dedeeef   fdaZ<dedeeef   fdbZ=dedeeef   dz  fdcZ>dedeeef   fddZ?	 dded edz  deeef   fdeZ@	 	 	 ddedz  d edz  dfedz  deeef   fdgZAdedeeef   fdhZBdedeeef   fdiZC	 dded edz  deeef   fdjZDdeeeef      fdkZEdledeeef   fdmZFdeeef   fdnZG	 ddleded edz  deeeef      fdoZHdee   fdpZIdeeef   fdqZJ	 	 	 ddedZedredz  deeef   ee   z  fdsZKdtedeeef   fduZLdvedwedefdxZMdvedyedzed{edef
d|ZNd}eeef   defd~ZOdededzededededefdZPdeeef   dz  fdZQdedefdZRdvedefdZS	 	 	 dded edz  dredz  dedz  deeeef      f
dZT	 	 dded edededeeef   f
dZUdeeef   fdZV	 ddededZedeeeef      fdZWdedeeef   fdZXdedeeef   fdZYdedeeef   fdZZ	 ddedededefdZ[ G d de\      Z] G d de\      Z^e]j                  fdvede]de`fdZadvedeeef   fdZbdvedeeef   fdZcdvedeeef   fdZddvedeeef   fdZedvedeeef   fdZfdvedeeef   fdZg	 ddvedededeeef   fdZhdveez  deeef   fdZidveez  deeef   fdZj	 ddedZedeeeef      fdZkdeeef   fdZldeeef   fdZmdedeeef   fdZnddedZedeeef   fdZodeez  deeef   fdZpdeez  de`fdZqdeeef   ee   z  ez  deeef   fdZrdtedeeef   fdZsded edeeef   fdZtdeeef   fdZudeeef   deeef   fdZvddZwy)Garminz,Class for fetching data from Garmin Connect.Nemailpasswordis_cn
prompt_mfareturn_on_mfar   c                 $   |t        |t              st        d      |t        |t              st        d      t        |t              st        d      t        |t              st        d      || _        || _        || _        || _        || _        d| _	        d| _
        d| _        d	| _        d
| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _         d| _!        d| _"        d | _#        d!| _$        d"| _%        d#| _&        d$| _'        d%| _(        d&| _)        d'| _*        d(| _+        d)| _,        d*| _-        d+| _.        d,| _/        d-| _0        d.| _1        d/| _2        d0| _3        d1| _4        d2| _5        d3| _6        d4| _7        d5| _8        d6| _9        d7| _:        d8| _;        d9| _<        d:| _=        d;| _>        d<| _?        d=| _@        d>| _A        d?| _B        d@| _C        dA| _D        dB| _E        dC| _F        dD| _G        d6| _H        dE| _I        t        j                  |rdFndGdHdHI      | _J        d| _L        d| _M        d| _N        y)JzCreate a new class instance.Nzemail must be a string or Nonez!password must be a string or Nonezis_cn must be a booleanzreturn_on_mfa must be a booleanz./userprofile-service/userprofile/user-settingsz)/userprofile-service/userprofile/settingsz*/device-service/deviceregistration/devicesz/device-service/deviceservicez0/web-gateway/device-info/primary-training-devicez/web-gateway/solarz/weight-servicez&/usersummary-service/usersummary/dailyz%/metrics-service/metrics/maxmet/dailyz/biometric-service/biometricz/biometric-service/statsz0/usersummary-service/usersummary/hydration/dailyz./usersummary-service/usersummary/hydration/logz&/usersummary-service/stats/steps/dailyz*/personalrecord-service/personalrecord/prsz/badge-service/badge/earnedz/badge-service/badge/availablez1/adhocchallenge-service/adHocChallenge/historicalz0/badgechallenge-service/badgeChallenge/completedz0/badgechallenge-service/badgeChallenge/availablez4/badgechallenge-service/badgeChallenge/non-completedz3/badgechallenge-service/virtualChallenge/inProgressz)/wellness-service/wellness/dailySleepDataz&/wellness-service/wellness/dailyStressz"/metrics-service/metrics/hillscorez4/wellness-service/wellness/bodyBattery/reports/dailyz-/wellness-service/wellness/bodyBattery/eventsz*/bloodpressure-service/bloodpressure/rangez$/bloodpressure-service/bloodpressurez'/metrics-service/metrics/endurancescorez//periodichealth-service/menstrualcycle/calendarz./periodichealth-service/menstrualcycle/dayviewz8/periodichealth-service/menstrualcycle/pregnancysnapshotz/goal-service/goal/goalsz!/userstats-service/wellness/dailyz/hrv-service/hrvz*/metrics-service/metrics/trainingreadinessz(/metrics-service/metrics/racepredictionsz2/metrics-service/metrics/trainingstatus/aggregatedz,/wellness-service/wellness/dailySummaryChartz0/wellness-service/wellness/floorsChartData/dailyz)/wellness-service/wellness/dailyHeartRatez,/wellness-service/wellness/daily/respirationz%/wellness-service/wellness/daily/spo2z#/wellness-service/wellness/daily/imz&/wellness-service/wellness/dailyEventsz2/activitylist-service/activities/search/activitiesz!/activitylist-service/activities/z/activity-service/activityz(/activity-service/activity/activityTypesz!/mobile-gateway/heartRate/forDatez/fitnessstats-service/activityz/fitnessage-service/fitnessagez /download-service/files/activityz%/download-service/export/tcx/activityz%/download-service/export/gpx/activityz%/download-service/export/kml/activityz%/download-service/export/csv/activityz/upload-service/uploadz/gear-service/gear/filterGearz/gear-service/gear/z(/wellness-service/wellness/epoch/requestz/workout-servicezgraphql-gateway/graphqlz	garmin.cnz
garmin.com   )domainpool_connectionspool_maxsize)Or   r   r   r(   usernamer>   r?   r@   rA    garmin_connect_user_settings_url'garmin_connect_userprofile_settings_urlgarmin_connect_devices_urlgarmin_connect_device_url!garmin_connect_primary_device_urlgarmin_connect_solar_urlgarmin_connect_weight_url garmin_connect_daily_summary_urlgarmin_connect_metrics_urlgarmin_connect_biometric_url"garmin_connect_biometric_stats_url"garmin_connect_daily_hydration_url garmin_connect_set_hydration_url$garmin_connect_daily_stats_steps_url"garmin_connect_personal_record_url garmin_connect_earned_badges_url#garmin_connect_available_badges_url#garmin_connect_adhoc_challenges_url#garmin_connect_badge_challenges_url-garmin_connect_available_badge_challenges_url1garmin_connect_non_completed_badge_challenges_url0garmin_connect_inprogress_virtual_challenges_urlgarmin_connect_daily_sleep_urlgarmin_connect_daily_stress_urlgarmin_connect_hill_score_url%garmin_connect_daily_body_battery_url&garmin_connect_body_battery_events_url&garmin_connect_blood_pressure_endpoint*garmin_connect_set_blood_pressure_endpoint"garmin_connect_endurance_score_url%garmin_connect_menstrual_calendar_url$garmin_connect_menstrual_dayview_url%garmin_connect_pregnancy_snapshot_urlgarmin_connect_goals_urlgarmin_connect_rhr_urlgarmin_connect_hrv_url%garmin_connect_training_readiness_url!garmin_connect_race_predictor_url"garmin_connect_training_status_url!garmin_connect_user_summary_chart%garmin_connect_floors_chart_daily_url#garmin_connect_heartrates_daily_url$garmin_connect_daily_respiration_urlgarmin_connect_daily_spo2_url&garmin_connect_daily_intensity_minutesgarmin_daily_events_urlgarmin_connect_activities!garmin_connect_activities_baseurlgarmin_connect_activitygarmin_connect_activity_typesgarmin_connect_activity_fordategarmin_connect_fitnessstatsgarmin_connect_fitnessagegarmin_connect_fit_downloadgarmin_connect_tcx_downloadgarmin_connect_gpx_downloadgarmin_connect_kml_downloadgarmin_connect_csv_downloadgarmin_connect_uploadgarmin_connect_geargarmin_connect_gear_baseurlgarmin_request_reload_urlgarmin_workouts"garmin_connect_delete_activity_urlgarmin_graphql_endpointgarthClientdisplay_name	full_nameunit_system)selfr=   r>   r?   r@   rA   s         r"   __init__zGarmin.__init__a   sr    Zs%;=>>
8S(A@AA%&677-.>?? 
$* = 	- 8 	4 +W')H& ? 	. )=%):&0X-*Q',J)2L/> 	/ = 	- 5 	1 9 	/ 1N-3S0? 	0 ? 	0 ? 	: C 	> B 	= 8 	+ 0X,-Q* C 	2
 < 	3
 9 	3
 3 	7
 6 	/ > 	2
 = 	1 G 	2 )C%&I#&8# 9 	2
 7 	. A 	/ ; 	. ? 	2 8 	0 ; 	1 .U*1 	3 (P$@ 	& 2U.'C$-W*/R,+K()I&+M(+R(+R(+R(+R(%="#B +@()S&12N/'@$\\"';\

 !r+   pathkwargsc                 F   	  | j                   j                  |fi |S # t        t        f$ r}t	        |t              r#t        t        |j                  dd      dd      }nt        t        |dd      dd      }t        j                  d|||       |dk(  rt        d|       ||dk(  rt        d|       ||r d	|cxk  rd
k  rn nt        d| d|       |t        d|       |d}~wt        $ r*}t        j                  d|       t        d|       |d}~ww xY w)z1Wrapper for garth connectapi with error handling.responseNstatus_codez-API call failed for path '%s': %s (status=%s)  Authentication failed:   Rate limit exceeded:     zAPI client error (): zHTTP error: z*Connection error during connectapi path=%szConnection error: )r   
connectapir   r   r   getattrerrorlogger GarminConnectAuthenticationError!GarminConnectTooManyRequestsErrorGarminConnectConnectionError	Exception	exceptionr   r   r   r!   statuss        r"   r   zGarmin.connectapi  sN   	P(4::((888>* 	N!^, AGGZ6t !J!=}dSLL?q& }6-aS1 37+A3/ C6/C/2(A37 3\!3EFAM 	PI4P.1CA3/GHaO	Ps!    D B<C**D 6%DD c                 D   	  | j                   j                  |fi |S # t        t        f$ r}t	        |t              r#t        t        |j                  dd      dd      }nt        t        |dd      dd      }t        j                  d||       |dk(  rt        d|       ||dk(  rt        d|       ||r d|cxk  rd	k  rn nt        d
| d|       |t        d|       |d}~wt        $ r*}t        j                  d|       t        d|       |d}~ww xY w)z/Wrapper for garth download with error handling.r   Nr   z)Download failed for path '%s' (status=%s)r   zDownload error: r   r   r   zDownload client error (r   zDownload failed for path '%s')r   downloadr   r   r   r   r   r   r   r   r   r   r   r   s        r"   r   zGarmin.download:  sA   	N&4::&&t6v66>* 	R!^, AGGZ6t !J!=}dSH$PVW}69I!7MNTUU37:J1#8NOUVVC6/C/2-fXS< 35EaS3IJPQQ 	N<dC.1A!/EFAM	Ns!    DB;C))D5%DD
tokenstorec                  
 |xs t        j                  d      }	 d}d}|rFt        |      dkD  r| j                  j	                  |       n| j                  j                  |       n| j                  r| j                  st        d      | j                  s%| j                  rd| j                  vrt        d      | j                  rC| j                  j                  | j                  | j                  | j                        \  }}||fS | j                  j                  | j                  | j                  | j                        \  }}t        | j                  d	d      sZ	 | j                  j                  d
      }|rd|vrt        d      |j!                  d      | _        |j!                  d      | _        nT| j                  j&                  j!                  d      | _        | j                  j&                  j!                  d      | _        | j                  j                  | j(                        }|st        d      d|vrt        d      |d   j!                  d      | _        ||fS # t        $ r}t        d      |d}~ww xY w# t,        t.        j0                  j,                  t2        f$ r}t        t        |dd      dd      }t4        j7                  d||       |dk(  rt        d|       ||dk(  rt9        d|       |t;        |      j=                         
g d}t?        
fd|D              rt        d|       |tA        d|       |d}~wtB        $ r  t        $ r|}tE        |t              r t;        |      j=                         
g d}t?        
fd|D              }	|	rt        d|       |t4        jG                  d       tA        d|       |d}~ww xY w) z
        Log in using Garth.

        Returns:
            Tuple[str | None, str | None]: (access_token, refresh_token) when using credential flow;
            (None, None) when loading from tokenstore.
        GARMINTOKENSNi   z"Username and password are required@zEmail must contain '@' symbol)rA   )r@   profilez(/userprofile-service/userprofile/profilezFailed to retrieve profiledisplayNamezInvalid profile data foundfullNamez Failed to retrieve user settingsuserDatazInvalid user settings foundmeasurementSystemr   r   zLogin failed: %s (status=%s)r   r   r   r   )401unauthorizedzauthentication failedc              3   &   K   | ]  }|v  
 y wN .0	indicator	error_strs     r"   	<genexpr>zGarmin.login.<locals>.<genexpr>  s     Ki9	)K   zLogin failed: )r   r   authenticationzlogin failedc              3   &   K   | ]  }|v  
 y wr   r   r   s     r"   r   zGarmin.login.<locals>.<genexpr>  s     X9	Y 6Xr   zLogin failed)$osgetenvlenr   loadsloadrG   r>   r   r?   rA   loginr@   r   r   r   getr   r   r   rH   r   r   requests
exceptionsr   r   r   r   r   loweranyr   FileNotFoundErrorr   r   )r   r   token1token2profr!   settingsr   auth_indicatorsis_auth_errorr   s             @r"   r   zGarmin.loginW  s     <299^#<
q	LFFz?S(JJ$$Z0JJOOJ/ }}DMM:< 
 zzdmm4==8P:7  %%%)ZZ%5%5&*&8&8 &6 &NFF "6>)%)ZZ%5%5#'?? &6 &NFF 4::y$7::00BD }D8:;WXX$(HH]$;!!%*!5$(JJ$6$6$:$:=$I!!%!3!3!7!7
!Czz,,T-R-RSH66  )67TUU'
3778KLD6>!3 ! :46 8..88.I 	LWQ
D9=$OFLL7FC }6-aS1 37+A3/
 AINOK?KK6-aS1
 /s/CD!K  	 	L!=>eg  XOXXXM6-aS1 ^,.s/CD!K	LsR   C.J
 AJ
 "I- =C/J
 -	J6JJJ
 
(O)2B&MO)-A7O$$O)client_statemfa_codec                 \   | j                   j                  ||      \  }}| j                   j                  r<| j                   j                  d   | _        | j                   j                  d   | _        | j                   j                  | j                        }|rd|v r|d   d   | _        ||fS )zResume login using Garth.r   r   r   r   )r   resume_loginr   r   r   r   rH   r   )r   r   r   result1result2r   s         r"   r   zGarmin.resume_login  s      ::22<J:: $

 2 2= AD!ZZ//
;DN::(()N)NO
h.'
34GHDr+   c                     | j                   S )zReturn full name.)r   r   s    r"   get_full_namezGarmin.get_full_name  s     ~~r+   c                     | j                   S )zReturn unit system.)r   r   s    r"   get_unit_systemzGarmin.get_unit_system  s     r+   cdatec                 $    | j                  |      S )zr
        Return user activity summary for 'cdate' format 'YYYY-MM-DD'
        (compat for garminconnect).
        )get_user_summary)r   r   s     r"   	get_statszGarmin.get_stats  s     $$U++r+   c                    t        |d      }| j                   d| j                   }d|i}t        j	                  d       | j                  ||      }|st        d      |j                  d      du rt        d	      |S )
z=Return user activity summary for 'cdate' format 'YYYY-MM-DD'.r   /calendarDatezRequesting user summaryparamszNo data received from serverprivacyProtectedTzAuthentication error)	r#   rO   r   r   debugr   r   r   r   r   r   urlr   r   s        r"   r   zGarmin.get_user_summary  s     &eW5667q9J9J8KL %(./??3v?6./MNN<<*+t323IJJr+   c                     t        |d      }| j                   d| j                   }d|i}t        j	                  d       | j                  ||      }|t        j                  d       g S |S )z7Fetch available steps data 'cDate' format 'YYYY-MM-DD'.r   r   r   zRequesting steps datar   zNo steps data received)r#   ro   r   r   r   r   warningr   s        r"   get_steps_datazGarmin.get_steps_data
  ss     &eW5778$:K:K9LM%,-??3v?6NN34Ir+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      }|t        d      |S )z8Fetch available floors data 'cDate' format 'YYYY-MM-DD'.r   r   zRequesting floors datazNo floors data received)r#   rp   r   r   r   r   )r   r   r   r   s       r"   
get_floorszGarmin.get_floors  sY     &eW5;;<AeWE-.??3'./HIIr+   startendc                 f   t        |d      }t        |d      }t        j                  |t              j	                         }t        j                  |t              j	                         }||kD  rt        d      | j                   d| d| }t        j                  d       | j                  |      S )zAFetch available steps data 'start' and 'end' format 'YYYY-MM-DD'.r   r   z#start date cannot be after end dater   zRequesting daily steps data)
r#   r   r   r    r   r   rU   r   r   r   )r   r   r   
start_dateend_dater   s         r"   get_daily_stepszGarmin.get_daily_steps,  s     &eW5#C/ &&uo>CCE
$$S/:??A BCC::;1UG1SEJ23s##r+   c                     t        |d      }| j                   d| j                   }d|i}t        j	                  d       | j                  ||      }|t        d      |S )a  Fetch available heart rates data 'cDate' format 'YYYY-MM-DD'.

        Args:
            cdate: Date string in format 'YYYY-MM-DD'

        Returns:
            Dictionary containing heart rate data for the specified date

        Raises:
            ValueError: If cdate format is invalid
            GarminConnectConnectionError: If no data received
            GarminConnectAuthenticationError: If authentication fails
        r   r   r   zRequesting heart ratesr   zNo heart rate data received)r#   rq   r   r   r   r   r   r   s        r"   get_heart_rateszGarmin.get_heart_rates?  sm      &eW599:!D<M<M;NO%-.??3v?6./LMMr+   c                     | j                  |      }| j                  |      }|j                  d      xs i }t        |t              si }i ||S )zEReturn activity data and body composition (compat for garminconnect).totalAverage)r   get_body_compositionr   r   dict)r   r   statsbodybody_avgs        r"   get_stats_and_bodyzGarmin.get_stats_and_body\  sT     u%((/88N+1r(D)H$%$8$$r+   	startdateenddatec                    t        |d      }||nt        |d      }t        j                  |t              j	                         t        j                  |t              j	                         kD  rt        d      | j                   d}t        |      t        |      d}t        j                  d       | j                  ||      S )z
        Return available body composition data for 'startdate' format
        'YYYY-MM-DD' through enddate 'YYYY-MM-DD'.
        r   r   z!startdate cannot be after enddatez/weight/dateRange	startDateendDatezRequesting body compositionr   )r#   r   r   r    r   r   rN   r   r   r   r   r   r   r   r   r   s        r"   r   zGarmin.get_body_compositionf  s     *)[A	 I.CGY.W 	 i9>>@9>>@A @AA//00AB"9~#g,G23s622r+   	timestampweightpercent_fatpercent_hydrationvisceral_fat_mass	bone_massmuscle_mass	basal_met
active_metphysique_ratingmetabolic_agevisceral_fat_ratingbmic                    t        |d      }|rt        j                  |      nt        j                         }t	               }|j                          |j                          |j                  |       |j                  |||||||||	|
|||       |j                          | j                  }dd|j                         fi}| j                  j                  d||d      j                         S )Nr  )r  r  r  r	  r
  r  r  r  r  r  r  r  filezbody_composition.fitr   Tfilesapi)r*   r   fromisoformatnowr   write_file_infowrite_file_creatorwrite_device_infowrite_weight_scalefinishr   getvaluer   postjson)r   r  r  r  r  r	  r
  r  r  r  r  r  r  r  r3   
fitEncoderr   r  s                     r"   add_body_compositionzGarmin.add_body_composition}  s      +68<2;X##I.%'
""$%%'$$R(%%#//#!+' 3 	& 	
 	((+Z-@-@-BC
 zz|S4HMMOOr+   unitKeyc                    t        |d      }|t        vrt        dt               | j                   d}	 |rt	        j
                  |      nt	        j                         }|j                  t        j                        }t        |      t        |      |d|d}t        j                  d       | j                  j                  d	||
      j                         S # t        $ r}t        d|       |d}~ww xY w)zAdd a weigh-in (default to kg)r  unitKey must be one of /user-weightzinvalid timestamp format: NMANUALdateTimestampgmtTimestampr#  
sourceTyper$   zAdding weigh-inr   r   )r*   VALID_WEIGHT_UNITSr   rN   r   r  r  
astimezoner   utcr:   r   r   r   r  r   )	r   r  r#  r  r   r3   r!   dtGMTpayloads	            r"   add_weigh_inzGarmin.add_weigh_in  s     +68<,,67I6JKLL//0=	F6?''	2X\\^B
 hll+$R[#EN"
 	&'zz|Sw?DDFF  	F9!=>AE	Fs   +C 	C9%C44C9r)  r*  c                    | j                    d}|t        vrt        dt               |r#t        j                  |      j                         n!t        j                         j                         }|rat        j                  |      }|j                   |j                  t        j                        }|j                  t        j                        }n|j                  t        j                        }t        |d      }t        |      t        |      |d|d}	t        j                  d|	       | j                  j!                  d||		      j#                         S )
z7Add a weigh-in with explicit timestamps (default to kg)r&  r%  r5   r  r'  r(  z,Adding weigh-in with explicit timestamps: %sr   r,  )rN   r-  r   r   r  r.  r  r6   r8   r   r/  r*   r:   r   r   r   r  r   )
r   r  r#  r)  r*  r   r3   gr0  r1  s
             r"   add_weigh_in_with_timestampsz#Garmin.add_weigh_in_with_timestamps  s$    //0=,,67I6JKLL  ""=1<<>**, 	
 &&|4AxxIIX\\I2LL.EMM(,,/E +68< %R[#EN"
 	CWM zz|Sw?DDFFr+   c                     t        |d      }t        |d      }| j                   d| d| }ddi}t        j                  d       | j	                  ||      S )	zFGet weigh-ins between startdate and enddate using format 'YYYY-MM-DD'.r   r   z/weight/range/r   
includeAllTRequesting weigh-insr   r#   rN   r   r   r   r  s        r"   get_weigh_inszGarmin.get_weigh_ins  sc     *)[A	';//0yk7)T%+,s622r+   c                     t        |d      }| j                   d| }ddi}t        j                  d       | j	                  ||      S )z.Get weigh-ins for 'cdate' format 'YYYY-MM-DD'.r   z/weight/dayview/r7  Tr8  r   r9  r   r   r   r   s       r"   get_daily_weigh_inszGarmin.get_daily_weigh_ins  sR     &eW5//00@H%+,s622r+   	weight_pkc                     t        |d      }| j                   d| d| }t        j                  d       | j                  j                  dd|d      S )	zDelete specific weigh-in.r   z/weight/z/byversion/zDeleting weigh-inDELETEr   Tr  )r#   rN   r   r   r   request)r   r>  r   r   s       r"   delete_weigh_inzGarmin.delete_weigh_in  s`    %eW5//0{9+V()zz!!	 " 
 	
r+   
delete_allc                 ~   | j                  |      }|j                  dg       }|rt        |      dk(  rt        j	                  d|        yt        |      dkD  r=t        j	                  d|        |s#t        j	                  dt        |       d       y|D ]  }| j                  |d	   |        t        |      S )
z
        Delete weigh-in for 'cdate' format 'YYYY-MM-DD'.
        Includes option to delete all weigh-ins for that date.
        dateWeightListr   zNo weigh-ins found on Nr   zMultiple weigh-ins found for z%Set delete_all to True to delete all z
 weigh-inssamplePk)r=  r   r   r   r   rC  )r   r   rD  daily_weigh_ins	weigh_insws         r"   delete_weigh_inszGarmin.delete_weigh_ins  s     2259#''(8"=	C	Na/NN3E7;<^aNN:5'BC;C	N;K:V  	7A  :6	7 9~r+   c                     t        |d      }||}nt        |d      }| j                  }t        |      t        |      d}t        j	                  d       | j                  ||      S )z|
        Return body battery values by day for 'startdate' format
        'YYYY-MM-DD' through enddate 'YYYY-MM-DD'
        r   r   r  zRequesting body battery datar   )r#   ra   r   r   r   r   r  s        r"   get_body_batteryzGarmin.get_body_battery0  sc     *)[A	?G+GY?G88"9~#g,G34s622r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )a   
        Return body battery events for date 'cdate' format 'YYYY-MM-DD'.
        The return value is a list of dictionaries, where each dictionary contains event data for a specific event.
        Events can include sleep, recorded activities, auto-detected activities, and naps
        r   r   z"Requesting body battery event data)r#   rb   r   r   r   r   r   r   s      r"   get_body_battery_eventszGarmin.get_body_battery_eventsC  sC     &eW5<<=QugF9:s##r+   systolic	diastolicpulsenotesc           	         | j                    }|rt        j                  |      nt        j                         }|j	                  t
        j                        }t        |      t        |      |||d|d}	d|ddfd|ddfd	|d
dffD ]8  \  }
}}}t        |t              r||cxk  r|k  r$n t        |
 d| d| d       t        j                  d       | j                  j                  d||	      j                         S )z0
        Add blood pressure measurement
        r'  )measurementTimestampLocalmeasurementTimestampGMTrQ  rR  rS  r+  rT  rQ  F   i  rR  (      rS  rC      z must be an int in [, ]zAdding blood pressurer   r,  )rd   r   r  r  r.  r   r/  r:   r   r/   r   r   r   r   r  r   )r   rQ  rR  rS  r  rT  r   r3   r0  r1  namevallohis                 r"   set_blood_pressurezGarmin.set_blood_pressureP  s
    @@A2;X##I.hll+)0'.u~ ""
 2s+)R-eR%"
 	KD#r2
 c3'cR D6)=bTB4q!IJJ	K 	,-zz|Sw?DDFFr+   c                     t        |d      }||}nt        |d      }| j                   d| d| }ddi}t        j                  d       | j	                  ||      S )zx
        Returns blood pressure by day for 'startdate' format
        'YYYY-MM-DD' through enddate 'YYYY-MM-DD'
        r   r   r   r7  TzRequesting blood pressure datar   )r#   rc   r   r   r   r  s        r"   get_blood_pressurezGarmin.get_blood_pressuret  sm     *)[A	?G+GY?G<<=Qyk7)T%56s622r+   versionc                     | j                    d| d| }t        j                  d       | j                  j	                  dd|d      j                         S )z+Delete specific blood pressure measurement.r   z#Deleting blood pressure measurementr@  r   TrA  )rd   r   r   r   rB  r   )r   re  r   r   s       r"   delete_blood_pressurezGarmin.delete_blood_pressure  s]    @@A5'7)T:;zz!!	 " 

 $&	r+   c                     t        |d      }| j                   d| d| }t        j                  d       | j	                  |      S )zAReturn available max metric data for 'cdate' format 'YYYY-MM-DD'.r   r   zRequesting max metrics)r#   rP   r   r   r   rO  s      r"   get_max_metricszGarmin.get_max_metrics  sI     &eW50015'5'B-.s##r+   Tdaily)latestr   r   aggregationrk  r   r   rl  c                t   |r| j                    d}| j                    dt        j                          d}| j                  |      }t	        |t
              r|r|d   }nt	        |t              r|}ni }| j                  |      }	dddddddd}
|	D ]~  }|j                  d      }|%|d   |
d<   |d	   |
d	<   |d
   |
d
<   |d   |
d<   ||
d<   |j                  d      xs |j                  d      }|||
d<   |j                  d      }|z||
d<    |
|dS |t        d      |"t        j                         j                         }t	        |t              r|j                         }nt        |d      }t	        |t              r|j                         }nt        |d      }h d}||vrt        d|       | j                   d| d| d| d}| j                   d| d| d| d}| j                   d| d| d| d}| j                  |      }| j                  |      }| j                  |      }|||dS )a  
        Returns Running Lactate Threshold information, including heart rate, power, and speed

        :param bool (Required) - latest: Whether to query for the latest Lactate Threshold info or a range.  False if querying a range
        :param date (Optional) - start_date: The first date in the range to query, format 'YYYY-MM-DD'.  Required if `latest` is False.  Ignored if `latest` is True
        :param date (Optional) - end_date: The last date in the range to query, format 'YYYY-MM-DD'. Defaults to current data. Ignored if `latest` is True
        :param str (Optional) - aggregation: How to aggregate the data. Must be one of `daily`, `weekly`, `monthly`, `yearly`.
        z/latestLactateThresholdz/powerToWeight/latest/z?sport=Runningr   N)userProfilePKre  r   sequencespeed	heartRateheartRateCyclingrp  rn  re  r   ro  rq  hearRaterr  )speed_and_heart_ratepowerz5you must either specify 'latest=True' or a start_dater   r   >   rj  weeklyyearlymonthlyzaggregation must be one of z/lactateThresholdSpeed/range/r   z?sport=RUNNING&aggregation=z&aggregationStrategy=LATESTz!/lactateThresholdHeartRate/range/z /functionalThresholdPower/range/)rp  
heart_rateru  )rQ   r   todayr   r   listr   r   r   	isoformatr#   rR   )r   rk  r   r   rl  speed_and_heart_rate_url	power_urlru  
power_dictrt  speed_and_heart_rate_dictentryrp  hrhrc_valid_aggregations	speed_urlheart_rate_urlry  s                      r"   get_lactate_thresholdzGarmin.get_lactate_threshold  s   " 4455LM %  <<==STXT^T^T`SaaopIOOI.E%&5"1X
E4("

#'??3K#L  "& $ !$()% . H		'*$AFAW-o>;@;K-i8@En@U-n=<A*<M-j99>-g6 YY{+Duyy/D>=?-k: ii 23?DG-.@A#H& )B# 
 TUUzz|--/H j$'#--/J.z<HJh%))+H,XzBHF11:;N:OPQQ>>??\]g\hhijris  tO  P[  O\  \w  x	 CCDDefpeqqrs{r|  }X  Yd  Xe  e@  A>>??_`j_kklmulv  wR  S^  R_  _z  {		*__^4
	*j5IIr+   value_in_mlc                    t        |t        j                        st        d      t	        |      t
        kD  rt        dt
         d      | j                  }|B|@t        j                         }t        |      }t        j                         }t        |      }n|4|2t        |d      }t        j                  |t              }t        |      }n|^|\t        |t              st        d      	 	 t        j                   |      }|j                         j#                         }t        |      }n~t        |d      }t        |t              st        d      	 	 t        j                   |      }|j                         j#                         }||k7  rt        d	| d
| d      t        |      }|||d}	t$        j'                  d       | j(                  j+                  d||	      j-                         S # t        $ r t        j                  |d      }Y w xY w# t        $ r}t        d      |d}~ww xY w# t        $ r t        j                  |d      }Y w xY w# t        $ r  w xY w)a  Add hydration data in ml.  Defaults to current date and current timestamp if left empty
        :param float required - value_in_ml: The number of ml of water you wish to add (positive) or subtract (negative)
        :param timestamp optional - timestamp: The timestamp of the hydration update, format 'YYYY-MM-DDThh:mm:ss.ms' Defaults to current timestamp
        :param date optional - cdate: The date of the weigh in, format 'YYYY-MM-DD'. Defaults to current date
        zvalue_in_ml must be a numberz&value_in_ml seems unreasonably high (>zml)Nr   ztimestamp must be a stringz%Y-%m-%dT%H:%M:%Sz,Invalid timestamp format (expected ISO 8601)ztimestamp date (z) doesn't match cdate ())r   timestampLocal	valueInMLzAdding hydration datar   r,  )r   r&   r'   r   absMAX_HYDRATION_MLrT   r   rz  r   r   r  r:   r#   r   r    r  r|  r   r   r   putr   )
r   r  r  r   r   raw_dateraw_tsr!   ts_dater1  s
             r"   add_hydration_datazGarmin.add_hydration_data  sb    +w||4;<< {..89I8J#N  33zz|HME\\^FI9#4)%9E&&uo>FI]y4i- !=>>XO%33I>F //1#FO	
 *%9Ei- !=>>O%33I>F !++-113e#$*7)3J5'QRS  $FO	
 "'$
 	,-zz~~lCg~>CCEEA " O%..y:MNFO  X !OPVWWX " O%..y:MNFO  sa   2G6 )H H9 /A I 6HH HH 	H6%H11H69II II I)c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z<Return available hydration data 'cdate' format 'YYYY-MM-DD'.r   r   zRequesting hydration data)r#   rS   r   r   r   rO  s      r"   get_hydration_datazGarmin.get_hydration_dataP  sC     &eW58895'B01s##r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z>Return available respiration data 'cdate' format 'YYYY-MM-DD'.r   r   zRequesting respiration data)r#   rr   r   r   r   rO  s      r"   get_respiration_datazGarmin.get_respiration_dataY  sC     &eW5::;1UGD23s##r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z7Return available SpO2 data 'cdate' format 'YYYY-MM-DD'.r   r   zRequesting SpO2 data)r#   rs   r   r   r   rO  s      r"   get_spo2_datazGarmin.get_spo2_datab  sC     &eW5334AeW=+,s##r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )zDReturn available Intensity Minutes data 'cdate' format 'YYYY-MM-DD'.r   r   z!Requesting Intensity Minutes data)r#   rt   r   r   r   rO  s      r"   get_intensity_minutes_dataz!Garmin.get_intensity_minutes_datak  sC     &eW5<<=QugF89s##r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )zAReturn available all day stress data 'cdate' format 'YYYY-MM-DD'.r   r   zRequesting all day stress datar#   r_   r   r   r   rO  s      r"   get_all_day_stresszGarmin.get_all_day_stresst  sC     &eW5556aw?56s##r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z
        Return available daily events data 'cdate' format 'YYYY-MM-DD'.
        Includes autodetected activities, even if not recorded on the watch
        r   z?calendarDate=zRequesting all day events data)r#   ru   r   r   r   rO  s      r"   get_all_day_eventszGarmin.get_all_day_events}  sC     &eW5--.nUGD56s##r+   c                     | j                    d| j                   }t        j                  d       | j	                  |      S )z)Return personal records for current user.r   z$Requesting personal records for user)rV   r   r   r   r   r   r   s     r"   get_personal_recordzGarmin.get_personal_record  s>     8894;L;L:MN;<s##r+   c                 f    | j                   }t        j                  d       | j                  |      S )z&Return earned badges for current user.z!Requesting earned badges for user)rW   r   r   r   r  s     r"   get_earned_badgeszGarmin.get_earned_badges  s+     3389s##r+   c                 n    | j                   }t        j                  d       | j                  |ddi      S )z)Return available badges for current user.z$Requesting available badges for usershowExclusiveBadgetruer   )rX   r   r   r   r  s     r"   get_available_badgeszGarmin.get_available_badges  s5     66;<s,@&+IJJr+   c                    t         j                  d       | j                         }| j                         }dt        dt
        fd}t        t        ||            }t        t        ||            }|D ci c]  }|d   |
 }}|j                  |D ci c]  }|d   |
 c}       t        |j                               S c c}w c c}w )z+Return in progress badges for current user.z&Requesting in progress badges for userbadger   c                     | j                  d      }|sy|dk(  ry| j                  d      }||k(  r*| j                  d      y| j                  dd      | d   k  S y)z(Return True if the badge is in progress.badgeProgressValueFr   badgeTargetValuebadgeLimitCountbadgeEarnedNumberT)r   )r  progresstargets      r"   is_badge_in_progressz;Garmin.get_in_progress_badges.<locals>.is_badge_in_progress  sk    yy!56H1}YY12F6!99./7 yy!4a85AR;SSSr+   badgeId)
r   r   r  r  r   r(   r{  filterupdatevalues)r   earned_badgesavailable_badgesr  earned_in_progress_badgesavailable_in_progress_badgesbcombineds           r"   get_in_progress_badgeszGarmin.get_in_progress_badges  s     	=>..0446	 	 	 %)0Dm)T$U!'+')9:(
$ .GGAiL!OGG2NOQ9qOPHOO%&& HOs   4B?Climitc                     t        |d      }t        |d      }| j                  }t        |      t        |      d}t        j                  d       | j                  ||      S )z)Return adhoc challenges for current user.r   r  r   r  z$Requesting adhoc challenges for userr   )r0   r2   rY   r   r   r   r   r   r   r  r   r   s        r"   get_adhoc_challengeszGarmin.get_adhoc_challenges  Y     /ug>*5':66u:E
;;<s622r+   c                     t        |d      }t        |d      }| j                  }t        |      t        |      d}t        j                  d       | j                  ||      S )z)Return badge challenges for current user.r   r  r  $Requesting badge challenges for userr   )r0   r2   rZ   r   r   r   r   r  s        r"   get_badge_challengeszGarmin.get_badge_challenges  r  r+   c                     t        |d      }t        |d      }| j                  }t        |      t        |      d}t        j                  d       | j                  ||      S )z"Return available badge challenges.r   r  r  z%Requesting available badge challengesr   )r0   r2   r[   r   r   r   r   r  s        r"   get_available_badge_challengesz%Garmin.get_available_badge_challenges  sY     /ug>*5':@@u:E
;<=s622r+   c                     t        |d      }t        |d      }| j                  }t        |      t        |      d}t        j                  d       | j                  ||      S )z7Return badge non-completed challenges for current user.r   r  r  r  r   )r0   r2   r\   r   r   r   r   r  s        r"   "get_non_completed_badge_challengesz)Garmin.get_non_completed_badge_challenges  sY    
 /ug>*5':DDu:E
;;<s622r+   c                     t        |d      }t        |d      }| j                  }t        |      t        |      d}t        j                  d       | j                  ||      S )z7Return in-progress virtual challenges for current user.r   r  r  z2Requesting in-progress virtual challenges for userr   )r0   r2   r]   r   r   r   r   r  s        r"   !get_inprogress_virtual_challengesz(Garmin.get_inprogress_virtual_challenges  sY    
 /ug>*5':CCu:E
;IJs622r+   c                     t        |d      }| j                   d| j                   }|dd}t        j	                  d       | j                  ||      S )z#Return sleep data for current user.r   r   <   )r   nonSleepBufferMinuteszRequesting sleep datar   )r#   r^   r   r   r   r   r<  s       r"   get_sleep_datazGarmin.get_sleep_data  sX     &eW5445Qt7H7H6IJ"=,-s622r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z$Return stress data for current user.r   r   zRequesting stress datar  rO  s      r"   get_stress_datazGarmin.get_stress_data  sC     &eW5556aw?-.s##r+   c                     t        |d      }| j                   d| j                   }||dd}t        j	                  d       | j                  ||      S )z/Return resting heartrate data for current user.r   r   r  )fromDate	untilDatemetricIdz!Requesting resting heartrate datar   )r#   rj   r   r   r   r   r<  s       r"   get_rhr_dayzGarmin.get_rhr_day  s`     &eW5,,-Qt/@/@.AB

 	89s622r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z:Return Heart Rate Variability (hrv) data for current user.r   r   z,Requesting Heart Rate Variability (hrv) data)r#   rk   r   r   r   rO  s      r"   get_hrv_datazGarmin.get_hrv_data  sC     &eW5,,-Qug6CDs##r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z0Return training readiness data for current user.r   r   z"Requesting training readiness data)r#   rl   r   r   r   rO  s      r"   get_training_readinesszGarmin.get_training_readiness&  sC     &eW5;;<AeWE9:s##r+   c                 V   t        |d      }|A| j                  }dt        |      i}t        j	                  d       | j                  ||      S | j                   d}t        |d      }t        |      t        |      dd}t        j	                  d	       | j                  ||      S )
a  
        Return endurance score by day for 'startdate' format 'YYYY-MM-DD'
        through enddate 'YYYY-MM-DD'.
        Using a single day returns the precise values for that day.
        Using a range returns the aggregated weekly values for that week.
        r   r   z0Requesting endurance score data for a single dayr   /statsr   rv  r  r  rl  z3Requesting endurance score data for a range of days)r#   re   r   r   r   r   r  s        r"   get_endurance_scorezGarmin.get_endurance_score/  s     *)[A	?99C$c)n5FLLKL??3v?66<<=VDC+GY?G ^w<'F
 LLNO??3v?66r+   _typec                 (   h d}||vrt        d|d      |1|/|-| j                  d| j                   z   }| j                  |      S |||t	        |d      }t	        |d      }t        j                  |t              j                         t        j                  |t              j                         z
  j                  dkD  rt        d      | j                  d	| d	| j                   z   }||d
}| j                  ||      S t        d      )a  
        Return race predictions for the 5k, 10k, half marathon and marathon.
        Accepts either 0 parameters or all three:
        If all parameters are empty, returns the race predictions for the current date
        Or returns the race predictions for each day or month in the range provided

        Keyword Arguments:
        'startdate' the date of the earliest race predictions
        Cannot be more than one year before 'enddate'
        'enddate' the date of the last race predictions
        '_type' either 'daily' (the predictions for each day in the range) or
        'monthly' (the aggregated monthly prediction for each month in the range)
        >   Nrj  rx  zresults: _type must be one of .z/latest/r   r   in  z5Startdate cannot be more than one year before enddater   )fromCalendarDatetoCalendarDater   z7you must either provide all parameters or no parameters)
r   rm   r   r   r#   r   r   r    r   days)r   r   r   r  validr   r   s          r"   get_race_predictionszGarmin.get_race_predictionsL  s:   ( +=eYaHII=Y.7?668DDUDUCV9WW  ??3''9#8W=P-iEI+GY?G!!'?;@@B##I?DDFGdS !K  661UG1TEVEVDW9XX  +4wOF??3v?66 VWWr+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z-Return training status data for current user.r   r   zRequesting training status data)r#   rn   r   r   r   rO  s      r"   get_training_statuszGarmin.get_training_status}  sC     &eW58895'B67s##r+   c                     t        |d      }| j                   d| }t        j                  d       | j	                  |      S )z)Return Fitness Age data for current user.r   r   zRequesting Fitness Age data)r#   r|   r   r   r   rO  s      r"   get_fitnessage_datazGarmin.get_fitnessage_data  sC     &eW5//0%923s##r+   c                 n   |M| j                   }t        |d      }dt        |      i}t        j	                  d       | j                  ||      S | j                    d}t        |d      }t        |d      }t        |      t        |      dd}t        j	                  d	       | j                  ||      S )
zo
        Return hill score by day from 'startdate' format 'YYYY-MM-DD'
        to enddate 'YYYY-MM-DD'
        r   r   z+Requesting hill score data for a single dayr   r  r   rj  r  z.Requesting hill score data for a range of days)r`   r#   r   r   r   r   r  s        r"   get_hill_scorezGarmin.get_hill_score  s     ?44C-iEI$c)n5FLLFG??3v?66 778?C-iEI+GY?G ^w<&F
 LLIJ??3v?66r+   c                 f    | j                   }t        j                  d       | j                  |      S )z6Return available devices for the current user account.zRequesting devices)rJ   r   r   r   r  s     r"   get_deviceszGarmin.get_devices  s+     --)*s##r+   	device_idc                 p    | j                    d| }t        j                  d       | j                  |      S )z3Return device settings for device with 'device_id'.z/device-info/settings/zRequesting device settingsrK   r   r   r   )r   r  r   s      r"   get_device_settingszGarmin.get_device_settings  s8     //00FykR12s##r+   c                 f    | j                   }t        j                  d       | j                  |      S )zReturn detailed information around primary training devices, included the specified device and the
        priority of all devices.
        z.Requesting primary training device information)rL   r   r   r   r  s     r"   get_primary_training_devicez"Garmin.get_primary_training_device  s+    
 44EFs##r+   c                     ||}d}nd}t        |d      }t        |d      }d|i}| j                   d| d| d| }| j                  ||      }|rd|vrt        d	      |d   S )
z8Return solar data for compatible device with 'device_id'TFr   r   singleDayViewr   r   deviceSolarInputz#No device solar input data received)r#   rM   r   r   )r   r  r   r   
single_dayr   r   resps           r"   get_device_solar_datazGarmin.get_device_solar_data  s     ?GJJ))[A	';!:.../q1YKq	Rs62)5./TUU&''r+   c                     t         j                  d       g }| j                         }|D ]/  }| j                  |d         }|j	                  d      }|+||z  }1 |S )z+Get list of active alarms from all devices.zRequesting device alarmsdeviceIdalarms)r   r   r  r  r   )r   r  devicesdevicedevice_settingsdevice_alarmss         r"   get_device_alarmszGarmin.get_device_alarms  so     	/0""$ 	(F"66vj7IJO+//9M(-'		(
 r+   c                 l    | j                    d}t        j                  d       | j                  |      S )zReturn device last used.z/mylastusedzRequesting device last usedr  r  s     r"   get_device_last_usedzGarmin.get_device_last_used  s3     //0<23s##r+   activitytypec                 X   t        |d      }t        |d      }|t        kD  rt        dt               | j                  }t        |      t        |      d}|rt        |      |d<   t        j                  d||       | j                  ||      }|t        j                  d       g S |S )	a,  
        Return available activities.
        :param start: Starting activity offset, where 0 means the most recent activity
        :param limit: Number of activities to return
        :param activitytype: (Optional) Filter activities by type
        :return: List of activities from Garmin
        r   r  zlimit cannot exceed r  activityTypez+Requesting activities from %d with limit %dr   zNo activities data received)
r0   r2   MAX_ACTIVITY_LIMITr   rv   r   r   r   r   r   )r   r   r  r  r   r   
activitiess          r"   get_activitieszGarmin.get_activities  s     /ug>*5':%%34F3GHII,,u:E
;%(%6F>"BE5Q__S_8
NN89Ir+   fordatec                     t        |d      }| j                   d| }t        j                  d|       | j	                  |      S )z%Return available activities for date.r  r   z!Requesting activities for date %s)r#   rz   r   r   r   r   r  r   s      r"   get_activities_fordatezGarmin.get_activities_fordate  sE     (;556ayA8'Bs##r+   activity_idtitlec                 l    | j                    d| }||d}| j                  j                  d||d      S )zSet name for activity with id.r   )
activityIdactivityNamer   Tr   r  )rx   r   r  )r   r  r  r   r1  s        r"   set_activity_namezGarmin.set_activity_name  s@     --.a}=!,eDzz~~lCg4~HHr+   type_idtype_keyparent_type_idc                     | j                    d| }||||dd}t        j                  d|       | j                  j	                  d||d      S )Nr   )typeIdtypeKeyparentTypeId)r  activityTypeDTOzChanging activity type: %sr   Tr  )rx   r   r   r   r  )r   r  r  r  r  r   r1  s          r"   set_activity_typezGarmin.set_activity_type&  s_     --.a}=%!# . 
 	17;zz~~lCg4~HHr+   r1  c                     | j                    }t        j                  dt        |             | j                  j                  d||d      S )NzUploading manual activity: %sr   Tr  )rx   r   r   r   r   r  )r   r1  r   s      r"    create_manual_activity_from_jsonz'Garmin.create_manual_activity_from_json9  s>    --.4c'lCzz|SwDIIr+   start_datetime	time_zonedistance_kmduration_minactivity_namec           	      \    d|idddd|i|ddi||dz  |d	z  d
d}| j                  |      S )a  
        Create a private activity manually with a few basic parameters.
        type_key - Garmin field representing type of activity. See https://connect.garmin.com/modern/main/js/properties/activity_types/activity_types.properties
                    Value to use is the key without 'activity_type_' prefix, e.g. 'resort_skiing'
        start_datetime - timestamp in this pattern "2023-12-02T10:00:00.000"
        time_zone - local timezone of the activity, e.g. 'Europe/Paris'
        distance_km - distance of the activity in kilometers
        duration_min - duration of the activity in minutes
        activity_name - the title
        r     private)r  r  r#  autoCalcCaloriesTr   r  )startTimeLocaldistanceduration)r  accessControlRuleDTOtimeZoneUnitDTOr  metadataDTO
summaryDTO)r"  )r   r#  r$  r  r%  r&  r'  r1  s           r"   create_manual_activityzGarmin.create_manual_activity>  s]    ( !*84/0Y$G )95)"D #1'$.(2-
 44W==r+   c                     | j                  dd      }|r#t        |t              rt        |      dkD  r|d   S |r.t        |t              rd|v r|d   }|rt        |      dkD  r|d   S y)zReturn last activity.r   r   activityListN)r  r   r{  r   r   )r   r  activity_lists      r"   get_last_activityzGarmin.get_last_activitya  sq     ((A.
*Z63z?Q;Nb>!:j$7Nj<X&~6M]!3a!7$R((r+   activity_pathc                 X   |st        d      t        |t              st        d      t        |      }|j	                         st        d|       |j                         st        d|       |j                  }|st        d      |j                  d      }t        |      dk  rt        d|       |d	   }|j                         t        j                  j                  v }|rM	 |j                  d
      5 }d||fi}| j                   }	| j"                  j%                  d|	|d      cddd       S dj+                  t        j                  j                  j-                               }t        d| d|       # 1 sw Y   nxY wy# t&        $ r}
t)        d| d|
       |
d}
~
ww xY w)z(Upload activity in fit format from file.zactivity_path cannot be emptyzactivity_path must be a stringzFile not found: zpath is not a file: z%invalid file path - no filename foundr  r)  zFile has no extension: r5  rbr  r   Tr  NzFailed to read file r   r\  zInvalid file format 'z'. Allowed formats: )r   r   r   r	   existsr   is_filer^  splitr   #GarminConnectInvalidFileFormatErrorupperr<   ActivityUploadFormat__members__openr   r   r  OSErrorr   joinkeys)r   r9  pfile_base_name
file_partsfile_extensionallowed_file_extensionfile_handler  r   r!   allowed_formatss               r"   upload_activityzGarmin.upload_activityp  s   
 <==--=>> xxz#&6}o$FGG yy{3M?CDDDEE $))#.
z?Q5)-9  $B  "f&A&A&M&MM 	 "	VVD\ U[#nk%BCE44C::??<Et?TU U #ii(C(C(O(O(T(T(VWO5''77KOK\] U U U  2*=/A3?s0   (F	 91E<*	F	 <FF	 		F)F$$F)c                     | j                    d| }t        j                  d|       | j                  j	                  dd|d      S )z!Delete activity with specified idr   zDeleting activity with id %sr@  r   TrA  )r   r   r   r   rB  r   r  r   s      r"   delete_activityzGarmin.delete_activity  sR     889;-H3[Azz!!	 " 
 	
r+   	sortorderc                    g }d}d}| j                   }t        |d      }|t        |d      }|t        |      t        |      d}	|r||	d<   |rt        |      |	d<   |rt        |      |	d<   t        j	                  d	||       	 t        |      |	d
<   t        j	                  d|||z          | j                  ||	      }
|
r|j                  |
       ||z   }n	 |S X)a  
        Fetch available activities between specific dates
        :param startdate: String in the format YYYY-MM-DD
        :param enddate: (Optional) String in the format YYYY-MM-DD
        :param activitytype: (Optional) Type of activity you are searching
                             Possible values are [cycling, running, swimming,
                             multi_sport, fitness_equipment, hiking, walking, other]
        :param sortorder: (Optional) sorting direction. By default, Garmin uses descending order by startLocal field.
                          Use "asc" to get activities from oldest to newest.
        :return: list of JSON activities
        r   rC   r   r   )r  r   r  r  r	  	sortOrderz+Requesting activities by date from %s to %sr   zRequesting activities %d to %dr   )rv   r#   r   r   r   r   extend)r   r   r   r  rR  r  r   r  r   r   acts              r"   get_activities_by_datezGarmin.get_activities_by_date  s    & 
 ,,))[A	+GY?G"ZZ

  'F9%(%6F>""%i.F;BIwW!%jF7OLL95%%-P//#f/5C!!#& r+   metricgroupbyactivitiesc                     | j                   }t        |d      }t        |d      }t        |      t        |      dt        |      t        |      d}t        j	                  d||       | j                  ||      S )a  
        Fetch progress summary data between specific dates
        :param startdate: String in the format YYYY-MM-DD
        :param enddate: String in the format YYYY-MM-DD
        :param metric: metric to be calculated in the summary:
            "elevationGain", "duration", "distance", "movingDuration"
        :param groupbyactivities: group the summary by activity type
        :return: list of JSON activities with their aggregated progress summary
        r   r   lifetime)r  r  rl  groupByParentActivityTyperX  z-Requesting fitnessstats by date from %s to %sr   )r{   r#   r   r   r   r   )r   r   r   rX  rY  r   r   s          r"   "get_progress_summary_between_datesz)Garmin.get_progress_summary_between_dates  sy    " ..))[A	';Y7|%),->)?&k
 	;Y	
 s622r+   c                 f    | j                   }t        j                  d       | j                  |      S )NzRequesting activity types)ry   r   r   r   r  s     r"   get_activity_typeszGarmin.get_activity_types
  s)    0001s##r+   r   c                    g }| j                   }h d}||vrt        d|       t        |d      }t        |d      }|t        |      t        |      dd}t        j                  d|       	 t        |      |d<   t        j                  d||||z   d	z
         | j                  ||
      }|r|j                  |       ||z   }n	 |S \)aj  
        Fetch all goals based on status
        :param status: Status of goals (valid options are "active", "future", or "past")
        :type status: str
        :param start: Initial goal index
        :type start: int
        :param limit: Pagination limit when retrieving goals
        :type limit: int
        :return: list of goals in JSON format
        >   pastactivefuturezstatus must be one of r   r  asc)r   r   r  rT  zRequesting %s goalszRequesting %s goals %d to %dr   r   )ri   r   r2   r   r   r   r   rU  )	r   r   r   r  goalsr   valid_statusesr   
goals_jsons	            r"   	get_goalszGarmin.get_goals  s     ++5'5n5EFGG*5':*5':ZZ	
 	*F3!%jF7OLL.uu}q?P V<JZ( r+   userProfileNumberc                 r    | j                    d| }t        j                  d|       | j                  |      S )zReturn all user gear.z?userProfilePk=zRequesting gear for user %s)r   r   r   r   r   ri  r   s      r"   get_gearzGarmin.get_gear:  s:    ))*/:K9LM24EFs##r+   gearUUIDc                 r    | j                    d| }t        j                  d|       | j                  |      S )Nzstats/z%Requesting gear stats for gearUUID %sr   r   r   r   )r   rm  r   s      r"   get_gear_statszGarmin.get_gear_statsA  s7    112&
C<hGs##r+   c                 t    | j                    d| d}t        j                  d|       | j                  |      S )Nzuser/z/activityTypesz$Requesting gear defaults for user %sro  rk  s      r"   get_gear_defaultszGarmin.get_gear_defaultsF  sC    //0 !1 	 	;=NOs##r+   r	  defaultGearc                     |rdnd}|rdnd}| j                    | d| | }| j                  j                  |d|d      S )	Nz/default/true PUTr@  z/activityType/r   TrA  )r   r   rB  )r   r	  rm  rs  defaultGearStringmethod_overrider   s          r"   set_gear_defaultzGarmin.set_gear_defaultN  sa     0;O#.%H//0
 ;(>*;)<> 	 zz!!/<$!OOr+   c                   V    e Zd ZdZ e       Z e       Z e       Z e       Z e       Z	y)Garmin.ActivityDownloadFormatzActivity variables.N)
__name__
__module____qualname____doc__r   ORIGINALTCXGPXKMLCSVr   r+   r"   ActivityDownloadFormatr{  Y  s(    !6ffffr+   r  c                   6    e Zd Z e       Z e       Z e       Zy)Garmin.ActivityUploadFormatN)r|  r}  r~  r   FITr  r  r   r+   r"   rA  r  b  s    fffr+   rA  dl_fmtc                 4   t        |      }t        j                  j                  | j                   d| t        j                  j
                  | j                   d| t        j                  j                  | j                   d| t        j                  j                  | j                   d| t        j                  j                  | j                   d| i}||vrt        d| d      ||   }t        j                  d|       | j!                  |      S )z
        Downloads activity in requested format and returns the raw bytes. For
        "Original" will return the zip file content, up to user to extract it.
        "CSV" will return a csv of the splits.
        r   zunexpected value z for dl_fmtzDownloading activity from %s)r   r<   r  r  r}   r  r~   r  r   r  r   r  r   r   r   r   r   )r   r  r  urlsr   s        r"   download_activityzGarmin.download_activityg  s    +&))22t7W7W6XXYZeYf4g))--$2R2R1SSTU`Ta/b))--$2R2R1SSTU`Ta/b))--$2R2R1SSTU`Ta/b))--$2R2R1SSTU`Ta/b
 0DEE6l3S9}}S!!r+   c                     t        |      }| j                   d| d}t        j                  d|       | j	                  |      S )zReturn activity splits.r   z/splitsz$Requesting splits for activity id %sr   rx   r   r   r   rP  s      r"   get_activity_splitszGarmin.get_activity_splits  sE     +&--.a}GD;[Is##r+   c                     t        |      }| j                   d| d}t        j                  d|       | j	                  |      S )zReturn typed activity splits. Contains similar info to `get_activity_splits`, but for certain activity types
        (e.g., Bouldering), this contains more detail.r   z/typedsplitsz*Requesting typed splits for activity id %sr  rP  s      r"   get_activity_typed_splitsz Garmin.get_activity_typed_splits  sE     +&--.a}LIA;Os##r+   c                     t        |      }| j                   d| d}t        j                  d|       | j	                  |      S )z Return activity split summaries.r   z/split_summariesz-Requesting split summaries for activity id %sr  rP  s      r"   get_activity_split_summariesz#Garmin.get_activity_split_summaries  sF     +&--.a}<LMDkRs##r+   c                     t        |      }| j                   d| d}t        j                  d|       | j	                  |      S )zReturn activity weather.r   z/weatherz%Requesting weather for activity id %sr  rP  s      r"   get_activity_weatherzGarmin.get_activity_weather  sE     +&--.a}HE<kJs##r+   c                     t        |      }| j                   d| d}t        j                  d|       | j	                  |      S )z'Return activity heartrate in timezones.r   z/hrTimeInZonesz.Requesting HR time-in-zones for activity id %sr  rP  s      r"   get_activity_hr_in_timezonesz#Garmin.get_activity_hr_in_timezones  sE     +&--.a}NKE{Ss##r+   c                     t        |      }| j                   d| }t        j                  d|       | j	                  |      S )z0Return activity summary, including basic splits.r   z3Requesting activity summary data for activity id %sr  rP  s      r"   get_activityzGarmin.get_activity  sC     +&--.a}=JKXs##r+   maxchartmaxpolyc                     t        |      }t        |d      }t        |d      }t        |      t        |      d}| j                   d| d}t        j	                  d|       | j                  ||      S )zReturn activity details.r  r  )maxChartSizemaxPolylineSizer   z/detailsz%Requesting details for activity id %sr   )r   r2   rx   r   r   r   )r   r  r  r  r   r   s         r"   get_activity_detailszGarmin.get_activity_details  ss    
 +&-h
C,Wi@"%h-CLQ--.a}HE<kJs622r+   c                     t        t        |      d      }| j                   d| d}t        j	                  d|       | j                  |      S )zReturn activity exercise sets.r  r   z/exerciseSetsz+Requesting exercise sets for activity id %s)r2   r/   rx   r   r   r   rP  s      r"   get_activity_exercise_setsz!Garmin.get_activity_exercise_sets  sL     1[1A=Q--.a}MJBKPs##r+   c                     t        t        |      d      }dt        |      i}| j                  }t        j                  d|       | j                  ||      S )z"Return gears used for activity id.r  r  z"Requesting gear for activity_id %sr   )r2   r/   r   r   r   r   r   )r   r  r   r   s       r"   get_activity_gearzGarmin.get_activity_gear  sU     1[1A=Q#k*
 &&9;Gs622r+   c                     t        |      }t        |d      }t        |t              }| j                   | d| }t
        j                  d|       | j                  |      S )a  Return activities where gear uuid was used.
        :param gearUUID: UUID of the gear to get activities for
        :param limit: Maximum number of activities to return (default: 1000)
        :return: List of activities where the specified gear was used
        r  z/gear?start=0&limit=z%Requesting activities for gearUUID %s)r   r2   minr
  rw   r   r   r   )r   rm  r  r   s       r"   get_gear_activitieszGarmin.get_gear_activities  sb     x=*5':E-.778
BVW\V]^<hGs##r+   c                 f    | j                   }t        j                  d       | j                  |      S )zGet all users settings.zRequesting user profile.)rH   r   r   r   r  s     r"   get_user_profilezGarmin.get_user_profile  s+     33/0s##r+   c                 f    | j                   }t        j                  d       | j                  |      S )zGet user settings.zGetting userprofile settings)rI   r   r   r   r  s     r"   get_userprofile_settingszGarmin.get_userprofile_settings  s+     ::34s##r+   c                     t        |d      }| j                   d| }t        j                  d|       | j                  j                  d|d      j                         S )z{
        Request reload of data for a specific date. This is necessary because
        Garmin offloads older data.
        r   r   z!Requesting reload of data for %s.r   TrA  )r#   r   r   r   r   r  r   rO  s      r"   request_reloadzGarmin.request_reload  sX     &eW5//0%98%@zz|Sd;@@BBr+   c                     | j                    d}t        |d      }t        |d      }t        j	                  d||       ||d}| j                  ||      S )zHReturn workouts starting at offset `start` with at most `limit` results.z	/workoutsr   r  z)Requesting workouts from %d with limit %dr  r   )r   r0   r2   r   r   r   r  s        r"   get_workoutszGarmin.get_workouts  s]     %%&i0.ug>*5':@%O 51s622r+   
workout_idc                 p    t        t        |      d      }| j                   d| }| j                  |      S )zReturn workout by id.r  z	/workout/)r2   r/   r   r   r   r  r   s      r"   get_workout_by_idzGarmin.get_workout_by_id  s:     0JN
%%&i
|<s##r+   c                     t        t        |      d      }| j                   d| }t        j	                  d|       | j                  |      S )zDownload workout by id.r  z/workout/FIT/zDownloading workout from %s)r2   r/   r   r   r   r   r  s      r"   download_workoutzGarmin.download_workout  sI     0JN
%%&mJ<@2C8}}S!!r+   workout_jsonc                 |   | j                    d}t        j                  d|       t        |t              rddl}	 |j                  |      }n|}t        |t        t        z        st        d      | j                  j                  d||d	      j                         S # t        $ r}t        d|       |d}~ww xY w)
zUpload workout using json data.z/workoutzUploading workout using %sr   Nzinvalid workout_json string: z+workout_json must be a JSON object or arrayr   Tr  )r   r   r   r   r   r   r   r   r   r   r{  r   r  )r   r  r   _jsonr1  r!   s         r"   upload_workoutzGarmin.upload_workout"  s    
 %%&h/137lC( M++l3 #G'4$;/JKKzz|SwDINNPP  M #@!DE1LMs   B 	B;'B66B;c                     t        |d      }| j                   d| }t        j                  d|       | j	                  |      S )zReturn menstrual data for date.r  r   z%Requesting menstrual data for date %s)r#   rg   r   r   r   r  s      r"   get_menstrual_data_for_datez"Garmin.get_menstrual_data_for_date7  sE     (;::;1WIF<gFs##r+   c                     t        |d      }t        |d      }| j                   d| d| }t        j                  d||       | j	                  |      S )zHReturn summaries of cycles that have days between startdate and enddate.r   r   r   z1Requesting menstrual data for dates %s through %s)r#   rf   r   r   r   )r   r   r   r   s       r"   get_menstrual_calendar_dataz"Garmin.get_menstrual_calendar_data@  s]    
 *)[A	';;;<Ai['S?G	
 s##r+   c                 h    | j                    }t        j                  d       | j                  |      S )z!Return snapshot of pregnancy dataz"Requesting pregnancy snapshot data)rh   r   r   r   r  s     r"   get_pregnancy_summaryzGarmin.get_pregnancy_summaryN  s.     ;;<9:s##r+   queryc                 f   t        |t              r|j                  d      xs dnd}t        |t              r,t        |j                  d      xs i j	                               ng }t
        j                  d||       | j                  j                  d| j                  |      j                         S )a  Execute a POST to Garmin's GraphQL endpoint.

        Args:
            query: A GraphQL request body, e.g. {"query": "...", "variables": {...}}
            See example.py for example queries.
        Returns:
            Parsed JSON response as a dict.
        operationNameunnamed	variablesz%Querying Garmin GraphQL op=%s vars=%sr   r,  )r   r   r   sortedrF  r   r   r   r  r   r   )r   r  op	vars_keyss       r"   query_garmin_graphqlzGarmin.query_garmin_graphqlV  s     %& YY'49 	 %& EIIk*0b6689 	
 	<b)Lzz$66U  

$&	r+   c                 .    t         j                  d       y)zLog user out of session.z@Deprecated: Alternative is to delete the login tokens to logout.N)r   r   r   s    r"   logoutzGarmin.logouto  s     	N	
r+   )NNFNFr   )NNNNNNNNNNN)r   ru  )r   ru  ru  )F)ru  ru  )NN)NNN)r   rC   N)r-  T)rb  r      )T)i  i  )r   )r   d   )r   N)xr|  r}  r~  r  r   r(   r   r   r
   r   r   tupler   r   r   r   r   r   r   r{  r   r   r   r   r   r   floatr"  r/   r2  r5  r:  r=  rC  rK  rM  rP  rb  rd  rg  ri  r   r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r"  r3  r8  rN  rQ  rW  r]  r_  rh  rl  rp  rr  ry  r   r  rA  r  bytesr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r+   r"   r<   r<   ^   su   6 !#/3#t Tzt  *t  	t 
 RW%,t  t  
t l!Ps !Pc !Pc !PFNS NC NC N:{L3: {LsTz3QU:?U9V {Lz  cN 69 	sCx  sTz 
 t  
,s ,tCH~ ,c d38n (C Dc3h,@ $ S#X  $S $s $tDcN7K $&S T#s(^ :% %S#X % 5933'*Tz3	c3h36 %)*.*."&$("&#'(,&*,0 +P:+P +P T\	+P
 !4<+P !4<+P 4<+P T\+P 4<+P DL+P +P t|+P #T\+P T\+P 
c3h+P\ JLGEkG,/GCFG	c3hGD +Ge+G +G 	+G
 +G 
c3h+GZ	3s 	3S 	3T#s(^ 	33 3c3h 3
 
S 
S 
c t d
 2 5933'*Tz3	d38n	3&$S $T$sCx.5I $$ "G"G "G 	"G
 "G "G 
c3h"GJ 5933'*Tz3	c3h3&
S 
 
c3h 
$S $T#s(^ $ (,&*"cJ cJ $J%	cJ
 *t#cJ cJ 
c3hcJP !% 	MFMF :MF Tz	MF
 
c3hMF^$ $S#X $$# $$sCx. $$3 $4S> $$ $S#X $$ $S#X $
$ 
$S#X 
$$T#s(^ $$4S#X#7 $Kd4S>&: K'T#s(^(< '@	3# 	3c 	3d38n 	3	3# 	3c 	3d38n 	3	3C 	3 	3SRUX 	333!$3	c3h333!$3	c3h33C 3DcN 3$S $T#s(^ $3 3c3h 3$# $$sCx.4*? $$C $DcN $ 5977'*Tz7	c3h7> !%" 	/X:/X t/X Tz	/X
 
c3h/Xb$ $c3h $$ $c3h $ 5977'*Tz7	c3h7:$T$sCx.1 $$S $T#s(^ $$T#s(^ $ EI((),(7:Tz(	d38n	(*49 $d38n $ #'	"" " Dj	"
 
c3h$s)	#"H$c $d38n $IS I I III I 	I
 I 
I&JS#X J3 J
!>!> !> 	!>
 !> !> !> 
!>F4S>D#8 4S 4S 4l
3 
3 
  ##' $44 t4 Dj	4
 :4 
d38n	4t !"&33 3 	3
  3 
c3h3B$DcN $ DF))-0)=@)	d38n	)V$# $$sCx. $$s $tCH~ $
$3 $4S> $ EI	P	P+.	P=A	P		P t  *@)C)C"" '" 
	"4$s $tCH~ $$S $T#s(^ $$ $S#X $$ $S#X $$ $S#X $$ $S#X $ FJ33*-3?B3	c3h3$cCi $DcN $
3S3Y 
34S> 
3 +/$$$'$	d38n	$"$$sCx. $$$sCx. $
CC 
CDcN 
C3# 3# 3S#X 3$C#I $$sCx. $"39 " "Q cNT#Y6<Q	c3hQ*$3 $4S> $$$'*$	c3h$$tCH~ $$sCx. T#s(^ 2
r+   r<   c                       e Zd ZdZy)r   z)Raised when communication ended in error.Nr|  r}  r~  r  r   r+   r"   r   r   w  s    3r+   r   c                       e Zd ZdZy)r   z#Raised when rate limit is exceeded.Nr  r   r+   r"   r   r   {  s    -r+   r   c                       e Zd ZdZy)r   z%Raised when authentication is failed.Nr  r   r+   r"   r   r     s    /r+   r   c                       e Zd ZdZy)r?  z7Raised when an invalid file format is passed to upload.Nr  r   r+   r"   r?  r?    s    Ar+   r?  )r   )r$   )/r  loggingr&   r   r   collections.abcr   r   r   r   enumr   r   pathlibr	   typingr
   r   r   	garth.excr   r   r   fitr   	getLoggerr|  r   r
  r  r   r    r-  r   r#   r/   r  r*   r0   r2   r:   r<   r   r   r   r   r?  r   r+   r"   <module>r     s8   .   	 	 $ - -      4  !			8	$   * E] C S c . +2;$'5[ # 3 S c s  I IS I
V 
 V 
r@49 4.	 .0y 0B) Br+   