diff options
-rw-r--r-- | docs/customservices.md | 56 | ||||
-rw-r--r-- | dummy-data/README.md | 13 | ||||
-rw-r--r-- | dummy-data/qBittorrent/api/v2/torrents/info | 53 | ||||
-rw-r--r-- | dummy-data/qBittorrent/api/v2/transfer/info | 10 | ||||
-rw-r--r-- | src/components/services/qBittorrent.vue | 120 |
5 files changed, 229 insertions, 23 deletions
diff --git a/docs/customservices.md b/docs/customservices.md index 3c4f5ad..e84afdb 100644 --- a/docs/customservices.md +++ b/docs/customservices.md | |||
@@ -7,24 +7,27 @@ if your homer instance is secured behind some form of authentication or access r | |||
7 | 7 | ||
8 | Available services are in `src/components/`. Here is an overview of all custom services that are available | 8 | Available services are in `src/components/`. Here is an overview of all custom services that are available |
9 | within Homer: | 9 | within Homer: |
10 | + [PiHole](#pihole) | ||
11 | + [OpenWeatherMap](#openweathermap) | ||
12 | + [Medusa](#medusa) | ||
13 | + [Lidarr, Prowlarr, Sonarr and Radarr](#lidarr-prowlarr-sonarr-and-radarr) | ||
14 | + [PaperlessNG](#paperlessng) | ||
15 | + [Ping](#ping) | ||
16 | + [Prometheus](#prometheus) | ||
17 | + [AdGuard Home](#adguard-home) | ||
18 | + [Portainer](#portainer) | ||
19 | + [Emby / Jellyfin](#emby--jellyfin) | ||
20 | + [Uptime Kuma](#uptime-kuma) | ||
21 | + [Tautulli](#tautulli) | ||
22 | + [Mealie](#mealie) | ||
23 | + [Healthchecks](#healthchecks) | ||
24 | + [Proxmox](#proxmox) | ||
25 | 10 | ||
26 | If you experiencing any issue, please have a look to the [troubleshooting](troubleshooting.md) page. | 11 | - [Custom Services](#custom-services) |
12 | - [Common options](#common-options) | ||
13 | - [PiHole](#pihole) | ||
14 | - [OpenWeatherMap](#openweathermap) | ||
15 | - [Medusa](#medusa) | ||
16 | - [Lidarr, Prowlarr, Sonarr and Radarr](#lidarr-prowlarr-sonarr-and-radarr) | ||
17 | - [PaperlessNG](#paperlessng) | ||
18 | - [Ping](#ping) | ||
19 | - [Prometheus](#prometheus) | ||
20 | - [AdGuard Home](#adguard-home) | ||
21 | - [Portainer](#portainer) | ||
22 | - [Emby / Jellyfin](#emby--jellyfin) | ||
23 | - [Uptime Kuma](#uptime-kuma) | ||
24 | - [Tautulli](#tautulli) | ||
25 | - [Mealie](#mealie) | ||
26 | - [Healthchecks](#healthchecks) | ||
27 | - [Proxmox](#proxmox) | ||
28 | - [qBittorrent](#qbittorrent) | ||
27 | 29 | ||
30 | If you experiencing any issue, please have a look to the [troubleshooting](troubleshooting.md) page. | ||
28 | 31 | ||
29 | ## Common options | 32 | ## Common options |
30 | 33 | ||
@@ -272,4 +275,23 @@ Configuration example: | |||
272 | warning_value: 50 | 275 | warning_value: 50 |
273 | danger_value: 80 | 276 | danger_value: 80 |
274 | api_token: "PVEAPIToken=root@pam!your-api-token-name=your-api-token-key" | 277 | api_token: "PVEAPIToken=root@pam!your-api-token-name=your-api-token-key" |
275 | ``` \ No newline at end of file | 278 | ``` |
279 | |||
280 | ## qBittorrent | ||
281 | |||
282 | This service displays the global upload and download rates, as well as the number of torrents | ||
283 | listed. The service communicates with the qBittorrent API interface which needs | ||
284 | to be accessible from the browser. Please consult | ||
285 | [the instructions](https://github.com/qbittorrent/qBittorrent/pull/12579) | ||
286 | for setting up qBittorrent and make sure the correct CORS-settings are applied. Examples for various | ||
287 | servers can be found at [enable-cors.org](https://enable-cors.org/server.html). | ||
288 | |||
289 | ```yaml | ||
290 | - name: "qBittorrent" | ||
291 | logo: "assets/tools/sample.png" | ||
292 | url: "http://192.168.1.2:8080" # Your rTorrent web UI, f.e. ruTorrent or Flood. | ||
293 | type: "qBittorrent" | ||
294 | rateInterval: 2000 # Interval for updating the download and upload rates. | ||
295 | torrentInterval: 5000 # Interval for updating the torrent count. | ||
296 | target: "_blank" # optional html a tag target attribute | ||
297 | ``` | ||
diff --git a/dummy-data/README.md b/dummy-data/README.md index eeff4ec..6db4712 100644 --- a/dummy-data/README.md +++ b/dummy-data/README.md | |||
@@ -1,15 +1,16 @@ | |||
1 | # Dummy data | 1 | # Dummy data |
2 | 2 | ||
3 | This directory content makes possible to test custom services cards or create a demo without actually running the service. | 3 | This directory content makes possible to test custom services cards or create a demo without actually running the service. |
4 | The principle is simple: save a sample output of the API used in the service in a static file in this directory. The path must be identical as the service endpoint to be used seamlessly. | 4 | The principle is simple: save a sample output of the API used in the service in a static file in this directory. The path must be identical as the service endpoint to be used seamlessly. |
5 | 5 | ||
6 | ## How to add a new services sample: | 6 | ## How to add a new services sample |
7 | 7 | ||
8 | - create a directory for your service, and any sub-folder existing in the service api path. | 8 | - create a directory for your service, and any sub-folder existing in the service api path. |
9 | - save the api output in a file named after the service endpoint. | 9 | - save the api output in a file named after the service endpoint. |
10 | 10 | ||
11 | Example: | 11 | Example: |
12 | ``` | 12 | |
13 | ```sh | ||
13 | mkdir pihole | 14 | mkdir pihole |
14 | curl http://my-pihole.me/admin/api.php -o pihole/api.php # /admin is omited because for PiHole, the implementation expect it to be in the base url (`url` or `enpoint` property) | 15 | curl http://my-pihole.me/admin/api.php -o pihole/api.php # /admin is omited because for PiHole, the implementation expect it to be in the base url (`url` or `endpoint` property) |
15 | ``` \ No newline at end of file | 16 | ``` |
diff --git a/dummy-data/qBittorrent/api/v2/torrents/info b/dummy-data/qBittorrent/api/v2/torrents/info new file mode 100644 index 0000000..e1bc722 --- /dev/null +++ b/dummy-data/qBittorrent/api/v2/torrents/info | |||
@@ -0,0 +1,53 @@ | |||
1 | [ | ||
2 | { | ||
3 | "added_on": 1666985518, | ||
4 | "amount_left": 0, | ||
5 | "auto_tmm": false, | ||
6 | "availability": -1, | ||
7 | "category": "", | ||
8 | "completed": 1474873344, | ||
9 | "completion_on": 1666985584, | ||
10 | "content_path": "/downloads/ubuntu-22.04.1-live-server-amd64.iso", | ||
11 | "dl_limit": -1, | ||
12 | "dlspeed": 0, | ||
13 | "download_path": "", | ||
14 | "downloaded": 1513976240, | ||
15 | "downloaded_session": 0, | ||
16 | "eta": 8640000, | ||
17 | "f_l_piece_prio": false, | ||
18 | "force_start": false, | ||
19 | "hash": "cf3ea75e2ebbd30e0da6e6e215e2226bf35f2e33", | ||
20 | "infohash_v1": "cf3ea75e2ebbd30e0da6e6e215e2226bf35f2e33", | ||
21 | "infohash_v2": "", | ||
22 | "last_activity": 1666985588, | ||
23 | "magnet_uri": "magnet:?xt=urn:btih:cf3ea75e2ebbd30e0da6e6e215e2226bf35f2e33&dn=ubuntu-22.04.1-live-server-amd64.iso&tr=https%3a%2f%2ftorrent.ubuntu.com%2fannounce&tr=https%3a%2f%2fipv6.torrent.ubuntu.com%2fannounce", | ||
24 | "max_ratio": 0, | ||
25 | "max_seeding_time": -1, | ||
26 | "name": "ubuntu-22.04.1-live-server-amd64.iso", | ||
27 | "num_complete": 0, | ||
28 | "num_incomplete": 583, | ||
29 | "num_leechs": 0, | ||
30 | "num_seeds": 0, | ||
31 | "priority": 0, | ||
32 | "progress": 1, | ||
33 | "ratio": 1.7163413343924075e-05, | ||
34 | "ratio_limit": -2, | ||
35 | "save_path": "/downloads/", | ||
36 | "seeding_time": 4, | ||
37 | "seeding_time_limit": -2, | ||
38 | "seen_complete": 1666985584, | ||
39 | "seq_dl": false, | ||
40 | "size": 1474873344, | ||
41 | "state": "pausedUP", | ||
42 | "super_seeding": false, | ||
43 | "tags": "", | ||
44 | "time_active": 69, | ||
45 | "total_size": 1474873344, | ||
46 | "tracker": "", | ||
47 | "trackers_count": 2, | ||
48 | "up_limit": -1, | ||
49 | "uploaded": 25985, | ||
50 | "uploaded_session": 0, | ||
51 | "upspeed": 0 | ||
52 | } | ||
53 | ] \ No newline at end of file | ||
diff --git a/dummy-data/qBittorrent/api/v2/transfer/info b/dummy-data/qBittorrent/api/v2/transfer/info new file mode 100644 index 0000000..dd7b318 --- /dev/null +++ b/dummy-data/qBittorrent/api/v2/transfer/info | |||
@@ -0,0 +1,10 @@ | |||
1 | { | ||
2 | "connection_status": "connected", | ||
3 | "dht_nodes": 318, | ||
4 | "dl_info_data": 23481469329, | ||
5 | "dl_info_speed": 1234567, | ||
6 | "dl_rate_limit": 40960000, | ||
7 | "up_info_data": 1788370216, | ||
8 | "up_info_speed": 765432, | ||
9 | "up_rate_limit": 10547200 | ||
10 | } \ No newline at end of file | ||
diff --git a/src/components/services/qBittorrent.vue b/src/components/services/qBittorrent.vue new file mode 100644 index 0000000..1f1ef49 --- /dev/null +++ b/src/components/services/qBittorrent.vue | |||
@@ -0,0 +1,120 @@ | |||
1 | <template> | ||
2 | <Generic :item="item"> | ||
3 | <template #content> | ||
4 | <p class="title is-4">{{ item.name }}</p> | ||
5 | <p class="subtitle is-6"> | ||
6 | <span v-if="error" class="error">An error has occurred.</span> | ||
7 | <template v-else> | ||
8 | <span class="down monospace"> | ||
9 | <p class="fas fa-download "></p> {{ downRate }} | ||
10 | </span> | ||
11 | <span class="up monospace"> | ||
12 | <p class="fas fa-upload"></p> {{ upRate }} | ||
13 | </span> | ||
14 | </template> | ||
15 | </p> | ||
16 | </template> | ||
17 | <template #indicator> | ||
18 | <span v-if="!error" class="count">{{ count }} | ||
19 | <template v-if="count === 1">torrent</template> | ||
20 | <template v-else>torrents</template> | ||
21 | </span> | ||
22 | </template> | ||
23 | </Generic> | ||
24 | </template> | ||
25 | |||
26 | <script> | ||
27 | import service from "@/mixins/service.js"; | ||
28 | import Generic from "./Generic.vue"; | ||
29 | const units = ["B", "KB", "MB", "GB"]; | ||
30 | |||
31 | // Take the rate in bytes and keep dividing it by 1k until the lowest | ||
32 | // value for which we have a unit is determined. Return the value with | ||
33 | // up to two decimals as a string and unit/s appended. | ||
34 | const displayRate = (rate) => { | ||
35 | let i = 0; | ||
36 | |||
37 | while (rate > 1000 && i < units.length) { | ||
38 | rate /= 1000; | ||
39 | i++; | ||
40 | } | ||
41 | return ( | ||
42 | Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format( | ||
43 | rate || 0 | ||
44 | ) + ` ${units[i]}/s` | ||
45 | ); | ||
46 | }; | ||
47 | |||
48 | export default { | ||
49 | name: "qBittorrent", | ||
50 | mixins: [service], | ||
51 | props: { item: Object }, | ||
52 | components: { Generic }, | ||
53 | data: () => ({ dl: null, ul: null, count: null, error: null }), | ||
54 | computed: { | ||
55 | downRate: function () { | ||
56 | return displayRate(this.dl); | ||
57 | }, | ||
58 | upRate: function () { | ||
59 | return displayRate(this.ul); | ||
60 | }, | ||
61 | }, | ||
62 | created() { | ||
63 | const rateInterval = parseInt(this.item.rateInterval, 10) || 0; | ||
64 | const torrentInterval = parseInt(this.item.torrentInterval, 10) || 0; | ||
65 | if (rateInterval > 0) { | ||
66 | setInterval(() => this.getRate(), rateInterval); | ||
67 | } | ||
68 | if (torrentInterval > 0) { | ||
69 | setInterval(() => this.fetchCount(), torrentInterval); | ||
70 | } | ||
71 | |||
72 | this.getRate(); | ||
73 | this.fetchCount(); | ||
74 | }, | ||
75 | methods: { | ||
76 | fetchCount: async function () { | ||
77 | try { | ||
78 | const body = await this.fetch('/api/v2/torrents/info'); | ||
79 | this.error = false; | ||
80 | this.count = body.length; | ||
81 | } catch (e) { | ||
82 | this.error = true; | ||
83 | console.error(e); | ||
84 | } | ||
85 | }, | ||
86 | getRate: async function () { | ||
87 | try { | ||
88 | const body = await this.fetch('/api/v2/transfer/info'); | ||
89 | this.error = false; | ||
90 | this.dl = body.dl_info_speed; | ||
91 | this.ul = body.up_info_speed; | ||
92 | } catch (e) { | ||
93 | this.error = true; | ||
94 | console.error(e); | ||
95 | } | ||
96 | }, | ||
97 | }, | ||
98 | }; | ||
99 | |||
100 | </script> | ||
101 | |||
102 | <style scoped lang="scss"> | ||
103 | .error { | ||
104 | color: #e51111 !important; | ||
105 | } | ||
106 | |||
107 | .down { | ||
108 | margin-right: 1em; | ||
109 | } | ||
110 | |||
111 | .count { | ||
112 | color: var(--text); | ||
113 | font-size: 0.8em; | ||
114 | } | ||
115 | |||
116 | .monospace { | ||
117 | font-weight: 300; | ||
118 | font-family: monospace; | ||
119 | } | ||
120 | </style> \ No newline at end of file | ||