Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/skills/code-standards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ description: NetAlertX coding standards and conventions. Use this when writing c
- code has to be maintainable, no duplicate code
- follow DRY principle - maintainability of code is more important than speed of implementation
- code files should be less than 500 LOC for better maintainability
- DB columns must not contain underscores, use camelCase instead (e.g., deviceInstanceId, not device_instance_id)

## File Length

Expand Down
36 changes: 18 additions & 18 deletions docs/API_EVENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ The Events API provides access to **device event logs**, allowing creation, retr
"success": true,
"events": [
{
"eve_MAC": "00:11:22:33:44:55",
"eve_IP": "192.168.1.10",
"eve_DateTime": "2025-08-24T12:00:00Z",
"eve_EventType": "Device Down",
"eve_AdditionalInfo": "",
"eve_PendingAlertEmail": 1
"eveMac": "00:11:22:33:44:55",
"eveIp": "192.168.1.10",
"eveDateTime": "2025-08-24T12:00:00Z",
"eveEventType": "Device Down",
"eveAdditionalInfo": "",
"evePendingAlertEmail": 1
}
]
}
Expand Down Expand Up @@ -102,11 +102,11 @@ The Events API provides access to **device event logs**, allowing creation, retr
"count": 5,
"events": [
{
"eve_DateTime": "2025-12-07 12:00:00",
"eve_EventType": "New Device",
"eve_MAC": "AA:BB:CC:DD:EE:FF",
"eve_IP": "192.168.1.100",
"eve_AdditionalInfo": "Device detected"
"eveDateTime": "2025-12-07 12:00:00",
"eveEventType": "New Device",
"eveMac": "AA:BB:CC:DD:EE:FF",
"eveIp": "192.168.1.100",
"eveAdditionalInfo": "Device detected"
}
]
}
Expand All @@ -127,9 +127,9 @@ The Events API provides access to **device event logs**, allowing creation, retr
"count": 10,
"events": [
{
"eve_DateTime": "2025-12-07 12:00:00",
"eve_EventType": "Device Down",
"eve_MAC": "AA:BB:CC:DD:EE:FF"
"eveDateTime": "2025-12-07 12:00:00",
"eveEventType": "Device Down",
"eveMac": "AA:BB:CC:DD:EE:FF"
}
]
}
Expand Down Expand Up @@ -159,9 +159,9 @@ The Events API provides access to **device event logs**, allowing creation, retr
1. Total events in the period
2. Total sessions
3. Missing sessions
4. Voided events (`eve_EventType LIKE 'VOIDED%'`)
5. New device events (`eve_EventType LIKE 'New Device'`)
6. Device down events (`eve_EventType LIKE 'Device Down'`)
4. Voided events (`eveEventType LIKE 'VOIDED%'`)
5. New device events (`eveEventType LIKE 'New Device'`)
6. Device down events (`eveEventType LIKE 'Device Down'`)

---

Expand All @@ -187,7 +187,7 @@ Event endpoints are available as **MCP Tools** for AI assistant integration:
```

* Events are stored in the **Events table** with the following fields:
`eve_MAC`, `eve_IP`, `eve_DateTime`, `eve_EventType`, `eve_AdditionalInfo`, `eve_PendingAlertEmail`.
`eveMac`, `eveIp`, `eveDateTime`, `eveEventType`, `eveAdditionalInfo`, `evePendingAlertEmail`.

* Event creation automatically logs activity for debugging.

Expand Down
24 changes: 12 additions & 12 deletions docs/API_SESSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ curl -X DELETE "http://<server_ip>:<GRAPHQL_PORT>/sessions/delete" \
"success": true,
"sessions": [
{
"ses_MAC": "AA:BB:CC:DD:EE:FF",
"ses_Connection": "2025-08-01 10:00",
"ses_Disconnection": "2025-08-01 12:00",
"ses_Duration": "2h 0m",
"ses_IP": "192.168.1.10",
"ses_Info": ""
"sesMac": "AA:BB:CC:DD:EE:FF",
"sesDateTimeConnection": "2025-08-01 10:00",
"sesDateTimeDisconnection": "2025-08-01 12:00",
"sesDuration": "2h 0m",
"sesIp": "192.168.1.10",
"sesAdditionalInfo": ""
}
]
}
Expand Down Expand Up @@ -194,12 +194,12 @@ curl -X GET "http://<server_ip>:<GRAPHQL_PORT>/sessions/calendar?start=2025-08-0
"success": true,
"sessions": [
{
"ses_MAC": "AA:BB:CC:DD:EE:FF",
"ses_Connection": "2025-08-01 10:00",
"ses_Disconnection": "2025-08-01 12:00",
"ses_Duration": "2h 0m",
"ses_IP": "192.168.1.10",
"ses_Info": ""
"sesMac": "AA:BB:CC:DD:EE:FF",
"sesDateTimeConnection": "2025-08-01 10:00",
"sesDateTimeDisconnection": "2025-08-01 12:00",
"sesDuration": "2h 0m",
"sesIp": "192.168.1.10",
"sesAdditionalInfo": ""
}
]
}
Expand Down
4 changes: 2 additions & 2 deletions docs/DEBUG_PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Input data from the plugin might cause mapping issues in specific edge cases. Lo
17:31:05 [Scheduler] run for PIHOLE: YES
17:31:05 [Plugin utils] ---------------------------------------------
17:31:05 [Plugin utils] display_name: PiHole (Device sync)
17:31:05 [Plugins] CMD: SELECT n.hwaddr AS Object_PrimaryID, {s-quote}null{s-quote} AS Object_SecondaryID, datetime() AS DateTime, na.ip AS Watched_Value1, n.lastQuery AS Watched_Value2, na.name AS Watched_Value3, n.macVendor AS Watched_Value4, {s-quote}null{s-quote} AS Extra, n.hwaddr AS ForeignKey FROM EXTERNAL_PIHOLE.Network AS n LEFT JOIN EXTERNAL_PIHOLE.Network_Addresses AS na ON na.network_id = n.id WHERE n.hwaddr NOT LIKE {s-quote}ip-%{s-quote} AND n.hwaddr is not {s-quote}00:00:00:00:00:00{s-quote} AND na.ip is not null
17:31:05 [Plugins] CMD: SELECT n.hwaddr AS objectPrimaryId, {s-quote}null{s-quote} AS objectSecondaryId, datetime() AS DateTime, na.ip AS watchedValue1, n.lastQuery AS watchedValue2, na.name AS watchedValue3, n.macVendor AS watchedValue4, {s-quote}null{s-quote} AS Extra, n.hwaddr AS ForeignKey FROM EXTERNAL_PIHOLE.Network AS n LEFT JOIN EXTERNAL_PIHOLE.Network_Addresses AS na ON na.network_id = n.id WHERE n.hwaddr NOT LIKE {s-quote}ip-%{s-quote} AND n.hwaddr is not {s-quote}00:00:00:00:00:00{s-quote} AND na.ip is not null
17:31:05 [Plugins] setTyp: subnets
17:31:05 [Plugin utils] Flattening the below array
17:31:05 ['192.168.1.0/24 --interface=eth1']
Expand All @@ -52,7 +52,7 @@ Input data from the plugin might cause mapping issues in specific edge cases. Lo
17:31:05 [Plugins] Convert to Base64: True
17:31:05 [Plugins] base64 value: b'MTkyLjE2OC4xLjAvMjQgLS1pbnRlcmZhY2U9ZXRoMQ=='
17:31:05 [Plugins] Timeout: 10
17:31:05 [Plugins] Executing: SELECT n.hwaddr AS Object_PrimaryID, 'null' AS Object_SecondaryID, datetime() AS DateTime, na.ip AS Watched_Value1, n.lastQuery AS Watched_Value2, na.name AS Watched_Value3, n.macVendor AS Watched_Value4, 'null' AS Extra, n.hwaddr AS ForeignKey FROM EXTERNAL_PIHOLE.Network AS n LEFT JOIN EXTERNAL_PIHOLE.Network_Addresses AS na ON na.network_id = n.id WHERE n.hwaddr NOT LIKE 'ip-%' AND n.hwaddr is not '00:00:00:00:00:00' AND na.ip is not null
17:31:05 [Plugins] Executing: SELECT n.hwaddr AS objectPrimaryId, 'null' AS objectSecondaryId, datetime() AS DateTime, na.ip AS watchedValue1, n.lastQuery AS watchedValue2, na.name AS watchedValue3, n.macVendor AS watchedValue4, 'null' AS Extra, n.hwaddr AS ForeignKey FROM EXTERNAL_PIHOLE.Network AS n LEFT JOIN EXTERNAL_PIHOLE.Network_Addresses AS na ON na.network_id = n.id WHERE n.hwaddr NOT LIKE 'ip-%' AND n.hwaddr is not '00:00:00:00:00:00' AND na.ip is not null
🔻
17:31:05 [Plugins] SUCCESS, received 2 entries
17:31:05 [Plugins] sqlParam entries: [(0, 'PIHOLE', '01:01:01:01:01:01', 'null', 'null', '2023-12-25 06:31:05', '172.30.0.1', 0, 'aaaa', 'vvvvvvvvv', 'not-processed', 'null', 'null', '01:01:01:01:01:01'), (0, 'PIHOLE', '02:42:ac:1e:00:02', 'null', 'null', '2023-12-25 06:31:05', '172.30.0.2', 0, 'dddd', 'vvvvv2222', 'not-processed', 'null', 'null', '02:42:ac:1e:00:02')]
Expand Down
2 changes: 1 addition & 1 deletion docs/NOTIFICATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The following device properties influence notifications. You can:
On almost all plugins there are 2 core settings, `<plugin>_WATCH` and `<plugin>_REPORT_ON`.

1. `<plugin>_WATCH` specifies the columns which the app should watch. If watched columns change the device state is considered changed. This changed status is then used to decide to send out notifications based on the `<plugin>_REPORT_ON` setting.
2. `<plugin>_REPORT_ON` let's you specify on which events the app should notify you. This is related to the `<plugin>_WATCH` setting. So if you select `watched-changed` and in `<plugin>_WATCH` you only select `Watched_Value1`, then a notification is triggered if `Watched_Value1` is changed from the previous value, but no notification is send if `Watched_Value2` changes.
2. `<plugin>_REPORT_ON` let's you specify on which events the app should notify you. This is related to the `<plugin>_WATCH` setting. So if you select `watched-changed` and in `<plugin>_WATCH` you only select `watchedValue1`, then a notification is triggered if `watchedValue1` is changed from the previous value, but no notification is send if `watchedValue2` changes.

Click the **Read more in the docs.** Link at the top of each plugin to get more details on how the given plugin works.

Expand Down
76 changes: 33 additions & 43 deletions docs/NOTIFICATION_TEMPLATES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@ HTML email tables are **not affected** by these templates.

1. Go to **Settings → Notification Processing**.
2. Set a template string for the section you want to customize, e.g.:
- **Text Template: New Devices** → `{Device name} ({MAC}) - {IP}`
- **Text Template: New Devices** → `{devName} ({eveMac}) - {eveIp}`
3. Save. The next notification will use your format.

**Before (default):**
```
🆕 New devices
---------
MAC: aa:bb:cc:dd:ee:ff
Datetime: 2025-01-15 10:30:00
IP: 192.168.1.42
Event Type: New Device
Device name: MyPhone
Comments:
devName: MyPhone
eveMac: aa:bb:cc:dd:ee:ff
devVendor: Apple
eveIp: 192.168.1.42
eveDateTime: 2025-01-15 10:30:00
eveEventType: New Device
devComments:
```

**After (with template `{Device name} ({MAC}) - {IP}`):**
**After (with template `{devName} ({eveMac}) - {eveIp}`):**
```
🆕 New devices
---------
Expand All @@ -50,60 +51,49 @@ When a template is **empty**, the section uses the original vertical `Header: Va
Use `{FieldName}` to insert a value from the notification data. Field names are **case-sensitive** and must match the column names exactly.

```
{Device name} ({MAC}) connected at {Datetime}
{devName} ({eveMac}) connected at {eveDateTime}
```

- No loops, conditionals, or nesting — just simple string replacement.
- If a `{FieldName}` does not exist in the data, it is left as-is in the output (safe failure). For example, `{NonExistent}` renders literally as `{NonExistent}`.

## Variable Availability by Section

Each section has different available fields because they come from different database queries.
All four device sections (`new_devices`, `down_devices`, `down_reconnected`, `events`) share the same unified field names.

### `new_devices` and `events`

| Variable | Description |
|----------|-------------|
| `{MAC}` | Device MAC address |
| `{Datetime}` | Event timestamp |
| `{IP}` | Device IP address |
| `{Event Type}` | Type of event (e.g. `New Device`, `Connected`) |
| `{Device name}` | Device display name |
| `{Comments}` | Device comments |

**Example:** `{Device name} ({MAC}) - {IP} [{Event Type}]`

### `down_devices` and `down_reconnected`
### `new_devices`, `down_devices`, `down_reconnected`, and `events`

| Variable | Description |
|----------|-------------|
| `{devName}` | Device display name |
| `{eve_MAC}` | Device MAC address |
| `{eveMac}` | Device MAC address |
| `{devVendor}` | Device vendor/manufacturer |
| `{eve_IP}` | Device IP address |
| `{eve_DateTime}` | Event timestamp |
| `{eve_EventType}` | Type of event |
| `{eveIp}` | Device IP address |
| `{eveDateTime}` | Event timestamp |
| `{eveEventType}` | Type of event (e.g. `New Device`, `Connected`, `Device Down`) |
| `{devComments}` | Device comments |

**Example (new_devices/events):** `{devName} ({eveMac}) - {eveIp} [{eveEventType}]`

**Example (down_devices):** `{devName} ({eveMac}) {devVendor} - went down at {eveDateTime}`

**Example:** `{devName} ({eve_MAC}) {devVendor} - went down at {eve_DateTime}`
**Example (down_reconnected):** `{devName} ({eveMac}) reconnected at {eveDateTime}`

### `plugins`

| Variable | Description |
|----------|-------------|
| `{Plugin}` | Plugin code name |
| `{Object_PrimaryId}` | Primary identifier of the object |
| `{Object_SecondaryId}` | Secondary identifier |
| `{DateTimeChanged}` | Timestamp of change |
| `{Watched_Value1}` | First watched value |
| `{Watched_Value2}` | Second watched value |
| `{Watched_Value3}` | Third watched value |
| `{Watched_Value4}` | Fourth watched value |
| `{Status}` | Plugin event status |

**Example:** `{Plugin}: {Object_PrimaryId} - {Status}`

> [!NOTE]
> Field names differ between sections because they come from different SQL queries. A template configured for `new_devices` cannot use `{devName}` — that field is only available in `down_devices` and `down_reconnected`.
| `{plugin}` | Plugin code name |
| `{objectPrimaryId}` | Primary identifier of the object |
| `{objectSecondaryId}` | Secondary identifier |
| `{dateTimeChanged}` | Timestamp of change |
| `{watchedValue1}` | First watched value |
| `{watchedValue2}` | Second watched value |
| `{watchedValue3}` | Third watched value |
| `{watchedValue4}` | Fourth watched value |
| `{status}` | Plugin event status |

**Example:** `{plugin}: {objectPrimaryId} - {status}`

## Section Headers Toggle

Expand Down
18 changes: 9 additions & 9 deletions docs/PLUGINS_DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,13 @@ Quick reference:

| Column | Name | Required | Example |
|--------|------|----------|---------|
| 0 | Object_PrimaryID | **YES** | `"device_name"` or `"192.168.1.1"` |
| 1 | Object_SecondaryID | no | `"secondary_id"` or `null` |
| 0 | objectPrimaryId | **YES** | `"device_name"` or `"192.168.1.1"` |
| 1 | objectSecondaryId | no | `"secondary_id"` or `null` |
| 2 | DateTime | **YES** | `"2023-01-02 15:56:30"` |
| 3 | Watched_Value1 | **YES** | `"online"` or `"200"` |
| 4 | Watched_Value2 | no | `"ip_address"` or `null` |
| 5 | Watched_Value3 | no | `null` |
| 6 | Watched_Value4 | no | `null` |
| 3 | watchedValue1 | **YES** | `"online"` or `"200"` |
| 4 | watchedValue2 | no | `"ip_address"` or `null` |
| 5 | watchedValue3 | no | `null` |
| 6 | watchedValue4 | no | `null` |
| 7 | Extra | no | `"additional data"` or `null` |
| 8 | ForeignKey | no | `"aa:bb:cc:dd:ee:ff"` or `null` |

Expand Down Expand Up @@ -243,7 +243,7 @@ Control which rows display in the UI:
{
"data_filters": [
{
"compare_column": "Object_PrimaryID",
"compare_column": "objectPrimaryId",
"compare_operator": "==",
"compare_field_id": "txtMacFilter",
"compare_js_template": "'{value}'.toString()",
Expand All @@ -267,7 +267,7 @@ To import plugin data into NetAlertX tables for device discovery or notification
"mapped_to_table": "CurrentScan",
"database_column_definitions": [
{
"column": "Object_PrimaryID",
"column": "objectPrimaryId",
"mapped_to_column": "scanMac",
"show": true,
"type": "device_mac",
Expand Down Expand Up @@ -345,7 +345,7 @@ See [PLUGINS_DEV_SETTINGS.md](PLUGINS_DEV_SETTINGS.md) for complete settings doc

### Plugin Output Format
```
Object_PrimaryID|Object_SecondaryID|DateTime|Watched_Value1|Watched_Value2|Watched_Value3|Watched_Value4|Extra|ForeignKey
objectPrimaryId|objectSecondaryId|DateTime|watchedValue1|watchedValue2|watchedValue3|watchedValue4|Extra|ForeignKey
```
9 required columns, 4 optional helpers = 13 max

Expand Down
8 changes: 4 additions & 4 deletions docs/PLUGINS_DEV_CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ It also describes plugin output expectations and the main plugin categories.
* `database_column_definitions`
* `mapped_to_table`

**Example:** `Object_PrimaryID → devMAC`
**Example:** `objectPrimaryId → devMAC`

---

Expand All @@ -88,9 +88,9 @@ Output values are pipe-delimited in a fixed order.

#### Identifiers

* `Object_PrimaryID` and `Object_SecondaryID` uniquely identify records (for example, `MAC|IP`).
* `objectPrimaryId` and `objectSecondaryId` uniquely identify records (for example, `MAC|IP`).

#### Watched Values (`Watched_Value1–4`)
#### Watched Values (`watchedValue1–4`)

* Used by the core to detect changes between runs.
* Changes in these fields can trigger notifications.
Expand All @@ -114,7 +114,7 @@ Output values are pipe-delimited in a fixed order.
### 7. Persistence

* Parsed data is **upserted** into the database.
* Conflicts are resolved using the combined key: `Object_PrimaryID + Object_SecondaryID`.
* Conflicts are resolved using the combined key: `objectPrimaryId + objectSecondaryId`.

---

Expand Down
16 changes: 8 additions & 8 deletions docs/PLUGINS_DEV_DATASOURCES.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Query the NetAlertX SQLite database and display results.
{
"function": "CMD",
"type": {"dataType": "string", "elements": [{"elementType": "input", "elementOptions": [], "transformers": []}]},
"default_value": "SELECT dv.devName as Object_PrimaryID, cast(dv.devLastIP as VARCHAR(100)) || ':' || cast(SUBSTR(ns.Port, 0, INSTR(ns.Port, '/')) as VARCHAR(100)) as Object_SecondaryID, datetime() as DateTime, ns.Service as Watched_Value1, ns.State as Watched_Value2, null as Watched_Value3, null as Watched_Value4, ns.Extra as Extra, dv.devMac as ForeignKey FROM (SELECT * FROM Nmap_Scan) ns LEFT JOIN (SELECT devName, devMac, devLastIP FROM Devices) dv ON ns.MAC = dv.devMac",
"default_value": "SELECT dv.devName as objectPrimaryId, cast(dv.devLastIP as VARCHAR(100)) || ':' || cast(SUBSTR(ns.Port, 0, INSTR(ns.Port, '/')) as VARCHAR(100)) as objectSecondaryId, datetime() as DateTime, ns.Service as watchedValue1, ns.State as watchedValue2, null as watchedValue3, null as watchedValue4, ns.Extra as Extra, dv.devMac as ForeignKey FROM (SELECT * FROM Nmap_Scan) ns LEFT JOIN (SELECT devName, devMac, devLastIP FROM Devices) dv ON ns.MAC = dv.devMac",
"localized": ["name"],
"name": [{"language_code": "en_us", "string": "SQL to run"}],
"description": [{"language_code": "en_us", "string": "This SQL query populates the plugin table"}]
Expand All @@ -118,13 +118,13 @@ Query the NetAlertX SQLite database and display results.

```sql
SELECT
e.EventValue as Object_PrimaryID,
d.devName as Object_SecondaryID,
e.EventValue as objectPrimaryId,
d.devName as objectSecondaryId,
e.EventDateTime as DateTime,
e.EventType as Watched_Value1,
d.devLastIP as Watched_Value2,
null as Watched_Value3,
null as Watched_Value4,
e.EventType as watchedValue1,
d.devLastIP as watchedValue2,
null as watchedValue3,
null as watchedValue4,
e.EventDetails as Extra,
d.devMac as ForeignKey
FROM
Expand Down Expand Up @@ -181,7 +181,7 @@ Then set data source and query:
```json
{
"function": "CMD",
"default_value": "SELECT hwaddr as Object_PrimaryID, cast('http://' || (SELECT ip FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC LIMIT 1) as VARCHAR(100)) || ':' || cast(SUBSTR((SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC LIMIT 1), 0, INSTR((SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC LIMIT 1), '/')) as VARCHAR(100)) as Object_SecondaryID, datetime() as DateTime, macVendor as Watched_Value1, lastQuery as Watched_Value2, (SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC LIMIT 1) as Watched_Value3, null as Watched_Value4, '' as Extra, hwaddr as ForeignKey FROM EXTERNAL_PIHOLE.network WHERE hwaddr NOT LIKE 'ip-%' AND hwaddr <> '00:00:00:00:00:00'",
"default_value": "SELECT hwaddr as objectPrimaryId, cast('http://' || (SELECT ip FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC LIMIT 1) as VARCHAR(100)) || ':' || cast(SUBSTR((SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC LIMIT 1), 0, INSTR((SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC LIMIT 1), '/')) as VARCHAR(100)) as objectSecondaryId, datetime() as DateTime, macVendor as watchedValue1, lastQuery as watchedValue2, (SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC LIMIT 1) as watchedValue3, null as watchedValue4, '' as Extra, hwaddr as ForeignKey FROM EXTERNAL_PIHOLE.network WHERE hwaddr NOT LIKE 'ip-%' AND hwaddr <> '00:00:00:00:00:00'",
"localized": ["name"],
"name": [{"language_code": "en_us", "string": "SQL to run"}]
}
Expand Down
Loading
Loading