sync
This commit is contained in:
@@ -22,10 +22,16 @@ except ImportError:
|
||||
FITBIT_LIBRARY = False
|
||||
|
||||
try:
|
||||
import garminconnect
|
||||
GARMIN_LIBRARY = "garminconnect"
|
||||
import garth
|
||||
GARTH_LIBRARY = True
|
||||
except ImportError:
|
||||
GARMIN_LIBRARY = None
|
||||
GARTH_LIBRARY = False
|
||||
|
||||
try:
|
||||
import garminconnect
|
||||
GARMINCONNECT_LIBRARY = True
|
||||
except ImportError:
|
||||
GARMINCONNECT_LIBRARY = False
|
||||
|
||||
import schedule
|
||||
|
||||
@@ -555,34 +561,12 @@ class GarminClient:
|
||||
self.garminconnect = garminconnect
|
||||
logger.info("Using garminconnect library")
|
||||
|
||||
# Monkey patch the login method to handle garth compatibility issue
|
||||
original_login = self.garminconnect.Garmin.login
|
||||
|
||||
def patched_login(self):
|
||||
"""Patched login method that handles garth returning None"""
|
||||
try:
|
||||
result = original_login(self)
|
||||
return result
|
||||
except TypeError as e:
|
||||
if "cannot unpack non-iterable NoneType object" in str(e):
|
||||
# Check if we have valid tokens despite the None return
|
||||
if (self.garth.oauth1_token and self.garth.oauth2_token):
|
||||
logger.info("Login successful (handled garth None return)")
|
||||
return True
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise
|
||||
|
||||
# Apply the patch
|
||||
self.garminconnect.Garmin.login = patched_login
|
||||
|
||||
except ImportError:
|
||||
logger.error("garminconnect library not installed. Install with: pip install garminconnect")
|
||||
raise ImportError("garminconnect library is required but not installed")
|
||||
|
||||
async def authenticate(self) -> bool:
|
||||
"""Authenticate with Garmin Connect"""
|
||||
"""Authenticate with Garmin Connect using garth"""
|
||||
if self.read_only_mode:
|
||||
logger.info("Running in read-only mode - skipping Garmin authentication")
|
||||
return True
|
||||
@@ -597,54 +581,24 @@ class GarminClient:
|
||||
if not self._setup_credentials():
|
||||
return False
|
||||
|
||||
logger.info("Initializing Garmin client...")
|
||||
# Set session file path for garminconnect library
|
||||
os.environ['GARMINTOKENS'] = str(self.session_file)
|
||||
|
||||
# Configure garth for domain if using Garmin China
|
||||
if self.is_china:
|
||||
garth.configure(domain="garmin.cn")
|
||||
|
||||
# Initialize garminconnect.Garmin with credentials.
|
||||
# It will use garth library for authentication and session management.
|
||||
self.garmin_client = self.garminconnect.Garmin(
|
||||
self.username,
|
||||
self.password,
|
||||
is_cn=self.is_china
|
||||
self.username, self.password
|
||||
)
|
||||
self.garmin_client.login()
|
||||
|
||||
# Use garth to load the session if it exists
|
||||
if os.path.exists(self.session_file):
|
||||
try:
|
||||
logger.info(f"Attempting to load session from {self.session_file}")
|
||||
self.garmin_client.garth.load(self.session_file)
|
||||
logger.info("Loaded existing session from file.")
|
||||
# Log garth state after loading
|
||||
logger.info(f"Garth tokens after load: oauth1={bool(self.garmin_client.garth.oauth1_token)}, oauth2={bool(self.garmin_client.garth.oauth2_token)}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not load session file: {e}. Performing fresh login.")
|
||||
|
||||
# Login (will use loaded session or perform a fresh auth)
|
||||
logger.info("Calling garmin_client.login()...")
|
||||
try:
|
||||
# Handle garth API compatibility issue - newer versions return None
|
||||
# when using existing sessions, but garminconnect expects a tuple
|
||||
login_result = self.garmin_client.login()
|
||||
|
||||
# Check if login returned None (new garth behavior with existing sessions)
|
||||
if login_result is None:
|
||||
# Verify that we actually have valid tokens after login
|
||||
if (self.garmin_client.garth.oauth1_token and
|
||||
self.garmin_client.garth.oauth2_token):
|
||||
logger.info("Login successful (garth returned None but tokens are valid)")
|
||||
else:
|
||||
logger.error("Login failed - garth returned None and no valid tokens")
|
||||
raise Exception("Garmin login failed: No valid tokens after authentication")
|
||||
else:
|
||||
logger.info("Login successful")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Login failed with exception: {e}")
|
||||
# Log garth state before re-raising
|
||||
logger.info(f"Garth tokens before failure: oauth1={bool(self.garmin_client.garth.oauth1_token)}, oauth2={bool(self.garmin_client.garth.oauth2_token)}")
|
||||
raise
|
||||
|
||||
# Save the session using garth's dump method
|
||||
self.garmin_client.garth.dump(self.session_file)
|
||||
|
||||
# Verify by getting the full name
|
||||
profile = self.garmin_client.get_full_name()
|
||||
logger.info(f"Successfully authenticated and saved session for user: {profile}")
|
||||
logger.info(f"Successfully authenticated with Garmin for user: {profile}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
@@ -653,6 +607,10 @@ class GarminClient:
|
||||
logger.error(f"Full traceback: {traceback.format_exc()}")
|
||||
return False
|
||||
|
||||
def _mfa_handler(self, _) -> str:
|
||||
"""Handle MFA code input from the user."""
|
||||
return input("Enter Garmin MFA code: ")
|
||||
|
||||
def _setup_credentials(self) -> bool:
|
||||
"""Setup Garmin credentials interactively"""
|
||||
print("\n🔑 Garmin Connect Credentials Setup")
|
||||
|
||||
Reference in New Issue
Block a user