mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
Compare commits
50 Commits
ba27769fbd
...
v25.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b0c7f2c01 | ||
|
|
bf3d497d26 | ||
|
|
47d9a9300e | ||
|
|
fd107fe4f7 | ||
|
|
9513a5a2ae | ||
|
|
bf151bd69a | ||
|
|
3a312fd5ed | ||
|
|
03bf4f4050 | ||
|
|
bf3fdd2766 | ||
|
|
befb2574e9 | ||
|
|
52de3ae872 | ||
|
|
9be9728cd6 | ||
|
|
65a5d35801 | ||
|
|
1e714005a5 | ||
|
|
2a25f38268 | ||
|
|
65a0f90bd8 | ||
|
|
4d77ff3ff1 | ||
|
|
500129c440 | ||
|
|
a320b2910f | ||
|
|
ac7e278a36 | ||
|
|
04ab1d1fb3 | ||
|
|
268ce870a3 | ||
|
|
cda1d8b877 | ||
|
|
a68aa0bc57 | ||
|
|
7f2a1740cc | ||
|
|
3ba5c70045 | ||
|
|
fec18daab4 | ||
|
|
b71037a129 | ||
|
|
4f4ca0cfcb | ||
|
|
adc761a3df | ||
|
|
458577e071 | ||
|
|
9d4eafea42 | ||
|
|
ac8f48c78e | ||
|
|
2000a4291b | ||
|
|
97389b988f | ||
|
|
daba38ee0a | ||
|
|
03b110950b | ||
|
|
7d9e84668c | ||
|
|
e37acba4c4 | ||
|
|
3f00c7fc40 | ||
|
|
c687128f68 | ||
|
|
8542d51dcf | ||
|
|
c02e725b04 | ||
|
|
a0e117f92e | ||
|
|
ffa0457342 | ||
|
|
838352388f | ||
|
|
dd01bebadd | ||
|
|
bdf6e62ea6 | ||
|
|
eb693bfdb2 | ||
|
|
cc5c4a6f06 |
18
.github/workflows/docker_dev.yml
vendored
18
.github/workflows/docker_dev.yml
vendored
@@ -1,4 +1,3 @@
|
||||
---
|
||||
name: docker
|
||||
|
||||
on:
|
||||
@@ -37,7 +36,7 @@ jobs:
|
||||
|
||||
- name: Get release version
|
||||
id: get_version
|
||||
run: echo "::set-output name=version::${{ 'Dev' }}"
|
||||
run: echo "version=Dev" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create .VERSION file
|
||||
run: echo "${{ steps.get_version.outputs.version }}" >> .VERSION
|
||||
@@ -46,13 +45,11 @@ jobs:
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
ghcr.io/jokob-sk/netalertx-dev
|
||||
jokobsk/netalertx-dev
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=schedule
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
@@ -60,24 +57,20 @@ jobs:
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
- name: Log in to Github Container registry
|
||||
- name: Log in to Github Container Registry (GHCR)
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: jokob-sk
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to DockerHub
|
||||
- name: Log in to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# # Disable this after use
|
||||
# - name: Prune Docker Builder
|
||||
# run: docker builder prune --force
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
@@ -86,6 +79,3 @@ jobs:
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
# # ⚠ disable cache if build is failing to download debian packages
|
||||
# cache-from: type=registry,ref=ghcr.io/jokob-sk/netalertx:buildcache
|
||||
# cache-to: type=registry,ref=ghcr.io/jokob-sk/netalertx:buildcache,mode=max
|
||||
|
||||
4
.github/workflows/docker_prod.yml
vendored
4
.github/workflows/docker_prod.yml
vendored
@@ -48,8 +48,8 @@ jobs:
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
jokobsk/pi.alert
|
||||
jokobsk/netalertx
|
||||
ghcr.io/jokob-sk/netalertx
|
||||
jokobsk/netalertx
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=semver,pattern={{version}},value=${{ inputs.version }}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
# NetAlertX - Network, presence scanner and alert framework
|
||||
|
||||
Get visibility of what's going on on your WIFI/LAN network and enable presence detection of important devices. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugins](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) with auto-generated UI and in-build notification system. Build out and easily maintain your network source of truth (NSoT).
|
||||
Get visibility of what's going on on your WIFI/LAN network and enable presence detection of important devices. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md#readme) with auto-generated UI and in-build notification system. Build out and easily maintain your network source of truth (NSoT).
|
||||
|
||||
|
||||
| [📑 Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) | [🚀 Releases](https://github.com/jokob-sk/NetAlertX/releases) | [📚 Docs](https://jokob-sk.github.io/NetAlertX/) | [🔌 Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) | [🤖 Ask AI](https://gurubase.io/g/netalertx)
|
||||
| [📑 Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) | [🚀 Releases](https://github.com/jokob-sk/NetAlertX/releases) | [📚 Docs](https://jokob-sk.github.io/NetAlertX/) | [🔌 Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) | [🤖 Ask AI](https://gurubase.io/g/netalertx)
|
||||
|----------------------| ----------------------| ----------------------| ----------------------| ----------------------|
|
||||
|
||||
![showcase][showcase]
|
||||
@@ -30,7 +30,7 @@ Get visibility of what's going on on your WIFI/LAN network and enable presence d
|
||||
|
||||
### Scanners
|
||||
|
||||
The app scans your network for **New devices**, **New connections** (re-connections), **Disconnections**, **"Always Connected" devices down**, Devices **IP changes** and **Internet IP address changes**. Discovery & scan methods include: **arp-scan**, **Pi-hole - DB import**, **Pi-hole - DHCP leases import**, **Generic DHCP leases import**, **UNIFI controller import**, **SNMP-enabled router import**. Check the [Plugins](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) docs for a full lits of avaliable plugins.
|
||||
The app scans your network for **New devices**, **New connections** (re-connections), **Disconnections**, **"Always Connected" devices down**, Devices **IP changes** and **Internet IP address changes**. Discovery & scan methods include: **arp-scan**, **Pi-hole - DB import**, **Pi-hole - DHCP leases import**, **Generic DHCP leases import**, **UNIFI controller import**, **SNMP-enabled router import**. Check the [Plugins](https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md#readme) docs for a full lits of avaliable plugins.
|
||||
|
||||
### Notification gateways
|
||||
|
||||
@@ -39,7 +39,7 @@ Send notifications to more than 80+ services, including Telegram via [Apprise](h
|
||||
### Integrations and Plugins
|
||||
|
||||
Feed your data and device changes into [Home Assistant](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HOME_ASSISTANT.md), read [API endpoints](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md), or use [Webhooks](https://github.com/jokob-sk/NetAlertX/blob/main/docs/WEBHOOK_N8N.md) to setup custom automation flows. You can also
|
||||
build your own scanners with the [Plugin system](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) in as little as [15 minutes](https://www.youtube.com/watch?v=cdbxlwiWhv8).
|
||||
build your own scanners with the [Plugin system](https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md#readme) in as little as [15 minutes](https://www.youtube.com/watch?v=cdbxlwiWhv8).
|
||||
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
# NetAlertX - Network scanner & notification framework
|
||||
|
||||
| [📑 Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) | [🚀 Releases](https://github.com/jokob-sk/NetAlertX/releases) | [📚 Docs](https://jokob-sk.github.io/NetAlertX/) | [🔌 Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) | [🤖 Ask AI](https://gurubase.io/g/netalertx)
|
||||
| [📑 Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) | [🚀 Releases](https://github.com/jokob-sk/NetAlertX/releases) | [📚 Docs](https://jokob-sk.github.io/NetAlertX/) | [🔌 Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) | [🤖 Ask AI](https://gurubase.io/g/netalertx)
|
||||
|----------------------| ----------------------| ----------------------| ----------------------| ----------------------|
|
||||
|
||||
<a href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/GENERAL/github_social_image.jpg" target="_blank">
|
||||
@@ -21,7 +21,7 @@ Head to [https://netalertx.com/](https://netalertx.com/) for more gifs and scree
|
||||
## 📕 Basic Usage
|
||||
|
||||
> [!WARNING]
|
||||
> You will have to run the container on the `host` network and specify `SCAN_SUBNETS` unless you use other [plugin scanners](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md). The initial scan can take a few minutes, so please wait 5-10 minutes for the initial discovery to finish.
|
||||
> You will have to run the container on the `host` network and specify `SCAN_SUBNETS` unless you use other [plugin scanners](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). The initial scan can take a few minutes, so please wait 5-10 minutes for the initial discovery to finish.
|
||||
|
||||
```yaml
|
||||
docker run -d --rm --network=host \
|
||||
@@ -45,7 +45,7 @@ See alternative [docked-compose examples](https://github.com/jokob-sk/NetAlertX/
|
||||
| `PGID` |Application User GID | `82` |
|
||||
| `LISTEN_ADDR` |Set the specific IP Address for the listener address for the nginx webserver (web interface). This could be useful when using multiple subnets to hide the web interface from all untrusted networks. | `0.0.0.0` |
|
||||
|`TZ` |Time zone to display stats correctly. Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `Europe/Berlin` |
|
||||
|`LOADED_PLUGINS` | Default [plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) to load. Plugins cannot be loaded with `APP_CONF_OVERRIDE`, you need to use this variable instead and then specify the plugins settings with `APP_CONF_OVERRIDE`. | `["PIHOLE","ASUSWRT"]` |
|
||||
|`LOADED_PLUGINS` | Default [plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) to load. Plugins cannot be loaded with `APP_CONF_OVERRIDE`, you need to use this variable instead and then specify the plugins settings with `APP_CONF_OVERRIDE`. | `["PIHOLE","ASUSWRT"]` |
|
||||
|`APP_CONF_OVERRIDE` | JSON override for settings (except `LOADED_PLUGINS`). | `{"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","GRAPHQL_PORT":"20212"}` |
|
||||
|`ALWAYS_FRESH_INSTALL` | ⚠ If `true` will delete the content of the `/db` & `/config` folders. For testing purposes. Can be coupled with [watchtower](https://github.com/containrrr/watchtower) to have an always freshly installed `netalertx`/`netalertx-dev` image. | `true` |
|
||||
|
||||
@@ -62,7 +62,7 @@ See alternative [docked-compose examples](https://github.com/jokob-sk/NetAlertX/
|
||||
| ✅ | `:/app/db` | Folder which will contain the `app.db` database file |
|
||||
| | `:/app/log` | Logs folder useful for debugging if you have issues setting up the container |
|
||||
| | `:/app/api` | A simple [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. |
|
||||
| | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md). |
|
||||
| | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). |
|
||||
| | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REVERSE_DNS.md). |
|
||||
|
||||
> Use separate `db` and `config` directories, do not nest them.
|
||||
|
||||
26
docs/API.md
26
docs/API.md
@@ -23,9 +23,9 @@ curl 'http://host:GRAPHQL_PORT/graphql' \
|
||||
|
||||
## API Endpoint: GraphQL
|
||||
|
||||
Endpoint URL: `php/server/query_graphql.php`
|
||||
Host: `same as front end (web ui)`
|
||||
Port: `20212` or as defined by the `GRAPHQL_PORT` setting
|
||||
- Endpoint URL: `php/server/query_graphql.php`
|
||||
- Host: `same as front end (web ui)`
|
||||
- Port: `20212` or as defined by the `GRAPHQL_PORT` setting
|
||||
|
||||
### Example Query to Fetch Devices
|
||||
|
||||
@@ -126,9 +126,9 @@ The response will be in JSON format, similar to the following:
|
||||
|
||||
This API endpoint retrieves static files, that are periodically updated.
|
||||
|
||||
Endpoint URL: `php/server/query_json.php?file=<file name>`
|
||||
Host: `same as front end (web ui)`
|
||||
Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
|
||||
- Endpoint URL: `php/server/query_json.php?file=<file name>`
|
||||
- Host: `same as front end (web ui)`
|
||||
- Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
|
||||
|
||||
### When are the endpoints updated
|
||||
|
||||
@@ -148,7 +148,7 @@ You can access the following files:
|
||||
| `table_devices.json` | All of the available Devices detected by the app. |
|
||||
| `table_plugins_events.json` | The list of the unprocessed (pending) notification events (plugins_events DB table). |
|
||||
| `table_plugins_history.json` | The list of notification events history. |
|
||||
| `table_plugins_objects.json` | The content of the plugins_objects table. Find more info on the [Plugin system here](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins)|
|
||||
| `table_plugins_objects.json` | The content of the plugins_objects table. Find more info on the [Plugin system here](https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md)|
|
||||
| `language_strings.json` | The content of the language_strings table, which in turn is loaded from the plugins `config.json` definitions. |
|
||||
| `table_custom_endpoint.json` | A custom endpoint generated by the SQL query specified by the `API_CUSTOM_SQL` setting. |
|
||||
| `table_settings.json` | The content of the settings table. |
|
||||
@@ -223,9 +223,9 @@ Example JSON of the `table_devices.json` endpoint with two Devices (database row
|
||||
|
||||
This API endpoint retrieves files from the `/app/log` folder.
|
||||
|
||||
Endpoint URL: `php/server/query_logs.php?file=<file name>`
|
||||
Host: `same as front end (web ui)`
|
||||
Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
|
||||
- Endpoint URL: `php/server/query_logs.php?file=<file name>`
|
||||
- Host: `same as front end (web ui)`
|
||||
- Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
|
||||
|
||||
| File | Description |
|
||||
|--------------------------|---------------------------------------------------------------|
|
||||
@@ -248,9 +248,9 @@ Port: `20211` or as defined by the $PORT docker environment variable (same as th
|
||||
|
||||
To retrieve files from the `/app/config` folder.
|
||||
|
||||
Endpoint URL: `php/server/query_config.php?file=<file name>`
|
||||
Host: `same as front end (web ui)`
|
||||
Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
|
||||
- Endpoint URL: `php/server/query_config.php?file=<file name>`
|
||||
- Host: `same as front end (web ui)`
|
||||
- Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
|
||||
|
||||
| File | Description |
|
||||
|--------------------------|--------------------------------------------------|
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# 💾 Backing things up
|
||||
# Backing things up
|
||||
|
||||
> [!NOTE]
|
||||
> To backup 99% of your configuration backup at least the `/app/config` folder. Please read the whole page (or at least "Scenario 2: Corrupted database") for details.
|
||||
> Please also note that database definitions might change over versions. The safest way is to restore your older backups into the **same version** of the app and then gradually upgarde between releases to the latest version.
|
||||
> Note that database definitions might change over time. The safest way is to restore your older backups into the **same version** of the app they were taken from and then gradually upgarde between releases to the latest version.
|
||||
|
||||
There are 3 artifacts that can be used to backup the application:
|
||||
|
||||
@@ -12,6 +12,46 @@ There are 3 artifacts that can be used to backup the application:
|
||||
| `/config/app.conf` | Configuration file | Can be overridden with the [`APP_CONF_OVERRIDE` env variable](https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles#docker-environment-variables). |
|
||||
| `/config/devices.csv` | CSV file containing device information | Doesn't contain historical data |
|
||||
|
||||
|
||||
## Backup strategies
|
||||
|
||||
The safest approach to backups is to backup everything, by taking regular file system backups (I use [Kopia](https://github.com/kopia/kopia)).
|
||||
|
||||
Arguably, the most time is spent setting up the device list, so if only one file is kept I'd recommend to have a latest backup of the `devices_<timestamp>.csv` or `devices.csv` file, followed by the `app.conf` file. You can also download `app.conf` and `devices.csv` file in the Maintenance section:
|
||||
|
||||

|
||||
|
||||
### Scenario 1: Full backup
|
||||
|
||||
End-result: Full restore
|
||||
|
||||
#### 💾 Source artifacts:
|
||||
|
||||
- `/app/db/app.db` (uncorrupted)
|
||||
- `/app/config/app.conf`
|
||||
|
||||
#### 📥 Recovery:
|
||||
|
||||
To restore the application map the above files as described in the [Setup documentation](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#docker-paths).
|
||||
|
||||
|
||||
### Scenario 2: Corrupted database
|
||||
|
||||
End-result: Partial restore (historical data and some plugin data will be missing)
|
||||
|
||||
#### 💾 Source artifacts:
|
||||
|
||||
- `/app/config/app.conf`
|
||||
- `/app/config/devices_<timestamp>.csv` or `/app/config/devices.csv`
|
||||
|
||||
#### 📥 Recovery:
|
||||
|
||||
Even with a corrupted database you can recover what I would argue is 99% of the configuration.
|
||||
|
||||
- upload the `app.conf` file into the mounted `/app/config/` folder as described in the [Setup documentation](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#docker-paths).
|
||||
- rename the `devices_<timestamp>.csv` to `devices.csv` and place it in the `/app/config` folder
|
||||
- Restore the `devices.csv` backup via the [Maintenance section](./DEVICES_BULK_EDITING.md)
|
||||
|
||||
## Data and backup storage
|
||||
|
||||
To decide on a backup strategy, check where the data is stored:
|
||||
@@ -44,43 +84,4 @@ Historical data is stored in the `app.db` database (See [Database overview](./DA
|
||||
- History of Events, Notifications, Workflow Events
|
||||
- Presence history
|
||||
|
||||
## 🧭 Backup strategies
|
||||
|
||||
The safest approach to backups is to backup all of the above, by taking regular file system backups (I use [Kopia](https://github.com/kopia/kopia)).
|
||||
|
||||
Arguably, the most time is spent setting up the device list, so if only one file is kept I'd recommend to have a latest backup of the `devices_<timestamp>.csv` or `devices.csv` file, followed by the `app.conf` file. You can also download `app.conf` and `devices.csv` file in the Maintenance section:
|
||||
|
||||

|
||||
|
||||
### Scenario 1: Full backup
|
||||
|
||||
End-result: Full restore
|
||||
|
||||
#### Source artifacts:
|
||||
|
||||
- `/app/db/app.db` (uncorrupted)
|
||||
- `/app/config/app.conf`
|
||||
|
||||
#### Recovery:
|
||||
|
||||
To restore the application map the above files as described in the [Setup documentation](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#docker-paths).
|
||||
|
||||
|
||||
### Scenario 2: Corrupted database
|
||||
|
||||
End-result: Partial restore (historical data & configurations from the Maintenance section will be missing)
|
||||
|
||||
#### Source artifacts:
|
||||
|
||||
- `/app/config/app.conf`
|
||||
- `/app/config/devices_<timestamp>.csv` or `/app/config/devices.csv`
|
||||
|
||||
#### Recovery:
|
||||
|
||||
Even with a corrupted database you can recover what I would argue is 99% of the configuration.
|
||||
|
||||
- upload the `app.conf` file into the mounted `/app/config/` folder as described in the [Setup documentation](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#docker-paths).
|
||||
- rename the `devices_<timestamp>.csv` to `devices.csv` and place it in the `/app/config` folder
|
||||
- Restore the `devices.csv` backup via the [Maintenance section](./DEVICES_BULK_EDITING.md)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
### Loading...
|
||||
|
||||
Often if the application is misconfigured the `Loading...` dialog is continuously displayed. This is most likely caused by the backed failing to start. The **Maintenance -> Logs** section should give you more details on what's happenning. If there is no exception, check the Portainer log, or start the container in the foreground (without the `-d` parameter) to observe any exceptions. It's advisable to enable `trace` or `debug`. Check the [Debug tips](./DEBUG_TIPS.md) on detailed instructions.
|
||||
Often if the application is misconfigured the `Loading...` dialog is continuously displayed. This is most likely caused by the backed failing to start. The **Maintenance -> Logs** section should give you more details on what's happening. If there is no exception, check the Portainer log, or start the container in the foreground (without the `-d` parameter) to observe any exceptions. It's advisable to enable `trace` or `debug`. Check the [Debug tips](./DEBUG_TIPS.md) on detailed instructions.
|
||||
|
||||
### Incorrect SCAN_SUBNETS
|
||||
|
||||
@@ -8,7 +8,7 @@ One of the most common issues is not configuring `SCAN_SUBNETS` correctly. If th
|
||||
|
||||
### Duplicate devices and notifications
|
||||
|
||||
The app uses the MAC address as an unique identifier for devices. If a new MAC is detected a new device is added to the application and corresponding notifications are triggered. This means that if the MAC of an existing device changes, the device will be logged as a new device. You can usually prevent this from happenning by changing the device configuration (in Android, iOS, or Windows) for your network. See the [Random Macs](./RANDOM_MAC.md) guide for details.
|
||||
The app uses the MAC address as an unique identifier for devices. If a new MAC is detected a new device is added to the application and corresponding notifications are triggered. This means that if the MAC of an existing device changes, the device will be logged as a new device. You can usually prevent this from happening by changing the device configuration (in Android, iOS, or Windows) for your network. See the [Random Macs](./RANDOM_MAC.md) guide for details.
|
||||
|
||||
### Permissions
|
||||
|
||||
@@ -45,4 +45,13 @@ The link above will probably break in time too. Go to https://packages.debian.or
|
||||
|
||||
### Only Router and own device show up
|
||||
|
||||
Make sure that the subnet and interface in `SCAN_SUBNETS` are correct. If your device/NAS has multiple ethernet ports, you probably need to change `eth0` to something else.
|
||||
Make sure that the subnet and interface in `SCAN_SUBNETS` are correct. If your device/NAS has multiple ethernet ports, you probably need to change `eth0` to something else.
|
||||
|
||||
### Losing my settings and devices after an update
|
||||
|
||||
If you lose your devices and/or settings after an update that means you don't have the `/app/db` and `/app/config` folders mapped to a permanent storage. That means every time you update these folders are re-created. Make sure you have the [volumes specified correctly](./DOCKER_COMPOSE.md) in your `docker-compose.yml` or run command.
|
||||
|
||||
|
||||
### The application is slow
|
||||
|
||||
Slowness is usually caused by incorrect settings (the app might restart, so check the `app.log`), too many background processes (disable unnecessary scanners), too long scans (limit the number of scanned devices), too many disk operations, or some maintenance plugins might have failed. See the [Performance tips](./PERFORMANCE.md) docs for details.
|
||||
@@ -1,11 +1,11 @@
|
||||
|
||||
# A high-level description of the database structure
|
||||
# A high-level description of the database structure
|
||||
|
||||
⚠ Disclaimer: As I'm not the original author, some of the information might be inaccurate. Feel free to submit a PR to correct anything within this page or documentation in general.
|
||||
|
||||
The MAC address is used as a foreign key in most cases.
|
||||
|
||||
## 🔍Tables overview
|
||||
## 🔍Tables overview
|
||||
|
||||
| Table name | Description | Sample data |
|
||||
|----------------------|----------------------| ----------------------|
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
If a Plugin supplies data to the main app it's doine either vie a SQL query or via a script that updates the `last_result.log` file in the plugin folder (`front/plugins/<plugin>`).
|
||||
|
||||
For a more in-depth overview on how plugins work check the [Plugins development docs](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md).
|
||||
For a more in-depth overview on how plugins work check the [Plugins development docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md).
|
||||
|
||||
### Prerequisites
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ docker run --rm --network=host \
|
||||
|
||||
If possible, check if your issue got fixed in the `_dev` image before opening a new issue. The container is:
|
||||
|
||||
`jokobsk/netalertx-dev:latest`
|
||||
`ghcr.io/jokob-sk/netalertx-dev:latest`
|
||||
|
||||
> ⚠ Please backup your DB and config beforehand!
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ The Main Info section is where most of the device identifiable information is st
|
||||
|
||||
- **MAC**: MAC addres of the device. Not editable, unless creating a new dummy device.
|
||||
- **Last IP**: IP addres of the device. Not editable, unless creating a new dummy device.
|
||||
- **Name**: Friendly device name. Autodetected via various 🆎 Name discovery [plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md). The app attaches `(IP match)` if the name is discovered via an IP match and not MAC match which could mean the name could be incorrect as IPs might change.
|
||||
- **Name**: Friendly device name. Autodetected via various 🆎 Name discovery [plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). The app attaches `(IP match)` if the name is discovered via an IP match and not MAC match which could mean the name could be incorrect as IPs might change.
|
||||
- **Icon**: Partially autodetected. Select an existing or [add a custom icon](./ICONS.md). You can also auto-apply the same icon on all devices of the same type.
|
||||
- **Owner**: Device owner (The list is self-populated with existing owners and you can add custom values).
|
||||
- **Type**: Select a device type from the dropdown list (`Smartphone`, `Tablet`,
|
||||
|
||||
@@ -7,7 +7,7 @@ services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "jokobsk/netalertx:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
@@ -33,12 +33,13 @@ To run the container execute: `sudo docker-compose up -d`
|
||||
Example by [SeimuS](https://github.com/SeimusS).
|
||||
|
||||
```yaml
|
||||
services:
|
||||
netalertx:
|
||||
container_name: NetAlertX
|
||||
hostname: NetAlertX
|
||||
privileged: true
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: jokobsk/netalertx:latest
|
||||
environment:
|
||||
- TZ=Europe/Bratislava
|
||||
@@ -60,7 +61,7 @@ services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "jokobsk/netalertx:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
|
||||
21
docs/HELPER_SCRIPTS.md
Executable file
21
docs/HELPER_SCRIPTS.md
Executable file
@@ -0,0 +1,21 @@
|
||||
# NetAlertX Community Helper Scripts Overview
|
||||
|
||||
This page provides an overview of community-contributed scripts for NetAlertX. These scripts are not actively maintained and are provided as-is.
|
||||
|
||||
## Community Scripts
|
||||
|
||||
You can find all scripts in this [scripts GitHub folder](https://github.com/jokob-sk/NetAlertX/tree/main/scripts).
|
||||
|
||||
| Script Name | Description | Author | Version | Release Date |
|
||||
|------------|-------------|--------|---------|--------------|
|
||||
| **New Devices Checkmk Script** | Checks for new devices in NetAlertX and reports status to Checkmk. | N/A | 1.0 | 08-Jan-2025 |
|
||||
| **DB Cleanup Script** | Queries and removes old device-related entries from the database. | [laxduke](https://github.com/laxduke) | 1.0 | 23-Dec-2024 |
|
||||
| **OPNsense DHCP Lease Converter** | Retrieves DHCP lease data from OPNsense and converts it to `dnsmasq` format. | [im-redactd](https://github.com/im-redactd) | 1.0 | 24-Feb-2025 |
|
||||
|
||||
## Important Notes
|
||||
|
||||
> [!NOTE]
|
||||
> These scripts are community-supplied and not actively maintained. Use at your own discretion.
|
||||
|
||||
For detailed usage instructions, refer to each script's documentation in each [scripts GitHub folder](https://github.com/jokob-sk/NetAlertX/tree/main/scripts).
|
||||
|
||||
@@ -32,6 +32,7 @@ NetAlertX comes with MQTT support, allowing you to show all detected devices as
|
||||
- Enable MQTT
|
||||
- Fill in the details from above
|
||||
- Fill in remaining settings as per description
|
||||
- set MQTT_RUN to schedule or on_notification depending on requirements
|
||||
|
||||
![Configuration Example][configuration]
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Copying the HTML code from [Font Awesome](https://fontawesome.com/search?o=r&m=f
|
||||
|
||||
- The dropdown contains all icons already used in the app for device icons. You might need to navigate away or refresh the page once you add a new icon.
|
||||
|
||||
## 🌟 Pro Font Awesome icons
|
||||
## Font Awesome Pro icons
|
||||
|
||||
If you own the premium package of Font Awesome icons you can mount it in your Docker container the following way:
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ services:
|
||||
pialert:
|
||||
container_name: pialert
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "jokobsk/pialert:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
@@ -77,7 +77,7 @@ services:
|
||||
netalertx: # ⚠ This has changed (🟡optional)
|
||||
container_name: netalertx # ⚠ This has changed (🟡optional)
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "jokobsk/netalertx:latest" # ⚠ This has changed (🟡optional/🔺required in future)
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
@@ -105,7 +105,7 @@ services:
|
||||
pialert:
|
||||
container_name: pialert
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "jokobsk/pialert:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
@@ -127,7 +127,7 @@ services:
|
||||
netalertx: # ⚠ This has changed (🟡optional)
|
||||
container_name: netalertx # ⚠ This has changed (🟡optional)
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "jokobsk/netalertx:latest" # ⚠ This has changed (🟡optional/🔺required in future)
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
|
||||
@@ -1,59 +1,96 @@
|
||||
# Performance tips
|
||||
# Performance Optimization Guide
|
||||
|
||||
The application runs regular maintenance and DB cleanup tasks. If these tasks fail, you might encounter performance issues.
|
||||
There are several ways to improve the application's performance. The application has been tested on a range of devices, from a Raspberry Pi 4 to NAS and NUC systems. If you are running the application on a lower-end device, carefully fine-tune the performance settings to ensure an optimal user experience.
|
||||
|
||||
Most performance issues are caused by a big database or large log files. Enabling unnecessary plugins will also lead to performance degradation.
|
||||
## Common Causes of Slowness
|
||||
|
||||
You can always check the size of your database and database tables under the Maintenance page.
|
||||
Performance issues are usually caused by:
|
||||
|
||||

|
||||
- **Incorrect settings** – The app may restart unexpectedly. Check `app.log` under **Maintenance → Logs** for details.
|
||||
- **Too many background processes** – Disable unnecessary scanners.
|
||||
- **Long scan durations** – Limit the number of scanned devices.
|
||||
- **Excessive disk operations** – Optimize scanning and logging settings.
|
||||
- **Failed maintenance plugins** – Ensure maintenance tasks are running properly.
|
||||
|
||||
The application performs regular maintenance and database cleanup. If these tasks fail, performance may degrade.
|
||||
|
||||
### Database and Log File Size
|
||||
|
||||
A large database or oversized log files can slow down performance. You can check database and table sizes on the **Maintenance** page.
|
||||
|
||||

|
||||
|
||||
> [!NOTE]
|
||||
> For around 100 devices the database should be approximately `50MB` and none of the entries (rows) should exceed the value of `10 000` on a healthy system. These numbers will depend on your network activity and settings.
|
||||
> - For **~100 devices**, the database should be around **50MB**.
|
||||
> - No table should exceed **10,000 rows** in a healthy system.
|
||||
> - These numbers vary based on network activity and settings.
|
||||
|
||||
## Maintenance plugins
|
||||
---
|
||||
|
||||
There are 2 plugins responsible for maintaining the overal health of the application. One is responsible for the database cleanup and one for other tasks, such as log cleanup.
|
||||
## Maintenance Plugins
|
||||
|
||||
### DB Cleanup (DBCLNP)
|
||||
Two plugins help maintain the application’s performance:
|
||||
|
||||
The database cleanup plugin. Check details and related setting in the [DB Cleanup plugin docs](/front/plugins/db_cleanup/README.md). Make sure the plugin is not failing by checking the logs. Try changing the schedule `DBCLNP_RUN_SCHD` and the timeout `DBCLNP_RUN_TIMEOUT` (increase) if the plugin is failing to execute.
|
||||
### **1. Database Cleanup (DBCLNP)**
|
||||
- Responsible for database maintenance.
|
||||
- Check settings in the [DB Cleanup Plugin Docs](/front/plugins/db_cleanup/README.md).
|
||||
- Ensure it’s not failing by checking logs.
|
||||
- Adjust the schedule (`DBCLNP_RUN_SCHD`) and timeout (`DBCLNP_RUN_TIMEOUT`) if needed.
|
||||
|
||||
### Maintenance (MAINT)
|
||||
### **2. Maintenance (MAINT)**
|
||||
- Handles log cleanup and other maintenance tasks.
|
||||
- Check settings in the [Maintenance Plugin Docs](/front/plugins/maintenance/README.md).
|
||||
- Ensure it’s running correctly by checking logs.
|
||||
- Adjust the schedule (`MAINT_RUN_SCHD`) and timeout (`MAINT_RUN_TIMEOUT`) if needed.
|
||||
|
||||
The maintenance plugin. Check details and related setting in the [Maintenance plugin docs](/front/plugins/maintenance/README.md). Make sure the plugin is not failing by checking the logs. Try changing the schedule `MAINT_RUN_SCHD` and the timeout `MAINT_RUN_TIMEOUT` (increase) if the plugin is failing to execute.
|
||||
---
|
||||
|
||||
## Scan frequency and coverage
|
||||
## Scan Frequency and Coverage
|
||||
|
||||
The more often you scan the networks the more resources, traffic and DB read/write cycles are executed. Especially on busy networks and lower end hardware, consider increasing scan intervals (`<PLUGIN>_RUN_SCHD`) and timeouts (`<PLUGIN>_RUN_TIMEOUT`).
|
||||
Frequent scans increase resource usage, network traffic, and database read/write cycles.
|
||||
|
||||
Also consider decreasing the scanned subnet, e.g. from `/16` to `/24` if need be.
|
||||
### **Optimizations**
|
||||
- **Increase scan intervals** (`<PLUGIN>_RUN_SCHD`) on busy networks or low-end hardware.
|
||||
- **Extend scan timeouts** (`<PLUGIN>_RUN_TIMEOUT`) to prevent failures.
|
||||
- **Reduce the subnet size** – e.g., from `/16` to `/24` to lower scan loads.
|
||||
|
||||
# Store temporary files in memory
|
||||
Some plugins have additional options to limit the number of scanned devices. If certain plugins take too long to complete, check if you can optimize scan times by selecting a scan range.
|
||||
|
||||
For example, the **ICMP plugin** allows you to specify a regular expression to scan only IPs that match a specific pattern.
|
||||
|
||||
---
|
||||
|
||||
## Storing Temporary Files in Memory
|
||||
|
||||
On systems with slower I/O speeds, you can optimize performance by storing temporary files in memory. This primarily applies to the `/app/api` and `/app/log` folders.
|
||||
|
||||
Using `tmpfs` reduces disk writes and improves performance. However, it should be **disabled** if persistent logs or API data storage are required.
|
||||
|
||||
Below is an optimized `docker-compose.yml` snippet:
|
||||
|
||||
You can also store temporary files in application memory (`/app/api` and `/app/log` folders). See highlighted lines `◀` below.
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# Uncomment the line below to test the latest dev image
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "jokobsk/netalertx:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config
|
||||
- local/path/db:/app/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
# (Optional) Useful for debugging setup issues
|
||||
- local/path/logs:/app/log
|
||||
# (API: OPTION 1) use for performance
|
||||
- type: tmpfs # ◀
|
||||
target: /app/api # ◀
|
||||
# (API: OPTION 2) use when debugging issues
|
||||
# - local/path/api:/app/api
|
||||
# (API: OPTION 1) Store temporary files in memory (recommended for performance)
|
||||
- type: tmpfs # ◀
|
||||
target: /app/api # ◀
|
||||
# (API: OPTION 2) Store API data on disk (useful for debugging)
|
||||
# - local/path/api:/app/api
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Integration with PiHole
|
||||
|
||||
NetAlertX comes with 2 plugins suitable for integarting with your existing PiHole instace. One plugin is using a direct SQLite DB connection, the other leverages the DHCP.leases file generated by PiHole. You can combine both approaches and also supplement it with other [plugins](/front/plugins/README.md).
|
||||
NetAlertX comes with 2 plugins suitable for integarting with your existing PiHole instace. One plugin is using a direct SQLite DB connection, the other leverages the DHCP.leases file generated by PiHole. You can combine both approaches and also supplement it with other [plugins](/docs/PLUGINS.md).
|
||||
|
||||
## Approach 1: `DHCPLSS` Plugin - Import devices from the PiHole DHCP leases file
|
||||
|
||||
@@ -42,4 +42,4 @@ Check the [PiHole plugin readme](https://github.com/jokob-sk/NetAlertX/tree/main
|
||||
| `:/etc/pihole/pihole-FTL.db` | PiHole's `pihole-FTL.db` database file. |
|
||||
|
||||
|
||||
Check out other [plugins](/front/plugins/README.md) that can help you discover more about your network or check how to scan [Remote networks](./REMOTE_NETWORKS.md).
|
||||
Check out other [plugins](/docs/PLUGINS.md) that can help you discover more about your network or check how to scan [Remote networks](./REMOTE_NETWORKS.md).
|
||||
|
||||
113
docs/PLUGINS.md
Executable file
113
docs/PLUGINS.md
Executable file
@@ -0,0 +1,113 @@
|
||||
# 🔌 Plugins
|
||||
|
||||
NetAlertX supports additional plugins to extend its functionality, each with its own settings and options. Plugins can be loaded via the General -> `LOADED_PLUGINS` setting. For custom plugin development, refer to the [Plugin development guide](./PLUGINS_DEV.md).
|
||||
|
||||
>[!NOTE]
|
||||
> Please check this [Plugins debugging guide](./DEBUG_PLUGINS.md) and the corresponding Plugin documentation in the below table if you are facing issues.
|
||||
|
||||
## ⚡ Quick start
|
||||
|
||||
> [!TIP]
|
||||
> You can load additional Plugins via the General -> `LOADED_PLUGINS` setting.
|
||||
|
||||
1. Pick your `🔍 dev scanner` plugin (e.g. `ARPSCAN` or `NMAPDEV`), or import devices into the application with an `📥 importer` plugin. (See **✅Enabling plugins** below)
|
||||
2. Pick a `▶️ publisher` plugin, if you want to send notifications. If you don't see a publisher you'd like to use, look at the [📚_publisher_apprise](/front/plugins/_publisher_apprise/) plugin which is a proxy for over 80 notification services.
|
||||
3. Setup your [Network topology diagram](./NETWORK_TREE.md)
|
||||
4. Fine-tune [Notifications](./NOTIFICATIONS.md)
|
||||
5. [Backup your setup](./BACKUPS.md)
|
||||
6. Contribute and [Create custom plugins](./PLUGINS_DEV.md)
|
||||
|
||||
|
||||
## 📑 Available Plugins
|
||||
|
||||
Device-detecting plugins insert values into the `CurrentScan` database table. The plugins that are not required are safe to ignore, however, it makes sense to have at least some device-detecting plugins enabled, such as `ARPSCAN` or `NMAPDEV`.
|
||||
|
||||
|
||||
| ID | Type | Description | Features | Required | Data source | Detailed docs |
|
||||
|---------------|---------|--------------------------------------------|----------|----------|--------------|---------------------------------------------------------------------|
|
||||
| `APPRISE` | ▶️ | Apprise notification proxy | | | Script | [_publisher_apprise](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_apprise/) |
|
||||
| `ARPSCAN` | 🔍 | ARP-scan on current network | | | Script | [arp_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/arp_scan/) |
|
||||
| `AVAHISCAN` | 🆎 | Avahi (mDNS-based) name resolution | | | Script | [avahi_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/avahi_scan/) |
|
||||
| `ASUSWRT` | 🔍 | Import connected devices from AsusWRT | | | Script | [asuswrt_import](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/asuswrt_import/) |
|
||||
| `CSVBCKP` | ⚙ | CSV devices backup | | | Script | [csv_backup](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/csv_backup/) |
|
||||
| `CUSTPROP` | ⚙ | Managing custom device properties values | | Yes | Template | [custom_props](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/custom_props/) |
|
||||
| `DBCLNP` | ⚙ | Database cleanup | | Yes* | Script | [db_cleanup](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/db_cleanup/) |
|
||||
| `DDNS` | ⚙ | DDNS update | | | Script | [ddns_update](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/ddns_update/) |
|
||||
| `DHCPLSS` | 🔍/📥/🆎| Import devices from DHCP leases | | | Script | [dhcp_leases](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_leases/) |
|
||||
| `DHCPSRVS` | ♻ | DHCP servers | | | Script | [dhcp_servers](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_servers/) |
|
||||
| `FREEBOX` | 🔍/♻/🆎| Pull data and names from Freebox/Iliadbox | | | Script | [freebox](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/freebox/) |
|
||||
| `ICMP` | 🔍 | ICMP (ping) status checker | | | Script | [icmp_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/icmp_scan/) |
|
||||
| `INTRNT` | 🔍 | Internet IP scanner | | | Script | [internet_ip](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/internet_ip/) |
|
||||
| `INTRSPD` | ♻ | Internet speed test | | | Script | [internet_speedtest](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/internet_speedtest/) |
|
||||
| `IPNEIGH` | 🔍 | Scan ARP (IPv4) and NDP (IPv6) tables | | | Script | [ipneigh](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/ipneigh/) |
|
||||
| `LUCIRPC` | 🔍 | Import connected devices from OpenWRT | | | Script | [luci_import](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/luci_import/) |
|
||||
| `MAINT` | ⚙ | Maintenance of logs, etc. | | | Script | [maintenance](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/maintenance/) |
|
||||
| `MQTT` | ▶️ | MQTT for synching to Home Assistant | | | Script | [_publisher_mqtt](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_mqtt/) |
|
||||
| `NBTSCAN` | 🆎 | Nbtscan (NetBIOS-based) name resolution | | | Script | [nbtscan_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nbtscan_scan/) |
|
||||
| `NEWDEV` | ⚙ | New device template | | Yes | Template | [newdev_template](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/newdev_template/) |
|
||||
| `NMAP` | ♻ | Nmap port scanning & discovery | | | Script | [nmap_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nmap_scan/) |
|
||||
| `NMAPDEV` | 🔍 | Nmap dev scan on current network | | | Script | [nmap_dev_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nmap_dev_scan/) |
|
||||
| `NSLOOKUP` | 🆎 | NSLookup (DNS-based) name resolution | | | Script | [nslookup_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nslookup_scan/) |
|
||||
| `NTFPRCS` | ⚙ | Notification processing | | Yes | Template | [notification_processing](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/notification_processing/)|
|
||||
| `NTFY` | ▶️ | NTFY notifications | | | Script | [_publisher_ntfy](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_ntfy/) |
|
||||
| `OMDSDN` | 📥/🆎 | OMADA TP-Link import | 🖧 🔄 | | Script | [omada_sdn_imp](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/omada_sdn_imp/) |
|
||||
| `OMDSDNOPENAPI`| 📥/🆎 | OMADA TP-Link import via OpenAPI | 🖧 | | Script | [omada_sdn_openapi](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/omada_sdn_openapi/) |
|
||||
| `PIHOLE` | 🔍/🆎/📥| Pi-hole device import & sync | | | SQLite DB | [pihole_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/pihole_scan/) |
|
||||
| `PUSHSAFER` | ▶️ | Pushsafer notifications | | | Script | [_publisher_pushsafer](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_pushsafer/) |
|
||||
| `PUSHOVER` | ▶️ | Pushover notifications | | | Script | [_publisher_pushover](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_pushover/) |
|
||||
| `SETPWD` | ⚙ | Set password | | Yes | Template | [set_password](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/set_password/) |
|
||||
| `SMTP` | ▶️ | Email notifications | | | Script | [_publisher_email](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_email/) |
|
||||
| `SNMPDSC` | 🔍/📥 | SNMP device import & sync | | | Script | [snmp_discovery](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/snmp_discovery/) |
|
||||
| `SYNC` | 🔍/⚙/📥| Sync & import from NetAlertX instances | 🖧 🔄 | Yes | Script | [sync](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/sync/) |
|
||||
| `TELEGRAM` | ▶️ | Telegram notifications | | | Script | [_publisher_telegram](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_telegram/) |
|
||||
| `UI` | ♻ | UI specific settings | | Yes | Template | [ui_settings](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/ui_settings/) |
|
||||
| `UNFIMP` | 🔍/📥/🆎| UniFi device import & sync | 🖧 | | Script | [unifi_import](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/unifi_import/) |
|
||||
| `VNDRPDT` | ⚙ | Vendor database update | | | Script | [vendor_update](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/vendor_update/) |
|
||||
| `WEBHOOK` | ▶️ | Webhook notifications | | | Script | [_publisher_webhook](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_webhook/) |
|
||||
| `WEBMON` | ♻ | Website down monitoring | | | Script | [website_monitor](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/website_monitor/) |
|
||||
| `WOL` | ♻ | Automatic wake-on-lan | | | Script | [wake_on_lan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/wake_on_lan/) |
|
||||
|
||||
|
||||
> \* The database cleanup plugin (`DBCLNP`) is not _required_ but the app will become unusable after a while if not executed.
|
||||
> ❌ marked for removal
|
||||
> ⌚It's recommended to use the same schedule interval for all plugins responsible for discovering new devices.
|
||||
|
||||
## Plugin types
|
||||
|
||||
|
||||
| Plugin type | Icon | Description | When to run | Required | Data source [?](./PLUGINS_DEV.md) |
|
||||
| -------------- | ---- | ---------------------------------------------------------------- | ----------------------------------- | -------- | ------------------------------------- |
|
||||
| publisher | ▶️ | Sending notifications to services. | `on_notification` | ✖ | Script |
|
||||
| dev scanner | 🔍 | Create devices in the app, manages online/offline device status. | `schedule` | ✖ | Script / SQLite DB |
|
||||
| name discovery | 🆎 | Discovers names of devices via various protocols. | `before_name_updates`, `schedule` | ✖ | Script |
|
||||
| importer | 📥 | Importing devices from another service. | `schedule` | ✖ | Script / SQLite DB |
|
||||
| system | ⚙ | Providing core system functionality. | `schedule` / always on | ✖/✔ | Script / Template |
|
||||
| other | ♻ | Other plugins | misc | ✖ | Script / Template |
|
||||
|
||||
## Features
|
||||
|
||||
| Icon | Description |
|
||||
| ---- | ------------------------------------------------------------ |
|
||||
| 🖧 | Auto-imports the network topology diagram |
|
||||
| 🔄 | Has the option to sync some data back into the plugin source |
|
||||
|
||||
|
||||
## ✅Enabling plugins
|
||||
|
||||
Plugins can be enabled via Settings, and can be disabled as needed.
|
||||
|
||||
1. Research which plugin you'd like to use, enable `DISCOVER_PLUGINS` and load the required plugins in Settings via the `LOADED_PLUGINS` setting.
|
||||
1. Save the changes and review the Settings of the newly loaded plugins.
|
||||
1. Change the `<prefix>_RUN` Setting to the recommended or custom value as per the documentation of the given setting
|
||||
- If using `schedule` on a `🔍 dev scanner` plugin, make sure the schedules are the same across all `🔍 dev scanner` plugins
|
||||
|
||||
### Disabling, Unloading and Ignoring plugins
|
||||
|
||||
1. Change the `<prefix>_RUN` Setting to `disabled` if you want to disable the plugin, but keep the settings
|
||||
1. If you want to speed up the application, you can unload the plugin by unselecting it in the `LOADED_PLUGINS` setting.
|
||||
- Careful, once you save the Settings Unloaded plugin settings will be lost (old `app.conf` files are kept in the `/config` folder)
|
||||
1. You can completely ignore plugins by placing a `ignore_plugin` file into the plugin directory. Ignored plugins won't show up in the `LOADED_PLUGINS` setting.
|
||||
|
||||
## 🆕 Developing new custom plugins
|
||||
|
||||
If you want to develop a custom plugin, please read this [Plugin development guide](./PLUGINS_DEV.md).
|
||||
@@ -1,4 +1,4 @@
|
||||
## 🌟 Create a custom plugin: Overview
|
||||
# Creating a custom plugin
|
||||
|
||||
NetAlertX comes with a plugin system to feed events from third-party scripts into the UI and then send notifications, if desired. The highlighted core functionality this plugin system supports, is:
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ There is also an in-app Help / FAQ section that should be answering frequently a
|
||||
- [Better name resolution with Reverse DNS](./REVERSE_DNS.md)
|
||||
- [Network treemap configuration](./NETWORK_TREE.md)
|
||||
- [Backups](./BACKUPS.md)
|
||||
- [Plugins overview](/front/plugins/README.md)
|
||||
- [Plugins overview](/docs/PLUGINS.md)
|
||||
|
||||
#### 🐛 Debugging help & tips
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ The following workarounds should work for most complex network setups.
|
||||
|
||||
## Supplementing Plugins
|
||||
|
||||
You can use supplementary plugins that employ alternate methods. Protocols used by the `SNMPDSC` or `DHCPLSS` plugins are widely supported on different routers and can be effective as workarounds. Check the [plugins list](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) to find a plugin that works with your router and network setup.
|
||||
You can use supplementary plugins that employ alternate methods. Protocols used by the `SNMPDSC` or `DHCPLSS` plugins are widely supported on different routers and can be effective as workarounds. Check the [plugins list](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) to find a plugin that works with your router and network setup.
|
||||
|
||||
## Multiple NetAlertX Instances
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ If you are running a DNS server, such as **AdGuard**, set up **Private reverse D
|
||||
> ```
|
||||
> jokob@Synology-NAS:/$ nslookup 192.168.1.58
|
||||
> ** server can't find 58.1.168.192.in-addr.arpa: NXDOMAIN
|
||||
>
|
||||
> ```
|
||||
|
||||
> Example 2: Reverse DNS `enabled`
|
||||
|
||||
29
docs/SECURITY.md
Executable file
29
docs/SECURITY.md
Executable file
@@ -0,0 +1,29 @@
|
||||
# Securing your NetAlertX instance
|
||||
|
||||
NetAlertX is an execution framework. In order to run scanners and plugins, the application has to have access to privileged system resources. It is not recommended to expose NetAlertX to the internet without taking basic security precautions. It is highly recommended to use a VPN to access the application and to set up a password for the web interface before exposing the UI online.
|
||||
|
||||
## VPN
|
||||
|
||||
VPNs allow you to securely access your NetAlertX instance from remote locations without exposing it to the internet. A VPN encrypts your connection and prevents unauthorized access.
|
||||
|
||||
### Tailscale as an Alternative
|
||||
|
||||
If setting up a traditional VPN is not ideal, you can use [Tailscale](https://tailscale.com/) as an easy alternative. Tailscale creates a secure, encrypted connection between your devices without complex configuration. Since NetAlertX is designed to be run on private networks, Tailscale can provide a simple way to securely connect to your instance from anywhere.
|
||||
|
||||
## Setting a Password
|
||||
|
||||
By default, NetAlertX does not enforce authentication, but it is highly recommended to set a password before exposing the web interface.
|
||||
|
||||
Configure `SETPWD_enable_password` to `true` and enter your password in `SETPWD_password`. When enabled, a login dialog is displayed. If facing issues, you can always disable the login by setting `SETPWD_enable_password=false` in your `app.conf` file.
|
||||
|
||||
- The default password is `123456`.
|
||||
- Passwords are stored as SHA256 hashes for security.
|
||||
|
||||
## Additional Security Measures
|
||||
|
||||
- **Firewall Rules**: Ensure that only trusted IPs can access the NetAlertX instance.
|
||||
- **Limit Plugin Permissions**: Only enable the plugins necessary for your setup.
|
||||
- **Keep Software Updated**: Regularly update NetAlertX to receive the latest security patches.
|
||||
- **Use Read-Only API Keys**: If exposing APIs, limit privileges with read-only keys where applicable.
|
||||
|
||||
By following these security recommendations, you can help protect your NetAlertX instance from unauthorized access and potential misuse.
|
||||
@@ -32,7 +32,7 @@ The App generates two `app.conf` entries for every setting (Since version 23.8+)
|
||||
> [!NOTE]
|
||||
> This is the preferred way adding settings going forward. I'll be likely migrating all app settings into plugin-based settings.
|
||||
|
||||
Plugin settings are loaded dynamically from the `config.json` of individual plugins. If a setting isn't defined in the `app.conf` file, it is initialized via the `default_value` property of a setting from the `config.json` file. Check the [Plugins documentation](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md#-setting-object-structure), section `⚙ Setting object structure` for details on the structure of the setting.
|
||||
Plugin settings are loaded dynamically from the `config.json` of individual plugins. If a setting isn't defined in the `app.conf` file, it is initialized via the `default_value` property of a setting from the `config.json` file. Check the [Plugins documentation](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md#-setting-object-structure), section `⚙ Setting object structure` for details on the structure of the setting.
|
||||
|
||||
![Screen 1][screen1]
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "jokobsk/netalertx:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
@@ -71,4 +71,4 @@ services:
|
||||

|
||||
|
||||
10. Navigate to `<Synology URL>:20211` (or your custom port).
|
||||
11. Read the [Subnets](./SUBNETS.md) and [Plugins](/front/plugins/README.md) docs to complete your setup.
|
||||
11. Read the [Subnets](./SUBNETS.md) and [Plugins](/docs/PLUGINS.md) docs to complete your setup.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Docker Update Strategies for NetAlertX
|
||||
# Docker Update Strategies to upgrade NetAlertX
|
||||
|
||||
This guide outlines several approaches for updating Docker containers, specifically using NetAlertX. Each method offers different benefits depending on the situation. Here are the methods:
|
||||
This guide outlines approaches for updating Docker containers, usually when upgrading to a newer version of NetAlertX. Each method offers different benefits depending on the situation. Here are the methods:
|
||||
|
||||
- Manual: Direct commands to stop, remove, and rebuild containers.
|
||||
- Dockcheck: Semi-automated with more control, suited for bulk updates.
|
||||
@@ -10,6 +10,9 @@ You can choose any approach that fits your workflow.
|
||||
|
||||
> In the examples I assume that the container name is `netalertx` and the image name is `netalertx` as well.
|
||||
|
||||
> [!NOTE]
|
||||
> See also [Backup strategies](./BACKUPS.md) to be on the safe side.
|
||||
|
||||
## 1. Manual Updates
|
||||
|
||||
Use this method when you need precise control over a single container or when dealing with a broken container that needs immediate attention.
|
||||
|
||||
BIN
docs/img/NetAlertX_logo_b_w_info.png
Executable file
BIN
docs/img/NetAlertX_logo_b_w_info.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
BIN
docs/img/netalertx_docs.png
Executable file
BIN
docs/img/netalertx_docs.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
@@ -7,7 +7,7 @@ Welcome to the official NetAlertX documentation! NetAlertX is a powerful tool de
|
||||
NetAlertX provides contextual help within the application:
|
||||
|
||||
- **Hover over settings, fields, or labels** to see additional tooltips and guidance.
|
||||
- **Click the blue ❔ (question-mark) icons** next to various elements to view detailed information.
|
||||
- **Click ❔ (question-mark) icons** next to various elements to view detailed information.
|
||||
- Access the in-app **Help / FAQ** section for frequently asked questions and quick answers.
|
||||
|
||||
---
|
||||
|
||||
28
docs/overrides/main.html
Executable file
28
docs/overrides/main.html
Executable file
@@ -0,0 +1,28 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block analytics %}
|
||||
<!-- Google Tag Manager -->
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-KCRSGLP8J2"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-KCRSGLP8J2');
|
||||
</script>
|
||||
|
||||
<!-- End Google Tag Manager -->
|
||||
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KCRSGLP8J2"
|
||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
@@ -361,14 +361,14 @@
|
||||
// Update data to server using POST
|
||||
$.post('php/server/devices.php?action=setDeviceData', {
|
||||
mac: $('#NEWDEV_devMac').val(),
|
||||
name: encodeURIComponent($('#NEWDEV_devName').val().replace(/'/g, "")),
|
||||
owner: encodeURIComponent($('#NEWDEV_devOwner').val().replace(/'/g, "")),
|
||||
name: encodeURIComponent($('#NEWDEV_devName').val().replace(/'/g, "’")),
|
||||
owner: encodeURIComponent($('#NEWDEV_devOwner').val().replace(/'/g, "’")),
|
||||
type: $('#NEWDEV_devType').val().replace(/'/g, ""),
|
||||
vendor: encodeURIComponent($('#NEWDEV_devVendor').val().replace(/'/g, "")),
|
||||
vendor: encodeURIComponent($('#NEWDEV_devVendor').val().replace(/'/g, "’")),
|
||||
icon: encodeURIComponent($('#NEWDEV_devIcon').val()),
|
||||
favorite: ($('#NEWDEV_devFavorite')[0].checked * 1),
|
||||
group: encodeURIComponent($('#NEWDEV_devGroup').val().replace(/'/g, "")),
|
||||
location: encodeURIComponent($('#NEWDEV_devLocation').val().replace(/'/g, "")),
|
||||
group: encodeURIComponent($('#NEWDEV_devGroup').val().replace(/'/g, "’")),
|
||||
location: encodeURIComponent($('#NEWDEV_devLocation').val().replace(/'/g, "’")),
|
||||
comments: encodeURIComponent(encodeSpecialChars($('#NEWDEV_devComments').val())),
|
||||
networknode: $('#NEWDEV_devParentMAC').val(),
|
||||
networknodeport: $('#NEWDEV_devParentPort').val(),
|
||||
|
||||
BIN
front/img/NetAlertX_logo_b_w_info.png
Executable file
BIN
front/img/NetAlertX_logo_b_w_info.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
BIN
front/img/netalertx_docs.png
Executable file
BIN
front/img/netalertx_docs.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
377
front/img/svg/netalertx_docs.svg
Executable file
377
front/img/svg/netalertx_docs.svg
Executable file
@@ -0,0 +1,377 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="200"
|
||||
height="200"
|
||||
viewBox="0 0 52.916667 52.916668"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
sodipodi:docname="netalertx_docs.svg"
|
||||
inkscape:export-filename="C:\Users\jokob\netalertx_docs.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="72.124892"
|
||||
inkscape:cy="128.33988"
|
||||
inkscape:window-width="3378"
|
||||
inkscape:window-height="1417"
|
||||
inkscape:window-x="54"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer6"
|
||||
units="px"
|
||||
width="50px" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect51283"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect51283"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect51278"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect51278"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect51273"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect51273"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect48754"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect48754"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath48972">
|
||||
<path
|
||||
style="fill:#000000;stroke-width:0.280643"
|
||||
id="path48974"
|
||||
width="56.128242"
|
||||
height="56.128246"
|
||||
x="-18.924671"
|
||||
y="-56.198174"
|
||||
transform="rotate(45.438374)"
|
||||
mask="none"
|
||||
sodipodi:type="rect" />
|
||||
</clipPath>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask49405">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:60.8695px;line-height:1.25;font-family:Amiri;-inkscape-font-specification:Amiri;display:inline;stroke-width:1.52174"
|
||||
x="66.930733"
|
||||
y="78.642288"
|
||||
id="text49409"
|
||||
transform="scale(1.4861626,0.67287388)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan49407"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Tw Cen MT';-inkscape-font-specification:'Tw Cen MT';fill:#ffffff;stroke-width:1.52174"
|
||||
x="66.930733"
|
||||
y="78.642288">A</tspan></text>
|
||||
</mask>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath50306">
|
||||
<circle
|
||||
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||
id="circle50308"
|
||||
cy="26.458334"
|
||||
cx="26.458334"
|
||||
r="26.458334"
|
||||
clip-path="url(#clipPath48972)"
|
||||
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath48972-7">
|
||||
<path
|
||||
style="fill:#000000;stroke-width:0.280643"
|
||||
id="path48974-5"
|
||||
width="56.128242"
|
||||
height="56.128246"
|
||||
x="-18.924671"
|
||||
y="-56.198174"
|
||||
transform="rotate(45.438374)"
|
||||
mask="none"
|
||||
sodipodi:type="rect" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath50306-6">
|
||||
<circle
|
||||
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||
id="circle50308-5"
|
||||
cy="26.458334"
|
||||
cx="26.458334"
|
||||
r="26.458334"
|
||||
clip-path="url(#clipPath48972)"
|
||||
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||
</clipPath>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask-powermask-path-effect51273">
|
||||
<path
|
||||
id="mask-powermask-path-effect51273_box"
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="m 71.788348,33.677177 h 2.00083 v 2.173766 h -2.00083 z" />
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path51263"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="66.211845"
|
||||
sodipodi:cy="37.490814"
|
||||
sodipodi:rx="3.9464016"
|
||||
sodipodi:ry="1.4616301"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 70.158247,37.490814 a 3.9464016,1.4616301 0 0 1 -0.0019,0.04543" />
|
||||
</mask>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask-powermask-path-effect51278">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path51267"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="66.211845"
|
||||
sodipodi:cy="37.490814"
|
||||
sodipodi:rx="3.9464016"
|
||||
sodipodi:ry="1.4616301"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc" />
|
||||
</mask>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask-powermask-path-effect51283">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path51271"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="66.211845"
|
||||
sodipodi:cy="37.490814"
|
||||
sodipodi:rx="3.9464016"
|
||||
sodipodi:ry="1.4616301"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc" />
|
||||
</mask>
|
||||
<filter
|
||||
id="mask-powermask-path-effect51273_inverse"
|
||||
inkscape:label="filtermask-powermask-path-effect51273"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
height="100"
|
||||
width="100"
|
||||
x="-50"
|
||||
y="-50">
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect51273_primitive1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect51273_primitive2"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||
in="fbSourceGraphic" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Red 1"
|
||||
style="display:none">
|
||||
<circle
|
||||
style="fill:#ff2a2a;stroke-width:0.176318"
|
||||
id="path31-8"
|
||||
cy="26.458334"
|
||||
cx="26.458334"
|
||||
r="26.458334" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="Black"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline">
|
||||
<ellipse
|
||||
style="fill:#000000;stroke-width:0.176146"
|
||||
id="path31"
|
||||
cy="26.51001"
|
||||
cx="26.458334"
|
||||
rx="26.458334"
|
||||
ry="26.406658" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="A - Layer 2"
|
||||
style="display:none">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke-width:0.328992"
|
||||
id="rect48998"
|
||||
width="26.0966"
|
||||
height="6.0620313"
|
||||
x="13.255443"
|
||||
y="41.262722" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="g48055"
|
||||
inkscape:label="Red top"
|
||||
style="display:none;mix-blend-mode:normal">
|
||||
<circle
|
||||
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||
id="circle48752"
|
||||
cy="26.458334"
|
||||
cx="26.458334"
|
||||
r="26.458334"
|
||||
clip-path="url(#clipPath48972)"
|
||||
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||
<ellipse
|
||||
style="display:inline;mix-blend-mode:normal;fill:#000000;stroke-width:0.43638"
|
||||
id="path50080"
|
||||
clip-path="url(#clipPath50306)"
|
||||
ry="13.739323"
|
||||
rx="16.735666"
|
||||
cy="22.874514"
|
||||
cx="26.36149"
|
||||
transform="translate(0,0.09980904)" />
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path51325"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="16.772207"
|
||||
sodipodi:cy="26.090099"
|
||||
sodipodi:rx="4.1291056"
|
||||
sodipodi:ry="7.6004772"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:arc-type="slice"
|
||||
d="m 20.901313,26.090099 a 4.1291056,7.6004772 0 0 1 -0.002,0.236231 l -4.127111,-0.236231 z" />
|
||||
<path
|
||||
style="fill:#d40000"
|
||||
id="path51717"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="26.441042"
|
||||
sodipodi:cy="-26.531424"
|
||||
sodipodi:rx="10.418671"
|
||||
sodipodi:ry="9.5820541"
|
||||
sodipodi:start="0.82219863"
|
||||
sodipodi:end="2.3054129"
|
||||
sodipodi:arc-type="slice"
|
||||
d="m 33.532115,-19.511189 a 10.418671,9.5820541 0 0 1 -14.074736,0.09049 l 6.983663,-7.110726 z"
|
||||
transform="matrix(1,0,0.0048047,-0.99998846,0,0)" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.276214"
|
||||
d="M 145.28835,50.354872 C 127.01317,34.62734 98.057144,30.012421 73.710372,38.947003 c -6.518003,2.391924 -14.288822,6.834002 -19.265958,11.01311 -1.198654,1.006465 -2.270358,1.829935 -2.381565,1.829935 -0.111206,0 -5.210052,-5.102002 -11.33077,-11.337781 L 29.603503,29.114489 30.822139,27.851613 c 0.670251,-0.69458 2.51592,-2.384634 4.101489,-3.755674 C 50.725112,10.43241 69.462577,2.3767456 90.736164,0.10085492 95.380582,-0.39601422 106.33043,-0.31105699 111.03786,0.25837091 133.04363,2.9202648 151.46536,11.26468 167.83762,25.986722 l 3.30701,2.97369 -2.29392,2.320103 c -1.26165,1.276057 -6.58213,6.517685 -11.82329,11.648065 l -9.52936,9.327957 z"
|
||||
id="path52311"
|
||||
transform="scale(0.26458333)" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.276214"
|
||||
d="M 86.538548,86.634546 74.145111,73.25799 74.899337,72.758689 c 4.93766,-3.268754 10.138703,-6.508578 16.602198,-7.437693 5.484021,-0.788317 12.228205,-0.984814 16.377135,-0.09119 6.77689,1.459652 11.87156,4.340971 17.02452,7.792011 l 0.97468,0.652765 -1.37124,1.269268 c -0.86863,0.804036 -6.82647,6.676301 -13.34742,13.259175 L 99.423152,99.796276 Z"
|
||||
id="path52350"
|
||||
transform="scale(0.26458333)"
|
||||
inkscape:export-filename="C:\Users\jokob\path52350.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
sodipodi:nodetypes="ccsssscsscc" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer6"
|
||||
inkscape:label="Circle"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path50026"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="71.071762"
|
||||
sodipodi:cy="34.677177"
|
||||
sodipodi:rx="1.7174155"
|
||||
sodipodi:ry="5.5907354"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
mask="url(#mask-powermask-path-effect51273)"
|
||||
d="m 72.789178,34.677177 a 1.7174155,5.5907354 0 0 1 -8.3e-4,0.173766"
|
||||
inkscape:path-effect="#path-effect51273" />
|
||||
<path
|
||||
style="fill:#d40000;stroke-width:0.276214"
|
||||
d="M 86.416478,86.793237 C 73.427951,73.815968 73.387119,73.801376 73.387119,73.801376 c 3.874197,-3.341721 11.025508,-6.981646 17.312424,-8.529335 2.339787,-0.576001 4.881362,-1.25628 8.810591,-1.259564 4.438736,-0.0037 8.292516,0.857843 13.253396,2.535104 4.59135,1.552325 7.8315,3.224336 11.49958,5.934101 l 1.61476,1.192897 -2.31005,2.336325 c -1.27053,1.284978 -7.22284,7.16236 -13.22736,13.060849 L 99.423152,99.796276 C 95.128284,95.409033 87.282899,87.658907 86.416478,86.793237 Z"
|
||||
id="path52465"
|
||||
transform="scale(0.26458333)"
|
||||
sodipodi:nodetypes="sssssscsscs" />
|
||||
<path
|
||||
style="fill:#d40000;stroke-width:0.074168"
|
||||
d="M 38.412677,13.39572 C 34.322163,9.945267 28.437517,8.4874766 22.684204,9.4993379 19.419721,10.073478 16.752307,11.410793 13.835187,13.872492 l -0.14691,0.126732 -0.587936,-0.661605 c -0.268568,-0.30222 -1.619514,-1.65761 -2.963235,-3.048642 L 7.7265561,7.8632145 7.9975963,7.5868118 C 9.8344314,5.713635 13.005888,3.476019 15.380049,2.3878744 20.659765,-0.03196726 26.24205,-0.73479764 31.856076,0.42838695 36.599757,1.4112419 40.746004,3.5106537 44.46876,7.1557672 l 0.709881,0.6950753 -0.663694,0.69037 C 44.080041,8.9935983 42.672626,10.391271 41.3963,11.655819 L 39.075708,13.955 Z"
|
||||
id="path52504"
|
||||
inkscape:export-filename="C:\Users\jokob\path52504.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
sodipodi:nodetypes="ssscsccsssscsscs" />
|
||||
<rect
|
||||
style="fill:#ffffff;stroke-width:0.270734"
|
||||
id="rect9599"
|
||||
width="8.0679188"
|
||||
height="21.176973"
|
||||
x="22.265251"
|
||||
y="30.578777" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.353553"
|
||||
d="m 86.613132,86.61313 -12.851203,-12.854821 0.86086,-0.706098 c 1.692083,-1.387887 6.387757,-3.998693 9.623614,-5.350752 7.33291,-3.063958 14.480764,-4.12547 20.582177,-3.056613 3.69356,0.647044 9.99695,2.663626 13.06868,4.180934 2.21267,1.092967 7.61419,4.559526 7.61419,4.886591 0,0.102465 -5.8606,5.939388 -13.02356,12.97094 L 99.464335,99.467952 Z"
|
||||
id="path13796"
|
||||
transform="scale(0.26458333)" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.353553"
|
||||
d="M 40.562149,41.010783 29.328726,29.577286 33.137528,26.202177 C 50.057066,11.209199 68.487351,2.8161465 89.979339,0.31672328 96.591211,-0.45220831 108.48969,-0.19409453 115.05495,0.86068879 135.48174,4.1424805 152.54396,12.522653 167.06663,26.406419 l 3.39168,3.242463 -11.39113,11.395174 -11.39113,11.395178 -2.86219,-2.330889 C 131.23238,39.047901 112.18782,33.324108 93.81593,34.781043 78.86759,35.966481 67.456828,40.362971 55.747418,49.448575 54.209095,50.642196 52.690616,51.804531 52.37302,52.031537 51.87959,52.384228 50.161133,50.780729 40.562149,41.010783 Z"
|
||||
id="path13835"
|
||||
transform="scale(0.26458333)"
|
||||
inkscape:export-filename="C:\Users\jokob\docs.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -4,262 +4,262 @@
|
||||
var modalCallbackFunction = "";
|
||||
|
||||
function showModalOK(title, message, callbackFunction) {
|
||||
showModalOk(title, message, callbackFunction);
|
||||
showModalOk(title, message, callbackFunction);
|
||||
}
|
||||
function showModalOk(title, message, callbackFunction) {
|
||||
// set captions
|
||||
$("#modal-ok-title").html(title);
|
||||
$("#modal-ok-message").html(message);
|
||||
// set captions
|
||||
$("#modal-ok-title").html(title);
|
||||
$("#modal-ok-message").html(message);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
$("#modal-ok-OK").click(function () {
|
||||
callbackFunction();
|
||||
});
|
||||
}
|
||||
if (callbackFunction != null) {
|
||||
$("#modal-ok-OK").click(function () {
|
||||
callbackFunction();
|
||||
});
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$("#modal-ok").modal("show");
|
||||
// Show modal
|
||||
$("#modal-ok").modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalDefault(title, message, btnCancel, btnOK, callbackFunction) {
|
||||
// set captions
|
||||
$("#modal-default-title").html(title);
|
||||
$("#modal-default-message").html(message);
|
||||
$("#modal-default-cancel").html(btnCancel);
|
||||
$("#modal-default-OK").html(btnOK);
|
||||
modalCallbackFunction = callbackFunction;
|
||||
// set captions
|
||||
$("#modal-default-title").html(title);
|
||||
$("#modal-default-message").html(message);
|
||||
$("#modal-default-cancel").html(btnCancel);
|
||||
$("#modal-default-OK").html(btnOK);
|
||||
modalCallbackFunction = callbackFunction;
|
||||
|
||||
// Show modal
|
||||
$("#modal-default").modal("show");
|
||||
// Show modal
|
||||
$("#modal-default").modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function showModalDefaultStrParam(
|
||||
title,
|
||||
message,
|
||||
btnCancel,
|
||||
btnOK,
|
||||
callbackFunction,
|
||||
param = ""
|
||||
title,
|
||||
message,
|
||||
btnCancel,
|
||||
btnOK,
|
||||
callbackFunction,
|
||||
param = ""
|
||||
) {
|
||||
// set captions
|
||||
$("#modal-str-title").html(title);
|
||||
$("#modal-str-message").html(message);
|
||||
$("#modal-str-cancel").html(btnCancel);
|
||||
$("#modal-str-OK").html(btnOK);
|
||||
$("#modal-str-OK").off("click"); //remove existing handlers
|
||||
$("#modal-str-OK").on("click", function () {
|
||||
$("#modal-str").modal("hide");
|
||||
callbackFunction(param);
|
||||
});
|
||||
// set captions
|
||||
$("#modal-str-title").html(title);
|
||||
$("#modal-str-message").html(message);
|
||||
$("#modal-str-cancel").html(btnCancel);
|
||||
$("#modal-str-OK").html(btnOK);
|
||||
$("#modal-str-OK").off("click"); //remove existing handlers
|
||||
$("#modal-str-OK").on("click", function () {
|
||||
$("#modal-str").modal("hide");
|
||||
callbackFunction(param);
|
||||
});
|
||||
|
||||
// Show modal
|
||||
$("#modal-str").modal("show");
|
||||
// Show modal
|
||||
$("#modal-str").modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalWarning(
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
callbackFunction = null,
|
||||
triggeredBy = null
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
callbackFunction = null,
|
||||
triggeredBy = null
|
||||
) {
|
||||
// set captions
|
||||
$("#modal-warning-title").html(title);
|
||||
$("#modal-warning-message").html(message);
|
||||
$("#modal-warning-cancel").html(btnCancel);
|
||||
$("#modal-warning-OK").html(btnOK);
|
||||
// set captions
|
||||
$("#modal-warning-title").html(title);
|
||||
$("#modal-warning-message").html(message);
|
||||
$("#modal-warning-cancel").html(btnCancel);
|
||||
$("#modal-warning-OK").html(btnOK);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
|
||||
if (triggeredBy != null) {
|
||||
$('#'+prefix).attr("data-myparam-triggered-by", triggeredBy)
|
||||
}
|
||||
if (triggeredBy != null) {
|
||||
$('#'+prefix).attr("data-myparam-triggered-by", triggeredBy)
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$("#modal-warning").modal("show");
|
||||
// Show modal
|
||||
$("#modal-warning").modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalInput(
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
callbackFunction = null,
|
||||
triggeredBy = null
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
callbackFunction = null,
|
||||
triggeredBy = null
|
||||
) {
|
||||
prefix = "modal-input";
|
||||
prefix = "modal-input";
|
||||
|
||||
// set captions
|
||||
$(`#${prefix}-title`).html(title);
|
||||
$(`#${prefix}-message`).html(message);
|
||||
$(`#${prefix}-cancel`).html(btnCancel);
|
||||
$(`#${prefix}-OK`).html(btnOK);
|
||||
// set captions
|
||||
$(`#${prefix}-title`).html(title);
|
||||
$(`#${prefix}-message`).html(message);
|
||||
$(`#${prefix}-cancel`).html(btnCancel);
|
||||
$(`#${prefix}-OK`).html(btnOK);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
|
||||
if (triggeredBy != null) {
|
||||
$('#'+prefix).attr("data-myparam-triggered-by", triggeredBy)
|
||||
}
|
||||
if (triggeredBy != null) {
|
||||
$('#'+prefix).attr("data-myparam-triggered-by", triggeredBy)
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$(`#${prefix}`).modal("show");
|
||||
// Show modal
|
||||
$(`#${prefix}`).modal("show");
|
||||
|
||||
setTimeout(function () {
|
||||
$(`#${prefix}-textarea`).focus();
|
||||
}, 500);
|
||||
|
||||
setTimeout(function () {
|
||||
$(`#${prefix}-textarea`).focus();
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalFieldInput(
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
curValue = "",
|
||||
callbackFunction = null,
|
||||
triggeredBy = null
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
curValue = "",
|
||||
callbackFunction = null,
|
||||
triggeredBy = null
|
||||
) {
|
||||
// set captions
|
||||
prefix = "modal-field-input";
|
||||
// set captions
|
||||
prefix = "modal-field-input";
|
||||
|
||||
$(`#${prefix}-title`).html(title);
|
||||
$(`#${prefix}-message`).html(message);
|
||||
$(`#${prefix}-cancel`).html(btnCancel);
|
||||
$(`#${prefix}-OK`).html(btnOK);
|
||||
$(`#${prefix}-title`).html(title);
|
||||
$(`#${prefix}-message`).html(message);
|
||||
$(`#${prefix}-cancel`).html(btnCancel);
|
||||
$(`#${prefix}-OK`).html(btnOK);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
if (callbackFunction != null) {
|
||||
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
|
||||
if (triggeredBy != null) {
|
||||
$('#'+prefix).attr("data-myparam-triggered-by", triggeredBy)
|
||||
}
|
||||
if (triggeredBy != null) {
|
||||
$('#'+prefix).attr("data-myparam-triggered-by", triggeredBy)
|
||||
}
|
||||
|
||||
$(`#${prefix}-field`).val(curValue);
|
||||
$(`#${prefix}-field`).val(curValue);
|
||||
|
||||
setTimeout(function () {
|
||||
$(`#${prefix}-field`).focus();
|
||||
}, 500);
|
||||
setTimeout(function () {
|
||||
$(`#${prefix}-field`).focus();
|
||||
}, 500);
|
||||
|
||||
// Show modal
|
||||
$(`#${prefix}`).modal("show");
|
||||
// Show modal
|
||||
$(`#${prefix}`).modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalDefaultOK() {
|
||||
// Hide modal
|
||||
$("#modal-default").modal("hide");
|
||||
// Hide modal
|
||||
$("#modal-default").modal("hide");
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
if (typeof modalCallbackFunction === "function") {
|
||||
modalCallbackFunction(); // Direct call
|
||||
} else if (typeof modalCallbackFunction === "string" && typeof window[modalCallbackFunction] === "function") {
|
||||
window[modalCallbackFunction](); // Call via window
|
||||
} else {
|
||||
console.error("Invalid callback function");
|
||||
}
|
||||
}, 100);
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
if (typeof modalCallbackFunction === "function") {
|
||||
modalCallbackFunction(); // Direct call
|
||||
} else if (typeof modalCallbackFunction === "string" && typeof window[modalCallbackFunction] === "function") {
|
||||
window[modalCallbackFunction](); // Call via window
|
||||
} else {
|
||||
console.error("Invalid callback function");
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalDefaultInput() {
|
||||
// Hide modal
|
||||
$("#modal-input").modal("hide");
|
||||
// Hide modal
|
||||
$("#modal-input").modal("hide");
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
if (typeof modalCallbackFunction === "function") {
|
||||
modalCallbackFunction(); // Direct call
|
||||
} else if (typeof modalCallbackFunction === "string" && typeof window[modalCallbackFunction] === "function") {
|
||||
window[modalCallbackFunction](); // Call via window
|
||||
} else {
|
||||
console.error("Invalid callback function");
|
||||
}
|
||||
}, 100);
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
if (typeof modalCallbackFunction === "function") {
|
||||
modalCallbackFunction(); // Direct call
|
||||
} else if (typeof modalCallbackFunction === "string" && typeof window[modalCallbackFunction] === "function") {
|
||||
window[modalCallbackFunction](); // Call via window
|
||||
} else {
|
||||
console.error("Invalid callback function");
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalDefaultFieldInput() {
|
||||
// Hide modal
|
||||
$("#modal-field-input").modal("hide");
|
||||
// Hide modal
|
||||
$("#modal-field-input").modal("hide");
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
if (typeof modalCallbackFunction === "function") {
|
||||
modalCallbackFunction(); // Direct call
|
||||
} else if (typeof modalCallbackFunction === "string" && typeof window[modalCallbackFunction] === "function") {
|
||||
window[modalCallbackFunction](); // Call via window
|
||||
} else {
|
||||
console.error("Invalid callback function");
|
||||
}
|
||||
}, 100);
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
if (typeof modalCallbackFunction === "function") {
|
||||
modalCallbackFunction(); // Direct call
|
||||
} else if (typeof modalCallbackFunction === "string" && typeof window[modalCallbackFunction] === "function") {
|
||||
window[modalCallbackFunction](); // Call via window
|
||||
} else {
|
||||
console.error("Invalid callback function");
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalWarningOK() {
|
||||
// Hide modal
|
||||
$("#modal-warning").modal("hide");
|
||||
// Hide modal
|
||||
$("#modal-warning").modal("hide");
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
if (typeof modalCallbackFunction === "function") {
|
||||
modalCallbackFunction(); // Direct call
|
||||
} else if (typeof modalCallbackFunction === "string" && typeof window[modalCallbackFunction] === "function") {
|
||||
window[modalCallbackFunction](); // Call via window
|
||||
} else {
|
||||
console.error("Invalid callback function");
|
||||
}
|
||||
}, 100);
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
if (typeof modalCallbackFunction === "function") {
|
||||
modalCallbackFunction(); // Direct call
|
||||
} else if (typeof modalCallbackFunction === "string" && typeof window[modalCallbackFunction] === "function") {
|
||||
window[modalCallbackFunction](); // Call via window
|
||||
} else {
|
||||
console.error("Invalid callback function");
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showMessage(textMessage = "", timeout = 3000, colorClass = "modal_green") {
|
||||
if (textMessage.toLowerCase().includes("error")) {
|
||||
// show error
|
||||
alert(textMessage);
|
||||
} else {
|
||||
// show temporary notification
|
||||
$("#notification_modal").removeClass(); // remove all classes
|
||||
$("#notification_modal").addClass("alert alert-dimissible notification_modal"); // add default ones
|
||||
$("#notification_modal").addClass(colorClass); // add color modifiers
|
||||
if (textMessage.toLowerCase().includes("error")) {
|
||||
// show error
|
||||
alert(textMessage);
|
||||
} else {
|
||||
// show temporary notification
|
||||
$("#notification_modal").removeClass(); // remove all classes
|
||||
$("#notification_modal").addClass("alert alert-dimissible notification_modal"); // add default ones
|
||||
$("#notification_modal").addClass(colorClass); // add color modifiers
|
||||
|
||||
// message
|
||||
$("#alert-message").html(textMessage);
|
||||
// message
|
||||
$("#alert-message").html(textMessage);
|
||||
|
||||
// timeout
|
||||
$("#notification_modal").fadeIn(1, function () {
|
||||
window.setTimeout(function () {
|
||||
$("#notification_modal").fadeOut(500);
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
// timeout
|
||||
$("#notification_modal").fadeIn(1, function () {
|
||||
window.setTimeout(function () {
|
||||
$("#notification_modal").fadeOut(500);
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showTickerAnnouncement(textMessage = "") {
|
||||
if (textMessage.toLowerCase().includes("error")) {
|
||||
// show error
|
||||
alert(textMessage);
|
||||
} else {
|
||||
// show permanent notification
|
||||
$("#ticker-message").html(textMessage);
|
||||
$("#tickerAnnouncement").removeClass("myhidden");
|
||||
// Move the tickerAnnouncement element to ticker_announcement_plc
|
||||
$("#tickerAnnouncement").appendTo("#ticker_announcement_plc");
|
||||
}
|
||||
if (textMessage.toLowerCase().includes("error")) {
|
||||
// show error
|
||||
alert(textMessage);
|
||||
} else {
|
||||
// show permanent notification
|
||||
$("#ticker-message").html(textMessage);
|
||||
$("#tickerAnnouncement").removeClass("myhidden");
|
||||
// Move the tickerAnnouncement element to ticker_announcement_plc
|
||||
$("#tickerAnnouncement").appendTo("#ticker_announcement_plc");
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -267,30 +267,30 @@ function showTickerAnnouncement(textMessage = "") {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
$(document).ready(function () {
|
||||
$(document).on("keydown", function (event) {
|
||||
// ESC key is pressed
|
||||
if (event.keyCode === 27) {
|
||||
// Trigger modal dismissal
|
||||
$(".modal").modal("hide");
|
||||
}
|
||||
$(document).on("keydown", function (event) {
|
||||
// ESC key is pressed
|
||||
if (event.keyCode === 27) {
|
||||
// Trigger modal dismissal
|
||||
$(".modal").modal("hide");
|
||||
}
|
||||
|
||||
// Enter key is pressed
|
||||
if (event.keyCode === 13) {
|
||||
$(".modal:visible").find(".btn-modal-submit").click(); // Trigger the click event of the OK button in visible modals
|
||||
}
|
||||
});
|
||||
// Enter key is pressed
|
||||
if (event.keyCode === 13) {
|
||||
$(".modal:visible").find(".btn-modal-submit").click(); // Trigger the click event of the OK button in visible modals
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Escape text
|
||||
function safeDecodeURIComponent(content) {
|
||||
try {
|
||||
return decodeURIComponent(content);
|
||||
} catch (error) {
|
||||
console.warn('Failed to decode URI component:', error);
|
||||
return content; // Return the original content if decoding fails
|
||||
}
|
||||
try {
|
||||
return decodeURIComponent(content);
|
||||
} catch (error) {
|
||||
console.warn('Failed to decode URI component:', error);
|
||||
return content; // Return the original content if decoding fails
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -299,82 +299,83 @@ function safeDecodeURIComponent(content) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to check for notifications
|
||||
function checkNotification() {
|
||||
const notificationEndpoint = 'php/server/utilNotification.php?action=get_unread_notifications';
|
||||
const phpEndpoint = 'php/server/utilNotification.php';
|
||||
const notificationEndpoint = 'php/server/utilNotification.php?action=get_unread_notifications';
|
||||
const phpEndpoint = 'php/server/utilNotification.php';
|
||||
|
||||
$.ajax({
|
||||
url: notificationEndpoint,
|
||||
type: 'GET',
|
||||
success: function(response) {
|
||||
// console.log(response);
|
||||
$.ajax({
|
||||
url: notificationEndpoint,
|
||||
type: 'GET',
|
||||
success: function(response) {
|
||||
// console.log(response);
|
||||
|
||||
if(response != "[]")
|
||||
{
|
||||
if(response != "[]")
|
||||
{
|
||||
// Find the oldest unread notification with level "interrupt"
|
||||
const oldestInterruptNotification = response.find(notification => notification.read === 0 && notification.level === "interrupt");
|
||||
const allUnreadNotification = response.filter(notification => notification.read === 0 && notification.level === "alert");
|
||||
|
||||
// Find the oldest unread notification with level "interrupt"
|
||||
const oldestInterruptNotification = response.find(notification => notification.read === 0 && notification.level === "interrupt");
|
||||
const allUnreadNotification = response.filter(notification => notification.read === 0 && notification.level === "alert");
|
||||
if (oldestInterruptNotification) {
|
||||
// Show modal dialog with the oldest unread notification
|
||||
|
||||
if (oldestInterruptNotification) {
|
||||
// Show modal dialog with the oldest unread notification
|
||||
console.log(oldestInterruptNotification.content);
|
||||
|
||||
console.log(oldestInterruptNotification.content);
|
||||
const decodedContent = safeDecodeURIComponent(oldestInterruptNotification.content);
|
||||
|
||||
const decodedContent = safeDecodeURIComponent(oldestInterruptNotification.content);
|
||||
|
||||
showModalOK("Notification", decodedContent, function() {
|
||||
// Mark the notification as read
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'mark_notification_as_read',
|
||||
guid: oldestInterruptNotification.guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// After marking the notification as read, check for the next one
|
||||
checkNotification();
|
||||
hideSpinner();
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error marking notification as read:", status, error);
|
||||
},
|
||||
complete:function() {
|
||||
hideSpinner();
|
||||
}
|
||||
});
|
||||
});
|
||||
// only check and display modal if no modal currently displayed to prevent looping
|
||||
if($("#modal-ok").is(":visible") == false)
|
||||
{
|
||||
showModalOK("Notification", decodedContent, function() {
|
||||
// Mark the notification as read
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'mark_notification_as_read',
|
||||
guid: oldestInterruptNotification.guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// After marking the notification as read, check for the next one
|
||||
checkNotification();
|
||||
hideSpinner();
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error marking notification as read:", status, error);
|
||||
},
|
||||
complete:function() {
|
||||
hideSpinner();
|
||||
}
|
||||
|
||||
handleUnreadNotifications(allUnreadNotification.length)
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
console.warn(`🟥 Error checking ${notificationEndpoint}`)
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
handleUnreadNotifications(allUnreadNotification.length)
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
console.warn(`🟥 Error checking ${notificationEndpoint}`)
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handling unread notifications favicon + bell floating number bublbe
|
||||
function handleUnreadNotifications(count) {
|
||||
$('#unread-notifications-bell-count').html(count);
|
||||
if (count > 0) {
|
||||
$('#unread-notifications-bell-count').show();
|
||||
// Change the favicon to show there are notifications
|
||||
$('#favicon').attr('href', 'img/NetAlertX_logo_notification.png');
|
||||
// Update the title to include the count
|
||||
document.title = `(${count}) ` + originalTitle;
|
||||
} else {
|
||||
$('#unread-notifications-bell-count').hide();
|
||||
// Change the favicon back to the original
|
||||
$('#favicon').attr('href', 'img/NetAlertX_logo.png');
|
||||
// Revert the title to the original title
|
||||
document.title = originalTitle;
|
||||
}
|
||||
$('#unread-notifications-bell-count').html(count);
|
||||
if (count > 0) {
|
||||
$('#unread-notifications-bell-count').show();
|
||||
// Change the favicon to show there are notifications
|
||||
$('#favicon').attr('href', 'img/NetAlertX_logo_notification.png');
|
||||
// Update the title to include the count
|
||||
document.title = `(${count}) ` + originalTitle;
|
||||
} else {
|
||||
$('#unread-notifications-bell-count').hide();
|
||||
// Change the favicon back to the original
|
||||
$('#favicon').attr('href', 'img/NetAlertX_logo.png');
|
||||
// Revert the title to the original title
|
||||
document.title = originalTitle;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the original title of the document
|
||||
@@ -392,73 +393,73 @@ const phpEndpoint = 'php/server/utilNotification.php';
|
||||
|
||||
// --------------------------------------------------
|
||||
// Write a notification
|
||||
function write_notification(content, level) {
|
||||
function write_notification(content, level) {
|
||||
|
||||
$.ajax({
|
||||
url: phpEndpoint, // Change this to the path of your PHP script
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'write_notification',
|
||||
content: content,
|
||||
level: level
|
||||
},
|
||||
success: function(response) {
|
||||
console.log('Notification written successfully.');
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Error writing notification:', error);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: phpEndpoint, // Change this to the path of your PHP script
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'write_notification',
|
||||
content: content,
|
||||
level: level
|
||||
},
|
||||
success: function(response) {
|
||||
console.log('Notification written successfully.');
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Error writing notification:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Write a notification
|
||||
function markNotificationAsRead(guid) {
|
||||
function markNotificationAsRead(guid) {
|
||||
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'mark_notification_as_read',
|
||||
guid: guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// Perform any further actions after marking the notification as read here
|
||||
showMessage(getString("Gen_Okay"))
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error marking notification as read:", status, error);
|
||||
},
|
||||
complete: function() {
|
||||
// Perform any cleanup tasks here
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'mark_notification_as_read',
|
||||
guid: guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// Perform any further actions after marking the notification as read here
|
||||
showMessage(getString("Gen_Okay"))
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error marking notification as read:", status, error);
|
||||
},
|
||||
complete: function() {
|
||||
// Perform any cleanup tasks here
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Remove a notification
|
||||
function removeNotification(guid) {
|
||||
function removeNotification(guid) {
|
||||
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'remove_notification',
|
||||
guid: guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// Perform any further actions after marking the notification as read here
|
||||
showMessage(getString("Gen_Okay"))
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error removing notification:", status, error);
|
||||
},
|
||||
complete: function() {
|
||||
// Perform any cleanup tasks here
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'remove_notification',
|
||||
guid: guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// Perform any further actions after marking the notification as read here
|
||||
showMessage(getString("Gen_Okay"))
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error removing notification:", status, error);
|
||||
},
|
||||
complete: function() {
|
||||
// Perform any cleanup tasks here
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Pots especificar una consulta SQL personalitzada que generarà un fitxer JSON i el mostrarà mitjançant <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> file endpoint</a>.",
|
||||
"API_CUSTOM_SQL_description": "Pots especificar una consulta SQL personalitzada que generarà un fitxer JSON i el mostrarà mitjançant <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> file endpoint</a>.",
|
||||
"API_CUSTOM_SQL_name": "Punt final personalitzat",
|
||||
"API_TOKEN_description": "Token API per assegurar les comunicacions, pots generar-ne un o introduir un valor clau. S'enviarà a la capçalera de la petició <code>SYNC</code> plugin, servidor GraphQL i altres endpoints API. Pots fer servir els endpoints API per crear integracions personalitzades tal com es descriu a <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md\" target=\"_blank\">la documentació API</a>.",
|
||||
"API_TOKEN_name": "Token API",
|
||||
@@ -48,7 +48,7 @@
|
||||
"BackDevices_DBTools_ImportCSVError": "El fitxer CSV no s'ha pogut importar. Comprovi que el format és correcte.",
|
||||
"BackDevices_DBTools_ImportCSVMissing": "No es pot trobar el fitxer CSV a la ubicació <b>/config/devices.csv.</b>",
|
||||
"BackDevices_DBTools_Purge": "Les còpies de seguretat més antigues s'han esborrat",
|
||||
"BackDevices_DBTools_UpdDev": "Dispositiu actualitzat correctament",
|
||||
"BackDevices_DBTools_UpdDev": "Dispositiu actualitzat amb èxit. La llista de dispositius principals pot necessitar un temps per tornar a carregar si una exploració està en curs.",
|
||||
"BackDevices_DBTools_UpdDevError": "Error actualitzant el dispositiu",
|
||||
"BackDevices_DBTools_Upgrade": "Base de dades actualitzada correctament",
|
||||
"BackDevices_DBTools_UpgradeError": "Actualització de la base de dades fallida",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Plataformes patrocinadores",
|
||||
"Donations_Text": "Hola 👋! </br> Gràcies per fer clic en aquest element de menú 😅 </br> </br> Estic intentant recollir algunes donacions per fer un millor programari. També, m'ajudaria per cremar-me, i així recolzar aquesta aplicació més temps. Qualsevol petit (recurrent o no) patrocini em farà posar més esforç a aquesta aplicació. </br> M'agradaria escurçar la meva setmana de feina i en el temps restant enfocar-me en el NetAlertX. Així rebries més funcionalitat, una aplicació més neta i menys bugs. </br> </br> Gràcies per llegir-ho - Agraeixo qualsevol suport ❤🙏 </br> </br> TL;DR: Pel teu suport reps: </br> </br> <ul><li>Actualitzacions regulars per seguir les vostres dades i mantenir la família segura 🔄</li><li>Menys bugs 🐛🔫</li><li>Millor i més funcionalitat➕</li><li>Que no m'arribi el \"burn out\" 🔥🤯</li><li>Menys actualitzacions d'emergència 💨</li><li>Millors documentacions📚</li><li>Suport més ràpid i millor amb les incidències 🆘</li></ul> </br> 📧Correu electrònic <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> si vols contactar o si hauria d'afegir altres programes de patrocini. </br>",
|
||||
"Donations_Title": "Donacions",
|
||||
"ENABLE_PLUGINS_description": "Habilita la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">connectors</a> funcionalitat. Carregar els connectors requereix més recursos de maquinari així podries voler desactivar-los en un sistema de baixos recursos.",
|
||||
"ENABLE_PLUGINS_description": "Habilita la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">connectors</a> funcionalitat. Carregar els connectors requereix més recursos de maquinari així podries voler desactivar-los en un sistema de baixos recursos.",
|
||||
"ENABLE_PLUGINS_name": "Activa els connectors(Plugins)",
|
||||
"ENCRYPTION_KEY_description": "Clau de xifrat de dades.",
|
||||
"ENCRYPTION_KEY_name": "Clau d'encriptació",
|
||||
@@ -328,11 +328,11 @@
|
||||
"Gen_Upd_Fail": "Actualització fallida",
|
||||
"Gen_Update": "Actualitza",
|
||||
"Gen_Update_Value": "Actualitzar Valor",
|
||||
"Gen_ValidIcon": "",
|
||||
"Gen_ValidIcon": "<i class=\"fa-solid fa-chevron-right \"></i>",
|
||||
"Gen_Warning": "Advertència",
|
||||
"Gen_Work_In_Progress": "Work in progress, un bon moment per retroalimentació a https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"Gen_create_new_device": "Nou dispositiu",
|
||||
"Gen_create_new_device_info": "Els dispositius són típicament descobert utilitzant <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugins</a>. Tanmateix, en certs casos, pots necessitar afegir dispositius a mà. Per explorar els temes concrets comproveu la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">documentació de Xarxes Remotes</a>.",
|
||||
"Gen_create_new_device_info": "Els dispositius són típicament descobert utilitzant <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins</a>. Tanmateix, en certs casos, pots necessitar afegir dispositius a mà. Per explorar els temes concrets comproveu la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">documentació de Xarxes Remotes</a>.",
|
||||
"General_display_name": "General",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Això és un paràmetre de manteniment <b>ELIMINANT dispositius</b>. Si s'activa (<code>0</code> està desactivat), els dispositius marcats com <b>Dispositiu Nou</b> seran eliminats si el temps de <b>Primera Sessió</b> es més vell que les hores especificades en aquest paràmetre. Faci servir aquest paràmetre si vol auto-eliminar <b>Nous Dispositius</b> després de <code>X</code> hores.",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "Un dispositiu és mostrat tan present tot i que és Fora de línia \"\".",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Si això passa, tens la possibilitat d'esborrar els esdeveniments del dispositiu en qüestió (veure detalls). Una altra possibilitat seria canviar el dispositiu i esperar fins a NetAlert X reconeix el dispositiu com a \"en línia\" amb la següent exploració i, a continuació, simplement torneu a desactivar el dispositiu. NetAlert X ha de tenir en compte l'estat del dispositiu a la base de dades amb la següent exploració.",
|
||||
"HelpFAQ_Title": "Ajuda / FAQ",
|
||||
"LOADED_PLUGINS_description": "Quins Plugins carregar. Afegir plugins podria alentir l'aplicació. Llegir més sobre quins connectors necessiten estar habilitats, els tipus, o les opcions d'escaneig dins del <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme\">documents de connectors</a>. Els connectors descarregats perdran els vostres paràmetres. Només <code>desactivats</code> es poden eliminar els connectors.",
|
||||
"LOADED_PLUGINS_description": "Quins Plugins carregar. Afegir plugins podria alentir l'aplicació. Llegir més sobre quins connectors necessiten estar habilitats, els tipus, o les opcions d'escaneig dins del <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">documents de connectors</a>. Els connectors descarregats perdran els vostres paràmetres. Només <code>desactivats</code> es poden eliminar els connectors.",
|
||||
"LOADED_PLUGINS_name": "Connectors carregats",
|
||||
"LOG_LEVEL_description": "Aquest paràmetre permetrà un registre més detallat. Útil per a la depuració d'esdeveniments d'escriptura a la base de dades.",
|
||||
"LOG_LEVEL_name": "Imprimeix el registre addicional",
|
||||
@@ -753,4 +753,4 @@
|
||||
"settings_update_item_warning": "Actualitza el valor sota. Sigues curós de seguir el format anterior. <b>No hi ha validació.</b>",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Deseu els canvis primer abans de comprovar la configuració."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Benutzerdefinierte SQL-Abfrage, welche eine JSON-Datei generiert und diese mit dem <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\">Dateiendpunkt <code>table_custom_endpoint.json</code></a> zur Verfügung stellt.",
|
||||
"API_CUSTOM_SQL_description": "Benutzerdefinierte SQL-Abfrage, welche eine JSON-Datei generiert und diese mit dem <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\">Dateiendpunkt <code>table_custom_endpoint.json</code></a> zur Verfügung stellt.",
|
||||
"API_CUSTOM_SQL_name": "Benutzerdefinierte SQL-Abfrage",
|
||||
"API_TOKEN_description": "API-Token zur Absicherung der Kommunikation – Sie können einen generieren oder einen beliebigen Wert eingeben. Er wird im Anfrage-Header übermittelt. Wird im <code>SYNC</code>-Plugin und GraphQL-Server verwendet.",
|
||||
"API_TOKEN_name": "API-Schlüssel",
|
||||
@@ -75,8 +75,8 @@
|
||||
"CustProps_cant_remove": "Kann nicht entfernt werden, es wird mindestens eine Eigenschaft benötigt.",
|
||||
"DAYS_TO_KEEP_EVENTS_description": "Dies ist eine Wartungseinstellung. Spezifiziert wie viele Tage Events gespeichert bleiben. Alle älteren Events werden periodisch gelöscht. Wird auch auf die Plugins History angewendet.",
|
||||
"DAYS_TO_KEEP_EVENTS_name": "Ereignisse löschen, die älter sind als",
|
||||
"DISCOVER_PLUGINS_description": "",
|
||||
"DISCOVER_PLUGINS_name": "",
|
||||
"DISCOVER_PLUGINS_description": "Deaktiviere diese Option um die Initialisierung und Speicherdauer der Einstellungen zu verringern. Wenn es deaktiviert ist, können keine Plugins gefunden oder neue Plugins zu den vorhandenen hinzugefügt werden.",
|
||||
"DISCOVER_PLUGINS_name": "Entdecke Erweiterungen",
|
||||
"DevDetail_Copy_Device_Title": "Details von Gerät kopieren",
|
||||
"DevDetail_Copy_Device_Tooltip": "Details vom Gerät aus der Dropdown-Liste kopieren. Alles auf dieser Seite wird überschrieben",
|
||||
"DevDetail_CustomProperties_Title": "Benutzerdefinierte Eigenschaften",
|
||||
@@ -205,7 +205,7 @@
|
||||
"DevDetail_button_Save": "Speichern",
|
||||
"DeviceEdit_ValidMacIp": "Gib eine gültige <b>MAC</b>- und <b>IP</b>-Adresse ein.",
|
||||
"Device_MultiEdit": "Mehrfach-bearbeiten",
|
||||
"Device_MultiEdit_Backup": "",
|
||||
"Device_MultiEdit_Backup": "Achtung! Falsche Eingaben können die Installation beschädigen. Bitte sichere deine Datenbank oder Gerätekonfiguration zuerst: (<a href=\"php/server/devices.php?action=ExportCSV\">Konfiguration herunterladen <i class=\"fa-solid fa-download fa-bounce\"></i></a>). Wie du dein Gerät wiederherstellen kannst findest du in der <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/BACKUPS.md#scenario-2-corrupted-database\" target=\"_blank\">Dokumentation über Backups</a>.",
|
||||
"Device_MultiEdit_Fields": "Felder bearbeiten:",
|
||||
"Device_MultiEdit_MassActions": "Massen aktionen:",
|
||||
"Device_MultiEdit_Tooltip": "Achtung! Beim Drücken werden alle Werte auf die oben ausgewählten Geräte übertragen.",
|
||||
@@ -219,9 +219,9 @@
|
||||
"Device_Shortcut_Favorites": "Favoriten",
|
||||
"Device_Shortcut_NewDevices": "Neue Geräte",
|
||||
"Device_Shortcut_OnlineChart": "Gerätepräsenz im Laufe der Zeit",
|
||||
"Device_TableHead_AlertDown": "",
|
||||
"Device_TableHead_AlertDown": "Alarm aus",
|
||||
"Device_TableHead_Connected_Devices": "Verbindungen",
|
||||
"Device_TableHead_CustomProps": "",
|
||||
"Device_TableHead_CustomProps": "Eigenschaften / Aktionen",
|
||||
"Device_TableHead_Favorite": "Favorit",
|
||||
"Device_TableHead_FirstSession": "Erste Sitzung",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
@@ -242,7 +242,7 @@
|
||||
"Device_TableHead_RowID": "Zeilen ID",
|
||||
"Device_TableHead_Rowid": "Zeilennummer",
|
||||
"Device_TableHead_SSID": "SSID",
|
||||
"Device_TableHead_SourcePlugin": "",
|
||||
"Device_TableHead_SourcePlugin": "Quellerweiterung",
|
||||
"Device_TableHead_Status": "Status",
|
||||
"Device_TableHead_SyncHubNodeName": "Synchronisationsknoten",
|
||||
"Device_TableHead_Type": "Typ",
|
||||
@@ -254,12 +254,12 @@
|
||||
"Device_Tablelenght": "Zeige _MENU_ Einträge",
|
||||
"Device_Tablelenght_all": "Alle",
|
||||
"Device_Title": "Geräte",
|
||||
"Devices_Filters": "",
|
||||
"Devices_Filters": "Filter",
|
||||
"Donations_Others": "Andere",
|
||||
"Donations_Platforms": "Sponsor-Platformen",
|
||||
"Donations_Text": "Hey 👋! </br> Thanks for clicking on this menu item 😅 </br> </br> I'm trying to collect some donations to make you better software. Also, it would help me not to get burned out. Me burning out might mean end of support for this app. Any small (recurring or not) sponsorship makes me want ot put more effort into this app. I don't want to lock features (new plugins) behind paywalls 🔐. </br> Currently, I'm waking up 2h before work so I contribute to the app a bit. If I had some recurring income I could shorten my workweek and in the remaining time fully focus on NetAlertX. You'd get more functionality, a more polished app and less bugs. </br> </br> Thanks for reading - I'm super grateful for any support ❤🙏 </br> </br> TL;DR: By supporting me you get: </br> </br> <ul><li>Regular updates to keep your data and family safe 🔄</li><li>Less bugs 🐛🔫</li><li>Better and more functionality➕</li><li>I don't get burned out 🔥🤯</li><li>Less rushed releases 💨</li><li>Better docs📚</li><li>Quicker and better support with issues 🆘</li><li>Less grumpy me 😄</li></ul> </br> 📧Email me to <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> if you want to get in touch or if I should add other sponsorship platforms. </br>",
|
||||
"Donations_Title": "Spenden",
|
||||
"ENABLE_PLUGINS_description": "NOTUSED Enables the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugins</a> functionality. Loading plugins requires more hardware resources so you might want to disable them on low-powered system.",
|
||||
"ENABLE_PLUGINS_description": "NOTUSED Enables the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins</a> functionality. Loading plugins requires more hardware resources so you might want to disable them on low-powered system.",
|
||||
"ENABLE_PLUGINS_name": "NOTUSED Enable Plugins",
|
||||
"ENCRYPTION_KEY_description": "Schlüssel zur Datenverschlüsselung.",
|
||||
"ENCRYPTION_KEY_name": "Verschlüsselungsschlüssel",
|
||||
@@ -332,7 +332,7 @@
|
||||
"Gen_Saved": "Gespeichert",
|
||||
"Gen_Search": "Suchen",
|
||||
"Gen_Select": "Auswählen",
|
||||
"Gen_SelectIcon": "",
|
||||
"Gen_SelectIcon": "<i class=\"fa-solid fa-chevron-down fa-fade\"></i>",
|
||||
"Gen_SelectToPreview": "Zur Vorschau auswählen",
|
||||
"Gen_Selected_Devices": "Ausgewählte Geräte:",
|
||||
"Gen_Switch": "Umschalten",
|
||||
@@ -340,7 +340,7 @@
|
||||
"Gen_Upd_Fail": "Aktualisierung fehlgeschlagen",
|
||||
"Gen_Update": "Aktualisieren",
|
||||
"Gen_Update_Value": "Wert aktualisieren",
|
||||
"Gen_ValidIcon": "",
|
||||
"Gen_ValidIcon": "<i class=\"fa-solid fa-chevron-right \"></i>",
|
||||
"Gen_Warning": "Warnung",
|
||||
"Gen_Work_In_Progress": "Keine Finalversion, feedback bitte unter: https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"Gen_create_new_device": "Neues Gerät",
|
||||
@@ -834,4 +834,4 @@
|
||||
"settings_update_item_warning": "",
|
||||
"test_event_icon": "",
|
||||
"test_event_tooltip": "Speichere die Änderungen, bevor Sie die Einstellungen testen."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "You can specify a custom SQL query which will generate a JSON file and then expose it via the <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> file endpoint</a>.",
|
||||
"API_CUSTOM_SQL_description": "You can specify a custom SQL query which will generate a JSON file and then expose it via the <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> file endpoint</a>.",
|
||||
"API_CUSTOM_SQL_name": "Custom endpoint",
|
||||
"API_TOKEN_description": "API token for secure communication. Generate one or enter any value. It's sent in the request header and used in the <code>SYNC</code> plugin, GraphQL server and other API endpoints. You can use the API endpoints to create custom integrations as descibed in the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md\" target=\"_blank\">API documentation</a>.",
|
||||
"API_TOKEN_name": "API token",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Sponsor platforms",
|
||||
"Donations_Text": "Hey 👋! </br> Thanks for clicking on this menu item 😅 </br> </br> I'm trying to collect some donations to make you better software. Also, it would help me not to get burned out, so I can support this app longer. Any small (recurring or not) sponsorship makes me want to put more effort into this app. </br> I'd love to shorten my work week and in the remaining time fully focus on NetAlertX. You'd get more functionality, a more polished app and less bugs. </br> </br> Thanks for reading - I'm grateful for any support ❤🙏 </br> </br> TL;DR: By supporting me you get: </br> </br> <ul><li>Regular updates to keep your data and family safe 🔄</li><li>Less bugs 🐛🔫</li><li>Better and more functionality➕</li><li>I don't get burned out 🔥🤯</li><li>Less rushed releases 💨</li><li>Better docs📚</li><li>Quicker and better support with issues 🆘</li></ul> </br> 📧Email me to <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> if you want to get in touch or if I should add other sponsorship platforms. </br>",
|
||||
"Donations_Title": "Donations",
|
||||
"ENABLE_PLUGINS_description": "Enables the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugins</a> functionality. Loading plugins requires more hardware resources so you might want to disable them on low-powered system.",
|
||||
"ENABLE_PLUGINS_description": "Enables the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins</a> functionality. Loading plugins requires more hardware resources so you might want to disable them on low-powered system.",
|
||||
"ENABLE_PLUGINS_name": "Enable Plugins",
|
||||
"ENCRYPTION_KEY_description": "Data encryption key.",
|
||||
"ENCRYPTION_KEY_name": "Encryption key",
|
||||
@@ -332,7 +332,7 @@
|
||||
"Gen_Warning": "Warning",
|
||||
"Gen_Work_In_Progress": "Work in progress, good time to feedback on https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"Gen_create_new_device": "New device",
|
||||
"Gen_create_new_device_info": "Devices are typically discovered using <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugins</a>. However, in certain cases, you may need to add devices manually. To explore specific scenarios check the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">Remote Networks documentation</a>.",
|
||||
"Gen_create_new_device_info": "Devices are typically discovered using <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins</a>. However, in certain cases, you may need to add devices manually. To explore specific scenarios check the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">Remote Networks documentation</a>.",
|
||||
"General_display_name": "General",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "This is a maintenance setting <b>DELETING devices</b>. If enabled (<code>0</code> is disabled), devices marked as <b>New Device</b> will be deleted if their <b>First Session</b> time was older than the specified hours in this setting. Use this setting if you want to auto-delete <b>New Devices</b> after <code>X</code> hours.",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "A device is displayed as present although it is \"Offline\".",
|
||||
"HelpFAQ_Cat_Presence_401_text": "If this happens, you have the possibility to delete the events for the device in question (details view). Another possibility would be to switch on the device and wait until NetAlertX recognizes the device as \"online\" with the next scan and then simply switch the device off again. Now NetAlertX should properly note the state of the device in the database with the next scan.",
|
||||
"HelpFAQ_Title": "Help / FAQ",
|
||||
"LOADED_PLUGINS_description": "Which Plugins to load. Adding plugins might slow the application. Read more about which plugins need to be enabled, types, or scanning options in the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme\">plugins docs</a>. Unloaded plugins will lose your settings. Only <code>disabled</code> plugins can be unloaded.",
|
||||
"LOADED_PLUGINS_description": "Which Plugins to load. Adding plugins might slow the application. Read more about which plugins need to be enabled, types, or scanning options in the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins docs</a>. Unloaded plugins will lose your settings. Only <code>disabled</code> plugins can be unloaded.",
|
||||
"LOADED_PLUGINS_name": "Loaded plugins",
|
||||
"LOG_LEVEL_description": "This setting will enable more verbose logging. Useful for debugging events writing into the database.",
|
||||
"LOG_LEVEL_name": "Print additional logging",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Puede especificar una consulta SQL personalizada que generará un archivo JSON y luego lo expondrá a través del <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\">archivo <code>table_custom_endpoint.json</code ></a>.",
|
||||
"API_CUSTOM_SQL_description": "Puede especificar una consulta SQL personalizada que generará un archivo JSON y luego lo expondrá a través del <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\">archivo <code>table_custom_endpoint.json</code ></a>.",
|
||||
"API_CUSTOM_SQL_name": "Endpoint personalizado",
|
||||
"API_TOKEN_description": "Token de API para asegurar la comunicación, puede generar uno o introducir cualquier valor. Se envía en el encabezado de solicitud. Se utiliza en el plugin <code>SYNC</code> del servidor GraphQL.",
|
||||
"API_TOKEN_name": "Token de la API",
|
||||
@@ -257,7 +257,7 @@
|
||||
"Donations_Platforms": "Plataforma de patrocinadores",
|
||||
"Donations_Text": "¡Hola! 👋 </br> Gracias por hacer clic en este elemento 😅 del menú </br> </br>, estoy tratando de recolectar algunas donaciones para mejorar el software. Además, me ayudaría a no quemarse, por lo que puedo apoyar esta aplicación por más tiempo. Cualquier pequeño patrocinio (recurrente o no) me hace querer esforzarme más en esta aplicación. </br> Me encantaría acortar mi semana de trabajo y en el tiempo que me queda centrarme por completo en NetAlertX. Obtendrías más funcionalidad, una aplicación más pulida y menos errores. </br> </br> Gracias por leer, agradezco cualquier apoyo ❤🙏 </br> </br> TL; DR: Al apoyarme, obtienes: </br> </br> <ul><li>Actualizaciones periódicas para mantener tus datos y tu familia seguros 🔄</li><li>Menos errores 🐛🔫</li><li>Mejor y más funcionalidad➕</li><li>No me quemo 🔥🤯</li><li>Lanzamientos 💨menos apresurados</li> <li>Mejores documentos📚</li><li>Soporte más rápido y mejor con problemas 🆘</li></ul> </br> 📧Envíame un correo electrónico a <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> si quieres ponerte en contacto o si debo añadir otras plataformas de patrocinio. </br>",
|
||||
"Donations_Title": "Donaciones",
|
||||
"ENABLE_PLUGINS_description": "Habilita la funcionalidad de los <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">complementos</a>. Cargar los complementos requiere más recursos de hardware, así que quizás quieras desactivarlo en hardware poco potente.",
|
||||
"ENABLE_PLUGINS_description": "Habilita la funcionalidad de los <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">complementos</a>. Cargar los complementos requiere más recursos de hardware, así que quizás quieras desactivarlo en hardware poco potente.",
|
||||
"ENABLE_PLUGINS_name": "Habilitar complementos",
|
||||
"ENCRYPTION_KEY_description": "",
|
||||
"ENCRYPTION_KEY_name": "",
|
||||
@@ -342,7 +342,7 @@
|
||||
"Gen_Warning": "Advertencia",
|
||||
"Gen_Work_In_Progress": "Trabajo en curso, un buen momento para hacer comentarios en https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"Gen_create_new_device": "Nuevo dispositivo",
|
||||
"Gen_create_new_device_info": "Los dispositivos se suelen descubrir utilizando <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugins</a>. Sin embargo, en algunos casos, es posible que necesite agregar dispositivos manualmente. Para explorar escenarios específicos, consulte la documentación <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">Redes remotas</a>.",
|
||||
"Gen_create_new_device_info": "Los dispositivos se suelen descubrir utilizando <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins</a>. Sin embargo, en algunos casos, es posible que necesite agregar dispositivos manualmente. Para explorar escenarios específicos, consulte la documentación <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">Redes remotas</a>.",
|
||||
"General_display_name": "General",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Se trata de una configuración de mantenimiento <b>BORRAR dispositivos</b>. Si está activado (<code>0</code> está desactivado), los dispositivos marcados como <b>Nuevo dispositivo</b> se eliminarán si su fecha de <b>primera sesión</b> es anterior a las horas especificadas en este ajuste. Use este ajuste si desea eliminar automáticamente <b>Nuevos dispositivos</b> después de <code>X</code> horas.",
|
||||
@@ -385,7 +385,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "Un dispositivo se muestra como presente aunque esté \"Offline\".",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Si esto ocurre, tiene la posibilidad de borrar los eventos del dispositivo en cuestión (vista de detalles). Otra posibilidad sería encender el dispositivo y esperar hasta que NetAlertX reconozca el dispositivo como \"en línea\" con el siguiente escaneo y, a continuación, simplemente apagar el dispositivo de nuevo. Ahora NetAlertX debería anotar correctamente el estado del dispositivo en la base de datos con el siguiente escaneo.",
|
||||
"HelpFAQ_Title": "Ayuda / FAQ",
|
||||
"LOADED_PLUGINS_description": "¿Qué plugins cargar?. Agregar plugins puede ralentizar la aplicación. Obtén más información sobre los complementos que deben habilitarse, los tipos o las opciones de escaneo en los documentos de <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme\">plugins</a>. Los plugins descargados perderán tu configuración. Solo se pueden descargar los complementos <code>deshabilitados</code>.",
|
||||
"LOADED_PLUGINS_description": "¿Qué plugins cargar?. Agregar plugins puede ralentizar la aplicación. Obtén más información sobre los complementos que deben habilitarse, los tipos o las opciones de escaneo en los documentos de <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins</a>. Los plugins descargados perderán tu configuración. Solo se pueden descargar los complementos <code>deshabilitados</code>.",
|
||||
"LOADED_PLUGINS_name": "Plugins cargados",
|
||||
"LOG_LEVEL_description": "Esto hará que el registro tenga más información. Util para depurar que eventos se van guardando en la base de datos.",
|
||||
"LOG_LEVEL_name": "Imprimir registros adicionales",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Vous pouvez spécifier votre propre requête SQL qui retournera un fichier JSON et l'exposer via <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> le point de terminaison de fichier</a>.",
|
||||
"API_CUSTOM_SQL_description": "Vous pouvez spécifier votre propre requête SQL qui retournera un fichier JSON et l'exposer via <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> le point de terminaison de fichier</a>.",
|
||||
"API_CUSTOM_SQL_name": "Point de terminaison personnalisé",
|
||||
"API_TOKEN_description": "Vous pouvez renseigner ou générer un jeton API pour sécuriser les échanges. Il est transmis dans le header de la requête, et utilisé dans le plugin <code>SYNC</code>, le serveur GraphQL et d'autres usages API. Vous pouvez utiliser les points de terminaison API pour créer des intégrations spécifiques, comme décrit dans la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md\" target=\"_blank\">documentation de l'API</a>.",
|
||||
"API_TOKEN_name": "Jeton d'API",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Plateformes de sponsoring",
|
||||
"Donations_Text": "Coucou 👋 ! </br> Merci d'avoir cliqué ici 😅 </br> </br> J'essaie de récolter des donations pour vous faire un meilleur produit. En plus, ça m'aide à éviter le burn-out pour développer cette application plus longtemps. Toute subvention (régulière ou non) me donne envie de poursuivre le développement de cette application.</br> J'aimerais réduire mon activité principale pour me concentrer plus longuement à NetAlertX. Vous auriez plus de fonctionnalités, une application mieux finie et avec moins de bugs.</br> </br> Merci de votre lecture - je vous suis reconnaissant pour votre soutien ❤🙏 </br> </br> Version courte : en me soutenant, vous aurez : </br> </br> <ul><li>Des mises à jour régulières pour protéger vos données personnelles et familiales 🔄</li><li>Moins de bugs 🐛🔫</li><li>Des fonctionnalités plus riches et plus nombreuses ➕</li><li>Je ne me retrouve pas en burn-out 🔥🤯</li><li>Des versions moins à la va-vite 💨</li><li>une meilleure documentation <20></li><li>Un support meilleur et plus réactif en cas de problème 🆘</li></ul> </br> 📧Envoyez-moi un courriel à <a href='mailto :jokob@duck.com ?subject=NetAlertX'>jokob@duck.com</a> si vous voulez me contacter ou du je peux ajouter une autre plateforme de soutien. </br>",
|
||||
"Donations_Title": "Dons",
|
||||
"ENABLE_PLUGINS_description": "Active les fonctionnalités des <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">Plugins</a>. Charger des plugins nécessite plus de ressources, il est recommandé de les désactiver sur des systèmes de faible puissance.",
|
||||
"ENABLE_PLUGINS_description": "Active les fonctionnalités des <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">Plugins</a>. Charger des plugins nécessite plus de ressources, il est recommandé de les désactiver sur des systèmes de faible puissance.",
|
||||
"ENABLE_PLUGINS_name": "Activer les Plugins",
|
||||
"ENCRYPTION_KEY_description": "Clé de chiffrement des données.",
|
||||
"ENCRYPTION_KEY_name": "Clé de chiffrement",
|
||||
@@ -332,7 +332,7 @@
|
||||
"Gen_Warning": "Avertissement",
|
||||
"Gen_Work_In_Progress": "Travaux en cours, c'est le bon moment pour faire un retour via la liste d'anomalies sur Github https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"Gen_create_new_device": "Nouvel appareil",
|
||||
"Gen_create_new_device_info": "Les appareils sont souvent découverts à l'aide d'un <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugin</a>. Cependant, dans certains cas, vous pouvez ajouter manuellement les appareils. Pour explorer des scénarios spécifiques, consulter la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">documentation des réseaux distants</a>.",
|
||||
"Gen_create_new_device_info": "Les appareils sont souvent découverts à l'aide d'un <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugin</a>. Cependant, dans certains cas, vous pouvez ajouter manuellement les appareils. Pour explorer des scénarios spécifiques, consulter la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">documentation des réseaux distants</a>.",
|
||||
"General_display_name": "Général",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Paramètre de maintenance. S'il est activé (<code>0</code> s'il est désactivé), les appareils marqués comme <b>Nouvel appareil</b> seront supprimés si leur durée depuis la <b>première session</b> est plus ancienne que le nombre d'heures paramétré. Utilisez ce paramétrage si vous voulez supprimer automatiquement les <b>Nouveaux appareils</b> après <code>X</code> heures.",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "Un appareil est affiché comme présent bien qu'il soit \"Hors ligne\".",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Si cela arrive, vous avez la possibilité de supprimer les événements pour les appareils concernés (vue détaillée). Vous pouvez aussi allumer l'appareil, attendre que NetAlertX le détecte comme en ligne, puis simplement éteindre l'appareil à nouveau. NetAlertX devait maintenant correctement détecter l'état de l'appareil lors du prochain scan.",
|
||||
"HelpFAQ_Title": "Aide / FAQ",
|
||||
"LOADED_PLUGINS_description": "Affiche les plugins chargés. Ajouter des plugins peut ralentir l'application. Obtenez plus d'informations dur quels plugins dont à activer, ou les options de scan dans la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme\">documentation des plugins</a>. Décharger des plugins leur fait perdre leurs paramètres. Seuls les plugins <code>désactivés</code> peuvent être déchargés.",
|
||||
"LOADED_PLUGINS_description": "Affiche les plugins chargés. Ajouter des plugins peut ralentir l'application. Obtenez plus d'informations dur quels plugins dont à activer, ou les options de scan dans la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">documentation des plugins</a>. Décharger des plugins leur fait perdre leurs paramètres. Seuls les plugins <code>désactivés</code> peuvent être déchargés.",
|
||||
"LOADED_PLUGINS_name": "Plugins chargés",
|
||||
"LOG_LEVEL_description": "Ce paramètre active une journalisation dans les logs plus verbeuse. Cela est utile pour identifier les événements écrivant dans la base de données.",
|
||||
"LOG_LEVEL_name": "Afficher des journaux de log additionnels",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Puoi specificare una query SQL personalizzata che genererà un file JSON e quindi lo esporrà tramite l'<a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code>endpoint del file</a>.",
|
||||
"API_CUSTOM_SQL_description": "Puoi specificare una query SQL personalizzata che genererà un file JSON e quindi lo esporrà tramite l'<a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code>endpoint del file</a>.",
|
||||
"API_CUSTOM_SQL_name": "Endpoint personalizzato",
|
||||
"API_TOKEN_description": "Token API per comunicazioni sicure. Generane uno o inserisci un valore qualsiasi. Viene inviato nell'intestazione della richiesta e utilizzato nel plugin <code>SYNC</code>, nel server GraphQL e in altri endpoint API. Puoi utilizzare gli endpoint API per creare integrazioni personalizzate come descritto nella <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md\" target=\"_blank\">documentazione API</a>.",
|
||||
"API_TOKEN_name": "Token API",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Piattaforme sponsor",
|
||||
"Donations_Text": "Hey 👋! </br> Grazie per aver cliccato su questa voce di menu 😅 </br> </br> Sto cercando di ricevere donazioni per poter fornire un software migliore. Inoltre potrebbe aiutarmi a non andare in burnout, in modo da poter supportare questa app più a lungo. Ogni piccola (ricorrente o non) sponsorizzazione mi invoglia a mettere più impegno nello sviluppo di questa app. </br> Mi piacerebbe accorciare la mia settimana lavorativa e nel tempo rimanente dedicarmi completamente a NetAlertX. Riceverai più funzionalità, un'applicazione più rifinita e con meno bug.</br> </br> Grazie per aver letto, ti sono grato per ogni tipo di supporto ❤🙏 </br> </br> TL;DR: Supportandomi otterrai: </br> </br> <ul><li>Aggiornamenti più regolari per mantenere i tuoi dati e la tua famiglia sicuri 🔄</li><li>Meno bug 🐛🔫</li><li>Funzionalità migliori e più numerose➕</li><li>Io non vado in burnout 🔥🤯</li><li>Rilasci meno affrettati 💨</li><li>Migliore documentazione 📚</li><li>Supporto migliore e più veloce in caso di problemi 🆘</li></ul> </br> 📧Invia una mail a <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> se vuoi contattarmi o chiedermi di aggiungere altre piattaforme di sponsorizzazione. </br>",
|
||||
"Donations_Title": "Donazioni",
|
||||
"ENABLE_PLUGINS_description": "Abilita la funzionalità <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugin</a>. Utilizzare i plugin richiede più risorse hardware, potresti voler disabilitare questa opzione sui dispositivi meno performanti.",
|
||||
"ENABLE_PLUGINS_description": "Abilita la funzionalità <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugin</a>. Utilizzare i plugin richiede più risorse hardware, potresti voler disabilitare questa opzione sui dispositivi meno performanti.",
|
||||
"ENABLE_PLUGINS_name": "Abilita plugin",
|
||||
"ENCRYPTION_KEY_description": "Chiave di crittografia dei dati.",
|
||||
"ENCRYPTION_KEY_name": "Chiave di crittografia",
|
||||
@@ -332,7 +332,7 @@
|
||||
"Gen_Warning": "Avviso",
|
||||
"Gen_Work_In_Progress": "Lavori in corso, è quindi un buon momento per un feedback su https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"Gen_create_new_device": "Nuovo dispositivo",
|
||||
"Gen_create_new_device_info": "I dispositivi vengono generalmente rilevati utilizzando <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugin</a>. Tuttavia, in alcuni casi, potrebbe essere necessario aggiungere manualmente i dispositivi. Per esplorare scenari specifici, consulta la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">documentazione sulle reti remote</a>.",
|
||||
"Gen_create_new_device_info": "I dispositivi vengono generalmente rilevati utilizzando <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugin</a>. Tuttavia, in alcuni casi, potrebbe essere necessario aggiungere manualmente i dispositivi. Per esplorare scenari specifici, consulta la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">documentazione sulle reti remote</a>.",
|
||||
"General_display_name": "Generale",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Questa è un'impostazione di manutenzione <b>ELIMINAZIONE dispositivi</b>. Se abilitata (<code>0</code> è disabilitata), tutti i dispositivi marcati con <b>Nuovo dispositivo</b> verranno eliminati se l'orario della <b>Prima sessione</b> è precedente all'orario di questa impostazione. Usa questa impostazione se vuoi eliminare automaticamente i <b>Nuovi dispositivi</b> dopo <code>X</code> ore.",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "Un dispositivo viene visualizzato come presente anche se è \"Offline\".",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Se questo accade, hai la possibilità di eliminare gli eventi per il dispositivo in questione (visualizzazione dettagli). Un'altra possibilità potrebbe essere quella di accendere il dispositivo, attendere fino a quando NetAlertX non riconosce il dispositivo come \"online\" con la scansione successiva e poi spegnere il dispositivo. Ora NetAlertX dovrebbe annotare correttamente lo stato del dispositivo nel database con la scansione successiva.",
|
||||
"HelpFAQ_Title": "Aiuto / FAQ",
|
||||
"LOADED_PLUGINS_description": "Quali Plugin caricare. L'aggiunta di plugin potrebbe rallentare l'applicazione. Leggi di più su quali plugin necessitano di essere abilitati, tipi e opzioni di scansione nella <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme\">documentazione plugin</a>. I plugin disinstallati perdono la loro configurazione. Solo i plugin <code>disabilitati</code> possono essere disinstallati.",
|
||||
"LOADED_PLUGINS_description": "Quali Plugin caricare. L'aggiunta di plugin potrebbe rallentare l'applicazione. Leggi di più su quali plugin necessitano di essere abilitati, tipi e opzioni di scansione nella <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">documentazione plugin</a>. I plugin disinstallati perdono la loro configurazione. Solo i plugin <code>disabilitati</code> possono essere disinstallati.",
|
||||
"LOADED_PLUGINS_name": "Plugin caricati",
|
||||
"LOG_LEVEL_description": "Questa impostazione abilita un log più dettagliato. Utile per il debug degli eventi salvati nel database.",
|
||||
"LOG_LEVEL_name": "Stampa log aggiuntivo",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Du kan spesifisere en egendefinert SQL-Spørring som vil generere en JSON-fil og deretter eksponere den via <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> file endpoint</a>.",
|
||||
"API_CUSTOM_SQL_description": "Du kan spesifisere en egendefinert SQL-Spørring som vil generere en JSON-fil og deretter eksponere den via <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> file endpoint</a>.",
|
||||
"API_CUSTOM_SQL_name": "Egendefinert endepunkt",
|
||||
"API_TOKEN_description": "",
|
||||
"API_TOKEN_name": "",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Sponsorplattformer",
|
||||
"Donations_Text": "Hei 👋! </br> Takk for at du klikket på dette menyelementet 😅 </br> </br> Jeg prøver å samle inn noen donasjoner for å lage bedre programvare. Dessuten ville det hjelpe meg å ikke bli utbrent, så jeg kan støtte denne appen lenger. Enhver liten (tilbakevendende eller ikke) sponsing gjør at jeg ønsker å legge mer innsats i denne appen. </br> Jeg vil gjerne forkorte arbeidsuken min og i den gjenværende tiden fokusere fullt ut på NetAlertX. Du vil få mer funksjonalitet, en mer polert app og mindre feil. </br> </br> Takk for at du leste - jeg er takknemlig for all støtte ❤🙏 </br> </br> TL;DR: Ved å støtte meg får du: </br> </br> <ul> <li>Jevne oppdateringer for å holde dataene dine og familien din trygge 🔄</li><li>Mindre feil 🐛🔫</li><li>Bedre og mer funksjonalitet➕</li><li>Jeg blir ikke utbrent 🔥🤯</li><li>Mindre forhastede utgivelser 💨</li><li>Bedre dokumenter📚</li><li>Raskere og bedre støtte med problemer 🆘</li></ul> </br> 📧 Send meg en e-post til <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> hvis du ønsker å komme i kontakt eller hvis jeg skal legge til andre sponsorplattformer. </br>",
|
||||
"Donations_Title": "Donasjoner",
|
||||
"ENABLE_PLUGINS_description": "Aktiverer <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugins</a> funksjonaliten. Å laste inn plugins krever mer maskinvareressurser, så det kan være lurt å deaktivere dem på et system med lav strøm.",
|
||||
"ENABLE_PLUGINS_description": "Aktiverer <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins</a> funksjonaliten. Å laste inn plugins krever mer maskinvareressurser, så det kan være lurt å deaktivere dem på et system med lav strøm.",
|
||||
"ENABLE_PLUGINS_name": "Aktiver Plugins",
|
||||
"ENCRYPTION_KEY_description": "",
|
||||
"ENCRYPTION_KEY_name": "",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "En enhet vises som tilstede selv om den er \"frakoblet\".",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Hvis dette skjer, har du muligheten til å slette hendelsene for den aktuelle enheten (detaljer visning). En annen mulighet ville være å slå på enheten og vente til NetAlertX gjenkjenner enheten som \"online\" med neste skanning og så ganske enkelt slå av enheten igjen. Nå bør NetAlertX registrere tilstanden til enheten i databasen med neste skanning.",
|
||||
"HelpFAQ_Title": "Hjelp / Vanlige spørsmål",
|
||||
"LOADED_PLUGINS_description": "Hvilke plugins som skal lastes. Å legge til plugins kan gjøre programmet tregere. Les mer om hvilke plugins som må aktiveres, typer eller skannealternativer i <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme\">plugin dokumentasjonen</a>. Ulastede plugins vil miste innstillingene sine. Bare <code>deaktiverte</code> plugins kan lastes ut.",
|
||||
"LOADED_PLUGINS_description": "Hvilke plugins som skal lastes. Å legge til plugins kan gjøre programmet tregere. Les mer om hvilke plugins som må aktiveres, typer eller skannealternativer i <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugin dokumentasjonen</a>. Ulastede plugins vil miste innstillingene sine. Bare <code>deaktiverte</code> plugins kan lastes ut.",
|
||||
"LOADED_PLUGINS_name": "Lastede plugins",
|
||||
"LOG_LEVEL_description": "Denne innstillingen vil aktivere mer detaljert logging. Nyttig for feilsøking av hendelser som skrives inn i databasen.",
|
||||
"LOG_LEVEL_name": "Skriv ut tilleggslogging",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Możesz określić własne zapytanie SQL które będzie generowało plik JSON i udostępnić je poprzez plik typu endpoint <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> </a>.",
|
||||
"API_CUSTOM_SQL_description": "Możesz określić własne zapytanie SQL które będzie generowało plik JSON i udostępnić je poprzez plik typu endpoint <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> </a>.",
|
||||
"API_CUSTOM_SQL_name": "Własny endpoint",
|
||||
"API_TOKEN_description": "",
|
||||
"API_TOKEN_name": "",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Platforma Sponsora",
|
||||
"Donations_Text": "Cześć 👋! </br> Dziękuje że kliknąłeś w to menu 😅 </br> </br> Próbuje zebrać trochę dotacji by ulepszyć to oprogramowanie. Także pomaga mi to się nie wypalić bym dalej mógł ulepszać to narzędzie. Każdy mały (powtarzający się lub nie) sponsoring sprawia że chce wkładać więcej pracy w tą aplikację. </br> Chciałbym skrócić mój tydzień pracy i w wolnym czasie skupić się nad NetAlertX. Dostawalibyście wtedy więcej funkcjonalności i były by one ciągle udoskonalane i posiadające mniej błędów. </br> </br> Dziękuję że to przeczytałeś - Jestem wdzięczny za pomoc ❤🙏</br> </br> TL;DR: Wspierając mnie otrzymujesz: </br> </br> <ul><li> Regularne aktualizacje by zapewnić twoim danym i rodzinie bezpieczeństwo 🔄 </li><li>Mniej błędów (bugów) 🐛🔫</li><li>Nowe i lepsze funkcjonalności➕</li><li>Nie tracę zapału do dalszego tworzenia 🔥🤯</li><li>Mniej pośpieszne, bardziej dopracowane wydania💨</li><li>Lepsza dokumentacja📚</li><li>Szybsza i lepsza pomoc w problemach🆘</li></ul> </br>📧Napisz E-mail do mnie na<a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> jeżeli chcesz nawiązać kontakt albo czy powinien dodać kolejną platformę z sponsoringiem.</br>",
|
||||
"Donations_Title": "Dotacje",
|
||||
"ENABLE_PLUGINS_description": "Włącza funkcjonalność <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">pluginów</a>. Uruchomienie pluginów wymaga więcej zasobów sprzętu więcej możesz chcieć to wyłączyć dla słabszych systemów.",
|
||||
"ENABLE_PLUGINS_description": "Włącza funkcjonalność <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">pluginów</a>. Uruchomienie pluginów wymaga więcej zasobów sprzętu więcej możesz chcieć to wyłączyć dla słabszych systemów.",
|
||||
"ENABLE_PLUGINS_name": "Włącz Pluginy",
|
||||
"ENCRYPTION_KEY_description": "",
|
||||
"ENCRYPTION_KEY_name": "",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "Urządzenie jest wyświetlane jako dostępne imo iż jest \"Offline\".",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Jeżeli tak się dzieje, to masz możliwość usunięcia wydarzenia na danym urządzenia (widok szczegółowy). Kolejna możliwość to włączenie urządzenia i oczekiwanie aż NetAlertX wykryje urządzenie jako \"online\" przy kolejnym skanowaniu, a następnie wyłączenia ponownie urządzenia. Teraz NetAlertX powinien poprawnie zanotować stan urządzenia w bazie danych przy kolejnym skanowaniu.",
|
||||
"HelpFAQ_Title": "Pomoc / FAQ",
|
||||
"LOADED_PLUGINS_description": "Które Wtyczki załadować. Dodanie wtyczek może spowolnić aplikację. Przeczytaj więcej o wtyczkach które muszą być podłączone, typach lub opcjach skanowania w <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme\"> dokumentacji wtyczek</a>. Rozłączone wtyczki stracą twoje ustawienia. Tylko <code>wyłączone</code> wtyczki mogą być rozłączone.",
|
||||
"LOADED_PLUGINS_description": "Które Wtyczki załadować. Dodanie wtyczek może spowolnić aplikację. Przeczytaj więcej o wtyczkach które muszą być podłączone, typach lub opcjach skanowania w <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\"> dokumentacji wtyczek</a>. Rozłączone wtyczki stracą twoje ustawienia. Tylko <code>wyłączone</code> wtyczki mogą być rozłączone.",
|
||||
"LOADED_PLUGINS_name": "Załadowane wtyczki",
|
||||
"LOG_LEVEL_description": "To ustawienie uruchomi bardziej dokładnie logi. Użyteczne do debugowania powiadomień czekających w bazie danych.",
|
||||
"LOG_LEVEL_name": "Pokarz dodatkowe logi",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Você pode especificar uma consulta SQL personalizada que irá gerar um arquivo JSON e, em seguida, expô-lo por meio do <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> endpoint do arquivo</a>.",
|
||||
"API_CUSTOM_SQL_description": "Você pode especificar uma consulta SQL personalizada que irá gerar um arquivo JSON e, em seguida, expô-lo por meio do <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> endpoint do arquivo</a>.",
|
||||
"API_CUSTOM_SQL_name": "Endpoint customizado",
|
||||
"API_TOKEN_description": "API token para comunicação segura, você pode gerar um valor ou inserir qualquer valor. Este é enviado no cabeçalho da requisição. Usado no <code>SYNC</code> plugin, servidor GraphQL .",
|
||||
"API_TOKEN_name": "API token",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Plataformas de patrocinadores",
|
||||
"Donations_Text": "Ei 👋! </br> Obrigado por clicar neste item de menu 😅 </br> </br> Estou tentando coletar algumas doações para melhorar o software. Além disso, isso me ajudaria a não ficar exausto, para que eu pudesse oferecer suporte a este aplicativo por mais tempo. Qualquer pequeno patrocínio (recorrente ou não) me faz querer colocar mais esforço neste aplicativo. </br> Eu adoraria encurtar minha semana de trabalho e no tempo restante focar totalmente no NetAlertX. Você obteria mais funcionalidades, um aplicativo mais sofisticado e menos bugs. </br> </br> Obrigado pela leitura - sou grato por qualquer apoio ❤🙏 </br> </br> TL;DR: Ao me apoiar, você obtém: </br> </br> <ul> <li>Atualizações regulares para manter seus dados e sua família seguros 🔄</li><li>Menos bugs 🐛🔫</li><li>Melhor e mais funcionalidade➕</li><li>Eu não fico exausto 🔥🤯</li><li>Lançamentos menos apressados 💨</li><li>Documentos melhores📚</li><li>Suporte melhor e mais rápido com problemas 🆘</li></ul> </br> 📧 Envie-me um e-mail para <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> se quiser entrar em contato ou se devo adicionar outras plataformas de patrocínio. </br>",
|
||||
"Donations_Title": "Doações",
|
||||
"ENABLE_PLUGINS_description": "Ativa a funcionalidade de <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugins</a>. Carregar plug-ins requer mais recursos de hardware, então você pode querer desativá-los em sistemas de baixa potência.",
|
||||
"ENABLE_PLUGINS_description": "Ativa a funcionalidade de <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">plugins</a>. Carregar plug-ins requer mais recursos de hardware, então você pode querer desativá-los em sistemas de baixa potência.",
|
||||
"ENABLE_PLUGINS_name": "Habilitar plug-ins",
|
||||
"ENCRYPTION_KEY_description": "",
|
||||
"ENCRYPTION_KEY_name": "",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Вы можете указать собственный SQL-запрос, который будет генерировать файл JSON, а затем предоставлять его через конечную точку файла <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code></a>.",
|
||||
"API_CUSTOM_SQL_description": "Вы можете указать собственный SQL-запрос, который будет генерировать файл JSON, а затем предоставлять его через конечную точку файла <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code></a>.",
|
||||
"API_CUSTOM_SQL_name": "Пользовательская конечная точка",
|
||||
"API_TOKEN_description": "API-токен для безопасной связи. Сгенерируйте его или введите любое значение. Он передается в заголовке запроса и используется в плагине <code>SYNC</code>, сервере GraphQL и других конечных точках API. Вы можете использовать конечные точки API для создания пользовательских интеграций, как описано в <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md\" target=\"_blank\">документации по API</a>.",
|
||||
"API_TOKEN_name": "API token",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Спонсорские платформы",
|
||||
"Donations_Text": "Привет 👋! </br> Спасибо, что нажали на этот пункт меню 😅 </br> </br> Я пытаюсь собрать пожертвования, чтобы сделать ваше программное обеспечение лучше. Кроме того, это поможет мне не перегореть, и я смогу дольше поддерживать это приложение. Любое небольшое спонсорство (периодическое или нет) вызывает у меня желание приложить больше усилий к этому приложению. </br> Мне бы хотелось сократить свою рабочую неделю и в оставшееся время полностью сосредоточиться на NetAlertX. Вы получите больше функциональности, более усовершенствованное приложение и меньше ошибок. </br> </br> Спасибо за прочтение – буду благодарен за любую поддержку❤🙏 </br> </br> TL;DR: Поддержав меня, вы получаете: </br> </br> <ul><li>Регулярные обновления для обеспечения безопасности ваших данных и семьи 🔄</li><li>Меньше ошибок 🐛🔫</li><li>Лучшую функциональность➕</li><li>Я не выгораю 🔥🤯</li><li>Меньше поспешных релизов 💨</li><li>Лучшая документация📚</li><li>Быстрее и лучше поддержка по вопросам 🆘</li></ul> </br> 📧Напишите мне на <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> если вы хотите связаться или если следует добавить другие спонсорские платформы. </br>",
|
||||
"Donations_Title": "Пожертвования",
|
||||
"ENABLE_PLUGINS_description": "Включает функциональность <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">плагинов.</a> Загрузка плагинов требует больше аппаратных ресурсов, поэтому вы можете отключить их в маломощной системе.",
|
||||
"ENABLE_PLUGINS_description": "Включает функциональность <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">плагинов.</a> Загрузка плагинов требует больше аппаратных ресурсов, поэтому вы можете отключить их в маломощной системе.",
|
||||
"ENABLE_PLUGINS_name": "Разрешить плагины",
|
||||
"ENCRYPTION_KEY_description": "Ключ шифрования данных.",
|
||||
"ENCRYPTION_KEY_name": "Ключ шифрования",
|
||||
@@ -328,11 +328,11 @@
|
||||
"Gen_Upd_Fail": "Не удалось обновить",
|
||||
"Gen_Update": "Обновление",
|
||||
"Gen_Update_Value": "Обновить значение",
|
||||
"Gen_ValidIcon": "",
|
||||
"Gen_ValidIcon": "<i class=\"fa-solid fa-chevron-right \"></i>",
|
||||
"Gen_Warning": "Предупреждение",
|
||||
"Gen_Work_In_Progress": "Работа продолжается, самое время оставить отзыв на https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"Gen_create_new_device": "Новое устройство",
|
||||
"Gen_create_new_device_info": "Устройства обычно обнаруживаются с помощью <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">плагинов</a>. Однако в некоторых случаях вам может потребоваться добавить устройства вручную. Для изучения конкретных сценариев ознакомьтесь с документацией <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">Remote Networks</a>.",
|
||||
"Gen_create_new_device_info": "Устройства обычно обнаруживаются с помощью <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">плагинов</a>. Однако в некоторых случаях вам может потребоваться добавить устройства вручную. Для изучения конкретных сценариев ознакомьтесь с документацией <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">Remote Networks</a>.",
|
||||
"General_display_name": "Главное",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Это настройка обслуживания <b>УДАЛЕНИЕ устройств</b>. Если этот параметр включен (<code>0</code> отключен), устройства, помеченные как <b>Новое устройство</b>, будут удалены, если время их <b>Первого сеанса</b> было старше указанных в этой настройке часов. Используйте этот параметр, если вы хотите автоматически удалять <b>Новые устройства</b> через <code>X</code> часов.",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "Устройство отображается как присутствующее, хотя оно находится в режиме «Оффлайн».",
|
||||
"HelpFAQ_Cat_Presence_401_text": "В этом случае у вас есть возможность удалить события для соответствующего устройства (просмотр подробностей). Другой вариант — включить устройство и подождать, пока NetAlertX распознает устройство как «онлайн» при следующем сканировании, а затем просто снова выключить устройство. Теперь NetAlertX должен правильно отмечать состояние устройства в базе данных при следующем сканировании.",
|
||||
"HelpFAQ_Title": "Помощь / FAQ",
|
||||
"LOADED_PLUGINS_description": "Какие плагины загружать. Добавление плагинов может замедлить работу приложения. Подробнее о том, какие плагины необходимо включить, их типах или параметрах сканирования, читайте в <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme \">Документация по плагинам</a>. Выгруженные плагины потеряют ваши настройки. Можно выгрузить только <code>отключенные</code> плагины.",
|
||||
"LOADED_PLUGINS_description": "Какие плагины загружать. Добавление плагинов может замедлить работу приложения. Подробнее о том, какие плагины необходимо включить, их типах или параметрах сканирования, читайте в <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md \">Документация по плагинам</a>. Выгруженные плагины потеряют ваши настройки. Можно выгрузить только <code>отключенные</code> плагины.",
|
||||
"LOADED_PLUGINS_name": "Загруженные плагины",
|
||||
"LOG_LEVEL_description": "Этот параметр включит более подробное ведение журнала. Полезно для отладки записи событий в базу данных.",
|
||||
"LOG_LEVEL_name": "Распечатать дополнительный журнал",
|
||||
@@ -753,4 +753,4 @@
|
||||
"settings_update_item_warning": "Обновить значение ниже. Будьте осторожны, следуя предыдущему формату. <b>Проверка не выполняется.</b>",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Сначала сохраните изменения, прежде чем проверять настройки."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Ви можете вказати спеціальний SQL-запит, який створить файл JSON, а потім відкриє його через кінцеву точку файлу <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code></a>.",
|
||||
"API_CUSTOM_SQL_description": "Ви можете вказати спеціальний SQL-запит, який створить файл JSON, а потім відкриє його через кінцеву точку файлу <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code></a>.",
|
||||
"API_CUSTOM_SQL_name": "Спеціальна кінцева точка",
|
||||
"API_TOKEN_description": "Маркер API для безпечного зв’язку. Згенеруйте один або введіть будь-яке значення. Він надсилається в заголовку запиту та використовується в плагіні <code>SYNC</code>, сервері GraphQL та інших кінцевих точках API. Ви можете використовувати кінцеві точки API для створення спеціальних інтеграцій, як описано в <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md\" target=\"_blank\">API документація</a>.",
|
||||
"API_TOKEN_name": "Маркер API",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "Спонсорські платформи",
|
||||
"Donations_Text": "Привіт 👋! </br> Дякуємо, що натиснули цей пункт меню 😅 </br> </br> Я намагаюся зібрати пожертви, щоб зробити ваше програмне забезпечення кращим. Крім того, це допоможе мені не згоріти, тому я можу підтримувати цю програму довше. Будь-яке невелике (постійне чи ні) спонсорство спонукає мене докласти більше зусиль до цієї програми. </br> Я хотів би скоротити свій робочий тиждень і в час, що залишився, повністю зосередитися на NetAlertX. Ви отримаєте більше функціональності, досконаліший додаток і менше помилок. </br> </br> Дякую, що прочитали – я вдячний за будь-яку підтримку ❤🙏 </br> </br> TL;DR: Підтримуючи мене, ви отримуєте: </br> </br> <ul> <li>Регулярні оновлення для захисту ваших даних і родини 🔄</li><li>Менше помилок 🐛🔫</li><li>Краще та більше функціональність➕</li><li>Я не згорів 🔥🤯</li><li>Менш поспішних випусків 💨</li><li>Кращі документи📚</li><li>Швидша та краща підтримка із проблемами 🆘</li></ul> </br> 📧Напишіть мені електронною поштою на <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a>, якщо ви хочете зв’язатися з нами або якщо я маю додати інші платформи спонсорства. </br>",
|
||||
"Donations_Title": "Пожертви",
|
||||
"ENABLE_PLUGINS_description": "Вмикає функції <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">плагінів</a>. Завантаження плагінів вимагає більше апаратних ресурсів, тому ви можете вимкнути їх у системі з низьким енергоспоживанням.",
|
||||
"ENABLE_PLUGINS_description": "Вмикає функції <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">плагінів</a>. Завантаження плагінів вимагає більше апаратних ресурсів, тому ви можете вимкнути їх у системі з низьким енергоспоживанням.",
|
||||
"ENABLE_PLUGINS_name": "Увімкнути плагіни",
|
||||
"ENCRYPTION_KEY_description": "Ключ шифрування даних.",
|
||||
"ENCRYPTION_KEY_name": "Ключ шифрування",
|
||||
@@ -332,7 +332,7 @@
|
||||
"Gen_Warning": "УВАГА",
|
||||
"Gen_Work_In_Progress": "Робота триває, час залишити відгук на https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"Gen_create_new_device": "новий пристрій",
|
||||
"Gen_create_new_device_info": "Пристрої зазвичай виявляють за допомогою <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">плагінів</a>. Однак у деяких випадках може знадобитися додати пристрої вручну. Щоб дослідити конкретні сценарії, перегляньте <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">документацію щодо віддалених мереж</a>.",
|
||||
"Gen_create_new_device_info": "Пристрої зазвичай виявляють за допомогою <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">плагінів</a>. Однак у деяких випадках може знадобитися додати пристрої вручну. Щоб дослідити конкретні сценарії, перегляньте <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md\">документацію щодо віддалених мереж</a>.",
|
||||
"General_display_name": "Загальний",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Це налаштування обслуговування <b>ВИДАЛЕННЯ пристроїв</b>. Якщо ввімкнено (<code>0</code> вимкнено), пристрої, позначені як <b>Новий пристрій</b>, буде видалено, якщо час їхнього <b>першого сеансу</b> був старшим за вказані години в цьому налаштування. Використовуйте це налаштування, якщо ви хочете автоматично видаляти <b>Нові пристрої</b> через <code>X</code> годин.",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "Пристрій відображається як присутній, хоча він «Офлайн».",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Якщо це станеться, у вас є можливість видалити події для відповідного пристрою (перегляд деталей). Іншою можливістю було б увімкнути пристрій і зачекати, поки NetAlertX розпізнає пристрій як «онлайн» під час наступного сканування, а потім просто знову вимкнути пристрій. Тепер NetAlertX повинен належним чином відзначати стан пристрою в базі даних під час наступного сканування.",
|
||||
"HelpFAQ_Title": "Довідка / FAQ",
|
||||
"LOADED_PLUGINS_description": "Які плагіни завантажити. Додавання плагінів може уповільнити роботу програми. Дізнайтеся більше про те, які плагіни потрібно ввімкнути, типи чи параметри сканування в <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme \">документи плагінів</a>. Вивантажені плагіни втратять налаштування. Лише <code>вимкнені</code> плагіни можна вивантажити.",
|
||||
"LOADED_PLUGINS_description": "Які плагіни завантажити. Додавання плагінів може уповільнити роботу програми. Дізнайтеся більше про те, які плагіни потрібно ввімкнути, типи чи параметри сканування в <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md \">документи плагінів</a>. Вивантажені плагіни втратять налаштування. Лише <code>вимкнені</code> плагіни можна вивантажити.",
|
||||
"LOADED_PLUGINS_name": "Завантажені плагіни",
|
||||
"LOG_LEVEL_description": "Цей параметр увімкне докладніше журналювання. Корисно для налагодження запису подій у базу даних.",
|
||||
"LOG_LEVEL_name": "Друк додаткового протоколювання",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "您可以指定一个自定义 SQL 查询,它将生成一个 JSON 文件,然后通过 <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> 文件端点</a> 公开它。",
|
||||
"API_CUSTOM_SQL_description": "您可以指定一个自定义 SQL 查询,它将生成一个 JSON 文件,然后通过 <a href=\"/php/server/query_json.php?file=table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> 文件端点</a> 公开它。",
|
||||
"API_CUSTOM_SQL_name": "自定义终点",
|
||||
"API_TOKEN_description": "",
|
||||
"API_TOKEN_name": "",
|
||||
@@ -247,7 +247,7 @@
|
||||
"Donations_Platforms": "赞助平台",
|
||||
"Donations_Text": "嘿👋!</br> 感谢您点击此菜单项😅 </br> </br> 我正在尝试收集一些捐款来为您制作更好的软件。此外,这将有助于我避免精疲力竭,这样我就可以更长时间地支持这个应用程序。任何小额(无论是否经常性)赞助都会让我想在这个应用程序上投入更多精力。</br> 我希望缩短我的工作周,并在剩余的时间里完全专注于 NetAlertX。您将获得更多功能、更精致的应用程序和更少的错误。 </br> </br> 感谢阅读 - 我感谢任何支持 ❤🙏 </br> </br> TL;DR:通过支持我,您将获得:</br> </br> <ul><li>定期更新以确保您的数据和家人安全🔄</li><li>更少的错误🐛🔫</li><li>更好、更多的功能➕</li><li>我不会精疲力竭🔥🤯</li><li>更不仓促的发布💨</li><li>更好的文档📚</li><li>更快、更好地解决问题🆘</li></ul> </br> 📧如果您想联系我或者我应该添加其他赞助平台,请给我发电子邮件至<a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a>。 </br>",
|
||||
"Donations_Title": "捐款",
|
||||
"ENABLE_PLUGINS_description": "启用<a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">插件</a>功能。加载插件需要更多硬件资源,因此您可能需要在低功耗系统上禁用它们。",
|
||||
"ENABLE_PLUGINS_description": "启用<a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">插件</a>功能。加载插件需要更多硬件资源,因此您可能需要在低功耗系统上禁用它们。",
|
||||
"ENABLE_PLUGINS_name": "启用插件",
|
||||
"ENCRYPTION_KEY_description": "",
|
||||
"ENCRYPTION_KEY_name": "",
|
||||
@@ -375,7 +375,7 @@
|
||||
"HelpFAQ_Cat_Presence_401_head": "尽管设备处于“离线”状态,但仍显示其存在。",
|
||||
"HelpFAQ_Cat_Presence_401_text": "如果发生这种情况,您可以删除相关设备的事件(详细信息视图)。另一种可能性是打开设备并等待,直到 NetAlertX 在下一次扫描时将设备识别为“在线”,然后再关闭设备。现在 NetAlertX 应该会在下一次扫描时在数据库中正确记录设备的状态。",
|
||||
"HelpFAQ_Title": "帮助/常见问题",
|
||||
"LOADED_PLUGINS_description": "加载哪些插件。添加插件可能会降低应用程序的速度。在<a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme\">插件文档</a>中详细了解需要启用哪些插件、插件类型或扫描选项。卸载插件将丢失您的设置。只有<code>已禁用</code>的插件才能卸载。",
|
||||
"LOADED_PLUGINS_description": "加载哪些插件。添加插件可能会降低应用程序的速度。在<a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md\">插件文档</a>中详细了解需要启用哪些插件、插件类型或扫描选项。卸载插件将丢失您的设置。只有<code>已禁用</code>的插件才能卸载。",
|
||||
"LOADED_PLUGINS_name": "已加载插件",
|
||||
"LOG_LEVEL_description": "此设置将启用更详细的日志记录。对于调试写入数据库的事件很有用。",
|
||||
"LOG_LEVEL_name": "打印附加日志",
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": "python3 /app/front/plugins/_publisher_mqtt/mqtt.py devices={devices}",
|
||||
"default_value": "python3 /app/front/plugins/_publisher_mqtt/mqtt.py",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
|
||||
@@ -464,7 +464,8 @@ def mqtt_start(db):
|
||||
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'first_connection', 'calendar-start', device["devMac"])
|
||||
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'last_connection', 'calendar-end', device["devMac"])
|
||||
|
||||
|
||||
# handle device_tracker
|
||||
# device_tracker attributes
|
||||
devJson = {
|
||||
"last_ip": device["devLastIP"],
|
||||
"is_new": str(device["devIsNew"]),
|
||||
@@ -472,11 +473,14 @@ def mqtt_start(db):
|
||||
"mac_address": str(device["devMac"]),
|
||||
"model": devDisplayName,
|
||||
"last_connection": prepTimeStamp(str(device["devLastConnection"])),
|
||||
"first_connection": prepTimeStamp(str(device["devFirstConnection"])) }
|
||||
"first_connection": prepTimeStamp(str(device["devFirstConnection"])),
|
||||
"sync_node": device["devSyncHubNode"],
|
||||
"group": device["devGroup"],
|
||||
"location": device["devLocation"],
|
||||
"network_parent_mac": device["devParentMAC"],
|
||||
"network_parent_name": next((dev["devName"] for dev in devices if dev["devMAC"] == device["devParentMAC"]), "")
|
||||
}
|
||||
|
||||
# bulk update device sensors in home assistant
|
||||
publish_mqtt(mqtt_client, sensorConfig.state_topic, devJson)
|
||||
|
||||
# create and update is_present sensor
|
||||
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'binary_sensor', 'is_present', 'wifi', device["devMac"])
|
||||
publish_mqtt(mqtt_client, sensorConfig.state_topic,
|
||||
|
||||
@@ -408,6 +408,34 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "TOKEN",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "NTFY token"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Enter a token if authentication is enabled for hosting an instance. If a token is provided, the username and password will be ignored."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "USER",
|
||||
"type": {
|
||||
|
||||
@@ -94,23 +94,29 @@ def send(html, text):
|
||||
response_text = ''
|
||||
response_status_code = ''
|
||||
|
||||
# settings
|
||||
token = get_setting_value('NTFY_TOKEN')
|
||||
user = get_setting_value('NTFY_USER')
|
||||
pwd = get_setting_value('NTFY_PASSWORD')
|
||||
|
||||
# prepare request headers
|
||||
headers = {
|
||||
"Title": "NetAlertX Notification",
|
||||
"Actions": "view, Open Dashboard, "+ get_setting_value('REPORT_DASHBOARD_URL'),
|
||||
"Priority": get_setting_value('NTFY_PRIORITY'),
|
||||
"Tags": "warning"
|
||||
}
|
||||
|
||||
# if username and password are set generate hash and update header
|
||||
if get_setting_value('NTFY_USER') != "" and get_setting_value('NTFY_PASSWORD') != "":
|
||||
# Generate hash for basic auth
|
||||
# usernamepassword = "{}:{}".format(get_setting_value('NTFY_USER'),get_setting_value('NTFY_PASSWORD'))
|
||||
basichash = b64encode(bytes(get_setting_value('NTFY_USER') + ':' + get_setting_value('NTFY_PASSWORD'), "utf-8")).decode("ascii")
|
||||
|
||||
# add authorization header with hash
|
||||
# if token or username and password are set generate hash and update header
|
||||
if token != '':
|
||||
headers["Authorization"] = "Bearer {}".format(token)
|
||||
elif user != "" and pwd != "":
|
||||
# Generate hash for basic auth
|
||||
basichash = b64encode(bytes(user + ':' + pwd, "utf-8")).decode("ascii")
|
||||
# add authorization header with hash
|
||||
headers["Authorization"] = "Basic {}".format(basichash)
|
||||
|
||||
# call NTFY service
|
||||
try:
|
||||
response = requests.post("{}/{}".format( get_setting_value('NTFY_HOST'),
|
||||
get_setting_value('NTFY_TOPIC')),
|
||||
@@ -119,8 +125,6 @@ def send(html, text):
|
||||
|
||||
response_status_code = response.status_code
|
||||
|
||||
|
||||
|
||||
# Check if the request was successful (status code 200)
|
||||
if response_status_code == 200:
|
||||
response_text = response.text # This captures the response body/message
|
||||
|
||||
@@ -7,7 +7,7 @@ An alternative to on-network scanners is to enable some other Device scanners/im
|
||||
> Note: The scan time itself depends on the number of IP addresses to check so set this up carefully with the appropriate network mask and interface.
|
||||
|
||||
> [!NOTE]
|
||||
> If you have a lot of offline devices, which should be online, look into using, or substituing, ARP scan with other scans, such as `NMAPDEV`. The [ARP scan protocol uses](https://networkencyclopedia.com/arp-command/) a cache so results may not be 100% reliable. You can find all available network scanning options (marked as `🔍 dev scanner`) in the [Plugins overview](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) readme.
|
||||
> If you have a lot of offline devices, which should be online, look into using, or substituing, ARP scan with other scans, such as `NMAPDEV`. The [ARP scan protocol uses](https://networkencyclopedia.com/arp-command/) a cache so results may not be 100% reliable. You can find all available network scanning options (marked as `🔍 dev scanner`) in the [Plugins overview](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) readme.
|
||||
|
||||
### Usage
|
||||
|
||||
@@ -19,6 +19,12 @@ An alternative to on-network scanners is to enable some other Device scanners/im
|
||||
- SAVE
|
||||
- Wait for the next scan to finish
|
||||
|
||||
### Common issues
|
||||
|
||||
#### IP flipping on Google Nest devices
|
||||
|
||||
Some devices might flip IP addresses after each scan triggering false notifications. This is because some devices respond to broadcast calls and thus different IPs after scans are logged. To preven this you can try to use the `--exclude-broadcast` flag in the `ARPSCAN_ARGS` setting or change the `SCAN_SUBNETS` setting from e.g.: `192.168.1.0/24` to `192.168.1.1-192.168.1.254` to exclude the broadcast address `192.168.1.255` from the scanned range.
|
||||
|
||||
#### Examples
|
||||
|
||||
Settings:
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
## Solution: Getting the `dnsmasq.leases` from the Asus router and configuriong the `DHCPLSS` plugin:
|
||||
|
||||
|
||||
1. Enable SSH login on your Asus router
|
||||
2. Generate a pair of SSH keys and place them inside `/root/.ssh/`
|
||||
3. In your router's admin-settings, paste the public key and disable "password login" for SSH
|
||||
@@ -42,6 +43,19 @@ Host ASUS-GT-AXE16000
|
||||
Port 22
|
||||
```
|
||||
6. Try a dry run with the command in step 4. If everything is fine, you should have a `dnsmasq.leases` file at your target location
|
||||
|
||||
> [!NOTE]
|
||||
> You can also use ed25519 keys with passphrases. That makes the rsync command a little bit more complex.
|
||||
> First, one have to install sshpass: apt-get install sshpass
|
||||
> 1. create a file with your password that is required for the SSH key: `nano .password`
|
||||
> 2. Then change the `grabdnsmasq.sh` to: `#!/bin/bash sshpass -P passphrase -f '/root/.password' rsync -avzh -e 'ssh -i /root/.ssh/<yourprivatekey>' <asususer>@192.168.1.1:/var/lib/misc/dnsmasq.leases /mnt/service-data/netalertx_dhcp.leases/`
|
||||
> 3. replace:
|
||||
> - `/root/.password` to the path of the `.password` file
|
||||
> - `/root/.ssh/yourprivatekey` to the path of your private SSH key that is required for the ASUS router
|
||||
> - `asususer` to the login name of your ASUS router (standard is `admin`, not recommended)
|
||||
> - IP address of the ASUS router
|
||||
> - Path where rsync should copy the dhcp file (it should be a mounted path to NetAlertX) - here I use `/mnt/service-data/netalertx_dhcp.leases/`
|
||||
|
||||
7. Edit crontab for root:
|
||||
|
||||
`crontab -e`
|
||||
|
||||
@@ -398,7 +398,8 @@
|
||||
"XC5sYW4=",
|
||||
"XC5ob21l",
|
||||
"LVthLWZBLUYwLTldezMyfQ==",
|
||||
"Iy4q"
|
||||
"Iy4q",
|
||||
"XC5cYg=="
|
||||
],
|
||||
"options": [],
|
||||
"localized": [
|
||||
@@ -651,7 +652,7 @@
|
||||
{
|
||||
"name": "value",
|
||||
"type": "sql",
|
||||
"value": "SELECT DISTINCT '' as id, '❌None' as name UNION SELECT devOwner as id, devOwner as name FROM (SELECT devOwner FROM Devices UNION SELECT 'House' ) AS all_devices ORDER BY id;"
|
||||
"value": "SELECT DISTINCT '' as id, '❌None' as name UNION SELECT devOwner as id, devOwner as name FROM (SELECT devOwner FROM Devices) AS all_devices ORDER BY id;"
|
||||
}
|
||||
],
|
||||
"localized": [
|
||||
@@ -811,7 +812,7 @@
|
||||
{
|
||||
"name": "value",
|
||||
"type": "sql",
|
||||
"value": "SELECT DISTINCT '' as id, '❌None' as name UNION SELECT devGroup as id, devGroup as name FROM (SELECT devGroup FROM Devices WHERE devGroup <> '' UNION SELECT 'Personal' UNION SELECT 'Always on' UNION SELECT 'Friends' UNION SELECT 'Others' ) AS all_devices ORDER BY id;"
|
||||
"value": "SELECT DISTINCT '' as id, '❌None' as name UNION SELECT devGroup as id, devGroup as name FROM (SELECT devGroup FROM Devices WHERE devGroup <> '' ) AS all_devices ORDER BY id;"
|
||||
}
|
||||
],
|
||||
"localized": [
|
||||
@@ -855,7 +856,7 @@
|
||||
{
|
||||
"name": "value",
|
||||
"type": "sql",
|
||||
"value": "SELECT DISTINCT '' AS id, '❌None' AS name UNION SELECT devLocation AS id, devLocation AS name FROM Devices WHERE devLocation NOT IN ('', 'null') AND devLocation IS NOT NULL UNION SELECT 'Bathroom' AS id, 'Bathroom' AS name UNION SELECT 'Bedroom', 'Bedroom' UNION SELECT 'Dining room', 'Dining room' UNION SELECT 'Hall', 'Hall' UNION SELECT 'Kitchen', 'Kitchen' UNION SELECT 'Laundry', 'Laundry' UNION SELECT 'Living room', 'Living room' UNION SELECT 'Study', 'Study' UNION SELECT 'Attic', 'Attic' UNION SELECT 'Basement', 'Basement' UNION SELECT 'Garage', 'Garage' UNION SELECT 'Back yard', 'Back yard' UNION SELECT 'Garden', 'Garden' UNION SELECT 'Terrace', 'Terrace' ORDER BY id;"
|
||||
"value": "SELECT DISTINCT '' AS id, '❌None' AS name UNION SELECT devLocation AS id, devLocation AS name FROM Devices WHERE devLocation NOT IN ('', 'null') AND devLocation IS NOT NULL ORDER BY id;"
|
||||
}
|
||||
],
|
||||
"localized": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
NMAP-scan is a command-line tool to discover and fingerprint IP hosts on the local network. The NMAP-scan (and other Network-scan plugin times using the `SCAN_SUBNETS` setting) time depends on the number of IP addresses to check so set this up carefully with the appropriate network mask and interface. Check the [subnets documentation](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) for help with setting up VLANs, what VLANs are supported, or how to figure out the network mask and your interface.
|
||||
|
||||
> [!NOTE]
|
||||
> The `NMAPDEV` plugin is great for detecting the availability of devices, however ARP scan might be better covering multiple VLANS and subnets as NMAP can't pickup the MAC address from other subnets (this is an NMAP limitation) which are necessary to identify a device. You can always combine different scan methods. You can find all available network scanning options (marked as `🔍 dev scanner`) in the [Plugins overview](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) readme.
|
||||
> The `NMAPDEV` plugin is great for detecting the availability of devices, however ARP scan might be better covering multiple VLANS and subnets as NMAP can't pickup the MAC address from other subnets (this is an NMAP limitation) which are necessary to identify a device. You can always combine different scan methods. You can find all available network scanning options (marked as `🔍 dev scanner`) in the [Plugins overview](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) readme.
|
||||
|
||||
|
||||
This plugin is not the best for name resolution (Use e.g.: `NSLOOKUP`, `AVAHISCAN` instead), however if available a name will be displayed in the `Resolved Name` column.
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
## Overview
|
||||
|
||||
The OMADA SDN plugin aims at synchronizing data between NetAlertX and a TPLINK OMADA SND controler by leveraging a tplink omada python library.
|
||||
#### features:
|
||||
|
||||
#### Features
|
||||
|
||||
1. extract list of OMADA Clients from OMADA and sync them up with NetAlertX
|
||||
2. extract list of OAMDA Devices (switches and access points) and sync them up with NetAlertX
|
||||
|
||||
> [!TIP]
|
||||
> some omada devices are apparently not fully compatible with the API which might lead to partial results.
|
||||
> Some omada devices are apparently not fully compatible with the API which might lead to partial results.
|
||||
|
||||
### Quick setup guide
|
||||
|
||||
1. You SHOULD (ie: strongly recommend) set up an account in your OMADA SDN console dedicated to NetAlertX OMADA_SDN plugin.
|
||||
- you should set USER TYPE = Local USer
|
||||
- you should set USER ROLE = Administrator (if you use a read-only role you won't be able to sync names from NetAlerX to OMADA SDN)
|
||||
- you should set USER TYPE = Local User
|
||||
- you should set USER ROLE = Administrator (if you use a read-only role you won't be able to sync names from NetAlerX to OMADA SDN)
|
||||
- you can set Site Privileges = All Sites (or limit it to specific sites )
|
||||
|
||||
2. populate the variables in NetAlertX as instructed in the config plugin page.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Required Settings
|
||||
|
||||
- OMDSDN_url
|
||||
- OMDSDN_sites
|
||||
- OMDSDN_username
|
||||
- OMDSDN_password
|
||||
- OMDSDN_force_overwrite
|
||||
|
||||
- `OMDSDN_url`
|
||||
- `OMDSDN_sites`
|
||||
- `OMDSDN_username`
|
||||
- `OMDSDN_password` (if using special characters, make sure they are python-friendly (e.g. `~`))
|
||||
- `OMDSDN_force_overwrite`
|
||||
|
||||
### Usage
|
||||
|
||||
@@ -36,7 +34,7 @@ The OMADA SDN plugin aims at synchronizing data between NetAlertX and a TPLINK O
|
||||
### Notes
|
||||
|
||||
#### features not implemented yet:
|
||||
3. extract list of OAMDA router Devices (er605...) and sync them up with NetAlertX
|
||||
3. Extract list of OAMDA router Devices (er605...) and sync them up with NetAlertX
|
||||
(I need to setup my own er605 however due to its limitations I have no use for it, and due to limitations of opensense dhcp servers, I can't deploy it yet without breaking dhcp self registration into opnsense unbound - see below)
|
||||
|
||||
#### know limitations:
|
||||
@@ -63,10 +61,8 @@ can not fix some of tplinks OMADA SDN own limitations/bugs:
|
||||
- OMADA EAP245 - to be fair to tp-link, this access point works inside OMADA SDN, so it might be an issue with our omada python library but we can't extract data from it.
|
||||
|
||||
|
||||
|
||||
|
||||
## Other info
|
||||
|
||||
- Version: 1.0
|
||||
- Author : [Flying Toto](https://github.com/FlyingToto)
|
||||
- Author : [Flying Toto](https://github.com/FlyingToto) + [Leicas](https://github.com/Leicas)
|
||||
- Release Date: 04-Jul-2024
|
||||
|
||||
@@ -193,24 +193,46 @@ def add_uplink(
|
||||
sadevices_linksbymac,
|
||||
port_byswitchmac_byclientmac,
|
||||
):
|
||||
# mylog(OMDLOGLEVEL, [f'[{pluginName}] trying to add uplink="{uplink_mac}" to switch="{switch_mac}"'])
|
||||
# mylog(OMDLOGLEVEL, [f'[{pluginName}] before adding:"{device_data_bymac[switch_mac]}"'])
|
||||
if device_data_bymac[switch_mac][SWITCH_AP] == "null":
|
||||
# Ensure switch_mac exists in device_data_bymac
|
||||
if switch_mac not in device_data_bymac:
|
||||
mylog("none", [f"[{pluginName}] switch_mac '{switch_mac}' not found in device_data_bymac"])
|
||||
return
|
||||
|
||||
# Ensure SWITCH_AP key exists in the dictionary
|
||||
if SWITCH_AP not in device_data_bymac[switch_mac]:
|
||||
mylog("none", [f"[{pluginName}] Missing key '{SWITCH_AP}' in device_data_bymac[{switch_mac}]"])
|
||||
return
|
||||
|
||||
# Check if uplink should be added
|
||||
if device_data_bymac[switch_mac][SWITCH_AP] in [None, "null"]:
|
||||
device_data_bymac[switch_mac][SWITCH_AP] = uplink_mac
|
||||
|
||||
# Ensure uplink_mac exists in device_data_bymac
|
||||
if uplink_mac not in device_data_bymac:
|
||||
mylog("none", [f"[{pluginName}] uplink_mac '{uplink_mac}' not found in device_data_bymac"])
|
||||
return
|
||||
|
||||
# Determine port to uplink
|
||||
if (
|
||||
device_data_bymac[switch_mac][TYPE] == "Switch"
|
||||
and device_data_bymac[uplink_mac][TYPE] == "Switch"
|
||||
device_data_bymac[switch_mac].get(TYPE) == "Switch"
|
||||
and device_data_bymac[uplink_mac].get(TYPE) == "Switch"
|
||||
):
|
||||
port_to_uplink = port_byswitchmac_byclientmac[switch_mac][uplink_mac]
|
||||
# find_port_of_uplink_switch(switch_mac, uplink_mac)
|
||||
port_to_uplink = port_byswitchmac_byclientmac.get(switch_mac, {}).get(uplink_mac)
|
||||
if port_to_uplink is None:
|
||||
mylog("none", [f"[{pluginName}] Missing port info for switch_mac '{switch_mac}' and uplink_mac '{uplink_mac}'"])
|
||||
return
|
||||
else:
|
||||
port_to_uplink = device_data_bymac[uplink_mac][PORT_SSID]
|
||||
port_to_uplink = device_data_bymac[uplink_mac].get(PORT_SSID)
|
||||
|
||||
# Assign port to switch_mac
|
||||
device_data_bymac[switch_mac][PORT_SSID] = port_to_uplink
|
||||
# mylog(OMDLOGLEVEL, [f'[{pluginName}] after adding:"{device_data_bymac[switch_mac]}"'])
|
||||
for link in sadevices_linksbymac[switch_mac]:
|
||||
|
||||
# Recursively add uplinks for linked devices
|
||||
for link in sadevices_linksbymac.get(switch_mac, []):
|
||||
if (
|
||||
device_data_bymac[link][SWITCH_AP] == "null"
|
||||
and device_data_bymac[switch_mac][TYPE] == "Switch"
|
||||
link in device_data_bymac
|
||||
and device_data_bymac[link].get(SWITCH_AP) in [None, "null"]
|
||||
and device_data_bymac[switch_mac].get(TYPE) == "Switch"
|
||||
):
|
||||
add_uplink(
|
||||
switch_mac,
|
||||
@@ -221,6 +243,7 @@ def add_uplink(
|
||||
)
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------
|
||||
# Main initialization
|
||||
def main():
|
||||
|
||||
75
front/plugins/omada_sdn_openapi/README.md
Executable file
75
front/plugins/omada_sdn_openapi/README.md
Executable file
@@ -0,0 +1,75 @@
|
||||
## 🔍 Overview
|
||||
|
||||
- This plugin imports online devices and clients from the Omada SDN (Omada Controller) through the provided OpenAPI.
|
||||
|
||||
### ✨ Features
|
||||
|
||||
1. Import online devices (gateways, switches, and access points) compatible with Omada SDN and send them to NetAlertX.
|
||||
2. Import online clients (e.g., computers and smartphones) and send them to NetAlertX.
|
||||
|
||||
### 📌 Requirements
|
||||
|
||||
- Omada Controller with Open API support.
|
||||
|
||||
#### ✅ Officially supported controllers - [Source](https://community.tp-link.com/en/business/forum/topic/590430)
|
||||
- All Omada Pro versions support Open API
|
||||
- Omada Software/Hardware Controller support Open API since Controller v5.12
|
||||
|
||||
### ⚙️ Setup guide & settings
|
||||
|
||||
1. Login to your **Omada Controller**.
|
||||
2. In the **Global Dashboard**, navigate to **Settings**, select **Platform Integration**, then click on **Open API**.
|
||||
3. Create new credentials by clicking **Add New App**.
|
||||
- The `App Name` can be anything.
|
||||
- Set the `Mode` to `Client`.
|
||||
- Set the `Role` to `Viewer` or `Administrator`.
|
||||
- For `Site Privileges`, choose `All (Including all new-created sites)` or select specific site(s).
|
||||
- Click `Apply` to create the application.
|
||||
4. From the created application, you will need the following fields.
|
||||
- `Omada ID` - visible by clicking the **eye** icon next to the **edit** and **delete** buttons.
|
||||
- `Client ID`
|
||||
- `Client Secret`
|
||||
5. Open **NetAlertX's Settings**, head to **Omada SDN using OpenAPI** `(OMDSDNOPENAPI)` and configure the plugin.
|
||||
- `OMDSDNOPENAPI_RUN` - When the scan should run, good option is `schedule`.
|
||||
- `OMDSDNOPENAPI_host` - Specify the host URL of your **Omada Controller**, including the protocol, e.g., `https://example.com:1234`.
|
||||
- `OMDSDNOPENAPI_omada_id` - Enter the **Omada ID** obtained in the previous step.
|
||||
- `OMDSDNOPENAPI_client_id` - Enter the **Client ID** obtained in the previous step.
|
||||
- `OMDSDNOPENAPI_client_secret` - Enter the **Client Secret** obtained in the previous step.
|
||||
- `OMDSDNOPENAPI_sites` (optional) - You can enter either the **site name** or **site ID**. If an invalid value is provided or neither is specified, the plugin will default to the first accessible site using the supplied credentials.
|
||||
- `OMDSDNOPENAPI_verify_ssl` - Check this option to enable SSL verification for requests to your Omada Controller's OpenAPI. If unchecked, SSL verification will be disabled.
|
||||
|
||||
### 📋 Data populated by the plugin
|
||||
|
||||
- This table outlines the data fields populated by the plugin, their conditions, descriptions, and where they are visible.
|
||||
|
||||
| 🔹 Field | 🔄 Population Condition | 📖 Description | 👀 Visibility |
|
||||
|---|---|---|---|
|
||||
| **MAC** | Always populated | The device's unique MAC address | Device details |
|
||||
| **Last IP** | Always populated | The device's assigned IP address | Device details |
|
||||
| **Name** | Always populated | The device name retrieved from Omada | Device details |
|
||||
| **Parent Node** | Only if available | MAC address of the parent device (switch, AP, or gateway) | Device details |
|
||||
| **Parent Node Port** | Only if available | The port number used to connect to the parent device | Device details |
|
||||
| **SSID** | Only if available | The SSID through which the device is connected | Device details |
|
||||
| **Device Type** | Only if available | Detected device type (e.g., iPhone, PC, Android) | Device details |
|
||||
| **Last Seen** | Always populated | Last recorded time the device was active on the network | Plugin details |
|
||||
| **Omada Site** | Always populated | Omada site to which the device is assigned | Device details |
|
||||
| **VLAN ID** | Only if available | VLAN ID assigned to the device | Plugin details |
|
||||
|
||||
|
||||
### ⚠️ Limitations and warnings
|
||||
|
||||
- The plugin can fetch up to 1000 devices and 1000 clients from the Omada Controller.
|
||||
- Using non-Omada SDN compatible devices (e.g., switches, APs) may result in incomplete or inaccurate data.
|
||||
|
||||
### 🖼️ Examples
|
||||
|
||||
- Settings:
|
||||
|
||||
- 
|
||||
|
||||
### ℹ️ Other info
|
||||
|
||||
- Version: 1.0
|
||||
- Author : [xfilo](https://github.com/xfilo)
|
||||
- Release Date: 24-February-2025
|
||||
- Omada Open API documentation: https://use1-omada-northbound.tplinkcloud.com/doc.html#/home (may take a moment to load)
|
||||
669
front/plugins/omada_sdn_openapi/config.json
Executable file
669
front/plugins/omada_sdn_openapi/config.json
Executable file
@@ -0,0 +1,669 @@
|
||||
{
|
||||
"code_name": "omada_sdn_openapi",
|
||||
"unique_prefix": "OMDSDNOPENAPI",
|
||||
"plugin_type": "device_scanner",
|
||||
"execution_order" : "Layer_0",
|
||||
"enabled": true,
|
||||
"data_source": "script",
|
||||
"mapped_to_table": "CurrentScan",
|
||||
"data_filters": [
|
||||
{
|
||||
"compare_column": "Object_PrimaryID",
|
||||
"compare_operator": "==",
|
||||
"compare_field_id": "txtMacFilter",
|
||||
"compare_js_template": "'{value}'.toString()",
|
||||
"compare_use_quotes": true
|
||||
}
|
||||
],
|
||||
"show_ui": true,
|
||||
"localized": ["display_name", "description", "icon"],
|
||||
"display_name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Omada SDN using OpenAPI"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "This plugin imports devices and clients from the Omada SDN (Omada Controller) through the provided OpenAPI."
|
||||
}
|
||||
],
|
||||
"icon": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "<i class=\"fa fa-search\"></i>"
|
||||
}
|
||||
],
|
||||
"params": [],
|
||||
"settings": [
|
||||
{
|
||||
"function": "RUN",
|
||||
"events": ["run"],
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{ "elementType": "select", "elementOptions": [], "transformers": [] }
|
||||
]
|
||||
},
|
||||
|
||||
"default_value": "disabled",
|
||||
"options": ["disabled", "once", "schedule", "always_after_scan"],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "When to run"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "When the scan should run, good option is <code>schedule</code>."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "RUN_SCHD",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "span",
|
||||
"elementOptions": [
|
||||
{
|
||||
"cssClasses": "input-group-addon validityCheck"
|
||||
},
|
||||
{
|
||||
"getStringKey": "Gen_ValidIcon"
|
||||
}
|
||||
],
|
||||
"transformers": []
|
||||
},
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [
|
||||
{
|
||||
"onChange": "validateRegex(this)"
|
||||
},
|
||||
{
|
||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||
}
|
||||
],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"default_value": "*/5 * * * *",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Schedule"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Only enabled if you select <code>schedule</code> in the <a href=\"#OMDSDNOPENAPI_RUN\"><code>OMDSDNOPENAPI_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>0 4 * * *</code> will run the scan after 4 am in the selected <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code></a>. Will be run NEXT time the time passes."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "host",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{ "elementType": "input", "elementOptions": [], "transformers": [] }
|
||||
]
|
||||
},
|
||||
"maxLength": 100,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Host URL"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Specify the host URL of your <code>Omada Controller</code>, including the protocol, eg. <code>https://example.com:1234</code>."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "omada_id",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{ "elementType": "input", "elementOptions": [], "transformers": [] }
|
||||
]
|
||||
},
|
||||
"maxLength": 100,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Omada ID"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Provide your <code>Omada ID</code>, which can be found in the <code>OpenAPI</code> section of your <code>Omada Controller</code>."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "client_id",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{ "elementType": "input", "elementOptions": [], "transformers": [] }
|
||||
]
|
||||
},
|
||||
"maxLength": 100,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Client ID"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Enter the <code>Client ID</code> generated by your <code>Omada Controller</code> in the <code>OpenAPI</code> section."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "client_secret",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [{ "type": "password" }],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxLength": 100,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Client Secret"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Input the <code>Client Secret</code> obtained from the <code>OpenAPI</code> section of your <code>Omada Controller</code>."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "sites",
|
||||
"type": {
|
||||
"dataType": "array",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [
|
||||
{ "placeholder": "Enter value" },
|
||||
{ "suffix": "_in" },
|
||||
{ "cssClasses": "col-sm-10" },
|
||||
{ "prefillValue": "null" }
|
||||
],
|
||||
"transformers": []
|
||||
},
|
||||
{
|
||||
"elementType": "button",
|
||||
"elementOptions": [
|
||||
{ "sourceSuffixes": ["_in"] },
|
||||
{ "separator": "" },
|
||||
{ "cssClasses": "col-xs-12" },
|
||||
{ "onClick": "addList(this, false)" },
|
||||
{ "getStringKey": "Gen_Add" }
|
||||
],
|
||||
"transformers": []
|
||||
},
|
||||
{
|
||||
"elementType": "select",
|
||||
"elementHasInputValue": 1,
|
||||
"elementOptions": [
|
||||
{ "multiple": "true" },
|
||||
{ "readonly": "true" },
|
||||
{ "editable": "true" }
|
||||
],
|
||||
"transformers": []
|
||||
},
|
||||
{
|
||||
"elementType": "button",
|
||||
"elementOptions": [
|
||||
{ "sourceSuffixes": [] },
|
||||
{ "separator": "" },
|
||||
{ "cssClasses": "col-xs-6" },
|
||||
{ "onClick": "removeAllOptions(this)" },
|
||||
{ "getStringKey": "Gen_Remove_All" }
|
||||
],
|
||||
"transformers": []
|
||||
},
|
||||
{
|
||||
"elementType": "button",
|
||||
"elementOptions": [
|
||||
{ "sourceSuffixes": [] },
|
||||
{ "separator": "" },
|
||||
{ "cssClasses": "col-xs-6" },
|
||||
{ "onClick": "removeFromList(this)" },
|
||||
{ "getStringKey": "Gen_Remove_Last" }
|
||||
],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": [],
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Omada Sites"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "You can enter either the <code>site name</code> or <code>site ID</code>. If an invalid value is provided or neither is specified, the plugin will default to the first accessible site using the supplied credentials."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "verify_ssl",
|
||||
"type": {
|
||||
"dataType": "boolean",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [{ "type": "checkbox" }],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": true,
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Verify SSL"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Check this option to enable SSL verification for requests to your Omada Controller's OpenAPI. If unchecked, SSL verification will be disabled."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "CMD",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [{ "readonly": "true" }],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": "python3 /app/front/plugins/omada_sdn_openapi/script.py",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Command"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Command to run. This can not be changed."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "RUN_TIMEOUT",
|
||||
"type": {
|
||||
"dataType": "integer",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [{ "type": "number" }],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": 30,
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Run timeout"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"default_value": [],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Send a notification if selected values change. Use <code>CTRL + Click</code> to select/deselect. <ul> <li><code>Watched_Value1</code> is Device Name </li><li><code>Watched_Value2</code> is Parent Node MAC</li><li><code>Watched_Value3</code> is Parent Node Port </li><li><code>Watched_Value4</code> is Parent Node SSID </li></ul>"
|
||||
}
|
||||
],
|
||||
"function": "WATCH",
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Watched"
|
||||
}
|
||||
],
|
||||
"options": [
|
||||
"Watched_Value1",
|
||||
"Watched_Value2",
|
||||
"Watched_Value3",
|
||||
"Watched_Value4"
|
||||
],
|
||||
"type": {
|
||||
"dataType": "array",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "select",
|
||||
"elementOptions": [{ "multiple": "true" }],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"default_value": ["new", "watched-changed"],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Send a notification only on these statuses. <code>new</code> means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. <code>watched-changed</code> means that selected <code>Watched_ValueN</code> columns changed."
|
||||
}
|
||||
],
|
||||
"function": "REPORT_ON",
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Report on"
|
||||
}
|
||||
],
|
||||
"options": [
|
||||
"new",
|
||||
"watched-changed",
|
||||
"watched-not-changed",
|
||||
"missing-in-last-scan"
|
||||
],
|
||||
"type": {
|
||||
"dataType": "array",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "select",
|
||||
"elementOptions": [{ "multiple": "true" }],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"database_column_definitions": [
|
||||
{
|
||||
"column": "Index",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "none",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Index"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "Object_PrimaryID",
|
||||
"mapped_to_column": "cur_MAC",
|
||||
"css_classes": "col-sm-3",
|
||||
"show": true,
|
||||
"type": "device_name_mac",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "MAC Address"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "Object_SecondaryID",
|
||||
"mapped_to_column": "cur_IP",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "device_ip",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "IP Address"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"mapped_to_column": "cur_Name",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Device Name"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value2",
|
||||
"mapped_to_column": "cur_NetworkNodeMAC",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "device_name_mac",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Parent Node MAC"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value3",
|
||||
"mapped_to_column": "cur_PORT",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Parent Node Port"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value4",
|
||||
"mapped_to_column": "cur_SSID",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Parent Node SSID"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "Extra",
|
||||
"mapped_to_column": "cur_Type",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Device Type"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "Dummy",
|
||||
"mapped_to_column": "cur_ScanMethod",
|
||||
"mapped_to_column_data": {
|
||||
"value": "OMDSDNOPENAPI"
|
||||
},
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Scan method"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "DateTimeCreated",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Created"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "DateTimeChanged",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Changed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "HelpVal1",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Last Seen"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "HelpVal2",
|
||||
"mapped_to_column": "cur_NetworkSite",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Omada Site"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"column": "HelpVal3",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "VLAN ID"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
front/plugins/omada_sdn_openapi/omada_sdn_openapi_settings.png
Executable file
BIN
front/plugins/omada_sdn_openapi/omada_sdn_openapi_settings.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 176 KiB |
522
front/plugins/omada_sdn_openapi/script.py
Executable file
522
front/plugins/omada_sdn_openapi/script.py
Executable file
@@ -0,0 +1,522 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This plugin imports devices and clients from Omada Controller using their OpenAPI.
|
||||
|
||||
It was inspired by the 'omada_sdn_imp/omada_sdn.py' plugin,
|
||||
which relied on the 'tplink_omada_client' library instead of OpenAPI.
|
||||
However, I found that approach somewhat unstable, so I decided
|
||||
to give it a shot and create a new plugin with the goal of providing
|
||||
same, but more reliable results.
|
||||
|
||||
Please note that this is my first plugin, and I'm not a Python developer.
|
||||
Any comments, bug fixes, or contributions are greatly appreciated.
|
||||
|
||||
Author: https://github.com/xfilo
|
||||
"""
|
||||
|
||||
__author__ = "xfilo"
|
||||
__version__ = 0.1 # Initial version
|
||||
__version__ = 0.2 # Rephrased error messages, improved logging and code logic
|
||||
__version__ = 0.3 # Refactored data collection into a class, improved code clarity with comments
|
||||
|
||||
import os
|
||||
import sys
|
||||
import urllib3
|
||||
import requests
|
||||
import time
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Literal, Any, Dict
|
||||
|
||||
# Define the installation path and extend the system path for plugin imports
|
||||
INSTALL_PATH = "/app"
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Objects, is_typical_router_ip, is_mac
|
||||
from logger import mylog, Logger
|
||||
from const import logPath
|
||||
from helper import get_setting_value
|
||||
import conf
|
||||
|
||||
# Make sure the TIMEZONE for logging is correct
|
||||
conf.tz = pytz.timezone(get_setting_value('TIMEZONE'))
|
||||
|
||||
# Make sure log level is initialized correctly
|
||||
Logger(get_setting_value('LOG_LEVEL'))
|
||||
|
||||
pluginName = 'OMDSDNOPENAPI'
|
||||
|
||||
# Define the current path and log file paths
|
||||
LOG_PATH = logPath + '/plugins'
|
||||
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
|
||||
|
||||
# Disable insecure request warning
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
|
||||
class OmadaHelper:
|
||||
@staticmethod
|
||||
def log(message: str, level: Literal["minimal", "verbose", "debug", "trace"] = "minimal") -> None:
|
||||
mylog(level, [f"[{pluginName}] [{level[:1].upper()}] {message}"])
|
||||
|
||||
@staticmethod
|
||||
def debug(message: str) -> None:
|
||||
return OmadaHelper.log(message, "debug")
|
||||
|
||||
@staticmethod
|
||||
def verbose(message: str) -> None:
|
||||
return OmadaHelper.log(message, "verbose")
|
||||
|
||||
@staticmethod
|
||||
def minimal(message: str) -> None:
|
||||
return OmadaHelper.log(message, "minimal")
|
||||
|
||||
@staticmethod
|
||||
def response(response_type: str, response_message: str, response_result: Any = None) -> Dict[str, Any]:
|
||||
return {"response_type": response_type, "response_message": response_message, "response_result": response_result}
|
||||
|
||||
@staticmethod
|
||||
def timestamp_to_datetime(ms: int, timezone: str) -> Dict[str, Any]:
|
||||
"""Returns datetime from millisecond timestamp with required timezone."""
|
||||
try:
|
||||
if not ms or not isinstance(ms, (str, int)):
|
||||
raise ValueError(f"Value '{ms}' is not a valid timestamp")
|
||||
|
||||
# Convert UTC millisecond timestamp to datetime in NetAlertX's timezone
|
||||
timestamp = ms / 1000
|
||||
tz = pytz.timezone("UTC")
|
||||
utc_datetime = datetime.fromtimestamp(timestamp, tz=tz)
|
||||
target_timezone = pytz.timezone(timezone)
|
||||
local_datetime = utc_datetime.astimezone(target_timezone)
|
||||
result = local_datetime.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
msg = f"Converted timestamp {ms} to datetime {result} with timezone {timezone}"
|
||||
OmadaHelper.debug(msg)
|
||||
return OmadaHelper.response("success", msg, result)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
msg = f"Failed to convert timestamp - unknown timezone: {timezone}"
|
||||
OmadaHelper.verbose(msg)
|
||||
return OmadaHelper.response("error", msg)
|
||||
except Exception as ex:
|
||||
msg = f"Failed to convert timestamp - error: {str(ex)}"
|
||||
OmadaHelper.verbose(msg)
|
||||
return OmadaHelper.response("error", msg)
|
||||
|
||||
@staticmethod
|
||||
def normalize_mac(mac: str) -> Dict[str, Any]:
|
||||
"""Returns a normalized version of MAC address."""
|
||||
try:
|
||||
if not mac or not isinstance(mac, str) or mac is None:
|
||||
raise Exception(f"Value '{mac}' is not a valid MAC address")
|
||||
|
||||
# Replace - with : in a MAC address and make it lowercase
|
||||
result = mac.lower().replace("-", ":")
|
||||
msg = f"Normalized MAC address from {mac} to {result}"
|
||||
OmadaHelper.debug(msg)
|
||||
return OmadaHelper.response("success", msg, result)
|
||||
except Exception as ex:
|
||||
msg = f"Failed to normalize MAC address '{mac}' - error: {str(ex)}"
|
||||
OmadaHelper.verbose(msg)
|
||||
return OmadaHelper.response("error", msg)
|
||||
|
||||
@staticmethod
|
||||
def normalize_data(input_data: list, input_type: str, site_name: str, timezone: str) -> Dict[str, Any]:
|
||||
"""Returns a normalized dictionary of input data (clients, devices)."""
|
||||
try:
|
||||
if not isinstance(input_data, list):
|
||||
raise Exception(f"Expected a list, but got '{type(input_data)}'.")
|
||||
|
||||
OmadaHelper.verbose(f"Starting normalization of {len(input_data)} {input_type}(s) from site: {site_name}")
|
||||
|
||||
# The default return structure for one device/client
|
||||
default_entry = {
|
||||
"mac_address": "",
|
||||
"ip_address": "",
|
||||
"name": "",
|
||||
"last_seen": "",
|
||||
"site_name": site_name,
|
||||
"parent_node_mac_address": "",
|
||||
"parent_node_port": "",
|
||||
"parent_node_ssid": "",
|
||||
"vlan_id": "",
|
||||
}
|
||||
|
||||
result = []
|
||||
|
||||
# Loop through each device/client
|
||||
for data in input_data:
|
||||
|
||||
# Normalize and verify MAC address
|
||||
mac = OmadaHelper.normalize_mac(data.get("mac"))
|
||||
if not isinstance(mac, dict) or mac.get("response_type") != "success":
|
||||
continue
|
||||
mac = mac.get("response_result")
|
||||
|
||||
if not is_mac(mac):
|
||||
OmadaHelper.debug(f"Skipping {input_type}, not a MAC address: {mac}")
|
||||
continue
|
||||
|
||||
# Assigning mandatory return values
|
||||
entry = default_entry.copy()
|
||||
entry["mac_address"] = mac
|
||||
entry["ip_address"] = data.get("ip")
|
||||
entry["name"] = data.get("name")
|
||||
|
||||
# Assign the last datetime the device/client was seen on the network
|
||||
last_seen = OmadaHelper.timestamp_to_datetime(data.get("lastSeen", 0), timezone)
|
||||
entry["last_seen"] = last_seen.get("response_result") if isinstance(last_seen, dict) and last_seen.get("response_type") == "success" else ""
|
||||
|
||||
# Applicable only for DEVICE
|
||||
if input_type == "device":
|
||||
entry["device_type"] = data.get("type")
|
||||
# If it's not a gateway try to assign parent node MAC
|
||||
if data.get("type", "") != "gateway":
|
||||
parent_mac = OmadaHelper.normalize_mac(data.get("uplinkDeviceMac"))
|
||||
entry["parent_node_mac_address"] = parent_mac.get("response_result") if isinstance(parent_mac, dict) and parent_mac.get("response_type") == "success" else ""
|
||||
|
||||
# Applicable only for CLIENT
|
||||
if input_type == "client":
|
||||
entry["vlan_id"] = data.get("vid")
|
||||
entry["device_type"] = data.get("deviceType")
|
||||
# Try to assign parent node MAC and PORT/SSID to the CLIENT
|
||||
if data.get("connectDevType", "") == "gateway":
|
||||
parent_mac = OmadaHelper.normalize_mac(data.get("gatewayMac"))
|
||||
entry["parent_node_mac_address"] = parent_mac.get("response_result") if isinstance(parent_mac, dict) and parent_mac.get("response_type") == "success" else ""
|
||||
entry["parent_node_port"] = data.get("port", "")
|
||||
elif data.get("connectDevType", "") == "switch":
|
||||
parent_mac = OmadaHelper.normalize_mac(data.get("switchMac"))
|
||||
entry["parent_node_mac_address"] = parent_mac.get("response_result") if isinstance(parent_mac, dict) and parent_mac.get("response_type") == "success" else ""
|
||||
entry["parent_node_port"] = data.get("port", "")
|
||||
elif data.get("connectDevType", "") == "ap":
|
||||
parent_mac = OmadaHelper.normalize_mac(data.get("apMac"))
|
||||
entry["parent_node_mac_address"] = parent_mac.get("response_result") if isinstance(parent_mac, dict) and parent_mac.get("response_type") == "success" else ""
|
||||
entry["parent_node_ssid"] = data.get("ssid", "")
|
||||
|
||||
# Add the entry to the result
|
||||
result.append(entry)
|
||||
OmadaHelper.debug(f"Processed {input_type} entry: {entry}")
|
||||
|
||||
msg = f"Successfully normalized {len(result)} {input_type}(s) from site: {site_name}"
|
||||
OmadaHelper.minimal(msg)
|
||||
return OmadaHelper.response("success", msg, result)
|
||||
except Exception as ex:
|
||||
msg = f"Failed normalizing {input_type}(s) from site '{site_name}' - error: {str(ex)}"
|
||||
OmadaHelper.verbose(msg)
|
||||
return OmadaHelper.response("error", msg)
|
||||
|
||||
|
||||
class OmadaAPI:
|
||||
def __init__(self, options: dict):
|
||||
OmadaHelper.debug("Initializing OmadaAPI with provided options")
|
||||
|
||||
# Define parameters: required, optional, and default values
|
||||
params = {
|
||||
"host": {"type": str, "required": True},
|
||||
"omada_id": {"type": str, "required": True},
|
||||
"client_id": {"type": str, "required": True},
|
||||
"client_secret": {"type": str, "required": True},
|
||||
"verify_ssl": {"type": bool, "required": False, "default": True},
|
||||
"page_size": {"type": int, "required": False, "default": 1000},
|
||||
"sites": {"type": list, "required": False, "default": []}
|
||||
}
|
||||
|
||||
# Validate and set attributes
|
||||
for param_name, param_info in params.items():
|
||||
# Get user parameter input, or default value if any
|
||||
value = options.get(param_name, param_info.get("default"))
|
||||
|
||||
# Check if a parameter is required and if it's value is non-empty
|
||||
if param_info["required"] and (value is None or (param_info["type"] == str and not value)):
|
||||
raise ValueError(f"{param_name} is required and must be a non-empty {param_info['type'].__name__}")
|
||||
|
||||
# Check if a parameter has a correct datatype
|
||||
if not isinstance(value, param_info["type"]):
|
||||
raise TypeError(f"{param_name} must be of type {param_info['type'].__name__}")
|
||||
|
||||
# Assign the parameter to the class
|
||||
setattr(self, param_name, value)
|
||||
OmadaHelper.debug(f"Initialized option '{param_name}' with value: {value}")
|
||||
|
||||
# Other parameters
|
||||
self.available_sites_dict = {}
|
||||
self.active_sites_dict = {}
|
||||
self.access_token = None
|
||||
self.refresh_token = None
|
||||
|
||||
OmadaHelper.verbose("OmadaAPI initialized")
|
||||
|
||||
def _get_headers(self, include_auth: bool = True) -> dict:
|
||||
"""Return request headers."""
|
||||
headers = {"Content-type": "application/json"}
|
||||
# Add access token to header if requested and available
|
||||
if include_auth == True:
|
||||
if not self.access_token:
|
||||
OmadaHelper.debug("No access token available for headers")
|
||||
else:
|
||||
headers["Authorization"] = f"AccessToken={self.access_token}"
|
||||
OmadaHelper.debug(f"Generated headers: {headers}")
|
||||
return headers
|
||||
|
||||
def _make_request(self, method: str, endpoint: str, **kwargs: Any) -> Dict[str, Any]:
|
||||
"""Make a request to an endpoint."""
|
||||
time.sleep(1) # Sleep before making any request so it does not rate-limited
|
||||
OmadaHelper.debug(f"{method} request to endpoint: {endpoint}")
|
||||
url = f"{getattr(self, 'host')}{endpoint}"
|
||||
headers = self._get_headers(kwargs.pop('include_auth', True))
|
||||
try:
|
||||
# Make the request and get the response
|
||||
response = requests.request(method, url, headers=headers, verify=getattr(self, 'verify_ssl'), **kwargs)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# Check if the response contains an error code and determine the function response type
|
||||
response_type = "error" if data.get("errorCode", 0) != 0 else "success"
|
||||
msg = f"{method} request completed: {endpoint}"
|
||||
OmadaHelper.verbose(msg)
|
||||
return OmadaHelper.response(response_type, msg, data)
|
||||
except requests.exceptions.RequestException as ex:
|
||||
OmadaHelper.minimal(f"{method} request failed: {url}")
|
||||
OmadaHelper.verbose(f"{method} request error: {str(ex)}")
|
||||
return OmadaHelper.response("error", f"{method} request failed to endpoint '{endpoint}' with error: {str(ex)}")
|
||||
|
||||
def authenticate(self) -> Dict[str, any]:
|
||||
"""Make an endpoint request to get access token."""
|
||||
OmadaHelper.verbose("Starting authentication process")
|
||||
|
||||
# Endpoint request
|
||||
endpoint = "/openapi/authorize/token?grant_type=client_credentials"
|
||||
payload = {
|
||||
"omadacId": getattr(self, 'omada_id'),
|
||||
"client_id": getattr(self, 'client_id'),
|
||||
"client_secret": getattr(self, 'client_secret')
|
||||
}
|
||||
response = self._make_request("POST", endpoint, json=payload, include_auth=False)
|
||||
|
||||
# Successful endpoint response
|
||||
if response.get("response_type") == "success":
|
||||
response_result = response.get("response_result")
|
||||
error_code = response_result.get("errorCode")
|
||||
access_token = response_result.get("result").get("accessToken")
|
||||
refresh_token = response_result.get("result").get("refreshToken")
|
||||
|
||||
# Authentication is successful if there isn't a response error, and access_token and refresh_token are set
|
||||
if error_code == 0 and access_token and refresh_token:
|
||||
self.access_token = access_token
|
||||
self.refresh_token = refresh_token
|
||||
msg = "Successfully authenticated"
|
||||
OmadaHelper.minimal(msg)
|
||||
return OmadaHelper.response("success", msg)
|
||||
|
||||
# Failed authentication
|
||||
OmadaHelper.debug(f"Authentication response: {response}")
|
||||
return OmadaHelper.response("error", f"Authentication failed - error: {response.get('response_message', 'Not provided')}")
|
||||
|
||||
def get_clients(self, site_id: str) -> Dict[str, Any]:
|
||||
"""Make an endpoint request to get all online clients on a site."""
|
||||
OmadaHelper.verbose(f"Retrieving clients for site: {site_id}")
|
||||
endpoint = f"/openapi/v1/{getattr(self, 'omada_id')}/sites/{site_id}/clients?page=1&pageSize={getattr(self, 'page_size')}"
|
||||
return self._make_request("GET", endpoint)
|
||||
|
||||
def get_devices(self, site_id: str) -> Dict[str, Any]:
|
||||
"""Make an endpoint request to get all online devices on a site."""
|
||||
OmadaHelper.verbose(f"Retrieving devices for site: {site_id}")
|
||||
endpoint = f"/openapi/v1/{getattr(self, 'omada_id')}/sites/{site_id}/devices?page=1&pageSize={getattr(self, 'page_size')}"
|
||||
return self._make_request("GET", endpoint)
|
||||
|
||||
def populate_sites(self) -> Dict[str, Any]:
|
||||
"""Make an endpoint request to populate all accessible sites."""
|
||||
OmadaHelper.verbose("Starting site population process")
|
||||
|
||||
# Endpoint request
|
||||
endpoint = f"/openapi/v1/{getattr(self, 'omada_id')}/sites?page=1&pageSize={getattr(self, 'page_size')}"
|
||||
response = self._make_request("GET", endpoint)
|
||||
|
||||
# Successful endpoint response
|
||||
if response.get("response_type") == "success":
|
||||
response_result = response.get("response_result")
|
||||
if response_result.get("errorCode") == 0:
|
||||
# All allowed sites for credentials
|
||||
all_sites = response_result.get("result", "").get("data", [])
|
||||
OmadaHelper.debug(f"Retrieved {len(all_sites)} sites in total")
|
||||
|
||||
# All available sites
|
||||
self.available_sites_dict = {site["siteId"]: site["name"] for site in all_sites}
|
||||
OmadaHelper.debug(f"Available sites: {self.available_sites_dict}")
|
||||
|
||||
# All valid sites from input
|
||||
active_sites_by_id = {site["siteId"]: site["name"] for site in all_sites if site["siteId"] in self.requested_sites()}
|
||||
active_sites_by_name = {site["siteId"]: site["name"] for site in all_sites if site["name"] in self.requested_sites()}
|
||||
self.active_sites_dict = active_sites_by_id | active_sites_by_name
|
||||
OmadaHelper.debug(f"Active sites after filtering: {self.active_sites_dict}")
|
||||
|
||||
# If none of the input sites is valid/accessible, default to the first available site
|
||||
if not self.active_sites_dict:
|
||||
OmadaHelper.verbose("No valid site requested by configuration options, defaulting to first available site")
|
||||
first_available_site = next(iter(self.available_sites_dict.items()), (None, None))
|
||||
if first_available_site[0]: # Check if there's an available site
|
||||
self.active_sites_dict = {first_available_site[0]: first_available_site[1]}
|
||||
OmadaHelper.debug(f"Using first available site: {first_available_site}")
|
||||
|
||||
# Successful site population
|
||||
msg = f"Successfully populated {len(self.active_sites_dict)} site(s)"
|
||||
OmadaHelper.minimal(msg)
|
||||
return OmadaHelper.response("success", msg)
|
||||
|
||||
# Failed site population
|
||||
OmadaHelper.debug(f"Site population response: {response}")
|
||||
return OmadaHelper.response("error", f"Site population failed - error: {response.get('response_message', 'Not provided')}")
|
||||
|
||||
def requested_sites(self) -> list:
|
||||
"""Returns sites requested by user."""
|
||||
return getattr(self, 'sites')
|
||||
|
||||
def available_sites(self) -> dict:
|
||||
"""Returns all available sites."""
|
||||
return self.available_sites_dict
|
||||
|
||||
def active_sites(self) -> dict:
|
||||
"""Returns the sites the code will use."""
|
||||
return self.active_sites_dict
|
||||
|
||||
|
||||
class OmadaData:
|
||||
@staticmethod
|
||||
def create_data(plugin_objects: Plugin_Objects, normalized_input_data: dict) -> None:
|
||||
"""Creates plugin object from normalized input data."""
|
||||
if normalized_input_data.get("response_type", "error") != "success":
|
||||
OmadaHelper.minimal(f"Unable to make entries - error: {normalized_input_data.get('response_message', 'Not provided')}")
|
||||
return
|
||||
|
||||
# Loop through every device/client and make an plugin entry
|
||||
response_result = normalized_input_data.get("response_result", {})
|
||||
for entry in response_result:
|
||||
if len(entry) == 0:
|
||||
OmadaHelper.minimal(f"Skipping entry, missing data.")
|
||||
continue
|
||||
|
||||
OmadaHelper.verbose(f"Making entry for: {entry['mac_address']}")
|
||||
|
||||
# If the device_type is gateway, set the parent_node to Internet
|
||||
device_type = entry["device_type"].lower()
|
||||
parent_node = entry["parent_node_mac_address"]
|
||||
if len(parent_node) == 0 and entry["device_type"] == "gateway" and is_typical_router_ip(entry["ip_address"]):
|
||||
parent_node = "Internet"
|
||||
|
||||
# Some device type naming exceptions
|
||||
if device_type == "iphone":
|
||||
device_type = "iPhone"
|
||||
elif device_type == "pc":
|
||||
device_type = "PC"
|
||||
else:
|
||||
device_type = device_type.capitalize()
|
||||
|
||||
# Add the plugin object
|
||||
plugin_objects.add_object(
|
||||
primaryId=entry["mac_address"],
|
||||
secondaryId=entry["ip_address"],
|
||||
watched1=entry["name"],
|
||||
watched2=parent_node,
|
||||
watched3=entry["parent_node_port"],
|
||||
watched4=entry["parent_node_ssid"],
|
||||
extra=device_type,
|
||||
foreignKey=entry["mac_address"],
|
||||
helpVal1=entry["last_seen"],
|
||||
helpVal2=entry["site_name"],
|
||||
helpVal3=entry["vlan_id"],
|
||||
helpVal4="null"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def collect_data(plugin_objects: Plugin_Objects) -> Plugin_Objects:
|
||||
"""Collects device and client data from Omada Controller."""
|
||||
omada_api = OmadaAPI(OPTIONS)
|
||||
|
||||
# Authenticate
|
||||
auth_result = omada_api.authenticate()
|
||||
if auth_result["response_type"] == "error":
|
||||
OmadaHelper.minimal("Authentication failed, aborting data collection")
|
||||
OmadaHelper.debug(f"{auth_result['response_message']}")
|
||||
return plugin_objects
|
||||
|
||||
# Populate sites
|
||||
sites_result = omada_api.populate_sites()
|
||||
if sites_result["response_type"] == "error":
|
||||
OmadaHelper.minimal("Site population failed, aborting data collection")
|
||||
OmadaHelper.debug(f"{sites_result['response_message']}")
|
||||
return plugin_objects
|
||||
|
||||
requested_sites = omada_api.requested_sites()
|
||||
available_sites = omada_api.available_sites()
|
||||
active_sites = omada_api.active_sites()
|
||||
|
||||
OmadaHelper.verbose(f"Requested sites: {requested_sites}")
|
||||
OmadaHelper.verbose(f"Available sites: {available_sites}")
|
||||
OmadaHelper.verbose(f"Active sites: {active_sites}")
|
||||
|
||||
OmadaHelper.minimal("Starting data collection process")
|
||||
|
||||
# Loop through sites and collect data
|
||||
for site_id, site_name in active_sites.items():
|
||||
OmadaHelper.verbose(f"Processing site: {site_name} ({site_id})")
|
||||
|
||||
# Collect device data
|
||||
devices_response = omada_api.get_devices(site_id)
|
||||
if devices_response["response_type"] != "success":
|
||||
OmadaHelper.minimal(f"Failed to retrieve devices for site: {site_name}")
|
||||
else:
|
||||
devices = devices_response["response_result"].get("result").get("data", [])
|
||||
OmadaHelper.debug(f"Retrieved {len(devices)} device(s) from site: {site_name}")
|
||||
devices = OmadaHelper.normalize_data(devices, "device", site_name, TIMEZONE)
|
||||
OmadaData.create_data(plugin_objects, devices)
|
||||
|
||||
# Collect client data
|
||||
clients_response = omada_api.get_clients(site_id)
|
||||
if clients_response["response_type"] != "success":
|
||||
OmadaHelper.minimal(f"Failed to retrieve clients for site {site_name}")
|
||||
else:
|
||||
clients = clients_response["response_result"].get("result").get("data", [])
|
||||
OmadaHelper.debug(f"Retrieved {len(clients)} client(s) from site: {site_name}")
|
||||
clients = OmadaHelper.normalize_data(clients, "client", site_name, TIMEZONE)
|
||||
OmadaData.create_data(plugin_objects, clients)
|
||||
|
||||
OmadaHelper.verbose(f"Site complete: {site_name} ({site_id})")
|
||||
|
||||
# Complete collection and return plugin object
|
||||
OmadaHelper.minimal("Completed data collection process")
|
||||
return plugin_objects
|
||||
|
||||
|
||||
def main():
|
||||
start_time = time.time()
|
||||
OmadaHelper.minimal(f"Starting execution, version {__version__}")
|
||||
|
||||
# Initialize the Plugin object output file
|
||||
plugin_objects = Plugin_Objects(RESULT_FILE)
|
||||
|
||||
# Retrieve options
|
||||
global OPTIONS, TIMEZONE
|
||||
TIMEZONE = get_setting_value("TIMEZONE")
|
||||
OPTIONS = {
|
||||
"host": get_setting_value(f"{pluginName}_host").strip(),
|
||||
"client_id": get_setting_value(f"{pluginName}_client_id").strip(),
|
||||
"client_secret": get_setting_value(f"{pluginName}_client_secret").strip(),
|
||||
"omada_id": get_setting_value(f"{pluginName}_omada_id").strip(),
|
||||
"sites": get_setting_value(f"{pluginName}_sites"),
|
||||
"verify_ssl": get_setting_value(f"{pluginName}_verify_ssl")
|
||||
}
|
||||
OmadaHelper.verbose("Configuration options loaded")
|
||||
|
||||
# Retrieve entries and write result
|
||||
plugin_objects = OmadaData.collect_data(plugin_objects)
|
||||
plugin_objects.write_result_file()
|
||||
|
||||
# Finish
|
||||
OmadaHelper.minimal(f"Execution completed in {time.time() - start_time:.2f}s, found {len(plugin_objects)} devices and clients")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -633,7 +633,7 @@
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "The host (IP) where the UNIFI controller is runnig. Do NOT include the protocol (e.g. <code>https://</code>)"
|
||||
"string": "The host (IP) where the UNIFI controller is running. Do NOT include the protocol (e.g. <code>https://</code>)"
|
||||
},
|
||||
{
|
||||
"language_code": "es_es",
|
||||
@@ -665,7 +665,7 @@
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "The port number where the UNIFI controller is runnig. Usually it is <code>8443</code>, for UDM(P) devices its 443."
|
||||
"string": "The port number where the UNIFI controller is running. Usually it is <code>8443</code>, for UDM(P) devices its 443."
|
||||
},
|
||||
{
|
||||
"language_code": "es_es",
|
||||
|
||||
20
mkdocs.yml
20
mkdocs.yml
@@ -11,7 +11,7 @@ nav:
|
||||
|
||||
- Installation:
|
||||
- Installation options: INSTALLATION.md
|
||||
- Quick setup: INITIAL_SETUP.md
|
||||
- Quick setup: INITIAL_SETUP.md
|
||||
- Docker:
|
||||
- Docker Guide: https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md
|
||||
- Docker Compose: DOCKER_COMPOSE.md
|
||||
@@ -27,11 +27,12 @@ nav:
|
||||
- Setup:
|
||||
- Getting started:
|
||||
- Subnets: SUBNETS.md
|
||||
- Enable Plugins: https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md
|
||||
- Enable Plugins: PLUGINS.md
|
||||
- Pi-hole Guide: PIHOLE_GUIDE.md
|
||||
- Home Assistant: HOME_ASSISTANT.md
|
||||
- Emails: SMTP.md
|
||||
- Backups: BACKUPS.md
|
||||
- Security: SECURITY.md
|
||||
- Advanced guides:
|
||||
- Remote Networks: REMOTE_NETWORKS.md
|
||||
- Notifications Guide: NOTIFICATIONS.md
|
||||
@@ -42,7 +43,7 @@ nav:
|
||||
- Webhooks (n8n): WEBHOOK_N8N.md
|
||||
- Help:
|
||||
- Common issues: COMMON_ISSUES.md
|
||||
- Random MAC: RANDOM_MAC.md
|
||||
- Random MAC: RANDOM_MAC.md
|
||||
|
||||
- Device guides:
|
||||
- Editing Devices:
|
||||
@@ -70,17 +71,22 @@ nav:
|
||||
- Integrations:
|
||||
- Webhook Secret: WEBHOOK_SECRET.md
|
||||
- API: API.md
|
||||
- Helper scripts: HELPER_SCRIPTS.md
|
||||
|
||||
|
||||
theme:
|
||||
name: material
|
||||
logo: img/NetAlertX_logo.png # Reference the favicon here
|
||||
favicon: img/NetAlertX_logo.png
|
||||
logo: img/netalertx_docs.png # Reference the favicon here
|
||||
favicon: img/netalertx_docs.png
|
||||
custom_dir: docs/overrides
|
||||
metadata:
|
||||
description: "NetAlertX Documentation - The go-to resource for all things related to NetAlertX."
|
||||
image: "https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/front/img/NetAlertX_logo.png"
|
||||
image: "https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/front/img/netalertx_docs.png"
|
||||
extra:
|
||||
home_hide_sidebar: true
|
||||
analytics:
|
||||
provider: google
|
||||
property: G-KCRSGLP8J2
|
||||
social:
|
||||
- icon: fontawesome/brands/github
|
||||
link: https://github.com/jokob-sk
|
||||
@@ -135,4 +141,4 @@ plugins:
|
||||
- gh-admonitions
|
||||
- search
|
||||
|
||||
favicon: /img/NetAlertX_logo.png
|
||||
favicon: /img/netalertx_docs.png
|
||||
|
||||
78
scripts/opnsense_leases/README.md
Executable file
78
scripts/opnsense_leases/README.md
Executable file
@@ -0,0 +1,78 @@
|
||||
# NetAlertX OPNsense DHCP Lease Converter
|
||||
|
||||
## Overview
|
||||
|
||||
This script retrieves DHCP lease data from an OPNsense firewall over SSH and converts it into the `dnsmasq` lease file format. You can combine it with the `DHCPLLSS` plugin to ingest devices from OPNsense.
|
||||
|
||||
## Features
|
||||
- Connects to OPNsense via SSH to retrieve DHCP lease data.
|
||||
- Parses active DHCP leases.
|
||||
- Converts lease data to `dnsmasq` lease format.
|
||||
- Saves the converted lease file to a specified output location.
|
||||
- Supports password and key-based SSH authentication.
|
||||
- Includes a debug mode for troubleshooting.
|
||||
|
||||
## Requirements
|
||||
- Python 3
|
||||
- `paramiko` library (for SSH connection)
|
||||
- An OPNsense firewall with SSH access enabled
|
||||
|
||||
## Usage
|
||||
Run the script with the required parameters:
|
||||
|
||||
```sh
|
||||
./script.py --host <OPNsense_IP> --username <SSH_User> --output <Output_File>
|
||||
```
|
||||
|
||||
### Available Options
|
||||
|
||||
| Option | Description |
|
||||
|--------------|-------------|
|
||||
| `--host` | OPNsense hostname or IP address (Required) |
|
||||
| `--username` | SSH username (Required) |
|
||||
| `--password` | SSH password (Optional if using key-based authentication) |
|
||||
| `--key-file` | Path to SSH private key file (Optional) |
|
||||
| `--port` | SSH port (Default: 22) |
|
||||
| `--output` | Output file path for converted lease file (Required) |
|
||||
| `--debug` | Enable debug logging (Optional) |
|
||||
|
||||
### Example Commands
|
||||
|
||||
#### Using Password Authentication
|
||||
```sh
|
||||
./script.py --host 192.168.1.1 --username admin --password mypassword --output /tmp/dnsmasq.leases
|
||||
```
|
||||
|
||||
#### Using SSH Key Authentication
|
||||
```sh
|
||||
./script.py --host 192.168.1.1 --username admin --key-file ~/.ssh/id_rsa --output /tmp/dnsmasq.leases
|
||||
```
|
||||
|
||||
## Output Format
|
||||
The script generates a `dnsmasq`-formatted lease file with lines structured as:
|
||||
|
||||
```
|
||||
[epoch timestamp] [MAC address] [IP address] [hostname] [client ID]
|
||||
```
|
||||
|
||||
Example:
|
||||
```sh
|
||||
1708212000 00:11:22:33:44:55 192.168.1.100 my-device 01:00:11:22:33:44:55
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Connection issues?** Ensure SSH is enabled on the OPNsense device and the correct credentials are used.
|
||||
- **No lease data?** Verify the DHCP lease file exists at `/var/dhcpd/var/db/dhcpd.leases`.
|
||||
- **Permission denied?** Ensure your SSH user has the required permissions to access the lease file.
|
||||
- **Debugging:** Run the script with the `--debug` flag to see more details.
|
||||
|
||||
|
||||
### Other info
|
||||
|
||||
- Version: 1.0
|
||||
- Author: [im-redactd](https://github.com/im-redactd)
|
||||
- Release Date: 24-Feb-2025
|
||||
|
||||
> [!NOTE]
|
||||
> This is a community supplied script and not maintained.
|
||||
259
scripts/opnsense_leases/opnsense_leases.py
Executable file
259
scripts/opnsense_leases/opnsense_leases.py
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python3
|
||||
import paramiko
|
||||
import re
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import time
|
||||
import logging
|
||||
|
||||
def setup_logging(debug=False):
|
||||
"""Configure logging based on debug flag."""
|
||||
level = logging.DEBUG if debug else logging.INFO
|
||||
logging.basicConfig(
|
||||
level=level,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
return logging.getLogger(__name__)
|
||||
|
||||
def parse_timestamp(date_str):
|
||||
"""Convert OPNsense timestamp to Unix epoch time."""
|
||||
try:
|
||||
# Format from OPNsense: "1 2025/02/17 20:08:29"
|
||||
# Remove the leading number and convert
|
||||
clean_date = ' '.join(date_str.split()[1:])
|
||||
dt = datetime.strptime(clean_date, '%Y/%m/%d %H:%M:%S')
|
||||
return int(dt.timestamp())
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to parse timestamp: {date_str}")
|
||||
return None
|
||||
|
||||
|
||||
def get_lease_file(hostname, username, password=None, key_filename=None, port=22, debug=False):
|
||||
"""Retrieve the lease file content from OPNsense via SSH."""
|
||||
logger = logging.getLogger(__name__)
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
try:
|
||||
logger.debug(f"Attempting to connect to {hostname}:{port} as {username}")
|
||||
ssh.connect(hostname, port=port, username=username,
|
||||
password=password, key_filename=key_filename)
|
||||
|
||||
# Get an interactive shell session
|
||||
logger.debug("Opening interactive SSH channel")
|
||||
channel = ssh.invoke_shell()
|
||||
time.sleep(2) # Wait for the menu to load
|
||||
|
||||
if debug:
|
||||
# Read and log the initial menu
|
||||
while channel.recv_ready():
|
||||
initial_output = channel.recv(4096).decode('utf-8')
|
||||
logger.debug(f"Initial menu output:\n{initial_output}")
|
||||
|
||||
# Send '8' to access the shell
|
||||
logger.debug("Sending option 8 to access shell")
|
||||
channel.send('8\n')
|
||||
time.sleep(2) # Wait for shell access
|
||||
|
||||
# Send the command to read the lease file
|
||||
command = 'cat /var/dhcpd/var/db/dhcpd.leases\n'
|
||||
logger.debug(f"Sending command: {command}")
|
||||
channel.send(command)
|
||||
time.sleep(2) # Wait for command execution
|
||||
|
||||
# Receive the output
|
||||
output = ""
|
||||
while channel.recv_ready():
|
||||
chunk = channel.recv(4096).decode('utf-8')
|
||||
output += chunk
|
||||
if debug:
|
||||
logger.debug(f"Received chunk:\n{chunk}")
|
||||
|
||||
# Clean up the output by removing the command echo and shell prompts
|
||||
lines = output.split('\n')
|
||||
# Remove first line (command echo) and any lines containing shell prompts
|
||||
cleaned_lines = [line for line in lines
|
||||
if not line.strip().startswith(command.strip())
|
||||
and not line.strip().endswith('> ')
|
||||
and not line.strip().endswith('# ')]
|
||||
cleaned_output = '\n'.join(cleaned_lines)
|
||||
|
||||
logger.debug(f"Final cleaned output length: {len(cleaned_output)} characters")
|
||||
|
||||
# Exit the shell properly
|
||||
channel.send('exit\n')
|
||||
ssh.close()
|
||||
|
||||
return cleaned_output
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error during SSH operation: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
def parse_lease_file(lease_content):
|
||||
"""Parse the DHCP lease file content and return a list of valid leases."""
|
||||
logger = logging.getLogger(__name__)
|
||||
leases = []
|
||||
current_lease = None
|
||||
|
||||
for line in lease_content.split('\n'):
|
||||
line = line.strip()
|
||||
|
||||
if not line or line.startswith('root@') or line.startswith('#'):
|
||||
continue
|
||||
|
||||
logger.debug(f"Processing line: {line}")
|
||||
|
||||
# Start of a lease block
|
||||
if line.startswith('lease'):
|
||||
if current_lease:
|
||||
leases.append(current_lease)
|
||||
logger.debug(f"Added lease: {current_lease}")
|
||||
current_lease = {}
|
||||
ip = line.split()[1]
|
||||
current_lease['ip'] = ip
|
||||
|
||||
# MAC address
|
||||
elif 'hardware ethernet' in line:
|
||||
mac = line.split()[2].rstrip(';')
|
||||
current_lease['mac'] = mac
|
||||
|
||||
# Hostname
|
||||
elif 'client-hostname' in line:
|
||||
hostname = line.split('"')[1] if '"' in line else line.split()[1].rstrip(';')
|
||||
current_lease['hostname'] = hostname
|
||||
|
||||
# Lease state
|
||||
elif line.startswith('binding state '):
|
||||
state = line.split('binding state')[1].strip().rstrip(';')
|
||||
current_lease['state'] = state
|
||||
|
||||
# End time
|
||||
elif line.startswith('ends'):
|
||||
date_str = ' '.join(line.split()[1:]).rstrip(';')
|
||||
current_lease['ends'] = date_str
|
||||
|
||||
# Client ID
|
||||
elif line.startswith('uid'):
|
||||
uid = line.split('"')[1] if '"' in line else line.split()[1].rstrip(';')
|
||||
current_lease['uid'] = uid
|
||||
|
||||
# End of lease block
|
||||
elif line.strip() == '}':
|
||||
if current_lease:
|
||||
leases.append(current_lease)
|
||||
logger.debug(f"Added lease at block end: {current_lease}")
|
||||
current_lease = None
|
||||
|
||||
# Add the last lease if exists
|
||||
if current_lease:
|
||||
leases.append(current_lease)
|
||||
logger.debug(f"Added final lease: {current_lease}")
|
||||
|
||||
# Filter only active leases
|
||||
active_leases = [lease for lease in leases
|
||||
if lease.get('state') == 'active'
|
||||
and 'mac' in lease
|
||||
and 'ip' in lease]
|
||||
|
||||
logger.debug(f"Found {len(active_leases)} active leases out of {len(leases)} total leases")
|
||||
logger.debug("Active leases:")
|
||||
for lease in active_leases:
|
||||
logger.debug(f" {lease}")
|
||||
|
||||
return active_leases
|
||||
|
||||
|
||||
def convert_to_dnsmasq(leases):
|
||||
"""Convert leases to dnsmasq lease file format."""
|
||||
logger = logging.getLogger(__name__)
|
||||
dnsmasq_lines = []
|
||||
|
||||
for lease in leases:
|
||||
logger.debug(f"Converting lease: {lease}")
|
||||
if 'mac' in lease and 'ip' in lease:
|
||||
# Get expiry time as Unix timestamp
|
||||
expiry = lease.get('ends', '')
|
||||
if expiry:
|
||||
expiry_epoch = parse_timestamp(expiry)
|
||||
if not expiry_epoch:
|
||||
logger.error(f"Skipping lease due to invalid timestamp: {lease}")
|
||||
continue
|
||||
else:
|
||||
logger.error(f"Skipping lease due to missing expiry time: {lease}")
|
||||
continue
|
||||
|
||||
# Get required fields
|
||||
mac = lease['mac']
|
||||
ip = lease['ip']
|
||||
hostname = lease.get('hostname', '*')
|
||||
|
||||
# Format client ID - if not available, use MAC address with '01:' prefix
|
||||
client_id = lease.get('uid', f"01:{mac}")
|
||||
# Clean up client ID - remove escape sequences and quotes
|
||||
client_id = client_id.replace('\\', '').replace('"', '')
|
||||
if not client_id.startswith('01:'):
|
||||
client_id = f"01:{mac}"
|
||||
|
||||
# Format: [epoch timestamp] [MAC address] [IP address] [hostname] [client ID]
|
||||
line = f"{expiry_epoch} {mac} {ip} {hostname} {client_id}"
|
||||
dnsmasq_lines.append(line)
|
||||
logger.debug(f"Added dnsmasq lease line: {line}")
|
||||
|
||||
return dnsmasq_lines
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Convert OPNsense DHCP leases to dnsmasq format')
|
||||
parser.add_argument('--host', required=True, help='OPNsense hostname or IP')
|
||||
parser.add_argument('--username', required=True, help='SSH username')
|
||||
parser.add_argument('--password', help='SSH password (if not using key-based auth)')
|
||||
parser.add_argument('--key-file', help='SSH private key file path')
|
||||
parser.add_argument('--port', type=int, default=22, help='SSH port (default: 22)')
|
||||
parser.add_argument('--output', required=True, help='Output file path')
|
||||
parser.add_argument('--debug', action='store_true', help='Enable debug logging')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Setup logging
|
||||
logger = setup_logging(args.debug)
|
||||
|
||||
try:
|
||||
# Get lease file content
|
||||
logger.info("Retrieving lease file from OPNsense")
|
||||
lease_content = get_lease_file(
|
||||
args.host,
|
||||
args.username,
|
||||
password=args.password,
|
||||
key_filename=args.key_file,
|
||||
port=args.port,
|
||||
debug=args.debug
|
||||
)
|
||||
|
||||
# Parse leases
|
||||
logger.info("Parsing lease file content")
|
||||
leases = parse_lease_file(lease_content)
|
||||
|
||||
# Convert to dnsmasq format
|
||||
logger.info("Converting to dnsmasq format")
|
||||
dnsmasq_lines = convert_to_dnsmasq(leases)
|
||||
|
||||
# Write output file
|
||||
output_path = Path(args.output)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
logger.info(f"Writing output to {args.output}")
|
||||
with open(output_path, 'w') as f:
|
||||
f.write('\n'.join(dnsmasq_lines) + '\n')
|
||||
|
||||
logger.info(f"Successfully wrote {len(dnsmasq_lines)} entries to {args.output}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -624,7 +624,7 @@ def cleanDeviceName(str, match_IP):
|
||||
str = re.sub(rgx, "", str)
|
||||
mylog('trace', ["[cleanDeviceName] name after regex : " + str])
|
||||
|
||||
str = re.sub(r'\.\b', '', str) # trailing dot after words
|
||||
# str = re.sub(r'\.\b', '', str) # trailing dot after words
|
||||
str = re.sub(r'\.$', '', str) # trailing dot at the end of the string
|
||||
str = str.replace(". (IP match)", " (IP match)") # Remove dot if (IP match) is added
|
||||
|
||||
|
||||
Reference in New Issue
Block a user