Compare commits

...

12 Commits

Author SHA1 Message Date
github-actions[bot]
d6188e52fe New Crowdin translations by GitHub Action (#3722)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-07-17 13:35:15 -07:00
shamoon
0a1a3bb8c2 Make unifi note more apparent 2024-07-17 13:32:16 -07:00
j0ton
16b45a313e Feature: Frigate service widget (#3743) 2024-07-17 07:09:58 -07:00
shamoon
1d820b02cb Remove Glime bot 2024-07-16 12:12:10 -07:00
shamoon
55a434c039 Try to fix search overlay broken by blur 2024-07-13 11:55:08 -07:00
shamoon
7831da6f6a Update widget index pages 2024-07-13 11:02:31 -07:00
shamoon
bebecacc89 Try to correct Safari bkgd issue 2024-07-13 10:36:20 -07:00
Charles Thomas
0d7072beea Fix: don't show "partial" for k8s status if there are completed job pods (#3735) 2024-07-12 18:44:30 -07:00
shamoon
95ab0706b6 Correct pfsense v2 percent calculations 2024-07-09 09:07:46 -07:00
Chris Hoffman
a522153df2 Documentation: fix mistaken documentation on field podSelector for services (#3719) 2024-07-08 15:18:30 -07:00
shamoon
1804d0af0f Fix pfsense version doc 2024-07-07 21:44:10 -07:00
shamoon
9d16e280cd Enhancement: support pfsense v2 api (#3716) 2024-07-07 21:07:24 -07:00
26 changed files with 389 additions and 124 deletions

View File

@@ -13,7 +13,7 @@ body:
attributes:
label: Before submitting, please confirm the following
options:
- label: I confirm this was discussed, and the maintainers suggest I open an issue (note that AI bots are not maintainers).
- label: I confirm this was discussed, and the maintainers asked that I open an issue.
required: true
- label: I am aware that if I create this issue without a discussion, it will be removed without a response.
required: true

View File

@@ -36,7 +36,7 @@ Inside of the service you'd like to connect to a pod:
The `app` field is used to create a label selector, in this example case it would match pods with the label: `app.kubernetes.io/name=emby`.
Sometimes this is insufficient for complex or atypical application deployments. In these cases, the `pod-selector` field can be used. Any field selector can be used with it, so it allows for some very powerful selection capabilities.
Sometimes this is insufficient for complex or atypical application deployments. In these cases, the `podSelector` field can be used. Any field selector can be used with it, so it allows for some very powerful selection capabilities.
For instance, it can be utilized to roll multiple underlying deployments under one application to see a high-level aggregate:
@@ -47,7 +47,7 @@ For instance, it can be utilized to roll multiple underlying deployments under o
description: Matrix Synapse Powered Chat
app: matrix-element
namespace: comms
pod-selector: >-
podSelector: >-
app.kubernetes.io/instance in (
matrix-element,
matrix-media-repo,
@@ -58,7 +58,7 @@ For instance, it can be utilized to roll multiple underlying deployments under o
!!! note
A blank string as a pod-selector does not deactivate it, but will actually select all pods in the namespace. This is a useful way to capture the resource usage of a complex application siloed to a single namespace, like Longhorn.
A blank string as a podSelector does not deactivate it, but will actually select all pods in the namespace. This is a useful way to capture the resource usage of a complex application siloed to a single namespace, like Longhorn.
## Automatic Service Discovery

View File

@@ -34,13 +34,6 @@ These companies help the Homepage project by providing services, tools, and reso
</p>
</div>
<div style="margin-bottom: 16px;">
<a href="https://glimelab.ai/"><img src="https://framerusercontent.com/images/28KxmT1G06GrFM8TKeNAC03QIms.svg" alt="Crowdin" style="max-width: 100%; height: 64px; display: block;" /></a>
<p>
GlimeLab provides the project with the awesome AI chatbot here and on GitHub and Discord.
</p>
</div>
<div style="margin-bottom: 16px;">
<a href="https://www.jetbrains.com/"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png" alt="JetBrains" style="max-width: 100%; height: 64px; display: block;" /></a>
<p>

View File

@@ -1,35 +0,0 @@
var glimeScript;
var glimeStyles = [];
document$.subscribe(function () {
if (!glimeScript) {
glimeScript = document.createElement("script");
glimeScript.setAttribute("src", "https://cdn.glimelab.ai/widget/1.0.0/widget.js");
glimeScript.setAttribute("onload", "onGlimeLoad()");
document.head.appendChild(glimeScript);
} else {
var newGlimeStyle = document.createElement("style");
document.head.appendChild(newGlimeStyle);
var i = 0;
glimeStyles.forEach((rule) => {
newGlimeStyle.sheet.insertRule(rule.cssText, i);
i++;
});
}
});
onGlimeLoad = () => {
window.glime.init("Bl3mlvfCnTnRm5");
setTimeout(() => {
const sheets = document.styleSheets;
[...sheets].forEach((sheet) => {
if (!sheet.href) {
[...sheet.cssRules].forEach((rule) => {
if (!rule || rule.href || !rule.selectorText) return;
if (rule.selectorText.indexOf(".css-") === 0 || rule.selectorText.indexOf("glime") > -1) {
glimeStyles.push(rule);
}
});
}
});
}, 1000);
};

View File

@@ -14,6 +14,16 @@
--md-default-fg-color: white;
}
[data-md-color-scheme="default"] .md-search__inner {
--md-default-fg-color--light: gray;
--md-default-fg-color--lighter: black;
--md-default-bg-color: hsla(0, 0%, 100%, 0.9);
}
[data-md-color-scheme="default"] .md-search__inner .md-search__input {
color: var(--md-default-fg-color--light);
}
[data-md-toggle="search"]:not(:checked) ~ .md-header .md-search__form::after {
position: absolute;
top: 0.3rem;
@@ -35,10 +45,6 @@
}
}
#glimeRoot * {
font-family: var(--md-text-font) !important;
}
#carbonads {
margin-top: 10px;
}
@@ -92,11 +98,11 @@
}
body {
background-color: transparent !important;
background-image: url("https://raw.githubusercontent.com/gethomepage/homepage/main/docs/assets/blossom_valley.jpg");
background-size: cover;
background-attachment: fixed;
background-position: center;
background-color: transparent;
color: rgba(255, 255, 255, 0.8);
}
@@ -152,6 +158,12 @@ body[data-md-color-scheme="default"] {
-webkit-backdrop-filter: blur(16px);
}
.md-header:has(.md-search-result__item),
.md-header:has(.md-search__input.focus-visible) {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
}
.md-footer-meta {
background-color: transparent;
}
@@ -212,7 +224,7 @@ body[data-md-color-scheme="default"] {
}
.md-search__scrollwrap {
background-color: hsla(0, 0%, 0%, 0.3);
background-color: hsla(0, 0%, 0%, 0.8);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
}

View File

@@ -6,10 +6,6 @@ hide:
- navigation
---
## Introducing the Homepage AI Bot
Thanks to the generous folks at [Glime](https://glimelab.ai), Homepage is now equipped with a pretty clever AI-powered bot. The bot has full knowledge of our docs, GitHub issues and discussions and is great at answering specific questions about setting up your Homepage. To use the bot, just hit the 'Ask AI' button on any page in our docs, [open a GitHub discussion](https://github.com/gethomepage/homepage/discussions) or check out the [#ai-support channel on Discord](https://discord.com/channels/1019316731635834932/1177885603552038993)!
## General Troubleshooting Tips
- For API errors, clicking the "API Error Information" button in the widget will usually show some helpful information as to whether the issue is reaching the service host, an authentication issue, etc.

View File

@@ -6,6 +6,8 @@ icon: material/widgets
Homepage has two types of widgets: info and service. Below we'll cover each type and how to configure them.
The left navigation of this site contains links to all available widgets.
## Service Widgets
Service widgets are used to display the status of a service, often a web service or API. Services (and their widgets) are defined in your `services.yaml` file. Here's an example:

View File

@@ -2,3 +2,18 @@
title: Info Widgets
description: Homepage info widgets.
---
You can also find a list of all available info widgets in the sidebar navigation.
- [Date & Time](datetime.md)
- [Glances](glances.md)
- [Greeting](greeting.md)
- [Kubernetes](kubernetes.md)
- [Logo](logo.md)
- [Longhorn](longhorn.md)
- [OpenMeteo](openmeteo.md)
- [OpenWeatherMap](openweathermap.md)
- [Resources](resources.md)
- [Search](search.md)
- [Stocks](stocks.md)
- [UniFi Controller](unifi_controller.md)

View File

@@ -9,7 +9,9 @@ You can display general connectivity status from your Unifi (Network) Controller
An optional 'site' parameter can be supplied, if it is not the widget will use the default site for the controller.
_Note: If you enter e.g. incorrect credentials and receive an "API Error", you may need to recreate the container to clear the cache._
!!! hint
If you enter e.g. incorrect credentials and receive an "API Error", you may need to recreate the container to clear the cache.
<img width="162" alt="unifi_infowidget" src="https://user-images.githubusercontent.com/4887959/197706832-f5a8706b-7282-4892-a666-b7d999752562.png">

View File

@@ -0,0 +1,17 @@
---
title: Frigate
description: Frigate Widget Configuration
---
Learn more about [Frigate](https://frigate.video/).
Allowed fields: `["cameras", "uptime", "version"]`.
A recent event listing is disabled by default, but can be enabled with the `enableRecentEvents` option.
```yaml
widget:
type: frigate
url: http://frigate.host.or.ip:port
enableRecentEvents: true # Optional, defaults to false
```

View File

@@ -2,3 +2,126 @@
title: Service Widgets
description: Homepage service widgets.
---
You can also find a list of all available service widgets in the sidebar navigation.
- [Adguard Home](adguard-home.md)
- [Atsumeru](atsumeru.md)
- [Audiobookshelf](audiobookshelf.md)
- [Authentik](authentik.md)
- [Autobrr](autobrr.md)
- [Azure DevOps](azuredevops.md)
- [Bazarr](bazarr.md)
- [Caddy](caddy.md)
- [Calendar](calendar.md)
- [Calibre-Web](calibre-web.md)
- [ChangeDetection.io](changedetectionio.md)
- [Channels DVR Server](channelsdvrserver.md)
- [Cloudflared](cloudflared.md)
- [Coin Market Cap](coin-market-cap.md)
- [CrowdSec](crowdsec.md)
- [Custom API](customapi.md)
- [Deluge](deluge.md)
- [DiskStation](diskstation.md)
- [DownloadStation](downloadstation.md)
- [Emby](emby.md)
- [ESPHome](esphome.md)
- [EVCC](evcc.md)
- [Fileflows](fileflows.md)
- [Flood](flood.md)
- [FreshRSS](freshrss.md)
- [Frigate](frigate.md)
- [Fritz!Box](fritzbox.md)
- [GameDig](gamedig.md)
- [Gatus](gatus.md)
- [Ghostfolio](ghostfolio.md)
- [Gitea](gitea.md)
- [Glances](glances.md)
- [Gluetun](gluetun.md)
- [Gotify](gotify.md)
- [Grafana](grafana.md)
- [HDHomeRun](hdhomerun.md)
- [Healthchecks](healthchecks.md)
- [Home Assistant](homeassistant.md)
- [HomeBox](homebox.md)
- [Homebridge](homebridge.md)
- [iFrame](iframe.md)
- [Immich](immich.md)
- [Jackett](jackett.md)
- [JDownloader](jdownloader.md)
- [Jellyfin](jellyfin.md)
- [Jellyseerr](jellyseerr.md)
- [Kavita](kavita.md)
- [Komga](komga.md)
- [Kopia](kopia.md)
- [Lidarr](lidarr.md)
- [Mastodon](mastodon.md)
- [Mealie](mealie.md)
- [Medusa](medusa.md)
- [Mikrotik](mikrotik.md)
- [Minecraft](minecraft.md)
- [Miniflux](miniflux.md)
- [MJpeg](mjpeg.md)
- [Moonraker](moonraker.md)
- [Mylar](mylar.md)
- [MySpeed](myspeed.md)
- [Navidrome](navidrome.md)
- [NetAlertX](netalertx.md)
- [Netdata](netdata.md)
- [Nextcloud](nextcloud.md)
- [NextDNS](nextdns.md)
- [NGINX Proxy Manager](nginx-proxy-manager.md)
- [NZBGet](nzbget.md)
- [OctoPrint](octoprint.md)
- [Omada](omada.md)
- [Ombi](ombi.md)
- [OpenDTU](opendtu.md)
- [OpenMediaVault](openmediavault.md)
- [OpenWRT](openwrt.md)
- [OPNsense](opnsense.md)
- [Overseerr](overseerr.md)
- [PaperlessNGX](paperlessngx.md)
- [Peanut](peanut.md)
- [pfSense](pfsense.md)
- [PhotoPrism](photoprism.md)
- [Pi-hole](pihole.md)
- [PlantIt](plantit.md)
- [Plex & Tautulli](plex-tautulli.md)
- [Plex](plex.md)
- [Portainer](portainer.md)
- [Prometheus](prometheus.md)
- [Prowlarr](prowlarr.md)
- [Proxmox](proxmox.md)
- [Proxmox Backup Server](proxmoxbackupserver.md)
- [Pterodactyl](pterodactyl.md)
- [PyLoad](pyload.md)
- [qBittorrent](qbittorrent.md)
- [QNAP](qnap.md)
- [Radarr](radarr.md)
- [Readarr](readarr.md)
- [ROMM](romm.md)
- [ruTorrent](rutorrent.md)
- [SABnzbd](sabnzbd.md)
- [Scrutiny](scrutiny.md)
- [Sonarr](sonarr.md)
- [Speedtest Tracker](speedtest-tracker.md)
- [Stash](stash.md)
- [Stocks](stocks.md)
- [SwagDashboard](swagdashboard.md)
- [Syncthing Relay Server](syncthing-relay-server.md)
- [Tailscale](tailscale.md)
- [Tandoor](tandoor.md)
- [TDarr](tdarr.md)
- [Traefik](traefik.md)
- [Transmission](transmission.md)
- [TrueNAS](truenas.md)
- [TubeArchivist](tubearchivist.md)
- [UniFi Controller](unifi-controller.md)
- [Unmanic](unmanic.md)
- [Uptime Kuma](uptime-kuma.md)
- [UptimeRobot](uptimerobot.md)
- [UrBackup](urbackup.md)
- [Watchtower](watchtower.md)
- [WGEasy](wgeasy.md)
- [WhatsUpDocker](whatsupdocker.md)
- [xTeVe](xteve.md)

View File

@@ -26,5 +26,6 @@ widget:
headers: # optional, or username/password
Authorization: client_id client_token
wan: igb0
version: 2 # optional, defaults to 1 for api v1
fields: ["load", "memory", "temp", "wanStatus"] # optional
```

View File

@@ -11,9 +11,11 @@ You can display general connectivity status from your Unifi (Network) Controller
An optional 'site' parameter can be supplied, if it is not the widget will use the default site for the controller.
Allowed fields: `["uptime", "wan", "lan", "lan_users", "lan_devices", "wlan", "wlan_users", "wlan_devices"]` (maximum of four).
Allowed fields: `["uptime", "wan", "lan", "lan_users", "lan_devices", "wlan", "wlan_users", "wlan_devices"]` (maximum of four). Fields unsupported by the unifi device will not be shown.
Note that fields unsupported by the unifi device will not be shown.
!!! hint
If you enter e.g. incorrect credentials and receive an "API Error", you may need to recreate the container to clear the cache.
```yaml
widget:

View File

@@ -55,6 +55,7 @@ nav:
- widgets/services/fileflows.md
- widgets/services/flood.md
- widgets/services/freshrss.md
- widgets/services/frigate.md
- widgets/services/fritzbox.md
- widgets/services/gamedig.md
- widgets/services/gatus.md
@@ -225,8 +226,6 @@ theme:
extra_css:
- "stylesheets/extra.css"
extra_javascript:
- "scripts/extra.js"
extra:
version:

View File

@@ -36,7 +36,7 @@
"wait": "Si us plau, espereu"
},
"search": {
"placeholder": "Cercar…"
"placeholder": "Cerca…"
},
"resources": {
"cpu": "CPU",
@@ -125,8 +125,8 @@
"flood": {
"download": "Descarregar",
"upload": "Pujada",
"leech": "Company",
"seed": "Llavor"
"leech": "Sangonera",
"seed": "Llavors"
},
"freshrss": {
"subscriptions": "Subcripcions",
@@ -203,14 +203,14 @@
"transmission": {
"download": "Descarregar",
"upload": "Pujada",
"leech": "Company",
"seed": "Llavor"
"leech": "Sangonera",
"seed": "Llavors"
},
"qbittorrent": {
"download": "Descarregar",
"upload": "Pujada",
"leech": "Company",
"seed": "Llavor"
"leech": "Sangonera",
"seed": "Llavors"
},
"qnap": {
"cpuUsage": "Ús de CPU",
@@ -223,14 +223,14 @@
"deluge": {
"download": "Descarregar",
"upload": "Pujada",
"leech": "Company",
"seed": "Llavor"
"leech": "Sangonera",
"seed": "Llavors"
},
"downloadstation": {
"download": "Descarregar",
"upload": "Pujada",
"leech": "Company",
"seed": "Llavor"
"leech": "Sangonera",
"seed": "Llavors"
},
"sonarr": {
"wanted": "Volgut",
@@ -241,7 +241,7 @@
},
"radarr": {
"wanted": "Volgut",
"missing": "Faltant",
"missing": "Falten",
"queued": "En cua",
"movies": "Pel·lícules",
"queue": "Cua",
@@ -386,7 +386,7 @@
"down": "Fora de línia"
},
"miniflux": {
"read": "Llegir",
"read": "Llegit",
"unread": "Sense llegir"
},
"authentik": {
@@ -414,8 +414,8 @@
"days": "d",
"hours": "h",
"crit": "Crític",
"read": "Llegir",
"write": "Escriure",
"read": "Llegit",
"write": "Escriptura",
"gpu": "GPU",
"mem": "Mem",
"swap": "Intercanvi"
@@ -423,9 +423,9 @@
"quicklaunch": {
"bookmark": "Marcador",
"service": "Servei",
"search": "Cercar",
"search": "Cerca",
"custom": "Personalitzat",
"visit": "Visitar",
"visit": "Visita",
"url": "URL",
"searchsuggestion": "Suggeriment"
},
@@ -581,7 +581,7 @@
"numberOfLeases": "IPs assignades"
},
"xteve": {
"streams_all": "Tots els fluxos",
"streams_all": "Tots els streams",
"streams_active": "Transmissions actives",
"streams_xepg": "Canals XEPG"
},
@@ -600,7 +600,7 @@
"moonraker": {
"printer_state": "Estat de l'impressora",
"print_status": "Estat de l'impressió",
"print_progress": "Progress",
"print_progress": "Progrés",
"layers": "Capes"
},
"octoprint": {
@@ -614,14 +614,14 @@
"status": "Estat"
},
"pfsense": {
"load": "Promig Càrrega",
"load": "Càrrega mitjana",
"memory": "Ús Memòria",
"wanStatus": "Estat WAN",
"up": "Actiu",
"down": "Inactiu",
"temp": "Temp",
"disk": "Ús Disc",
"wanIP": "WAN IP"
"wanIP": "IP WAN"
},
"proxmoxbackupserver": {
"datastore_usage": "Datastore",
@@ -677,7 +677,7 @@
},
"grafana": {
"dashboards": "Taulells",
"datasources": "Origen de dades",
"datasources": "Orígens de dades",
"totalalerts": "Alertes Totals",
"alertstriggered": "Alertes disparades"
},
@@ -718,10 +718,10 @@
"ghostfolio": {
"gross_percent_today": "Avui",
"gross_percent_1y": "Un any",
"gross_percent_max": "Tot"
"gross_percent_max": "Sempre"
},
"audiobookshelf": {
"podcasts": "Podcasts",
"podcasts": "Pòdcasts",
"books": "Llibres",
"podcastsDuration": "Durada",
"booksDuration": "Durada"
@@ -760,8 +760,8 @@
"failed": "Error",
"canceled": "Cancel·lat",
"inProgress": "En curs",
"totalPrs": "RP Totals",
"myPrs": "Els meus RP",
"totalPrs": "PRs Totals",
"myPrs": "Les meves PRs",
"approved": "Aprovat"
},
"gamedig": {
@@ -798,7 +798,7 @@
},
"openwrt": {
"uptime": "Temps actiu",
"cpuLoad": "Càrrega promig de CPU (5m)",
"cpuLoad": "Càrrega mitjana de CPU (5min)",
"up": "Actiu",
"down": "Inactiu",
"bytesTx": "Enviat",
@@ -849,10 +849,10 @@
"scenesPlayed": "Escenes reproduïdes",
"playCount": "Total reproduccions",
"playDuration": "Temps visionat",
"sceneSize": "Tamany Escena",
"sceneSize": "Tamany d'escenes",
"sceneDuration": "Duració Escenes",
"images": "Imatges",
"imageSize": "Mida Imatges",
"imageSize": "Tamany d'imatges",
"galleries": "Biblioteques",
"performers": "Intèrprets",
"studios": "Estudis",
@@ -884,10 +884,10 @@
"total": "Total"
},
"swagdashboard": {
"proxied": "Proxied",
"auth": "With Auth",
"outdated": "Outdated",
"banned": "Banned"
"proxied": "Intermediat",
"auth": "Amb autentificació",
"outdated": "Obsolet",
"banned": "Bloquejat"
},
"myspeed": {
"ping": "Latència",
@@ -895,10 +895,10 @@
"upload": "Pujada"
},
"stocks": {
"stocks": "Stocks",
"loading": "Loading",
"open": "Open - US Market",
"closed": "Closed - US Market",
"invalidConfiguration": "Invalid Configuration"
"stocks": "Accions",
"loading": "Carregant",
"open": "Obert - Mercat EEUU",
"closed": "Tancat - Mercat EEUU",
"invalidConfiguration": "Configuració no vàlida"
}
}

View File

@@ -900,5 +900,10 @@
"open": "Open - US Market",
"closed": "Closed - US Market",
"invalidConfiguration": "Invalid Configuration"
},
"frigate": {
"cameras": "Cameras",
"uptime": "Uptime",
"version": "Version"
}
}

View File

@@ -77,7 +77,7 @@
"unknown": "Desconocido",
"healthy": "Saludable",
"starting": "Comenzando",
"unhealthy": "Insalubre",
"unhealthy": "No saludable",
"not_found": "No encontrado",
"exited": "Terminado",
"partial": "Parcial"
@@ -895,10 +895,10 @@
"upload": "Subida"
},
"stocks": {
"stocks": "Stocks",
"loading": "Loading",
"open": "Open - US Market",
"closed": "Closed - US Market",
"invalidConfiguration": "Invalid Configuration"
"stocks": "Acciones",
"loading": "Cargando",
"open": "Abierto - Mercado EEUU",
"closed": "Cerrado - Mercado EEUU",
"invalidConfiguration": "Configuración no válida"
}
}

View File

@@ -895,8 +895,8 @@
"upload": "Carregar"
},
"stocks": {
"stocks": "Stocks",
"loading": "Loading",
"stocks": "Ações",
"loading": "Carregando",
"open": "Abrir - Mercado Americano",
"closed": "Fechado - Mercado americano",
"invalidConfiguration": "Configuração Inválida"

View File

@@ -48,8 +48,8 @@ export default async function handler(req, res) {
logger.error(`no pods found with namespace=${namespace} and labelSelector=${labelSelector}`);
return;
}
const someReady = pods.find((pod) => pod.status.phase === "Running");
const allReady = pods.every((pod) => pod.status.phase === "Running");
const someReady = pods.find((pod) => pod.status.phase in ["Completed", "Running"]);
const allReady = pods.every((pod) => pod.status.phase in ["Completed", "Running"]);
let status = "down";
if (allReady) {
status = "running";

View File

@@ -399,7 +399,10 @@ export function cleanServiceGroups(groups) {
expandOneStreamToTwoRows,
showEpisodeNumber,
// glances, pihole
// frigate
enableRecentEvents,
// glances, pihole, pfsense
version,
// glances
@@ -509,6 +512,9 @@ export function cleanServiceGroups(groups) {
if (type === "unifi") {
if (site) cleanedService.widget.site = site;
}
if (type === "pfsense") {
if (version) cleanedService.widget.version = version;
}
if (type === "proxmox") {
if (node) cleanedService.widget.node = node;
}
@@ -611,6 +617,9 @@ export function cleanServiceGroups(groups) {
if (type === "wgeasy") {
if (threshold !== undefined) cleanedService.widget.threshold = parseInt(threshold, 10);
}
if (type === "frigate") {
if (enableRecentEvents !== undefined) cleanedService.widget.enableRecentEvents = enableRecentEvents;
}
}
return cleanedService;

View File

@@ -29,6 +29,7 @@ const components = {
fileflows: dynamic(() => import("./fileflows/component")),
flood: dynamic(() => import("./flood/component")),
freshrss: dynamic(() => import("./freshrss/component")),
frigate: dynamic(() => import("./frigate/component")),
fritzbox: dynamic(() => import("./fritzbox/component")),
gamedig: dynamic(() => import("./gamedig/component")),
gatus: dynamic(() => import("./gatus/component")),

View File

@@ -0,0 +1,70 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data, error } = useWidgetAPI(widget, "stats");
const { data: eventsData, error: eventsError } = useWidgetAPI(widget, "events");
if (error) {
return <Container service={service} error={error} />;
}
if (eventsError) {
return <Container service={service} error={eventsError} />;
}
if (!data || !eventsData) {
return (
<Container service={service}>
<Block label="frigate.cameras" />
<Block label="frigate.uptime" />
<Block label="frigate.version" />
</Container>
);
}
return (
<>
<Container service={service}>
<Block
label="frigate.cameras"
value={t("common.number", {
value: data.num_cameras,
})}
/>
<Block
label="frigate.uptime"
value={t("common.uptime", {
value: data.uptime,
})}
/>
<Block label="frigate.version" value={data.version} />
</Container>
{widget.enableRecentEvents &&
eventsData?.map((event) => (
<div
key={event.id}
className="text-theme-700 dark:text-theme-200 _relative h-5 rounded-md bg-theme-200/50 dark:bg-theme-900/20 m-1 px-1 flex"
>
<div className="text-xs z-10 self-center ml-2 relative h-4 grow mr-2">
<div className="absolute w-full h-4 whitespace-nowrap text-ellipsis overflow-hidden text-left">
{event.camera} ({event.label} {t("common.percent", { value: event.score * 100 })})
</div>
</div>
<div className="self-center text-xs flex justify-end mr-1.5 pl-1 z-10 text-ellipsis overflow-hidden whitespace-nowrap">
{t("common.date", {
value: event.start_time,
formatParams: { value: { timeStyle: "short", dateStyle: "medium" } },
})}
</div>
</div>
))}
</>
);
}

View File

@@ -0,0 +1,38 @@
import { asJson } from "utils/proxy/api-helpers";
import genericProxyHandler from "utils/proxy/handlers/generic";
const widget = {
api: "{url}/api/{endpoint}",
proxyHandler: genericProxyHandler,
mappings: {
stats: {
endpoint: "stats",
map: (data) => {
const jsonData = asJson(data);
return {
num_cameras: jsonData?.cameras !== undefined ? Object.keys(jsonData?.cameras).length : 0,
uptime: jsonData?.service?.uptime,
version: jsonData?.service.version,
};
},
},
events: {
endpoint: "events",
map: (data) =>
asJson(data)
.slice(0, 5)
.map((event) => ({
id: event.id,
camera: event.camera,
label: event.label,
start_time: new Date(event.start_time * 1000),
thumbnail: event.thumbnail,
score: event.data.score,
type: event.data.type,
})),
},
},
};
export default widget;

View File

@@ -9,8 +9,12 @@ export default function Component({ service }) {
const { widget } = service;
const { data: systemData, error: systemError } = useWidgetAPI(widget, "system");
const { data: interfaceData, error: interfaceError } = useWidgetAPI(widget, "interface");
const version = widget.version ?? 1;
const { data: systemData, error: systemError } = useWidgetAPI(widget, version === 1 ? "system" : "systemv2");
const { data: interfaceData, error: interfaceError } = useWidgetAPI(
widget,
version === 1 ? "interface" : "interfacev2",
);
const showWanIP = widget.fields?.filter((f) => f !== "wanIP").length <= 4 && widget.fields?.includes("wanIP");
const showDiskUsage = widget.fields?.filter((f) => f !== "disk").length <= 4 && widget.fields?.includes("disk");
@@ -34,14 +38,20 @@ export default function Component({ service }) {
}
const wan = interfaceData.data.filter((l) => l.hwif === widget.wan)[0];
let memUsage = systemData?.data.mem_usage;
let diskUsage = systemData.data.disk_usage;
if (version === 1) {
memUsage *= 100;
diskUsage *= 100;
}
return (
<Container service={service}>
<Block label="pfsense.load" value={systemData.data.load_avg[0]} />
<Block
label="pfsense.memory"
value={t("common.percent", { value: (systemData.data.mem_usage * 100).toFixed(2) })}
label="pfsense.load"
value={version === 1 ? systemData.data.load_avg[0] : systemData.data.cpu_load_avg[0]}
/>
<Block label="pfsense.memory" value={t("common.percent", { value: memUsage.toFixed(2) })} />
<Block
label="pfsense.temp"
value={t("common.number", { value: systemData.data.temp_c, style: "unit", unit: "celsius" })}
@@ -57,12 +67,7 @@ export default function Component({ service }) {
}
/>
{showWanIP && <Block label="pfsense.wanIP" value={wan.ipaddr} />}
{showDiskUsage && (
<Block
label="pfsense.disk"
value={t("common.percent", { value: (systemData.data.disk_usage * 100).toFixed(2) })}
/>
)}
{showDiskUsage && <Block label="pfsense.disk" value={t("common.percent", { value: diskUsage.toFixed(2) })} />}
</Container>
);
}

View File

@@ -1,16 +1,24 @@
import genericProxyHandler from "utils/proxy/handlers/generic";
const widget = {
api: "{url}/api/v1/{endpoint}",
api: "{url}/api/{endpoint}",
proxyHandler: genericProxyHandler,
mappings: {
system: {
endpoint: "status/system",
endpoint: "v1/status/system",
validate: ["data"],
},
interface: {
endpoint: "status/interface",
endpoint: "v1/status/interface",
validate: ["data"],
},
systemv2: {
endpoint: "v2/status/system",
validate: ["data"],
},
interfacev2: {
endpoint: "v2/status/interfaces?limit=0&offset=0",
validate: ["data"],
},
},

View File

@@ -23,6 +23,7 @@ import evcc from "./evcc/widget";
import fileflows from "./fileflows/widget";
import flood from "./flood/widget";
import freshrss from "./freshrss/widget";
import frigate from "./frigate/widget";
import fritzbox from "./fritzbox/widget";
import gamedig from "./gamedig/widget";
import gatus from "./gatus/widget";
@@ -141,6 +142,7 @@ const widgets = {
fileflows,
flood,
freshrss,
frigate,
fritzbox,
gamedig,
gatus,