Compare commits

..

No commits in common. "master" and "v0.5-4" have entirely different histories.

13 changed files with 38 additions and 1784 deletions

View File

@ -1,26 +1,8 @@
# DashMachine
### Another web application bookmark dashboard, with fun features.
![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/dashmachine?style=social)
![GitHub last commit](https://img.shields.io/github/last-commit/rmountjoy92/dashmachine)
![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/rmountjoy/dashmachine)
![Docker Pulls](https://img.shields.io/docker/pulls/rmountjoy/dashmachine)
![GitHub Repo stars](https://img.shields.io/github/stars/rmountjoy92/dashmachine?style=social)
![GitHub repo size](https://img.shields.io/github/repo-size/rmountjoy92/dashmachine)
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/rmountjoy/dashmachine/latest?label=Docker%20Image%20Size)
![Lines of code](https://img.shields.io/tokei/lines/github/rmountjoy92/dashmachine)
[![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-yellow.svg)](https://opensource.org/licenses/)
[![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome)
[![Donate](https://img.shields.io/badge/$-support-ff69b4.svg?style=flat)](https://liberapay.com/rmountjoy)
![Bountysource](https://img.shields.io/bountysource/team/dashmachine/activity)
Want a feature added now? [Open a bounty](https://www.bountysource.com/teams/dashmachine-app)
## Screenshots
## Before Installing
Please read the latest update post: https://www.reddit.com/r/DashMachine/comments/fqk8gl/version_05/
![screenshot](https://raw.githubusercontent.com/rmountjoy92/DashMachine/master/screenshot1.png)
@ -30,6 +12,20 @@ Want a feature added now? [Open a bounty](https://www.bountysource.com/teams/das
![screenshot](https://raw.githubusercontent.com/rmountjoy92/DashMachine/master/screenshot4.png)
### Features
* creates a dashboard to view web pages
* uses a single .ini file for configuration
* dark mode/light mode and accent colors
* custom backgrounds and icons
* web interface to edit the config file and add image files
* ability to open web pages in current tab, new tab or iframe
* hideable sidebar with dragable reveal button
* user login system
* 'app templates' which are sample config entries for popular self hosted apps
* powerful plugin system for adding data from various sources to display on cards
* multiple card types including collections and custom cards
* multiple users, access groups, access settings
* tagging system
## Installation
### Docker
@ -86,13 +82,27 @@ https://github.com/rmountjoy92/DashMachine/blob/master/pull_request_template.md
See this link for how to create a pull request:
https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request
## Subreddit
https://www.reddit.com/r/DashMachine
## Want to buy me a coffee?
recurring:
<a href="https://liberapay.com/rmountjoy/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
recurring or one-time:
https://www.bountysource.com/teams/dashmachine-app
## Want a feature to be added faster?
Open a bounty on https://www.bountysource.com/
Bountysource faq: https://github.com/bountysource/core/wiki/Frequently-Asked-Questions
## Tech used
* Flask (Python 3)
* SQLalchemy w/ SQLite database
* HTML5/Jinja2
* Flask
* SQLalchemy w/ SQLite
* Jinja2
* Materialize css
* JavaScript/jQuery/jQueryUI
* .ini (for configuration)
## FAQs
1. application does not work in iframe

View File

@ -1,6 +1,5 @@
#!/usr/bin/env python3
import os
import uuid
from flask import Flask
from flask_caching import Cache
from flask_sqlalchemy import SQLAlchemy
@ -12,23 +11,13 @@ from dashmachine.paths import user_data_folder
if not os.path.isdir(user_data_folder):
os.mkdir(user_data_folder)
secret_file = os.path.join(user_data_folder, ".secret")
if not os.path.isfile(secret_file):
with open(secret_file, "w") as new_file:
new_file.write(uuid.uuid4().hex)
with open(secret_file, "r") as secret_file:
secret_key = secret_file.read().encode("utf-8")
if len(secret_key) < 32:
secret_key = uuid.uuid4().hex
context_path = os.getenv("CONTEXT_PATH", "")
app = Flask(__name__, static_url_path=context_path + "/static")
cache = Cache(app, config={"CACHE_TYPE": "simple"})
api = Api(app)
app.config["AVATARS_IDENTICON_BG"] = (255, 255, 255)
app.config["SECRET_KEY"] = secret_key
app.config["SECRET_KEY"] = "66532a62c4048f976e22a39638b6f10e"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///user_data/site.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 0

View File

@ -1,394 +0,0 @@
"""
##### Docker
Display information from Docker API. Informations can be displayed on a custom card or on an app card (e.g. Portainer App)
```ini
[variable_name]
platform = docker
prefix = http://
host = localhost
port = 2375
value_template = {{ value_template }}
```
> **Returns:** `value_template` as rendered string
| Variable | Required | Description | Options |
|-----------------|----------|-----------------------------------------------------------------|-------------------|
| [variable_name] | Yes | Name for the data source. | [variable_name] |
| platform | Yes | Name of the platform. | docker |
| prefix | No | The prefix for the app's url. | web prefix, e.g. http:// or https:// |
| host | Yes | Docker Host | url,ip |
| port | No | Docker Port | port, usually 2375 (Insecure) or 2376 (TLS) |
| api_version | No | Docker API version to use (Default : platform will try to find latest version) | 1.40 |
| tls_mode | No | TLS verification mode, default is None | Server, Client, Both, None |
| tls_ca | No | Requierd for tls_mode=Both or tls_mode=Server, default is None | /path/to/ca, None |
| tls_cert | No | Requierd for tls_mode=Both or tls_mode=Client, default is None | /path/to/cert, None |
| tls_key | No | Requierd for tls_mode=Both or tls_mode=Client, default is None | /path/to/key, None|
| card_type | No | Set to Custom if you want to display informations in a custom card. Default is App | Custom, App|
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
<br />
###### **Available fields for value_template**
* version
* max_api_version
* name
* containers
* containers_running
* containers_paused
* containers_stopped
* images
* driver
* cpu
* memory
* warnings
* error (for debug)
> **Working example (using un-encrypted connection, on Portainer card):**
>```ini
> [docker-endpoint-1]
> platform = docker
> prefix = http://
> host = 192.168.0.110
> port = 2375
> value_template = {{error}}<p style="text-align:right;text-transform:uppercase;font-size:14px;font-family: monospace;">{{name}}<br /><i style="position: relative; top: .2rem" class="material-icons md-18 theme-success-text" title="Running">fiber_manual_record</i>{{containers_running}}<i style="position: relative; top: .2rem" class="material-icons md-18 theme-warning-text" title="Paused">fiber_manual_record</i>{{containers_paused}}<i style="position: relative; top: .2rem" class="material-icons md-18 theme-failure-text" title="Stopped">fiber_manual_record</i>{{containers_stopped}}</p>
>
> [Portainer]
> prefix = http://
> url = 192.168.0.110:2375
> icon = static/images/apps/portainer.png
> sidebar_icon = static/images/apps/portainer.png
> description = Making Docker management easy
> open_in = this_tab
> data_sources = docker-endpoint-1
>```
>
>
> **Working example (using encrypted connection, on Portainer card):**
>```ini
> [docker-endpoint-2]
> platform = docker
> prefix = https://
> host = 192.168.0.110
> port = 2376
> tls_mode = Both
> tls_ca = /path/to/ca_file
> tls_cert = /path/to/cert_file
> tls_key = /path/to/key_file
> value_template = {{error}}<p style="text-align:right;text-transform:uppercase;font-size:14px;font-family: monospace;">{{name}}<br /><i style="position: relative; top: .2rem" class="material-icons md-18 theme-success-text" title="Running">fiber_manual_record</i>{{containers_running}}<i style="position: relative; top: .2rem" class="material-icons md-18 theme-warning-text" title="Paused">fiber_manual_record</i>{{containers_paused}}<i style="position: relative; top: .2rem" class="material-icons md-18 theme-failure-text" title="Stopped">fiber_manual_record</i>{{containers_stopped}}</p>
>
> [Portainer]
> prefix = http://
> url = 192.168.0.110:2375
> icon = static/images/apps/portainer.png
> sidebar_icon = static/images/apps/portainer.png
> description = Making Docker management easy
> open_in = this_tab
> data_sources = docker-endpoint-2
>```
>
>
> **Working example (using un-encrypted connection, on custom Docker card):**
>```ini
> [docker-endpoint-3]
> platform = docker
> prefix = http://
> host = 192.168.0.110
> port = 2375
> card_type = Custom
>
> [Docker]
> type = custom
> data_sources = docker-endpoint-3
>```
"""
import json
from flask import render_template_string
import requests
import re
class Docker(object):
def __init__(
self,
method,
prefix,
host,
port,
api_version,
card_type,
tls_mode,
tls_ca,
tls_cert,
tls_key,
):
self.endpoint = None
self.method = method
self.prefix = prefix
self.host = host
self.port = port
self.api_version = api_version
self.card_type = card_type
self.tls_mode = tls_mode
self.tls_ca = tls_ca
self.tls_key = tls_key
self.tls_cert = tls_cert
# Initialize results
self.error = None
self.version = "?"
self.max_api_version = "?"
self.name = "?"
self.running = 0
self.paused = 0
self.stopped = 0
self.images = 0
self.driver = "?"
self.cpu = "?"
self.memory = "?"
self.html_template = ""
def check(self):
port = "" if self.port == None else ":" + self.port
if self.method.upper() == "GET":
try:
response = ""
request = requests.get(
self.prefix + self.host + port + "/v999/info",
verify=self.tls_ca,
cert=(self.tls_cert, self.tls_key),
timeout=10,
)
response = request.text
if "text/plain" in request.headers["content-type"]:
self.error = request.text
rawdata = None
elif "application/json" in request.headers["content-type"]:
rawdata = request.json()
else:
error = request
rawdata = None
except Exception as e:
rawdata = None
self.error = f"{e}" + " " + response
self.setHtml()
if rawdata != None:
if "message" in rawdata:
regex = r"\bv?[0-9]+\.[0-9]+(?:\.[0-9]+)?\b"
r = re.search(regex, rawdata["message"])
self.max_api_version = r.group(0)
self.api_version = (
self.api_version
if self.api_version != None
else self.max_api_version
)
self.endpoint = "/v" + self.api_version + "/"
def getStatus(self):
port = "" if self.port == None else ":" + self.port
if self.method.upper() == "GET":
try:
rawdata = requests.get(
self.prefix + self.host + port + self.endpoint + "/info",
verify=self.tls_ca,
cert=(self.tls_cert, self.tls_key),
timeout=10,
).json()
except Exception as e:
rawdata = None
self.error = f"{e}"
self.setHtml()
if rawdata != None:
self.name = rawdata["Name"]
self.containers = rawdata["Containers"]
self.containers_running = rawdata["ContainersRunning"]
self.containers_paused = rawdata["ContainersPaused"]
self.containers_stopped = rawdata["ContainersStopped"]
self.images = rawdata["Images"]
self.warnings = rawdata["Warnings"]
self.driver = rawdata["Driver"]
self.cpu = rawdata["NCPU"]
self.memory = self.formatSize(rawdata["MemTotal"])
if self.card_type == "Custom":
self.setHtml()
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.getStatus()
def setHtml(self):
if self.error != None and self.error != "":
self.html_template = """
<div class="row">
<div class="col s6">
<span class="mt-0 mb-0 theme-primary-text font-weight-700" style="font-size: 36px"><i class="material-icons md-18 theme-failure-text" title="Error">error</i></h3>
</div>
<div class="col s6 right-align">
<img height="48px" src="static/images/apps/docker.png" alt="Docker">
</div>
</div>
<div class="row">
<h6 class="font-weight-900 center theme-muted-text">Error</h6>
</div>
<div class="row center-align">
<i class="material-icons-outlined">keyboard_arrow_down</i>
</div>
<div class="row center-align">
<div class="col s12">
<div class="collection theme-muted-text">
<div class="collection-item">{{ error }}</div>
</div>
</div>
</div>
"""
else:
if self.tls_mode == None:
img_tls = """
<i class="material-icons md-18 theme-warning-text" title="TLS disabled">lock_open</i>
"""
else:
img_tls = """
<i class="material-icons md-18 theme-success-text" title="TLS enabled">lock</i>
"""
if len(self.warnings) > 0:
img_warnings = """
<i class="material-icons md-18 theme-warning-text" title="{{warnings}}">warning</i>
"""
else:
img_warnings = """
<i class="material-icons md-18 theme-muted2-text" title="No warnings">warning</i>
"""
self.html_template = (
"""
<div class="row">
<div class="col s6">
<span class="mt-0 mb-0 theme-primary-text font-weight-700" style="font-size: 36px">"""
+ img_tls
+ img_warnings
+ """</h3>
</div>
<div class="col s6 right-align">
<img height="48px" src="static/images/apps/docker.png" alt="Docker">
</div>
</div>
<div class="row">
<h6 class="font-weight-900 center theme-muted-text">{{name}}</h6>
</div>
<div class="row center-align">
<i class="material-icons-outlined">keyboard_arrow_down</i>
</div>
<div class="row center-align">
<div class="col s12">
<div class="collection theme-muted-text">
<div class="collection-item"><span class="font-weight-900">Containers: </span>{{ containers }}</div>
<div class="collection-item"><span class="font-weight-900">Running: </span>{{ containers_running }}</div>
<div class="collection-item"><span class="font-weight-900">Paused: </span>{{ containers_paused }}</div>
<div class="collection-item"><span class="font-weight-900">Stopped: </span>{{ containers_stopped }}</div>
<div class="collection-item"><span class="font-weight-900">Images: </span>{{ images }}</div>
<div class="collection-item"><span class="font-weight-900">Driver: </span>{{ driver }}</div>
<div class="collection-item"><span class="font-weight-900">CPU: </span>{{ cpu }}</div>
<div class="collection-item"><span class="font-weight-900">Memory: </span>{{ memory }}</div>
</div>
</div>
</div>
"""
)
def getHtml(self):
return self.html_template
class Platform:
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 = 2375
if not hasattr(self, "api_version"):
self.api_version = None
if not hasattr(self, "card_type"):
self.card_type = "App"
if not hasattr(self, "tls_ca"):
self.tls_ca = None
if not hasattr(self, "tls_cert"):
self.tls_cert = None
if not hasattr(self, "tls_key"):
self.tls_key = None
# Without TLS
if not hasattr(self, "tls_mode"):
self.tls_mode = None
self.tls_ca = None
self.tls_cert = None
self.tls_key = None
else:
if self.tls_mode == "Both":
if self.tls_ca == None or self.tls_cert == None or self.tls_key == None:
return "tls_mode set to Both, and missing tls_ca/tls_cert/tls_key"
elif self.tls_mode == "Client":
self.tls_ca = False
elif self.tls_mode == "Server":
self.tls_cert = ""
self.tls_key = ""
elif self.tls_mode == "None":
self.tls_ca = None
self.tls_cert = None
self.tls_key = None
self.docker = Docker(
self.method,
self.prefix,
self.host,
self.port,
self.api_version,
self.card_type,
self.tls_mode,
self.tls_ca,
self.tls_cert,
self.tls_key,
)
def process(self):
if self.host == None:
return "host missing"
# TLS check
if self.tls_mode == "Both":
if self.tls_ca == None or self.tls_cert == None or self.tls_key == None:
return "tls_mode set to Both, and missing tls_ca/tls_cert/tls_key"
elif self.tls_mode == "Client":
if self.tls_cert == None or self.tls_key == None:
return "tls_mode set to Client, and missing tls_cert/tls_key"
elif self.tls_mode == "Server":
if self.tls_ca == None:
return "tls_mode set to Server, and missing tls_ca"
else:
if self.tls_mode != None:
return "Invalid tls_mode : " + self.tls_mode
self.docker.refresh()
if self.card_type == "Custom":
return render_template_string(self.docker.getHtml(), **self.docker.__dict__)
else:
return render_template_string(self.value_template, **self.docker.__dict__)

View File

@ -1,199 +0,0 @@
"""
##### Healthchecks
Display information from Healthchecks API
```ini
[variable_name]
platform = healthchecks
prefix = http://
host = localhost
port = 8080
api_key = {{ Healthchecks project API Key }}
project = {{ Healthchecks project name }}
verify = true
value_template = {{ value_template }}
```
> **Returns:** `value_template` as rendered string
| Variable | Required | Description | Options |
|-----------------|----------|-----------------------------------------------------------------|-------------------|
| [variable_name] | Yes | Name for the data source. | [variable_name] |
| platform | Yes | Name of the platform. | healthchecks |
| prefix | No | The prefix for the app's url. | web prefix, e.g. http:// or https:// |
| host | Yes | Healthchecks Host | url,ip |
| port | No | Healthchecks Port | port |
| api_key | Yes | ApiKey | api key |
| project | No | Healthchecks project name | project |
| verify | No | Turn TLS verification on or off, default is true | true,false |
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
<br />
###### **Available fields for value_template**
* status
* count_checks
* count_up
* count_down
* count_grace
* count_paused
* error (for debug)
> **Working 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}}<p style="text-align:right;text-transform:uppercase;font-size:14px;font-family: monospace;"><i style="position: relative; top: .2rem" class="material-icons md-18 theme-success-text" title="Up">fiber_manual_record</i>{{count_up}}<i style="position: relative; top: .2rem" class="material-icons md-18 theme-warning-text" title="Grace">fiber_manual_record</i>{{count_grace}}<i style="position: relative; top: .2rem" class="material-icons md-18 theme-failure-text" title="Down">fiber_manual_record</i>{{count_down}}</p>
>
> [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
>```
"""
import json
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 __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

View File

@ -65,7 +65,7 @@ class Platform:
self.return_codes = "2xx,3xx"
if not hasattr(self, "ssl_ignore"):
self.ssl_ignore = "No"
def process(self):
# Check if method is within allowed methods for http_status
if self.method.upper() not in ["GET", "HEAD", "OPTIONS", "TRACE"]:
@ -87,7 +87,7 @@ class Platform:
)
prepped = req.prepare()
if self.ssl_ignore == "yes":
resp = s.send(prepped, verify=False)
resp = s.send(prepped,verify=False)
else:
resp = s.send(prepped)
resp = s.send(prepped)

View File

@ -1,290 +0,0 @@
"""
##### Lidarr
Display information from Lidarr API
```ini
[variable_name]
platform = lidarr
prefix = http://
host = localhost
port = 8686
api_key = my_api_key
verify = true
value_template = {{ value_template }}
```
> **Returns:** `value_template` as rendered string
| Variable | Required | Description | Options |
|-----------------|----------|-----------------------------------------------------------------|-------------------|
| [variable_name] | Yes | Name for the data source. | [variable_name] |
| platform | Yes | Name of the platform. | lidarr |
| prefix | No | The prefix for the app's url. | web prefix, e.g. http:// or https:// |
| host | Yes | Lidarr Host | url,ip |
| port | No | Lidarr Port | port |
| api_key | Yes | ApiKey | api key |
| api_version | No | API version (default : v1) | v1 |
| verify | No | Turn TLS verification on or off, default is true | true,false |
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
<br />
###### **Available fields for value_template**
* version
* wanted_missing
* wanted_cutoff
* queue
* diskspace[x]['path']
* diskspace[x]['total']
* diskspace[x]['used']
* diskspace[x]['free']
* error (for debug)
> **Working example:**
>```ini
> [lidarr-data]
> platform = lidarr
> prefix = http://
> host = 192.168.0.110
> port = 8686
> api_key = {{ API Key }}
> verify = False
> value_template = {{error}}Missing : {{wanted_missing}}<br />Queue : {{queue}} <br />Free ({{diskspace[0]['path']}}) : {{diskspace[0]['free']}}
>
> [Lidarr]
> prefix = http://
> url = 192.168.0.110:8686
> icon = static/images/apps/lidarr.png
> sidebar_icon = static/images/apps/lidarr.png
> description = Looks and smells like Sonarr but made for music
> open_in = this_tab
> data_sources = lidarr-data
>```
"""
import json
from flask import render_template_string
import requests
class Lidarr(object):
def __init__(self, method, prefix, host, port, api_key, api_version, verify):
self.api_version = api_version
self.endpoint = "/api/" + self.api_version
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.wanted_cutoff = 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, wType):
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/"
+ wType
+ "/",
headers=headers,
verify=verify,
timeout=10,
).json()
except Exception as e:
rawdata = None
self.error = f"{e}"
if rawdata != None:
if wType == "missing":
self.wanted_missing = rawdata["totalRecords"]
else:
self.wanted_cutoff = 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 = rawdata["totalRecords"]
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("missing")
self.getWanted("cutoff")
self.getQueue()
self.getDiskspace()
class Platform:
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, "api_version"):
self.api_version = "v1"
if not hasattr(self, "verify"):
self.verify = True
self.lidarr = Lidarr(
self.method,
self.prefix,
self.host,
self.port,
self.api_key,
self.api_version,
self.verify,
)
def process(self):
if self.api_key == None:
return "api_key missing"
if self.host == None:
return "host missing"
self.lidarr.refresh()
value_template = render_template_string(
self.value_template, **self.lidarr.__dict__
)
return value_template

View File

@ -1,95 +0,0 @@
"""
##### Plex Media Server
Connect to Plex Media Server and see current sessions details
```ini
[variable_name]
platform = plex
url = http://plex_host:plex_port
token = plex_token
value_template = {{ value_template }}
```
> **Returns:** `value_template` as rendered string
| Variable | Required | Description | Options |
|-----------------|----------|-----------------------------------------------------------------|-------------------|
| [variable_name] | Yes | Name for the data source. | [variable_name] |
| platform | Yes | Name of the platform. | plex |
| host | Yes | URL of Plex Media Server (include port, normally 32400) | url |
| token | Yes | X-Plex-Token (See [here](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/) for how to find it.) | string |
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
<br />
###### **Available fields for value_template**
* sessions
* transcodes
* libraries
> **Example:**
>```ini
>[plex]
>platform = plex
>host = http://plex.example.com:32400
>token = abcde_fghi_jklmnopqr
>value_template = Sessions: {{sessions}}<br />Transcodes: {{transcodes}}
>
>[Plex]
>prefix = http://
>url = plex.example.com:32400
>icon = static/images/apps/plex.png
>description = Plex data sources example
>open_in = this_tab
>data_sources = plex
>```
"""
import requests
from flask import render_template_string
json_header = {"Accept": "application/json"}
class Plex(object):
def __init__(self, url, token):
self.url = url
self.token = token
def refresh(self):
if self.token != None:
sessions = requests.get(
self.url + "/status/sessions?X-Plex-Token=" + self.token,
headers=json_header,
).json()
self.sessions = sessions["MediaContainer"]["size"]
transcodes = requests.get(
self.url + "/transcode/sessions?X-Plex-Token=" + self.token,
headers=json_header,
).json()
self.transcodes = transcodes["MediaContainer"]["size"]
libraries = requests.get(
self.url + "/library/sections?X-Plex-Token=" + self.token,
headers=json_header,
).json()
self.libraries = libraries["MediaContainer"]["size"]
class Platform:
def __init__(self, *args, **kwargs):
# parse the user's options from the config entries
for key, value in kwargs.items():
self.__dict__[key] = value
if not hasattr(self, "token"):
print(
"Please add a token\nSee https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/ to find it."
)
exit(1)
else:
self.plex = Plex(self.host, self.token)
def process(self):
self.plex.refresh()
value_template = render_template_string(
self.value_template, **self.plex.__dict__
)
return value_template

View File

@ -1,268 +0,0 @@
"""
##### Radarr
Display information from Radarr API
```ini
[variable_name]
platform = radarr
prefix = http://
host = localhost
port = 7878
api_key = my_api_key
verify = true
value_template = {{ value_template }}
```
> **Returns:** `value_template` as rendered string
| Variable | Required | Description | Options |
|-----------------|----------|-----------------------------------------------------------------|-------------------|
| [variable_name] | Yes | Name for the data source. | [variable_name] |
| platform | Yes | Name of the platform. | radarr |
| prefix | No | The prefix for the app's url. | web prefix, e.g. http:// or https:// |
| host | Yes | Radarr Host | url,ip |
| port | No | Radarr Port | port |
| api_key | Yes | ApiKey | api key |
| verify | No | Turn TLS verification on or off, default is true | true,false |
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
<br />
###### **Available fields for value_template**
* version
* movies
* queue
* diskspace[x]['path']
* diskspace[x]['total']
* diskspace[x]['used']
* diskspace[x]['free']
* error (for debug)
> **Working 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}}<br />Queue : {{queue}} <br />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
>```
"""
import json
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 __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

View File

@ -1,268 +0,0 @@
"""
##### Sonarr
Display information from Sonarr API
```ini
[variable_name]
platform = sonarr
prefix = http://
host = localhost
port = 8989
api_key = {{ Sonarr API Key }}
verify = true
value_template = {{ value_template }}
```
> **Returns:** `value_template` as rendered string
| Variable | Required | Description | Options |
|-----------------|----------|-----------------------------------------------------------------|-------------------|
| [variable_name] | Yes | Name for the data source. | [variable_name] |
| platform | Yes | Name of the platform. | sonarr |
| prefix | No | The prefix for the app's url. | web prefix, e.g. http:// or https:// |
| host | Yes | Sonarr Host | url,ip |
| port | No | Sonarr Port | port |
| api_key | Yes | ApiKey | api key |
| verify | No | Turn TLS verification on or off, default is true | true,false |
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
<br />
###### **Available fields for value_template**
* version
* wanted_missing
* queue
* diskspace[x]['path']
* diskspace[x]['total']
* diskspace[x]['used']
* diskspace[x]['free']
* error (for debug)
> **Working 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}}<br />Queue : {{queue}} <br />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
>```
"""
import json
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 __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

View File

@ -1,224 +0,0 @@
"""
##### Tautulli
Display information from Tautulli API
```ini
[variable_name]
platform = tautulli
prefix = http://
host = localhost
port = 8181
api_key = {{ Tautulli API Key }}
verify = true
value_template = {{ value_template }}
```
> **Returns:** `value_template` as rendered string
| Variable | Required | Description | Options |
|-----------------|----------|-----------------------------------------------------------------|-------------------|
| [variable_name] | Yes | Name for the data source. | [variable_name] |
| platform | Yes | Name of the platform. | tautulli |
| prefix | No | The prefix for the app's url. | web prefix, e.g. http:// or https:// |
| host | Yes | Tautulli Host | url,ip |
| port | No | Tautulli Port | port |
| api_key | Yes | ApiKey | api key |
| verify | No | Turn TLS verification on or off, default is true | true,false |
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
<br />
###### **Available fields for value_template**
* stream_count
* stream_count_direct_play
* stream_count_direct_stream
* stream_count_transcode
* total_bandwidth
* wan_bandwidth
* update_available
* update_message
* error (for debug)
> **Working 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
>```
"""
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 __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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,2 +1,2 @@
version = "v0.5"
revision_number = "4.1"
revision_number = "4"

View File

@ -1,7 +0,0 @@
[Docker]
prefix = http://
url = your-website.com
icon = static/images/apps/docker.png
sidebar_icon = static/images/apps/docker.png
description = Empowering App Development for Developers
open_in = this_tab