mirror of
https://github.com/Mo3he/Axis_Cam_Tailscale.git
synced 2026-10-01 03:55:40 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd598ec8c6 |
@@ -6,7 +6,7 @@ This repository provides an **ACAP package** that installs the [Tailscale VPN cl
|
||||
|
||||
- Secure remote access to cameras
|
||||
- Easy to install via EAP package
|
||||
- Works on **Axis OS 11.11+** (non-root version)
|
||||
- Works on **Axis OS 12+** (non-root version)
|
||||
- Works on **legacy Axis OS 9.x / 10.x** via the ACAP 3 variant
|
||||
- Based on **WireGuard VPN** technology
|
||||
|
||||
@@ -14,9 +14,8 @@ This repository provides an **ACAP package** that installs the [Tailscale VPN cl
|
||||
[](LICENSE)
|
||||

|
||||
[](https://github.com/sponsors/Mo3he)
|
||||
[](https://www.buymeacoffee.com/mo3he)
|
||||
|
||||
> **Disclaimer:** This is an independent, community-developed ACAP package and is not an official Axis Communications product. It is not affiliated with, endorsed by, or supported by Axis Communications AB. Use it at your own risk. For official Axis software, visit axis.com
|
||||
> **Disclaimer:** This is an independent, community-developed ACAP package and is not an official Axis Communications product. It was developed entirely on personal time and is not affiliated with, endorsed by, or supported by Axis Communications AB. Use it at your own risk. For official Axis software, visit axis.com
|
||||
|
||||
> **Tailscale Notice:** Tailscale is a product of Tailscale Inc. This package independently redistributes the Tailscale binaries under the [BSD 3-Clause License](LICENSE) and is not affiliated with, endorsed by, or supported by Tailscale Inc. For the official Tailscale client, visit [tailscale.com](https://tailscale.com).
|
||||
---
|
||||
@@ -31,7 +30,6 @@ This repository provides an **ACAP package** that installs the [Tailscale VPN cl
|
||||
- [Purpose](#purpose)
|
||||
- [Useful Links](#useful-links)
|
||||
- [Compatibility](#compatibility)
|
||||
- [Roadmap](#roadmap)
|
||||
- [Star History](#star-history)
|
||||
- [Support](#support)
|
||||
|
||||
@@ -74,8 +72,6 @@ All parameters are configurable via the web UI (**Open → Settings** card) and
|
||||
| Auth Key | *(empty)* | Pre-authentication key (`tskey-auth-...`). Cleared automatically after first successful connection. Leave blank to authenticate via browser. |
|
||||
| HTTP Proxy Port | `8080` | Port for the outbound HTTP/HTTPS proxy. |
|
||||
| SOCKS5 Proxy Port | `1080` | Port for the outbound SOCKS5 proxy. |
|
||||
| Accept DNS | `off` | Passes `--accept-dns=true` to `tailscale up`. Allows the tailnet to push DNS settings to the camera. Off by default to avoid overriding the camera's DNS configuration. Not available on `armv7hf_acap3`. |
|
||||
| Accept Routes | `off` | Passes `--accept-routes=true` to `tailscale up`. Allows the camera to use subnet routes advertised by other nodes in the tailnet. Not available on `armv7hf_acap3`. |
|
||||
|
||||
---
|
||||
|
||||
@@ -129,11 +125,11 @@ docker cp $(docker create <package_name>):/opt/app ./build
|
||||
|
||||
## Good News
|
||||
|
||||
Tailscale ACAP can now run **without root privileges**, making it compatible with **Axis OS 11.11+**.
|
||||
Tailscale ACAP can now run **without root privileges**, making it compatible with **Axis OS 12+**.
|
||||
|
||||
- Runs in **user space networking mode**.
|
||||
|
||||
For **full kernel networking**, use the **ROOT** version. Note: ROOT mode requires Axis OS 11.11–11.x — Axis OS 12 and later removed root access for third-party applications.
|
||||
For **full networking features**, use the **ROOT** version on Axis OS < 12.
|
||||
|
||||
### Legacy camera support (Axis OS 9.x / 10.x)
|
||||
|
||||
@@ -168,11 +164,11 @@ The Tailscale ACAP is compatible with Axis cameras with **ARM** and **AARCH64**-
|
||||
|---|---|---|---|
|
||||
| `aarch64` | AArch64 | 11.11+ (ACAP 4) | Standard, userspace networking, configurable proxy ports |
|
||||
| `armv7hf` | ARMv7 | 11.11+ (ACAP 4) | Standard, userspace networking, configurable proxy ports |
|
||||
| `aarch64_root` | AArch64 | 11.11 – 11.x (ACAP 4) | Full kernel networking (root) — not supported on OS 12+ |
|
||||
| `armv7hf_root` | ARMv7 | 11.11 – 11.x (ACAP 4) | Full kernel networking (root) — not supported on OS 12+ |
|
||||
| `aarch64_root` | AArch64 | 10 or earlier | Full kernel networking (root) |
|
||||
| `armv7hf_root` | ARMv7 | 10 or earlier | Full kernel networking (root) |
|
||||
| `armv7hf_acap3` | ARMv7 | **9.x – 10.x** | Legacy cameras, ACAP SDK 3 |
|
||||
|
||||
> Not sure which variant to use? Check **System → Properties → Firmware version** on your camera. Axis OS 12+ → use the standard variant (`aarch64` or `armv7hf`). Axis OS 11.11–11.x → standard variant, or ROOT if you need kernel networking. Axis OS 9.x/10.x on ARMv7 → use `armv7hf_acap3`.
|
||||
> Not sure which variant to use? Check **System → Properties → Firmware version** on your camera. Axis OS 11.11+ → use the standard variant (`aarch64` or `armv7hf`). Axis OS 9/10 on ARMv7 → use `armv7hf_acap3`. Axis OS 10 or earlier on AArch64 → use `aarch64_root`.
|
||||
|
||||
You can verify your device details using the following command:
|
||||
|
||||
@@ -185,26 +181,6 @@ curl --anyauth "*" -u <username>:<password> <device_ip>/axis-cgi/basicdeviceinfo
|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
### AXIS OS 13 Preparation
|
||||
|
||||
AXIS OS 13 (scheduled for September 2026) introduces several breaking changes that affect all ACAP applications. The following items are required to maintain compatibility. See the full [AXIS OS 13 breaking changes](https://www.axis.com/for-developers/news/AXIS-OS-13-breaking-changes) announcement for details.
|
||||
|
||||
- [ ] **Recompile for 64-bit time (Y2038)** - AXIS OS 13 switches to a 64-bit time interface. All ACAP apps must be recompiled against the updated SDK. Cameras with incompatible apps installed will roll back the OS upgrade rather than proceed.
|
||||
- [ ] **Sign the ACAP via the Axis ACAP Portal** - AXIS OS 13 removes the ability to install unsigned applications in production environments. The app must be submitted and signed through the official Axis ACAP Portal to remain installable.
|
||||
- [ ] **Migrate to Manifest Schema v2** - The `manifest.json` must use Manifest Schema v2, including an explicit declaration of compatible AXIS OS versions, to satisfy the new signing and compatibility requirements.
|
||||
- [ ] **Audit for executable stack usage** - Any ACAP compiled with an executable stack must be recompiled to comply with the new security restrictions in AXIS OS 13.
|
||||
- [ ] **Verify web UI works over HTTPS** - AXIS OS 13 enforces HTTPS-only connections by default. The bundled web UI must be tested to confirm it functions correctly under this constraint.
|
||||
|
||||
### General Improvements
|
||||
|
||||
- [x] **Accept DNS from tailnet toggle** - Add an opt-in setting to the settings page that passes `--accept-dns=true` to `tailscale up`. Defaults to off to prevent Tailscale from overriding `resolv.conf` on cameras that don't need MagicDNS.
|
||||
- [x] **Accept routes toggle** - Add an opt-in setting that passes `--accept-routes=true` to `tailscale up`, allowing the camera to use subnet routes advertised by other nodes in the tailnet.
|
||||
- [ ] **Switch to tiny-tailscale binaries** - Evaluate replacing the bundled `tailscale` and `tailscaled` binaries with [tiny-tailscale](https://github.com/iamromulan/tiny-tailscale) builds. These combine both into a single binary, strip unused features, and are significantly smaller (~43% reduction), reducing install size and memory footprint across all architectures.
|
||||
|
||||
---
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#Mo3he/Axis_Cam_Tailscale&Date)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
--border: #262a35;
|
||||
--text: #e4e6ed;
|
||||
--muted: #8b8fa3;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #4f8ff7;
|
||||
--green: #22c55e;
|
||||
--yellow: #f59e0b;
|
||||
--red: #ef4444;
|
||||
@@ -26,7 +26,7 @@
|
||||
--border: #e0e3e8;
|
||||
--text: #1a1a2e;
|
||||
--muted: #6b7084;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #2563eb;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
@@ -231,7 +231,7 @@
|
||||
word-break: break-all;
|
||||
}
|
||||
.log-box .log-line { display: block; }
|
||||
.log-box .log-line:hover { background: rgba(46,45,45,0.06); }
|
||||
.log-box .log-line:hover { background: rgba(79,143,247,0.06); }
|
||||
.log-line .ts { color: var(--muted); opacity: 0.6; }
|
||||
.log-line .msg-info { color: var(--accent); }
|
||||
.log-line .msg-warn { color: var(--yellow); }
|
||||
@@ -291,8 +291,8 @@
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 14px;
|
||||
background: rgba(46,45,45,0.1);
|
||||
border: 1px solid rgba(46,45,45,0.2);
|
||||
background: rgba(79,143,247,0.1);
|
||||
border: 1px solid rgba(79,143,247,0.2);
|
||||
}
|
||||
.update-banner.visible { display: flex; }
|
||||
.update-banner .update-text {
|
||||
@@ -326,6 +326,18 @@
|
||||
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<svg width="26" height="26" viewBox="0 0 128 128" fill="none">
|
||||
<rect width="128" height="128" rx="28" fill="#0166FF"/>
|
||||
<circle cx="32" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="96" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="32" cy="64" r="13" fill="white"/>
|
||||
<circle cx="64" cy="64" r="13" fill="white"/>
|
||||
<circle cx="96" cy="64" r="13" fill="white"/>
|
||||
<circle cx="32" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="96" r="13" fill="white"/>
|
||||
<circle cx="96" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
</svg>
|
||||
<h1>Tailscale VPN</h1>
|
||||
</div>
|
||||
<button class="theme-btn" id="themeToggle" aria-label="Toggle theme">
|
||||
@@ -407,22 +419,22 @@
|
||||
<div class="settings-form">
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-server">Custom Server URL</label>
|
||||
<input class="settings-input" id="input-server" type="text" autocomplete="off" placeholder="https://controlplane.example.com (leave blank for Tailscale)">
|
||||
<input class="settings-input" id="input-server" type="text" placeholder="https://controlplane.example.com (leave blank for Tailscale)">
|
||||
<span class="settings-hint">Leave blank to use official Tailscale servers.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-authkey">Auth Key</label>
|
||||
<input class="settings-input" id="input-authkey" type="text" autocomplete="off" placeholder="tskey-auth-... (leave blank to use browser login)">
|
||||
<input class="settings-input" id="input-authkey" type="password" placeholder="tskey-auth-... (leave blank to use browser login)">
|
||||
<span class="settings-hint">One-time use. Cleared automatically after first successful connection.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-http-port">HTTP Proxy Port</label>
|
||||
<input class="settings-input" id="input-http-port" type="text" autocomplete="off" placeholder="8080">
|
||||
<input class="settings-input" id="input-http-port" type="text" placeholder="8080">
|
||||
<span class="settings-hint">Port for the outbound HTTP/HTTPS proxy. Default: 8080.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-socks-port">SOCKS5 Proxy Port</label>
|
||||
<input class="settings-input" id="input-socks-port" type="text" autocomplete="off" placeholder="1080">
|
||||
<input class="settings-input" id="input-socks-port" type="text" placeholder="1080">
|
||||
<span class="settings-hint">Port for the SOCKS5 proxy. Default: 1080.</span>
|
||||
</div>
|
||||
<div class="settings-actions">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"embeddedSdkVersion": "3.0",
|
||||
"vendorUrl": "https://github.com/Mo3he/Axis_Cam_Tailscale",
|
||||
"runMode": "respawn",
|
||||
"version": "1.98.4",
|
||||
"version": "1.96.4",
|
||||
"architecture": "aarch64"
|
||||
},
|
||||
"configuration": {
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#define CONFIG_FILE "/usr/local/packages/Tailscale_VPN/localdata/params.conf"
|
||||
#define RUN_SCRIPT "/usr/local/packages/Tailscale_VPN/Tailscale_VPN_run"
|
||||
|
||||
static AXParameter *g_ax_handle = NULL;
|
||||
static pid_t child_pid = -1;
|
||||
static guint reload_timer_id = 0;
|
||||
|
||||
@@ -103,7 +102,7 @@ static gboolean watchdog_cb(gpointer G_GNUC_UNUSED data) {
|
||||
/* If child exited 0, auth succeeded — clear AuthKey via axparameter */
|
||||
if (exit_code == 0 && g_ax_handle && cfg_auth_key && *cfg_auth_key) {
|
||||
GError *err = NULL;
|
||||
if (ax_parameter_set(g_ax_handle, "AuthKey", "", TRUE, &err)) {
|
||||
if (ax_parameter_set(g_ax_handle, "AuthKey", "", &err)) {
|
||||
free(cfg_auth_key); cfg_auth_key = strdup("");
|
||||
syslog(LOG_INFO, "AuthKey cleared after successful auth");
|
||||
} else {
|
||||
@@ -163,6 +162,8 @@ static void write_config_file(void) {
|
||||
|
||||
/* ── ACAP parameter callback ─────────────────────────────────────────────── */
|
||||
|
||||
static AXParameter *g_ax_handle = NULL;
|
||||
|
||||
static gboolean debounced_restart(gpointer G_GNUC_UNUSED data) {
|
||||
reload_timer_id = 0;
|
||||
if (g_ax_handle)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
--border: #262a35;
|
||||
--text: #e4e6ed;
|
||||
--muted: #8b8fa3;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #4f8ff7;
|
||||
--green: #22c55e;
|
||||
--yellow: #f59e0b;
|
||||
--red: #ef4444;
|
||||
@@ -26,7 +26,7 @@
|
||||
--border: #e0e3e8;
|
||||
--text: #1a1a2e;
|
||||
--muted: #6b7084;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #2563eb;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
@@ -231,7 +231,7 @@
|
||||
word-break: break-all;
|
||||
}
|
||||
.log-box .log-line { display: block; }
|
||||
.log-box .log-line:hover { background: rgba(46,45,45,0.06); }
|
||||
.log-box .log-line:hover { background: rgba(79,143,247,0.06); }
|
||||
.log-line .ts { color: var(--muted); opacity: 0.6; }
|
||||
.log-line .msg-info { color: var(--accent); }
|
||||
.log-line .msg-warn { color: var(--yellow); }
|
||||
@@ -291,8 +291,8 @@
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 14px;
|
||||
background: rgba(46,45,45,0.1);
|
||||
border: 1px solid rgba(46,45,45,0.2);
|
||||
background: rgba(79,143,247,0.1);
|
||||
border: 1px solid rgba(79,143,247,0.2);
|
||||
}
|
||||
.update-banner.visible { display: flex; }
|
||||
.update-banner .update-text {
|
||||
@@ -326,6 +326,18 @@
|
||||
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<svg width="26" height="26" viewBox="0 0 128 128" fill="none">
|
||||
<rect width="128" height="128" rx="28" fill="#0166FF"/>
|
||||
<circle cx="32" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="96" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="32" cy="64" r="13" fill="white"/>
|
||||
<circle cx="64" cy="64" r="13" fill="white"/>
|
||||
<circle cx="96" cy="64" r="13" fill="white"/>
|
||||
<circle cx="32" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="96" r="13" fill="white"/>
|
||||
<circle cx="96" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
</svg>
|
||||
<h1>Tailscale VPN</h1>
|
||||
</div>
|
||||
<button class="theme-btn" id="themeToggle" aria-label="Toggle theme">
|
||||
@@ -407,22 +419,22 @@
|
||||
<div class="settings-form">
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-server">Custom Server URL</label>
|
||||
<input class="settings-input" id="input-server" type="text" autocomplete="off" placeholder="https://controlplane.example.com (leave blank for Tailscale)">
|
||||
<input class="settings-input" id="input-server" type="text" placeholder="https://controlplane.example.com (leave blank for Tailscale)">
|
||||
<span class="settings-hint">Leave blank to use official Tailscale servers.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-authkey">Auth Key</label>
|
||||
<input class="settings-input" id="input-authkey" type="text" autocomplete="off" placeholder="tskey-auth-... (leave blank to use browser login)">
|
||||
<input class="settings-input" id="input-authkey" type="password" placeholder="tskey-auth-... (leave blank to use browser login)">
|
||||
<span class="settings-hint">One-time use. Cleared automatically after first successful connection.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-http-port">HTTP Proxy Port</label>
|
||||
<input class="settings-input" id="input-http-port" type="text" autocomplete="off" placeholder="8080">
|
||||
<input class="settings-input" id="input-http-port" type="text" placeholder="8080">
|
||||
<span class="settings-hint">Port for the outbound HTTP/HTTPS proxy. Default: 8080.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-socks-port">SOCKS5 Proxy Port</label>
|
||||
<input class="settings-input" id="input-socks-port" type="text" autocomplete="off" placeholder="1080">
|
||||
<input class="settings-input" id="input-socks-port" type="text" placeholder="1080">
|
||||
<span class="settings-hint">Port for the SOCKS5 proxy. Default: 1080.</span>
|
||||
</div>
|
||||
<div class="settings-actions">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"vendorUrl": "https://github.com/Mo3he/Axis_Cam_Tailscale",
|
||||
"runMode": "respawn",
|
||||
"version": "1.98.4",
|
||||
"version": "1.96.4",
|
||||
"architecture": "aarch64"
|
||||
},
|
||||
"configuration": {
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#define CONFIG_FILE "/usr/local/packages/Tailscale_VPN/localdata/params.conf"
|
||||
#define RUN_SCRIPT "/usr/local/packages/Tailscale_VPN/Tailscale_VPN_run"
|
||||
|
||||
static AXParameter *g_ax_handle = NULL;
|
||||
static pid_t child_pid = -1;
|
||||
static guint reload_timer_id = 0;
|
||||
|
||||
@@ -87,7 +86,7 @@ static gboolean watchdog_cb(gpointer G_GNUC_UNUSED data) {
|
||||
/* If child exited 0, auth succeeded — clear AuthKey via axparameter */
|
||||
if (exit_code == 0 && g_ax_handle && cfg_auth_key && *cfg_auth_key) {
|
||||
GError *err = NULL;
|
||||
if (ax_parameter_set(g_ax_handle, "AuthKey", "", TRUE, &err)) {
|
||||
if (ax_parameter_set(g_ax_handle, "AuthKey", "", &err)) {
|
||||
free(cfg_auth_key); cfg_auth_key = strdup("");
|
||||
syslog(LOG_INFO, "AuthKey cleared after successful auth");
|
||||
} else {
|
||||
@@ -137,6 +136,8 @@ static void write_config_file(void) {
|
||||
cache_get(&cfg_custom_server, "(default)"));
|
||||
}
|
||||
|
||||
static AXParameter *g_ax_handle = NULL;
|
||||
|
||||
static gboolean debounced_restart(gpointer G_GNUC_UNUSED data) {
|
||||
reload_timer_id = 0;
|
||||
if (g_ax_handle)
|
||||
|
||||
+21
-9
@@ -11,7 +11,7 @@
|
||||
--border: #262a35;
|
||||
--text: #e4e6ed;
|
||||
--muted: #8b8fa3;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #4f8ff7;
|
||||
--green: #22c55e;
|
||||
--yellow: #f59e0b;
|
||||
--red: #ef4444;
|
||||
@@ -26,7 +26,7 @@
|
||||
--border: #e0e3e8;
|
||||
--text: #1a1a2e;
|
||||
--muted: #6b7084;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #2563eb;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
@@ -231,7 +231,7 @@
|
||||
word-break: break-all;
|
||||
}
|
||||
.log-box .log-line { display: block; }
|
||||
.log-box .log-line:hover { background: rgba(46,45,45,0.06); }
|
||||
.log-box .log-line:hover { background: rgba(79,143,247,0.06); }
|
||||
.log-line .ts { color: var(--muted); opacity: 0.6; }
|
||||
.log-line .msg-info { color: var(--accent); }
|
||||
.log-line .msg-warn { color: var(--yellow); }
|
||||
@@ -291,8 +291,8 @@
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 14px;
|
||||
background: rgba(46,45,45,0.1);
|
||||
border: 1px solid rgba(46,45,45,0.2);
|
||||
background: rgba(79,143,247,0.1);
|
||||
border: 1px solid rgba(79,143,247,0.2);
|
||||
}
|
||||
.update-banner.visible { display: flex; }
|
||||
.update-banner .update-text {
|
||||
@@ -326,6 +326,18 @@
|
||||
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<svg width="26" height="26" viewBox="0 0 128 128" fill="none">
|
||||
<rect width="128" height="128" rx="28" fill="#0166FF"/>
|
||||
<circle cx="32" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="96" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="32" cy="64" r="13" fill="white"/>
|
||||
<circle cx="64" cy="64" r="13" fill="white"/>
|
||||
<circle cx="96" cy="64" r="13" fill="white"/>
|
||||
<circle cx="32" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="96" r="13" fill="white"/>
|
||||
<circle cx="96" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
</svg>
|
||||
<h1>Tailscale VPN</h1>
|
||||
</div>
|
||||
<button class="theme-btn" id="themeToggle" aria-label="Toggle theme">
|
||||
@@ -407,22 +419,22 @@
|
||||
<div class="settings-form">
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-server">Custom Server URL</label>
|
||||
<input class="settings-input" id="input-server" type="text" autocomplete="off" placeholder="https://controlplane.example.com (leave blank for Tailscale)">
|
||||
<input class="settings-input" id="input-server" type="text" placeholder="https://controlplane.example.com (leave blank for Tailscale)">
|
||||
<span class="settings-hint">Leave blank to use official Tailscale servers.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-authkey">Auth Key</label>
|
||||
<input class="settings-input" id="input-authkey" type="text" autocomplete="off" placeholder="tskey-auth-... (leave blank to use browser login)">
|
||||
<input class="settings-input" id="input-authkey" type="password" placeholder="tskey-auth-... (leave blank to use browser login)">
|
||||
<span class="settings-hint">One-time use. Cleared automatically after first successful connection.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-http-port">HTTP Proxy Port</label>
|
||||
<input class="settings-input" id="input-http-port" type="text" autocomplete="off" placeholder="8080">
|
||||
<input class="settings-input" id="input-http-port" type="text" placeholder="8080">
|
||||
<span class="settings-hint">Port for the outbound HTTP/HTTPS proxy. Default: 8080.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-socks-port">SOCKS5 Proxy Port</label>
|
||||
<input class="settings-input" id="input-socks-port" type="text" autocomplete="off" placeholder="1080">
|
||||
<input class="settings-input" id="input-socks-port" type="text" placeholder="1080">
|
||||
<span class="settings-hint">Port for the SOCKS5 proxy. Default: 1080.</span>
|
||||
</div>
|
||||
<div class="settings-actions">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"embeddedSdkVersion": "3.0",
|
||||
"vendorUrl": "https://github.com/Mo3he/Axis_Cam_Tailscale",
|
||||
"runMode": "respawn",
|
||||
"version": "1.98.4",
|
||||
"version": "1.96.4",
|
||||
"architecture": "armv7hf"
|
||||
},
|
||||
"configuration": {
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
|
||||
static pid_t child_pid = -1;
|
||||
static guint reload_timer_id = 0;
|
||||
static AXParameter *g_ax_handle = NULL;
|
||||
|
||||
static char *cfg_custom_server = NULL;
|
||||
static char *cfg_auth_key = NULL;
|
||||
@@ -103,7 +102,7 @@ static gboolean watchdog_cb(gpointer G_GNUC_UNUSED data) {
|
||||
/* If child exited 0, auth succeeded — clear AuthKey via axparameter */
|
||||
if (exit_code == 0 && g_ax_handle && cfg_auth_key && *cfg_auth_key) {
|
||||
GError *err = NULL;
|
||||
if (ax_parameter_set(g_ax_handle, "AuthKey", "", TRUE, &err)) {
|
||||
if (ax_parameter_set(g_ax_handle, "AuthKey", "", &err)) {
|
||||
free(cfg_auth_key); cfg_auth_key = strdup("");
|
||||
syslog(LOG_INFO, "AuthKey cleared after successful auth");
|
||||
} else {
|
||||
@@ -163,6 +162,8 @@ static void write_config_file(void) {
|
||||
|
||||
/* ── ACAP parameter callback ─────────────────────────────────────────────── */
|
||||
|
||||
static AXParameter *g_ax_handle = NULL;
|
||||
|
||||
static gboolean debounced_restart(gpointer G_GNUC_UNUSED data) {
|
||||
reload_timer_id = 0;
|
||||
if (g_ax_handle)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
--border: #262a35;
|
||||
--text: #e4e6ed;
|
||||
--muted: #8b8fa3;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #4f8ff7;
|
||||
--green: #22c55e;
|
||||
--yellow: #f59e0b;
|
||||
--red: #ef4444;
|
||||
@@ -26,7 +26,7 @@
|
||||
--border: #e0e3e8;
|
||||
--text: #1a1a2e;
|
||||
--muted: #6b7084;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #2563eb;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
@@ -231,7 +231,7 @@
|
||||
word-break: break-all;
|
||||
}
|
||||
.log-box .log-line { display: block; }
|
||||
.log-box .log-line:hover { background: rgba(46,45,45,0.06); }
|
||||
.log-box .log-line:hover { background: rgba(79,143,247,0.06); }
|
||||
.log-line .ts { color: var(--muted); opacity: 0.6; }
|
||||
.log-line .msg-info { color: var(--accent); }
|
||||
.log-line .msg-warn { color: var(--yellow); }
|
||||
@@ -291,8 +291,8 @@
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 14px;
|
||||
background: rgba(46,45,45,0.1);
|
||||
border: 1px solid rgba(46,45,45,0.2);
|
||||
background: rgba(79,143,247,0.1);
|
||||
border: 1px solid rgba(79,143,247,0.2);
|
||||
}
|
||||
.update-banner.visible { display: flex; }
|
||||
.update-banner .update-text {
|
||||
@@ -326,6 +326,18 @@
|
||||
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<svg width="26" height="26" viewBox="0 0 128 128" fill="none">
|
||||
<rect width="128" height="128" rx="28" fill="#0166FF"/>
|
||||
<circle cx="32" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="96" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="32" cy="64" r="13" fill="white"/>
|
||||
<circle cx="64" cy="64" r="13" fill="white"/>
|
||||
<circle cx="96" cy="64" r="13" fill="white"/>
|
||||
<circle cx="32" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="96" r="13" fill="white"/>
|
||||
<circle cx="96" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
</svg>
|
||||
<h1>Tailscale VPN</h1>
|
||||
</div>
|
||||
<button class="theme-btn" id="themeToggle" aria-label="Toggle theme">
|
||||
@@ -407,22 +419,22 @@
|
||||
<div class="settings-form">
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-server">Custom Server URL</label>
|
||||
<input class="settings-input" id="input-server" type="text" autocomplete="off" placeholder="https://controlplane.example.com (leave blank for Tailscale)">
|
||||
<input class="settings-input" id="input-server" type="text" placeholder="https://controlplane.example.com (leave blank for Tailscale)">
|
||||
<span class="settings-hint">Leave blank to use official Tailscale servers.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-authkey">Auth Key</label>
|
||||
<input class="settings-input" id="input-authkey" type="text" autocomplete="off" placeholder="tskey-auth-... (leave blank to use browser login)">
|
||||
<input class="settings-input" id="input-authkey" type="password" placeholder="tskey-auth-... (leave blank to use browser login)">
|
||||
<span class="settings-hint">One-time use. Cleared automatically after first successful connection.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-http-port">HTTP Proxy Port</label>
|
||||
<input class="settings-input" id="input-http-port" type="text" autocomplete="off" placeholder="8080">
|
||||
<input class="settings-input" id="input-http-port" type="text" placeholder="8080">
|
||||
<span class="settings-hint">Port for the outbound HTTP/HTTPS proxy. Default: 8080.</span>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<label class="settings-label" for="input-socks-port">SOCKS5 Proxy Port</label>
|
||||
<input class="settings-input" id="input-socks-port" type="text" autocomplete="off" placeholder="1080">
|
||||
<input class="settings-input" id="input-socks-port" type="text" placeholder="1080">
|
||||
<span class="settings-hint">Port for the SOCKS5 proxy. Default: 1080.</span>
|
||||
</div>
|
||||
<div class="settings-actions">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"vendorUrl": "https://github.com/Mo3he/Axis_Cam_Tailscale",
|
||||
"runMode": "respawn",
|
||||
"version": "1.98.4",
|
||||
"version": "1.96.4",
|
||||
"architecture": "armv7hf"
|
||||
},
|
||||
"configuration": {
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
static pid_t child_pid = -1;
|
||||
static guint reload_timer_id = 0;
|
||||
static AXParameter *g_ax_handle = NULL;
|
||||
|
||||
static char *cfg_custom_server = NULL;
|
||||
static char *cfg_auth_key = NULL;
|
||||
@@ -87,7 +86,7 @@ static gboolean watchdog_cb(gpointer G_GNUC_UNUSED data) {
|
||||
/* If child exited 0, auth succeeded — clear AuthKey via axparameter */
|
||||
if (exit_code == 0 && g_ax_handle && cfg_auth_key && *cfg_auth_key) {
|
||||
GError *err = NULL;
|
||||
if (ax_parameter_set(g_ax_handle, "AuthKey", "", TRUE, &err)) {
|
||||
if (ax_parameter_set(g_ax_handle, "AuthKey", "", &err)) {
|
||||
free(cfg_auth_key); cfg_auth_key = strdup("");
|
||||
syslog(LOG_INFO, "AuthKey cleared after successful auth");
|
||||
} else {
|
||||
@@ -137,6 +136,8 @@ static void write_config_file(void) {
|
||||
cache_get(&cfg_custom_server, "(default)"));
|
||||
}
|
||||
|
||||
static AXParameter *g_ax_handle = NULL;
|
||||
|
||||
static gboolean debounced_restart(gpointer G_GNUC_UNUSED data) {
|
||||
reload_timer_id = 0;
|
||||
if (g_ax_handle)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
--border: #262a35;
|
||||
--text: #e4e6ed;
|
||||
--muted: #8b8fa3;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #4f8ff7;
|
||||
--green: #22c55e;
|
||||
--yellow: #f59e0b;
|
||||
--red: #ef4444;
|
||||
@@ -26,7 +26,7 @@
|
||||
--border: #e0e3e8;
|
||||
--text: #1a1a2e;
|
||||
--muted: #6b7084;
|
||||
--accent: #2e2d2d;
|
||||
--accent: #2563eb;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
@@ -231,7 +231,7 @@
|
||||
word-break: break-all;
|
||||
}
|
||||
.log-box .log-line { display: block; }
|
||||
.log-box .log-line:hover { background: rgba(46,45,45,0.06); }
|
||||
.log-box .log-line:hover { background: rgba(79,143,247,0.06); }
|
||||
.log-line .ts { color: var(--muted); opacity: 0.6; }
|
||||
.log-line .msg-info { color: var(--accent); }
|
||||
.log-line .msg-warn { color: var(--yellow); }
|
||||
@@ -257,8 +257,8 @@
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 14px;
|
||||
background: rgba(46,45,45,0.1);
|
||||
border: 1px solid rgba(46,45,45,0.2);
|
||||
background: rgba(79,143,247,0.1);
|
||||
border: 1px solid rgba(79,143,247,0.2);
|
||||
}
|
||||
.update-banner.visible { display: flex; }
|
||||
.update-banner .update-text {
|
||||
@@ -292,6 +292,18 @@
|
||||
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<svg width="26" height="26" viewBox="0 0 128 128" fill="none">
|
||||
<rect width="128" height="128" rx="28" fill="#0166FF"/>
|
||||
<circle cx="32" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="96" cy="32" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="32" cy="64" r="13" fill="white"/>
|
||||
<circle cx="64" cy="64" r="13" fill="white"/>
|
||||
<circle cx="96" cy="64" r="13" fill="white"/>
|
||||
<circle cx="32" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
<circle cx="64" cy="96" r="13" fill="white"/>
|
||||
<circle cx="96" cy="96" r="13" fill="white" opacity="0.4"/>
|
||||
</svg>
|
||||
<h1>Tailscale VPN</h1>
|
||||
</div>
|
||||
<button class="theme-btn" id="themeToggle" aria-label="Toggle theme">
|
||||
|
||||
@@ -2,7 +2,7 @@ PACKAGENAME=Tailscale_VPN
|
||||
MENUNAME="Tailscale VPN"
|
||||
VENDOR="Mo3he"
|
||||
APPMAJORVERSION=1
|
||||
APPMINORVERSION=98
|
||||
APPMINORVERSION=96
|
||||
APPMICROVERSION=4
|
||||
APPTYPE=armv7hf
|
||||
APPNAME=Tailscale_VPN
|
||||
|
||||
+39
-57
@@ -3,22 +3,22 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tailscale VPN for Axis Devices</title>
|
||||
<meta name="description" content="Install Tailscale VPN directly on your Axis device. Secure remote access with WireGuard, no extra hardware needed.">
|
||||
<title>Tailscale VPN for Axis Cameras</title>
|
||||
<meta name="description" content="Install Tailscale VPN directly on your Axis camera. Secure remote access with WireGuard, no extra hardware needed.">
|
||||
|
||||
<!-- Open Graph / LinkedIn -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://mo3he.github.io/Axis_Cam_Tailscale/">
|
||||
<meta property="og:title" content="Tailscale VPN for Axis Devices">
|
||||
<meta property="og:description" content="Secure remote access to your Axis devices over WireGuard. No extra hardware, no complex network config - just install and connect.">
|
||||
<meta property="og:title" content="Tailscale VPN for Axis Cameras">
|
||||
<meta property="og:description" content="Secure remote access to your Axis cameras over WireGuard. No extra hardware, no complex network config - just install and connect.">
|
||||
<meta property="og:image" content="https://mo3he.github.io/Axis_Cam_Tailscale/og-image.png">
|
||||
<meta property="og:image:width" content="1340">
|
||||
<meta property="og:image:height" content="724">
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Tailscale VPN for Axis Devices">
|
||||
<meta name="twitter:description" content="Secure remote access to your Axis devices over WireGuard. No extra hardware, no complex network config - just install and connect.">
|
||||
<meta name="twitter:title" content="Tailscale VPN for Axis Cameras">
|
||||
<meta name="twitter:description" content="Secure remote access to your Axis cameras over WireGuard. No extra hardware, no complex network config - just install and connect.">
|
||||
<meta name="twitter:image" content="https://mo3he.github.io/Axis_Cam_Tailscale/og-image.png">
|
||||
|
||||
<style>
|
||||
@@ -117,11 +117,6 @@
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.hero h1 span { color: var(--accent); }
|
||||
.hero-word {
|
||||
display: inline-block;
|
||||
color: var(--accent);
|
||||
transition: opacity 0.25s, transform 0.25s;
|
||||
}
|
||||
.hero p {
|
||||
font-size: 1.2rem;
|
||||
color: var(--muted);
|
||||
@@ -228,7 +223,7 @@
|
||||
width: fit-content;
|
||||
}
|
||||
.tag-recommended { background: rgba(52, 211, 153, 0.15); color: var(--green); }
|
||||
.tag-acap3 { background: rgba(168, 85, 247, 0.15); color: #a855f7; }
|
||||
.tag-custom { background: rgba(251, 191, 36, 0.15); color: #fbbf24; }
|
||||
.tag-root { background: rgba(239, 68, 68, 0.15); color: #ef4444; }
|
||||
.download-card h3 {
|
||||
font-size: 1.1rem;
|
||||
@@ -349,8 +344,8 @@
|
||||
<!-- Hero -->
|
||||
<section class="hero">
|
||||
<div class="badge">Open Source · ACAP Package</div>
|
||||
<h1>Tailscale VPN for<br>Axis <span class="hero-word" id="heroWord">Cameras</span></h1>
|
||||
<p>Secure remote access to your Axis devices over WireGuard. No extra hardware, no complex network config - just install and connect.</p>
|
||||
<h1>Tailscale VPN for <span>Axis Cameras</span></h1>
|
||||
<p>Secure remote access to your Axis cameras over WireGuard. No extra hardware, no complex network config - just install and connect.</p>
|
||||
<div class="hero-buttons">
|
||||
<a href="#downloads" class="btn btn-primary">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||||
@@ -368,7 +363,7 @@
|
||||
<div style="background:rgba(79,143,247,0.07);border:1px solid rgba(79,143,247,0.18);border-radius:10px;padding:12px 18px;font-size:12.5px;color:var(--muted);line-height:1.6;">
|
||||
<strong style="color:var(--text);">Disclaimer:</strong>
|
||||
This is an independent, community-developed ACAP package and is <strong style="color:var(--text);">not an official Axis Communications product</strong>.
|
||||
It is not affiliated with, endorsed by, or supported by Axis Communications AB.
|
||||
It was developed entirely on personal time and is not affiliated with, endorsed by, or supported by Axis Communications AB.
|
||||
Use at your own risk. For official Axis software, visit <a href="https://www.axis.com" target="_blank" rel="noopener">axis.com</a>.
|
||||
</div>
|
||||
</div>
|
||||
@@ -383,12 +378,12 @@
|
||||
<div class="feature-card">
|
||||
<div class="icon">⚡</div>
|
||||
<h3>No Root Required</h3>
|
||||
<p>Runs in user-space networking mode on Axis OS 12+. No need to enable root access on your device.</p>
|
||||
<p>Runs in user-space networking mode on Axis OS 12+. No need to enable root access on your camera.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">📦</div>
|
||||
<h3>Simple EAP Install</h3>
|
||||
<p>Upload the .eap file through your device's web interface. Start the app and authenticate - done.</p>
|
||||
<p>Upload the .eap file through your camera's web interface. Start the app and authenticate - done.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🔄</div>
|
||||
@@ -398,19 +393,19 @@
|
||||
<div class="feature-card">
|
||||
<div class="icon">🌐</div>
|
||||
<h3>Headscale Compatible</h3>
|
||||
<p>Supports self-hosted Headscale servers with configurable server URL and auth key, built into every variant.</p>
|
||||
<p>The Custom variant supports self-hosted Headscale servers with configurable server URL and auth key.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🔀</div>
|
||||
<h3>Outbound Proxy</h3>
|
||||
<p>Allows the device to route its own outbound traffic through Tailscale via a local HTTP/HTTPS and SOCKS5 proxy.</p>
|
||||
<div class="icon">🏗️</div>
|
||||
<h3>ARM & AARCH64</h3>
|
||||
<p>Supports both ARM (armv7hf) and AARCH64 architectures, covering a wide range of Axis camera models.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Downloads -->
|
||||
<section id="downloads" class="downloads">
|
||||
<h2>Download</h2>
|
||||
<p class="subtitle">Pick the right variant for your device and Axis OS version.</p>
|
||||
<p class="subtitle">Pick the right variant for your camera and Axis OS version.</p>
|
||||
<div class="download-grid">
|
||||
<!-- Standard -->
|
||||
<div class="download-card">
|
||||
@@ -428,6 +423,22 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Custom -->
|
||||
<div class="download-card">
|
||||
<div class="tag tag-custom">Custom / Headscale</div>
|
||||
<h3>Custom Server</h3>
|
||||
<p>Supports custom Tailscale control servers and auth keys. Ideal for self-hosted Headscale setups.</p>
|
||||
<div class="arch-buttons">
|
||||
<a class="arch-btn" data-asset="custom" data-arch="aarch64" href="https://github.com/Mo3he/Axis_Cam_Tailscale/releases/latest" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||||
AARCH64
|
||||
</a>
|
||||
<a class="arch-btn" data-asset="custom" data-arch="armv7hf" href="https://github.com/Mo3he/Axis_Cam_Tailscale/releases/latest" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||||
ARM
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ROOT -->
|
||||
<div class="download-card">
|
||||
<div class="tag tag-root">Root · Legacy</div>
|
||||
@@ -444,18 +455,6 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ACAP3 -->
|
||||
<div class="download-card">
|
||||
<div class="tag tag-acap3">ACAP3 · Legacy</div>
|
||||
<h3>ACAP3 (Older Axis OS)</h3>
|
||||
<p>For devices running Axis OS versions that do not support ACAP4. ARM only.</p>
|
||||
<div class="arch-buttons">
|
||||
<a class="arch-btn" data-asset="acap3" data-arch="armv7hf" href="https://github.com/Mo3he/Axis_Cam_Tailscale/releases/latest" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||||
ARM
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -467,14 +466,14 @@
|
||||
<div class="step-num">1</div>
|
||||
<div class="step-text">
|
||||
<strong>Download the EAP</strong>
|
||||
<span>Grab the right .eap file for your device architecture from the releases page above.</span>
|
||||
<span>Grab the right .eap file for your camera architecture from the releases page above.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num">2</div>
|
||||
<div class="step-text">
|
||||
<strong>Upload to your device</strong>
|
||||
<span>Log into your Axis device web interface and go to <strong>Apps → Add App</strong>. Upload the .eap file.</span>
|
||||
<strong>Upload to your camera</strong>
|
||||
<span>Log into your Axis camera web interface and go to <strong>Apps → Add App</strong>. Upload the .eap file.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
@@ -493,7 +492,6 @@
|
||||
<a href="https://github.com/Mo3he/Axis_Cam_Tailscale" target="_blank" rel="noopener">GitHub</a>
|
||||
<a href="https://github.com/Mo3he/Axis_Cam_Tailscale/releases" target="_blank" rel="noopener">Releases</a>
|
||||
<a href="https://github.com/sponsors/Mo3he" target="_blank" rel="noopener">Sponsor</a>
|
||||
<a href="https://buymeacoffee.com/mo3he" target="_blank" rel="noopener">Buy Me a Coffee</a>
|
||||
<a href="https://tailscale.com/" target="_blank" rel="noopener">Tailscale</a>
|
||||
<a href="https://www.axis.com/" target="_blank" rel="noopener">Axis</a>
|
||||
</div>
|
||||
@@ -540,22 +538,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Rotate hero word
|
||||
var heroWord = document.getElementById('heroWord');
|
||||
var devices = ['Cameras', 'Door Stations', 'Intercoms', 'Speakers', 'Radars', 'Encoders'];
|
||||
var wordIdx = 0;
|
||||
function cycleWord() {
|
||||
wordIdx = (wordIdx + 1) % devices.length;
|
||||
heroWord.style.opacity = '0';
|
||||
heroWord.style.transform = 'translateY(8px)';
|
||||
setTimeout(function() {
|
||||
heroWord.textContent = devices[wordIdx];
|
||||
heroWord.style.opacity = '1';
|
||||
heroWord.style.transform = 'translateY(0)';
|
||||
}, 250);
|
||||
}
|
||||
setInterval(cycleWord, 3000);
|
||||
|
||||
// Rewrite download links to point directly to latest release assets
|
||||
fetch('https://api.github.com/repos/Mo3he/Axis_Cam_Tailscale/releases/latest')
|
||||
.then(function(r) { return r.json(); })
|
||||
@@ -571,9 +553,9 @@
|
||||
var archMatch = name.indexOf(arch) !== -1;
|
||||
var typeMatch = false;
|
||||
if (type === 'Tailscale_VPN') {
|
||||
typeMatch = name.indexOf('root') === -1 && name.indexOf('acap3') === -1;
|
||||
} else if (type === 'acap3') {
|
||||
typeMatch = name.indexOf('acap3') !== -1;
|
||||
typeMatch = name.indexOf('custom') === -1 && name.indexOf('root') === -1;
|
||||
} else if (type === 'custom') {
|
||||
typeMatch = name.indexOf('custom') !== -1;
|
||||
} else if (type === 'ROOT') {
|
||||
typeMatch = name.indexOf('root') !== -1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user