diff --git a/dashmachine/platform/docker.py b/dashmachine/platform/docker.py
new file mode 100644
index 0000000..db873b6
--- /dev/null
+++ b/dashmachine/platform/docker.py
@@ -0,0 +1,449 @@
+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 = """
+
+
+ error
+
+
+

+
+
+
+
Error
+
+
+ keyboard_arrow_down
+
+
+ """
+ else:
+ if self.tls_mode == None:
+ img_tls = """
+ lock_open
+ """
+ else:
+ img_tls = """
+ lock
+ """
+ if len(self.warnings) > 0:
+ img_warnings = """
+ warning
+ """
+ else:
+ img_warnings = """
+ warning
+ """
+ self.html_template = (
+ """
+
+
+ """
+ + img_tls
+ + img_warnings
+ + """
+
+
+

+
+
+
+
{{name}}
+
+
+ keyboard_arrow_down
+
+
+
+
+
Containers: {{ containers }}
+
Running: {{ containers_running }}
+
Paused: {{ containers_paused }}
+
Stopped: {{ containers_stopped }}
+
Images: {{ images }}
+
Driver: {{ driver }}
+
CPU: {{ cpu }}
+
Memory: {{ memory }}
+
+
+
+ """
+ )
+
+ def getHtml(self):
+ return self.html_template
+
+
+class Platform:
+ def docs(self):
+ documentation = {
+ "name": "docker",
+ "author": "Thlb",
+ "author_url": "https://github.com/Thlb",
+ "version": 1.0,
+ "description": "Display information from Docker API. Informations can be displayed on a custom card or on an app card (e.g. Portainer App)",
+ "returns": "`value_template` as rendered string",
+ "returns_json_keys": [
+ "version",
+ "max_api_version",
+ "name",
+ "containers",
+ "containers_running",
+ "containers_paused",
+ "containers_stopped",
+ "images",
+ "driver",
+ "cpu",
+ "memory",
+ "warnings",
+ "error (for debug)",
+ ],
+ "example": """
+```ini
+# Working example (using un-encrypted connection, on Portainer card)
+[docker-endpoint-1]
+platform = docker
+prefix = http://
+host = 192.168.0.110
+port = 2375
+value_template = {{error}}{{name}}
fiber_manual_record{{containers_running}}fiber_manual_record{{containers_paused}}fiber_manual_record{{containers_stopped}}
+
+[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}}{{name}}
fiber_manual_record{{containers_running}}fiber_manual_record{{containers_paused}}fiber_manual_record{{containers_stopped}}
+
+[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
+```
+ """,
+ "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": "docker",
+ "options": "docker",
+ },
+ {
+ "variable": "prefix",
+ "description": "The prefix for the app's url.",
+ "default": "",
+ "options": "web prefix, e.g. http:// or https://",
+ },
+ {
+ "variable": "host",
+ "description": "Docker Host",
+ "default": "",
+ "options": "url,ip",
+ },
+ {
+ "variable": "port",
+ "description": "Docker Port",
+ "default": "",
+ "options": "port",
+ },
+ {
+ "variable": "api_version",
+ "description": "API version, by default platform will try to find latest version",
+ "default": "",
+ "options": "1.40",
+ },
+ {
+ "variable": "tls_mode",
+ "description": "TLS verification mode",
+ "default": "None",
+ "options": "Server, Client, Both, None",
+ },
+ {
+ "variable": "tls_ca",
+ "description": "Requiered for tls_mode=Both or tls_mode=Server",
+ "default": "None",
+ "options": "/path/to/ca, None",
+ },
+ {
+ "variable": "tls_cert",
+ "description": "Requierd for tls_mode=Both or tls_mode=Client",
+ "default": "None",
+ "options": "/path/to/cert, None",
+ },
+ {
+ "variable": "tls_key",
+ "description": "Requierd for tls_mode=Both or tls_mode=Client",
+ "default": "None",
+ "options": "/path/to/key, None",
+ },
+ {
+ "variable": "card_type",
+ "description": "Set to Custom if you want to display informations in a custom card",
+ "default": "App",
+ "options": "Custom, App",
+ },
+ {
+ "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 = 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__)
diff --git a/dashmachine/platform/lidarr.py b/dashmachine/platform/lidarr.py
index 0c4ff45..8a97ddf 100644
--- a/dashmachine/platform/lidarr.py
+++ b/dashmachine/platform/lidarr.py
@@ -1,62 +1,3 @@
-"""
-##### 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 |
-
-###### **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}}
Queue : {{queue}}
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
diff --git a/dashmachine/static/images/apps/docker.png b/dashmachine/static/images/apps/docker.png
new file mode 100644
index 0000000..cea2f21
Binary files /dev/null and b/dashmachine/static/images/apps/docker.png differ
diff --git a/template_apps/Docker.ini b/template_apps/Docker.ini
new file mode 100644
index 0000000..de77274
--- /dev/null
+++ b/template_apps/Docker.ini
@@ -0,0 +1,7 @@
+[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
\ No newline at end of file