Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
baf22aa01e | ||
|
|
6c50005385 | ||
|
|
67d2c7dacd | ||
|
|
59ee4202e5 |
No files matched your search
@@ -16,7 +16,7 @@ import { networkControllerStatusWidget } from "@site/docs/widgets/network-contro
|
||||
<IntegrationHeader integration={unifiControllerIntegration} categories={["Network controller"]} />
|
||||
|
||||
This integration is using the [node-unifi](https://www.npmjs.com/package/node-unifi) library under the hood to connect to the Unifi Controller.
|
||||
Therfore it should support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro controllers as well as self-hosted UniFi controllers.
|
||||
Therefore it should support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro controllers as well as self-hosted UniFi controllers.
|
||||
|
||||
### Widgets & Capabilities
|
||||
|
||||
@@ -28,6 +28,14 @@ Therfore it should support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro c
|
||||
|
||||
<AddingIntegration />
|
||||
|
||||
Use the local HTTPS address of your controller:
|
||||
|
||||
- UniFi OS consoles such as Dream Router, Dream Machine, Cloud Gateway, and CloudKey use `https://<host>` (port 443).
|
||||
- Self-hosted UniFi Network Servers normally use `https://<host>:8443`.
|
||||
- If you omit the port, Homarr tries port 443 first and then port 8443 when the first port cannot be reached.
|
||||
|
||||
Use a local administrator account. Cloud-only accounts and accounts with multi-factor authentication are not supported by the underlying client.
|
||||
|
||||
### Secrets
|
||||
|
||||
<IntegrationSecrets
|
||||
|
||||
+62
-62
@@ -62,378 +62,378 @@
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/adguard-home" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/adguard-home.svg" alt="AdGuard Home" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">AdGuard<br/>Home</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/anchor" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/anchor.svg" alt="Anchor" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Anchor</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/archiveteam-warrior" target="_blank" rel="noreferrer noopener">
|
||||
<a href="null" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/selfhst/icons/png/archiveteam-warrior.png" alt="ArchiveTeam Warrior" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">ArchiveTeam<br/>Warrior</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/aria2" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/PapirusDevelopmentTeam/papirus_icons@latest/src/system_downloads_3.svg" alt="Aria2" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Aria2</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/audiobookshelf" target="_blank" rel="noreferrer noopener">
|
||||
<a href="null" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/audiobookshelf.svg" alt="Audiobookshelf" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Audiobookshelf</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/bazarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/bazarr.svg" alt="Bazarr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Bazarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/beszel" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/beszel.svg" alt="Beszel" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Beszel</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/coolify" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/coolify.svg" alt="Coolify" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Coolify</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/dash-dot" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/dashdot.png" alt="Dash." width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Dash.</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/deluge" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/deluge.svg" alt="Deluge" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Deluge</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/emby" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/emby.svg" alt="Emby" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Emby</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/glances" target="_blank" rel="noreferrer noopener">
|
||||
<a href="null" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/glances.svg" alt="Glances" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Glances</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/gluetun" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/gluetun.svg" alt="Gluetun" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Gluetun</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/gotify" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/gotify.svg" alt="Gotify" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Gotify</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/home-assistant" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/home-assistant.svg" alt="Home Assistant" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Home<br/>Assistant</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/ical" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/ical.svg" alt="iCal" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">iCal</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/immich" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/immich.svg" alt="Immich" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Immich</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/jellyfin" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyfin.svg" alt="Jellyfin" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Jellyfin</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/jellyseerr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyseerr.svg" alt="Jellyseerr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Jellyseerr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/lidarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/lidarr.svg" alt="Lidarr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Lidarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/navidrome" target="_blank" rel="noreferrer noopener">
|
||||
<a href="null" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/navidrome.svg" alt="Navidrome" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Navidrome</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/nextcloud" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nextcloud.svg" alt="Nextcloud" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Nextcloud</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/ntfy" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/ntfy.svg" alt="ntfy" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">ntfy</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/nzbget" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nzbget.svg" alt="NZBGet" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">NZBGet</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/open-media-vault" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/openmediavault.svg" alt="OpenMediaVault" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">OpenMediaVault</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/opnsense" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/opnsense.svg" alt="OPNsense" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">OPNsense</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/overseerr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/overseerr.svg" alt="Overseerr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Overseerr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/paperless-ngx" target="_blank" rel="noreferrer noopener">
|
||||
<a href="null" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/paperless-ngx.svg" alt="Paperless-ngx" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Paperless-ngx</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/patchmon" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/patchmon.svg" alt="PatchMon" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">PatchMon</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/peanut" target="_blank" rel="noreferrer noopener">
|
||||
<a href="null" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/peanut.svg" alt="PeaNUT" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">PeaNUT</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/pi-hole" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/pi-hole.svg" alt="Pi-hole" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Pi-hole</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/plex" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/plex.svg" alt="Plex" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Plex</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/prowlarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/prowlarr.svg" alt="Prowlarr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Prowlarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/proxmox" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/proxmox.svg" alt="Proxmox" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Proxmox</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/q-bittorent" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/qbittorrent.svg" alt="qBittorrent" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">qBittorrent</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/radarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/radarr.svg" alt="Radarr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Radarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/readarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@main/png/readarr.png" alt="Readarr" width="90" height="90" />
|
||||
<br/>
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/readarr.svg" alt="Readarr" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Readarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/sabnzbd" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sabnzbd.svg" alt="SABnzbd" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">SABnzbd</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/seerr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/seerr.svg" alt="Seerr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Seerr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/slskd" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/slskd.svg" alt="Slskd" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Slskd</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/sonarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sonarr.svg" alt="Sonarr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Sonarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/speedtest-tracker" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png" alt="Speedtest Tracker" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Speedtest<br/>Tracker</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/synology" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/synology.svg" alt="Synology DiskStation" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Synology<br/>DiskStation</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/tdarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/tdarr.png" alt="Tdarr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Tdarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/technitium-dns" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/selfhst/icons/svg/technitium.svg" alt="Technitium DNS" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Technitium<br/>DNS</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/tracearr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/tracearr.svg" alt="Tracearr" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Tracearr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/traefik" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/traefik.svg" alt="Traefik" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Traefik</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/transmission" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/transmission.svg" alt="Transmission" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Transmission</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/truenas" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/truenas.svg" alt="TrueNAS" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">TrueNAS</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/umami" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/umami.svg" alt="Umami" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Umami</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/unifi-controller" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/unifi.png" alt="Unifi Controller" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Unifi<br/>Controller</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/unraid" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/unraid.svg" alt="Unraid" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Unraid</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/uptime-kuma" target="_blank" rel="noreferrer noopener">
|
||||
<a href="null" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/uptime-kuma.svg" alt="Uptime Kuma" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">Uptime<br/>Kuma</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/whats-up-docker" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/whats-up-docker.svg" alt="What's Up Docker" width="90" height="90" />
|
||||
<br/>
|
||||
<br/>
|
||||
<p align="center">What's<br/>Up<br/>Docker</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
vi.hoisted(() => {
|
||||
process.env.SKIP_ENV_VALIDATION = "true";
|
||||
process.env.SECRET_ENCRYPTION_KEY = "ff3f4f7ce30e870c9630de9e5d244ffa81101a24ed0dfe5f064beb53a7e684f1";
|
||||
process.env.ENABLE_DNS_CACHING = "false";
|
||||
});
|
||||
|
||||
const controllerCtor = vi.fn();
|
||||
const controllerLogin = vi.fn().mockResolvedValue(true);
|
||||
|
||||
vi.mock("@homarr/redis", () => ({
|
||||
createGetSetChannel: () => ({
|
||||
getAsync: vi.fn().mockResolvedValue(null),
|
||||
setAsync: vi.fn().mockResolvedValue(undefined),
|
||||
removeAsync: vi.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@homarr/core/infrastructure/logs", () => ({
|
||||
createLogger: () => ({ debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }),
|
||||
ErrorWithMetadata: class extends Error {},
|
||||
}));
|
||||
|
||||
vi.mock("@homarr/core/infrastructure/http", () => ({
|
||||
createCustomCheckServerIdentity: () => (() => undefined) as never,
|
||||
fetchWithTrustedCertificatesAsync: vi.fn(),
|
||||
createAxiosCertificateInstanceAsync: vi.fn().mockResolvedValue({}),
|
||||
createCertificateAgentAsync: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
vi.mock("@homarr/core/infrastructure/certificates", () => ({
|
||||
getTrustedCertificateHostnamesAsync: vi.fn().mockResolvedValue([]),
|
||||
getAllTrustedCertificatesAsync: vi.fn().mockResolvedValue([]),
|
||||
}));
|
||||
|
||||
vi.mock("axios", () => ({
|
||||
AxiosError: Error,
|
||||
default: {
|
||||
create: vi.fn().mockReturnValue({}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("http-cookie-agent/http", () => ({
|
||||
HttpCookieAgent: class {},
|
||||
HttpsCookieAgent: class {},
|
||||
}));
|
||||
|
||||
vi.mock("@homarr/node-unifi", () => {
|
||||
class FakeController {
|
||||
public options: Record<string, unknown>;
|
||||
constructor(options: Record<string, unknown>) {
|
||||
this.options = options;
|
||||
controllerCtor(options);
|
||||
}
|
||||
public async login(): Promise<true> {
|
||||
return await controllerLogin(this.options.port);
|
||||
}
|
||||
public async getSitesStats(): Promise<unknown[]> {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return { default: { Controller: FakeController } };
|
||||
});
|
||||
|
||||
import { UnifiControllerIntegration } from "../unifi-controller-integration";
|
||||
|
||||
const createIntegration = (url: string) =>
|
||||
new UnifiControllerIntegration({
|
||||
id: "test-unifi",
|
||||
name: "Test Unifi",
|
||||
url,
|
||||
externalUrl: null,
|
||||
decryptedSecrets: [
|
||||
{ kind: "username", value: "admin" },
|
||||
{ kind: "password", value: "secret" },
|
||||
],
|
||||
});
|
||||
|
||||
describe("UnifiControllerIntegration port resolution", () => {
|
||||
test("a URL without a port uses the UniFi OS port 443", async () => {
|
||||
controllerCtor.mockClear();
|
||||
controllerLogin.mockClear().mockResolvedValue(true);
|
||||
await createIntegration("http://192.168.1.1").getNetworkSummaryAsync();
|
||||
|
||||
expect(controllerCtor).toHaveBeenCalledTimes(1);
|
||||
expect(controllerCtor).toHaveBeenCalledWith(expect.objectContaining({ host: "192.168.1.1", port: 443 }));
|
||||
});
|
||||
|
||||
test("a connection failure on port 443 falls back to the self-hosted controller port 8443", async () => {
|
||||
controllerCtor.mockClear();
|
||||
controllerLogin
|
||||
.mockClear()
|
||||
.mockRejectedValueOnce(Object.assign(new Error("connect ECONNREFUSED"), { isAxiosError: true }))
|
||||
.mockResolvedValueOnce(true);
|
||||
|
||||
await createIntegration("http://controller.lan").getNetworkSummaryAsync();
|
||||
|
||||
expect(controllerCtor).toHaveBeenNthCalledWith(1, expect.objectContaining({ port: 443 }));
|
||||
expect(controllerCtor).toHaveBeenNthCalledWith(2, expect.objectContaining({ port: 8443 }));
|
||||
});
|
||||
|
||||
test("https://controller.lan:8443 keeps the user-specified port", async () => {
|
||||
controllerCtor.mockClear();
|
||||
controllerLogin.mockClear().mockResolvedValue(true);
|
||||
await createIntegration("https://controller.lan:8443").getNetworkSummaryAsync();
|
||||
|
||||
expect(controllerCtor).toHaveBeenCalledWith(expect.objectContaining({ host: "controller.lan", port: 8443 }));
|
||||
});
|
||||
|
||||
test("https://192.168.1.1:8443 keeps the user-specified port", async () => {
|
||||
controllerCtor.mockClear();
|
||||
controllerLogin.mockClear().mockResolvedValue(true);
|
||||
await createIntegration("https://192.168.1.1:8443").getNetworkSummaryAsync();
|
||||
|
||||
expect(controllerCtor).toHaveBeenCalledWith(expect.objectContaining({ host: "192.168.1.1", port: 8443 }));
|
||||
});
|
||||
|
||||
test("an unusual port like 10443 is honored verbatim", async () => {
|
||||
controllerCtor.mockClear();
|
||||
controllerLogin.mockClear().mockResolvedValue(true);
|
||||
await createIntegration("https://192.168.1.1:10443").getNetworkSummaryAsync();
|
||||
|
||||
expect(controllerCtor).toHaveBeenCalledWith(expect.objectContaining({ host: "192.168.1.1", port: 10443 }));
|
||||
});
|
||||
|
||||
test("an authentication failure on port 443 does not try another port", async () => {
|
||||
controllerCtor.mockClear();
|
||||
controllerLogin.mockClear().mockRejectedValueOnce(
|
||||
Object.assign(new Error("Request failed with status code 401"), {
|
||||
isAxiosError: true,
|
||||
response: { status: 401 },
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(createIntegration("https://controller.lan").getNetworkSummaryAsync()).rejects.toThrow();
|
||||
|
||||
expect(controllerCtor).toHaveBeenCalledTimes(1);
|
||||
expect(controllerCtor).toHaveBeenCalledWith(expect.objectContaining({ port: 443 }));
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,6 @@ import type tls from "node:tls";
|
||||
import axios from "axios";
|
||||
import { HttpCookieAgent, HttpsCookieAgent } from "http-cookie-agent/http";
|
||||
|
||||
import { getPortFromUrl } from "@homarr/common";
|
||||
import {
|
||||
getAllTrustedCertificatesAsync,
|
||||
getTrustedCertificateHostnamesAsync,
|
||||
@@ -67,26 +66,53 @@ export class UnifiControllerIntegration extends Integration implements NetworkCo
|
||||
checkServerIdentity: createCustomCheckServerIdentity(await getTrustedCertificateHostnamesAsync()),
|
||||
};
|
||||
|
||||
const client = new Unifi.Controller({
|
||||
host: url.hostname,
|
||||
port: getPortFromUrl(url),
|
||||
username: this.getSecretValue("username"),
|
||||
password: this.getSecretValue("password"),
|
||||
createAxiosInstance({ cookies }) {
|
||||
return axios.create({
|
||||
adapter: "http",
|
||||
httpAgent: new HttpCookieAgent({ cookies }),
|
||||
httpsAgent: new HttpsCookieAgent({
|
||||
cookies,
|
||||
requestCert: true,
|
||||
...certificateOptions,
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
// node-unifi always connects over HTTPS, regardless of the protocol in the
|
||||
// integration URL. UniFi OS consoles use port 443 while self-hosted controllers normally
|
||||
// use 8443. If no port was provided, try both without hiding authentication errors.
|
||||
const ports = url.port ? [Number(url.port)] : [443, 8443];
|
||||
const createClientForPortAsync = async (port: number) => {
|
||||
const client = new Unifi.Controller({
|
||||
host: url.hostname,
|
||||
port,
|
||||
username: this.getSecretValue("username"),
|
||||
password: this.getSecretValue("password"),
|
||||
createAxiosInstance({ cookies }) {
|
||||
return axios.create({
|
||||
adapter: "http",
|
||||
httpAgent: new HttpCookieAgent({ cookies }),
|
||||
httpsAgent: new HttpsCookieAgent({
|
||||
cookies,
|
||||
requestCert: true,
|
||||
...certificateOptions,
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
await client.login(this.getSecretValue("username"), this.getSecretValue("password"), null);
|
||||
return client;
|
||||
await client.login(this.getSecretValue("username"), this.getSecretValue("password"), null);
|
||||
return client;
|
||||
};
|
||||
|
||||
for (const [index, port] of ports.entries()) {
|
||||
try {
|
||||
return await createClientForPortAsync(port);
|
||||
} catch (error) {
|
||||
const isLastPort = index === ports.length - 1;
|
||||
if (isLastPort || !this.isPortFallbackError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Unable to connect to the UniFi controller");
|
||||
}
|
||||
|
||||
private isPortFallbackError(error: unknown) {
|
||||
if (typeof error === "object" && error !== null && "isAxiosError" in error && error.isAxiosError === true) {
|
||||
return !("response" in error) || error.response === undefined;
|
||||
}
|
||||
|
||||
return error instanceof Error && error.message === "failed to detect UniFiOS status";
|
||||
}
|
||||
|
||||
private getStatusValueOverAllSites<S extends HealthSubsystem>(
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { readFile, writeFile } from "fs/promises";
|
||||
import {
|
||||
getIntegrationDocumentationUrl,
|
||||
integrationDefs,
|
||||
integrationKinds,
|
||||
} from "../packages/definitions/src/integration";
|
||||
import { integrationDefs } from "../packages/definitions/src/integration";
|
||||
|
||||
const FILE = "docs/README.md";
|
||||
const MAX_COLUMNS_PER_ROW = 7;
|
||||
@@ -25,29 +21,19 @@ async function updateIntegrationList() {
|
||||
}
|
||||
|
||||
// Generate the new integration list
|
||||
const integrations = integrationKinds
|
||||
.filter((kind) => kind !== "mock")
|
||||
.map((kind) => ({
|
||||
...integrationDefs[kind],
|
||||
documentationUrl: getIntegrationDocumentationUrl(kind),
|
||||
}))
|
||||
.toSorted((a, b) => a.name.localeCompare(b.name));
|
||||
const integrations = Object.values(integrationDefs)
|
||||
.filter((def) => def.name !== "Mock")
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
const tableRows: string[] = [];
|
||||
let currentRow: string[] = [];
|
||||
|
||||
integrations.forEach((integration) => {
|
||||
const cellBody = `
|
||||
<img src="${integration.iconUrl}" alt="${integration.name}" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">${integration.name.replaceAll(" ", "<br/>")}</p>
|
||||
`;
|
||||
let cellContent = `<div>${cellBody}</div>`;
|
||||
if (integration.documentationUrl !== null) {
|
||||
cellContent = `<a href="${integration.documentationUrl}" target="_blank" rel="noreferrer noopener">${cellBody}</a>`;
|
||||
}
|
||||
|
||||
currentRow.push(`<td align="center">
|
||||
${cellContent}
|
||||
<a href="${integration.documentationUrl}" target="_blank" rel="noreferrer noopener">
|
||||
<img src="${integration.iconUrl}" alt="${integration.name}" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">${integration.name.replaceAll(" ", "<br/>")}</p>
|
||||
</a>
|
||||
</td>`);
|
||||
|
||||
if (currentRow.length === MAX_COLUMNS_PER_ROW) {
|
||||
@@ -81,7 +67,4 @@ ${endMarker}`;
|
||||
await writeFile(FILE, newContent, "utf8");
|
||||
}
|
||||
|
||||
updateIntegrationList().catch((error: unknown) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
updateIntegrationList().catch(console.error);
|
||||
Reference in new issue
Block a user