# Homepage configuration
# See https://fontawesome.com/icons for icons options
-# Optional: Use external configuration file.
+# Optional: Use external configuration file.
# Using this will ignore remaining config in this file
# externalConfig: https://example.com/server-luci/config.yaml
# background: red # optional color for card to set color directly without custom stylesheet
```
+
+View [Custom Services](customservices.md) for details about all available custom services (like PiHole) and how to configure them.
+
If 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)):
```json
type: "PiHole"
```
+
+## OpenWeatherMap
+
+Using the OpenWeatherMap service you can display weather information about a given location.
+The following configuration is available for the OpenWeatherMap service:
+
+```
+items:
+ - name: "Weather"
+ location: "Amsterdam" # your location.
+ locationId: "2759794" # Optional: Specify OpenWeatherMap city ID for better accuracy
+ apiKey: "<---insert-api-key-here--->" # insert your own API key here. Request one from https://openweathermap.org/api.
+ units: "metric" # units to display temperature. Can be one of: metric, imperial, kelvin. Defaults to kelvin.
+ background: "square" # choose which type of background you want behind the image. Can be one of: square, cicle, none. Defaults to none.
+ type: "OpenWeather"
+```
+
+**Remarks:**
+If 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).
+
+
## Medusa
This service displays News (grey), Warning (orange) or Error (red) notifications bubbles from the Medusa application.
## Ping
-For Paperless you need an API-Key which you have to store at the item in the field `apikey`.
+For Ping you need an API-Key which you have to store at the item in the field `apikey`.
{
"name": "homer",
- "version": "21.07.1",
+ "version": "21.09.1",
"license": "Apache-2.0",
"scripts": {
"serve": "vue-cli-service serve",
"lint": "vue-cli-service lint"
},
"dependencies": {
- "@fortawesome/fontawesome-free": "^5.15.3",
+ "@fortawesome/fontawesome-free": "^5.15.4",
"bulma": "^0.9.3",
- "core-js": "^3.15.2",
+ "core-js": "^3.17.3",
"js-yaml": "^4.1.0",
"lodash.merge": "^4.6.2",
"register-service-worker": "^1.7.2",
}
.first-line {
- height: 100px;
+ min-height: 100px;
vertical-align: center;
background-color: var(--highlight-primary);
}
.container {
- height: 80px;
+ min-height: 80px;
padding: 10px 0;
}
}
}
}
- .navbar,
- .navbar-menu {
+ .navbar {
background-color: var(--highlight-secondary);
a {
background-color: var(--highlight-hover);
}
}
+ .navbar-menu {
+ background-color: inherit;
+ }
}
.navbar-end {
text-align: right;
},
methods: {
fetchStatus: async function () {
- this.status = await fetch(`${this.item.url}/control/status`).then(
- (response) => response.json()
- );
+ this.status = await fetch(`${this.item.url}/control/status`, {
+ credentials: "include",
+ }).then((response) => response.json());
},
},
};
--- /dev/null
+<template>
+ <div>
+ <div class="card" :class="item.class">
+ <a
+ :href="`https://openweathermap.org/city/${id}`"
+ :target="item.target"
+ rel="noreferrer"
+ >
+ <div class="card-content">
+ <div class="media">
+ <div v-if="icon" class="media-left" :class="item.background">
+ <figure class="image is-48x48">
+ <img
+ :src="`https://openweathermap.org/img/wn/${icon}@2x.png`"
+ :alt="conditions"
+ :title="conditions"
+ />
+ </figure>
+ </div>
+ <div class="media-content">
+ <p v-if="error" class="error">Data could not be retrieved</p>
+ <div v-else>
+ <p class="title is-4">{{ name }}</p>
+ <p class="subtitle is-6">
+ {{ temp | tempSuffix(this.item.units) }}
+ </p>
+ </div>
+ </div>
+ </div>
+ <div class="tag" :class="item.tagstyle" v-if="item.tag">
+ <strong class="tag-text">#{{ item.tag }}</strong>
+ </div>
+ </div>
+ </a>
+ </div>
+ </div>
+</template>
+
+<script>
+export default {
+ name: "OpenWeather",
+ props: {
+ item: Object,
+ },
+ data: () => ({
+ id: null,
+ icon: null,
+ name: null,
+ temp: null,
+ conditions: null,
+ error: false,
+ }),
+ created() {
+ this.fetchWeather();
+ },
+ methods: {
+ fetchWeather: async function () {
+ let locationQuery;
+
+ // Use location ID if specified, otherwise retrieve value from location (name).
+ if (this.item.locationId) {
+ locationQuery = `id=${this.item.locationId}`;
+ } else {
+ locationQuery = `q=${this.item.location}`;
+ }
+
+ const url = `https://api.openweathermap.org/data/2.5/weather?${locationQuery}&appid=${this.item.apiKey}&units=${this.item.units}`;
+ fetch(url)
+ .then((response) => {
+ if (!response.ok) {
+ throw Error(response.statusText);
+ }
+ return response.json();
+ })
+ .then((weather) => {
+ this.id = weather.id;
+ this.name = weather.name;
+ this.temp = parseInt(weather.main.temp).toFixed(1);
+ this.icon = weather.weather[0].icon;
+ this.conditions = weather.weather[0].description;
+ })
+ .catch((e) => {
+ console.log(e);
+ this.error = true;
+ });
+ },
+ },
+ filters: {
+ tempSuffix: function (value, type) {
+ if (!value) return "";
+
+ let unit = "K";
+ if (type === "metric") {
+ unit = "°C";
+ } else if (type === "imperial") {
+ unit = "°F";
+ }
+ return `${value} ${unit}`;
+ },
+ },
+};
+</script>
+
+<style scoped lang="scss">
+// Add a border around the weather image.
+// Otherwise the image is not always distinguishable.
+.media-left {
+ &.circle,
+ &.square {
+ background-color: #e4e4e4;
+ }
+
+ &.circle {
+ border-radius: 90%;
+ }
+
+ img {
+ max-height: 100%;
+ }
+}
+
+.error {
+ color: #de0000;
+}
+
+// Change background color in dark mode.
+.is-dark {
+ .media-left {
+ &.circle,
+ &.square {
+ background-color: #909090;
+ }
+ }
+}
+</style>
}
const url = `${this.item.url}/api/documents/`;
this.api = await fetch(url, {
+ credentials: "include",
headers: {
Authorization: "Token " + this.item.apikey,
},
methods: {
fetchStatus: async function () {
const url = `${this.item.url}/api.php`;
- this.api = await fetch(url)
+ this.api = await fetch(url, {
+ credentials: "include",
+ })
.then((response) => response.json())
.catch((e) => console.log(e));
},
methods: {
fetchStatus: async function () {
const url = `${this.item.url}`;
- fetch(url, { method: "HEAD", cache: "no-cache" })
+ fetch(url, {
+ method: "HEAD",
+ cache: "no-cache",
+ credentials: "include",
+ })
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
},
methods: {
fetchConfig: function () {
- fetch(`${this.item.url}/api/health`, {
- credentials: "include",
- headers: { "X-Api-Key": `${this.item.apikey}` },
- })
+ fetch(`${this.item.url}/api/health?apikey=${this.item.apikey}`)
.then((response) => {
if (response.status != 200) {
throw new Error(response.statusText);
console.error(e);
this.serverError = true;
});
- fetch(`${this.item.url}/api/queue`, {
- credentials: "include",
- headers: { "X-Api-Key": `${this.item.apikey}` },
- })
+ fetch(`${this.item.url}/api/queue?apikey=${this.item.apikey}`)
.then((response) => {
if (response.status != 200) {
throw new Error(response.statusText);
},
methods: {
fetchConfig: function () {
- fetch(`${this.item.url}/api/health`, {
- credentials: "include",
- headers: { "X-Api-Key": `${this.item.apikey}` },
- })
+ fetch(`${this.item.url}/api/health?apikey=${this.item.apikey}`)
.then((response) => {
if (response.status != 200) {
throw new Error(response.statusText);
console.error(e);
this.serverError = true;
});
- fetch(`${this.item.url}/api/queue`, {
- credentials: "include",
- headers: { "X-Api-Key": `${this.item.apikey}` },
- })
+ fetch(`${this.item.url}/api/queue?apikey=${this.item.apikey}`)
.then((response) => {
if (response.status != 200) {
throw new Error(response.statusText);
"@babel/helper-validator-identifier" "^7.14.5"
to-fast-properties "^2.0.0"
-"@fortawesome/fontawesome-free@^5.15.3":
- version "5.15.3"
- resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz#c36ffa64a2a239bf948541a97b6ae8d729e09a9a"
- integrity sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w==
+"@fortawesome/fontawesome-free@^5.15.4":
+ version "5.15.4"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz#ecda5712b61ac852c760d8b3c79c96adca5554e5"
+ integrity sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==
"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
-core-js@^3.15.2, core-js@^3.6.5:
+core-js@^3.17.3:
+ version "3.17.3"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.17.3.tgz#8e8bd20e91df9951e903cabe91f9af4a0895bc1e"
+ integrity sha512-lyvajs+wd8N1hXfzob1LdOCCHFU4bGMbqqmLn1Q4QlCpDqWPpGf+p0nj+LNrvDDG33j0hZXw2nsvvVpHysxyNw==
+
+core-js@^3.6.5:
version "3.15.2"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61"
integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==