]> git.giorgioravera.it Git - mercedes_me_api.git/commitdiff
Updated Python Version with Classes
authorGiorgio Ravera <giorgio.ravera@gmail.com>
Thu, 22 Oct 2020 20:06:58 +0000 (22:06 +0200)
committerGiorgio Ravera <giorgio.ravera@gmail.com>
Thu, 22 Oct 2020 20:06:58 +0000 (22:06 +0200)
.gitignore
config.py [new file with mode: 0644]
mercedes_me_api.py
mercedes_me_api.sh
query.py [new file with mode: 0644]
resources.py [new file with mode: 0644]

index 2ef5a3b1bfb847331bf0c6036c2db89dd020c8d2..3661afef5819bec1edf93a80995d1833cde8ae29 100644 (file)
@@ -1,3 +1,4 @@
+__pycache__
 .mercedesme_credentials
 .mercedesme_token
 .mercedesme_resources
diff --git a/config.py b/config.py
new file mode 100644 (file)
index 0000000..0ec9642
--- /dev/null
+++ b/config.py
@@ -0,0 +1,178 @@
+"""
+Mercedes Me APIs
+
+Author: G. Ravera
+
+For more details about this component, please refer to the documentation at
+https://github.com/xraver/mercedes_me_api/
+"""
+from configobj import ConfigObj
+import base64
+import json
+import logging
+import os
+import requests
+
+from query import (
+       GetResource,
+       GetToken
+)
+
+# Logger
+logger = logging.getLogger(__name__)
+
+# Software Parameters
+NAME = "Mercedes Me API"
+DOMAIN = "mercedesmeapi"
+VERSION = "0.2"
+TOKEN_FILE = ".mercedesme_token"
+CREDENTIAL_FILE = ".mercedesme_credentials"
+RESOURCES_FILE = ".mercedesme_resources"
+REDIRECT_URL = "https://localhost"
+SCOPE = "mb:vehicle:mbdata:fuelstatus%20mb:vehicle:mbdata:vehiclestatus%20mb:vehicle:mbdata:vehiclelock%20offline_access"
+URL_OAUTH = "https://id.mercedes-benz.com/as/token.oauth2"
+URL_RES_PREFIX = "https://api.mercedes-benz.com/vehicledata/v2"
+
+# File Parameters
+CONF_CLIENT_ID = "CLIENT_ID"
+CONF_CLIENT_SECRET = "CLIENT_SECRET"
+CONF_VEHICLE_ID = "VEHICLE_ID"
+
+class MercedesMeConfig:
+
+    name = NAME
+    domain = DOMAIN
+    version = VERSION
+    token_file = TOKEN_FILE
+    credentials_file = CREDENTIAL_FILE
+    resources_file = RESOURCES_FILE
+    redirect_uri = REDIRECT_URL
+    scope = SCOPE
+    client_id = ""
+    client_secret = ""
+    vin = ""
+    base64 = ""
+    access_token = ""
+    refresh_token = ""
+    oauth_url = URL_OAUTH
+    res_url_prefix = URL_RES_PREFIX
+
+    ########################
+    # Read Configuration
+    ########################
+    def ReadConfig(self):
+        # Read credentials from file
+        if not os.path.isfile(self.credentials_file):
+            logger.error ("Credential File " + self.credentials_file + " not found")
+            return False
+        try:
+            f = ConfigObj(self.credentials_file)
+        except Exception:
+            logger.error ("Wrong "+ self.credentials_file + " file found")
+            return False
+        # Client ID
+        self.client_id = f.get(CONF_CLIENT_ID)
+        if not self.client_id:
+            logger.error ("No "+ CONF_CLIENT_ID + " found in the configuration")
+            return False
+        # Client Secret
+        self.client_secret = f.get(CONF_CLIENT_SECRET)
+        if not self.client_secret:
+            logger.error ("No "+ CONF_CLIENT_SECRET + " found in the configuration")
+            return False
+        # Vehicle ID
+        self.vin = f.get(CONF_VEHICLE_ID)
+        if not self.vin:
+            logger.error ("No "+ CONF_VEHICLE_ID + " found in the configuration")
+            return False
+        # Base64
+        b64_str = self.client_id + ":" + self.client_secret
+        b64_bytes = base64.b64encode( b64_str.encode('ascii') )
+        self.base64 = b64_bytes.decode('ascii')
+
+        # Read Token
+        token_file = self.token_file
+        if not os.path.isfile(token_file):
+            logger.error ("Token File missing - Creating a new one")
+            token = self.CreateToken()
+            if token == None:
+                logger.error ("Error creating token")
+                return False
+        else:
+            with open(token_file, 'r') as file:
+                try:
+                    token = json.load(file)
+                except ValueError:
+                    token = None
+                if not self.CheckToken(token):
+                    logger.error ("Token File not correct - Creating a new one")
+                    token = self.CreateToken()
+                    if token == None:
+                        logger.error ("Error creating token")
+                        return False
+        # Save Token
+        self.access_token = token['access_token']
+        self.refresh_token = token['refresh_token']
+        return True
+
+    ########################
+    # Write Token
+    ########################
+    def WriteToken(self, token):
+        token_file = self.token_file
+        with open(token_file, 'w') as file:
+            json.dump(token, file)
+
+    ########################
+    # Check Token
+    ########################
+    def CheckToken(self, token):
+        if "error" in token:
+            if "error_description" in token:
+                logger.error ("Error retriving token: " + token["error_description"])
+            else:
+                logger.error ("Error retriving token: " + token["error"])
+            return False
+        if len(token) == 0:
+            logger.error ("Empty token found.")
+            return False
+        if not 'access_token' in token:
+            logger.error ("Access token not present.")
+            return False
+        if not 'refresh_token' in token:
+            logger.error ("Refresh token not present.")
+            return False
+        return True
+
+    ########################
+    # Create Token
+    ########################
+    def CreateToken(self):
+        print( "Open the browser and insert this link:\n" )
+        print( "https://id.mercedes-benz.com/as/authorization.oauth2?response_type=code&client_id=" + self.client_id + "&redirect_uri=" + self.redirect_uri + "&scope=" + self.scope + "\n")
+        print( "Copy the code in the url:")
+        auth_code = input()
+
+        token = GetToken(self, refresh=False, auth_code=auth_code)
+
+        # Check Token
+        if not self.CheckToken(token):
+            return None
+        else:
+            self.WriteToken(token)
+            return token
+
+    ########################
+    # Refresh Token
+    ########################
+    def RefreshToken(self):
+
+        token = GetToken(self, refresh=True)
+
+        # Check Token
+        if not self.CheckToken(token):
+            return None
+        else:
+            self.WriteToken(token)
+            return token
+        return True
index 5f0c220c858314c72e320f96a46e2d77d10baa14..48d326b4c3c4094436cd7c1096477f5c2f3bca90 100644 (file)
 """
+Mercedes Me APIs
+
 Author: G. Ravera
-Version 0.1
-Creation Date: 22/10/2020
 
-Change log:
-            22/10/2020 - 0.1 - First Issue
+For more details about this component, please refer to the documentation at
+https://github.com/xraver/mercedes_me_api/
 """
 import argparse
-import base64
-from configobj import ConfigObj
-import getopt
 import logging
-import json
-import os
-import requests
 import sys
 
-# URLs
-oauth_url = "https://id.mercedes-benz.com/as/token.oauth2"
-res_url_prefix = "https://api.mercedes-benz.com/vehicledata/v2"
-# File Parameters
-CONF_CLIENT_ID = "CLIENT_ID"
-CONF_CLIENT_SECRET = "CLIENT_SECRET"
-CONF_VEHICLE_ID = "VEHICLE_ID"
-# Config Parameters
-MercedeMeConfig = { 
-            "name": "Mercedes Me API",
-            "domain": "mercedesmeapi",
-            "version": "0.1",
-            "token_file": ".mercedesme_token",
-            "credentials_file": ".mercedesme_credentials",
-            "available_resources_file": ".mercedesme_resources",
-            "redirect_uri": "https://localhost",
-            "scope": "mb:vehicle:mbdata:fuelstatus%20mb:vehicle:mbdata:vehiclestatus%20mb:vehicle:mbdata:vehiclelock%20offline_access",
-            "client_id": "",
-            "client_secret": "",
-            "vin": "",
-            "base64": "",
-            "access_token": "",
-            "refresh_token": "",
-}
-# Available Resources (Array)
-availableResources = {}
-# Response Validity
-valid_res = False
+from config import MercedesMeConfig
+from resources import MercedesMeResources
+
 # Logger
 logger = logging.getLogger(__name__)
 
-########################
-# Read Configuration
-########################
-def ReadCfg():
-    global availableResources
-
-    # Read Credentials
-    if not os.path.isfile(MercedeMeConfig['credentials_file']):
-        logger.error ("Credential File " + MercedeMeConfig['credentials_file'] + " not found")
-        return False
-    f = ConfigObj(MercedeMeConfig['credentials_file'])
-    MercedeMeConfig['client_id'] = f.get(CONF_CLIENT_ID)
-    if not MercedeMeConfig['client_id']:
-        logger.error ("No "+ CONF_CLIENT_ID + " found in the configuration")
-        return False
-    MercedeMeConfig['client_secret'] = f.get(CONF_CLIENT_SECRET)
-    if not MercedeMeConfig['client_secret']:
-        logger.error ("No "+ CONF_CLIENT_SECRET + " found in the configuration")
-        return False
-    MercedeMeConfig['vin'] = f.get(CONF_VEHICLE_ID)
-    if not MercedeMeConfig['vin']:
-        logger.error ("No "+ CONF_VEHICLE_ID + " found in the configuration")
-        return False
-    b64_str = MercedeMeConfig['client_id'] + ":" + MercedeMeConfig['client_secret']
-    b64_bytes = base64.b64encode( b64_str.encode('ascii') )
-    MercedeMeConfig['base64'] = b64_bytes.decode('ascii')
-
-    # Read Token
-    if not os.path.isfile(MercedeMeConfig['token_file']):
-        logger.error ("Token File missing - Creating a new one")
-        token = CreateToken()
-        if token == None:
-            logger.error ("Error creating token")
-            return False
-    else:
-        with open(MercedeMeConfig['token_file'], 'r') as file:
-            try:
-                token = json.load(file)
-            except ValueError:
-                token = {}
-            if not CheckToken(token):
-                logger.error ("Token File not correct - Creating a new one")
-                token = CreateToken()
-                if token == None:
-                    return False
-    # Save Token
-    MercedeMeConfig['access_token'] = token['access_token']
-    MercedeMeConfig['refresh_token'] = token['refresh_token']
-
-    # Get Resources List
-    if not os.path.isfile(MercedeMeConfig['available_resources_file']):
-        logger.error ("Resource File missing - Creating a new one")
-        return CreateResourcesFile()
-    else:
-        with open(MercedeMeConfig['available_resources_file'], 'r') as file:
-            try:
-                availableResources = json.load(file)
-            except ValueError:
-                availableResources = {}
-            if not CheckResources(availableResources):
-                logger.error ("Resource File not correct - Creating a new one")
-                return CreateResourcesFile()
-
-    return True
-
-########################
-# Write Token
-########################
-def WriteToken(token):
-    with open(MercedeMeConfig['token_file'], 'w') as file:
-        json.dump(token, file)
-
-########################
-# Check Token
-########################
-def CheckToken(token):
-    if "error" in token:
-        if "error_description" in token:
-            logger.error ("Error retriving token: " + token["error_description"])
-        else:
-            logger.error ("Error retriving token: " + token["error"])
-        return False
-    if len(token) == 0:
-        logger.error ("Empty token found.")
-        return False
-    if not 'access_token' in token:
-        logger.error ("Access token not present.")
-        return False
-    if not 'refresh_token' in token:
-        logger.error ("Refresh token not present.")
-        return False
-
-    return True
-
-########################
-# Create Token
-########################
-def CreateToken():
-    print( "Open the browser and insert this link:\n" )
-    print( "https://id.mercedes-benz.com/as/authorization.oauth2?response_type=code&client_id=" + MercedeMeConfig["client_id"] + "&redirect_uri=" + MercedeMeConfig["redirect_uri"] + "&scope=" + MercedeMeConfig["scope"] + "\n")
-    print( "Copy the code in the url:")
-    auth_code = input()
-
-    headers = {
-        "Authorization": "Basic " + MercedeMeConfig['base64'],
-        "content-type": "application/x-www-form-urlencoded"
-    }
-    data = "grant_type=authorization_code&code=" + auth_code + "&redirect_uri=" + MercedeMeConfig["redirect_uri"]
-
-    res = requests.post(oauth_url, data = data, headers = headers)
-    try:
-        token = res.json()
-    except ValueError:
-        logger.error ("Error retriving token " + str(res.status_code))
-        return None
-
-    # Check Token
-    if not CheckToken(token):
-        return None
-    else:
-        WriteToken(token)
-        return token
-
-########################
-# Refresh Token
-########################
-def RefreshToken():
-    headers = {
-        "Authorization": "Basic " + MercedeMeConfig['base64'],
-        "content-type": "application/x-www-form-urlencoded"
-    }
-    data = "grant_type=refresh_token&refresh_token=" + MercedeMeConfig['refresh_token']
-
-    res = requests.post(oauth_url, data = data, headers = headers)
-    try:
-        token = res.json()
-    except ValueError:
-        logger.error ("Error refreshing token: " + str(res.status_code))
-        return False
-
-    MercedeMeConfig['access_token'] = token['access_token']
-    MercedeMeConfig['refresh_token'] = token['refresh_token']
-
-    WriteToken(token)
-
-    return True
-
-########################
-# Init Resources
-########################
-def InitResources():
-    # Create Empty Status
-    for res in availableResources:
-        res["status"] = ""
-        res["timestamp"] = 0
-        res["valid"] = False
-
-########################
-# Write Resources File
-########################
-def WriteResourcesFile():
-    with open(MercedeMeConfig['available_resources_file'], 'w') as file:
-        json.dump(availableResources, file)
-
-########################
-# Check Resources
-########################
-def CheckResources(resources):
-    if "error" in resources:
-        if "error_description" in resources:
-            logger.error ("Error retriving resources: " + resources["error_description"])
-        else:
-            logger.error ("Error retriving resources: " + resources["error"])
-        return False
-    if len(resources) == 0:
-        logger.error ("Empty resources found.")
-        return False
-
-    return True
-
-########################
-# Create Resources File
-########################
-def CreateResourcesFile():
-    global availableResources
-    resName = "resources"
-    resURL = res_url_prefix + "/vehicles/" + MercedeMeConfig['vin'] + "/" + resName
-    availableResources = GetResource(resName, resURL)
-    if valid_res:
-        InitResources()
-        WriteResourcesFile()
-        return True
-    else:
-        logger.error ("Error retriving available resources")
-        logger.error ("-> " + availableResources["reason"] + " (" + str(availableResources["code"]) + ")")
-        return False
-
-########################
-# GetResource
-########################
-def GetResource(resourceName, resourceURL):
-    global valid_res
-
-    # Set Header
-    headers = {
-        "accept": "application/json;charset=utf-8", 
-        "authorization": "Bearer "+MercedeMeConfig['access_token']
-    }
-
-    # Send Request
-    res = requests.get(resourceURL, headers=headers)
-    try:
-        data = res.json()
-        valid_res = True
-    except ValueError:
-        data = { "reason": "No Data",
-                 "code" : res.status_code 
-        }
-        valid_res = False
-
-    # Check Error
-    if not res.ok:
-        if ("reason" in data):
-            reason = data["reason"]
-        else:
-            if res.status_code == 400:
-                reason = "Bad Request"
-            elif res.status_code == 401:
-                reason = "Invalid or missing authorization in header"
-            elif res.status_code == 402:
-                reason = "Payment required"
-            elif res.status_code == 403:
-                reason = "Forbidden"
-            elif res.status_code == 404:
-                reason = "Page not found"
-            elif res.status_code == 429:
-                reason = "The service received too many requests in a given amount of time"
-            elif res.status_code == 500:
-                reason = "An error occurred on the server side"
-            elif res.status_code == 503:
-                reason = "The server is unable to service the request due to a temporary unavailability condition"
-            else:
-                reason = "Generic Error"
-        data["reason"] = reason
-        data["code"] = res.status_code
-        valid_res = False
-    return data
-
-########################
-# Print Available Resources
-########################
-def PrintAvailableResources():
-    print ("Found %d resources" % len(availableResources) + ":")
-    for res in availableResources:
-        print (res["name"] + ": " + res_url_prefix + res["href"])
-
-########################
-# Print Resources Status
-########################
-def PrintResourcesStatus(valid = True):
-    for res in availableResources:
-        if((not valid) | res["valid"]):
-            print (res["name"] + ":")
-            print ("\tvalid: " + str(res["valid"]))
-            print ("\tstatus: " + res["status"])
-            print ("\ttimestamp: " + str(res["timestamp"]))
-
-########################
-# Update Resources Status
-########################
-def UpdateResourcesStatus():
-    for res in availableResources:
-        resName = res["name"]
-        resURL = res_url_prefix + res["href"]
-        result = GetResource(resName, resURL)
-        if valid_res:
-            res["valid"] = True
-            res["timestamp"] = result[resName]["timestamp"]
-            res["status"] = result[resName]["value"]
-    # Write Resource File
-    WriteResourcesFile()
+class MercedesMeData:
+    def __init__(self):
+        # Configuration Data
+        self.config = MercedesMeConfig()
+        # Resource Data
+        self.resources = MercedesMeResources(self.config)
 
 ########################
 # Parse Input
@@ -338,7 +32,7 @@ def ParseInput():
     parser.add_argument('-r', '--refresh', action='store_true', help="Procedure to refresh the Access Token")
     parser.add_argument('-s', '--status', action='store_true', help="Retrive the Status of your Vehicle")
     parser.add_argument('-R', '--resources', action='store_true', help="Retrive the list of available resources of your Vehicle")
-    parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + MercedeMeConfig['version'])
+    parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + data.config.version)
 
     if len(sys.argv)==1:
         parser.print_help(sys.stderr)
@@ -350,26 +44,34 @@ def ParseInput():
 # Main
 ########################
 if __name__ == "__main__":
+
+    # Creating Data Structure
+    data = MercedesMeData()
+
     args = ParseInput()
 
     # Reading Configuration
-    if not ReadCfg():
-        logger.error ("Error reading configuration")
+    if not data.config.ReadConfig():
+        logger.error ("Error initializing configuration")
+        exit (1)
+
+    if not data.resources.ReadResources():
+        logger.error ("Error initializing resources")
         exit (1)
 
-    if ( args.token == True):
-        if not CreateToken():
+    if (args.token == True):
+        if not data.config.CreateToken():
             logger.error ("Error creating token")
             exit (1)
 
     if (args.refresh == True):
-        if not RefreshToken():
+        if not data.config.RefreshToken():
             logger.error ("Error refreshing token")
             exit (1)
             
     if (args.resources):
-        PrintAvailableResources()
+        data.resources.PrintAvailableResources()
 
     if (args.status == True):
-        UpdateResourcesStatus()
-        PrintResourcesStatus()
+        data.resources.UpdateResourcesStatus()
+        data.resources.PrintResourcesStatus()
index 01b4dcba01364ea5735bbe952924c374955152e6..ab9ea6329c0e0186addd3415a0104fdc0da668fc 100755 (executable)
 
 # Script Name & Version
 NAME="mercedes_me_api.sh"
-VERSION="0.1"
+VERSION="0.2"
 
 # Static Parameters
 REDIRECT_URL="https://localhost"
 SCOPE="mb:vehicle:mbdata:fuelstatus mb:vehicle:mbdata:vehiclestatus mb:vehicle:mbdata:vehiclelock offline_access"
 STATE="12345678"
 TOKEN_FILE=".mercedesme_token"
-CREDENTIALS_FILE=".mercedesmw_credentials"
+CREDENTIALS_FILE=".mercedesme_credentials"
 
 # Credentials
 CLIENT_ID=""
diff --git a/query.py b/query.py
new file mode 100644 (file)
index 0000000..7727528
--- /dev/null
+++ b/query.py
@@ -0,0 +1,73 @@
+import requests
+
+########################
+# GetResource
+########################
+def GetResource(resourceName, resourceURL, config):
+
+    # Set Header
+    headers = {
+        "accept": "application/json;charset=utf-8", 
+        "authorization": "Bearer "+ config.access_token
+    }
+
+    # Send Request
+    res = requests.get(resourceURL, headers=headers)
+    try:
+        data = res.json()
+    except ValueError:
+        data = { "reason": "No Data",
+                 "code" : res.status_code 
+        }
+
+    # Check Error
+    if not res.ok:
+        if ("reason" in data):
+            reason = data["reason"]
+        else:
+            if res.status_code == 400:
+                reason = "Bad Request"
+            elif res.status_code == 401:
+                reason = "Invalid or missing authorization in header"
+            elif res.status_code == 402:
+                reason = "Payment required"
+            elif res.status_code == 403:
+                reason = "Forbidden"
+            elif res.status_code == 404:
+                reason = "Page not found"
+            elif res.status_code == 429:
+                reason = "The service received too many requests in a given amount of time"
+            elif res.status_code == 500:
+                reason = "An error occurred on the server side"
+            elif res.status_code == 503:
+                reason = "The server is unable to service the request due to a temporary unavailability condition"
+            else:
+                reason = "Generic Error"
+        data["reason"] = reason
+        data["code"] = res.status_code
+    return data
+
+########################
+# GetToken
+########################
+def GetToken(config, refresh=True, auth_code=""):
+    headers = {
+        "Authorization": "Basic " + config.base64,
+        "content-type": "application/x-www-form-urlencoded"
+    }
+
+    if (not refresh):
+        # New Token
+        data = "grant_type=authorization_code&code=" + auth_code + "&redirect_uri=" + config.redirect_uri
+    else:
+        # Refresh
+        data = "grant_type=refresh_token&refresh_token=" + config.refresh_token
+
+    res = requests.post(config.oauth_url, data = data, headers = headers)
+    try:
+        token = res.json()
+    except ValueError:
+        logger.error ("Error retriving token " + str(res.status_code))
+        return None
+
+    return token
diff --git a/resources.py b/resources.py
new file mode 100644 (file)
index 0000000..327befa
--- /dev/null
@@ -0,0 +1,132 @@
+"""
+Mercedes Me APIs
+
+Author: G. Ravera
+
+For more details about this component, please refer to the documentation at
+https://github.com/xraver/mercedes_me_api/
+"""
+import logging
+import json
+import os
+
+from config import MercedesMeConfig
+from query import GetResource
+
+# Logger
+logger = logging.getLogger(__name__)
+
+class MercedesMeResources:
+
+    resources = None
+    config = None
+
+    ########################
+    # Init
+    ########################
+    def __init__(self, config):
+        self.config = config
+        
+    ########################
+    # Read Resources
+    ########################
+    def ReadResources(self):
+        # Get Resources List
+        if not os.path.isfile(self.config.resources_file):
+            logger.error ("Resource File missing - Creating a new one.")
+            if (self.CreateResourcesFile() == False):
+                logger.error ("Error creating resources file.")
+                return None
+        else:
+            with open(self.config.resources_file, 'r') as file:
+                try:
+                    self.resources = json.load(file)
+                except ValueError:
+                    resources = None
+                if not self.CheckResources():
+                    logger.error ("Resource File not correct - Creating a new one.")
+                    if (self.CreateResourcesFile() == False):
+                        logger.error ("Error creating resources file.")
+                        return None
+        return True
+
+    ########################
+    # Init Resources
+    ########################
+    def InitResources(self):
+        # Create Empty Status
+        for res in self.resources:
+            res["status"] = ""
+            res["timestamp"] = 0
+            res["valid"] = False
+
+    ########################
+    # Write Resources File
+    ########################
+    def WriteResourcesFile(self):
+        with open(self.config.resources_file, 'w') as file:
+            json.dump(self.resources, file)
+
+    ########################
+    # Check Resources
+    ########################
+    def CheckResources(self):
+        if "error" in self.resources:
+            if "error_description" in self.resources:
+                logger.error ("Error retriving resources: " + self.resources["error_description"])
+            else:
+                logger.error ("Error retriving resources: " + self.resources["error"])
+            return False
+        if len(self.resources) == 0:
+            logger.error ("Empty resources found.")
+            return False
+        return True
+
+    ########################
+    # Create Resources File
+    ########################
+    def CreateResourcesFile(self):
+        resName = "resources"
+        resURL = self.config.res_url_prefix + "/vehicles/" + self.config.vin + "/" + resName
+        self.resources = GetResource(resName, resURL, self.config)
+        if "reason" in self.resources:
+            logger.error ("Error retriving available resources - " + self.resources["reason"] + " (" + str(self.resources["code"]) + ")")
+            return False
+        else:
+            self.InitResources()
+            self.WriteResourcesFile()
+            return True
+
+    ########################
+    # Print Available Resources
+    ########################
+    def PrintAvailableResources(self):
+        print ("Found %d resources" % len(self.resources) + ":")
+        for res in self.resources:
+            print (res["name"] + ": " + self.config.res_url_prefix + res["href"])
+
+    ########################
+    # Print Resources Status
+    ########################
+    def PrintResourcesStatus(self, valid = True):
+        for res in self.resources:
+            if((not valid) | res["valid"]):
+                print (res["name"] + ":")
+                print ("\tvalid: " + str(res["valid"]))
+                print ("\tstatus: " + res["status"])
+                print ("\ttimestamp: " + str(res["timestamp"]))
+
+    ########################
+    # Update Resources Status
+    ########################
+    def UpdateResourcesStatus(self):
+        for res in self.resources:
+            resName = res["name"]
+            resURL = self.config.res_url_prefix + res["href"]
+            result = GetResource(resName, resURL, self.config)
+            if not "reason" in result:
+                res["valid"] = True
+                res["timestamp"] = result[resName]["timestamp"]
+                res["status"] = result[resName]["value"]
+        # Write Resource File
+        self.WriteResourcesFile()
\ No newline at end of file