aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/configuration.md5
-rw-r--r--docs/customservices.md23
-rw-r--r--package.json6
-rw-r--r--src/assets/app.scss10
-rw-r--r--src/components/services/OpenWeather.vue135
-rw-r--r--src/components/services/PaperlessNG.vue1
-rw-r--r--src/components/services/PiHole.vue4
-rw-r--r--src/components/services/Ping.vue6
-rw-r--r--src/components/services/Radarr.vue10
-rw-r--r--src/components/services/Sonarr.vue10
-rw-r--r--yarn.lock15
11 files changed, 193 insertions, 32 deletions
diff --git a/docs/configuration.md b/docs/configuration.md
index a472b41..a76ecb3 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -7,7 +7,7 @@ Title, icons, links, colors, and services can be configured in the `config.yml`
7# Homepage configuration 7# Homepage configuration
8# See https://fontawesome.com/icons for icons options 8# See https://fontawesome.com/icons for icons options
9 9
10# Optional: Use external configuration file. 10# Optional: Use external configuration file.
11# Using this will ignore remaining config in this file 11# Using this will ignore remaining config in this file
12# externalConfig: https://example.com/server-luci/config.yaml 12# externalConfig: https://example.com/server-luci/config.yaml
13 13
@@ -136,6 +136,9 @@ services:
136 # background: red # optional color for card to set color directly without custom stylesheet 136 # background: red # optional color for card to set color directly without custom stylesheet
137``` 137```
138 138
139
140View [Custom Services](customservices.md) for details about all available custom services (like PiHole) and how to configure them.
141
139If you choose to fetch message information from an endpoint, the output format should be as follows (or you can [custom map fields as shown in tips-and-tricks](./tips-and-tricks.md#mapping-fields)): 142If you choose to fetch message information from an endpoint, the output format should be as follows (or you can [custom map fields as shown in tips-and-tricks](./tips-and-tricks.md#mapping-fields)):
140 143
141```json 144```json
diff --git a/docs/customservices.md b/docs/customservices.md
index 43f45f4..150e232 100644
--- a/docs/customservices.md
+++ b/docs/customservices.md
@@ -19,6 +19,27 @@ The following configuration is available for the PiHole service.
19 type: "PiHole" 19 type: "PiHole"
20``` 20```
21 21
22
23## OpenWeatherMap
24
25Using the OpenWeatherMap service you can display weather information about a given location.
26The following configuration is available for the OpenWeatherMap service:
27
28```
29items:
30 - name: "Weather"
31 location: "Amsterdam" # your location.
32 locationId: "2759794" # Optional: Specify OpenWeatherMap city ID for better accuracy
33 apiKey: "<---insert-api-key-here--->" # insert your own API key here. Request one from https://openweathermap.org/api.
34 units: "metric" # units to display temperature. Can be one of: metric, imperial, kelvin. Defaults to kelvin.
35 background: "square" # choose which type of background you want behind the image. Can be one of: square, cicle, none. Defaults to none.
36 type: "OpenWeather"
37```
38
39**Remarks:**
40If for some reason your city can't be found by entering the name in the `location` property, you could also try to configure the OWM city ID in the `locationId` property. To retrieve your specific City ID, go to the [OWM website](https://openweathermap.org), search for your city and retrieve the ID from the URL (for example, the City ID of Amsterdam is 2759794).
41
42
22## Medusa 43## Medusa
23 44
24This service displays News (grey), Warning (orange) or Error (red) notifications bubbles from the Medusa application. 45This service displays News (grey), Warning (orange) or Error (red) notifications bubbles from the Medusa application.
@@ -50,4 +71,4 @@ For Paperless you need an API-Key which you have to store at the item in the fie
50 71
51## Ping 72## Ping
52 73
53For Paperless you need an API-Key which you have to store at the item in the field `apikey`. 74For Ping you need an API-Key which you have to store at the item in the field `apikey`.
diff --git a/package.json b/package.json
index 61b3073..c5486bb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
1{ 1{
2 "name": "homer", 2 "name": "homer",
3 "version": "21.07.1", 3 "version": "21.09.1",
4 "license": "Apache-2.0", 4 "license": "Apache-2.0",
5 "scripts": { 5 "scripts": {
6 "serve": "vue-cli-service serve", 6 "serve": "vue-cli-service serve",
@@ -8,9 +8,9 @@
8 "lint": "vue-cli-service lint" 8 "lint": "vue-cli-service lint"
9 }, 9 },
10 "dependencies": { 10 "dependencies": {
11 "@fortawesome/fontawesome-free": "^5.15.3", 11 "@fortawesome/fontawesome-free": "^5.15.4",
12 "bulma": "^0.9.3", 12 "bulma": "^0.9.3",
13 "core-js": "^3.15.2", 13 "core-js": "^3.17.3",
14 "js-yaml": "^4.1.0", 14 "js-yaml": "^4.1.0",
15 "lodash.merge": "^4.6.2", 15 "lodash.merge": "^4.6.2",
16 "register-service-worker": "^1.7.2", 16 "register-service-worker": "^1.7.2",
diff --git a/src/assets/app.scss b/src/assets/app.scss
index 6bb5068..f2dfb37 100644
--- a/src/assets/app.scss
+++ b/src/assets/app.scss
@@ -106,7 +106,7 @@ body {
106 } 106 }
107 107
108 .first-line { 108 .first-line {
109 height: 100px; 109 min-height: 100px;
110 vertical-align: center; 110 vertical-align: center;
111 background-color: var(--highlight-primary); 111 background-color: var(--highlight-primary);
112 112
@@ -121,7 +121,7 @@ body {
121 } 121 }
122 122
123 .container { 123 .container {
124 height: 80px; 124 min-height: 80px;
125 padding: 10px 0; 125 padding: 10px 0;
126 } 126 }
127 127
@@ -140,8 +140,7 @@ body {
140 } 140 }
141 } 141 }
142 } 142 }
143 .navbar, 143 .navbar {
144 .navbar-menu {
145 background-color: var(--highlight-secondary); 144 background-color: var(--highlight-secondary);
146 145
147 a { 146 a {
@@ -153,6 +152,9 @@ body {
153 background-color: var(--highlight-hover); 152 background-color: var(--highlight-hover);
154 } 153 }
155 } 154 }
155 .navbar-menu {
156 background-color: inherit;
157 }
156 } 158 }
157 .navbar-end { 159 .navbar-end {
158 text-align: right; 160 text-align: right;
diff --git a/src/components/services/OpenWeather.vue b/src/components/services/OpenWeather.vue
new file mode 100644
index 0000000..09ff76a
--- /dev/null
+++ b/src/components/services/OpenWeather.vue
@@ -0,0 +1,135 @@
1<template>
2 <div>
3 <div class="card" :class="item.class">
4 <a
5 :href="`https://openweathermap.org/city/${id}`"
6 :target="item.target"
7 rel="noreferrer"
8 >
9 <div class="card-content">
10 <div class="media">
11 <div v-if="icon" class="media-left" :class="item.background">
12 <figure class="image is-48x48">
13 <img
14 :src="`https://openweathermap.org/img/wn/${icon}@2x.png`"
15 :alt="conditions"
16 :title="conditions"
17 />
18 </figure>
19 </div>
20 <div class="media-content">
21 <p v-if="error" class="error">Data could not be retrieved</p>
22 <div v-else>
23 <p class="title is-4">{{ name }}</p>
24 <p class="subtitle is-6">
25 {{ temp | tempSuffix(this.item.units) }}
26 </p>
27 </div>
28 </div>
29 </div>
30 <div class="tag" :class="item.tagstyle" v-if="item.tag">
31 <strong class="tag-text">#{{ item.tag }}</strong>
32 </div>
33 </div>
34 </a>
35 </div>
36 </div>
37</template>
38
39<script>
40export default {
41 name: "OpenWeather",
42 props: {
43 item: Object,
44 },
45 data: () => ({
46 id: null,
47 icon: null,
48 name: null,
49 temp: null,
50 conditions: null,
51 error: false,
52 }),
53 created() {
54 this.fetchWeather();
55 },
56 methods: {
57 fetchWeather: async function () {
58 let locationQuery;
59
60 // Use location ID if specified, otherwise retrieve value from location (name).
61 if (this.item.locationId) {
62 locationQuery = `id=${this.item.locationId}`;
63 } else {
64 locationQuery = `q=${this.item.location}`;
65 }
66
67 const url = `https://api.openweathermap.org/data/2.5/weather?${locationQuery}&appid=${this.item.apiKey}&units=${this.item.units}`;
68 fetch(url)
69 .then((response) => {
70 if (!response.ok) {
71 throw Error(response.statusText);
72 }
73 return response.json();
74 })
75 .then((weather) => {
76 this.id = weather.id;
77 this.name = weather.name;
78 this.temp = parseInt(weather.main.temp).toFixed(1);
79 this.icon = weather.weather[0].icon;
80 this.conditions = weather.weather[0].description;
81 })
82 .catch((e) => {
83 console.log(e);
84 this.error = true;
85 });
86 },
87 },
88 filters: {
89 tempSuffix: function (value, type) {
90 if (!value) return "";
91
92 let unit = "K";
93 if (type === "metric") {
94 unit = "°C";
95 } else if (type === "imperial") {
96 unit = "°F";
97 }
98 return `${value} ${unit}`;
99 },
100 },
101};
102</script>
103
104<style scoped lang="scss">
105// Add a border around the weather image.
106// Otherwise the image is not always distinguishable.
107.media-left {
108 &.circle,
109 &.square {
110 background-color: #e4e4e4;
111 }
112
113 &.circle {
114 border-radius: 90%;
115 }
116
117 img {
118 max-height: 100%;
119 }
120}
121
122.error {
123 color: #de0000;
124}
125
126// Change background color in dark mode.
127.is-dark {
128 .media-left {
129 &.circle,
130 &.square {
131 background-color: #909090;
132 }
133 }
134}
135</style>
diff --git a/src/components/services/PaperlessNG.vue b/src/components/services/PaperlessNG.vue
index 4fb31f8..af13317 100644
--- a/src/components/services/PaperlessNG.vue
+++ b/src/components/services/PaperlessNG.vue
@@ -59,6 +59,7 @@ export default {
59 } 59 }
60 const url = `${this.item.url}/api/documents/`; 60 const url = `${this.item.url}/api/documents/`;
61 this.api = await fetch(url, { 61 this.api = await fetch(url, {
62 credentials: "include",
62 headers: { 63 headers: {
63 Authorization: "Token " + this.item.apikey, 64 Authorization: "Token " + this.item.apikey,
64 }, 65 },
diff --git a/src/components/services/PiHole.vue b/src/components/services/PiHole.vue
index 7042a7b..87f7090 100644
--- a/src/components/services/PiHole.vue
+++ b/src/components/services/PiHole.vue
@@ -64,7 +64,9 @@ export default {
64 methods: { 64 methods: {
65 fetchStatus: async function () { 65 fetchStatus: async function () {
66 const url = `${this.item.url}/api.php`; 66 const url = `${this.item.url}/api.php`;
67 this.api = await fetch(url) 67 this.api = await fetch(url, {
68 credentials: "include",
69 })
68 .then((response) => response.json()) 70 .then((response) => response.json())
69 .catch((e) => console.log(e)); 71 .catch((e) => console.log(e));
70 }, 72 },
diff --git a/src/components/services/Ping.vue b/src/components/services/Ping.vue
index 8a9b7a4..e693af4 100644
--- a/src/components/services/Ping.vue
+++ b/src/components/services/Ping.vue
@@ -50,7 +50,11 @@ export default {
50 methods: { 50 methods: {
51 fetchStatus: async function () { 51 fetchStatus: async function () {
52 const url = `${this.item.url}`; 52 const url = `${this.item.url}`;
53 fetch(url, { method: "HEAD", cache: "no-cache" }) 53 fetch(url, {
54 method: "HEAD",
55 cache: "no-cache",
56 credentials: "include",
57 })
54 .then((response) => { 58 .then((response) => {
55 if (!response.ok) { 59 if (!response.ok) {
56 throw Error(response.statusText); 60 throw Error(response.statusText);
diff --git a/src/components/services/Radarr.vue b/src/components/services/Radarr.vue
index 93831a7..9d38292 100644
--- a/src/components/services/Radarr.vue
+++ b/src/components/services/Radarr.vue
@@ -70,10 +70,7 @@ export default {
70 }, 70 },
71 methods: { 71 methods: {
72 fetchConfig: function () { 72 fetchConfig: function () {
73 fetch(`${this.item.url}/api/health`, { 73 fetch(`${this.item.url}/api/health?apikey=${this.item.apikey}`)
74 credentials: "include",
75 headers: { "X-Api-Key": `${this.item.apikey}` },
76 })
77 .then((response) => { 74 .then((response) => {
78 if (response.status != 200) { 75 if (response.status != 200) {
79 throw new Error(response.statusText); 76 throw new Error(response.statusText);
@@ -95,10 +92,7 @@ export default {
95 console.error(e); 92 console.error(e);
96 this.serverError = true; 93 this.serverError = true;
97 }); 94 });
98 fetch(`${this.item.url}/api/queue`, { 95 fetch(`${this.item.url}/api/queue?apikey=${this.item.apikey}`)
99 credentials: "include",
100 headers: { "X-Api-Key": `${this.item.apikey}` },
101 })
102 .then((response) => { 96 .then((response) => {
103 if (response.status != 200) { 97 if (response.status != 200) {
104 throw new Error(response.statusText); 98 throw new Error(response.statusText);
diff --git a/src/components/services/Sonarr.vue b/src/components/services/Sonarr.vue
index 8cebac4..7851b6b 100644
--- a/src/components/services/Sonarr.vue
+++ b/src/components/services/Sonarr.vue
@@ -70,10 +70,7 @@ export default {
70 }, 70 },
71 methods: { 71 methods: {
72 fetchConfig: function () { 72 fetchConfig: function () {
73 fetch(`${this.item.url}/api/health`, { 73 fetch(`${this.item.url}/api/health?apikey=${this.item.apikey}`)
74 credentials: "include",
75 headers: { "X-Api-Key": `${this.item.apikey}` },
76 })
77 .then((response) => { 74 .then((response) => {
78 if (response.status != 200) { 75 if (response.status != 200) {
79 throw new Error(response.statusText); 76 throw new Error(response.statusText);
@@ -95,10 +92,7 @@ export default {
95 console.error(e); 92 console.error(e);
96 this.serverError = true; 93 this.serverError = true;
97 }); 94 });
98 fetch(`${this.item.url}/api/queue`, { 95 fetch(`${this.item.url}/api/queue?apikey=${this.item.apikey}`)
99 credentials: "include",
100 headers: { "X-Api-Key": `${this.item.apikey}` },
101 })
102 .then((response) => { 96 .then((response) => {
103 if (response.status != 200) { 97 if (response.status != 200) {
104 throw new Error(response.statusText); 98 throw new Error(response.statusText);
diff --git a/yarn.lock b/yarn.lock
index c79c2fd..79ad871 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -903,10 +903,10 @@
903 "@babel/helper-validator-identifier" "^7.14.5" 903 "@babel/helper-validator-identifier" "^7.14.5"
904 to-fast-properties "^2.0.0" 904 to-fast-properties "^2.0.0"
905 905
906"@fortawesome/fontawesome-free@^5.15.3": 906"@fortawesome/fontawesome-free@^5.15.4":
907 version "5.15.3" 907 version "5.15.4"
908 resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz#c36ffa64a2a239bf948541a97b6ae8d729e09a9a" 908 resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz#ecda5712b61ac852c760d8b3c79c96adca5554e5"
909 integrity sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w== 909 integrity sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==
910 910
911"@hapi/address@2.x.x": 911"@hapi/address@2.x.x":
912 version "2.1.4" 912 version "2.1.4"
@@ -2773,7 +2773,12 @@ core-js@^2.4.0:
2773 resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" 2773 resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
2774 integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== 2774 integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
2775 2775
2776core-js@^3.15.2, core-js@^3.6.5: 2776core-js@^3.17.3:
2777 version "3.17.3"
2778 resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.17.3.tgz#8e8bd20e91df9951e903cabe91f9af4a0895bc1e"
2779 integrity sha512-lyvajs+wd8N1hXfzob1LdOCCHFU4bGMbqqmLn1Q4QlCpDqWPpGf+p0nj+LNrvDDG33j0hZXw2nsvvVpHysxyNw==
2780
2781core-js@^3.6.5:
2777 version "3.15.2" 2782 version "3.15.2"
2778 resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" 2783 resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61"
2779 integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q== 2784 integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==