diff --git a/dashmachine/platform/healthchecks.py b/dashmachine/platform/healthchecks.py
new file mode 100644
index 0000000..d2d88fc
--- /dev/null
+++ b/dashmachine/platform/healthchecks.py
@@ -0,0 +1,238 @@
+from flask import render_template_string
+import requests
+
+
+class Healthchecks(object):
+ def __init__(self, method, prefix, host, port, api_key, project, verify):
+ self.endpoint = "/api/v1/checks/"
+ self.method = method
+ self.prefix = prefix
+ self.host = host
+ self.port = port
+ self.api_key = api_key
+ self.project = project
+ self.verify = verify
+
+ # Initialize results
+ self.error = None
+ self.status = ""
+ self.count_checks = 0
+ self.count_up = 0
+ self.count_down = 0
+ self.count_grace = 0
+ self.count_paused = 0
+
+ def check(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint,
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ if "error" in rawdata:
+ self.error = rawdata["error"]
+
+ def getChecks(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint,
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ for check in rawdata["checks"]:
+ self.count_checks += 1
+ if check["status"] == "up":
+ self.count_up += 1
+ if check["status"] == "down":
+ self.count_down += 1
+ if check["status"] == "grace":
+ self.count_grace += 1
+ if check["status"] == "paused":
+ self.count_paused += 1
+
+ if self.count_down > 0:
+ self.status = "down"
+ if self.count_down == 0 and self.count_grace > 0:
+ self.status = "grace"
+ if self.count_down == 0 and self.count_grace == 0:
+ self.status = "up"
+
+ def refresh(self):
+ self.check()
+ if self.error == None:
+ self.error = ""
+ self.getChecks()
+
+
+class Platform:
+ def docs(self):
+ documentation = {
+ "name": "healthchecks",
+ "author": "Thlb",
+ "author_url": "https://github.com/Thlb",
+ "version": 1.0,
+ "description": "Display information from Healthchecks API",
+ "returns": "`value_template` as rendered string",
+ "returns_json_keys": [
+ "status",
+ "count_checks",
+ "count_up",
+ "count_down",
+ "count_grace",
+ "count_paused",
+ "error (for debug)",
+ ],
+ "example": """
+```ini
+[healthchecks-data]
+platform = healthchecks
+prefix = http://
+host = 192.168.0.110
+port = 8080
+api_key = {{ API Key }}
+project = {{ Project name }}
+verify = False
+value_template = {{error}}
fiber_manual_record{{count_up}}fiber_manual_record{{count_grace}}fiber_manual_record{{count_down}}
+
+[Healthchecks]
+prefix = http://
+url = 192.168.0.110
+icon = static/images/apps/healthchecks.png
+description = Healthchecks is a watchdog for your cron jobs. It's a web server that listens for pings from your cron jobs, plus a web interface.
+open_in = this_tab
+data_sources = healthchecks-data
+```
+ """,
+ "variables": [
+ {
+ "variable": "[variable_name]",
+ "description": "Name for the data source.",
+ "default": "None, entry is required",
+ "options": ".ini header",
+ },
+ {
+ "variable": "platform",
+ "description": "Name of the platform.",
+ "default": "healthchecks",
+ "options": "healthchecks",
+ },
+ {
+ "variable": "prefix",
+ "description": "The prefix for the app's url.",
+ "default": "",
+ "options": "web prefix, e.g. http:// or https://",
+ },
+ {
+ "variable": "host",
+ "description": "Healthchecks Host",
+ "default": "",
+ "options": "url,ip",
+ },
+ {
+ "variable": "port",
+ "description": "Healthchecks Port",
+ "default": "",
+ "options": "port",
+ },
+ {
+ "variable": "api_key",
+ "description": "ApiKey",
+ "default": "",
+ "options": "api key",
+ },
+ {
+ "variable": "project",
+ "description": "Healthchecks project name",
+ "default": "",
+ "options": "project name",
+ },
+ {
+ "variable": "verify",
+ "description": "Turn TLS verification on or off, default is true",
+ "default": "",
+ "options": "true,false",
+ },
+ {
+ "variable": "value_template",
+ "description": "Jinja template for how the returned data from API is displayed.",
+ "default": "",
+ "options": "jinja template",
+ },
+ ],
+ }
+ return documentation
+
+ def __init__(self, *args, **kwargs):
+ # parse the user's options from the config entries
+ for key, value in kwargs.items():
+ self.__dict__[key] = value
+
+ # set defaults for omitted options
+ if not hasattr(self, "method"):
+ self.method = "GET"
+ if not hasattr(self, "prefix"):
+ self.prefix = "http://"
+ if not hasattr(self, "host"):
+ self.host = None
+ if not hasattr(self, "port"):
+ self.port = None
+ if not hasattr(self, "api_key"):
+ self.api_key = None
+ if not hasattr(self, "project"):
+ self.project = None
+ if not hasattr(self, "verify"):
+ self.verify = True
+
+ self.healthchecks = Healthchecks(
+ self.method,
+ self.prefix,
+ self.host,
+ self.port,
+ self.api_key,
+ self.project,
+ self.verify,
+ )
+
+ def process(self):
+ if self.api_key == None:
+ return "api_key missing"
+ if self.host == None:
+ return "host missing"
+
+ self.healthchecks.refresh()
+ value_template = render_template_string(
+ self.value_template, **self.healthchecks.__dict__
+ )
+ return value_template
diff --git a/dashmachine/platform/radarr.py b/dashmachine/platform/radarr.py
new file mode 100644
index 0000000..9997650
--- /dev/null
+++ b/dashmachine/platform/radarr.py
@@ -0,0 +1,303 @@
+from flask import render_template_string
+import requests
+
+
+class Radarr(object):
+ def __init__(self, method, prefix, host, port, api_key, verify):
+ self.endpoint = "/api"
+ self.method = method
+ self.prefix = prefix
+ self.host = host
+ self.port = port
+ self.api_key = api_key
+ self.verify = verify
+
+ # Initialize results
+ self.error = None
+ self.version = "?"
+ self.movies = 0
+ self.queue = 0
+ self.diskspace = [
+ {"path": "", "total": "", "free": "", "used": ""},
+ {"path": "", "total": "", "free": "", "used": ""},
+ ]
+
+ def check(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/system/status",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ if "error" in rawdata:
+ self.error = rawdata["error"]
+
+ def getVersion(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/system/status",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.version = rawdata["version"]
+
+ def getMovies(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/movie",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.movies = len(rawdata)
+
+ def getQueue(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/queue",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.queue = len((rawdata))
+
+ def getDiskspace(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/diskspace",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.diskspace = rawdata
+ for item in self.diskspace:
+ item["used"] = self.formatSize(item["totalSpace"] - item["freeSpace"])
+ item["total"] = self.formatSize(item["totalSpace"])
+ item["free"] = self.formatSize(item["freeSpace"])
+ item.pop("totalSpace", None)
+ item.pop("freeSpace", None)
+
+ def formatSize(self, size):
+ # 2**10 = 1024
+ power = 2 ** 10
+ n = 0
+ power_labels = {0: "", 1: "KB", 2: "MB", 3: "GB", 4: "TB"}
+ while size > power:
+ size /= power
+ n += 1
+ return str(round(size, 1)) + " " + power_labels[n]
+
+ def refresh(self):
+ self.check()
+ if self.error == None:
+ self.error = ""
+ self.getVersion()
+ self.getMovies()
+ self.getQueue()
+ self.getDiskspace()
+
+
+class Platform:
+ def docs(self):
+ documentation = {
+ "name": "radarr",
+ "author": "Thlb",
+ "author_url": "https://github.com/Thlb",
+ "version": 1.0,
+ "description": "Display information from Radarr API",
+ "returns": "`value_template` as rendered string",
+ "returns_json_keys": [
+ "version",
+ "movies",
+ "queue",
+ "diskspace[x]['path']",
+ "diskspace[x]['total']",
+ "diskspace[x]['used']",
+ "diskspace[x]['free']",
+ "error (for debug)",
+ ],
+ "example": """
+```ini
+[radarr-data]
+platform = radarr
+prefix = http://
+host = 192.168.0.110
+port = 7878
+api_key = {{ API Key }}
+verify = False
+value_template = {{error}}Movies : {{movies}}
Queue : {{queue}}
Free ({{diskspace[0]['path']}}) : {{diskspace[0]['free']}}
+
+[Radarr]
+prefix = http://
+url = 192.168.0.110:7878
+icon = static/images/apps/radarr.png
+sidebar_icon = static/images/apps/radarr.png
+description = A fork of Sonarr to work with movies à la Couchpotato
+open_in = this_tab
+data_sources = radarr-data
+```
+ """,
+ "variables": [
+ {
+ "variable": "[variable_name]",
+ "description": "Name for the data source.",
+ "default": "None, entry is required",
+ "options": ".ini header",
+ },
+ {
+ "variable": "platform",
+ "description": "Name of the platform.",
+ "default": "radarr",
+ "options": "radarr",
+ },
+ {
+ "variable": "prefix",
+ "description": "The prefix for the app's url.",
+ "default": "",
+ "options": "web prefix, e.g. http:// or https://",
+ },
+ {
+ "variable": "host",
+ "description": "Radarr Host",
+ "default": "",
+ "options": "url,ip",
+ },
+ {
+ "variable": "port",
+ "description": "Radarr Port",
+ "default": "",
+ "options": "port",
+ },
+ {
+ "variable": "api_key",
+ "description": "ApiKey",
+ "default": "",
+ "options": "api key",
+ },
+ {
+ "variable": "verify",
+ "description": "Turn TLS verification on or off, default is true",
+ "default": "",
+ "options": "true,false",
+ },
+ {
+ "variable": "value_template",
+ "description": "Jinja template for how the returned data from API is displayed.",
+ "default": "",
+ "options": "jinja template",
+ },
+ ],
+ }
+ return documentation
+
+ def __init__(self, *args, **kwargs):
+ # parse the user's options from the config entries
+ for key, value in kwargs.items():
+ self.__dict__[key] = value
+
+ # set defaults for omitted options
+ if not hasattr(self, "method"):
+ self.method = "GET"
+ if not hasattr(self, "prefix"):
+ self.prefix = "http://"
+ if not hasattr(self, "host"):
+ self.host = None
+ if not hasattr(self, "port"):
+ self.port = None
+ if not hasattr(self, "api_key"):
+ self.api_key = None
+ if not hasattr(self, "verify"):
+ self.verify = True
+
+ self.radarr = Radarr(
+ self.method, self.prefix, self.host, self.port, self.api_key, self.verify
+ )
+
+ def process(self):
+ if self.api_key == None:
+ return "api_key missing"
+ if self.host == None:
+ return "host missing"
+
+ self.radarr.refresh()
+ value_template = render_template_string(
+ self.value_template, **self.radarr.__dict__
+ )
+ return value_template
diff --git a/dashmachine/platform/sonarr.py b/dashmachine/platform/sonarr.py
new file mode 100644
index 0000000..b7a8ed0
--- /dev/null
+++ b/dashmachine/platform/sonarr.py
@@ -0,0 +1,303 @@
+from flask import render_template_string
+import requests
+
+
+class Sonarr(object):
+ def __init__(self, method, prefix, host, port, api_key, verify):
+ self.endpoint = "/api"
+ self.method = method
+ self.prefix = prefix
+ self.host = host
+ self.port = port
+ self.api_key = api_key
+ self.verify = verify
+
+ # Initialize results
+ self.error = None
+ self.version = "?"
+ self.wanted_missing = 0
+ self.queue = 0
+ self.diskspace = [
+ {"path": "", "total": "", "free": "", "used": ""},
+ {"path": "", "total": "", "free": "", "used": ""},
+ ]
+
+ def check(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/system/status",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ if "error" in rawdata:
+ self.error = rawdata["error"]
+
+ def getVersion(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/system/status",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.version = rawdata["version"]
+
+ def getWanted(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/wanted/missing/",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.wanted_missing = rawdata["totalRecords"]
+
+ def getQueue(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/queue",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.queue = len(rawdata)
+
+ def getDiskspace(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix + self.host + port + self.endpoint + "/diskspace",
+ headers=headers,
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.diskspace = rawdata
+ for item in self.diskspace:
+ item["used"] = self.formatSize(item["totalSpace"] - item["freeSpace"])
+ item["total"] = self.formatSize(item["totalSpace"])
+ item["free"] = self.formatSize(item["freeSpace"])
+ item.pop("totalSpace", None)
+ item.pop("freeSpace", None)
+
+ def formatSize(self, size):
+ # 2**10 = 1024
+ power = 2 ** 10
+ n = 0
+ power_labels = {0: "", 1: "KB", 2: "MB", 3: "GB", 4: "TB"}
+ while size > power:
+ size /= power
+ n += 1
+ return str(round(size, 1)) + " " + power_labels[n]
+
+ def refresh(self):
+ self.check()
+ if self.error == None:
+ self.error = ""
+ self.getVersion()
+ self.getWanted()
+ self.getQueue()
+ self.getDiskspace()
+
+
+class Platform:
+ def docs(self):
+ documentation = {
+ "name": "sonarr",
+ "author": "Thlb",
+ "author_url": "https://github.com/Thlb",
+ "version": 1.0,
+ "description": "Display information from Sonarr API",
+ "returns": "`value_template` as rendered string",
+ "returns_json_keys": [
+ "version",
+ "wanted_missing",
+ "queue",
+ "diskspace[x]['path']",
+ "diskspace[x]['total']",
+ "diskspace[x]['used']",
+ "diskspace[x]['free']",
+ "error (for debug)",
+ ],
+ "example": """
+```ini
+[sonarr-data]
+platform = sonarr
+prefix = http://
+host = 192.168.0.110
+port = 8989
+api_key = {{ API Key }}
+verify = False
+value_template = {{error}}Missing : {{wanted_missing}}
Queue : {{queue}}
Free ({{diskspace[0]['path']}}) : {{diskspace[0]['free']}}
+
+[Sonarr]
+prefix = http://
+url = 192.168.0.110:8989
+icon = static/images/apps/sonarr.png
+sidebar_icon = static/images/apps/sonarr.png
+description = Smart PVR for newsgroup and bittorrent users
+open_in = this_tab
+data_sources = sonarr-data
+```
+ """,
+ "variables": [
+ {
+ "variable": "[variable_name]",
+ "description": "Name for the data source.",
+ "default": "None, entry is required",
+ "options": ".ini header",
+ },
+ {
+ "variable": "platform",
+ "description": "Name of the platform.",
+ "default": "sonarr",
+ "options": "sonarr",
+ },
+ {
+ "variable": "prefix",
+ "description": "The prefix for the app's url.",
+ "default": "",
+ "options": "web prefix, e.g. http:// or https://",
+ },
+ {
+ "variable": "host",
+ "description": "Sonarr Host",
+ "default": "",
+ "options": "url,ip",
+ },
+ {
+ "variable": "port",
+ "description": "Sonarr Port",
+ "default": "",
+ "options": "port",
+ },
+ {
+ "variable": "api_key",
+ "description": "ApiKey",
+ "default": "",
+ "options": "api key",
+ },
+ {
+ "variable": "verify",
+ "description": "Turn TLS verification on or off, default is true",
+ "default": "",
+ "options": "true,false",
+ },
+ {
+ "variable": "value_template",
+ "description": "Jinja template for how the returned data from API is displayed.",
+ "default": "",
+ "options": "jinja template",
+ },
+ ],
+ }
+ return documentation
+
+ def __init__(self, *args, **kwargs):
+ # parse the user's options from the config entries
+ for key, value in kwargs.items():
+ self.__dict__[key] = value
+
+ # set defaults for omitted options
+ if not hasattr(self, "method"):
+ self.method = "GET"
+ if not hasattr(self, "prefix"):
+ self.prefix = "http://"
+ if not hasattr(self, "host"):
+ self.host = None
+ if not hasattr(self, "port"):
+ self.port = None
+ if not hasattr(self, "api_key"):
+ self.api_key = None
+ if not hasattr(self, "verify"):
+ self.verify = True
+
+ self.sonarr = Sonarr(
+ self.method, self.prefix, self.host, self.port, self.api_key, self.verify
+ )
+
+ def process(self):
+ if self.api_key == None:
+ return "api_key missing"
+ if self.host == None:
+ return "host missing"
+
+ self.sonarr.refresh()
+ value_template = render_template_string(
+ self.value_template, **self.sonarr.__dict__
+ )
+ return value_template
diff --git a/dashmachine/platform/tautulli.py b/dashmachine/platform/tautulli.py
new file mode 100644
index 0000000..7df565d
--- /dev/null
+++ b/dashmachine/platform/tautulli.py
@@ -0,0 +1,260 @@
+from flask import render_template_string
+import requests
+
+
+class Tautulli(object):
+ def __init__(self, method, prefix, host, port, api_key, verify):
+ self.endpoint = "/api/v2"
+ self.method = method
+ self.prefix = prefix
+ self.host = host
+ self.port = port
+ self.api_key = api_key
+ self.verify = verify
+
+ # Initialize results
+ self.error = None
+ self.update_available = ""
+ self.update_message = ""
+ self.stream_count = ""
+
+ def check(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ headers = {"X-Api-Key": self.api_key}
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix
+ + self.host
+ + port
+ + self.endpoint
+ + "?apikey="
+ + self.api_key
+ + "&cmd="
+ + "update_check",
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ if "response" in rawdata and rawdata["response"]["result"] == "error":
+ self.error = rawdata["response"]["message"]
+
+ def getUpdate(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix
+ + self.host
+ + port
+ + self.endpoint
+ + "?apikey="
+ + self.api_key
+ + "&cmd="
+ + "update_check",
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.update_message = rawdata["response"]["message"]
+ self.update_available = rawdata["response"]["data"]["update"]
+
+ def getActivity(self):
+ verify = (
+ False
+ if str(self.verify).lower() == "false"
+ or str(self.prefix).lower() == "http://"
+ else True
+ )
+ port = "" if self.port == None else ":" + self.port
+
+ if self.method.upper() == "GET":
+ try:
+ rawdata = requests.get(
+ self.prefix
+ + self.host
+ + port
+ + self.endpoint
+ + "?apikey="
+ + self.api_key
+ + "&cmd="
+ + "get_activity",
+ verify=verify,
+ timeout=10,
+ ).json()
+ except Exception as e:
+ rawdata = None
+ self.error = f"{e}"
+
+ if rawdata != None:
+ self.stream_count = rawdata["response"]["data"]["stream_count"]
+ self.stream_count_direct_play = rawdata["response"]["data"][
+ "stream_count_direct_play"
+ ]
+ self.stream_count_direct_stream = rawdata["response"]["data"][
+ "stream_count_direct_stream"
+ ]
+ self.stream_count_transcode = rawdata["response"]["data"][
+ "stream_count_transcode"
+ ]
+ self.total_bandwidth = rawdata["response"]["data"]["total_bandwidth"]
+ self.wan_bandwidth = rawdata["response"]["data"]["wan_bandwidth"]
+
+ def refresh(self):
+ self.check()
+ if self.error == None:
+ self.error = ""
+ self.getUpdate()
+ self.getActivity()
+
+
+class Platform:
+ def docs(self):
+ documentation = {
+ "name": "tautulli",
+ "author": "Thlb",
+ "author_url": "https://github.com/Thlb",
+ "version": 1.0,
+ "description": "Display information from Tautulli API",
+ "returns": "`value_template` as rendered string",
+ "returns_json_keys": [
+ "stream_count",
+ "stream_count_direct_play",
+ "stream_count_direct_stream",
+ "stream_count_transcode",
+ "total_bandwidth",
+ "wan_bandwidth",
+ "update_available",
+ "update_message",
+ "error (for debug)",
+ ],
+ "example": """
+```ini
+[tautulli-data]
+platform = tautulli
+prefix = http://
+host = 192.168.0.110
+port = 8181
+api_key = myApiKey
+verify = False
+value_template = {{error}}Active sessions : {{stream_count}}
+
+[Tautulli]
+prefix = http://
+url = 192.168.0.110:8181
+icon = static/images/apps/tautulli.png
+sidebar_icon = static/images/apps/tautulli.png
+description = A Python based monitoring and tracking tool for Plex Media Server
+open_in = this_tab
+data_sources = tautulli-data
+```
+ """,
+ "variables": [
+ {
+ "variable": "[variable_name]",
+ "description": "Name for the data source.",
+ "default": "None, entry is required",
+ "options": ".ini header",
+ },
+ {
+ "variable": "platform",
+ "description": "Name of the platform.",
+ "default": "tautulli",
+ "options": "tautulli",
+ },
+ {
+ "variable": "prefix",
+ "description": "The prefix for the app's url.",
+ "default": "",
+ "options": "web prefix, e.g. http:// or https://",
+ },
+ {
+ "variable": "host",
+ "description": "Tautulli Host",
+ "default": "",
+ "options": "url,ip",
+ },
+ {
+ "variable": "port",
+ "description": "Tautulli Port",
+ "default": "",
+ "options": "port",
+ },
+ {
+ "variable": "api_key",
+ "description": "ApiKey",
+ "default": "",
+ "options": "api key",
+ },
+ {
+ "variable": "verify",
+ "description": "Turn TLS verification on or off, default is true",
+ "default": "",
+ "options": "true,false",
+ },
+ {
+ "variable": "value_template",
+ "description": "Jinja template for how the returned data from API is displayed.",
+ "default": "",
+ "options": "jinja template",
+ },
+ ],
+ }
+ return documentation
+
+ def __init__(self, *args, **kwargs):
+ # parse the user's options from the config entries
+ for key, value in kwargs.items():
+ self.__dict__[key] = value
+
+ # set defaults for omitted options
+ if not hasattr(self, "method"):
+ self.method = "GET"
+ if not hasattr(self, "prefix"):
+ self.prefix = "http://"
+ if not hasattr(self, "host"):
+ self.host = None
+ if not hasattr(self, "port"):
+ self.port = None
+ if not hasattr(self, "api_key"):
+ self.api_key = None
+ if not hasattr(self, "verify"):
+ self.verify = True
+
+ self.tautulli = Tautulli(
+ self.method, self.prefix, self.host, self.port, self.api_key, self.verify
+ )
+
+ def process(self):
+ if self.api_key == None:
+ return "api_key missing"
+ if self.host == None:
+ return "host missing"
+
+ self.tautulli.refresh()
+ value_template = render_template_string(
+ self.value_template, **self.tautulli.__dict__
+ )
+ return value_template