Skip to content

Commit 96a3caa

Browse files
authored
Merge pull request #209 from not-first/tailscale-devices
Update Tailscale Devices Widget
2 parents 03b939c + 0286c47 commit 96a3caa

File tree

3 files changed

+58
-171
lines changed

3 files changed

+58
-171
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ A collection of custom widgets for <a href="https://github.com/glanceapp/glance"
124124
* [Steam specials](widgets/steam-specials/README.md) - show a list of discounted games on Steam (by @svilenmarkov)
125125
* [Syncthing](widgets/syncthing/README.md) - show Syncthing folder status (by @JohnCannon97)
126126
* [Synology Disk Station](widgets/synology-disk-station/README.md) - displays RAM and CPU usage and used storage of your Synology NAS (by @Chachigo)
127-
* [Tailscale devices](widgets/tailscale-devices/README.md) - show all devices inside to a Tailscale tailnet along with their connection status, update availability and IP (by @not-first)
127+
* [Tailscale devices](widgets/tailscale-devices/README.md) - show all devices inside a Tailscale tailnet along with their connection status, owner, update availability and IP (by @not-first)
128128
* [Tautulli Stats](widgets/tautulli-stats/README.md) - show various home stats from Tautulli such as recently watched, top movies/shows, etc (by @SkyAllinott)
129129
* [Technitium DNS Stats](widgets/technitium-dns-stats/README.md) - show stats from Technitium DNS Server (by @eribbey)
130130
* [Time Bar](widgets/time-bar/README.md) - show percentage of day/month/year elapsed (by @anant-j)

widgets/tailscale-devices/README.md

Lines changed: 56 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
A widget for Glance that displays all devices in your Tailscale tailnet, showing their connection status, update availability, and IP addresses.
66

7-
## Option 1: API Key (Recommended)
7+
## Setup
88

9-
The standard implementation using Tailscale API keys. Simple to set up but requires manual api key renewal every 90 days maximum.
9+
### Option 1: API Key (Recommended)
1010

11-
### Setup
11+
The standard implementation using Tailscale API keys. Simple to set up but requires manual API key renewal every 90 days maximum.
1212

1313
1. **Generate API Key:**
1414
- Go to [Tailscale admin console](https://login.tailscale.com/admin/settings/keys)
@@ -18,169 +18,49 @@ The standard implementation using Tailscale API keys. Simple to set up but requi
1818
2. **Configure Widget:**
1919
- Set the `TAILSCALE_API_KEY` environment variable to your generated key
2020

21-
```yaml
22-
- type: custom-api
23-
title: Tailscale Devices
24-
title-url: https://login.tailscale.com/admin/machines
25-
url: https://api.tailscale.com/api/v2/tailnet/-/devices
26-
headers:
27-
Authorization: Bearer ${TAILSCALE_API_KEY}
28-
cache: 10m
29-
template: |
30-
{{/* User Variables */}}
31-
{{/* Set to true if you'd like an indicator for online devices */}}
32-
{{ $enableOnlineIndicator := false }}
33-
34-
<style>
35-
.device-info-container {
36-
position: relative;
37-
overflow: hidden;
38-
height: 1.5em;
39-
}
40-
41-
.device-info {
42-
display: flex;
43-
transition: transform 0.2s ease, opacity 0.2s ease;
44-
}
45-
46-
.device-ip {
47-
position: absolute;
48-
top: 0;
49-
left: 0;
50-
transform: translateY(-100%);
51-
opacity: 0;
52-
transition: transform 0.2s ease, opacity 0.2s ease;
53-
}
54-
55-
.device-info-container:hover .device-info {
56-
transform: translateY(100%);
57-
opacity: 0;
58-
}
59-
60-
.device-info-container:hover .device-ip {
61-
transform: translateY(0);
62-
opacity: 1;
63-
}
64-
65-
.update-indicator {
66-
width: 8px;
67-
height: 8px;
68-
border-radius: 50%;
69-
background-color: var(--color-primary);
70-
display: inline-block;
71-
margin-left: 4px;
72-
vertical-align: middle;
73-
}
74-
75-
.offline-indicator {
76-
width: 8px;
77-
height: 8px;
78-
border-radius: 50%;
79-
background-color: var(--color-negative);
80-
display: inline-block;
81-
margin-left: 4px;
82-
vertical-align: middle;
83-
}
84-
85-
.online-indicator {
86-
width: 8px;
87-
height: 8px;
88-
border-radius: 50%;
89-
background-color: var(--color-positive);
90-
display: inline-block;
91-
margin-left: 4px;
92-
vertical-align: middle;
93-
}
94-
95-
.device-name-container {
96-
display: flex;
97-
align-items: center;
98-
gap: 8px;
99-
}
100-
101-
.indicators-container {
102-
display: flex;
103-
align-items: center;
104-
gap: 4px;
105-
}
106-
</style>
107-
<ul class="list list-gap-10 collapsible-container" data-collapse-after="4">
108-
{{ range .JSON.Array "devices" }}
109-
<li>
110-
<div class="flex items-center gap-10">
111-
<div class="device-name-container grow">
112-
<span class="size-h4 block text-truncate color-primary">
113-
{{ findMatch "^([^.]+)" (.String "name") }}
114-
</span>
115-
<div class="indicators-container">
116-
{{ if (.Bool "updateAvailable") }}
117-
<span class="update-indicator" data-popover-type="text" data-popover-text="Update Available"></span>
118-
{{ end }}
119-
120-
{{ $lastSeen := .String "lastSeen" | parseTime "rfc3339" }}
121-
{{ if not ($lastSeen.After (offsetNow "-10s")) }}
122-
{{ $lastSeenTimezoned := $lastSeen.In now.Location }}
123-
<span class="offline-indicator" data-popover-type="text"
124-
data-popover-text="Offline - Last seen {{ $lastSeenTimezoned.Format " Jan 2 3:04pm" }}"></span>
125-
{{ else if $enableOnlineIndicator }}
126-
<span class="online-indicator" data-popover-type="text" data-popover-text="Online"></span>
127-
{{ end }}
128-
</div>
129-
</div>
130-
</div>
131-
<div class="device-info-container">
132-
<ul class="list-horizontal-text device-info">
133-
<li>{{ .String "os" }}</li>
134-
<li>{{ .String "user" }}</li>
135-
</ul>
136-
<div class="device-ip">
137-
{{ .String "addresses.0"}}
138-
</div>
139-
</div>
140-
</li>
141-
{{ end }}
142-
</ul>
143-
```
144-
145-
## Option 2: OAuth Proxy (Alternative)
21+
### Option 2: OAuth Proxy (Alternative)
14622

14723
An alternative implementation that uses automatically refreshing OAuth tokens, eliminating the need for manual key renewal. Created by @5at0ri, requires [5at0ri's OAuth Proxy](https://github.com/5at0ri/tailscale-token-manager).
14824

149-
### Setup
150-
15125
1. **Deploy OAuth Proxy:**
15226
- Set up OAuth client credentials in your [Tailscale admin console](https://login.tailscale.com/admin/settings/oauth)
15327
- Deploy the [OAuth Proxy container](https://github.com/5at0ri/tailscale-token-manager)
15428
- Configure it with your OAuth Client ID and Client Secret
15529

15630
2. **Configure Widget:**
157-
- Point the `url` to your OAuth proxy container endpoint
158-
- The proxy handles all authentication automatically
31+
- Replace the `url` line with: `url: http://tailscale-token-manager:1180/devices`
32+
- Remove the `headers:` section entirely
33+
- Change `cache:` to `10s` for more frequent updates
34+
35+
## Template
15936

16037
```yaml
16138
- type: custom-api
16239
title: Tailscale Devices
16340
title-url: https://login.tailscale.com/admin/machines
164-
url: http://tailscale-token-manager:1180/devices # URL to your OAuth proxy container
165-
cache: 10s
41+
url: https://api.tailscale.com/api/v2/tailnet/-/devices
42+
headers:
43+
Authorization: Bearer ${TAILSCALE_API_KEY}
44+
cache: 10m
45+
options:
46+
# collapseAfter: 4
47+
# disableOfflineIndicator: true
48+
# disableUpdateIndicator: true
49+
# prioritiseTags: true
16650
template: |
167-
{{/* User Variables */}}
168-
{{/* Set to true if you'd like an indicator for online devices */}}
169-
{{ $enableOnlineIndicator := false }}
170-
17151
<style>
172-
.device-info-container {
52+
.device-info-container-tailscale {
17353
position: relative;
17454
overflow: hidden;
17555
height: 1.5em;
17656
}
17757
178-
.device-info {
58+
.device-info-tailscale {
17959
display: flex;
18060
transition: transform 0.2s ease, opacity 0.2s ease;
18161
}
18262
183-
.device-ip {
63+
.device-ip-tailscale {
18464
position: absolute;
18565
top: 0;
18666
left: 0;
@@ -189,17 +69,17 @@ An alternative implementation that uses automatically refreshing OAuth tokens, e
18969
transition: transform 0.2s ease, opacity 0.2s ease;
19070
}
19171
192-
.device-info-container:hover .device-info {
72+
.device-info-container-tailscale:hover .device-info-tailscale {
19373
transform: translateY(100%);
19474
opacity: 0;
19575
}
19676
197-
.device-info-container:hover .device-ip {
77+
.device-info-container-tailscale:hover .device-ip-tailscale {
19878
transform: translateY(0);
19979
opacity: 1;
20080
}
20181
202-
.update-indicator {
82+
.update-indicator-tailscale {
20383
width: 8px;
20484
height: 8px;
20585
border-radius: 50%;
@@ -209,7 +89,7 @@ An alternative implementation that uses automatically refreshing OAuth tokens, e
20989
vertical-align: middle;
21090
}
21191
212-
.offline-indicator {
92+
.offline-indicator-tailscale {
21393
width: 8px;
21494
height: 8px;
21595
border-radius: 50%;
@@ -219,62 +99,69 @@ An alternative implementation that uses automatically refreshing OAuth tokens, e
21999
vertical-align: middle;
220100
}
221101
222-
.online-indicator {
223-
width: 8px;
224-
height: 8px;
225-
border-radius: 50%;
226-
background-color: var(--color-positive);
227-
display: inline-block;
228-
margin-left: 4px;
229-
vertical-align: middle;
230-
}
231-
232-
.device-name-container {
102+
.device-name-container-tailscale {
233103
display: flex;
234104
align-items: center;
235105
gap: 8px;
236106
}
237107
238-
.indicators-container {
108+
.indicators-container-tailscale {
239109
display: flex;
240110
align-items: center;
241111
gap: 4px;
242112
}
243113
</style>
244-
<ul class="list list-gap-10 collapsible-container" data-collapse-after="4">
114+
<ul class="list list-gap-10 collapsible-container" data-collapse-after="{{ .Options.IntOr "collapseAfter" 3 }}">
245115
{{ range .JSON.Array "devices" }}
246116
<li>
247117
<div class="flex items-center gap-10">
248-
<div class="device-name-container grow">
118+
<div class="device-name-container-tailscale grow">
249119
<span class="size-h4 block text-truncate color-primary">
250120
{{ findMatch "^([^.]+)" (.String "name") }}
251121
</span>
252-
<div class="indicators-container">
253-
{{ if (.Bool "updateAvailable") }}
254-
<span class="update-indicator" data-popover-type="text" data-popover-text="Update Available"></span>
122+
<div class="indicators-container-tailscale">
123+
{{ if and (not ($.Options.BoolOr "disableUpdateIndicator" false)) (.Bool "updateAvailable") }}
124+
<span class="update-indicator-tailscale" data-popover-type="text" data-popover-text="Update Available"></span>
255125
{{ end }}
256126
127+
{{ if not ($.Options.BoolOr "disableOfflineIndicator" false) }}
257128
{{ $lastSeen := .String "lastSeen" | parseTime "rfc3339" }}
258129
{{ if not ($lastSeen.After (offsetNow "-10s")) }}
259130
{{ $lastSeenTimezoned := $lastSeen.In now.Location }}
260-
<span class="offline-indicator" data-popover-type="text"
131+
<span class="offline-indicator-tailscale" data-popover-type="text"
261132
data-popover-text="Offline - Last seen {{ $lastSeenTimezoned.Format " Jan 2 3:04pm" }}"></span>
262-
{{ else if $enableOnlineIndicator }}
263-
<span class="online-indicator" data-popover-type="text" data-popover-text="Online"></span>
264133
{{ end }}
134+
{{ end }}
135+
265136
</div>
266137
</div>
267138
</div>
268-
<div class="device-info-container">
269-
<ul class="list-horizontal-text device-info">
139+
<div class="device-info-container-tailscale">
140+
<ul class="list-horizontal-text device-info-tailscale">
270141
<li>{{ .String "os" }}</li>
271-
<li>{{ .String "user" }}</li>
142+
<li>
143+
{{ if and ($.Options.BoolOr "prioritiseTags" false) (.Exists "tags.0") }}
144+
{{ trimPrefix "tag:" (.String "tags.0") }}
145+
{{ else }}
146+
{{ .String "user" }}
147+
{{ end }}
148+
</li>
272149
</ul>
273-
<div class="device-ip">
150+
<div class="device-ip-tailscale">
274151
{{ .String "addresses.0"}}
275152
</div>
276153
</div>
277154
</li>
278155
{{ end }}
279156
</ul>
280157
```
158+
159+
## Available Options
160+
*All options have default values and are not required for the widget to function.*
161+
162+
| Option | Type | Default | Description |
163+
| ------------------------- | ------- | ------- | -------------------------------------------------------------------------------------------- |
164+
| `collapseAfter` | integer | `3` | Number of devices to show before collapsing the list. |
165+
| `disableOfflineIndicator` | boolean | `false` | When set to `true`, hides the red dot indicator for offline devices. |
166+
| `disableUpdateIndicator` | boolean | `false` | When set to `true`, hides the blue dot indicator when updates are available. |
167+
| `prioritiseTags` | boolean | `false` | When set to `true`, displays the device's primary tag instead of the user (if a tag exists). |

widgets/tailscale-devices/meta.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
title: Tailscale devices
2-
description: show all devices inside to a Tailscale tailnet along with their connection status, update availability and IP
2+
description: show all devices inside a Tailscale tailnet along with their connection status, owner, update availability and IP
33
author: not-first

0 commit comments

Comments
 (0)