Compare commits

..

205 Commits

Author SHA1 Message Date
jokob-sk
ddd28a0607 Update docker_prod.yml 2023-06-16 08:30:59 +10:00
jokob-sk
fa92df6567 Update docker_prod.yml 2023-06-16 08:28:54 +10:00
Jokob-sk
76a3efe039 Load only last few lines from pialert.log 2023-06-16 08:05:47 +10:00
Jokob-sk
601ed15f20 conf. variable fix & #247 2023-06-12 15:41:09 +10:00
Jokob-sk
2a3b505dff PLugin reports not sent fix 2023-06-12 11:24:42 +10:00
Jokob-sk
9550227672 Readme 2023-06-10 21:49:29 +10:00
Jokob-sk
89c0750463 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-06-10 21:48:11 +10:00
jokob-sk
0fa71362b8 Merge pull request #249 from jokob-sk/split-up-work-2023-05-30
Split up work 2023 05 30
2023-06-10 21:45:59 +10:00
Jokob-sk
d8df097e83 Fixes 2023-06-10 11:11:40 +10:00
Jokob-sk
67c20cabc3 Upgrade DB fixes + NTFY fix 2023-06-10 10:57:26 +10:00
Jokob-sk
5867961383 Upgrade DB fixes 2023-06-10 09:01:24 +10:00
jokob-sk
649e280ce1 Merge pull request #248 from Data-Monkey/split_it_up
fix issue #4 by @Data-Monkey
2023-06-10 08:02:44 +10:00
Data-Monkey
9d982eff1b fix issue #4 2023-06-09 08:44:50 +10:00
jokob-sk
6ac8225b19 Merge pull request #246 from Data-Monkey/split_it_up
Split it up
2023-06-05 18:23:03 +10:00
Data-Monkey
55ed3c4ae0 PiHole testing and fixing 2023-06-05 15:40:32 +10:00
Data-Monkey
5d0804639c attempt to fix issue #6 as well as db.read_one() 2023-06-05 13:31:14 +10:00
Data-Monkey
6edb623b9c fix issue #7 plugins not executed 2023-06-04 14:20:19 +10:00
jokob-sk
8c2a1e17d9 Merge pull request #245 from Data-Monkey/split_it_up
Split it up
2023-06-04 14:18:03 +10:00
Data-Monkey
2a2f96d726 Issue #4 - fixed fetachall, fetchone mismatch 2023-06-04 14:06:09 +10:00
Data-Monkey
ec705df38b fixing issue #3 - webhook 2023-06-04 13:45:48 +10:00
jokob-sk
cb20fad13b Merge pull request #244 from Data-Monkey/split_it_up
Split it up
2023-06-04 08:17:59 +10:00
Data-Monkey
969cae0343 send_webhook fixed parameters 2023-06-03 21:56:10 +10:00
Data-Monkey
f9652258e9 Merge branch 'split_it_up' of https://github.com/Data-Monkey/Pi.Alert into split_it_up 2023-06-03 21:42:28 +10:00
Data-Monkey
6bb891f830 added default DB
fixed re-import of config file
2023-06-03 21:42:04 +10:00
Jokob-sk
4659d2c941 PR - day view 2023-06-03 11:57:27 +10:00
Jokob-sk
8644818949 PR - docs 2023-06-03 11:48:33 +10:00
Jokob-sk
de62956c40 PR - dev setup fixes 2023-06-03 11:36:21 +10:00
Jokob-sk
3547aec75f PR - dev setup fixes 2023-06-03 09:39:50 +10:00
Jokob-sk
21b5d775d2 PR - gitignore DB 2023-06-03 09:26:21 +10:00
Jokob-sk
0973bc538e PR - default db 2023-06-03 09:24:20 +10:00
Jokob-sk
bf2c4b87ab PR - default db 2023-06-03 09:20:24 +10:00
jokob-sk
b03087c1c5 Merge pull request #243 from Data-Monkey/split_it_up
Split it up by @Data-Monkey - Huge thanks!
2023-06-03 09:18:09 +10:00
jokob-sk
b814a856d0 Merge branch 'main' into pr/243 2023-06-03 09:17:08 +10:00
Jokob-sk
f6b69a63e2 PR 2023-06-03 09:12:57 +10:00
jokob-sk
df499ea33c PR work 2023-06-03 09:04:09 +10:00
Data-Monkey
007611c429 manually added es translations from master 2023-05-30 18:57:56 +10:00
Data-Monkey
12bf4c7bcc more scanning 2023-05-30 18:47:28 +10:00
Data-Monkey
5b05be24ad split publishers 2023-05-29 16:35:22 +10:00
Data-Monkey
f50e3d4e92 split pubishers 2023-05-29 16:35:09 +10:00
Data-Monkey
7177cdd51d more cleanup and removed files.py module again 2023-05-28 16:10:58 +10:00
Data-Monkey
a71f16ee37 changed to __main__.py and scanners folder 2023-05-27 22:38:12 +10:00
jokob-sk
070e31ef19 Merge pull request #237 from Data-Monkey/patch-1
Update pialert.py - Thanks @Data-Monkey !
2023-05-27 10:00:03 +10:00
Roland Beck
42df2b255a Update pialert.py
add filter to ignore __pycache__ in plugin dir
2023-05-26 19:08:46 +10:00
Data-Monkey
52027c65b3 working docker version 2023-05-26 19:04:20 +10:00
Data-Monkey
a2f2bce3ab working docker version 2023-05-26 19:04:11 +10:00
Data-Monkey
ebcf6fa49b Merge branch 'split_it_up' of https://github.com/Data-Monkey/Pi.Alert into split_it_up 2023-05-25 19:51:11 +10:00
Data-Monkey
c14c762bde cleanup 2023-05-25 19:51:03 +10:00
Roland Beck
fd50ab7deb Create README.md 2023-05-25 13:53:08 +10:00
Data-Monkey
0db7521bee change to import conf 2023-05-24 22:34:09 +10:00
Data-Monkey
3adfa2c268 resolved cirqular imports 2023-05-23 21:18:44 +10:00
Data-Monkey
e27610a199 everything split out ut not tested 2023-05-22 22:05:54 +10:00
Data-Monkey
bd43a16975 everything split out not tested 2023-05-22 22:05:21 +10:00
jokob-sk
eb56126224 Merge pull request #235 from BanCrash/spanish
Update spanish strings by @BanCrash - thanks a lot!
2023-05-22 14:40:32 +10:00
BanCrash
7945fce65d Added new string 2023-05-21 16:21:18 +02:00
BanCrash
8a7ddfbb47 Added remaining spanish translations
Big thanks to @antoniog for giving me the file with all new english strings added!

Co-Authored-By: Antonio Caro <2288057+antoniog@users.noreply.github.com>
2023-05-21 16:03:11 +02:00
Data-Monkey
03163e424f more splitting done 2023-05-21 21:22:51 +10:00
Data-Monkey
d5b7023927 more splitting 2023-05-21 21:22:09 +10:00
Data-Monkey
884aca149a more splitting done 2023-05-21 17:40:05 +10:00
Data-Monkey
1836567f97 to keep up with jakob 2023-05-21 15:18:57 +10:00
Data-Monkey
78f71abd31 moved database out 2023-05-21 15:03:16 +10:00
Data-Monkey
0f63497847 split const and logger from main 2023-05-21 12:08:15 +10:00
Jokob-sk
8a1e472fed Established plugin_helper.py as best practice 2023-05-21 11:37:00 +10:00
Jokob-sk
3831b5a50a SUpport for unauthenticated SMTP #234 2023-05-21 11:04:03 +10:00
Jokob-sk
3756e1a327 re-add DB dir to gitignore 2023-05-21 10:58:12 +10:00
Jokob-sk
c9eb866acd add DB 2023-05-21 10:56:40 +10:00
jokob-sk
55530c05f9 Merge pull request #233 from Data-Monkey/undiscoverables_plugin
Un-Discoverable Devices Plugin by @Data-Monkey
2023-05-21 10:51:31 +10:00
Jokob-sk
422997be9b php errors log 2023-05-20 13:33:45 +10:00
Jokob-sk
13ff086412 Setting select presence statuses #221 2023-05-20 12:42:30 +10:00
Jokob-sk
298b5ac03e Attempt at fixing #228 2023-05-20 11:08:24 +10:00
Jokob-sk
e9af2efbd1 DOnt Awesome Pro docs 2023-05-20 10:56:01 +10:00
Roland Beck
4027970975 Update README.md
added some pictures
2023-05-18 17:57:48 +10:00
Data-Monkey
0ca7116167 Merge branches 'undiscoverables_plugin' and 'undiscoverables_plugin' of https://github.com/Data-Monkey/Pi.Alert into undiscoverables_plugin 2023-05-18 17:51:21 +10:00
Data-Monkey
1474cf424b code and documentation tidied up 2023-05-18 17:43:16 +10:00
Roland Beck
b763d75703 Merge branch 'jokob-sk:main' into undiscoverables_plugin 2023-05-17 22:33:32 +10:00
Data-Monkey
a0501d88ec working version of UnDIS plugin 2023-05-17 22:25:33 +10:00
Jokob-sk
f62d94ba61 database docs 0.1 2023-05-17 22:15:42 +10:00
Jokob-sk
e99d855284 database docs 0.1 2023-05-17 22:15:03 +10:00
Jokob-sk
31b78ff106 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-05-17 22:09:31 +10:00
Jokob-sk
ae0c45a716 database docs 2023-05-17 22:09:03 +10:00
jokob-sk
0105844410 Merge pull request #231 from Data-Monkey/plugin_config_dir
get_plugin_config changed to top level folders - Thanks to @Data-Monkey 🙏
2023-05-17 21:08:06 +10:00
Data-Monkey
07e8395536 file path fixed 2023-05-17 13:45:21 +10:00
Data-Monkey
6c8fc093af update git ignore to ignore pycache 2023-05-17 13:05:50 +10:00
Data-Monkey
95a7dcc7fc undiscoverables initial version 2023-05-17 12:55:34 +10:00
Data-Monkey
c54156ca1e get_plugin_config changed to top level folders 2023-05-17 11:39:29 +10:00
Jokob-sk
941a8ef661 Version number to foorter #209 0.1 2023-05-13 14:14:54 +10:00
Jokob-sk
fc79ffc956 Version number to foorter #209 2023-05-13 14:09:19 +10:00
jokob-sk
8e86343942 Merge pull request #229 from jordantrizz/main
feat: Place buildnumber and version in footer. Thanks @jordantrizz !
2023-05-13 09:49:58 +10:00
Jordan Trask
bb87e65745 feat: Place buildnumber and version in footer. 2023-05-11 15:06:59 -04:00
jokob-sk
d1989acd5c Merge pull request #227 from Data-Monkey/patch-1
Update README.md
2023-05-10 20:56:09 +10:00
Roland Beck
e5bc4ad41b Update README.md
added statement about file format and example
2023-05-10 20:46:58 +10:00
jokob-sk
927bdc2f2b Merge pull request #226 from Final-Hawk/patch-1
Fix table mobile view thanks to @Final-Hawk 👍
2023-05-10 19:35:19 +10:00
Joshua
2ff57d8272 Fix table mobile view
Remove obsolete column changing for mobile. 

This is now handled by the column selector in the maintenance tab.
2023-05-10 12:05:40 +10:00
Jokob-sk
49baf4b613 Docs 2023-05-07 10:27:41 +10:00
Jokob-sk
a573fd9841 Docs README 2023-05-07 09:56:37 +10:00
Jokob-sk
e91b640bff Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-05-07 09:03:52 +10:00
Jokob-sk
8ee47e2fcc Versions docs 2023-05-07 09:03:27 +10:00
jokob-sk
092797e75c Merge pull request #220 from webysther/patch-1
Update DEVICE_MANAGEMENT.md
2023-05-06 08:07:56 +10:00
Webysther Sperandio
816a01f8af Update DEVICE_MANAGEMENT.md 2023-05-05 20:52:16 +02:00
Jokob-sk
3578bbfcad emty columns fixes 2023-05-05 22:42:11 +10:00
Jokob-sk
b4b15af887 FIX SNMDSCV, NMAPSRV, UNFIDSCR bugs 2023-05-05 21:21:01 +10:00
Jokob-sk
c74ef127d1 update only unknown vendors 2023-05-05 19:08:10 +10:00
Jokob-sk
90b3a491c7 Icons docs 2023-04-29 09:10:52 +10:00
jokob-sk
ad41d02eeb Update README.md 2023-04-14 04:31:04 +00:00
jokob-sk
19fe48c7ec Merge pull request #207 from macf0x/patch-1
Update README.md
2023-04-14 14:15:40 +10:00
Macfox
f3d05ca222 Update README.md 2023-04-14 00:33:39 +09:30
Jokob-sk
4aa1848ece Code cleanup 2023-04-13 22:30:27 +10:00
Jokob-sk
2176c58cb5 SNMPDSC plugin 0.4 2023-04-12 20:06:22 +10:00
Jokob-sk
2b95daa248 Fix MAC not passed - #205 2023-04-10 17:00:29 +10:00
Jokob-sk
e7c0bcf419 SNMPDSC plugin 0.3 2023-04-10 12:37:03 +10:00
Jokob-sk
063682510e SNMPDSC plugin 0.2 2023-04-10 12:27:10 +10:00
Jokob-sk
8542d05f66 SNMPDSC plugin 0.1 + PLUG README updates 2023-04-08 13:45:15 +10:00
Jokob-sk
42aa89971d Update README 2023-04-02 12:31:24 +10:00
Jokob-sk
abd607ea10 Update docker README 2023-04-02 10:52:27 +10:00
Jokob-sk
5936ba4626 Improve Plugins README 0.1 2023-04-02 10:22:22 +10:00
Jokob-sk
a6c2b9254b Fix ENABLE_PLUGINS bug discovered in #203 2023-04-02 09:50:50 +10:00
Jokob-sk
62669fd181 Fix empty Plugin header tabs 2023-04-02 09:37:16 +10:00
Jokob-sk
c8fcf6227e UNIFI import plugin 0.2 2023-04-01 22:11:14 +11:00
Jokob-sk
310b6de2cc UNIFI import plugin 0.1 2023-04-01 21:02:36 +11:00
Jokob-sk
306535a2a6 UNIFI import plugin 0.1 2023-04-01 21:01:16 +11:00
Jokob-sk
5944b1b6f5 expanded Plugins readme 0.1 2023-03-30 16:13:33 +11:00
Jokob-sk
6de9e1d4bd expanded Plugins readme 2023-03-30 15:58:16 +11:00
Jokob-sk
4bf6ab9c8c Possible fix for #202 2023-03-27 22:14:29 +11:00
Jokob-sk
1e93dfa35e Expanding on plugins in README 2023-03-27 22:04:14 +11:00
Jokob-sk
7f2567264c Support for mapping plun obj to DB table 2023-03-26 12:12:30 +11:00
Jokob-sk
d9a9246f1b Attempt at Fixing #199 & Rougue DHCP fix 2023-03-26 09:47:56 +11:00
Jokob-sk
aa8fb62f15 README update #200 2023-03-25 08:03:04 +11:00
Jokob-sk
6d5eeb88d3 dhcp.leases v0.1 2023-03-19 15:48:20 +11:00
Jokob-sk
ea1d710209 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-03-19 12:22:57 +11:00
Jokob-sk
032b787b66 Optimizing API updates #193 2023-03-19 12:22:25 +11:00
Jokob-sk
7024cd22de update vendors 2023-03-18 08:21:50 +11:00
jokob-sk
b9b66d5af1 Merge pull request #198 from pbek/patch-1
fix: remove trailing slash for REPORT_DASHBOARD_URL
2023-03-15 08:06:00 +00:00
Jokob-sk
b73a0d6347 Fix how to set pwd #196 2023-03-15 18:39:55 +11:00
Patrizio Bekerle
a0bc318ff9 fix: remove trailing slash for REPORT_DASHBOARD_URL 2023-03-15 08:29:08 +01:00
Jokob-sk
642e8464cd better widescreen support 2023-03-15 18:13:21 +11:00
Jokob-sk
63a9e55d4e Rougue DHCP plugin based on work of @leiweibau 2023-03-12 19:31:59 +11:00
Jokob-sk
a07c73155f Spanish strings - based on work of @mariorodriguezlopez 2023-03-12 11:03:10 +11:00
Jokob-sk
efbc32d3ed Vlan info & setting CSS tuning 2023-03-12 10:21:42 +11:00
Jokob-sk
83f50bd0d8 Vlan config sample 2023-03-12 10:11:39 +11:00
Jokob-sk
87cb0f50b5 Plugins - small screen optimization 2023-03-12 09:57:18 +11:00
Jokob-sk
73c779c238 Wake on Lan by @leiweibau 2023-03-12 09:33:47 +11:00
Jokob-sk
40615cf17a Setting to disable/enable plugins 2023-03-12 08:21:32 +11:00
Jokob-sk
6dd1448667 Fix #165 2023-03-12 07:32:35 +11:00
Jokob-sk
d8b4091043 Plugins 1 - Readme 2023-03-11 17:18:02 +11:00
Jokob-sk
7ea920efa9 Plugins 1 - Readme 2023-03-11 17:16:23 +11:00
Jokob-sk
7306dfbdd3 Plugins 1 - Readme 2023-03-11 17:14:22 +11:00
Jokob-sk
53c8c41133 Plugins 1 - Readme 2023-03-11 17:00:09 +11:00
Jokob-sk
395dfe5fe7 Plugins 1 - JSON UI / Settings mapping sample 2023-03-11 16:51:00 +11:00
Jokob-sk
2ec0074a45 Plugins 1 - Readme 2023-03-11 16:32:46 +11:00
Jokob-sk
93dce378ea Plugins 1 - Screen 2023-03-11 16:25:53 +11:00
Jokob-sk
df6fae7aa5 Plugins 1 - Screen 2023-03-11 16:24:59 +11:00
Jokob-sk
57e0705e64 Plugins 1 - Readme 2023-03-11 16:12:29 +11:00
Jokob-sk
f3b31479c4 Plugins 1 - Screenshot + fixes 2023-03-11 15:55:00 +11:00
Jokob-sk
b914be9f0e Plugins 1 - Screenshot + fixes 2023-03-11 15:49:20 +11:00
Jokob-sk
2d0a4b79d8 Plugins 0.4 - ForeignKey support added 2023-03-11 13:57:25 +11:00
Jokob-sk
d090b29c55 Plugins 0.4 - UI work + bugfixes 2023-03-11 08:33:43 +11:00
Jokob-sk
5a8be94cdc Plugins 0.4 - UI work + refactoring 2023-03-09 22:30:36 +11:00
Jokob-sk
176a436ad4 Removed pialert_pholus.log 2023-03-04 20:47:25 +11:00
Jokob-sk
1a05435691 Plugins 0.4 - Further UI work 2023-03-04 20:41:48 +11:00
Jokob-sk
49ce3edbdb Invalid JSON Debug readme 2023-03-04 09:11:27 +11:00
Jokob-sk
51df759e25 debug image 2023-02-28 09:56:13 +11:00
Jokob-sk
992aa00c3c Plugins 0.3 - UI fixes 0.1 2023-02-26 16:27:14 +11:00
Jokob-sk
d87d933058 Nmap fix - Results not written into DB 2023-02-26 15:40:38 +11:00
Jokob-sk
e0d639cba0 Plugins 0.3 - Readme 0.3 2023-02-26 13:06:08 +11:00
Jokob-sk
1cbbfb25cc Plugins 0.3 - Readme 0.2 2023-02-26 12:59:23 +11:00
Jokob-sk
f205e6f5c1 Plugins 0.3 - Readme 0.1 2023-02-26 12:53:04 +11:00
Jokob-sk
5e67ea22f5 Plugins 0.3 - Readme 2023-02-26 12:46:20 +11:00
Jokob-sk
99f522e625 Plugins 0.3 - SQL support, UI CSS tweks, Lang fixes 2023-02-26 12:28:15 +11:00
Jokob-sk
f04cd7e28b Plugins 0.3 - dbHelper & remove unnecessary API settings 2023-02-25 15:29:01 +11:00
Jokob-sk
3392a1f17c Fix logout #181 2023-02-25 13:24:24 +11:00
Jokob-sk
d0d9a1a65d Plugins 0.3 - SQL call fix and additional UI controls 2023-02-25 12:56:40 +11:00
Jokob-sk
16d04fe485 Plugins 0.3 - UI custom form controls 2023-02-25 09:31:29 +11:00
Jokob-sk
43d5c51e7c Plugins 0.2 - UI fixes 2023-02-20 20:45:18 +11:00
Jokob-sk
647013f3ff Plugins 0.2 - Readme 2023-02-19 13:12:08 +11:00
Jokob-sk
2ef631a440 Plugins 0.2 - Reports working + Report status setting 2023-02-19 13:08:41 +11:00
Jokob-sk
86315a245b Plugins 0.2 - Fixed issues detecting changes + added UserData column 2023-02-18 15:14:31 +11:00
Jokob-sk
b0ce1b87a9 Plugins 0.2 - Show objects in UI 2023-02-14 22:24:12 +11:00
Jokob-sk
541c16aea6 Fix #175 2023-02-14 18:11:02 +11:00
Jokob-sk
ee1a3fc683 Plugins 0.2 - Show unprocessed events in UI 2023-02-13 22:20:48 +11:00
Jokob-sk
b90edcccbd Plugins 0.1 - Readme update 0.3 2023-02-12 18:19:55 +11:00
Jokob-sk
4614b93780 Plugins 0.1 - Readme update 0.2 2023-02-12 17:58:17 +11:00
Jokob-sk
4f548803cb Plugins 0.1 - Readme update 0.1 2023-02-12 17:50:49 +11:00
Jokob-sk
d16dd95d65 Plugins 0.1 - Readme update 2023-02-12 17:45:49 +11:00
Jokob-sk
97f7494c34 Plugin UI 0.1 2023-02-12 17:03:04 +11:00
Jokob-sk
6179dabfa6 Fixed empty settings 2023-02-12 14:30:35 +11:00
Jokob-sk
05ca683f91 LOG_LEVEL added 2023-02-12 13:22:55 +11:00
Jokob-sk
170e61e73f Plugins 0.1 - List param working 2023-02-11 16:11:27 +11:00
Jokob-sk
33f0356ca7 Fix CSV import 0.1 #175 2023-02-10 20:51:56 +11:00
Jokob-sk
4b9117dcb4 Fix CSV import #175 2023-02-10 20:20:26 +11:00
Jokob-sk
ec274c90da Plugins 0.1 - Adding LIST setting 0.2 2023-02-09 21:06:38 +11:00
Jokob-sk
e7a1f013df Plugins 0.1 - Adding LIST setting 0.1 2023-02-08 23:49:53 +11:00
Jokob-sk
c287bc2f22 Plugins 0.1 - Multi-Execution support + Fix #177 2023-02-08 22:53:05 +11:00
Jokob-sk
5b8f8f2c5d Plugins 0.1 - ONCE execution support 2023-02-07 21:48:06 +11:00
Jokob-sk
887c2d0f42 Plugins 0.1 - ONCE execution support 2023-02-07 21:42:02 +11:00
Jokob-sk
06d7aa6623 Plugins 0.1 - Lang Strings in DB 2023-02-06 21:49:25 +11:00
Jokob-sk
5ed142a6b8 Plugins 0.1 - Surfaced setings 2023-02-05 16:35:25 +11:00
Jokob-sk
da9ca8a1f4 Plugins 0.1 - Website monitoring cleanup 2023-02-05 15:24:46 +11:00
Jokob-sk
1cb5375a92 Plugins 0.1 - Basic definition 2023-02-05 13:02:38 +11:00
jokob-sk
fd11cc30f5 Merge pull request #167 from jokob-sk/revert-161-main
Revert "Change in front"
2023-02-04 00:24:04 +00:00
jokob-sk
a1bfdc0f18 Revert "Change in front" 2023-02-04 11:20:25 +11:00
jokob-sk
e88362ce80 Merge pull request #161 from mariorodriguezlopez/main
Change in front
2023-02-04 00:04:24 +00:00
Jokob-sk
b5f0a64e7c Bugfix issues 165, 163 0.1 2023-02-04 10:40:21 +11:00
Jokob-sk
e3623420b0 Bugfix issues 165, 163 2023-02-04 10:38:57 +11:00
Mario Rodriguez
3344ed4b99 Changes front 2023-02-02 13:06:05 +01:00
Mario Rodriguez
b0c91f7804 Merge branch 'jokob-sk:main' into main 2023-02-02 12:56:53 +01:00
Mario Rodriguez
f1dc33761d Merge pull request #3 from jokob-sk/main
Sync from main
2023-02-01 07:48:39 +01:00
130 changed files with 11020 additions and 3896 deletions

View File

@@ -12,35 +12,31 @@ assignees: ''
**Paste last few lines from `pialert.log`**
> You can use `tail -20 /home/pi/pialert/front/log/pialert.log`
```
paste here
paste_here
```
**Paste your `pialert.conf` (remove personal info)**
```
paste here
paste_here
```
**Paste your `docker-compose.yml` and `.env` (remove personal info)**
`docker-compose.yml`
```
paste here
```
paste_here
```
`.env`
```
paste here
```
paste_here
```
**Screenshots**
If applicable, add screenshots to help explain your problem.

View File

@@ -12,7 +12,8 @@ name: Publish Docker image
on:
release:
types: [published]
tags:
- '*.*.*'
jobs:
docker:
runs-on: ubuntu-latest
@@ -39,14 +40,19 @@ jobs:
jokobsk/pi.alert
# generate Docker tags based on the following events/attributes
tags: |
type=raw,value=latest
type=schedule
type=ref,event=branch
type=semver,pattern={{version}},value=${{ inputs.version }}
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.version }}
type=semver,pattern={{major}},value=${{ inputs.version }}
type=ref,event=branch,suffix=-{{ sha }}
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2

7
.gitignore vendored
View File

@@ -2,7 +2,12 @@
.DS_Store
config/pialert.conf
db/*
db/pialert.db
front/log/*
front/api/*
front/plugins/**/*.log
**/%40eaDir/
**/@eaDir/
__pycache__/
*.py[cod]
*$py.class

View File

@@ -1,14 +1,15 @@
FROM debian:bullseye-slim
# default UID and GID
ENV USER=pi USER_ID=1000 USER_GID=1000 TZ=Europe/London PORT=20211
ENV USER=pi USER_ID=1000 USER_GID=1000 PORT=20211
#TZ=Europe/London
# Todo, figure out why using a workdir instead of full paths don't work
# Todo, do we still need all these packages? I can already see sudo which isn't needed
RUN apt-get update \
&& apt-get install --no-install-recommends tini ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools python3 iproute2 nmap python3-pip zip -y \
&& pip3 install requests paho-mqtt scapy cron-converter pytz json2table \
&& apt-get install --no-install-recommends tini snmp ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools python3 iproute2 nmap python3-pip zip -y \
&& pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi \
&& update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \
&& apt-get clean autoclean \
&& apt-get autoremove \
@@ -46,3 +47,8 @@ RUN rm /etc/nginx/sites-available/default \
ENTRYPOINT ["tini", "--"]
CMD ["/home/pi/pialert/dockerfiles/start.sh"]
## command to build docker: DOCKER_BUILDKIT=1 docker build . --iidfile dockerID

View File

@@ -7,7 +7,6 @@ Scans for devices connected to your WIFI / LAN and alerts you if new and unknown
![Main screen][main]
# 🐳 Docker image
[![Docker](https://img.shields.io/github/actions/workflow/status/jokob-sk/Pi.Alert/docker_prod.yml?label=Build&logo=GitHub)](https://github.com/jokob-sk/Pi.Alert/actions/workflows/docker_prod.yml)
[![GitHub Committed](https://img.shields.io/github/last-commit/jokob-sk/Pi.Alert?color=40ba12&label=Committed&logo=GitHub&logoColor=fff)](https://github.com/jokob-sk/Pi.Alert)
@@ -15,7 +14,7 @@ Scans for devices connected to your WIFI / LAN and alerts you if new and unknown
[![Docker Pulls](https://img.shields.io/docker/pulls/jokobsk/pi.alert?label=Pulls&logo=docker&color=0aa8d2&logoColor=fff)](https://hub.docker.com/r/jokobsk/pi.alert)
[![Docker Pushed](https://img.shields.io/badge/dynamic/json?color=0aa8d2&logoColor=fff&label=Pushed&query=last_updated&url=https%3A%2F%2Fhub.docker.com%2Fv2%2Frepositories%2Fjokobsk%2Fpi.alert%2F&logo=docker&link=http://left&link=https://hub.docker.com/repository/docker/jokobsk/pi.alert)](https://hub.docker.com/r/jokobsk/pi.alert)
🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📄 [Dockerfile](https://github.com/jokob-sk/Pi.Alert/blob/main/Dockerfile) | 📚 [Docker instructions](https://github.com/jokob-sk/Pi.Alert/blob/main//dockerfiles/README.md) | 🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases)
🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📄 [Dockerfile](https://github.com/jokob-sk/Pi.Alert/blob/main/Dockerfile) | 📚 [Docker instructions](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md) | 🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases)
## 🔍 Scan Methods
The system continuously scans the network for, **New devices**, **New connections** (re-connections), **Disconnections**, **"Always Connected" devices down**, Devices **IP changes** and **Internet IP address changes**. Scanning methods are:
@@ -30,68 +29,55 @@ The system continuously scans the network for, **New devices**, **New connection
examines the DHCP leases (addresses assigned) to find active devices
that were not discovered by the other methods.
## 🧩 Integrations
- [Apprise](https://hub.docker.com/r/caronc/apprise), [Pushsafer](https://www.pushsafer.com/), [NTFY](https://ntfy.sh/)
- [Webhooks](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md) ([sample JSON](docs/webhook_json_sample.json))
- Home Assistant via [MQTT](https://www.home-assistant.io/integrations/mqtt/)
- discovery ~10s per device, deleting not supported, use [MQTT Explorer](https://mqtt-explorer.com/) for now
- A simple [API endpoint](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md)
## 🔐 Security
- Configurable login to prevent unauthorized use.
## 📑 Features
- Display:
- Sessions, Connected devices, Favorites, Events, Presence, Concurrent devices, Down alerts, IP's
- Manual Nmap scans, Optional speedtest for Device "Internet"
- Simple Network relationship display
- Maintenance tasks and Settings like:
- Status Infos (active scans, database size, backup counter)
- Theme Selection (blue, red, green, yellow, black, purple) and Light/Dark-Mode Switch
- Language Selection (English, German, Spanish)
- Pause arp-scan
- DB maintenance, Backup, Restore tools and CSV Export / Import
- Help/FAQ Section
| ![Screen 1][screen1] | ![Screen 2][screen2] |
|----------------------|----------------------|
| ![Screen 3][screen3] | ![Screen 4][screen4] |
| ![Screen 5][screen5] | ![Screen 6][screen6] |
| ![Report 1][report1] | ![Report 2][report2] |
- Home Assistant via [MQTT](https://www.home-assistant.io/integrations/mqtt/) - discovery ~10s per device, use [MQTT Explorer](https://mqtt-explorer.com/) to delete devices
- [API endpoint](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md)
- [Plugin system](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins) for custom script monitoring
# 📥 Installation
<!--- --------------------------------------------------------------------- --->
This [fork (jokob-sk)](https://github.com/jokob-sk/Pi.Alert) is only tested as a [docker container](dockerfiles/README.md). Check out [leiweibau's fork](https://github.com/leiweibau/Pi.Alert/) if you want to install Pi.Alert on the server directly.
Only tested as a [docker container - follow these instructions here](dockerfiles/README.md).
> Check out [leiweibau's fork](https://github.com/leiweibau/Pi.Alert/) if you want to install Pi.Alert on the server directly or original instructions for [pucherot's original code](https://github.com/pucherot/Pi.Alert/)
Instructions for [pucherot's original code can be found here](https://github.com/pucherot/Pi.Alert/)
# 📑 Features
- Display:
- Sessions, Connected devices, Favorites, Events, Presence, Concurrent devices, Down alerts, IP's
- Manual Nmap scans, Optional speedtest for Device "Internet"
- Simple Network relationship display
- Maintenance tasks and Settings like:
- Theme Selection (blue, red, green, yellow, black, purple) and Light/Dark-Mode Switch
- DB maintenance, Backup, Restore tools and CSV Export / Import
- Simple login Support
- 🌟[Plugin system](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins)
- Create custom plugins with automatically generated settings and UI.
- Monitor anything for changes
- Check the [instructions](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins) carefully if you are up for a challenge! Current plugins include:
- Detecting Rogue DHCP servers via NMAP
- Monitoring HTTP status changes of domains/URLs
- Import devices from DHCP.leases files, a UniFi controller, or an SNMP enabled router
- Creation of dummy devices to visualize your [network map](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/NETWORK_TREE.md)
| ![Screen 1][screen1] | ![Screen 2][screen2] | ![Screen 5][screen5] |
|----------------------|----------------------| ----------------------|
| ![Screen 3][screen3] | ![Screen 4][screen4] | ![Screen 6][screen6] |
| ![Screen 8][screen8] | ![Report 2][report2] | ![Screen 9][screen9] |
## 🔗 Other
<!--- --------------------------------------------------------------------- --->
<!--- --------------------------------------------------------------------- --->
### Alternatives
### 🔗 Other Alternatives
- [WatchYourLAN](https://github.com/aceberg/WatchYourLAN) - Lightweight network IP scanner with web GUI (Open source)
- [Fing](https://www.fing.com/) - Network scanner app for your Internet security (Commercial, Phone App, Proprietary hardware)
### Old docs
### 📚 Documentation
- [Device Management instructions](docs/DEVICE_MANAGEMENT.md)
- [Versions History](docs/VERSIONS_HISTORY.md)
- Initial Docker Setup: [Docker instructions](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md)
- App Usage and Configuration: [All Documentation](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/README.md)
### License
GPL 3.0
- [Read more here](LICENSE.txt)
- Source of the [animated GIF (Loading Animation)](https://commons.wikimedia.org/wiki/File:Loading_Animation.gif)
- Source of the [selfhosted Fonts](https://github.com/adobe-fonts/source-sans)
GPL 3.0 | [Read more here](LICENSE.txt) | Source of the [animated GIF (Loading Animation)](https://commons.wikimedia.org/wiki/File:Loading_Animation.gif) | Source of the [selfhosted Fonts](https://github.com/adobe-fonts/source-sans)
### 🥇 Special thanks
@@ -102,8 +88,16 @@ Instructions for [pucherot's original code can be found here](https://github.com
- [Macleykun](https://github.com/Macleykun): Help with Dockerfile clean-up
- [Final-Hawk](https://github.com/Final-Hawk): Help with NTFY, styling and other fixes
- [TeroRERO](https://github.com/terorero): Spanish translation
- [jokob-sk](https://github.com/jokob-sk/Pi.Alert): DB Maintenance tools
- Please see the [Git commit history](https://github.com/jokob-sk/Pi.Alert/commits/main) for a full list of people and their contributions to the project
- [Data-Monkey](https://github.com/Data-Monkey): Split-up of the python.py file and more
- Please see the [Git contributors](https://github.com/jokob-sk/Pi.Alert/graphs/contributors) for a full list of people and their contributions to the project
## ☕ Support me
<a href="https://github.com/sponsors/jokob-sk" target="_blank"><img src="https://i.imgur.com/X6p5ACK.png" alt="Sponsor Me on GitHub" style="height: 30px !important;width: 117px !important;" width="150px" ></a>
<a href="https://www.buymeacoffee.com/jokobsk" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 117px !important;" width="117px" height="30px" ></a>
<a href="https://www.patreon.com/user?u=84385063" target="_blank"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Patreon_logo_with_wordmark.svg/512px-Patreon_logo_with_wordmark.svg.png" alt="Support me on patreon" style="height: 30px !important;width: 117px !important;" width="117px" ></a>
BTC: 1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM
<!--- --------------------------------------------------------------------- --->
[main]: ./docs/img/devices_split.png "Main screen"
@@ -113,7 +107,9 @@ Instructions for [pucherot's original code can be found here](https://github.com
[screen4]: ./docs/img/maintenance.png "Screen 4"
[screen5]: ./docs/img/network.png "Screen 5"
[screen6]: ./docs/img/settings.png "Screen 6"
[screen7]: ./docs/img/help_faq.png "Screen 6"
[screen7]: ./docs/img/help_faq.png "Screen 7"
[screen8]: ./docs/img/plugins_webmon.png "Screen 8"
[screen9]: ./docs/img/device_nmap.png "Screen 9"
[report1]: ./docs/img/4_report_1.jpg "Report sample 1"
[report2]: ./docs/img/4_report_2.jpg "Report sample 2"
[main_dark]: /docs/img/1_devices_dark.jpg "Main screen dark"

View File

File diff suppressed because it is too large Load Diff

View File

@@ -34,14 +34,11 @@
<td bgcolor=#F5F5F5 height=200 valign=top style="padding: 10px">
<INTERNET_TABLE>
<NEW_DEVICES_TABLE>
<DOWN_DEVICES_TABLE>
<EVENTS_TABLE>
<NEW_DEVICES_TABLE>
<DOWN_DEVICES_TABLE>
<EVENTS_TABLE>
<PORTS_TABLE>
<PLUGINS_TABLE>
</td>
</tr>

View File

@@ -6,3 +6,4 @@ Server: <SERVER_NAME>
<SECTION_EVENTS>
<SECTION_INTERNET>
<PORTS_TABLE>
<PLUGINS_TABLE>

View File

@@ -38,14 +38,11 @@
<td bgcolor=#F5F5F5 height=200 valign=top style="padding: 10px">
<INTERNET_TABLE>
<NEW_DEVICES_TABLE>
<DOWN_DEVICES_TABLE>
<EVENTS_TABLE>
<NEW_DEVICES_TABLE>
<DOWN_DEVICES_TABLE>
<EVENTS_TABLE>
<PORTS_TABLE>
<PLUGINS_TABLE>
<tr>
<td>

View File

@@ -26,16 +26,16 @@ sudo cp *.csv 2_backup
echo ""
echo Download Start
echo ""
sudo curl $1 -O https://standards-oui.ieee.org/iab/iab.csv \
-O https://standards-oui.ieee.org/iab/iab.txt \
-O https://standards-oui.ieee.org/oui28/mam.csv \
-O https://standards-oui.ieee.org/iab/iab.txt \
-O https://standards-oui.ieee.org/oui28/mam.csv \
-O https://standards-oui.ieee.org/oui28/mam.txt \
-O https://standards-oui.ieee.org/oui36/oui36.csv \
-O https://standards-oui.ieee.org/oui36/oui36.txt \
-O https://standards-oui.ieee.org/oui/oui.csv \
-O https://standards-oui.ieee.org/oui/oui.txt
sudo curl $1 -LO https://standards-oui.ieee.org/iab/iab.csv \
-LO https://standards-oui.ieee.org/iab/iab.txt \
-LO https://standards-oui.ieee.org/oui28/mam.csv \
-LO https://standards-oui.ieee.org/iab/iab.txt \
-LO https://standards-oui.ieee.org/oui28/mam.csv \
-LO https://standards-oui.ieee.org/oui28/mam.txt \
-LO https://standards-oui.ieee.org/oui36/oui36.csv \
-LO https://standards-oui.ieee.org/oui36/oui36.txt \
-LO https://standards-oui.ieee.org/oui/oui.csv \
-LO https://standards-oui.ieee.org/oui/oui.txt
echo ""
echo Download Finished

View File

@@ -23,7 +23,8 @@ PIALERT_WEB_PASSWORD='8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923a
INCLUDED_SECTIONS=['internet','new_devices','down_devices','events']
SCAN_CYCLE_MINUTES=5
DAYS_TO_KEEP_EVENTS=90
REPORT_DASHBOARD_URL='http://pi.alert/'
# Used for generating links in emails. Make sure not to add a trailing slash!
REPORT_DASHBOARD_URL='http://pi.alert'
# Email

View File

@@ -7,17 +7,25 @@ services:
network_mode: "host"
restart: unless-stopped
volumes:
- ${APP_DATA_LOCATION}/pialert/config2:/home/pi/pialert/config
# - ${APP_DATA_LOCATION}/pialert/db/pialert.db:/home/pi/pialert/db/pialert.db
- ${APP_DATA_LOCATION}/pialert/db2:/home/pi/pialert/db
- ${APP_DATA_LOCATION}/pialert_dev/config:/home/pi/pialert/config
# - ${APP_DATA_LOCATION}/pialert/config:/home/pi/pialert/config
- ${APP_DATA_LOCATION}/pialert_dev/db:/home/pi/pialert/db
# - ${APP_DATA_LOCATION}/pialert/db:/home/pi/pialert/db
# (optional) useful for debugging if you have issues setting up the container
- ${LOGS_LOCATION}:/home/pi/pialert/front/log
# ---------------------------------------------------------------------------
# DELETE START anyone trying to use this file: comment out / delete BELOW lines, they are only for development purposes
- ${DEV_LOCATION}/back/pialert.py:/home/pi/pialert/back/pialert.py
- ${APP_DATA_LOCATION}/pialert/dhcp_samples/dhcp1.leases:/mnt/dhcp1.leases
- ${APP_DATA_LOCATION}/pialert/dhcp_samples/dhcp2.leases:/mnt/dhcp2.leases
# - ${DEV_LOCATION}/back/pialert.py:/home/pi/pialert/back/pialert.py
- ${DEV_LOCATION}/pialert:/home/pi/pialert/pialert
# - ${DEV_LOCATION}/back/report_template.html:/home/pi/pialert/back/report_template.html
# - ${DEV_LOCATION}/back/report_template_new_version.html:/home/pi/pialert/back/report_template_new_version.html
# - ${DEV_LOCATION}/back/report_template.txt:/home/pi/pialert/back/report_template.txt
- ${DEV_LOCATION}/pholus:/home/pi/pialert/pholus
- ${DEV_LOCATION}/dockerfiles:/home/pi/pialert/dockerfiles
- ${APP_DATA_LOCATION}/pialert/php.ini:/etc/php/7.4/fpm/php.ini
- ${DEV_LOCATION}/front/api:/home/pi/pialert/front/api
# - ${DEV_LOCATION}/front/api:/home/pi/pialert/front/api
- ${DEV_LOCATION}/front/css:/home/pi/pialert/front/css
- ${DEV_LOCATION}/front/lib/AdminLTE:/home/pi/pialert/front/lib/AdminLTE
- ${DEV_LOCATION}/front/js:/home/pi/pialert/front/js
@@ -25,13 +33,16 @@ services:
- ${DEV_LOCATION}/front/deviceDetails.php:/home/pi/pialert/front/deviceDetails.php
- ${DEV_LOCATION}/front/devices.php:/home/pi/pialert/front/devices.php
- ${DEV_LOCATION}/front/events.php:/home/pi/pialert/front/events.php
- ${DEV_LOCATION}/front/plugins.php:/home/pi/pialert/front/plugins.php
- ${DEV_LOCATION}/front/help_faq.php:/home/pi/pialert/front/help_faq.php
- ${DEV_LOCATION}/front/index.php:/home/pi/pialert/front/index.php
- ${DEV_LOCATION}/front/maintenance.php:/home/pi/pialert/front/maintenance.php
- ${DEV_LOCATION}/front/network.php:/home/pi/pialert/front/network.php
- ${DEV_LOCATION}/front/presence.php:/home/pi/pialert/front/presence.php
- ${DEV_LOCATION}/front/settings.php:/home/pi/pialert/front/settings.php
- ${DEV_LOCATION}/front/plugins:/home/pi/pialert/front/plugins
# DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
# ---------------------------------------------------------------------------
environment:
- TZ=${TZ}
- PORT=${PORT}

View File

@@ -6,7 +6,7 @@
# 🐳 A docker image for Pi.Alert
🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📄 [Dockerfile](https://github.com/jokob-sk/Pi.Alert/blob/main/Dockerfile) | 📚 [Docker instructions](https://github.com/jokob-sk/Pi.Alert/blob/main//dockerfiles/README.md) | 🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases)
🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📄 [Dockerfile](https://github.com/jokob-sk/Pi.Alert/blob/main/Dockerfile) | 📚 [Docker instructions](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md) | 🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases)
<a href="https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/devices_split.png" target="_blank">
<img src="https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/devices_split.png" width="300px" />
@@ -53,26 +53,39 @@ docker run -d --rm --network=host \
### Config (`pialert.conf`)
- Modify [pialert.conf](https://github.com/jokob-sk/Pi.Alert/tree/main/config) or manage the configuration via Settings.
- ❗ Set the `SCAN_SUBNETS` variable.
- If unavailable, the app generates a default `pialert.conf` and `pialert.db` file on the first run.
- The preferred way is to manage the configuration via the Settings section in the UI.
- You can modify [pialert.conf](https://github.com/jokob-sk/Pi.Alert/tree/main/config) directly, if needed.
#### Important settings
These are the most important settings to get at least some output in your Devices screen. Usually, only one approach is used, but you should be able to combine these approaches.
##### For arp-scan: ENABLE_ARPSCAN, SCAN_SUBNETS
- ❗ To use the arp-scan method, you need to set the `SCAN_SUBNETS` variable.
* The adapter will probably be `eth0` or `eth1`. (Run `iwconfig` to find your interface name(s))
* Specify the network filter (which **significantly** speeds up the scan process). For example, the filter `192.168.1.0/24` covers IP ranges 192.168.1.0 to 192.168.1.255.
* Examples for one and two subnets (❗ Note the `['...', '...']` format):
* One subnet: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0']`
* Two subnets: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0', '192.168.1.0/24 --interface=eth1']`
* Two subnets: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0', '192.168.1.0/24 --interface=eth1 -vlan=107']`
* More documentation on how to e.g. [setup vlans & limitations](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SUBNETS.md)
##### For pihole: PIHOLE_ACTIVE, DHCP_ACTIVE
### 🛑 **Common issues**
* `PIHOLE_ACTIVE`: You need to map `:/etc/pihole/pihole-FTL.db in the docker-compose.yml` file if you enable this setting.
* `DHCP_ACTIVE` : You need to map `:/etc/pihole/dhcp.leases in the docker-compose.yml` file if you enable this setting.
### **Common issues**
💡 Before creating a new issue, please check if a similar issue was [already resolved](https://github.com/jokob-sk/Pi.Alert/issues?q=is%3Aissue+is%3Aclosed).
**Permissions**
* If facing issues (AJAX errors, can't write to DB, empty screen, etc,) make sure permissions are set correctly, and check the logs under `/home/pi/pialert/front/log`.
* To solve permission issues you can also try to create a DB backup and then run a DB Restore via the **Maintenance > Backup/Restore** section.
* You can try also setting the owner and group of the `pialert.db` by executing the following on the host system: `docker exec pialert chown -R www-data:www-data /home/pi/pialert/db/pialert.db`.
* To solve permission issues you can try setting the owner and group of the `pialert.db` by executing the following on the host system: `docker exec pialert chown -R www-data:www-data /home/pi/pialert/db/pialert.db`.
* Map to local User and Group IDs. Specify the enviroment variables `HOST_USER_ID` and `HOST_USER_GID` if needed.
* Map the pialert.db file (⚠ not folder) to `:/home/pi/pialert/db/pialert.db` (see Examples below for details)
* If still facing issues, try to map the pialert.db file (⚠ not folder) to `:/home/pi/pialert/db/pialert.db` (see Examples below for details)
**Container restarts / crashes**
@@ -113,6 +126,27 @@ To run the container execute: `sudo docker-compose up -d`
### Example 2
Example by [SeimuS](https://github.com/SeimusS).
```yaml
pialert:
container_name: PiAlert
hostname: PiAlert
privileged: true
image: jokobsk/pi.alert:latest
environment:
- TZ=Europe/Bratislava
restart: always
volumes:
- ./pialert/pialert_db:/home/pi/pialert/db
- ./pialert/pialert_config:/home/pi/pialert/config
network_mode: host
```
To run the container execute: `sudo docker-compose up -d`
### Example 3
`docker-compose.yml`
```yaml
@@ -158,7 +192,7 @@ DEV_LOCATION=/path/to/local/source/code
To run the container execute: `sudo docker-compose --env-file /path/to/.env up`
### Example 3
### Example 4
Courtesy of [pbek](https://github.com/pbek). The volume `pialert_db` is used by the db directory. The two config files are mounted directly from a local folder to their places in the config folder. You can backup the `docker-compose.yaml` folder and the docker volumes folder.
@@ -188,8 +222,8 @@ Big thanks to <a href="https://github.com/Macleykun">@Macleykun</a> for help and
## ☕ Support me
Disclaimer: Please only donate if you don't have any debt yourself. Support yourself first, then others.
<a href="https://github.com/sponsors/jokob-sk" target="_blank"><img src="https://i.imgur.com/X6p5ACK.png" alt="Sponsor Me on GitHub" style="height: 30px !important;width: 117px !important;" width="150px" ></a>
<a href="https://www.buymeacoffee.com/jokobsk" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 117px !important;" width="117px" height="30px" ></a>
<a href="https://www.patreon.com/user?u=84385063" target="_blank"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Patreon_logo_with_wordmark.svg/512px-Patreon_logo_with_wordmark.svg.png" alt="Support me on patreon" style="height: 30px !important;width: 117px !important;" width="117px" ></a>
BTC: 1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM

View File

@@ -1,15 +1,15 @@
#!/bin/sh
/home/pi/pialert/dockerfiles/user-mapping.sh
# if custom variables not set we do not need to do anything
if [ -n "${TZ}" ]; then
FILECONF=/home/pi/pialert/config/pialert.conf
if [ -f "$FILECONF" ]; then
sed -ie "s|Europe/Berlin|${TZ}|g" /home/pi/pialert/config/pialert.conf
else
sed -ie "s|Europe/Berlin|${TZ}|g" /home/pi/pialert/back/pialert.conf_bak
fi
fi
# # if custom variables not set we do not need to do anything
# if [ -n "${TZ}" ]; then
# FILECONF=/home/pi/pialert/config/pialert.conf
# if [ -f "$FILECONF" ]; then
# sed -ie "s|Europe/Berlin|${TZ}|g" /home/pi/pialert/config/pialert.conf
# else
# sed -ie "s|Europe/Berlin|${TZ}|g" /home/pi/pialert/back/pialert.conf_bak
# fi
# fi
if [ -n "${PORT}" ]; then
sed -ie 's/listen 20211/listen '${PORT}'/g' /etc/nginx/sites-available/default
@@ -28,4 +28,6 @@ chmod -R a+rw /home/pi/pialert/config
/etc/init.d/nginx start
# cron -f
python /home/pi/pialert/back/pialert.py
#python /home/pi/pialert/back/pialert.py
# echo "[DEBUG] DATA MONKEY VERSION ..."
python /home/pi/pialert/pialert/

View File

@@ -5,12 +5,7 @@ PiAlert comes with a simple API. These API endpoints are static files, that are
### When are the endpoints updated
Once you enable the API (`ENABLE_API` setting), the endpoints are updated during these events:
1) Always during a notification event.
2) (optional) If `API_RUN` is set to `schedule` on a specified cron-like schedule specified by the `API_RUN_SCHD` setting.
3) (optional) If `API_RUN` is set to `interval` every N seconds specified by the `API_RUN_INTERVAL` setting (minimum 5).
Once you enable the API (`ENABLE_API` setting), the endpoints are updated when objects in the API endpoints are changed:
### Location of the endpoints
@@ -28,8 +23,12 @@ You can access the following files:
| `table_devices.json` | The current (at the time of the last update as mentioned above on this page) state of all of the available Devices detected by the app. |
| `table_nmap_scan.json` | The current state of the discovered ports by the regular NMAP scans. |
| `table_pholus_scan.json` | The latest state of the [pholus](https://github.com/jokob-sk/Pi.Alert/tree/main/pholus) (A multicast DNS and DNS Service Discovery Security Assessment Tool) scan results. |
| `table_events_pending_alert.json` | The list of the unprocessed (pending) notification events. |
| `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/Pi.Alert/tree/main/front/plugins)|
| `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. |
Current/latest state of the aforementioned files depends on your settings.

47
docs/DATABASE.md Executable file
View File

@@ -0,0 +1,47 @@
# A high-level description of the datbase 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
| Table name | Description | Sample data |
|----------------------|----------------------| ----------------------|
| CurrentScan | Result of the current scan | ![Screen1][screen1] |
| Devices | The main devices database that also contains the Network tree mappings. | ![Screen2][screen2] |
| DHCP_Leases | Used for importing devices from DHCP_Leases files. Also leveraged by some plugins. | ![Screen3][screen3] |
| Events | Used to collect connection/disconnection events. | ![Screen4][screen4] |
| Nmap_Scan | Contains results of the scheduled Nmap scan, taht is also displayed in the Nmap tab on each device. | ![Screen5][screen5] |
| Online_History | Used to display the `Device presence over time` chart | ![Screen6][screen6] |
| Parameters | Used to pass values between the frontend and backend. | ![Screen7][screen7] |
| Pholus_Scan | Scan results of the Pholus python network penetration script. | ![Screen8][screen8] |
| PiHole_Network | Table to copy the devices from the PiHole database | ![Screen9][screen9] |
| Plugins_Events | For capturing events exposed by a plugin via the `last_result.log` file. If unique then saved into the `Plugins_Objects` table. Entries are deleted once processed and stored in the `Plugins_History` and/or `Plugins_Objects` tables. | ![Screen10][screen10] |
| Plugins_History | History of all entries from the `Plugins_Events` table | ![Screen11][screen11] |
| Plugins_Language_Strings | Language strings colelcted from the plugin `config.json` files used for string resolution in the frontend. | ![Screen12][screen12] |
| Plugins_Objects | Unique objects detected by individual plugins. | ![Screen13][screen13] |
| ScanCycles | (obsolete) Used to determine and identify different scan cycles. | ![Screen14][screen14] |
| Sessions | Used to display sessions in the charts | ![Screen15][screen15] |
| Settings | Database representation of the sum of all settings from `pialert.conf` and plugins coming from `config.json` files. | ![Screen16][screen16] |
[screen1]: /docs/img/DATABASE/CurrentScan.png
[screen2]: /docs/img/DATABASE/Devices.png
[screen3]: /docs/img/DATABASE/DHCP_Leases.png
[screen4]: /docs/img/DATABASE/Events.png
[screen5]: /docs/img/DATABASE/Nmap_Scan.png
[screen6]: /docs/img/DATABASE/Online_History.png
[screen7]: /docs/img/DATABASE/Parameters.png
[screen8]: /docs/img/DATABASE/Pholus_Scan.png
[screen9]: /docs/img/DATABASE/PiHole_Network.png
[screen10]: /docs/img/DATABASE/Plugins_Events.png
[screen11]: /docs/img/DATABASE/Plugins_History.png
[screen12]: /docs/img/DATABASE/Plugins_Language_Strings.png
[screen13]: /docs/img/DATABASE/Plugins_Objects.png
[screen14]: /docs/img/DATABASE/ScanCycles.png
[screen15]: /docs/img/DATABASE/Sessions.png
[screen16]: /docs/img/DATABASE/Settings.png

31
docs/DEBUG_INVALID_JSON.md Executable file
View File

@@ -0,0 +1,31 @@
# How to debug the Invalid JSON response error
Check the the HTTP response of the failing backend call by following these steps:
- Open developer console in your browser (usually, e. g. for Chrome, key F12 on the keyboard).
- Follow the steps in this screenshot:
![F12DeveloperConsole][F12DeveloperConsole]
- Copy the URL causing the error and enter it in the address bar of your browser directly and hit enter. The copied URLs could look something like this (notice the query strings at the end):
- `http://<pialert URL>:20211/php/server/devices.php?action=getDevicesTotals`
- `http://<pialert URL>:20211/php/server/devices.php?action=getDevicesList&status=all`
- Post the error response in the existing issue thread on GitHub or create a new issue and include the redacted response of the failing query.
For reference, the above queries should return results in the following format:
First URL:
![array][array]
Second URL:
![json][json]
You can copy and paste any JSON result (result of the second query) into an online JSON checker, such as [this one](https://jsonchecker.com/) to check if it's valid.
[F12DeveloperConsole]: ./img/DEBUG/Invalid_JSON_repsonse_debug.png "F12DeveloperConsole"
[array]: ./img/DEBUG/array_result_example.png "array"
[json]: ./img/DEBUG/JSON_result_example.png "json"

View File

@@ -18,7 +18,7 @@ To edit device information:
- **Owner**: Device owner (The list is self-populated with existing owners)
- **Type**: Select a device type from the dropdown list (Smartphone, Table,
Laptop, TV, router, ....) or type a new device type
- **Vendor**: Automatically updated by Pi.Alert
- **Vendor**: Automatically updated by Pi.Alert when empty or unknown
- **Favorite**: Mark the device as favorite and then it will appears at the
begining of the device list
- **Group**: Select a grouper ('Always on', 'Personal', Friends') or type

30
docs/ICONS.md Executable file
View File

@@ -0,0 +1,30 @@
## Icons overview
Icons are used to visually distinguish devices in the app in most of the device listing tables and the [network tree](/docs/NETWORK_TREE.md). Currently only free [Font Awesome](https://fontawesome.com/search?o=r&m=free) icons (up-to v 6.4.0) are supported (I have an unblockable [sponsorship goal](https://github.com/sponsors/jokob-sk) to add the material design icon pack).
![Raspberry Pi with a brand icon](/docs/img/ICONS/devices-icons.png)
## ⚙ How to use custom device Icons
You can assign icons individually on each device in the Details tab.
![Raspberry Pi device details](/docs/img/ICONS/device-icon.png)
- You can click into the `Icon` field or click the Pencil (2) icon in the above screenshot to enter any text. Only [free Font Awesome](https://fontawesome.com/search?o=r&m=free) icons in the following format will work:
1. For any value that is only prefixed with `fa-`, you can enter the value directly, such as `server`, `tv`, `ethernet`.
2. If you want to add another classname, e.g. `fa-brands`, you can enter `brands fa-[fontawesome-icon-name]`, so for `apple` that is using the syntax`fa-brands fa-apple`, you would enter `brands fa-apple`.
- If you want to mass-apply an icon to all devices of the same device type (Field marked (4) in the above screenshot), you can click the copy button (Marked (1) in the above screenshot). A confirmation prompt is displayed. If you proceed, icons of all devices set to the same device type as the current device, will be overwritten with the current device's icon.
- The dropdown (3) contains all icons already used in the app for device icons. You need to navigate away or refresh the page once you add a new icon.
## 🌟 Pro Font Awesome icons
If you own the premium package of Font Awesome icons you can mount it in your Docker container the following way:
```yaml
/font-awesome:/home/pi/pialert/front/lib/AdminLTE/bower_components/font-awesome:ro
```
You can use the full range of Font Awesome icons afterwards.

View File

@@ -1,8 +1,19 @@
## How to setup your Network page
Make sure you have a root device with the MAC `Internet` (No other MAC addresses are currently support as root)
Make sure you have a root device with the MAC `Internet` (No other MAC addresses are currently supported as the root node).
To setup a device named `rapberrypi` as a `Switch` in our network.
## ⚡Quick setup:
* Go to Devices > Device Details.
* Find the device(s) you want to use as network devices (network nodes).
* Set the Type of such a device to one of the following: AP, Firewall, Gateway, PLC, Powerline, Router, Switch, USB LAN Adapter, USB WIFI Adapter and WLAN.
* Save and go to Network where the devices you've marked as network devices (by selecting the Type as mentioned above) will show up as tabs.
* You can now assign the Unassigend devices to the correct network node.
## 🔍Detailed example:
In this example you will setup a device named `rapberrypi` as a `Switch` in our network.
### 1) Device details page
@@ -15,7 +26,7 @@ To setup a device named `rapberrypi` as a `Switch` in our network.
> Note: Only the following device types will show up as selectable Network nodes ( = devices you can connect other devices to):
> AP, Firewall, Gateway, PLC, Powerline, Router, Switch, USB LAN Adapter, USB WIFI Adapter and WLAN.
- Assign a device to your root device from the `Node` (5) dropdown whitch has the MAC `Internet` (6) (Your name may differ, but the MAC needs to be set to `Internet` - this is done by default).
- Assign a device to your root device from the `Node` (5) dropdown which has the MAC `Internet` (6) (Your name may differ, but the MAC needs to be set to `Internet` - this is done by default).
- Save your changes (7)

102
docs/README.md Executable file
View File

@@ -0,0 +1,102 @@
## Documentation overview
In the app hover-over settings or fields/labels or click blue in-app ❔ (question-mark) icons to get to relevant documentation pages.
![In-app help](/docs/img/GENERAL/in-app-help.png)
There is also an in-app Help / FAQ section that should be answering frequently asked questions.
### 📥 Installation
⚠ Only tested as a [docker container - follow these instructions here](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md).
> Check out [leiweibau's fork](https://github.com/leiweibau/Pi.Alert/) if you want to install Pi.Alert on the server directly or original instructions for [pucherot's original code](https://github.com/pucherot/Pi.Alert/)
### 📚 Table of contents
#### Popular/Suggested
- [API endpoints details](/docs/API.md)
- [Plugin system details and how to develop your own](/front/plugins/README.md)
- [Network tree map configuration](/docs/NETWORK_TREE.md)
- [Gmail as SMTP server for sending emails](/docs/SMTP_GMAIL.md)
- [Subnets and vlans configuration for arp-scan](/docs/SUBNETS.md)
#### System Management
- [Manage devices (legacy docs)](/docs/DEVICE_MANAGEMENT.md)
- [Random MAC/MAC icon meaning (legacy docs)](/docs/RANDOM_MAC.md)
- [Custom Icons configuration and support](/docs/ICONS.md)
#### Examples
- [N8N webhook example](/docs/WEBHOOK_N8N.md)
#### Misc
- [New Version notifications](/docs/VERSIONS.md)
- [Version history (legacy)](/docs/VERSIONS_HISTORY.md)
- [Invalid JSON errors debug help](/docs/DEBUG_INVALID_JSON.md)
- [Database structure](/docs/DATABASE.md)
Feel free to suggest or submit new docs via a PR.
## 👨‍💻 Development priorities
Highest to lowest:
* Fixing core functionality bugs not solvable with workarounds
* New core functionality unlocking other opportunities (e.g.: plugins)
* Refactoring enabling faster implementation of future functionality
* UI improvements
Design philosophy: Focus on core functionality and leverage existing apps and tools to make PiAlert integratable into other workflows.
Examples:
1. Supporting apprise makes more sense than implementing multiple individual notification gateways
2. Implementing regular expressions support across settings for validation makes more sense than validating one setting with a specific expression.
UI specific requests are low priority as the framework picked by the original developer is not very extensible (and afaik doesn't support components) and has limited mobile support. Also I argue the value proposition is smaller than working on something else.
Feel free to submit PRs if interested. try to **keep the PRs small/on topic** so they are easier to review and approve.
That being said, I'd reconsider if more people and or recurring sponsors file a request 😉.
## 🙏 Feature requests
Please be as detailed as possible with **workarounds** you considered and why a native feature is the better way. This gives me better context and will make it more likely to be implemented. Ideally a feature request should be in the format "I want to be able to do XYZ so that ZYX. I considered these approaches XYZ".
## Pull-requests (PRs)
If you submit a PR please:
1. Check that your changes are backward compatible with existing installations and with a blank setup.
2. Existing features should always be preserved.
3. Keep the PR small, on-topic and don't change code that is not necessary for the PR to work
4. New features code should ideally be re-usable for different purposes, not be for a very narrow use-case.
5. New functionality should ideally be implemented via the Plugins system, if possible.
Suggested test cases:
- Blank setup with no DB or config
- Existing DB / config
- Sending a notification (e. g. Delete a device and wait for a scan to run) and testing all notification gateways, especially:
- Email, Apprise (e.g. via Telegram), webhook (e.g. via Discord), MQTT (e.g. via HomeAssitant)
- Saving settings
- Test a couple of plugins
- Check the Error log for anything unusual
Some additional context:
* Permanent settings/config is stored in the `pialert.conf` file
* Currently temporary (session?) settings are stored in the `Parameters` DB table as key - value pairs. This table is wiped during a container rebuild/restart and it's values re-initialized from cookies / session data from the browser.
## 🐛 Submitting an issue or bug
Before submitting a new issue please spend a couple of minutes on research:
* Check [🛑 Common issues](https://github.com/jokob-sk/Pi.Alert/tree/main/dockerfiles#-common-issues)
* Check [💡 Closed issues](https://github.com/jokob-sk/Pi.Alert/issues?q=is%3Aissue+is%3Aclosed) if a similar issue was solved in the past.
⚠ Please follow the pre-defined issue template to resolve your issue faster.

21
docs/SUBNETS.md Executable file
View File

@@ -0,0 +1,21 @@
## Subnets configuration
The arp-scan time itself depends on the number of IP addresses to check.
The number of IPs to check depends on the [network mask](https://www.calculator.net/ip-subnet-calculator.html) you set on the `SCAN_SUBNETS` setting.
For example, a `/24` mask results in 256 IPs to check, where as a `/16` mask checks around 65,536. Every IP takes a couple seconds. This means that with an incorrect configuration the arp-scan will take hours to complete instead of seconds.
- Specify the network mask. For example, the filter `192.168.1.0/24` covers IP ranges 192.168.1.0 to 192.168.1.255
- Run `iwconfig` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`).
- Append e.g.: ` -vlan=107` to the interface field (e.g.: `eth0 -vlan=107`) for multiple vlans. More details in this [comment in this issue](https://github.com/jokob-sk/Pi.Alert/issues/170#issuecomment-1419902988)
### 🔍Example:
![Vlan configuration example](/docs/img/SUBNETS/subnets_vlan.png)
### Support for VLANS
Please note about the accessibility of the macvlans when they are configured on the same computer. My understanding this is a general networking behavior, but feel free to clarify via a PR/issue.
- Pi.Alert does not detect the macvlan container when it is running on the same computer.
- Pi.Alert recognizes the macvlan container when it is running on a different computer.

25
docs/VERSIONS.md Executable file
View File

@@ -0,0 +1,25 @@
## Am I running the latest released version?
Since version 23.01.14 PiAlert uses a simple timestamp-based version check to verify if a new version is available. You can check the [current and past releases here](https://github.com/jokob-sk/Pi.Alert/releases), or have a look at what I'm [currently working on](https://github.com/jokob-sk/Pi.Alert/issues/138).
If you are not on the latest version, the app will notify you, that a new released version is avialable the following way:
### 📧 Via email on a notification event
If any notification occurs and an email is sent, the email will contain a note that a new version is available. See the sample email below:
![Sample email if a new version is available](/docs/img/VERSIONS/new-version-available-email.png)
### 🆕 In the UI
In the UI via a notification Icon and via a custom message in the Maintenance section.
![UI screenshot if a new version is available](/docs/img/VERSIONS/new-version-available-maintenance.png)
For a comparison, this is how the UI looks like if you are on the latest stable image:
![UI screenshot if on latest version](/docs/img/VERSIONS/latest-version-maintenance.png)
## Implementation details
During build a [/home/pi/pialert/front/buildtimestamp.txt](https://github.com/jokob-sk/Pi.Alert/blob/092797e75ccfa8359444ad149e727358ac4da05f/Dockerfile#L44) file is created. The app then periodically checks if a new release is available with a newer timestamp in GitHub's rest-based JSON endpoint (check the `def isNewVersion():` method in `pialert.py` for details).

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/img/DATABASE/CurrentScan.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
docs/img/DATABASE/DHCP_Leases.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
docs/img/DATABASE/Devices.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
docs/img/DATABASE/Events.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
docs/img/DATABASE/Nmap_Scan.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
docs/img/DATABASE/Parameters.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
docs/img/DATABASE/Pholus_Scan.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
docs/img/DATABASE/ScanCycles.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/img/DATABASE/Sessions.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
docs/img/DATABASE/Settings.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
docs/img/GENERAL/in-app-help.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
docs/img/ICONS/device-icon.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
docs/img/ICONS/devices-icons.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/img/SUBNETS/subnets_vlan.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
docs/img/device_nmap.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

BIN
docs/img/plugins.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

BIN
docs/img/plugins_json_ui.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

BIN
docs/img/plugins_settings.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
docs/img/plugins_webmon.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

2
front/api/.gitignore vendored Executable file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

0
front/buildtimestamp.txt Executable file
View File

View File

@@ -591,7 +591,14 @@ height: 50px;
}
.nav-tabs-custom .tab-content {
background-color: white;
background-color: white;
}
@media (max-width: 767px) {
.nav-tabs-custom .tab-content {
overflow: scroll;
}
}
.top_small_box_gray_text {
@@ -623,20 +630,21 @@ height: 50px;
width:30%;
}
}
@media (min-width: 768px) {
.setting_description {
/* color: green; */
display: block;
}
.setting_input{
width:35%;
/* background-color: green; */
}
.setting_name
{
width:19%;
}
}
@media (min-width: 768px) {
.setting_description {
/* color: green; */
display: block;
}
.setting_input{
width:40%;
/* background-color: green; */
}
.setting_name
{
width:19%;
}
}
.table_row {
padding: 3px;
@@ -663,7 +671,7 @@ height: 50px;
.setting_description
{
width:46%;
width:40%;
}
.myhidden
@@ -747,6 +755,13 @@ height: 50px;
margin-bottom: 6px;
}
.pageHelp{
position: absolute;
font-size: x-small;
margin-bottom: 6px;
}
#networkTree .box
{
padding:2px;
@@ -799,5 +814,28 @@ height: 50px;
text-overflow: ellipsis;
}
.plugin-content
{
padding-bottom: 7px;
}
.plugin-content #tabs-content-location
{
margin-top: 20px;
margin-left: 7px;
margin-right: 7px;
margin-bottom: 9px;
padding-bottom: 8px;
}
.plugins-description
{
padding-top: 20px;
}
.login-page .login-custom
{
width:480px;
}

View File

@@ -194,7 +194,7 @@
<div class="form-group">
<label class="col-sm-3 control-label">
<?= lang('DevDetail_Icon');?>
<a href="https://fontawesome.com/search?q=laptop&o=r&m=free" target="_blank"> <span><i class="fa fa-fw fa-arrow-up-right-from-square"></i></a><span>
<a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/ICONS.md" target="_blank"> <span><i class="fa fa-circle-question"></i></a><span>
</label>
<div class="col-sm-9">
<div class="input-group">
@@ -441,9 +441,32 @@
<i class="fa fa-info-circle"></i> </a>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12">
<h4 class="bottom-border-aqua"><?= lang('DevDetail_Run_Actions_Title');?></h4>
<div class="box-body form-horizontal">
<label class="col-sm-3 control-label">
<?= lang('Gen_Action');?>
</label>
<div class="col-sm-9">
<div class="input-group">
<input class="form-control" title="<?= lang('DevDetail_Run_Actions_Tooltip');?>" id="txtAction" type="text" value="--">
<span class="input-group-addon" title='<?= lang('Gen_Run');?>'><i class="fa fa-play pointer" onclick="askRunAction();"></i></span>
<div class="input-group-btn">
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="fa fa-caret-down"></span>
</button>
<ul id="dropdownAction" class="dropdown-menu dropdown-menu-right">
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Buttons -->
<div class="col-xs-12">
@@ -924,7 +947,8 @@ function initializeCombos () {
initializeCombo ( '#dropdownGroup', 'getGroups', 'txtGroup', true);
initializeCombo ( '#dropdownLocation', 'getLocations', 'txtLocation', true);
initializeCombo ( '#dropdownNetworkNodeMac', 'getNetworkNodes', 'txtNetworkNodeMac', false);
initializeCombo ( '#dropdownIcon', 'getIcons', 'txtIcon', false);
initializeCombo ( '#dropdownIcon', 'getIcons', 'txtIcon', false);
initializeCombo ( '#dropdownAction', 'getActions', 'txtAction', false);
// Initialize static combos
initializeComboSkipRepeated ();
@@ -1547,8 +1571,6 @@ function setDeviceData (direction='', refreshCallback='') {
});
}
// -----------------------------------------------------------------------------
function askSkipNotifications () {
// Check MAC
@@ -1601,6 +1623,39 @@ function deleteDeviceEvents () {
$('#panDetails :input').attr('disabled', true);
}
// -----------------------------------------------------------------------------
function askRunAction() {
// Ask
showModalWarning('<?= lang('BackDevDetail_Actions_Title_Run');?>', '<?= lang('BackDevDetail_Actions_Ask_Run');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Run');?>', 'runAction');
}
function runAction() {
action = $('#txtAction').val();
switch(action)
{
case 'wake-on-lan':
wakeonlan();
break;
default:
showMessage (`<?= lang('BackDevDetail_Actions_Not_Registered');?> ${action} `);
break;
}
}
function wakeonlan() {
// Execute
$.get('php/server/devices.php?action=wakeonlan&'
+ '&mac=' + $('#txtMAC').val()
+ '&ip=' + $('#txtLastIP').val()
, function(msg) {
showMessage (msg);
});
}
// -----------------------------------------------------------------------------
// Overwrite all devices of the same type with the currently selected icon
function askOverwriteIconType () {
@@ -1623,7 +1678,7 @@ function overwriteIconType () {
var icon = $('#txtIcon').val();
// Delete device events
// Mass update icons
$.get('php/server/devices.php?action=overwriteIconType&mac='+ mac + '&icon=' + icon, function(msg) {
showMessage (msg);
});
@@ -1775,7 +1830,7 @@ function initializeTabsNew () {
function loadNmap()
{
$(".deviceSpecific").remove(); // remove any previous data listed in teh table
$(".deviceSpecific").remove(); // remove any previous data listed in the table
$.get('php/server/devices.php?action=getNmap&mac='+ mac, function(data) {

View File

@@ -225,6 +225,8 @@ function main () {
// save the columns order in the Devices page
tableColumnOrder = numberArrayFromString(data);
//initialize the table headers in the correct order
var headersDefaultOrder = [ '<?= lang('Device_TableHead_Name');?>',
'<?= lang('Device_TableHead_Owner');?>',
@@ -308,21 +310,13 @@ function mapIndx(oldIndex)
function initializeDatatable () {
for(i = 0; i < tableColumnOrder.length; i++)
{
// hide this column if not in the tableColumnVisible variable
// hide this column if not in the tableColumnVisible variable (we need to keep the MAC address (index 11) for functionality reasons)
if(tableColumnVisible.includes(tableColumnOrder[i]) == false)
{
tableColumnHide.push(mapIndx(tableColumnOrder[i]));
}
}
// If the device has a small width (mobile) only show name, ip, and status columns.
if (window.screen.width < 400) {
tableColumnHide = [11,12,13,1,2,4,5,6,7,9];
}
// else {
// // var tableColumnHide = [11, 12, 13];
// tableColumnHide = [11, 12, 13];
// };
var table=
$('#tableDevices').DataTable({
'paging' : true,
@@ -348,7 +342,7 @@ function initializeDatatable () {
// Device Name
{targets: [mapIndx(0)],
'createdCell': function (td, cellData, rowData, row, col) {
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html ('<b class="anonymizeDev"><a href="deviceDetails.php?mac='+ rowData[mapIndx(11)] +'" class="">'+ cellData +'</a></b>');
} },

View File

@@ -1,29 +1,43 @@
<!-- Pi.Alert CSS -->
<link rel="stylesheet" href="css/pialert.css">
<?php
require dirname(__FILE__).'/php/server/init.php';
require 'php/templates/security.php';
if ($Pia_WebProtection != 'true')
{
header('Location: devices.php');
$_SESSION["login"] = 1;
exit;
}
{
header('Location: devices.php');
$_SESSION["login"] = 1;
exit;
}
// Logout
if (isset ($_GET["action"]) && $_GET["action"] == 'logout')
{
setcookie("PiAlert_SaveLogin", '', time()+1); // reset cookie
$_SESSION["login"] = 0;
header('Location: index.php');
exit;
}
// Password without Cookie check -> pass and set initial cookie
if (isset ($_POST["loginpassword"]) && $Pia_Password == hash('sha256',$_POST["loginpassword"]))
{
header('Location: devices.php');
$_SESSION["login"] = 1;
if (isset($_POST['PWRemember'])) {setcookie("PiAlert_SaveLogin", hash('sha256',$_POST["loginpassword"]), time()+604800);}
}
{
header('Location: devices.php');
$_SESSION["login"] = 1;
if (isset($_POST['PWRemember'])) {setcookie("PiAlert_SaveLogin", hash('sha256',$_POST["loginpassword"]), time()+604800);}
}
// active Session or valid cookie (cookie not extends)
if (( isset ($_SESSION["login"]) && ($_SESSION["login"] == 1)) || (isset ($_COOKIE["PiAlert_SaveLogin"]) && $Pia_Password == $_COOKIE["PiAlert_SaveLogin"]))
{
header('Location: devices.php');
$_SESSION["login"] = 1;
if (isset($_POST['PWRemember'])) {setcookie("PiAlert_SaveLogin", hash('sha256',$_POST["loginpassword"]), time()+604800);}
}
{
header('Location: devices.php');
$_SESSION["login"] = 1;
if (isset($_POST['PWRemember'])) {setcookie("PiAlert_SaveLogin", hash('sha256',$_POST["loginpassword"]), time()+604800);}
}
$login_headline = lang('Login_Toggle_Info_headline');
$login_info = "";
@@ -33,22 +47,22 @@ $login_icon = 'fa-info';
// no active session, cookie not checked
if (isset ($_SESSION["login"]) == FALSE || $_SESSION["login"] != 1)
{
if ($Pia_Password == '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92')
{
if ($Pia_Password == '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92')
{
$login_info = lang('Login_Default_PWD');
$login_mode = 'danger';
$login_display_mode = 'display: block;';
$login_headline = lang('Login_Toggle_Alert_headline');
$login_icon = 'fa-ban';
}
else
{
$login_mode = 'info';
$login_display_mode = 'display: none;';
$login_headline = lang('Login_Toggle_Info_headline');
$login_icon = 'fa-info';
}
$login_info = lang('Login_Default_PWD');
$login_mode = 'danger';
$login_display_mode = 'display: block;';
$login_headline = lang('Login_Toggle_Alert_headline');
$login_icon = 'fa-ban';
}
else
{
$login_mode = 'info';
$login_display_mode = 'display: none;';
$login_headline = lang('Login_Toggle_Info_headline');
$login_icon = 'fa-info';
}
}
// ##################################################
@@ -89,7 +103,7 @@ if ($ENABLED_DARKMODE === True) {
<link rel="stylesheet" href="/front/css/offline-font.css">
</head>
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-box login-custom">
<div class="login-logo">
<a href="/index2.php">Pi.<b>Alert</b></a>
</div>
@@ -134,7 +148,7 @@ if ($ENABLED_DARKMODE === True) {
<button type="button" class="close" data-dismiss="alert" aria-hidden="true"><3E></button>
<h4><i class="icon fa <?php echo $login_icon;?>"></i><?php echo $login_headline;?></h4>
<p><?php echo $login_info;?></p>
<p><?= lang('Login_Psw_run');?><br><span style="border: solid 1px yellow; padding: 2px;">./reset_password.sh <?= lang('Login_Psw_new');?></span><br><?= lang('Login_Psw_folder');?></p>
<p><?= lang('Login_Psw_run');?><br><span style="border: solid 1px yellow; padding: 2px;"> /home/pi/pialert/back/pialert-cli set_password <?= lang('Login_Psw_new');?></span><br><?= lang('Login_Psw_folder');?></p>
</div>
</div>

View File

@@ -345,4 +345,30 @@ function openInNewTab (url) {
}
// -----------------------------------------------------------------------------
function navigateToDeviceWithIp (ip) {
$.get('api/table_devices.json', function(res) {
devices = res["data"];
mac = ""
$.each(devices, function(index, obj) {
if(obj.dev_LastIP.trim() == ip.trim())
{
mac = obj.dev_MAC;
window.open(window.location.origin +'/deviceDetails.php?mac=' + mac , "_blank");
}
});
});
}

View File

@@ -131,8 +131,11 @@ if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
</div>
<div class="box-body" style="padding-bottom: 5px;">
<div class="db_info_table">
<div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_version');?></div>
<div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_version');?>
<a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/VERSIONS.md" target="_blank"> <span><i class="fa fa-circle-question"></i></a><span>
</div>
<div class="db_info_table_cell">
<div class="version" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>"><?php echo '<span id="new-version-text" class="myhidden">' .lang('Maintenance_new_version').'</span>'.'<span id="current-version-text" class="myhidden">' .lang('Maintenance_current_version').'</span>';?></div>
</div>
@@ -244,7 +247,7 @@ if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
<option value="16"><?= lang('Device_TableHead_Location');?></option>
<option value="17"><?= lang('Device_TableHead_Vendor');?></option>
</select>
<span class="input-group-addon"><i title="<?= lang('DevDetail_GoToNetworkNode');?>" class="fa fa-save pointer" onclick="saveSelectedColumns();"></i></span>
<span class="input-group-addon"><i title="<?= lang('Gen_Save');?>" class="fa fa-save pointer" onclick="saveSelectedColumns();"></i></span>
</div>
</div>
@@ -335,7 +338,7 @@ if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
<div class="db_info_table">
<div class="log-area">
<div class="row logs-row">
<textarea id="pialert_log" class="logs" cols="70" rows="10" wrap='off' readonly ><?php echo file_get_contents( "./log/pialert.log" ); ?>
<textarea id="pialert_log" class="logs" cols="70" rows="10" wrap='off' readonly ><?php echo file_get_contents( "./log/pialert.log", false, null, -200000); ?>
</textarea>
</div>
<div class="row logs-row" >
@@ -364,25 +367,24 @@ if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
</div>
</div>
</div>
</div>
</div>
<div class="log-area">
<div class="row logs-row">
<textarea id="pialert_pholus_log" class="logs" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/pialert_pholus.log" ); ?>
</textarea>
</div>
<div class="row logs-row" >
<div>
<div class="log-file">pialert_pholus.log<div class="logs-size"><?php echo number_format((filesize("./log/pialert_pholus.log") / 1000000),2,",",".") . ' MB';?>
<span class="span-padding"><a href="./log/pialert_pholus.log"><i class="fa fa-download"></i> </a></span>
</div></div>
<div class="log-purge">
<button class="btn btn-primary" onclick="logManage('pialert_pholus.log','cleanLog')"><?= lang('Gen_Purge');?></button>
</div>
</div>
</div>
</div>
<div class="row logs-row">
<textarea id="pialert_php_log" class="logs" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/pialert.php_errors.log" ); ?>
</textarea>
</div>
<div class="row logs-row" >
<div>
<div class="log-file">pialert.php_errors.log<div class="logs-size"><?php echo number_format((filesize("./log/pialert.php_errors.log") / 1000000),2,",",".") . ' MB';?>
<span class="span-padding"><a href="./log/pialert.php_errors.log"><i class="fa fa-download"></i> </a></span>
</div></div>
<div class="log-purge">
<button class="btn btn-primary" onclick="logManage('pialert.php_errors.log','cleanLog')"><?= lang('Gen_Purge');?></button>
</div>
</div>
</div>
</div>
<div class="log-area">
<div class="row logs-row">
@@ -720,12 +722,16 @@ function performLogManage() {
// --------------------------------------------------------
function scrollDown()
{
var areaIDs = ['pialert_log', 'pialert_front_log', 'IP_changes_log', 'stdout_log', 'stderr_log', 'pialert_pholus_log', 'pialert_pholus_lastrun_log'];
var areaIDs = ['pialert_log', 'pialert_front_log', 'IP_changes_log', 'stdout_log', 'stderr_log', 'pialert_pholus_log', 'pialert_pholus_lastrun_log', 'pialert_php_log'];
for (let i = 0; i < areaIDs.length; i++) {
var tempArea = $('#' + areaIDs[i]);
$(tempArea[0]).scrollTop(tempArea[0].scrollHeight);
if (tempArea.length > 0)
{
$(tempArea[0]).scrollTop(tempArea[0].scrollHeight);
}
}
}
@@ -733,8 +739,8 @@ function scrollDown()
// --------------------------------------------------------
// Manage displayed columns
// --------------------------------------------------------
colDefaultOrder = ['0','1','2','3','4','5','6','7','8','9','10','12','13','14','15','16','17'];
colDefaultOrderTxt = '[0,1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17]';
colDefaultOrder = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17'];
colDefaultOrderTxt = '[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]';
function saveSelectedColumns () {
@@ -742,9 +748,7 @@ function saveSelectedColumns () {
// save full order of all columns to simplify mapping later on
colDisplayed = $('#columnsSelect').val();
colNewOrder = colDisplayed;
// append the remaining columns in the previous order

View File

@@ -620,7 +620,7 @@
nodeHeight = ((emSize*100*0.30).toFixed(0))
$("#networkTree").attr('style', "height:"+treeAreaHeight+"px; width:1070px")
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${$('.content-header').width()}px`)
myTree = Treeviz.create({
htmlId: "networkTree",

193
front/php/server/dbHelper.php Executable file
View File

@@ -0,0 +1,193 @@
<?php
//------------------------------------------------------------------------------
// Pi.Alert
// Open Source Network Guard / WIFI & LAN intrusion detector
//
// parameters.php - Front module. Server side. Manage Parameters
//------------------------------------------------------------------------------
# Puche 2022+ jokob jokob@duck.com GNU GPLv3
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// External files
require dirname(__FILE__).'/init.php';
//------------------------------------------------------------------------------
// Action selector
//------------------------------------------------------------------------------
// Set maximum execution time to 15 seconds
ini_set ('max_execution_time','15');
$skipCache = FALSE;
$expireMinutes = 5;
$defaultValue = '';
$dbtable = '';
$columns = '';
$values = '';
if (isset ($_REQUEST['skipcache'])) {
$skipCache = TRUE;
}
if (isset ($_REQUEST['defaultValue'])) {
$defaultValue = $_REQUEST['defaultValue'];
}
if (isset ($_REQUEST['expireMinutes'])) {
$expireMinutes = $_REQUEST['expireMinutes'];
}
if (isset ($_REQUEST['columnName'])) {
$columnName = $_REQUEST['columnName'];
}
if (isset ($_REQUEST['id'])) {
$id = $_REQUEST['id'];
}
if (isset ($_REQUEST['values'])) {
$values = $_REQUEST['values'];
}
if (isset ($_REQUEST['columns'])) {
$columns = $_REQUEST['columns'];
}
if (isset ($_REQUEST['dbtable'])) {
$dbtable = $_REQUEST['dbtable'];
}
// TODO: Security, read, delete, edge cases
// Action functions
if (isset ($_REQUEST['action']) && !empty ($_REQUEST['action'])) {
$action = $_REQUEST['action'];
switch ($action) {
case 'create': create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values ); break;
// case 'read' : read($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
case 'update': update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
case 'delete': delete($columnName, $id, $dbtable); break;
default: logServerConsole ('Action: '. $action); break;
}
}
//------------------------------------------------------------------------------
// update
//------------------------------------------------------------------------------
function update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
global $db;
// handle one or multiple columns
if(strpos($columns, ',') !== false)
{
$columnsArr = explode(",", $columns);
}else
{
$columnsArr = array($columns);
}
// handle one or multiple values
if(strpos($values, ',') !== false)
{
$valuesArr = explode(",", $values);
} else
{
$valuesArr = array($values);
}
$columnValues = '';
$index = 0;
foreach($columnsArr as $column)
{
$columnValues = $columnValues .' "' .$column.'" = "'.$valuesArr[$index] . '",' ;
$index = $index + 1;
}
$columnValues = substr($columnValues, 0, -1);
// Update value
$sql = 'UPDATE '.$dbtable.' SET '. $columnValues .'
WHERE "'. $columnName .'"="'. $id.'"';
$result = $db->query($sql);
if (! $result == TRUE) {
echo "Error updating parameter\n\n$sql \n\n". $db->lastErrorMsg();
return;
}
$changes = $db->changes();
if ($changes == 0) {
// Insert new value
create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values);
}
// update cache
$uniqueHash = hash('ripemd160', $dbtable . $columns);
setCache($uniqueHash, $values, $expireMinutes);
echo 'OK';
}
//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
function create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values)
{
global $db;
// Insert new value
$sql = 'INSERT INTO '.$dbtable.' ('.$columns.')
VALUES ("'. quotes($parameter) .'",
"'. $values .'")';
$result = $db->query($sql);
if (! $result == TRUE) {
echo "Error creating entry\n\n$sql \n\n". $db->lastErrorMsg();
return;
}
}
//------------------------------------------------------------------------------
// delete
//------------------------------------------------------------------------------
function delete($columnName, $id, $dbtable)
{
global $db;
// handle one or multiple ids
if(strpos($id, ',') !== false)
{
$idsArr = explode(",", $id);
}else
{
$idsArr = array($id);
}
$idsStr = "";
foreach ($idsArr as $item)
{
$idsStr = $idsStr . '"' .$item.'"';
}
// Insert new value
$sql = 'DELETE FROM '.$dbtable.' WHERE "'.$columnName.'" IN ('. $idsStr .')';
$result = $db->query($sql);
if (! $result == TRUE) {
echo "Error deleting entry\n\n$sql \n\n". $db->lastErrorMsg();
return;
} else
{
echo lang('Gen_DataUpdatedUITakesTime');
return;
}
}
?>

View File

@@ -11,10 +11,11 @@
// External files
require dirname(__FILE__).'/init.php';
//------------------------------------------------------------------------------
// Action selector
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Action selector
//------------------------------------------------------------------------------
// Set maximum execution time to 15 seconds
ini_set ('max_execution_time','30');
// Action functions
@@ -56,6 +57,8 @@
case 'updateNetworkLeaf': updateNetworkLeaf(); break;
case 'overwriteIconType': overwriteIconType(); break;
case 'getIcons': getIcons(); break;
case 'getActions': getActions(); break;
case 'wakeonlan': wakeonlan(); break;
default: logServerConsole ('Action: '. $action); break;
}
@@ -478,34 +481,41 @@ function ImportCSV() {
// sql
$sql = 'DELETE FROM Devices';
// execute sql
$result = $db->query($sql);
// Open the CSV file with read-only mode
$csvFile = fopen($file, 'r');
// Skip the first line
fgetcsv($csvFile);
$data = file_get_contents($file);
$data = explode("\n", $data);
$columns = getDevicesColumns();
// Parse data from CSV file line by line (max 10000 lines)
while (($row = fgetcsv($csvFile, 10000, ",")) !== FALSE)
// Parse data from CSV file line by line (max 10000 lines)
foreach($data as $row)
{
$sql = 'INSERT INTO Devices ('.implode(',', $columns).') VALUES ("' .implode('","', $row).'")';
// Check if not empty and skipping first line
$rowArray = explode(',',$row);
$result = $db->query($sql);
// check result
if ($result != TRUE) {
$error = $db->lastErrorMsg();
// break the while loop on error
break;
}
if(count($rowArray) > 20)
{
$cleanMac = str_replace("\"","",$rowArray[0]);
if(filter_var($cleanMac , FILTER_VALIDATE_MAC) == True || $cleanMac == "Internet")
{
$sql = "INSERT INTO Devices (".implode(',', $columns).") VALUES (" . $row.")";
$result = $db->query($sql);
// check result
if ($result != TRUE) {
$error = $db->lastErrorMsg();
// break the while loop on error
break;
}
}
}
}
// Close opened CSV file
fclose($csvFile);
if($error == "")
{
@@ -515,7 +525,7 @@ function ImportCSV() {
}
else{
// an error occurred while writing to the DB, display the last error message
echo lang('BackDevices_DBTools_ImportCSVError')."\n\n$sql \n\n".$error;
echo lang('BackDevices_DBTools_ImportCSVError')."\n\n$sql \n\n".$result;
}
} else {
@@ -560,72 +570,6 @@ function getDevicesTotals() {
}
//------------------------------------------------------------------------------
// Query the List of devices in a determined Status
//------------------------------------------------------------------------------
// function getDevicesListForNetworkTree() {
// global $db;
// $sql = 'SELECT *, CASE
// WHEN t1.dev_AlertDeviceDown=1 AND t1.dev_PresentLastScan=0 THEN "Down"
// WHEN t1.dev_NewDevice=1 THEN "New"
// WHEN t1.dev_PresentLastScan=1 THEN "On-line"
// ELSE "Off-line" END AS dev_Status
// FROM (Devices ) t1
// LEFT JOIN
// (
// SELECT *,
// count() as connected_devices
// FROM Devices b
// WHERE b.dev_Network_Node_MAC_ADDR NOT NULL group by b.dev_Network_Node_MAC_ADDR
// ) t2
// ON (t1.dev_MAC = t2.dev_MAC); ';
// $result = $db->query($sql);
// // arrays of rows
// $tableData = array();
// while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// $defaultOrder = array ($row['dev_Name'],
// $row['dev_Owner'],
// handleNull($row['dev_DeviceType']),
// handleNull($row['dev_Icon'], "laptop"),
// $row['dev_Favorite'],
// $row['dev_Group'],
// formatDate ($row['dev_FirstConnection']),
// formatDate ($row['dev_LastConnection']),
// $row['dev_LastIP'],
// ( in_array($row['dev_MAC'][1], array("2","6","A","E","a","e")) ? 1 : 0),
// $row['dev_Status'],
// $row['dev_MAC'], // MAC (hidden)
// formatIPlong ($row['dev_LastIP']), // IP orderable
// $row['rowid'], // Rowid (hidden)
// handleNull($row['dev_Network_Node_MAC_ADDR']), //
// handleNull($row['connected_devices']) //
// );
// $newOrder = array();
// // reorder columns based on user settings
// for($index = 0; $index < count($columnOrderMapping); $index++)
// {
// array_push($newOrder, $defaultOrder[$columnOrderMapping[$index][2]]);
// }
// $tableData['data'][] = $newOrder;
// }
// // Control no rows
// if (empty($tableData['data'])) {
// $tableData['data'] = '';
// }
// // Return json
// echo (json_encode ($tableData));
// }
//------------------------------------------------------------------------------
// Query the List of devices in a determined Status
//------------------------------------------------------------------------------
@@ -639,7 +583,7 @@ function getDevicesList() {
$forceDefaultOrder = TRUE;
}
// This object is used to map from the old order ( second parameter, first number) to the 3rd parameter (Second number (here initialized to -1))
// This object is used to map from the old order ( second parameter, first number) to the new mapping, that is represented by the 3rd parameter (Second number)
$columnOrderMapping = array(
array("dev_Name", 0, 0),
array("dev_Owner", 1, 1),
@@ -674,9 +618,11 @@ function getDevicesList() {
$orderedColumns = createArray($row[0]);
// init ordered columns
for($i = 0; $i < count($orderedColumns); $i++) {
$columnOrderMapping[$i][2] = $orderedColumns[$i];
for($i = 0; $i < count($orderedColumns); $i++) {
$columnOrderMapping[$i][2] = $orderedColumns[$i];
}
}
}
@@ -861,6 +807,18 @@ function getIcons() {
echo (json_encode ($tableData));
}
//------------------------------------------------------------------------------
function getActions() {
$tableData = array(
array('id' => 'wake-on-lan',
'name' => lang('DevDetail_WOL_Title'))
);
// Return json
echo (json_encode ($tableData));
}
//------------------------------------------------------------------------------
// Query the List of types
@@ -1184,12 +1142,33 @@ function overwriteIconType()
if ($result == TRUE) {
echo 'OK';
} else {
echo 'KO';
echo lang('BackDevices_Device_UpdDevError');
}
}
}
//------------------------------------------------------------------------------
// Wake-on-LAN
// Inspired by @leiweibau: https://github.com/leiweibau/Pi.Alert/commit/30427c7fea180670c71a2b790699e5d9e9e88ffd
//------------------------------------------------------------------------------
function wakeonlan() {
$WOL_HOST_IP = $_REQUEST['ip'];
$WOL_HOST_MAC = $_REQUEST['mac'];
if (!filter_var($WOL_HOST_IP, FILTER_VALIDATE_IP)) {
echo "Invalid IP! ". lang('BackDevDetail_Tools_WOL_error'); exit;
}
elseif (!filter_var($WOL_HOST_MAC, FILTER_VALIDATE_MAC)) {
echo "Invalid MAC! ". lang('BackDevDetail_Tools_WOL_error'); exit;
}
exec('wakeonlan '.$WOL_HOST_MAC , $output);
echo lang('BackDevDetail_Tools_WOL_okay');
}
//------------------------------------------------------------------------------
// Status Where conditions
//------------------------------------------------------------------------------

View File

@@ -3,3 +3,4 @@ require dirname(__FILE__).'/../templates/timezone.php';
require dirname(__FILE__).'/db.php';
require dirname(__FILE__).'/util.php';
require dirname(__FILE__).'/../templates/language/lang.php';
?>

View File

@@ -35,7 +35,6 @@ elseif ($FUNCTION == 'cleanLog')
cleanLog($SETTINGS);
}
//------------------------------------------------------------------------------
// Formatting data functions
//------------------------------------------------------------------------------
@@ -203,7 +202,7 @@ function cleanLog($logFile)
$path = "";
$allowedFiles = ['pialert.log', 'pialert_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', "pialert_pholus.log", "pialert_pholus_lastrun.log"];
$allowedFiles = ['pialert.log', 'pialert_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', "pialert_pholus_lastrun.log"];
if(in_array($logFile, $allowedFiles))
{
@@ -275,28 +274,35 @@ function saveSettings()
{
if($group == $setting[0])
{
if($setting[3] == 'text' or $setting[3] == 'password' or $setting[3] == 'readonly' or $setting[3] == 'selecttext')
if($setting[2] == 'text' or $setting[2] == 'password' or $setting[2] == 'readonly' or $setting[2] == 'selecttext')
{
$txt = $txt.$setting[1]."='".$setting[2]."'\n" ;
} elseif($setting[3] == 'integer' or $setting[3] == 'selectinteger')
$val = encode_single_quotes($setting[3]);
$txt = $txt.$setting[1]."='".$val."'\n" ;
} elseif($setting[2] == 'integer' or $setting[2] == 'selectinteger')
{
$txt = $txt.$setting[1]."=".$setting[2]."\n" ;
} elseif($setting[3] == 'boolean')
$txt = $txt.$setting[1]."=".$setting[3]."\n" ;
} elseif($setting[2] == 'boolean')
{
$val = "False";
if($setting[2] == 'true')
if($setting[3] == 'true')
{
$val = "True";
}
$txt = $txt.$setting[1]."=".$val."\n" ;
}elseif($setting[3] == 'multiselect' or $setting[3] == 'subnets')
}elseif($setting[2] == 'multiselect' or $setting[2] == 'subnets' or $setting[2] == 'list')
{
$temp = '[';
foreach($setting[2] as $val)
{
$temp = $temp."'". $val."',";
$temp = '[';
if (count($setting) > 3 && is_array( $setting[3]) == True){
foreach($setting[3] as $val)
{
$temp = $temp."'". encode_single_quotes($val)."',";
}
$temp = substr_replace($temp, "", -1); // remove last comma ','
}
$temp = substr_replace($temp, "", -1).']'; // close brackets and remove last comma ','
$temp = $temp.']'; // close brackets
$txt = $txt.$setting[1]."=".$temp."\n" ;
}
}
@@ -322,8 +328,6 @@ function saveSettings()
}
// -------------------------------------------------------------------------------------------
function getString ($codeName, $default) {
$result = lang($codeName);
@@ -338,6 +342,16 @@ function getString ($codeName, $default) {
// -------------------------------------------------------------------------------------------
function encode_single_quotes ($val) {
$result = str_replace ('\'','{s-quote}',$val);
return $result;
}
// -------------------------------------------------------------------------------------------
function getDateFromPeriod () {
$period = $_REQUEST['period'];
return '"'. date ('Y-m-d', strtotime ('+1 day -'. $period) ) .'"';
@@ -403,7 +417,8 @@ function getDevicesColumns(){
"dev_Location",
"dev_Archived",
"dev_Network_Node_port",
"dev_Network_Node_MAC_ADDR"];
"dev_Network_Node_MAC_ADDR",
"dev_Icon"];
return $columns;
}
@@ -426,6 +441,4 @@ function setCache($key, $value, $expireMinutes = 5) {
}
?>
?>

View File

@@ -12,9 +12,38 @@
<!-- Default to the left -->
<!-- &copy; 2020 Puche -->
<?php
echo '<span style="display:inline-block; transform: rotate(180deg)">&copy;</span> 2020 Puche (2022+ <a href="mailto:jokob@duck.com?subject=PiAlert">jokob-sk</a>)';
?>
<span style="display:inline-block; transform: rotate(180deg)">&copy;</span>
2020 Puche (2022+ <a href="mailto:jokob@duck.com?subject=PiAlert">jokob-sk</a>) | <b>Built on: </b>
<?php
echo date("Y-m-d", ((int)file_get_contents( "buildtimestamp.txt")));
?>
| <b> Version: </b>
<?php
$filename = "/.VERSION";
if(file_exists($filename))
{
echo file_get_contents($filename);
}
else{
echo "File not found";
}
?>
|
<a href="https://github.com/jokob-sk/Pi.Alert/tree/main/docs" target="_blank">
<span>Docs <i class="fa fa-circle-question"></i>
</a><span>
<!-- To the right -->
<div class="pull-right no-hidden-xs">

View File

@@ -8,15 +8,43 @@ $Pia_Graph_Device_Online = array();
$Pia_Graph_Device_Down = array();
$Pia_Graph_Device_Arch = array();
$statusesToShow = "'online', 'offline', 'archived'";
$statQuery = $db->query("SELECT * FROM Settings WHERE Code_Name = 'UI_PRESENCE'");
while($r = $statQuery->fetchArray(SQLITE3_ASSOC))
{
$statusesToShow = $r['Value'];
}
$results = $db->query('SELECT * FROM Online_History ORDER BY Scan_Date DESC LIMIT 144');
while ($row = $results->fetchArray()) {
$time_raw = explode(' ', $row['Scan_Date']);
$time = explode(':', $time_raw[1]);
array_push($Pia_Graph_Device_Time, $time[0].':'.$time[1]);
array_push($Pia_Graph_Device_Down, $row['Down_Devices']);
array_push($Pia_Graph_Device_All, $row['All_Devices']);
array_push($Pia_Graph_Device_Online, $row['Online_Devices']);
array_push($Pia_Graph_Device_Arch, $row['Archived_Devices']);
while ($row = $results->fetchArray())
{
$time_raw = explode(' ', $row['Scan_Date']);
$time = explode(':', $time_raw[1]);
array_push($Pia_Graph_Device_Time, $time[0].':'.$time[1]);
// Offline
if(strpos($statusesToShow, 'offline') !== false)
{
array_push($Pia_Graph_Device_Down, $row['Down_Devices']);
}
// All
array_push($Pia_Graph_Device_All, $row['All_Devices']);
// Online
if(strpos($statusesToShow, 'online') !== false)
{
array_push($Pia_Graph_Device_Online, $row['Online_Devices']);
}
// Archived
if(strpos($statusesToShow, 'archived') !== false)
{
array_push($Pia_Graph_Device_Arch, $row['Archived_Devices']);
}
}
function pia_graph_devices_data($Pia_Graph_Array) {
$Pia_Graph_Array_rev = array_reverse($Pia_Graph_Array);

View File

@@ -112,7 +112,7 @@ if ($ENABLED_DARKMODE === True) {
<!-- ----------------------------------------------------------------------- -->
<!-- Layout Boxed Yellow -->
<body class="hold-transition <?php echo $pia_skin_selected;?> layout-boxed sidebar-mini" <?php echo $BACKGROUND_IMAGE_PATCH;?> onLoad="show_pia_servertime();" >
<body class="hold-transition <?php echo $pia_skin_selected;?> sidebar-mini" <?php echo $BACKGROUND_IMAGE_PATCH;?> onLoad="show_pia_servertime();" >
<!-- Site wrapper -->
<div class="wrapper">
@@ -222,6 +222,10 @@ if ($ENABLED_DARKMODE === True) {
<a href="network.php"><span><i class="fa fa-fw fa-network-wired"></i> <?= lang('Navigation_Network');?></span></a>
</li>
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php') ) ){ echo 'active'; } ?>">
<a href="plugins.php"><span><i class="fa fa-fw fa-plug"></i> <?= lang('Navigation_Plugins');?></span></a>
</li>
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'active'; } ?>">
<div class="new-version myhidden" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">🆕</div>
<a href="maintenance.php"><i class="fa fa-wrench "></i> <span><?= lang('Navigation_Maintenance');?></span></a>

View File

@@ -13,10 +13,13 @@ $lang['en_us'] = array(
// General
//////////////////////////////////////////////////////////////////
'Gen_Delete' => 'Delete',
'Gen_DeleteAll' => 'Delete all',
'Gen_Cancel' => 'Cancel',
'Gen_Okay' => 'Ok',
'Gen_Save' => 'Save',
'Gen_Saved' => 'Saved',
'Gen_Run' => 'Run',
'Gen_Action' => 'Action',
'Gen_Purge' => 'Purge',
'Gen_Backup' => 'Run Backup',
'Gen_Restore' => 'Run Restore',
@@ -24,6 +27,9 @@ $lang['en_us'] = array(
'Gen_AreYouSure' => 'Are you sure?',
'Gen_Upd' => 'Updated successfully',
'Gen_Upd_Fail' => 'Update failed',
'Gen_Help' => 'Need help?',
'Gen_DataUpdatedUITakesTime' => 'OK - It may take a while for the UI to update if a scan is runnig.',
'Gen_LockedDB' => 'ERROR - DB might be locked - Check F12 Dev tools -> Console or try later.',
//////////////////////////////////////////////////////////////////
// Login Page - Update by @TeroRERO 03ago2022
@@ -50,9 +56,10 @@ $lang['en_us'] = array(
'Navigation_Devices' => 'Devices',
'Navigation_Presence' => 'Presence',
'Navigation_Events' => 'Events',
'Navigation_Network' => 'Network',
'Navigation_Plugins' => 'Plugins',
'Navigation_Maintenance' => 'Maintenance',
'Navigation_Settings' => 'Settings',
'Navigation_Network' => 'Network',
'Navigation_HelpFAQ' => 'Help / FAQ',
'Device_Title' => 'Devices',
'Device_Shortcut_AllDevices' => 'All Devices',
@@ -109,6 +116,7 @@ $lang['en_us'] = array(
'Presence_CalHead_quarter' => 'quarter',
'Presence_CalHead_month' => 'month',
'Presence_CalHead_week' => 'week',
'Presence_CalHead_day' => 'day',
//////////////////////////////////////////////////////////////////
// Events Page
@@ -207,7 +215,7 @@ $lang['en_us'] = array(
'DevDetail_button_Reset' => 'Reset Changes',
'DevDetail_button_Save' => 'Save',
'DevDetail_button_OverwriteIcons' => 'Overwrite Icons',
'DevDetail_button_OverwriteIcons_Tooltip' => 'Overwrite icons of all devices with the same type',
'DevDetail_button_OverwriteIcons_Tooltip' => 'Overwrite icons of all devices with the same device type',
'DevDetail_button_OverwriteIcons_Warning' => 'Are you sure you want to overwrite all icons of all devices with the same device type as the current device type?',
'DevDetail_SessionTable_Order' => 'Order',
'DevDetail_SessionTable_Connection' => 'Connection',
@@ -225,6 +233,17 @@ $lang['en_us'] = array(
'DevDetail_Nmap_buttonSkipDiscovery' => 'Skip host discovery',
'DevDetail_Nmap_buttonSkipDiscovery_text' => 'Skip host discovery (-Pn option): Default scan without host discovery',
'DevDetail_Nmap_resultsLink' => 'You can leave this page after starting a scan. Results will be also available in the <code>pialert_front.log</code> file.',
'BackDevDetail_Actions_Title_Run' => 'Run action',
'BackDevDetail_Actions_Not_Registered' => 'Action not registered: ',
'BackDevDetail_Actions_Ask_Run' => 'Do you want to execute the action?',
'BackDevDetail_Tools_WOL_okay' => 'The command was executed.',
'BackDevDetail_Tools_WOL_error' => 'The command was NOT executed.',
'DevDetail_Tools_WOL_noti' => 'Wake-on-LAN',
'DevDetail_Tools_WOL_noti_text' => 'The Wake-on-LAN command is sent to the broadcast address. If the target is not in the subnet/vlan of Pi.Alert, the target device will not respond.',
'DevDetail_Tools_WOL' => 'Send Wol command to ',
'DevDetail_WOL_Title' => '<i class="fa fa-power-off"></i> Wake-on-LAN',
'DevDetail_Run_Actions_Title' => '<i class="fa fa-play"></i> Run action on device',
'DevDetail_Run_Actions_Tooltip' => 'Run an action on the current device from the dropdown list.',
//////////////////////////////////////////////////////////////////
// Maintenance Page
@@ -347,6 +366,7 @@ $lang['en_us'] = array(
'BackDevices_DBTools_ImportCSV' => 'The devices from the CSV file were imported successfully.',
'BackDevices_DBTools_ImportCSVError' => 'The CSV file couldn\'t be imported. Make sure the format is correct.',
'BackDevices_DBTools_ImportCSVMissing' => 'The CSV file couldn\'t be found under <b>/config/devices.csv.</b>',
'BackDevices_Device_UpdDevError' => 'Error updating devices, try later. The database is probable locked due to an ongoing task.',
//////////////////////////////////////////////////////////////////
@@ -469,6 +489,14 @@ $lang['en_us'] = array(
//////////////////////////////////////////////////////////////////
// Plugins
//////////////////////////////////////////////////////////////////
'Plugins_Unprocessed_Events' => 'Unprocessed Events',
'Plugins_Objects' => 'Plugin Objects',
'Plugins_History' => 'Events History',
//////////////////////////////////////////////////////////////////
// Settings
//////////////////////////////////////////////////////////////////
@@ -482,44 +510,42 @@ $lang['en_us'] = array(
//General
'General_settings_group' => '<i class="fa fa-gears"></i> General',
'General_display_name' => 'General',
'General_icon' => '<i class="fa fa-gears"></i>',
'ENABLE_ARPSCAN_name' => 'Enable ARP scan',
'ENABLE_ARPSCAN_description' => 'Arp-scan is a command-line tool that uses the ARP protocol to discover and fingerprint IP hosts on the local network. An alternative to ARP scan is to enable the <a onclick="toggleAllSettings()" href="#PIHOLE_ACTIVE"><code>PIHOLE_ACTIVE</code>PiHole integration settings</a>.',
'SCAN_SUBNETS_name' => 'Subnets to scan',
'SCAN_SUBNETS_description' => '
The arp-scan time itself depends on the number of IP addresses to check.
The number of IPs to check depends on the <a target="_blank" href="https://www.calculator.net/ip-subnet-calculator.html">network mask</a> you set here.
For example, a <code>/24</code> mask results in 256 IPs to check, where as a <code>/16</code>
mask checks around 65,536. Every IP takes a couple seconds. This means that with an incorrect configuration
the arp-scan will take hours to complete instead of seconds.
<ol>
<li>Specify the network mask. For example, the filter <code>192.168.1.0/24</code> covers IP ranges 192.168.1.0 to 192.168.1.255.</li>
<li>Run <code>iwconfig</code> in your container to find your interface name(s) (e.g.: <code>eth0</code>, <code>eth1</code>)</li>
</ol>
The arp-scan time itself depends on the number of IP addresses to check so set this up carefully with the appropriate network mask and interface. Check the <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SUBNETS.md" target="_blank">subnets documentation</a> for help on setting up VLANs, what VLANs are supported, or how to figure out the network mask and your interface.
',
'PRINT_LOG_name' => 'Print additional logging',
'PRINT_LOG_description' => 'This setting will enable more verbose logging. Useful for debugging events writing into the database.',
'LOG_LEVEL_name' => 'Print additional logging',
'LOG_LEVEL_description' => 'This setting will enable more verbose logging. Useful for debugging events writing into the database.',
'TIMEZONE_name' => 'Time zone',
'TIMEZONE_description' => 'Time zone to display stats correctly. Find your time zone <a target="_blank" href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" rel="nofollow">here</a>.',
'ENABLE_PLUGINS_name' => 'Enable Plugins',
'ENABLE_PLUGINS_description' => 'Enables the <a target="_blank" href="https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins">plugins</a> functionality. Loading plugins requires more hardware resources so you might want to disable them on low-powered system.',
'PIALERT_WEB_PROTECTION_name' => 'Enable login',
'PIALERT_WEB_PROTECTION_description' => 'When enabled a login dialog is displayed. Read below carefully if you get locked out of your instance.',
'PIALERT_WEB_PASSWORD_name' => 'Login password',
'PIALERT_WEB_PASSWORD_description' => 'The default password is <code>123456</code>. To change the password run <code>/home/pi/pialert/back/pialert-cli</code> in the container',
'INCLUDED_SECTIONS_name' => 'Notify on',
'INCLUDED_SECTIONS_description' => 'Specifies which events trigger notifications. Remove the event type(s) you don\'t want to get notified on. This setting overrides device-specific settings in the UI. (<code>CTRL + Click</code> to select / deselect).',
'INCLUDED_SECTIONS_description' => 'Specifies which events trigger notifications. Remove the event type(s) you don\'t want to get notified on. This setting overrides device-specific settings in the UI. (<code>CTRL + Click</code> to select/deselect).',
'SCAN_CYCLE_MINUTES_name' => 'Scan cycle delay',
'SCAN_CYCLE_MINUTES_description' => 'The delay between scans. If using arp-scan, the scan time itself depends on the number of IP addresses to check. This is influenced by the network mask set in the <a href="#SCAN_SUBNETS"><code>SCAN_SUBNETS</code> setting</a> at the top. Every IP takes a couple seconds to scan.',
'SCAN_CYCLE_MINUTES_description' => 'The delay between scans in minutes. If using arp-scan, the scan time itself depends on the number of IP addresses to check. This is influenced by the network mask set in the <a href="#SCAN_SUBNETS"><code>SCAN_SUBNETS</code> setting</a> at the top. Every IP takes a couple seconds to scan.',
'DAYS_TO_KEEP_EVENTS_name' => 'Delete events older than',
'DAYS_TO_KEEP_EVENTS_description' => 'This is a maintenance setting. This specifies the number of days worth of event entries that will be kept. All older events will be deleted periodically.',
'DAYS_TO_KEEP_EVENTS_description' => 'This is a maintenance setting. This specifies the number of days worth of event entries that will be kept. All older events will be deleted periodically. Also applies on Plugin Events History.',
'REPORT_DASHBOARD_URL_name' => 'Pi.Alert URL',
'REPORT_DASHBOARD_URL_description' => 'This URL is used as the base for generating links in the emails. Enter full URL starting with <code>http://</code> including the port number (no trailig slash <code>/</code>).',
'DIG_GET_IP_ARG_name' => 'Internet IP discovery',
'DIG_GET_IP_ARG_description' => 'Change the <a href="https://linux.die.net/man/1/dig" target="_blank">dig utility</a> arguments if you have issues resolving your Internet IP. Arguments are added at the end of the following command: <code>dig +short </code>.',
'UI_LANG_name' => 'UI Language',
'UI_LANG_description' => 'Select the preferred UI language.',
'UI_PRESENCE_name' => 'Show in presence chart',
'UI_PRESENCE_description' => 'Select what statuses should be shown in the <b>Device presence over time</b> chart in the <a href="/devices.php" target="_blank">Devices</a> page. (<code>CTRL + Click</code> to select/deselect)',
//Email
'Email_settings_group' => '<i class="fa fa-at"></i> Email',
'Email_display_name' => 'Email',
'Email_icon' => '<i class="fa fa-at"></i>',
'REPORT_MAIL_name' => 'Enable email',
'REPORT_MAIL_description' => 'If enabled an email is sent out with a list of changes you\'ve subscribed to. Please also fill out all remaining settings related to the SMTP setup below.',
'SMTP_SERVER_name' => 'SMTP server URL',
@@ -542,7 +568,8 @@ the arp-scan will take hours to complete instead of seconds.
'REPORT_FROM_description' => 'Notification email subject line.',
//Webhooks
'Webhooks_settings_group' => '<i class="fa fa-circle-nodes"></i> Webhooks',
'Webhooks_display_name' => 'Webhooks',
'Webhooks_icon' => '<i class="fa fa-circle-nodes"></i>',
'REPORT_WEBHOOK_name' => 'Enable Webhooks',
'REPORT_WEBHOOK_description' => 'Enable webhooks for notifications. Webhooks help you to connect to a lot of 3rd party tools, such as IFTTT, Zapier or <a href="https://n8n.io/" target="_blank">n8n</a> to name a few. Check out this simple <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md" target="_blank">n8n guide here</a> to get started. If enabled, configure related settings below.',
'WEBHOOK_URL_name' => 'Target URL',
@@ -553,7 +580,8 @@ the arp-scan will take hours to complete instead of seconds.
'WEBHOOK_REQUEST_METHOD_description' => 'The HTTP request method to be used for the webhook call.',
// Apprise
'Apprise_settings_group' => '<i class="fa fa-bullhorn"></i> Apprise',
'Apprise_display_name' => 'Apprise',
'Apprise_icon' => '<i class="fa fa-bullhorn"></i>',
'REPORT_APPRISE_name' => 'Enable Apprise',
'REPORT_APPRISE_description' => 'Enable sending notifications via <a target="_blank" href="https://hub.docker.com/r/caronc/apprise">Apprise</a>.',
'APPRISE_HOST_name' => 'Apprise host URL',
@@ -562,7 +590,8 @@ the arp-scan will take hours to complete instead of seconds.
'APPRISE_URL_description' => 'Apprise notification target URL. For example for Telegram it would be <code>tgram://{bot_token}/{chat_id}</code>.',
// NTFY
'NTFY_settings_group' => '<i class="fa fa-terminal"></i> NTFY',
'NTFY_display_name' => 'NTFY',
'NTFY_icon' => '<i class="fa fa-terminal"></i>',
'REPORT_NTFY_name' => 'Enable NTFY',
'REPORT_NTFY_description' => 'Enable sending notifications via <a target="_blank" href="https://ntfy.sh/">NTFY</a>.',
'NTFY_HOST_name' => 'NTFY host URL',
@@ -575,7 +604,8 @@ the arp-scan will take hours to complete instead of seconds.
'NTFY_PASSWORD_description' => 'Enter password if you need (host) an instance with enabled authetication.',
// Pushsafer
'PUSHSAFER_settings_group' => '<i class="fa fa-bell"></i> Pushsafer',
'PUSHSAFER_display_name' => 'Pushsafer',
'PUSHSAFER_icon' => '<i class="fa fa-bell"></i>',
'REPORT_PUSHSAFER_name' => 'Enable Pushsafer',
'REPORT_PUSHSAFER_description' => 'Enable sending notifications via <a target="_blank" href="https://www.pushsafer.com/">Pushsafer</a>.',
'PUSHSAFER_TOKEN_name' => 'Pushsafer token',
@@ -585,7 +615,8 @@ the arp-scan will take hours to complete instead of seconds.
// MQTT
'MQTT_settings_group' => '<i class="fa fa-square-rss"></i> MQTT',
'MQTT_display_name' => 'MQTT',
'MQTT_icon' => '<i class="fa fa-square-rss"></i>',
'REPORT_MQTT_name' => 'Enable MQTT',
'REPORT_MQTT_description' => 'Enable sending notifications via <a target="_blank" href="https://www.home-assistant.io/integrations/mqtt/">MQTT</a> to your Home Assistance instance.',
'MQTT_BROKER_name' => 'MQTT broker URL',
@@ -602,7 +633,8 @@ the arp-scan will take hours to complete instead of seconds.
'MQTT_DELAY_SEC_description' => 'A little hack - delay adding to the queue in case the process is restarted and previous publish processes aborted (it takes ~<code>2</code>s to update a sensor config on the broker). Tested with <code>2</code>-<code>3</code> seconds of delay. This delay is only applied when devices are created (during the first notification loop). It doesn\'t affect subsequent scans or notifications.',
//DynDNS
'DynDNS_settings_group' => '<i class="fa fa-globe"></i> DynDNS',
'DynDNS_display_name' => 'DynDNS',
'DynDNS_icon' => '<i class="fa fa-globe"></i>',
'DDNS_ACTIVE_name' => 'Enable DynDNS',
'DDNS_ACTIVE_description' => '',
'DDNS_DOMAIN_name' => 'DynDNS domain URL',
@@ -615,14 +647,16 @@ the arp-scan will take hours to complete instead of seconds.
'DDNS_UPDATE_URL_description' => 'Update URL starting with <code>http://</code> or <code>https://</code>.',
// PiHole
'PiHole_settings_group' => '<i class="fa fa-seedling"></i> PiHole',
'PiHole_display_name' => 'PiHole',
'PiHole_icon' => '<i class="fa fa-seedling"></i>',
'PIHOLE_ACTIVE_name' => 'Enable PiHole mapping',
'PIHOLE_ACTIVE_description' => 'You need to map<code>:/etc/pihole/pihole-FTL.db</code> in the <code>docker-compose.yml</code> file if you enable this setting.',
'DHCP_ACTIVE_name' => 'Enable PiHole DHCP',
'DHCP_ACTIVE_description' => 'You need to map <code>:/etc/pihole/dhcp.leases</code> in the <code>docker-compose.yml</code> file if you enable this setting.',
// Pholus
'Pholus_settings_group' => '<i class="fa fa-search"></i> Pholus',
'Pholus_display_name' => 'Pholus',
'Pholus_icon' => '<i class="fa fa-search"></i>',
'PHOLUS_ACTIVE_name' => 'Cycle run',
'PHOLUS_ACTIVE_description' => '<a href="https://github.com/jokob-sk/Pi.Alert/tree/main/pholus" target="_blank" >Pholus</a> is a sniffing tool to discover additional information about the devices on the network, including the device name. If enabled this will execute the scan before every network scan cycle until there are no <code>(unknown)</code> or <code>(name not found)</code> devices. Please be aware it can spam the network with unnecessary traffic. Depends on the <a onclick="toggleAllSettings()" href="#SCAN_SUBNETS"><code>SCAN_SUBNETS</code> setting</a>. For a scheduled or one-off scan, check the <a href="#PHOLUS_RUN"><code>PHOLUS_RUN</code> setting</a>.',
'PHOLUS_TIMEOUT_name' => 'Cycle run timeout',
@@ -637,10 +671,11 @@ the arp-scan will take hours to complete instead of seconds.
'PHOLUS_RUN_SCHD_description' => 'Only enabled if you select <code>schedule</code> in the <a href="#PHOLUS_RUN"><code>PHOLUS_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 <a onclick="toggleAllSettings()" href="#TIMEZONE"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes.',
'PHOLUS_DAYS_DATA_name' => 'Data retention',
'PHOLUS_DAYS_DATA_description' => 'How many days of Pholus scan entries should be kept (globally, not device specific!). The <a href="/maintenance.php#tab_Logging">pialert_pholus.log</a> file is not touched. Enter <code>0</code> to disable.',
'PHOLUS_DAYS_DATA_description' => 'How many days of Pholus scan entries should be kept (globally, not device specific!) Enter <code>0</code> to disable.',
// Nmap
'Nmap_settings_group' => '<i class="fa fa-ethernet"></i> Nmap',
'Nmap_display_name' => 'Nmap',
'Nmap_icon' => '<i class="fa fa-ethernet"></i>',
'NMAP_ACTIVE_name' => 'Cycle run',
'NMAP_ACTIVE_description' => 'If enabled this will execute a scan on a newly found device. For a scheduled or one-off scan, check the <a href="#NMAP_RUN"><code>NMAP_RUN</code> setting</a>.',
'NMAP_TIMEOUT_name' => 'Run timeout',
@@ -653,20 +688,15 @@ the arp-scan will take hours to complete instead of seconds.
'NMAP_ARGS_description' => 'Arguments used to run the Nmap scan. Be careful to specify <a href="https://linux.die.net/man/1/nmap" target="_blank">the arguments</a> correctly. For example <code>-p -10000</code> scans ports from 1 to 10000.',
// API
'API_settings_group' => '<i class="fa fa-arrow-down-up-across-line"></i> API',
'ENABLE_API_name' => 'Enable API',
'ENABLE_API_description' => 'If enabled the app will start publishing and updating <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md" target="_blank">simple API endpoints</a> under the <code>/home/pi/pialert/front/api/</code> folder and thus on the <code>pialert_url/api/File_name</code> url.',
'API_RUN_name' => 'Scheduling updates',
'API_RUN_description' => 'Scheduling settings to specify when the API endpoints should be updated. If set to <code>schedule</code> then endpoints will be updated on a specified cron-like schedule specified by the <code>API_RUN_SCHD</code> setting. Otherwise if set to <code>interval</code> endpoints will be updated every N seconds specified by the <code>API_RUN_INTERVAL</code> setting.',
'API_RUN_SCHD_name' => 'Schedule',
'API_RUN_SCHD_description' => 'Depends on the <code>API_RUN</code> settings to be set to <code>schedule</code>. 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>).',
'API_RUN_INTERVAL_name' => 'Update interval',
'API_RUN_INTERVAL_description' => 'Depends on the <code>API_RUN</code> settings to be set to <code>interval</code>. The minimum cycle is <code>5</code> seconds.',
'API_display_name' => 'API',
'API_icon' => '<i class="fa fa-arrow-down-up-across-line"></i>',
'API_CUSTOM_SQL_name' => 'Custom endpoint',
'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>.',
);
?>

View File

@@ -5,6 +5,7 @@ $lang['es_es'] = array(
//////////////////////////////////////////////////////////////////
// About - Update by @TeroRERO 07ago2022
//////////////////////////////////////////////////////////////////
'About_Title' => 'Guadián de Red <br>(Código Abierto)',
'About_Design' => 'Diseñado para:',
'About_Exit' => 'Salir',
@@ -12,20 +13,31 @@ $lang['es_es'] = array(
//////////////////////////////////////////////////////////////////
// General - Update by @TeroRERO 01ago2022
//////////////////////////////////////////////////////////////////
'Gen_Delete' => 'Eliminar',
'Gen_DeleteAll' => 'Eliminar todo',
'Gen_Cancel' => 'Cancelar',
'Gen_Okay' => 'Ok',
'Gen_Okay' => 'Aceptar',
'Gen_Save' => 'Guardar',
'Gen_Saved' => 'Guardado',
'Gen_Run' => 'Ejecutar',
'Gen_Action' => 'Acción',
'Gen_Purge' => 'Purgar',
'Gen_Backup' => 'Ejecutar copia de seguridad',
'Gen_Restore' => 'Ejecutar restauración',
'Gen_Switch' => 'Cambiar',
'Gen_AreYouSure' => '¿Estás seguro de',
'Gen_Upd' => 'Actualizado correctamente',
'Gen_Upd_Fail' => 'Fallo al actualizar',
'Gen_Help' => 'Ayuda',
'Gen_DataUpdatedUITakesTime' => 'Correcto - La interfaz puede tardar en actualizarse si se está ejecutando un escaneo.',
'Gen_LockedDB' => 'Fallo - La base de datos puede estar bloqueada - Pulsa F1 -> Ajustes de desarrolladores -> Consola o prueba más tarde.',
//////////////////////////////////////////////////////////////////
// Login Page - Update by @TeroRERO 03ago2022
//////////////////////////////////////////////////////////////////
// TeroRERO Off 'Login_Box' => 'Inicie su sesión',
'Login_Box' => 'Ingrese su contraseña',
'Login_Remember' => 'Recordar',
'Login_Remember_small' => '(válido por 7 días)',
@@ -38,6 +50,7 @@ $lang['es_es'] = array(
'Login_Toggle_Info' => 'Información sobre la contraseña',
'Login_Toggle_Info_headline' => 'Información sobre la contraseña',
'Login_Toggle_Alert_headline' => 'Alerta de Contraseña!',
'Login_Default_PWD' => 'La contraseña por defecto "123456" sigue activa.',
//////////////////////////////////////////////////////////////////
// Device Page - Update by @TeroRERO 03ago2022
@@ -46,10 +59,11 @@ $lang['es_es'] = array(
'Navigation_Devices' => 'Dispositivos',
'Navigation_Presence' => 'Historial',
'Navigation_Events' => 'Eventos',
'Navigation_Network' => 'Red',
'Navigation_Plugins' => 'Plugins',
'Navigation_Maintenance' => 'Mantenimiento',
'Navigation_Settings' => 'Configuración',
'Navigation_Network' => 'Red',
'Navigation_HelpFAQ' => 'Ayuda / FAQ',
'Navigation_HelpFAQ' => 'Ayuda / Preguntas frecuentes',
'Device_Title' => 'Dispositivos',
'Device_Shortcut_AllDevices' => 'Todos',
'Device_Shortcut_Connected' => 'Conectado(s)',
@@ -62,14 +76,21 @@ $lang['es_es'] = array(
'Device_TableHead_Name' => 'Nombre',
'Device_TableHead_Owner' => 'Propietario',
'Device_TableHead_Type' => 'Tipo',
'Device_TableHead_Icon' => 'Icon',
'Device_TableHead_RowID' => 'Row ID',
'Device_TableHead_Rowid' => 'Row ID',
'Device_TableHead_Parent_MAC' => 'Nodo principal de la MAC',
'Device_TableHead_Connected_Devices' => 'Dispositivos conectados',
'Device_TableHead_Location' => 'Ubicación',
'Device_TableHead_Vendor' => 'Fabricante',
'Device_TableHead_Favorite' => 'Favorito',
'Device_TableHead_Group' => 'Grupo',
'Device_TableHead_FirstSession' => '1ra. sesión',
'Device_TableHead_LastSession' => 'Última sesión',
'Device_TableHead_LastIP' => 'Última IP',
'Device_TableHead_MAC' => 'MAC',
'Device_TableHead_MAC_full' => 'MAC completa',
'Device_TableHead_LastIPOrder' => 'Última orden de IP',
'Device_TableHead_Rowid' => 'Rowid',
'Device_TableHead_Status' => 'Estado',
'Device_Searchbox' => 'Búsqueda',
'Device_Tablelenght' => 'Mostrar _MENU_ entradas',
@@ -153,17 +174,26 @@ $lang['es_es'] = array(
'DevDetail_Tab_Sessions' => 'Sesiones',
'DevDetail_Tab_Presence' => 'Historial',
'DevDetail_Tab_Events' => 'Eventos',
'DevDetail_Tab_Pholus' => '<i class="fa fa-search"></i> Pholus',
'DevDetail_Tab_PholusEmpty' => 'No se ha encontrado nada para este dispositivo con Pholus.',
'DevDetail_Tab_NmapTableHeader' => 'Resultados del escaneo programado',
'DevDetail_Tab_NmapTableText' => 'Establece la programación en los <a href="/settings.php#NMAP_ACTIVE">Ajustes</a>',
'DevDetail_Tab_NmapEmpty' => 'Ningún puerto detectado en este dispositivo con Nmap.',
'DevDetail_MainInfo_Title' => 'Información principal',
'DevDetail_MainInfo_mac' => 'MAC',
'DevDetail_MainInfo_Name' => 'Nombre',
'DevDetail_MainInfo_Owner' => 'Propietario',
'DevDetail_MainInfo_Type' => 'Tipo',
'DevDetail_Icon' => 'Icono',
'DevDetail_Icon_Descr' => 'Enter a font awesome icon name without the fa- prefix or with complete class, e.g.: fa fa-brands fa-apple.',
'DevDetail_MainInfo_Vendor' => 'Proveedor',
'DevDetail_MainInfo_Favorite' => 'Favorito',
'DevDetail_MainInfo_Group' => 'Grupo',
'DevDetail_MainInfo_Location' => 'Ubicación',
'DevDetail_MainInfo_Comments' => 'Comentario',
'DevDetail_MainInfo_Network' => 'Hardware de Red (ID)',
'DevDetail_MainInfo_Network' => '<i class="fa fa-server"></i> Nodo (MAC)',
'DevDetail_GoToNetworkNode' => 'Navegar a la página de Internet del nodo seleccionado.',
'DevDetail_MainInfo_Network_Port' => 'Puerto de Red HW',
'DevDetail_SessionInfo_Title' => 'Información de sesión',
'DevDetail_SessionInfo_Status' => 'Estado',
@@ -183,7 +213,12 @@ $lang['es_es'] = array(
'DevDetail_EveandAl_ScanCycle_z' => 'No Escanear Dispositivo',
'DevDetail_button_Delete' => 'Eliminar dispositivo',
'DevDetail_button_Reset' => 'Restablecer cambios',
'DevDetail_button_DeleteEvents_Warning' => '¿Desea eliminar todos los eventos de este dispositivo?<br><br>(se eliminarán el <b>Historial de eventos</b> y las <b>Sesiones</b>, y puede ayudar en el caso de notificaciones constantes)',
'DevDetail_button_Reset' => 'Restablecer cambios',
'DevDetail_button_Save' => 'Guardar',
'DevDetail_button_OverwriteIcons' => 'Sobreescribir iconos',
'DevDetail_button_OverwriteIcons_Tooltip' => 'Sobreescribir los iconos de todos los dispositivos con el mismo tipo',
'DevDetail_button_OverwriteIcons_Warning' => '¿Sobreescribir todos los iconos de todos los dispositivos con el mismo tipo que el dispositivo actual?',
'DevDetail_SessionTable_Order' => 'Ordenar',
'DevDetail_SessionTable_Connection' => 'Conexión',
'DevDetail_SessionTable_Disconnection' => 'Desconexión',
@@ -199,12 +234,27 @@ $lang['es_es'] = array(
'DevDetail_Nmap_buttonDetail_text' => 'Escaneo detallado: escaneo predeterminado con detección de sistema operativo habilitado, detección de versiones, escaneo de script y traceroute (hasta 30 segundos o más)',
'DevDetail_Nmap_buttonSkipDiscovery' => 'Omitir detección de host',
'DevDetail_Nmap_buttonSkipDiscovery_text' => 'Omitir detección de host (-Pn opción): Escaneo predeterminado sin detección de host',
'DevDetail_Nmap_resultsLink' => 'Puedes abandonar esta página después de empezar un escaneo. Los resultados también estarán disponibles en el archivo <code>pialert_front.log</code>.',
'BackDevDetail_Actions_Title_Run' => 'Ejecutar acción',
'BackDevDetail_Actions_Not_Registered' => 'Acción no registrada: ',
'BackDevDetail_Actions_Ask_Run' => '¿Desea ejecutar la acción?',
'BackDevDetail_Tools_WOL_okay' => 'El comando se ha ejecutado correctamente.',
'BackDevDetail_Tools_WOL_error' => 'Ha ocurrido un error al ejectuar el comando.',
'DevDetail_Tools_WOL_noti' => 'Wake-on-LAN',
'DevDetail_Tools_WOL_noti_text' => 'El comando de Wake-on-LAN en enviado a la dirección de escucha. Si el dispositivo no está en la misma subred/vlan que Pi.Alert, el dispositivo no responderá.',
'DevDetail_Tools_WOL' => 'Enviar comando WOL a ',
'DevDetail_WOL_Title' => '<i class="fa fa-power-off"></i> Wake-on-LAN',
'DevDetail_Run_Actions_Title' => '<i class="fa fa-play"></i> Ejecutar acción en el dispositivo',
'DevDetail_Run_Actions_Tooltip' => 'Ejecutar la acción del desplegable sobre el dispositivo actual.',
//////////////////////////////////////////////////////////////////
// Maintenance Page - Update by @TeroRERO 07ago2022
//////////////////////////////////////////////////////////////////
'Maintenance_Title' => 'Herramientas de mantenimiento',
'Maintenance_version' => 'Actualizaciones de la aplicación',
'Maintenance_new_version' => '🆕 Una nueva versión está disponible. Comprueba las <a href="https://github.com/jokob-sk/Pi.Alert/releases" target="_blank">notas de lanzamiento</a>.',
'Maintenance_current_version' => 'No hay actualizaciones disponibles. Comprueba en que <a href="https://github.com/jokob-sk/Pi.Alert/issues/138" target="_blank">se está trabajando</a>.',
'Maintenance_database_path' => 'Ruta de la base de datos:',
'Maintenance_database_size' => 'Tamaño de base de datos:',
'Maintenance_database_lastmod' => 'Última modificación:',
@@ -214,10 +264,10 @@ $lang['es_es'] = array(
'Maintenance_arp_status' => 'Estado de escaneo:',
'Maintenance_arp_status_off' => 'está actualmente deshabilitado',
'Maintenance_arp_status_on' => 'escaneo(s) actualmente en ejecución',
'Maintenance_themeselector_lable' => 'Seleccionar Skin',
'Maintenance_themeselector_empty' => 'Elija un Skin',
'Maintenance_themeselector_text' => 'El cambio tiene lugar en el lado del servidor, por lo que afecta todos los dispositivos en uso.',
'Maintenance_themeselector_lable' => 'Seleccionar tema',
'Maintenance_themeselector_empty' => 'Elige un tema',
'Maintenance_themeselector_apply' => 'Aplicar',
'Maintenance_themeselector_text' => 'El cambio tiene lugar en el lado del servidor, por lo que afecta todos los dispositivos en uso.',
'Maintenance_lang_selector_lable' => 'Seleccione su idioma',
'Maintenance_lang_selector_empty' => 'Elija un idioma',
'Maintenance_lang_en_us' => 'English (US)',
@@ -226,10 +276,15 @@ $lang['es_es'] = array(
'Maintenance_lang_selector_text' => 'El cambio tiene lugar en el lado del servidor, por lo que afecta todos los dispositivos en uso.',
'Maintenance_lang_selector_apply' => 'Aplicar',
'Maintenance_Tools_Tab_Settings' => 'Ajustes',
'Maintenance_Tools_Tab_Tools' => 'Tools',
'Maintenance_Tools_Tab_UISettings' => 'Ajustes de interfaz',
'Maintenance_Tools_Tab_Tools' => 'Herramientas',
'Maintenance_Tools_Tab_BackupRestore' => 'Respaldo / Restaurar',
'Maintenance_Tools_Tab_Logging' => 'Logs',
'Maintenance_Tools_Tab_Logging' => 'Registros',
'Maintenance_Tool_displayed_columns_text' => 'Cambia la visibilidad y el orden de las columnas en la página <a href="devices.php"><b> <i class="fa fa-laptop"></i> Dispositivos</b></a> . (La función de coger y arrastrar funciona un poco mal, pero funciona. (Se intentó arreglar <a href="https://github.com/jokob-sk/Pi.Alert/commit/94b32f0f7332879f5a7d2af05dafa2e5d5cfa5da">como por 3 horas</a> - se agradecerían PRs para arreglarlo)).',
'Maintenance_Tool_order_columns_text' => '',
'Maintenance_Tool_darkmode' => 'Cambiar Modo (Dark/Light)',
'Maintenance_Tool_drag_me' => 'Coger para rearrastrar columnas.',
'Maintenance_Tool_check_visible' => 'Desactivar para ocultar columna.',
'Maintenance_Tool_darkmode_text' => 'Alternar entre el modo oscuro y el modo de luz. Si el interruptor no funciona correctamente, intente borrar el caché del navegador. El cambio tiene lugar en el lado del servidor, por lo que afecta todos los dispositivos en uso.',
'Maintenance_Tool_darkmode_noti' => 'Cambiar Modo',
'Maintenance_Tool_darkmode_noti_text' => 'Después del cambio de tema, la página intenta volver a cargar para activar el cambio. Si es necesario, el caché debe ser eliminado.',
@@ -284,8 +339,6 @@ $lang['es_es'] = array(
'Maintenance_Tool_ImportCSV_text' => 'Antes de usar esta función, haga una copia de seguridad. Importe un archivo CSV (valor separado por comas) que contiene la lista de dispositivos, incluidas las relaciones de red entre nodos de red y dispositivos conectados. Para hacer eso, coloque el archivo CSV llamado <b> devices.csv </b> en su carpeta <b>/config </b>.',
'Maintenance_Tool_ImportCSV_noti' => 'Importación CSV',
'Maintenance_Tool_ImportCSV_noti_text' => '¿Está seguro de que quiere importar el archivo CSV? Esto sobrescribirá completamente los dispositivos de su base de datos.',
'Maintenance_Github_package_a' => 'La última versión de Pi.Alert (Fork leiweibau) se publicó en ',
'Maintenance_Github_package_b' => '',
//////////////////////////////////////////////////////////////////
// Maintenance Page - Update by @TeroRERO 25jul2022
@@ -312,6 +365,10 @@ $lang['es_es'] = array(
'BackDevices_DBTools_Upgrade' => 'Base de datos actualizada correctamente',
'BackDevices_DBTools_UpgradeError' => 'Falló la actualización de la base de datos',
'BackDevices_DBTools_Purge' => 'Las copias de seguridad más antiguas fueron eliminadas',
'BackDevices_DBTools_ImportCSV' => 'Los dispositivos del archivo CSV han sido importados correctamente.',
'BackDevices_DBTools_ImportCSVError' => 'El archivo CSV no pudo ser importado. Asegúrate de que el formato es correcto.',
'BackDevices_DBTools_ImportCSVMissing' => 'El archivo CSV no se pudo encontrar en <b>/config/devices.csv.</b>',
'BackDevices_Device_UpdDevError' => 'Fallo al actualizar dispositivos, pruebe de nuevo más tarde. La base de datos probablemente esté bloqueada por una tarea en curso.',
//////////////////////////////////////////////////////////////////
// Network Page - Update by @TeroRERO 01ago2022
@@ -320,6 +377,8 @@ $lang['es_es'] = array(
'Network_Title' => 'Descripción general de la red',
'Network_ManageDevices' => 'Administrar dispositivos',
'Network_ManageAdd' => 'Añadir dispositivo',
'Network_ManageAssign' => 'Asignar',
'Network_ManageUnassign' => 'Desasignar',
'Network_ManageEdit' => 'Actualizar dispositivo',
'Network_ManageDel' => 'Eliminar dispositivo',
'Network_ManageAdd_Name' => 'Nombre del dispositivo',
@@ -344,6 +403,14 @@ $lang['es_es'] = array(
'Network_Table_State' => 'Estado',
'Network_Table_Hostname' => 'Nombre de host',
'Network_Table_IP' => 'Dirección IP',
'Network_UnassignedDevices' => 'Dispositivos sin asignar',
'Network_Assign' => 'Conectar al nodo de <i class="fa fa-server"></i> red',
'Network_Connected' => 'Dispositivos conectados',
'Network_ManageLeaf' => 'Gestionar asignación',
'Network_Node' => 'Nodo de red',
'Network_Node_Name' => 'Nombre de nodo',
'Network_Parent' => 'Dispositivo primario de la red',
'Network_NoAssignedDevices' => 'Este nodo de red no tiene asignado ningún dispositivo (nodo externo). Asigna uno de la lista o ve a la pestaña <b><i class="fa fa-info-circle"></i> Detalles</b> de cualquier dispositivo en la página<a href="devices.php"><b> <i class="fa fa-laptop"></i> Dispositivos</b></a>, y asígnalo a un <b><i class="fa fa-server"></i> Nodo (MAC)</b> <b><i class="fa fa-ethernet"></i> Puerto</b> de la red ahí.',
//////////////////////////////////////////////////////////////////
// Help Page - Update by @TeroRERO 07ago2022
@@ -369,13 +436,13 @@ $lang['es_es'] = array(
chmod -R 770 ~/pialert/db
</span><br>
Si la base de datos sigue siendo de sólo lectura, intente reinstalar o restaurar una copia de seguridad de la base de datos desde la página de mantenimiento.',
'HelpFAQ_Cat_General_102docker_head' => '(🐳 Docker only) Database issues (AJAX errors, read-only, not found)',
'HelpFAQ_Cat_General_102docker_text' => 'Double-check you\'ve followed the <a href="https://github.com/jokob-sk/Pi.Alert/tree/main/dockerfiles">dockerfile readme (most up-to-date info)</a>. <br/> <br/> <ul data-sourcepos="49:4-52:146" dir="auto">
<li data-sourcepos="49:4-49:106">Download the <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/db/pialert.db">original DB from GitHub</a>.</li>
<li data-sourcepos="50:4-50:195">Map the <code>pialert.db</code> file (<g-emoji class="g-emoji" alias="warning" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/26a0.png">⚠</g-emoji> not folder) from above to <code>/home/pi/pialert/db/pialert.db</code> (see <a href="https://github.com/jokob-sk/Pi.Alert/tree/main/dockerfiles#-examples">Examples</a> for details).</li>
<li data-sourcepos="51:4-51:161">If facing issues (AJAX errors, can\'t write to DB, etc,) make sure permissions are set correctly, alternatively check the logs under <code>/home/pi/pialert/front/log</code>.</li>
<li data-sourcepos="52:4-52:146">To solve permission issues you can also try to create a DB backup and then run a DB Restore via the <strong>Maintenance &gt; Backup/Restore</strong> section.</li>
<li data-sourcepos="53:4-53:228">If the database is in read-only mode you can solve this by setting the owner and group by executing the following command on the host system: <code>docker exec pialert chown -R www-data:www-data /home/pi/pialert/db/pialert.db</code>.</li>
'HelpFAQ_Cat_General_102docker_head' => '(🐳 Solo Docker) Problemas con la base de datos (errores de AJAX, solo lectura, no encontrado)',
'HelpFAQ_Cat_General_102docker_text' => 'Comprueba que has seguido las instrucciones del <a href="https://github.com/jokob-sk/Pi.Alert/tree/main/dockerfiles">dockerfile (la información más actualizada)</a>. <br/> <br/> <ul data-sourcepos="49:4-52:146" dir="auto">
<li data-sourcepos="49:4-49:106">Descarga la <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/db/pialert.db">base de datos original desde GitHub</a>.</li>
<li data-sourcepos="50:4-50:195">Mapea el archivo <code>pialert.db</code> (<g-emoji class="g-emoji" alias="warning" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/26a0.png">⚠</g-emoji> no carpeta) de arriba a <code>/home/pi/pialert/db/pialert.db</code> (puedes comprobar los <a href="https://github.com/jokob-sk/Pi.Alert/tree/main/dockerfiles#-examples">ejemplos</a> para más detalles).</li>
<li data-sourcepos="51:4-51:161">Si aparecen problemas (errores de AJAX, no se puede escribir a la base de datos, etc,) asegúrate que los permisos están establecidos correctamente. También puedes comprobar los registros en <code>/home/pi/pialert/front/log</code>.</li>
<li data-sourcepos="52:4-52:146">Para arreglar los problemas de los permisos, puedes probar a crear una copia de seguridad de la base de datos y después restaurarla desde la sección <strong>Mantenimiento &gt; Copia de seguridad/Restaurar</strong>.</li>
<li data-sourcepos="53:4-53:228">Si la base de datos está en modo solo lectura, lo puedes arreglar ejecutando el siguiente comando para establecer el propietario y grupo en el sistema host: <code>docker exec pialert chown -R www-data:www-data /home/pi/pialert/db/pialert.db</code>.</li>
</ul>',
'HelpFAQ_Cat_General_103_head' => 'La página de inicio de sesión no aparece, incluso después de cambiar la contraseña.',
'HelpFAQ_Cat_General_103_text' => 'Además de la contraseña, el archivo de configuración debe contener <span class="text-danger help_faq_code">~/pialert/config/pialert.conf</span>
@@ -411,9 +478,216 @@ $lang['es_es'] = array(
ya detectados. Esta asignación se realiza en la vista detallada del dispositivo a asignar. Por lo tanto, es posible determinar rápidamente a qué puerto está conectado un host y si está en línea. Es posible asignar un dispositivo a múltiples
puertos (agrupación de puertos), así como múltiples dispositivos a un puerto (máquinas virtuales).',
//////////////////////////////////////////////////////////////////
// Front end events
//////////////////////////////////////////////////////////////////
'test_event_tooltip' => 'Guarda tus cambios antes de probar nuevos ajustes.',
'test_event_icon' => 'fa-vial-circle-check',
'run_event_tooltip' => 'Activa el ajuste y guarda tus cambios antes de ejecutarlo.',
'run_event_icon' => 'fa-play',
'general_event_title' => 'Ejecutar un evento ad-hoc',
'general_event_description' => 'El evento que has ejecutado puede tardar un rato mientras finalizan procesos en segundo plano. La ejecución ha terminado cuando ves <code>finalizado</code> abajo. Comprueba el <a onclick=\'setCache(\"activeMaintenanceTab\", \"tab_Logging_id\")\' href=\"/maintenance.php#tab_Logging\">registro de error</a> si no has obtenido el resultado esperado. <br/> <br/> Estado: ',
//////////////////////////////////////////////////////////////////
// Plugins
//////////////////////////////////////////////////////////////////
'Plugins_Unprocessed_Events' => 'Eventos sin procesar',
'Plugins_Objects' => 'Objetos del Plugin',
'Plugins_History' => 'Historial de eventos',
//////////////////////////////////////////////////////////////////
// Settings
//////////////////////////////////////////////////////////////////
'settings_missing' => 'Actualiza la página, no todos los ajustes se han cargado. Probablemente sea por una sobrecarga de la base de datos.',
'settings_missing_block' => 'No puedes guardar los ajustes sin establecer todas las claves. Actualiza la página. Problabmente esté causado por una sobrecarga de la base de datos.',
'settings_old' => 'Los ajustes mostrados en esta página están desactualizados. Probablemente sea por un escaneo en proceso. Los ajustes se guardan en el archivo <code>pialert.conf</code>, pero el proceso en segundo plano no las ha importado todavía a la base de datos. Puedes esperar a que los ajustes se actualicen para evitar sobreescribirlos con los ajustes antiguos. Si te da igual perder los ajustes desde la última vez que guardaste y ahora, siéntete libre de guardarlos de nuevo. También hay copias de seguridad creadas si necesitas comparar tus ajustes más tarde.',
'settings_imported' => 'Última vez que los ajustes fueron importados desde el archivo pialert.conf:',
'settings_expand_all' => 'Expandir todo',
// General
'General_display_name' => 'General',
'General_icon' => '<i class="fa fa-gears"></i>',
'ENABLE_ARPSCAN_name' => 'Activar escaneo ARP',
'ENABLE_ARPSCAN_description' => 'El escaneo Arp es una herramienta de la línea de comandos que usa el protocolo ARP para encontrar e identificar la ip de los dispositivos. Una alternativa a este escaneo sería activar los ajustes de la <a onclick="toggleAllSettings()" href="#PIHOLE_ACTIVE"><code>PIHOLE_ACTIVE</code>integración con PiHole</a>.',
'SCAN_SUBNETS_name' => 'Subredes para escanear',
'SCAN_SUBNETS_description' => 'El tiempo del escaneo ARP depende del número de ips a comprobar, así que es importante establecer correctamente la máscara y la interfaz de red. Comprueba la <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SUBNETS.md" target="_blank">documentación sobre sudredes</a> para obtener ayuda para establecer VLANs, cuáles son soportadas o como averiguar la máscara y la interfaz de red.',
'LOG_LEVEL_name' => 'Imprimir registros adicionales',
'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.',
'TIMEZONE_name' => 'Zona horaria',
'TIMEZONE_description' => 'La zona horaria para mostrar las estadísticas correctamente. Encuentra tu zona horaria <a target="_blank" href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" rel="nofollow">aquí</a>.',
'ENABLE_PLUGINS_name' => 'Habilitar complementos',
'ENABLE_PLUGINS_description' => 'Habilita la funcionalidad de los <a target="_blank" href="https://github.com/jokob-sk/Pi.Alert/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.',
'PIALERT_WEB_PROTECTION_name' => 'Habilitar inicio de sesión',
'PIALERT_WEB_PROTECTION_description' => 'Cuando está habilitado, se muestra un cuadro de diálogo de inicio de sesión. Lea detenidamente a continuación si se le bloquea el acceso a su instancia.',
'PIALERT_WEB_PASSWORD_name' => 'Contraseña de inicio de sesión',
'PIALERT_WEB_PASSWORD_description' => 'La contraseña predeterminada es <code>123456</code>. Para cambiar la contraseña, ejecute <code>/home/pi/pialert/back/pialert-cli</code> en el contenedor',
'INCLUDED_SECTIONS_name' => 'Notificar en',
'INCLUDED_SECTIONS_description' => 'Especifica que eventos envían notificaciones. Elimina los tipos de eventos de los que no quieras recibir notificaciones. Este ajuste sobreescribe los ajustes específicos de los dispositivos en la interfaz. (<code>CTRL + Clic</code> para seleccionar / deseleccionar).',
'SCAN_CYCLE_MINUTES_name' => 'Retraso del ciclo de escaneo',
'SCAN_CYCLE_MINUTES_description' => 'El retraso entre escaneos. Si usa arp-scan, el tiempo de escaneo en sí depende de la cantidad de direcciones IP para verificar. Esto está influenciado por la máscara de red configurada en la configuración <a href="#SCAN_SUBNETS"><code>SCAN_SUBNETS</code></a> en la parte superior. Cada IP toma un par de segundos para escanear.',
'DAYS_TO_KEEP_EVENTS_name' => 'Eliminar eventos anteriores a',
'DAYS_TO_KEEP_EVENTS_description' => 'Esta es una configuración de mantenimiento. Esto especifica el número de días de entradas de eventos que se guardarán. Todos los eventos anteriores se eliminarán periódicamente.',
'REPORT_DASHBOARD_URL_name' => 'URL de Pi.Alert',
'REPORT_DASHBOARD_URL_description' => 'Esta URL se utiliza como base para generar enlaces en los correos electrónicos. Ingrese la URL completa que comienza con <code>http://</code>, incluido el número de puerto (sin barra inclinada al final <code>/</code>).',
'DIG_GET_IP_ARG_name' => 'Descubrir de IP de Internet',
'DIG_GET_IP_ARG_description' => 'Cambie los argumentos de la <a href="https://linux.die.net/man/1/dig" target="_blank">utilidad de dig</a> si tiene problemas para resolver su IP de Internet. Los argumentos se agregan al final del siguiente comando: <code>dig +short </code>.',
'UI_LANG_name' => 'Idioma de interfaz',
'UI_LANG_description' => 'Seleccione el idioma de interfaz de usuario preferido.',
'UI_PRESENCE_name' => 'Mostrar en el gráfico de presencia',
'UI_PRESENCE_description' => 'Elige que estados del dispositivo deben mostrarse en la gráfica de <b>Presencia del dispositivo a lo largo del tiempo</b> de la página de <a href="/devices.php" target="_blank">Dispositivos</a>. (<code>CTRL + Clic</code> para seleccionar / deseleccionar)',
// Email
'Email_display_name' => 'Email',
'Email_icon' => '<i class="fa fa-at"></i>',
'REPORT_MAIL_name' => 'Habilitar email',
'REPORT_MAIL_description' => 'Si está habilitado, se envía un correo electrónico con una lista de cambios a los que se ha suscrito. Complete también todas las configuraciones restantes relacionadas con la configuración de SMTP a continuación',
'SMTP_SERVER_name' => 'URL del servidor SMTP',
'SMTP_SERVER_description' => 'La URL del host del servidor SMTP. Por ejemplo, <code>smtp-relay.sendinblue.com</code>. Para utilizar Gmail como servidor SMTP <a target="_blank" href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SMTP_GMAIL.md">siga esta guía</a >',
'SMTP_PORT_name' => 'Puerto del servidor SMTP',
'SMTP_PORT_description' => 'Número de puerto utilizado para la conexión SMTP. Establézcalo en <code>0</code> si no desea utilizar un puerto al conectarse al servidor SMTP.',
'SMTP_SKIP_LOGIN_name' => 'Omitir autenticación',
'SMTP_SKIP_LOGIN_description' => 'No utilice la autenticación cuando se conecte al servidor SMTP.',
'SMTP_USER_name' => 'Nombre de usuario SMTP',
'SMTP_USER_description' => 'El nombre de usuario utilizado para iniciar sesión en el servidor SMTP (a veces, una dirección de correo electrónico completa).',
'SMTP_PASS_name' => 'Contraseña de SMTP',
'SMTP_PASS_description' => 'La contraseña del servidor SMTP.',
'SMTP_SKIP_TLS_name' => 'No usar TLS',
'SMTP_SKIP_TLS_description' => 'Deshabilite TLS cuando se conecte a su servidor SMTP.',
'SMTP_FORCE_SSL_name' => 'Forzar SSL',
'SMTP_FORCE_SSL_description' => 'Forzar SSL al conectarse a su servidor SMTP',
'REPORT_TO_name' => 'Enviar el email a',
'REPORT_TO_description' => 'Dirección de correo electrónico a la que se enviará la notificación.',
'REPORT_FROM_name' => 'Asunto del email',
'REPORT_FROM_description' => 'Asunto del correo electrónico de notificación.',
// Webhooks
'Webhooks_display_name' => 'Webhooks',
'Webhooks_icon' => '<i class="fa fa-circle-nodes"></i>',
'REPORT_WEBHOOK_name' => 'Habilitar webhooks',
'REPORT_WEBHOOK_description' => 'Habilite webhooks para notificaciones. Los webhooks lo ayudan a conectarse a muchas herramientas de terceros, como IFTTT, Zapier o <a href="https://n8n.io/" target="_blank">n8n</a>, por nombrar algunas. Consulte esta sencilla <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md" target="_blank">guía de n8n aquí</a> para obtener comenzó. Si está habilitado, configure los ajustes relacionados a continuación.',
'WEBHOOK_URL_name' => 'URL de destino',
'WEBHOOK_URL_description' => 'URL de destino comienza con <code>http://</code> o <code>https://</code>.',
'WEBHOOK_PAYLOAD_name' => 'Tipo de carga',
'WEBHOOK_PAYLOAD_description' => 'El formato de datos de carga de Webhook para el atributo <code>body</code> > <code>attachments</code> > <code>text</code> en el json de carga. Vea un ejemplo de la carga <a target="_blank" href="https://github.com/jokob-sk/Pi.Alert/blob/main/back/webhook_json_sample.json">aquí</a>. (por ejemplo: para discord use <code>html</code>)',
'WEBHOOK_REQUEST_METHOD_name' => 'Método de solicitud',
'WEBHOOK_REQUEST_METHOD_description' => 'El método de solicitud HTTP que se utilizará para la llamada de webhook.',
'Webhooks_settings_group' => '<i class="fa fa-circle-nodes"></i> Webhooks',
// Apprise
'Apprise_display_name' => 'Apprise',
'Apprise_icon' => '<i class="fa fa-bullhorn"></i>',
'REPORT_APPRISE_name' => 'Habilitar Apprise',
'REPORT_APPRISE_description' => 'Habilitar el envío de notificaciones a través de <a target="_blank" href="https://hub.docker.com/r/caronc/apprise">Apprise</a>.',
'APPRISE_HOST_description' => 'Apprise host URL que comienza con <code>http://</code> o <code>https://</code>. (no olvide incluir <code>/notify</code> al final)',
'APPRISE_HOST_name' => 'Apprise host URL',
'APPRISE_URL_name' => 'URL de notificación de Apprise',
'APPRISE_URL_description' => 'Informar de la URL de destino de la notificación. Por ejemplo, para Telegram sería <code>tgram://{bot_token}/{chat_id}</code>.',
// NTFY
'NTFY_display_name' => 'NTFY',
'NTFY_icon' => '<i class="fa fa-terminal"></i>',
'REPORT_NTFY_name' => 'Habilitar NTFY',
'REPORT_NTFY_description' => 'Habilitar el envío de notificaciones a través de <a target="_blank" href="https://ntfy.sh/">NTFY</a>.',
'NTFY_HOST_name' => 'NTFY host URL',
'NTFY_HOST_description' => 'URL de host NTFY que comienza con <code>http://</code> o <code>https://</code>. Puede usar la instancia alojada en <a target="_blank" href="https://ntfy.sh/">https://ntfy.sh</a> simplemente ingresando <code>https://ntfy. sh</código>.',
'NTFY_TOPIC_name' => 'Tema de NTFY',
'NTFY_TOPIC_description' => 'Tu tema secreto.',
'NTFY_USER_name' => 'Usuario de NTFY',
'NTFY_USER_description' => 'Ingrese usuario si necesita (alojar) una instancia con autenticación habilitada.',
'NTFY_PASSWORD_name' => 'Contraseña de NTFY',
'NTFY_PASSWORD_description' => 'Ingrese la contraseña si necesita (host) una instancia con autenticación habilitada.',
// Pushsafer
'PUSHSAFER_display_name' => 'Pushsafer',
'PUSHSAFER_icon' => '<i class="fa fa-bell"></i>',
'REPORT_PUSHSAFER_name' => 'Habilitar Pushsafer',
'REPORT_PUSHSAFER_description' => 'Habilitar el envío de notificaciones a través de <a target="_blank" href="https://www.pushsafer.com/">Pushsafer</a>.',
'PUSHSAFER_TOKEN_name' => 'Token de Pushsafer',
'PUSHSAFER_TOKEN_description' => 'Su clave secreta de la API de Pushsafer (token).',
'APPRISE_PAYLOAD_name' => 'Tipo de carga',
'APPRISE_PAYLOAD_description' => 'Seleccione el tipo de carga útil enviada a Apprise. Por ejemplo, <code>html</code> funciona bien con correos electrónicos, <code>text</code> con aplicaciones de chat, como Telegram.',
// MQTT
'MQTT_display_name' => 'MQTT',
'MQTT_icon' => '<i class="fa fa-square-rss"></i>',
'REPORT_MQTT_name' => 'Habilitar MQTT',
'REPORT_MQTT_description' => 'Habilitar el envío de notificaciones a través de <a target="_blank" href="https://www.home-assistant.io/integrations/mqtt/">MQTT</a> a su Home Assistance.',
'MQTT_BROKER_name' => 'URL del broker MQTT',
'MQTT_BROKER_description' => 'URL del host MQTT (no incluya <code>http://</code> o <code>https://</code>).',
'MQTT_PORT_name' => 'Puerto del broker MQTT',
'MQTT_PORT_description' => 'Puerto donde escucha el broker MQTT. Normalmente <code>1883</code>.',
'MQTT_USER_name' => 'Usuario de MQTT',
'MQTT_USER_description' => 'Nombre de usuario utilizado para iniciar sesión en su instancia de agente de MQTT.',
'MQTT_PASSWORD_name' => 'Contraseña de MQTT',
'MQTT_PASSWORD_description' => 'Contraseña utilizada para iniciar sesión en su instancia de agente de MQTT.',
'MQTT_QOS_name' => 'Calidad de servicio MQTT',
'MQTT_QOS_description' => 'Configuración de calidad de servicio para el envío de mensajes MQTT. <code>0</code>: baja calidad a <code>2</code>: alta calidad. Cuanto mayor sea la calidad, mayor será el retraso.',
'MQTT_DELAY_SEC_name' => 'Retraso de MQTT por dispositivo',
'MQTT_DELAY_SEC_description' => 'Un pequeño truco: retrase la adición a la cola en caso de que el proceso se reinicie y los procesos de publicación anteriores se anulen (se necesitan ~<code>2</code>s para actualizar la configuración de un sensor en el intermediario). Probado con <code>2</code>-<code>3</code> segundos de retraso. Este retraso solo se aplica cuando se crean dispositivos (durante el primer bucle de notificación). No afecta los escaneos o notificaciones posteriores.',
//DYNDNS
'DynDNS_display_name' => 'DynDNS',
'DynDNS_icon' => '<i class="fa fa-globe"></i>',
'DDNS_ACTIVE_name' => 'Habilitar DynDNS',
'DDNS_ACTIVE_description' => '',
'DDNS_DOMAIN_name' => 'URL del dominio DynDNS',
'DDNS_DOMAIN_description' => '',
'DDNS_USER_name' => 'Usuario de DynDNS',
'DDNS_USER_description' => '',
'DDNS_PASSWORD_name' => 'Contraseña de DynDNS',
'DDNS_PASSWORD_description' => '',
'DDNS_UPDATE_URL_name' => 'URL de actualización de DynDNS',
'DDNS_UPDATE_URL_description' => 'Actualice la URL que comienza con <code>http://</code> o <code>https://</code>.',
// PiHole
'PiHole_display_name' => 'PiHole',
'PiHole_icon' => '<i class="fa fa-seedling"></i>',
'PIHOLE_ACTIVE_name' => 'Habilitar el mapeo de PiHole',
'PIHOLE_ACTIVE_description' => 'Debe mapear <code>:/etc/pihole/pihole-FTL.db</code> en el archivo <code>docker-compose.yml</code> si habilita esta configuración.',
'DHCP_ACTIVE_name' => 'Habilitar PiHole DHCP',
'DHCP_ACTIVE_description' => 'Debe asignar <code>:/etc/pihole/dhcp.leases</code> en el archivo <code>docker-compose.yml</code> si habilita esta configuración.',
// Pholus
'Pholus_display_name' => 'Pholus',
'Pholus_icon' => '<i class="fa fa-search"></i>',
'PHOLUS_ACTIVE_name' => 'Ejecución del ciclo',
'PHOLUS_ACTIVE_description' => '<a href="https://github.com/jokob-sk/Pi.Alert/tree/main/pholus" target="_blank" >Pholus</a> es una herramienta de rastreo para descubrir información adicional sobre los dispositivos en la red, incluido el nombre del dispositivo. Si está habilitado, ejecutará el escaneo antes de cada ciclo de escaneo de red hasta que no haya dispositivos <code>(unknown)</code> o <code>(name not found)</code>. Tenga en cuenta que puede enviar spam a la red con tráfico innecesario. Depende de la configuración de <a onclick="toggleAllSettings()" href="#SCAN_SUBNETS"><code>SCAN_SUBNETS</code></a>. Para un análisis programado o único, verifique la configuración de <a href="#PHOLUS_RUN"><code>PHOLUS_RUN</code></a>.',
'PHOLUS_TIMEOUT_name' => 'Tiempo de espera de ciclo',
'PHOLUS_TIMEOUT_description' => '¿Cuánto tiempo en segundos debe rastrear Pholus en cada interfaz si se cumple la condición anterior? Cuanto más tiempo lo deje encendido, es más probable que los dispositivos transmitan más información. Este tiempo de espera se suma al tiempo que lleva realizar un escaneo arp en su red.',
'PHOLUS_FORCE_name' => 'Escaneo de fuerza de ciclo',
'PHOLUS_FORCE_description' => 'Fuerce el escaneo de cada escaneo de red, incluso si no hay dispositivos <code>(unknown)</code> o <code>(name not found)</code>. Tenga cuidado al habilitar esto, ya que la detección puede inundar fácilmente su red.',
'PHOLUS_RUN_name' => 'Ejecución programada',
'PHOLUS_RUN_description' => 'Habilite un escaneo regular de Pholus en su red. Los ajustes de programación se pueden encontrar a continuación. Si selecciona <code>una vez</code>, Pholus se ejecuta solo una vez al inicio durante el tiempo especificado en la configuración de <a href="#PHOLUS_RUN_TIMEOUT"><code>PHOLUS_RUN_TIMEOUT</code></a>.',
'PHOLUS_RUN_TIMEOUT_name' => 'Tiempo de espera de ejecución programado',
'PHOLUS_RUN_TIMEOUT_description' => 'El tiempo de espera en segundos para el escaneo Pholus programado. Se aplican las mismas notas con respecto a la duración que en la configuración de <a href="#PHOLUS_TIMEOUT"><code>PHOLUS_TIMEOUT</code></a>. Un escaneo programado no verifica si hay dispositivos <code>(unknown)</code> o <code>(name not found)</code>, el escaneo se ejecuta de cualquier manera.',
'PHOLUS_RUN_SCHD_name' => 'Programar',
'PHOLUS_RUN_SCHD_description' => 'Solo está habilitado si selecciona <code>programar</code> en la configuración de <a href="#PHOLUS_RUN"><code>PHOLUS_RUN</code></a>. Asegúrese de ingresar el horario en el formato similar a cron correcto
(por ejemplo, validar en <a href="https://crontab.guru/" target="_blank">crontab.guru</a>). Por ejemplo, ingresar <code>0 4 * * *</code> ejecutará el escaneo después de las 4 am en el <a onclick="toggleAllSettings()" href="#TIMEZONE"><code>TIMEZONE</code> que configuró arriba</a>. Se ejecutará la PRÓXIMA vez que pase el tiempo.',
'PHOLUS_DAYS_DATA_name' => 'Retención de datos',
'PHOLUS_DAYS_DATA_description' => 'Cuántos días de entradas de escaneo de Pholus deben conservarse (globalmente, ¡no específico del dispositivo!). El archivo <a href="/maintenance.php#tab_Logging">pialert_pholus.log</a> no se modifica. Introduzca <code>0</code> para desactivar.',
// NMAP
'Nmap_display_name' => 'Nmap',
'Nmap_icon' => '<i class="fa fa-ethernet"></i>',
'NMAP_ACTIVE_name' => 'Ejecución del ciclo',
'NMAP_ACTIVE_description' => 'Si está habilitado, ejecutará un escaneo en un dispositivo recién encontrado. Para un análisis programado o único, verifique la configuración de <a href="#NMAP_RUN"><code>NMAP_RUN</code></a>.',
'PHOLUS_TIMEOUT_name' => 'Tiempo de espera de ciclo',
'NMAP_TIMEOUT_description' => 'Tiempo máximo en segundos para esperar a que finalice un escaneo de Nmap en cualquier dispositivo.',
'NMAP_RUN_name' => 'Ejecución programada',
'NMAP_RUN_description' => 'Habilite un escaneo regular de Nmap en su red en todos los dispositivos. Los ajustes de programación se pueden encontrar a continuación. Si selecciona <code>una vez</code>, Nmap se ejecuta solo una vez al inicio durante el tiempo especificado en la configuración de <a href="#NMAP_TIMEOUT"><code>NMAP_TIMEOUT</code></a>.',
'NMAP_RUN_SCHD_name' => 'Programar',
'NMAP_RUN_SCHD_description' => 'Solo está habilitado si selecciona <code>programar</code> en la configuración de <a href="#NMAP_RUN"><code>NMAP_RUN</code></a>. Asegúrese de ingresar el cronograma en el formato tipo cron correcto.',
'NMAP_ARGS_name' => 'Argumentos',
'NMAP_ARGS_description' => 'Argumentos utilizados para ejecutar el análisis de Nmap. Tenga cuidado de especificar <a href="https://linux.die.net/man/1/nmap" target="_blank">los argumentos</a> correctamente. Por ejemplo, <code>-p -10000</code> escanea los puertos del 1 al 10000.',
//API
'API_display_name' => 'API',
'API_icon' => '<i class="fa fa-arrow-down-up-across-line"></i>',
'API_CUSTOM_SQL_name' => 'Endpoint personalizado',
'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>.',
);
?>
?>

View File

@@ -5,6 +5,7 @@
// ###################################
$defaultLang = "en_us";
$allLanguages = ["en_us","es_es","de_de"];
global $db;
@@ -17,6 +18,20 @@ switch($result){
if (isset($pia_lang_selected) == FALSE or (strlen($pia_lang_selected) == 0)) {$pia_lang_selected = $defaultLang;}
//Language_Strings ("Language_Code", "String_Key", "String_Value", "Extra")
$result = $db->query("SELECT * FROM Plugins_Language_Strings");
// array
$strings = array();
while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// Push row data
$strings[] = array( 'Language_Code' => $row['Language_Code'],
'String_Key' => $row['String_Key'],
'String_Value' => $row['String_Value'],
'Extra' => $row['Extra']
);
}
require dirname(__FILE__).'/../skinUI.php';
require dirname(__FILE__).'/en_us.php';
require dirname(__FILE__).'/de_de.php';
@@ -24,7 +39,13 @@ require dirname(__FILE__).'/es_es.php';
function lang($key)
{
global $pia_lang_selected, $lang, $defaultLang;
global $pia_lang_selected, $lang, $defaultLang, $strings;
// get strings from the DB and append them to the ones from the files
foreach ($strings as $string)
{
$lang[$string["Language_Code"]][$string["String_Key"]] = $string["String_Value"];
}
// check if key exists in selected language
if(array_key_exists($key, $lang[$pia_lang_selected]) == FALSE)

469
front/plugins.php Executable file
View File

@@ -0,0 +1,469 @@
<?php
require 'php/templates/header.php';
?>
<script src="js/pialert_common.js"></script>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<?php require 'php/templates/notification.php'; ?>
<h1 id="pageTitle">
<i class="fa fa-fw fa-plug"></i> <?= lang('Navigation_Plugins');?>
<span class="pageHelp"> <a target="_blank" href="https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins"><i class="fa fa-circle-question"></i></a><span>
</h1>
</section>
<!-- Main content ---------------------------------------------------------- -->
<section class="content">
<div class="nav-tabs-custom plugin-content" style="margin-bottom: 0px;">
<ul id="tabs-location" class="nav nav-tabs">
<!-- PLACEHOLDER -->
</ul>
<div id="tabs-content-location" class="tab-content">
<!-- PLACEHOLDER -->
</div>
</section>
</div>
<script defer>
// -----------------------------------------------------------------------------
// Get form control according to the column definition from config.json > database_column_definitions
function getFormControl(dbColumnDef, value, index) {
result = ''
switch(dbColumnDef.type)
{
case 'label':
result = `<span>${value}<span>`;
break;
case 'textboxsave':
value = value == 'null' ? '' : value; // hide 'null' values
id = `${dbColumnDef.column}_${index}`
result = `<span class="form-group">
<div class="input-group">
<input class="form-control" type="text" value="${value}" id="${id}" data-my-column="${dbColumnDef.column}" data-my-index="${index}" name="${dbColumnDef.column}">
<span class="input-group-addon"><i class="fa fa-save pointer" onclick="saveData('${id}');"></i></span>
</div>
<span>`;
break;
case 'url':
result = `<span><a href="${value}" target="_blank">${value}</a><span>`;
break;
case 'devicemac':
result = `<span class="anonymizeMac"><a href="/deviceDetails.php?mac=${value}" target="_blank">${value}</a><span>`;
break;
case 'deviceip':
result = `<span class="anonymizeIp"><a href="#" onclick="navigateToDeviceWithIp('${value}')" >${value}</a><span>`;
break;
case 'threshold':
$.each(dbColumnDef.options, function(index, obj) {
if(Number(value) < obj.maximum && result == '')
{
result = `<div style="background-color:${obj.hexColor}">${value}</div>`
// return;
}
});
break;
case 'replace':
$.each(dbColumnDef.options, function(index, obj) {
if(value == obj.equals)
{
result = `<span title="${value}">${obj.replacement}</span>`
}
});
break;
default:
result = value;
}
return result;
}
// -----------------------------------------------------------------------------
// Update the coresponding DB column and entry
function saveData (id) {
columnName = $(`#${id}`).attr('data-my-column')
index = $(`#${id}`).attr('data-my-index')
columnValue = $(`#${id}`).val()
$.get(`php/server/dbHelper.php?action=update&dbtable=Plugins_Objects&columnName=Index&id=${index}&columns=UserData&values=${columnValue}`, function(data) {
// var result = JSON.parse(data);
console.log(data)
if(sanitize(data) == 'OK')
{
showMessage('<?= lang('Gen_DataUpdatedUITakesTime');?>')
// Remove navigation prompt "Are you sure you want to leave..."
window.onbeforeunload = null;
} else
{
showMessage('<?= lang('Gen_LockedDB');?>')
}
});
}
// -----------------------------------------------------------------------------
// Get translated string
function localize (obj, key) {
currLangCode = getCookie("language")
result = ""
en_us = ""
if(obj.localized && obj.localized.includes(key))
{
for(i=0;i<obj[key].length;i++)
{
code = obj[key][i]["language_code"]
if( code == 'en_us')
{
en_us = obj[key][i]["string"]
}
if(code == currLangCode)
{
result = obj[key][i]["string"]
}
}
}
result == "" ? result = en_us : result ;
return result;
}
// -----------------------------------------------------------------------------
pluginDefinitions = []
pluginUnprocessedEvents = []
pluginObjects = []
pluginHistory = []
function getData(){
$.get('api/plugins.json', function(res) {
pluginDefinitions = res["data"];
$.get('api/table_plugins_events.json', function(res) {
pluginUnprocessedEvents = res["data"];
$.get('api/table_plugins_objects.json', function(res) {
pluginObjects = res["data"];
$.get('api/table_plugins_history.json', function(res) {
pluginHistory = res["data"];
generateTabs()
});
});
});
});
}
// -----------------------------------------------------------------------------
function generateTabs()
{
activetab = 'active'
$.each(pluginDefinitions, function(index, obj) {
// console.log(obj)
$('#tabs-location').append(
`<li class=" ${activetab}">
<a href="#${obj.unique_prefix}" data-plugin-prefix="${obj.unique_prefix}" id="${obj.unique_prefix}_id" data-toggle="tab" >
${localize(obj, 'icon')} ${localize(obj, 'display_name')}
</a>
</li>`
);
activetab = '' // only first tab is active
});
activetab = 'active'
$.each(pluginDefinitions, function(index, obj) {
headersHtml = ""
colDefinitions = []
evRows = ""
obRows = ""
hiRows = ""
// Generate the header
$.each(obj["database_column_definitions"], function(index, colDef){
if(colDef.show == true) // select only the ones to show
{
colDefinitions.push(colDef)
headersHtml += `<th class="${colDef.css_classes}" >${localize(colDef, "name" )}</th>`
}
});
// Generate the event rows
var eveCount = 0;
for(i=0;i<pluginUnprocessedEvents.length;i++)
{
if(pluginUnprocessedEvents[i].Plugin == obj.unique_prefix)
{
clm = ""
for(j=0;j<colDefinitions.length;j++)
{
clm += '<td>'+ pluginUnprocessedEvents[i][colDefinitions[j].column] +'</td>'
}
evRows += `<tr data-my-index="${pluginUnprocessedEvents[i]["Index"]}" >${clm}</tr>`
eveCount++;
}
}
// Generate the history rows
var histCount = 0
var histCountDisplayed = 0
for(i=pluginHistory.length-1;i >= 0;i--) // from latest to the oldest
{
if(pluginHistory[i].Plugin == obj.unique_prefix)
{
if(histCount < 50) // only display 50 entries to optimize performance
{
clm = ""
for(j=0;j<colDefinitions.length;j++)
{
clm += '<td>'+ pluginHistory[i][colDefinitions[j].column] +'</td>'
}
hiRows += `<tr data-my-index="${pluginHistory[i]["Index"]}" >${clm}</tr>`
histCountDisplayed++;
}
histCount++; // count and display the total
}
}
// Generate the object rows
var obCount = 0;
for(var i=0;i<pluginObjects.length;i++)
{
if(pluginObjects[i].Plugin == obj.unique_prefix)
{
clm = ""
for(var j=0;j<colDefinitions.length;j++)
{
clm += '<td>'+ getFormControl(colDefinitions[j], pluginObjects[i][colDefinitions[j].column], pluginObjects[i]["Index"]) +'</td>'
}
obRows += `<tr data-my-index="${pluginObjects[i]["Index"]}" >${clm}</tr>`
obCount++;
}
}
$('#tabs-content-location').append(
`
<div id="${obj.unique_prefix}" class="tab-pane ${activetab}">
<div class="nav-tabs-custom" style="margin-bottom: 0px">
<ul class="nav nav-tabs">
<li class="active" >
<a href="#objectsTarget_${obj.unique_prefix}" data-toggle="tab" >
<i class="fa fa-cube"></i> <?= lang('Plugins_Objects');?> (${obCount})
</a>
</li>
<li >
<a href="#eventsTarget_${obj.unique_prefix}" data-toggle="tab" >
<i class="fa fa-bolt"></i> <?= lang('Plugins_Unprocessed_Events');?> (${eveCount})
</a>
</li>
<li >
<a href="#historyTarget_${obj.unique_prefix}" data-toggle="tab" >
<i class="fa fa-clock"></i> <?= lang('Plugins_History');?> (${histCountDisplayed} out of ${histCount} )
</a>
</li>
<ul>
</div>
<div class="tab-content">
<div id="objectsTarget_${obj.unique_prefix}" class="tab-pane ${activetab}">
<table class="table table-striped" data-my-dbtable="Plugins_Objects">
<tbody>
<tr>
${headersHtml}
</tr>
${obRows}
</tbody>
</table>
<div class="plugin-obj-purge">
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Objects' )"><?= lang('Gen_DeleteAll');?></button>
</div>
</div>
<div id="eventsTarget_${obj.unique_prefix}" class="tab-pane">
<table class="table table-striped" data-my-dbtable="Plugins_Events">
<tbody>
<tr>
${headersHtml}
</tr>
${evRows}
</tbody>
</table>
<div class="plugin-obj-purge">
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Events' )"><?= lang('Gen_DeleteAll');?></button>
</div>
</div>
<div id="historyTarget_${obj.unique_prefix}" class="tab-pane">
<table class="table table-striped" data-my-dbtable="Plugins_History">
<tbody>
<tr>
${headersHtml}
</tr>
${hiRows}
</tbody>
</table>
<div class="plugin-obj-purge">
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_History' )"><?= lang('Gen_DeleteAll');?></button>
</div>
</div>
</div>
<div class='plugins-description'>
${localize(obj, 'description')}
<span>
<a href="https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins/${obj.code_name}" target="_blank"><?= lang('Gen_Help');?></a>
</span>
</div>
</div>
`);
activetab = '' // only first tab is active
});
initTabs()
}
// --------------------------------------------------------
// handle first tab (objectsTarget_) display
function initTabs()
{
// events on tab change
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href") // activated tab
// save the last prefix
if(target.includes('_') == false )
{
pref = target.split('#')[1]
} else
{
pref = target.split('_')[1]
}
everythingHidden = false;
if($('#objectsTarget_'+ pref) != undefined && $('#historyTarget_'+ pref) != undefined && $('#eventsTarget_'+ pref) != undefined)
{
everythingHidden = $('#objectsTarget_'+ pref).attr('class').includes('active') == false && $('#historyTarget_'+ pref).attr('class').includes('active') == false && $('#eventsTarget_'+ pref).attr('class').includes('active') == false;
}
// show the objectsTarget if no specific pane selected or if selected is hidden
if((target == '#'+pref ) && everythingHidden)
{
var classTmp = $('#objectsTarget_'+ pref).attr('class');
if($('#objectsTarget_'+ pref).attr('class').includes('active') == false)
{
classTmp += ' active';
$('#objectsTarget_'+ pref).attr('class', classTmp)
}
}
});
}
// --------------------------------------------------------
plugPrefix = ''
dbTable = ''
function purgeAll(callback) {
plugPrefix = arguments[0]; // plugin prefix
dbTable = arguments[1]; // DB table
// Ask
showModalWarning('<?= lang('Gen_Purge');?>' + ' ' + plugPrefix + ' ' + dbTable , '<?= lang('Gen_AreYouSure');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', "purgeAllExecute");
}
// --------------------------------------------------------
function purgeAllExecute() {
$.ajax({
method: "POST",
url: "php/server/dbHelper.php",
data: { action: "delete", dbtable: dbTable, columnName: 'Plugin', id:plugPrefix },
success: function(data, textStatus) {
showModalOk ('Result', data );
}
})
}
// --------------------------------------------------------
function purgeVisible() {
idArr = $(`#${plugPrefix} table[data-my-dbtable="${dbTable}"] tr[data-my-index]`).map(function(){return $(this).attr("data-my-index");}).get();
$.ajax({
method: "POST",
url: "php/server/dbHelper.php",
data: { action: "delete", dbtable: dbTable, columnName: 'Index', id:idArr.toString() },
success: function(data, textStatus) {
showModalOk ('Result', data );
}
})
}
// -----------------------------------------------------------------------------
getData()
</script>
<?php
require 'php/templates/footer.php';
?>

462
front/plugins/README.md Executable file
View File

@@ -0,0 +1,462 @@
## Overview
| ![Screen 1][screen1] | ![Screen 2][screen2] |
|----------------------|----------------------|
| ![Screen 3][screen3] | ![Screen 4][screen4] |
PiAlert comes with a plugin system to feed events from third-party scripts into the UI and then send notifications, if desired. The highlighted functionality this plugin system supports, is dynamic creation of a simple UI to interact with the discovered objects, a mechanism to surface settings of plugins in the UI, or to import objects into existing PiAlert database tables. (Currently update/overwriting of existing objects is not supported.)
Example use cases for plugins could be:
* Monitor a web service and alert me if it's down
* Import devices from dhcp.leases files instead/complementary to using PiHole or arp-scans
* Creating ad-hoc UI tables from existing data in the PiAlert database, e.g. to show all open ports on devices, to list devices that disconnected in the last hour, etc.
* Using other device discovery methods on the network and importing the data as new devices
* Creating a script to create FAKE devices based on user input via custom settings
* ...at this point the limitation is mostly the creativity than the capability (there might be edge cases and need to support more form controls for user input off custom settings, but you probably get the idea)
If you wish to develop a plugin, please check the existing plugin structure. Once the settings are saved by the user they need to be removed from the `pialert.conf` file manually if you want to re-initialize them from the `config.json` of the plugin.
Again, please read the below carefully if you'd like to contribute with a plugin yourself. This documentation file might be outdated, so double check the sample plugins as well.
## ⚠ Disclaimer
Experimental feature used also to speed up development and to make the app more maintainable. Follow the below very carefully and check example plugin(s) if you'd like to write one yourself. Plugin UI is not my priority right now, happy to approve PRs if you are interested in extending/improvintg the UI experience. Example improvements for the taking:
* Making the tables sortable/filterable
* Using the same approach to display table data as in the Devices section (solves above)
* Adding form controls supported to display the data (Currently supported ones are listed in the section "UI settings in database_column_definitions" below)
* ...
## ❗ Known issues:
These issues will be hopefully fixed with time, so please don't report them. Instead, if you know how, feel free to investigate and submit a PR to fix the below. Keep the PRs small as it's easier to approve them:
* Existing plugin objects sometimes not interpreted correctly and a new object is created instead, resulting in duplicate entries.
* Occasional (experienced twice) hanging of processing plugin script file.
* UI displaying outdated values until the API endpoints get refreshed.
## Plugin file structure overview
> Folder name must be the same as the code name value in: `"code_name": "<value>"`
> Unique prefix needs to be unique compared to the other settings prefixes, e.g.: the prefix `APPRISE` is already in use.
| File | Required | Description |
|----------------------|----------------------|----------------------|
| `config.json` | yes | Contains the plugin configuration (manifest) including the settings available to the user. |
| `script.py` | yes (script) | The Python script itself |
| `last_result.log` | yes (script) | The file used to interface between PiAlert and the plugin (script). |
| `script.log` | no | Logging output (recommended) |
| `README.md` | no | Any setup considerations or overview (recommended) |
More on specifics below.
### Column order and values
| Order | Represented Column | Required | Description |
|----------------------|----------------------|----------------------|----------------------|
| 0 | `Object_PrimaryID` | yes | The primary ID used to group Events under. |
| 1 | `Object_SecondaryID` | no | Optional secondary ID to create a relationship beween other entities, such as a MAC address |
| 2 | `DateTime` | yes | When the event occured in the format `2023-01-02 15:56:30` |
| 3 | `Watched_Value1` | yes | A value that is watched and users can receive notifications if it changed compared to the previously saved entry. For example IP address |
| 4 | `Watched_Value2` | no | As above |
| 5 | `Watched_Value3` | no | As above |
| 6 | `Watched_Value4` | no | As above |
| 7 | `Extra` | no | Any other data you want to pass and display in PiAlert and the notifications |
| 8 | `ForeignKey` | no | A foreign key that can be used to link to the parent object (usually a MAC address) |
# config.json structure
## Supported data sources
Currently only two data sources are supported:
- Script
- SQL query on the PiAlert database
You need to set the `data_source` to either `pialert-db-query` or `python-script`:
```json
"data_source": "pialert-db-query"
```
Any of the above datasources have to return a "table" of the exact structure as outlined above.
### "data_source": "python-script"
If the datasource is set to `python-script` the `CMD` setting (that you specify in the `settings` array section in the `config.json`) needs to contain a executable linux command, that generates a `last_result.log` file. This file needs to be stored in the same folder as the plugin.
The content of the `last_result.log` file needs to contain the columns as defined in the "Column order and values" section above. The order of columns can't be changed. After every scan it should contain only the results from the latest scan/execution.
- The format of the `last_result.log` is a `csv`-like file with the pipe `|` as a separator.
- 9 (nine) values need to be supplied, so every line needs to contain 8 pipe separators. Empty values are represented by `null`.
- Don't render "headers" for these "columns".
- Every scan result / event entry needs to be on a new line.
- You can find which "columns" need to be present, and if the value is required or optional, in the "Column order and values" section.
- The order of these "columns" can't be changed.
### 👍 Python script.py tips
The [Undicoverables plugins `script.py` file](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/undiscoverables/script.py) is a good and simple example to start with if you are considering creating a custom plugin. It uses the [`plugin_helper.py` library](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/plugin_helper.py) that significantly simplifies the creation of your custom script.
#### last_result.log examples
Valid CSV:
```csv
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|null|null|null|null
https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|ff:ee:ff:11:ff:11
```
Invalid CSV with different errors on each line:
```csv
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898||null|null|null
https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|
|https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
null|192.168.1.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine
https://www.duckduckgo.com|192.168.1.1|2023-01-02 15:56:30|null|0.9898|null|null|Best search engine
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|
```
### "data_source": "pialert-db-query"
If the datasource is set to `pialert-db-query` the `CMD` setting needs to contain a SQL query rendering the columns as defined in the "Column order and values" section above. The order of columns is important.
This SQL query is executed on the `pialert.db` SQLite database file.
#### Examples
SQL query example:
```SQL
SELECT dv.dev_Name as Object_PrimaryID,
cast(dv.dev_LastIP 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.dev_MAC as ForeignKey
FROM
(SELECT * FROM Nmap_Scan) ns
LEFT JOIN
(SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv
ON ns.MAC = dv.dev_MAC
```
Required `CMD` setting example with above query (you can set `"type": "label"` if you want it to make uneditable in the UI):
```json
{
"function": "CMD",
"type": "text",
"default_value":"SELECT dv.dev_Name as Object_PrimaryID, cast(dv.dev_LastIP 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 FROM (SELECT * FROM Nmap_Scan) ns LEFT JOIN (SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv ON ns.MAC = dv.dev_MAC",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "SQL to run"
}],
"description": [{
"language_code":"en_us",
"string" : "This SQL query is used to populate the coresponding UI tables under the Plugins section."
}]
}
```
### Mapping the plugin results into a database table
PiAlert will take the results of the plugin execution and insert these results into a database table, if a plugin contains the property `"mapped_to_table"` in the `config.json` root. The mapping of the columns is defined in the `database_column_definitions` array.
This approach is used to implement the `DHCPLSS` plugin. The script parses all supplied "dhcp.leases" files, get's the results in the generic table format outlined in the "Column order and values" section above and takes individual values and inserts them into the `"DHCP_Leases"` database table in the PiAlert database. All this is achieved by:
1) Specifying the database table into which the results are inserted by defining `"mapped_to_table": "DHCP_Leases"` in the root of the `config.json` file as shown below:
```json
{
"code_name": "dhcp_leases",
"unique_prefix": "DHCPLSS",
...
"data_source": "python-script",
"localized": ["display_name", "description", "icon"],
"mapped_to_table": "DHCP_Leases",
...
}
```
2) Defining the target column with the `mapped_to_column` property for individual columns in the `database_column_definitions` array of the `config.json` file. For example in the `DHCPLSS` plugin, I needed to map the value of the `Object_PrimaryID` column returned by the plugin, to the `DHCP_MAC` column in the PiAlert database `DHCP_Leases` table. Notice the `"mapped_to_column": "DHCP_MAC"` key-value pair in the sample below.
```json
{
"column": "Object_PrimaryID",
"mapped_to_column": "DHCP_MAC",
"css_classes": "col-sm-2",
"show": true,
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "MAC address"
}]
}
```
3) That's it. PiAlert takes care of the rest. It loops thru the objects discovered by the plugin, takes the results line, by line and inserts them into the database table specified in `"mapped_to_table"`. The columns are translated from the generic plugin columns to the target table via the `"mapped_to_column"` property in the column definitions.
#### params
The `params` array in the `config.json` is used to enable the user to change the parameters of the executed script. For example, the user wants to monitor a specific URL.
##### Example:
Passing user defined settings to a command. Let's say, you want to have a script, that is called with a user-defined parameter called `urls`:
```bash
root@server# python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls=https://google.com,https://duck.com
```
* You can allow the user to add URLs to a setting with the `function` property set to a custom name, such as `urls_to_check` (this is not a reserved name from the section "Supported settings `function` values" below).
* You specify the parameter `urls` in the `params` section of the `config.json` the following way (`WEBMON_` is the plugin prefix automatically added to all the settings):
```json
{
"params" : [
{
"name" : "urls",
"type" : "setting",
"value" : "WEBMON_urls_to_check"
}]
}
```
* Then you use this setting as an input parameter for your command in the `CMD` setting. Notice `urls={urls}` in the below json:
```json
{
"function": "CMD",
"type": "text",
"default_value":"python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls={urls}",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Command"
}],
"description": [{
"language_code":"en_us",
"string" : "Command to run"
}]
}
```
During script execution, the app will take the command `"python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls={urls}"`, take the `{urls}` wildcard and replace it by with the value from the `WEBMON_urls_to_check` setting. This is because:
1) The app checks the `params` entries
2) It finds `"name" : "urls"`
3) Checks the type of the `urls` params and finds `"type" : "setting"`
4) Gets the setting name from `"value" : "WEBMON_urls_to_check"`
- IMPORTANT: in the `config.json` this setting is identified by `"function":"urls_to_check"`, not `"function":"WEBMON_urls_to_check"`
- You can also use a global setting, or a setting from a different plugin
5) The app gets the user defined value from the setting with the code name `WEBMON_urls_to_check`
- let's say the setting with the code name `WEBMON_urls_to_check` contains 2 values entered by the user:
- `WEBMON_urls_to_check=['https://google.com','https://duck.com']`
6) The app takes the value from `WEBMON_urls_to_check` and replaces the `{urls}` wildcard in the setting where `"function":"CMD"`, so you go from:
- `python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls={urls}`
- to
- `python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls=https://google.com,https://duck.com`
Below are some general additional notes, when definig `params`:
- `"name":"name_value"` - is used as a wildcard replacement in the `CMD` setting value by using curly brackets `{name_value}`. The wildcard is replaced by the result of the `"value" : "param_value"` and `"type":"type_value"` combo configuration below.
- `"type":"<sql|setting>"` - is used to specify the type of the params, currently only 2 supported (`sql`,`setting`).
- `"type":"sql"` - will execute the SQL query specified in the `value` property. The sql query needs to return only one column. The column is flattened and separated by commas (`,`), e.g: `SELECT dev_MAC from DEVICES` -> `Internet,74:ac:74:ac:74:ac,44:44:74:ac:74:ac`. This is then used to replace the wildcards in the `CMD` setting.
- `"type":"setting"` - The setting code name. A combination of the value from `unique_prefix` + `_` + `function` value, or otherwise the code name you can find in the Settings page under the Setting dispaly name, e.g. `SCAN_CYCLE_MINUTES`.
- `"value" : "param_value"` - Needs to contain a setting code name or sql query without wildcards.
Example:
```json
{
"params" : [{
"name" : "macs",
"type" : "sql",
"value" : "SELECT dev_MAC from DEVICES"
},
{
"name" : "urls",
"type" : "setting",
"value" : "WEBMON_urls_to_check"
},
{
"name" : "internet_ip",
"type" : "setting",
"value" : "WEBMON_SQL_internet_ip"
}]
}
```
#### Setting object struncture
- `"function": "<see Supported settings function values>"` - What function the setting drives or a simple unique code name
- `"type": "<text|integer|boolean|password|readonly|selectinteger|selecttext|multiselect|list>"` - The form control used for the setting displayed in the Settings page and what values are accepted.
- `"localized"` - a list of properties on the current JSON level which need to be localized
- `"name"` and `"description"` - Displayed in the Settings page. An array of localized strings. (see Localized strings below).
##### Supported settings `function` values
You can have any `"function": "my_custom_name"` custom name, however, the ones listed below have a specific functionality attached to them. If you use a custom name, then the setting is mostly used as an input parameter for the `params` section.
- `RUN` - (required) Specifies when the service is executed
- Supported Options: "disabled", "once", "schedule" (if included then a `RUN_SCHD` setting needs to be specified), "always_after_scan", "on_new_device"
- `RUN_SCHD` - (required if you include the above `RUN` function) Cron-like scheduling used if the `RUN` setting set to `schedule`
- `CMD` - (required) What command should be executed.
- `API_SQL` - (optional) Generates a `table_` + code_name + `.json` file as per [API docs](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md).
- `RUN_TIMEOUT` - (optional) Max execution time of the script. If not specified a default value of 10 seconds is used to prevent hanging.
- `WATCH` - (optional) Which database columns are watched for changes for this particular plugin. If not specified no notifications are sent.
- `REPORT_ON` - (optional) Send a notification only on these statuses. Supported options are:
- `new` means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered.
- `watched-changed` - means that selected `Watched_ValueN` columns changed
- `watched-not-changed` - reports even on events where selected `Watched_ValueN` did not change
Example:
```json
{
"function": "RUN",
"type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "When to run"
}],
"description": [{
"language_code":"en_us",
"string" : "Enable a regular scan of your services. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) for the time specified in <a href=\"#WEBMON_RUN_TIMEOUT\"><code>WEBMON_RUN_TIMEOUT</code> setting</a>."
}]
}
```
##### Localized strings
- `"language_code":"<en_us|es_es|de_de>"` - code name of the language string. Only these three currently supported. At least the `"language_code":"en_us"` variant has to be defined.
- `"string"` - The string to be displayed in the given language.
Example:
```json
{
"language_code":"en_us",
"string" : "When to run"
}
```
##### UI settings in database_column_definitions
The UI will adjust how columns are displayed in the UI based on the definition of the `database_column_definitions` object. Thease are the supported form controls and related functionality:
- Only columns with `"show": true` and also with at least an English translation will be shown in the UI.
- Supported types: `label`, `text`, `threshold`, `replace`, `deviceip`, `devicemac`, `url`. Check for details below, how columns behave based on the type.
- `label` makes a column display only
- `text` makes a column editable and a save icon is displayed next to it.
- See below for information on `threshold`, `replace`
- The `options` property is used in conjunction with these types:
- `threshold` - The `options` array contains objects from lowest `maximum` to highest with corresponding `hexColor` used for the value background color if it's less than the specified `maximum`, but more than the previous one in the `options` array
- `replace` - The `options` array contains objects with an `equals` property, that is compared to the "value" and if the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value"
- `devicemac` - The value is considered to be a mac address and a link pointing to the device with the given mac address is generated.
- `deviceip` - The value is considered to be an IP address and a link pointing to the device with the given IP is generated. The IP is cheked against the last detected IP addresses and translated into a mac address that is then used for the link itself.
- `url` - The value is considered to be a url so a link is generated.
```json
{
"column": "Watched_Value1",
"css_classes": "col-sm-2",
"show": true,
"type": "threshold",
"default_value":"",
"options": [
{
"maximum": 199,
"hexColor": "#792D86"
},
{
"maximum": 299,
"hexColor": "#5B862D"
},
{
"maximum": 399,
"hexColor": "#7D862D"
},
{
"maximum": 499,
"hexColor": "#BF6440"
},
{
"maximum": 599,
"hexColor": "#D33115"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status code"
}]
},
{
"column": "Status",
"show": true,
"type": "replace",
"default_value":"",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<i class='fa-solid fa-square-check'></i>"
},
{
"equals": "watched-changed",
"replacement": "<i class='fa-solid fa-triangle-exclamation'></i>"
},
{
"equals": "new",
"replacement": "<i class='fa-solid fa-circle-plus'></i>"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status"
}]
}
```
## Full Examples
### Script based plugins
- [website_monitor (WEBMON) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/website_monitor/config.json)
- [dhcp_servers (DHCPSRVS) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/dhcp_servers/config.json)
- [dhcp_leases (DHCPLSS) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/dhcp_leases/config.json)
- [unifi_import (UNFIMP) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/unifi_import/config.json)
- [snmp_discovery (SNMPDSC) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/snmp_discovery/config.json)
- [undiscoverables (UNDIS) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/undiscoverables/config.json)
### SQL query based plugins
- [nmap_services (NMAPSERV) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/nmap_services/config.json)
[screen1]: https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/plugins.png "Screen 1"
[screen2]: https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/plugins_settings.png "Screen 2"
[screen3]: https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/plugins_json_settings.png "Screen 3"
[screen4]: https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/plugins_json_ui.png "Screen 4"

View File

@@ -0,0 +1,53 @@
## Overview
A plugin allowing for importing devices from DHCP.leases files.
### Usage
- Specify full paths of all `dhcp.leases` files you want to import and watch in the `DHCPLSS_paths_to_check`setting.
- Map the paths specified in the `DHCPLSS_paths_to_check`setting in your `docker-compose.yml` file.
#### Example:
`docker-compose.yml` excerpt:
```yaml
volumes:
...
# mapping different dhcp.leases files
- /first/location/dhcp.leases:/mnt/dhcp1.leases
- /second/location/dhcp.leases:/mnt/dhcp2.leases
...
```
`DHCPLSS_paths_to_check` Setting:
```python
DHCPLSS_paths_to_check = ['/mnt/dhcp1.leases','/mnt/dhcp2.leases']
```
### Notes
- No specific configuration needed.
- This plugin expects the dhcp.leases file(s) to be in the format of **dhcpd.leases** that is different to the format that PiHole uses.
[dhcpd.leases(5) - Linux man page]( https://linux.die.net/man/5/dhcpd.leases#:~:text=This%20database%20is%20a%20free,file%20is%20the%20current%20one.)
Example File Format: _(not all lines are required)_
```
lease 192.168.79.15 {
starts 0 2016/08/21 13:25:45;
ends 0 2016/08/21 19:25:45;
cltt 0 2016/08/21 13:25:45;
binding state active;
next binding state free;
rewind binding state free;
hardware ethernet 8c:1a:bf:11:00:ea;
uid "\001\214\032\277\021\000\352";
option agent.circuit-id 0:17;
option agent.remote-id c0:a8:9:5;
client-hostname "android-8182e21c852776e7";
}
```

View File

@@ -0,0 +1,327 @@
{
"code_name": "dhcp_leases",
"unique_prefix": "DHCPLSS",
"enabled": true,
"data_source": "python-script",
"localized": ["display_name", "description", "icon"],
"mapped_to_table": "DHCP_Leases",
"display_name" : [{
"language_code":"en_us",
"string" : "DHCP Leases"
}],
"icon":[{
"language_code":"en_us",
"string" : "<i class=\"fa-solid fa-hourglass-half\"></i>"
}],
"description": [{
"language_code":"en_us",
"string" : "This plugin is to import devices from dhcp.leases files."
}],
"params" : [
{
"name" : "paths",
"type" : "setting",
"value" : "DHCPLSS_paths_to_check"
}],
"database_column_definitions":
[
{
"column": "Index",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Plugin",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
},
{
"column": "Object_PrimaryID",
"mapped_to_column": "DHCP_MAC",
"css_classes": "col-sm-2",
"show": true,
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "MAC address"
}]
},
{
"column": "Object_SecondaryID",
"mapped_to_column": "DHCP_IP",
"css_classes": "col-sm-2",
"show": true,
"type": "deviceip",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "IP"
}]
} ,
{
"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",
"mapped_to_column": "DHCP_DateTime",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Changed"
}]
},
{
"column": "Watched_Value1",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Is active"
}]
},
{
"column": "Watched_Value2",
"mapped_to_column": "DHCP_Name",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Hostname"
}]
},
{
"column": "Watched_Value3",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Hardware"
}]
} ,
{
"column": "Watched_Value4",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "State"
}]
} ,
{
"column": "UserData",
"css_classes": "col-sm-2",
"show": false,
"type": "textboxsave",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Comments"
}]
},
{
"column": "Extra",
"css_classes": "col-sm-3",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "DHCP leases file"
}]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"type": "replace",
"default_value":"",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status"
}]
}
],
"settings":[
{
"function": "RUN",
"type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "When to run"
}],
"description": [{
"language_code":"en_us",
"string" : "Enable import of devices from <code>dhcp.leases</code> files. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) or after you update your settings."
}]
},
{
"function": "CMD",
"type": "text",
"default_value":"python3 /home/pi/pialert/front/plugins/dhcp_leases/script.py paths={paths}",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Command"
}],
"description": [{
"language_code":"en_us",
"string" : "Command to run"
}]
},
{
"function": "paths_to_check",
"type": "list",
"default_value":["/mnt/dhcp1.leases", "/mnt/dhcp2.leases"],
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Files"
}],
"description": [{
"language_code":"en_us",
"string" : "Add all dhcp.leases mapped paths to watch. Enter full path within the container, e.g. <code>/mnt/dhcp2.leases</code>. You must map these files accordingly in your <code>docker-compose.yml</code> file."
}]
},
{
"function": "RUN_SCHD",
"type": "text",
"default_value":"0 2 * * *",
"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=\"#DHCPLSS_RUN\"><code>DHCPLSS_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 <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
}]
},
{
"function": "RUN_TIMEOUT",
"type": "integer",
"default_value":5,
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Run timeout"
},
{
"language_code":"de_de",
"string" : "Wartezeit"
}],
"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."
}]
},
{
"function": "WATCH",
"type": "multiselect",
"default_value":["Watched_Value1", "Watched_Value4"],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Watched"
}] ,
"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 Active </li><li><code>Watched_Value2</code> is Hostname </li><li><code>Watched_Value3</code> is hardware </li><li><code>Watched_Value4</code> is State </li></ul>"
}]
},
{
"function": "REPORT_ON",
"type": "multiselect",
"default_value":["new","watched-changed"],
"options": ["new","watched-changed","watched-not-changed"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Report on"
}] ,
"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."
}]
}
]
}

View File

@@ -0,0 +1,116 @@
#!/usr/bin/env python
# Based on the work of https://github.com/leiweibau/Pi.Alert
from __future__ import unicode_literals
from time import sleep, time, strftime
import requests
import pathlib
import threading
import subprocess
import socket
import argparse
import io
import sys
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import pwd
import os
from dhcp_leases import DhcpLeases
curPath = str(pathlib.Path(__file__).parent.resolve())
log_file = curPath + '/script.log'
last_run = curPath + '/last_result.log'
print(last_run)
# Workflow
def main():
last_run_logfile = open(last_run, 'a')
# empty file
last_run_logfile.write("")
parser = argparse.ArgumentParser(description='Import devices from dhcp.leases files')
parser.add_argument('paths', action="store", help="absolute dhcp.leases file paths to check separated by ','")
values = parser.parse_args()
# parse output
newEntries = []
if values.paths:
for path in values.paths.split('=')[1].split(','):
newEntries = get_entries(newEntries, path)
for e in newEntries:
# Insert list into the log
service_monitoring_log(e.primaryId, e.secondaryId, e.created, e.watched1, e.watched2, e.watched3, e.watched4, e.extra, e.foreignKey )
# -----------------------------------------------------------------------------
def service_monitoring_log(primaryId, secondaryId, created, watched1, watched2 = '', watched3 = '', watched4 = '', extra ='', foreignKey ='' ):
if watched1 == '':
watched1 = 'null'
if watched2 == '':
watched2 = 'null'
if watched3 == '':
watched3 = 'null'
if watched4 == '':
watched4 = 'null'
with open(last_run, 'a') as last_run_logfile:
# https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format(
primaryId,
secondaryId,
created,
watched1,
watched2,
watched3,
watched4,
extra,
foreignKey
)
)
# -----------------------------------------------------------------------------
def get_entries(newEntries, path):
leases = DhcpLeases(path)
leasesList = leases.get()
for lease in leasesList:
tmpPlugObj = plugin_object_class(lease.ethernet, lease.ip, lease.active, lease.hostname, lease.hardware, lease.binding_state, path)
newEntries.append(tmpPlugObj)
return newEntries
# -------------------------------------------------------------------
class plugin_object_class:
def __init__(self, primaryId = '',secondaryId = '', watched1 = '',watched2 = '',watched3 = '',watched4 = '',extra = '',foreignKey = ''):
self.pluginPref = ''
self.primaryId = primaryId
self.secondaryId = secondaryId
self.created = strftime("%Y-%m-%d %H:%M:%S")
self.changed = ''
self.watched1 = watched1
self.watched2 = watched2
self.watched3 = watched3
self.watched4 = watched4
self.status = ''
self.extra = extra
self.userData = ''
self.foreignKey = foreignKey
#===============================================================================
# BEGIN
#===============================================================================
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,11 @@
## Overview
A simple sample plugin allowing for detecting rogue DHCP servers on the network.
### Usage
- No specific configuration needed.
### Notes
- No specific configuration needed.

View File

@@ -0,0 +1,302 @@
{
"code_name": "dhcp_servers",
"unique_prefix": "DHCPSRVS",
"enabled": true,
"data_source": "python-script",
"localized": ["display_name", "description", "icon"],
"display_name" : [{
"language_code":"en_us",
"string" : "Rogue DHCP"
}],
"icon":[{
"language_code":"en_us",
"string" : "<i class=\"fa-solid fa-skull-crossbones\"></i>"
}],
"description": [{
"language_code":"en_us",
"string" : "This plugin is to use NMAP to monitor for rogue DHCP servers."
}],
"params" : [],
"database_column_definitions":
[
{
"column": "Index",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Plugin",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
},
{
"column": "Object_PrimaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "deviceip",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Server Identifier"
}]
},
{
"column": "Object_SecondaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Domain Name"
}]
} ,
{
"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": "Watched_Value1",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Domain Name Server"
}]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "IP Offered"
}]
},
{
"column": "Watched_Value3",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Interface"
}]
} ,
{
"column": "Watched_Value4",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Router"
}]
} ,
{
"column": "UserData",
"css_classes": "col-sm-2",
"show": true,
"type": "textboxsave",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Comments"
}]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"type": "replace",
"default_value":"",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status"
}]
},
{
"column": "Extra",
"css_classes": "col-sm-3",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Extra info"
}]
}
],
"settings":[
{
"function": "RUN",
"type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "When to run"
}],
"description": [{
"language_code":"en_us",
"string" : "Enable a regular scan of rogue DHCP servers. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) or after you update your settings."
}]
},
{
"function": "CMD",
"type": "text",
"default_value":"python3 /home/pi/pialert/front/plugins/dhcp_servers/script.py",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Command"
}],
"description": [{
"language_code":"en_us",
"string" : "Command to run"
}]
},
{
"function": "RUN_SCHD",
"type": "text",
"default_value":"0 2 * * *",
"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=\"#DHCPSRVS_RUN\"><code>DHCPSRVS_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 <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
}]
},
{
"function": "RUN_TIMEOUT",
"type": "integer",
"default_value":5,
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Run timeout"
},
{
"language_code":"de_de",
"string" : "Wartezeit"
}],
"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."
}]
},
{
"function": "WATCH",
"type": "multiselect",
"default_value":["Watched_Value1"],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Watched"
}] ,
"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 Domain Name Server</li><li><code>Watched_Value2</code> is IP Offered</li><li><code>Watched_Value3</code> is Interface </li><li><code>Watched_Value4</code> is Router </li></ul>"
}]
},
{
"function": "REPORT_ON",
"type": "multiselect",
"default_value":["new","watched-changed"],
"options": ["new","watched-changed","watched-not-changed"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Report on"
}] ,
"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."
}]
}
]
}

View File

@@ -0,0 +1,133 @@
#!/usr/bin/env python
# Based on the work of https://github.com/leiweibau/Pi.Alert
from __future__ import unicode_literals
from time import sleep, time, strftime
import requests
import pathlib
import threading
import subprocess
import socket
import argparse
import io
import sys
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import pwd
import os
curPath = str(pathlib.Path(__file__).parent.resolve())
log_file = curPath + '/script.log'
last_run = curPath + '/last_result.log'
print(last_run)
# Workflow
def main():
last_run_logfile = open(last_run, 'a')
timeoutSec = 10
nmapArgs = ['sudo', 'nmap', '--script', 'broadcast-dhcp-discover']
# Execute N probes and insert in list
dhcp_probes = 1 # N probes
newLines = []
newLines.append(strftime("%Y-%m-%d %H:%M:%S"))
#dhcp_server_list_time = []
for _ in range(dhcp_probes):
output = subprocess.check_output (nmapArgs, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(timeoutSec ))
newLines = newLines + output.split("\n")
# parse output
newEntries = []
duration = ""
for line in newLines:
if newEntries is None:
index = 0
else:
index = len(newEntries) - 1
if 'Response ' in line and ' of ' in line:
newEntries.append(plugin_object_class())
elif 'Server Identifier' in line :
newEntries[index].primaryId = line.split(':')[1].strip()
elif 'Domain Name' in line :
newEntries[index].secondaryId = line.split(':')[1].strip()
elif 'Domain Name Server' in line :
newEntries[index].watched1 = line.split(':')[1].strip()
elif 'IP Offered' in line :
newEntries[index].watched2 = line.split(':')[1].strip()
elif 'Interface' in line :
newEntries[index].watched3 = line.split(':')[1].strip()
elif 'Router' in line :
newEntries[index].watched4 = line.split(':')[1].strip()
newEntries[index].foreignKey = line.split(':')[1].strip()
elif ('IP Address Lease Time' in line or 'Subnet Mask' in line or 'Broadcast Address' in line) :
newVal = line.split(':')[1].strip()
if newEntries[index].extra == '':
newEntries[index].extra = newVal
else:
newEntries[index].extra = newEntries[index].extra + ',' + newVal
for e in newEntries:
# Insert list into the log
service_monitoring_log(e.primaryId, e.secondaryId, e.created, e.watched1, e.watched2, e.watched3, e.watched4, e.extra, e.foreignKey )
# -----------------------------------------------------------------------------
def service_monitoring_log(primaryId, secondaryId, created, watched1, watched2 = '', watched3 = '', watched4 = '', extra ='', foreignKey ='' ):
if watched1 == '':
watched1 = 'null'
if watched2 == '':
watched2 = 'null'
if watched3 == '':
watched3 = 'null'
if watched4 == '':
watched4 = 'null'
with open(last_run, 'a') as last_run_logfile:
# https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format(
primaryId,
secondaryId,
created,
watched1,
watched2,
watched3,
watched4,
extra,
foreignKey
)
)
# -------------------------------------------------------------------
class plugin_object_class:
def __init__(self, primaryId = '',secondaryId = '', watched1 = '',watched2 = '',watched3 = '',watched4 = '',extra = '',foreignKey = ''):
self.pluginPref = ''
self.primaryId = primaryId
self.secondaryId = secondaryId
self.created = strftime("%Y-%m-%d %H:%M:%S")
self.changed = ''
self.watched1 = watched1
self.watched2 = watched2
self.watched3 = watched3
self.watched4 = watched4
self.status = ''
self.extra = extra
self.userData = ''
self.foreignKey = foreignKey
#===============================================================================
# BEGIN
#===============================================================================
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,11 @@
## Overview
This plugin shows all Services discovered by regular NMAP scans. It's also a sample plugin showcasing how to use a SQL Query to show existing data from the PiAlert database.
### Usage
- The sql query from the `NMAPSRV_CMD` setting is used to create source data for this plugin. Column order and values need to adhere to the ones specified in the [documentation](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins).
### Notes
- N/A

View File

@@ -0,0 +1,282 @@
{
"code_name": "nmap_services",
"unique_prefix": "NMAPSRV",
"enabled": true,
"data_source": "pialert-db-query",
"localized": ["display_name", "description", "icon"],
"display_name" : [{
"language_code":"en_us",
"string" : "Services (NMAP)"
}],
"icon":[{
"language_code":"en_us",
"string" : "<i class=\"fa-solid fa-satellite-dish\"></i>"
}],
"description": [{
"language_code":"en_us",
"string" : "This plugin shows all services discovered by NMAP scans."
}],
"params" : [],
"database_column_definitions":
[
{
"column": "Index",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Plugin",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
},
{
"column": "Object_PrimaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Device MAC"
}]
},
{
"column": "Object_SecondaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "url",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Ip and Port"
}]
} ,
{
"column": "DateTimeCreated",
"css_classes": "col-sm-2",
"show": false,
"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": "Watched_Value1",
"css_classes": "col-sm-1",
"show": true,
"type": "label",
"default_value":"",
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Service"
}]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-1",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "State"
}]
},
{
"column": "Watched_Value3",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Watched_Value4",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Extra",
"css_classes": "col-sm-3",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Extra"
}]
},
{
"column": "ForeignKey",
"css_classes": "col-sm-2",
"show": true,
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "MAC"
}]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"type": "replace",
"default_value":"",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status"
}]
}
],
"settings":[
{
"function": "RUN",
"type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "When to run"
}],
"description": [{
"language_code":"en_us",
"string" : "Specify when the SQL query is executed."
}]
},
{
"function": "CMD",
"type": "text",
"default_value":"SELECT ns.MAC as Object_PrimaryID, cast('http://' || dv.dev_LastIP 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, dv.dev_Name as Watched_Value3, 'null' as Watched_Value4, ns.Extra as Extra, ns.MAC as ForeignKey FROM (SELECT * FROM Nmap_Scan) ns left JOIN (SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv ON ns.MAC = dv.dev_MAC",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "SQL to run"
}],
"description": [{
"language_code":"en_us",
"string" : "This SQL query is used to populate the coresponding UI tables under the Plugins section."
}]
},
{
"function": "RUN_SCHD",
"type": "text",
"default_value":"0 2 * * *",
"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=\"#NMAPSRV_RUN\"><code>NMAPSRV_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 <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
}]
},
{
"function": "WATCH",
"type": "multiselect",
"default_value":["Watched_Value1"],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Watched"
}] ,
"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 service type (e.g.: http, ssh)</li><li><code>Watched_Value2</code> is Status (open or closed)</li><li><code>Watched_Value3</code> unused </li><li><code>Watched_Value4</code> unused </li></ul>"
}]
},
{
"function": "REPORT_ON",
"type": "multiselect",
"default_value":["new","watched-changed"],
"options": ["new","watched-changed","watched-not-changed"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Report on"
}] ,
"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."
}]
}
]
}

96
front/plugins/plugin_helper.py Executable file
View File

@@ -0,0 +1,96 @@
from time import strftime
# -------------------------------------------------------------------
class Plugin_Object:
"""
Plugin_Object class to manage one object introduced by the plugin
An object typically is a device but could also be a website or something
else that is monitored by the plugin.
"""
def __init__(
self,
primaryId="",
secondaryId="",
watched1="",
watched2="",
watched3="",
watched4="",
extra="",
foreignKey="",
):
self.pluginPref = ""
self.primaryId = primaryId
self.secondaryId = secondaryId
self.created = strftime("%Y-%m-%d %H:%M:%S")
self.changed = ""
self.watched1 = watched1
self.watched2 = watched2
self.watched3 = watched3
self.watched4 = watched4
self.status = ""
self.extra = extra
self.userData = ""
self.foreignKey = foreignKey
def write(self):
"""
write the object details as a string in the
format required to write the result file
"""
line = "{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format(
self.primaryId,
self.secondaryId,
self.created,
self.watched1,
self.watched2,
self.watched3,
self.watched4,
self.extra,
self.foreignKey,
)
return line
class Plugin_Objects:
"""
Plugin_Objects is the class that manages and holds all the objects created by the plugin.
It contains a list of Plugin_Object instances.
And can write the required result file.
"""
def __init__(self, result_file):
self.result_file = result_file
self.objects = []
def add_object(
self,
primaryId="",
secondaryId="",
watched1="",
watched2="",
watched3="",
watched4="",
extra="",
foreignKey="",
):
self.objects.append(
Plugin_Object(
primaryId,
secondaryId,
watched1,
watched2,
watched3,
watched4,
extra,
foreignKey,
)
)
def write_result_file(self):
# print ("writing file: "+self.result_file)
with open(self.result_file, mode="w") as fp:
for obj in self.objects:
fp.write(obj.write())
fp.close()

View File

@@ -0,0 +1,36 @@
## Overview
A plugin for importing devices from an SNMP enabled router or switch. Using SNMP offers an efficient way to discover IPv4 devices across one or more networks/subnets/vlans.
### Usage
Specify the following settings in the Settings section of PiAlert:
- `SNMPDSC_routers` - A list of `snmpwalk` commands to execute against IP addresses of roputers/switches with SNMP turned on. For example: `snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2`
### Setup Cisco IOS
Enable IOS SNMP service and restrict to selected (internal) IP/Subnet.
````
! Add standard ip access-list 10
ip access-list standard 10
permit 192.168.1.0 0.0.0.255
permit host 192.168.2.10
!
! Enable IOS snmp server with Read Only community 'mysnmpcommunitysecret' name.
! Restrict connections to access-list 10
snmp-server community mysnmpcommunitysecret RO 10
````
Confirm SNMP enabled
````
show snmp
````
### Notes
- Only IPv4 supported.
- The SNMP OID `.1.1.1.3.6.1.2.1.3.1.1.2` is specifically for devices IPv4 ARP table. This OID has been tested on Cisco ISRs and other L3 devices. Support may vary between other vendors / devices.
- Expected output (ingestion) in format `iso.3.6.1.2.1.3.1.1.2.3.1.192.168.1.2 "6C 6C 6C 6C 6C 6C "`.

View File

@@ -0,0 +1,328 @@
{
"code_name": "snmp_discovery",
"unique_prefix": "SNMPDSC",
"enabled": true,
"data_source": "python-script",
"localized": ["display_name", "description", "icon"],
"mapped_to_table": "DHCP_Leases",
"display_name" : [{
"language_code":"en_us",
"string" : "SNMP discovery"
}],
"icon":[{
"language_code":"en_us",
"string" : "<i class=\"fa-solid fa-s\"></i>"
}],
"description": [{
"language_code":"en_us",
"string" : "This plugin is used to discover devices via the arp table(s) of a RFC1213 compliant router or switch."
}],
"params" : [
{
"name" : "routers",
"type" : "setting",
"value" : "SNMPDSC_routers"
}
],
"database_column_definitions":
[
{
"column": "Index",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Plugin",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
},
{
"column": "Object_PrimaryID",
"mapped_to_column": "DHCP_MAC",
"css_classes": "col-sm-2",
"show": true,
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "MAC address"
}]
},
{
"column": "Object_SecondaryID",
"mapped_to_column": "DHCP_IP",
"css_classes": "col-sm-2",
"show": true,
"type": "deviceip",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "IP"
}]
} ,
{
"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",
"mapped_to_column": "DHCP_DateTime",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Changed"
}]
},
{
"column": "Watched_Value1",
"mapped_to_column": "DHCP_Name",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"(unknown)",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Hostname"
}]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Router IP"
}]
},
{
"column": "Watched_Value3",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Type"
}]
} ,
{
"column": "Watched_Value4",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Network"
}]
} ,
{
"column": "UserData",
"css_classes": "col-sm-2",
"show": false,
"type": "textboxsave",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Comments"
}]
},
{
"column": "Extra",
"css_classes": "col-sm-3",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "RAW output"
}]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"type": "replace",
"default_value":"",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status"
}]
}
],
"settings":[
{
"function": "RUN",
"type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "When to run"
}],
"description": [{
"language_code":"en_us",
"string" : "Enable import of devices from a SNMP enabled device. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) or after you update your settings."
}]
},
{
"function": "CMD",
"type": "text",
"default_value":"python3 /home/pi/pialert/front/plugins/snmp_discovery/script.py routers={s-quote}{routers}{s-quote}",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Command"
}],
"description": [{
"language_code":"en_us",
"string" : "Command to run. Not recommended to change."
}]
},
{
"function": "routers",
"type": "list",
"default_value":["snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2"],
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Routers"
}],
"description": [{
"language_code":"en_us",
"string" : "A list of <code>snmpwalk</code> commands to execute against IP addresses of roputers/switches with SNMP turned on. <br/> <br/> Example with the router on the IP <code>192.168.1.1</code>: <br/> <code>snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2</code> <br/><br/> Only IPv4 supported. Authentication is not supported. More info on the plugin <a href='https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins/snmp_discovery' target='_blank'>here</a>."
}]
},
{
"function": "RUN_SCHD",
"type": "text",
"default_value":"0 2 * * *",
"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=\"#SNMPDSC_RUN\"><code>SNMPDSC_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 <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
}]
},
{
"function": "RUN_TIMEOUT",
"type": "integer",
"default_value":5,
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Run timeout"
},
{
"language_code":"de_de",
"string" : "Wartezeit"
}],
"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."
}]
},
{
"function": "WATCH",
"type": "multiselect",
"default_value":["Watched_Value1"],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Watched"
}] ,
"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 Hostname (not discoverable) </li><li><code>Watched_Value2</code> is Router IP </li><li><code>Watched_Value3</code> is not used </li><li><code>Watched_Value4</code> is not used </li></ul>"
}]
},
{
"function": "REPORT_ON",
"type": "multiselect",
"default_value":["new","watched-changed"],
"options": ["new","watched-changed","watched-not-changed"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Report on"
}] ,
"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."
}]
}
]
}

View File

@@ -0,0 +1,170 @@
#!/usr/bin/env python
# Example call
# python3 /home/pi/pialert/front/plugins/snmp_discovery/script.py routers='snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2'
from __future__ import unicode_literals
from time import sleep, time, strftime
import requests
from requests import Request, Session, packages
import pathlib
import threading
import subprocess
import socket
import json
import argparse
import io
import sys
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import pwd
import os
curPath = str(pathlib.Path(__file__).parent.resolve())
log_file = curPath + '/script.log'
last_run = curPath + '/last_result.log'
# Workflow
def main():
# init global variables
global ROUTERS
# empty file
open(last_run , 'w').close()
last_run_logfile = open(last_run, 'a')
parser = argparse.ArgumentParser(description='This plugin is used to discover devices via the arp table(s) of a RFC1213 compliant router or switch.')
parser.add_argument('routers', action="store", help="IP(s) of routers, separated by comma (,) if passing multiple")
values = parser.parse_args()
# parse output
newEntries = []
if values.routers:
ROUTERS = values.routers.split('=')[1].replace('\'','')
newEntries = get_entries(newEntries)
for e in newEntries:
# Insert list into the log
service_monitoring_log(e.primaryId, e.secondaryId, e.created, e.watched1, e.watched2, e.watched3, e.watched4, e.extra, e.foreignKey )
# -----------------------------------------------------------------------------
def get_entries(newEntries):
routers = []
if ',' in ROUTERS:
# multiple
routers = ROUTERS.split(',')
else:
# only one
routers.append(ROUTERS)
for router in routers:
# snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2
print(router)
timeoutSec = 10
snmpwalkArgs = router.split(' ')
# Execute N probes and insert in list
probes = 1 # N probes
newLines = []
for _ in range(probes):
output = subprocess.check_output (snmpwalkArgs, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(timeoutSec ))
newLines = newLines + output.split("\n")
# Process outputs
# Sample: iso.3.6.1.2.1.3.1.1.2.3.1.192.168.1.2 "6C 6C 6C 6C 6C 6C "
with open(log_file, 'a') as run_logfile:
for line in newLines:
# debug
run_logfile.write(line)
tmpSplt = line.split('"')
if len(tmpSplt) == 3:
ipStr = tmpSplt[0].split('.') # contains IP
macStr = tmpSplt[1].split(' ') # contains MAC
if 'iso.' in line and len(ipStr) == 16:
tmpEntry = plugin_object_class(
f'{macStr[0]}:{macStr[1]}:{macStr[2]}:{macStr[3]}:{macStr[4]}:{macStr[5]}',
f'{ipStr[12]}.{ipStr[13]}.{ipStr[14]}.{ipStr[15]}'.strip(),
watched1='(unknown)',
watched2=snmpwalkArgs[6], # router IP
extra=line
)
newEntries.append(tmpEntry)
return newEntries
# -------------------------------------------------------------------
class plugin_object_class:
def __init__(self, primaryId = '',secondaryId = '', watched1 = '',watched2 = '',watched3 = '',watched4 = '',extra = '',foreignKey = ''):
self.pluginPref = ''
self.primaryId = primaryId
self.secondaryId = secondaryId
self.created = strftime("%Y-%m-%d %H:%M:%S")
self.changed = ''
self.watched1 = watched1
self.watched2 = watched2
self.watched3 = watched3
self.watched4 = watched4
self.status = ''
self.extra = extra
self.userData = ''
self.foreignKey = foreignKey
# -----------------------------------------------------------------------------
def service_monitoring_log(primaryId, secondaryId, created, watched1, watched2 = 'null', watched3 = 'null', watched4 = 'null', extra ='null', foreignKey ='null' ):
if watched1 == '':
watched1 = 'null'
if watched2 == '':
watched2 = 'null'
if watched3 == '':
watched3 = 'null'
if watched4 == '':
watched4 = 'null'
if extra == '':
extra = 'null'
if foreignKey == '':
foreignKey = 'null'
with open(last_run, 'a') as last_run_logfile:
last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format(
primaryId,
secondaryId,
created,
watched1,
watched2,
watched3,
watched4,
extra,
foreignKey
)
)
#===============================================================================
# BEGIN
#===============================================================================
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,27 @@
## Overview
A plugin allowing for importing Un-Discoverable devices from the settings page.
The main usecase is to add dumb network gear like unmanaged hubs and switches to the network view.
There might be other usecases, please let me know.
### Usage
- Go to settings and find Un-Discoverabe Devices in the list of plugins.
- Enable the plugin by changing the RUN parameter from disabled to `once` or `always_after_scan`.
- Add the name of your device to the list. (remove the sample entry first)
- SAVE
- wait for the next scan to finish
#### Examples:
Settings:
![settings](https://github.com/Data-Monkey/Pi.Alert/assets/7224371/52883307-19a5-4602-b13a-9825461f6cc4)
resulting in these devices:
![devices](https://github.com/Data-Monkey/Pi.Alert/assets/7224371/9f7659e7-75a8-4ae9-9f5f-781bdbcbc949)
Allowing Un-Discoverable devices like hubs, switches or APs to be added to the network view.
![network](https://github.com/Data-Monkey/Pi.Alert/assets/7224371/b5ccc3b3-f5fd-4f5b-b0f0-e4e637c6da33)
### Known Limitations
- Un-Discoverable Devices always show as offline. That is expected as they can not be discovered by Pi.Alert.
- All IPs are set to 0.0.0.0 therefore the "Random MAC" icon might show up.

View File

@@ -0,0 +1,217 @@
{
"code_name": "undiscoverables",
"unique_prefix": "UNDIS",
"enabled": true,
"data_source": "python-script",
"mapped_to_table": "DHCP_Leases",
"localized": ["display_name", "description", "icon"],
"display_name": [
{
"language_code": "en_us",
"string": "Un-Discoverable Devices"
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa-solid fa-binoculars\"></i>"
}
],
"description": [
{
"language_code": "en_us",
"string": "This plugin is to import undiscoverable devices from a file."
}
],
"params" : [
{
"name" : "devices",
"type" : "setting",
"value" : "UNDIS_devices_to_import"
}],
"settings": [
{
"function": "RUN",
"type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "always_after_scan"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "When to run"
}],
"description": [{
"language_code":"en_us",
"string" : "When enabled, ONCE is the preferred option. It runs at startup and after every save of the config here.<br> Changes will only show in the devices <b> after the next scan!</b>"
}]
},
{
"function": "CMD",
"type": "readonly",
"default_value": "python3 /home/pi/pialert/front/plugins/undiscoverables/script.py devices={devices}",
"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": "integer",
"default_value": 10,
"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."
}
]
},
{
"function": "WATCH",
"type": "lable",
"default_value": [],
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Watched"
}
],
"description": [
{
"language_code": "en_us",
"string": "Undiscoverable Devices can not change their status, no watch is enabled."
}
]
},
{
"function": "REPORT_ON",
"type": "lable",
"default_value": [],
"options": ["new", "watched-changed", "watched-not-changed"],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Report on"
}
],
"description": [
{
"language_code": "en_us",
"string": "No notifications will be sent."
}
]
},
{
"function": "devices_to_import",
"type": "list",
"default_value":["dummy_router"],
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "UnDiscoverable Devices"
}],
"description": [{
"language_code":"en_us",
"string" : "Devices to be added to the devices list."
}]
}
],
"database_column_definitions":
[
{
"column": "Watched_Value1",
"mapped_to_column": "DHCP_Name",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Device Name"
}]
},
{
"column": "Object_PrimaryID",
"mapped_to_column": "DHCP_MAC",
"css_classes": "col-sm-2",
"show": true,
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "MAC address"
}]
},
{
"column": "Object_SecondaryID",
"mapped_to_column": "DHCP_IP",
"css_classes": "col-sm-2",
"show": true,
"type": "deviceip",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "IP"
}]
} ,
{
"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",
"mapped_to_column": "DHCP_DateTime",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Changed"
}]
}
]
}

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env python
# test script by running python script.py devices=test,dummy
import os
import pathlib
import argparse
import sys
sys.path.append("/home/pi/pialert/front/plugins")
from plugin_helper import Plugin_Objects
CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
LOG_FILE = os.path.join(CUR_PATH , 'script.log')
RESULT_FILE = os.path.join(CUR_PATH , 'last_result.log')
def main():
# the script expects a parameter in the format of devices=device1,device2,...
parser = argparse.ArgumentParser(description='Import devices from settings')
parser.add_argument('devices', action="store", help="list of device names separated by ','")
values = parser.parse_args()
UNDIS_devices = Plugin_Objects( RESULT_FILE )
if values.devices:
for fake_dev in values.devices.split('=')[1].split(','):
UNDIS_devices.add_object(
primaryId=fake_dev, # MAC (Device Name)
secondaryId="0.0.0.0", # IP Address (always 0.0.0.0)
watched1=fake_dev, # Device Name
watched2="",
watched3="",
watched4="",
extra="",
foreignKey="")
UNDIS_devices.write_result_file()
return 0
#===============================================================================
# BEGIN
#===============================================================================
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,19 @@
## Overview
A plugin allowing for importing devices from a UniFi controller.
### Usage
Specify the following settings in the Settings section of PiAlert:
- `UNFIMP_username` - Username used to login into the UNIFI controller.
- `UNFIMP_password` - Password used to login into the UNIFI controller.
- `UNFIMP_host` - Host url or IP address where the UNIFI controller is hosted (excluding http://)
- `UNFIMP_sites` - Name of the sites (usually 'default', check the URL in your UniFi controller UI if unsure. The site id is in the following part of the URL: `https://192.168.1.1:8443/manage/site/this-is-the-site-id/settings/`).
- `UNFIMP_protocol` - https:// or http://
- `UNFIMP_port` - Usually 8443
### Notes
- Currently only used to import devices, not their status, type or network map.
- It is recommend to create a read-only user in your UniFi controller

View File

@@ -0,0 +1,448 @@
{
"code_name": "unifi_import",
"unique_prefix": "UNFIMP",
"enabled": true,
"data_source": "python-script",
"localized": ["display_name", "description", "icon"],
"mapped_to_table": "DHCP_Leases",
"display_name" : [{
"language_code":"en_us",
"string" : "UniFi import"
}],
"icon":[{
"language_code":"en_us",
"string" : "<i class=\"fa-solid fa-upload\"></i>"
}],
"description": [{
"language_code":"en_us",
"string" : "This plugin is used to import devices from an UNIFI controller."
}],
"params" : [
{
"name" : "username",
"type" : "setting",
"value" : "UNFIMP_username"
},
{
"name" : "password",
"type" : "setting",
"value" : "UNFIMP_password"
},
{
"name" : "host",
"type" : "setting",
"value" : "UNFIMP_host"
},
{
"name" : "sites",
"type" : "setting",
"value" : "UNFIMP_sites"
},
{
"name" : "protocol",
"type" : "setting",
"value" : "UNFIMP_protocol"
},
{
"name" : "port",
"type" : "setting",
"value" : "UNFIMP_port"
},
{
"name" : "version",
"type" : "setting",
"value" : "UNFIMP_version"
}
],
"database_column_definitions":
[
{
"column": "Index",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Plugin",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
},
{
"column": "Object_PrimaryID",
"mapped_to_column": "DHCP_MAC",
"css_classes": "col-sm-2",
"show": true,
"type": "devicemac",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "MAC address"
}]
},
{
"column": "Object_SecondaryID",
"mapped_to_column": "DHCP_IP",
"css_classes": "col-sm-2",
"show": true,
"type": "deviceip",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "IP"
}]
} ,
{
"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",
"mapped_to_column": "DHCP_DateTime",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Changed"
}]
},
{
"column": "Watched_Value1",
"mapped_to_column": "DHCP_Name",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Hostname"
}]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Vendor"
}]
},
{
"column": "Watched_Value3",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Type"
}]
} ,
{
"column": "Watched_Value4",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Is online?"
}]
} ,
{
"column": "UserData",
"css_classes": "col-sm-2",
"show": false,
"type": "textboxsave",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Comments"
}]
},
{
"column": "Extra",
"css_classes": "col-sm-3",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Network"
}]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"type": "replace",
"default_value":"",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status"
}]
}
],
"settings":[
{
"function": "RUN",
"type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "When to run"
}],
"description": [{
"language_code":"en_us",
"string" : "Enable import of devices from a UNIFI controller. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) or after you update your settings."
}]
},
{
"function": "CMD",
"type": "text",
"default_value":"python3 /home/pi/pialert/front/plugins/unifi_import/script.py username={username} password={password} host={host} sites={sites} protocol={protocol} port={port} version={version}",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Command"
}],
"description": [{
"language_code":"en_us",
"string" : "Command to run. Not recommended to change."
}]
},
{
"function": "username",
"type": "text",
"default_value":"",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Username"
}],
"description": [{
"language_code":"en_us",
"string" : "The username used to login into your UNIFI controller. It is recommended to create a read-only user account."
}]
},
{
"function": "password",
"type": "password",
"default_value":"",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Password"
}],
"description": [{
"language_code":"en_us",
"string" : "The password used to login into your UNIFI controller."
}]
},
{
"function": "protocol",
"type": "selecttext",
"default_value":"https://",
"options": ["https://", "http://"],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Protocol"
}],
"description": [{
"language_code":"en_us",
"string" : "The protocol to use to access the controller."
}]
},
{
"function": "host",
"type": "text",
"default_value":"192.168.1.1",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Host"
}],
"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>)"
}]
},
{
"function": "port",
"type": "text",
"default_value":"8443",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Port number"
}],
"description": [{
"language_code":"en_us",
"string" : "The port number where the UNIFI controller is runnig. Usually it is <code>8443</code>."
}]
},
{
"function": "version",
"type": "text",
"default_value":"",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "API version"
}],
"description": [{
"language_code":"en_us",
"string" : "The base version of the Unify controller API. Supported values as of time of writing are <code>v4|v5|unifiOS|UDMP-unifiOS</code>."
}]
},
{
"function": "sites",
"type": "list",
"default_value":["default"],
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "UNIFI sites"
}],
"description": [{
"language_code":"en_us",
"string" : "The sites you want to connect to. Usually it is only one and the name is <code>default</code>. Check the URL in your UniFi controller UI if unsure."
}]
},
{
"function": "RUN_SCHD",
"type": "text",
"default_value":"0 2 * * *",
"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=\"#UNFIMP_RUN\"><code>UNFIMP_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 <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
}]
},
{
"function": "RUN_TIMEOUT",
"type": "integer",
"default_value":5,
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Run timeout"
},
{
"language_code":"de_de",
"string" : "Wartezeit"
}],
"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."
}]
},
{
"function": "WATCH",
"type": "multiselect",
"default_value":["Watched_Value1", "Watched_Value4"],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Watched"
}] ,
"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 Hostname </li><li><code>Watched_Value2</code> is Vendor </li><li><code>Watched_Value3</code> is Type </li><li><code>Watched_Value4</code> is Network </li></ul>"
}]
},
{
"function": "REPORT_ON",
"type": "multiselect",
"default_value":["new","watched-changed"],
"options": ["new","watched-changed","watched-not-changed"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Report on"
}] ,
"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."
}]
}
]
}

View File

@@ -0,0 +1,245 @@
#!/usr/bin/env python
# Inspired by https://github.com/stevehoek/Pi.Alert
# Example call
# python3 /home/pi/pialert/front/plugins/unifi_import/script.py username=pialert password=passw0rd host=192.168.1.1 site=default protocol=https:// port=8443
from __future__ import unicode_literals
from time import sleep, time, strftime
import requests
from requests import Request, Session, packages
import pathlib
import threading
import subprocess
import socket
import json
import argparse
import io
import sys
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import pwd
import os
from pyunifi.controller import Controller
curPath = str(pathlib.Path(__file__).parent.resolve())
log_file = curPath + '/script.log'
last_run = curPath + '/last_result.log'
# Workflow
def main():
# init global variables
global UNIFI_USERNAME, UNIFI_PASSWORD, UNIFI_HOST
global UNIFI_SITES, PORT, PROTOCOL, VERSION
last_run_logfile = open(last_run, 'a')
# empty file
last_run_logfile.write("")
parser = argparse.ArgumentParser(description='Import devices from an UNIFI controller')
parser.add_argument('username', action="store", help="Username used to login into the UNIFI controller")
parser.add_argument('password', action="store", help="Password used to login into the UNIFI controller")
parser.add_argument('host', action="store", help="Host url or IP address where the UNIFI controller is hosted (excluding http://)")
parser.add_argument('sites', action="store", help="Name of the sites (usually 'default', check the URL in your UniFi controller UI). Separated by comma (,) if passing multiple sites")
parser.add_argument('protocol', action="store", help="https:// or http://")
parser.add_argument('port', action="store", help="Usually 8443")
parser.add_argument('version', action="store", help="The base version of the controller API [v4|v5|unifiOS|UDMP-unifiOS]")
values = parser.parse_args()
# parse output
newEntries = []
if values.username and values.password and values.host and values.sites:
UNIFI_USERNAME = values.username.split('=')[1]
UNIFI_PASSWORD = values.password.split('=')[1]
UNIFI_HOST = values.host.split('=')[1]
UNIFI_SITES = values.sites.split('=')[1]
PROTOCOL = values.protocol.split('=')[1]
PORT = values.port.split('=')[1]
VERSION = values.version.split('=')[1]
newEntries = get_entries(newEntries)
for e in newEntries:
# Insert list into the log
service_monitoring_log(e.primaryId, e.secondaryId, e.created, e.watched1, e.watched2, e.watched3, e.watched4, e.extra, e.foreignKey )
# -----------------------------------------------------------------------------
def get_entries(newEntries):
sites = []
if ',' in UNIFI_SITES:
sites = UNIFI_SITES.split(',')
else:
sites.append(UNIFI_SITES)
for site in sites:
c = Controller(UNIFI_HOST, UNIFI_USERNAME, UNIFI_PASSWORD, version=VERSION, ssl_verify=False, site_id=site )
for ap in c.get_aps():
# print(f'{json.dumps(ap)}')
deviceType = ''
if (ap['type'] == 'udm'):
deviceType = 'Router'
elif (ap['type'] == 'usg'):
deviceType = 'Router'
elif (ap['type'] == 'usw'):
deviceType = 'Switch'
elif (ap['type'] == 'uap'):
deviceType = 'AP'
name = get_unifi_val(ap, 'name')
hostName = get_unifi_val(ap, 'hostname')
if name == 'null' and hostName != 'null':
name = hostName
tmpPlugObj = plugin_object_class(
ap['mac'],
get_unifi_val(ap, 'ip'),
name,
'Ubiquiti Networks Inc.',
deviceType,
ap['state'],
get_unifi_val(ap, 'connection_network_name')
)
newEntries.append(tmpPlugObj)
# print(f'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
for cl in c.get_clients():
# print(f'{json.dumps(cl)}')
name = get_unifi_val(cl, 'name')
hostName = get_unifi_val(cl, 'hostname')
if name == 'null' and hostName != 'null':
name = hostName
tmpPlugObj = plugin_object_class(
cl['mac'],
get_unifi_val(cl, 'ip'),
name,
get_unifi_val(cl, 'oui'),
'Other',
1,
get_unifi_val(cl, 'connection_network_name')
)
# print(f'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
for us in c.get_clients():
# print(f'{json.dumps(us)}')
name = get_unifi_val(us, 'name')
hostName = get_unifi_val(us, 'hostname')
if name == 'null' and hostName != 'null':
name = hostName
tmpPlugObj = plugin_object_class(
us['mac'],
get_unifi_val(us, 'ip'),
name,
get_unifi_val(us, 'oui'),
'Other',
1,
get_unifi_val(us, 'connection_network_name')
)
newEntries.append(tmpPlugObj)
return newEntries
# -----------------------------------------------------------------------------
def get_unifi_val(obj, key):
res = ''
if key in obj:
res = obj[key]
if res not in ['','None']:
return res
if obj.get(key) is not None:
res = obj.get(key)
if res not in ['','None']:
return res
return 'null'
# -------------------------------------------------------------------
class plugin_object_class:
def __init__(self, primaryId = '',secondaryId = '', watched1 = '',watched2 = '',watched3 = '',watched4 = '',extra = '',foreignKey = ''):
self.pluginPref = ''
self.primaryId = primaryId
self.secondaryId = secondaryId
self.created = strftime("%Y-%m-%d %H:%M:%S")
self.changed = ''
self.watched1 = watched1
self.watched2 = watched2
self.watched3 = watched3
self.watched4 = watched4
self.status = ''
self.extra = extra
self.userData = ''
self.foreignKey = foreignKey
# -----------------------------------------------------------------------------
def service_monitoring_log(primaryId, secondaryId, created, watched1, watched2 = 'null', watched3 = 'null', watched4 = 'null', extra ='null', foreignKey ='null' ):
if watched1 == '':
watched1 = 'null'
if watched2 == '':
watched2 = 'null'
if watched3 == '':
watched3 = 'null'
if watched4 == '':
watched4 = 'null'
if extra == '':
extra = 'null'
if foreignKey == '':
foreignKey = 'null'
with open(last_run, 'a') as last_run_logfile:
last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format(
primaryId,
secondaryId,
created,
watched1,
watched2,
watched3,
watched4,
extra,
foreignKey
)
)
#===============================================================================
# BEGIN
#===============================================================================
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,12 @@
## Overview
A simple sample plugin allowing for monitoring web services or urls. The status code corresponds to the commonly used [HTTP response status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
### Usage
- The user can specify which services (websites) to monitor via the `WEBMON_urls_to_check` setting.
### Notes
- Setting `(WEBMON_)SQL_internet_ip` is not used and specified for demonstration purposes only.
- Parameters `macs` and `internet_ip` in the `config.json` file are not used and specified for demonstration purposes only.

View File

@@ -0,0 +1,383 @@
{
"code_name": "website_monitor",
"unique_prefix": "WEBMON",
"enabled": true,
"data_source": "python-script",
"localized": ["display_name", "description", "icon"],
"display_name" : [{
"language_code":"en_us",
"string" : "Website monitor"
}],
"icon":[{
"language_code":"en_us",
"string" : "<i class=\"fa-solid fa-globe\"></i>"
}],
"description": [{
"language_code":"en_us",
"string" : "This plugin is to monitor status changes of services or websites."
}],
"params" : [{
"name" : "macs",
"type" : "sql",
"value" : "SELECT dev_MAC from DEVICES"
},
{
"name" : "urls",
"type" : "setting",
"value" : "WEBMON_urls_to_check"
},
{
"name" : "internet_ip",
"type" : "setting",
"value" : "WEBMON_SQL_internet_ip"
}],
"database_column_definitions":
[
{
"column": "Index",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Plugin",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
},
{
"column": "Object_PrimaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "url",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Monitored URL"
}]
},
{
"column": "Object_SecondaryID",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"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": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Changed"
}]
},
{
"column": "Watched_Value1",
"css_classes": "col-sm-2",
"show": true,
"type": "threshold",
"default_value":"",
"options": [
{
"maximum": 199,
"hexColor": "#792D86"
},
{
"maximum": 299,
"hexColor": "#5B862D"
},
{
"maximum": 399,
"hexColor": "#7D862D"
},
{
"maximum": 499,
"hexColor": "#BF6440"
},
{
"maximum": 599,
"hexColor": "#D33115"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status code"
}]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Latency"
}]
},
{
"column": "Watched_Value3",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "Watched_Value4",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "N/A"
}]
} ,
{
"column": "UserData",
"css_classes": "col-sm-2",
"show": true,
"type": "textboxsave",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Comments"
}]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"type": "replace",
"default_value":"",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
}
],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Status"
}]
},
{
"column": "Extra",
"css_classes": "col-sm-3",
"show": false,
"type": "label",
"default_value":"",
"options": [],
"localized": ["name"],
"name":[{
"language_code":"en_us",
"string" : "Extra"
}]
}
],
"settings":[
{
"function": "RUN",
"type": "selecttext",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "When to run"
}],
"description": [{
"language_code":"en_us",
"string" : "Enable a regular scan of your services. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) for the time specified in <a href=\"#WEBMON_RUN_TIMEOUT\"><code>WEBMON_RUN_TIMEOUT</code> setting</a>."
}]
},
{
"function": "CMD",
"type": "text",
"default_value":"python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls={urls}",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Command"
}],
"description": [{
"language_code":"en_us",
"string" : "Command to run"
}]
},
{
"function": "RUN_SCHD",
"type": "text",
"default_value":"0 2 * * *",
"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=\"#WEBMON_RUN\"><code>WEBMON_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 <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
}]
},
{
"function": "API_SQL",
"type": "text",
"default_value":"SELECT * FROM plugin_website_monitor",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "API endpoint (not implemented)"
}],
"description": [{
"language_code":"en_us",
"string" : "You can specify a custom SQL query which will generate a JSON file and then expose it via the <a href=\"/api/plugin_website_monitor.json\" target=\"_blank\"><code>plugin_website_monitor.json</code> file endpoint</a>."
}]
},
{
"function": "RUN_TIMEOUT",
"type": "integer",
"default_value":5,
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Run timeout"
},
{
"language_code":"de_de",
"string" : "Wartezeit"
}],
"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."
}]
},
{
"function": "WATCH",
"type": "multiselect",
"default_value":["Watched_Value1"],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Watched"
}] ,
"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 response status code (e.g.: 200, 404)</li><li><code>Watched_Value2</code> is Latency (not recommended)</li><li><code>Watched_Value3</code> unused </li><li><code>Watched_Value4</code> unused </li></ul>"
}]
},
{
"function": "REPORT_ON",
"type": "multiselect",
"default_value":["new","watched-changed"],
"options": ["new","watched-changed","watched-not-changed"],
"localized": ["name", "description"],
"name" :[{
"language_code":"en_us",
"string" : "Report on"
}] ,
"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": "urls_to_check",
"type": "list",
"default_value":["https://google.com", "https://duck.com"],
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Arguments"
}],
"description": [{
"language_code":"en_us",
"string" : "Services to watch. Enter full URL, e.g. <code>https://google.com</code>. The values from this setting will be used to replace the <code>{urls}</code> wildcard in the <code>WEBMON_CMD</code> setting."
}]
},
{
"function": "SQL_internet_ip",
"type": "readonly",
"default_value":"SELECT dev_LastIP FROM Devices WHERE dev_MAC = 'Internet'",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code":"en_us",
"string" : "Helper variable"
}],
"description": [{
"language_code":"en_us",
"string" : "Unused setting - for demonstration only. Getting the IP address of the Router / Internet. "
}]
}
]
}

View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python
# Based on the work of https://github.com/leiweibau/Pi.Alert
# python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls=http://google.com,http://bing.com
from __future__ import unicode_literals
from time import sleep, time, strftime
import requests
import pathlib
import argparse
import io
#import smtplib
import sys
#from smtp_config import sender, password, receivers, host, port
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import pwd
import os
curPath = str(pathlib.Path(__file__).parent.resolve())
log_file = curPath + '/script.log'
last_run = curPath + '/last_result.log'
print(last_run)
# Workflow
def main():
parser = argparse.ArgumentParser(description='Simple URL monitoring tool')
parser.add_argument('urls', action="store", help="urls to check separated by ','")
values = parser.parse_args()
if values.urls:
with open(last_run, 'w') as last_run_logfile:
# empty file
last_run_logfile.write("")
service_monitoring(values.urls.split('=')[1].split(','))
else:
return
# -----------------------------------------------------------------------------
def service_monitoring_log(site, status, latency):
# global monitor_logfile
# Log status message to log file
with open(log_file, 'a') as monitor_logfile:
monitor_logfile.write("{} | {} | {} | {}\n".format(strftime("%Y-%m-%d %H:%M:%S"),
site,
status,
latency,
)
)
with open(last_run, 'a') as last_run_logfile:
# https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format(
site,
'null',
strftime("%Y-%m-%d %H:%M:%S"),
status,
latency,
'null',
'null',
'null',
'null',
)
)
# -----------------------------------------------------------------------------
def check_services_health(site):
# Enable self signed SSL
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
"""Send GET request to input site and return status code"""
try:
resp = requests.get(site, verify=False, timeout=10)
latency = resp.elapsed
latency_str = str(latency)
latency_str_seconds = latency_str.split(":")
format_latency_str = latency_str_seconds[2]
if format_latency_str[0] == "0" and format_latency_str[1] != "." :
format_latency_str = format_latency_str[1:]
return resp.status_code, format_latency_str
except requests.exceptions.SSLError:
pass
except:
latency = "99999"
return 503, latency
# -----------------------------------------------------------------------------
def get_username():
return pwd.getpwuid(os.getuid())[0]
# -----------------------------------------------------------------------------
def service_monitoring(urls):
# Empty Log and write new header
print("Prepare Services Monitoring")
print("... Prepare Logfile")
with open(log_file, 'w') as monitor_logfile:
monitor_logfile.write("Pi.Alert [Prototype]:\n---------------------------------------------------------\n")
monitor_logfile.write("Current User: %s \n\n" % get_username())
monitor_logfile.write("Monitor Web-Services\n")
monitor_logfile.write("Timestamp: " + strftime("%Y-%m-%d %H:%M:%S") + "\n")
monitor_logfile.close()
print("... Get Services List")
sites = urls
print("Start Services Monitoring")
with open(log_file, 'a') as monitor_logfile:
monitor_logfile.write("\nStart Services Monitoring\n\n| Timestamp | URL | StatusCode | ResponseTime |\n-----------------------------------------------\n")
monitor_logfile.close()
while sites:
for site in sites:
status,latency = check_services_health(site)
scantime = strftime("%Y-%m-%d %H:%M:%S")
# Debugging
# print("{} - {} STATUS: {} ResponseTime: {}".format(strftime("%Y-%m-%d %H:%M:%S"),
# site,
# status,
# latency)
# )
# Write Logfile
service_monitoring_log(site, status, latency)
sys.stdout.flush()
break
else:
with open(log_file, 'a') as monitor_logfile:
monitor_logfile.write("\n\nNo site(s) to monitor!")
monitor_logfile.close()
#===============================================================================
# BEGIN
#===============================================================================
if __name__ == '__main__':
sys.exit(main())

View File

@@ -234,7 +234,7 @@ function initializeCalendar () {
header: {
left : 'prev,next today',
center : 'title',
right : 'timelineYear,timelineMonth,timelineWeek'
right : 'timelineYear,timelineMonth,timelineWeek,timelineDay'
},
defaultView : 'timelineMonth',
height : 'auto',
@@ -286,6 +286,13 @@ function initializeCalendar () {
buttonText : '<?= lang('Presence_CalHead_week');?>',
slotLabelFormat : 'D',
slotDuration : '24:00:01'
},
timelineDay: {
type : 'timeline',
duration : { day: 1 },
buttonText : '<?= lang('Presence_CalHead_day');?>',
slotLabelFormat : 'H',
slotDuration : '00:30:00'
}
},
@@ -305,6 +312,15 @@ function initializeCalendar () {
if (date.format('YYYY-MM-DD') == moment().format('YYYY-MM-DD')) {
cell.addClass ('fc-today'); };
if ($('#calendar').fullCalendar('getView').name == 'timelineDay') {
cell.removeClass('fc-sat');
cell.removeClass('fc-sun');
cell.removeClass('fc-today');
if (date.format('YYYY-MM-DD HH') == moment().format('YYYY-MM-DD HH')) {
cell.addClass('fc-today');
}
};
},
resourceRender: function (resourceObj, labelTds, bodyTds) {

View File

@@ -14,11 +14,25 @@ $confPath = "../config/pialert.conf";
checkPermissions([$dbPath, $confPath]);
// get settings from the API json file
// path to your JSON file
$file = '../front/api/table_settings.json';
// put the content of the file in a variable
$data = file_get_contents($file);
// JSON decode
$settingsJson = json_decode($data);
// get settings from the DB
global $db;
global $settingKeyOfLists;
$result = $db->query("SELECT * FROM Settings");
// array
$settingKeyOfLists = array();
$settingCoreGroups = array('General', 'Email', 'Webhooks', 'Apprise', 'NTFY', 'PUSHSAFER', 'MQTT', 'DynDNS', 'PiHole', 'Pholus', 'Nmap', 'API');
$settings = array();
while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// Push row data
@@ -69,10 +83,18 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
$isIn = ' in ';
foreach ($groups as $group)
{
if (in_array($group, $settingCoreGroups))
{
$settingGroupTypeHtml = "";
} else
{
$settingGroupTypeHtml = ' (<i class="fa-regular fa-plug fa-sm"></i>) ';
}
$html = $html.'<div class=" box panel panel-default">
<a data-toggle="collapse" data-parent="#accordion_gen" href="#'.$group.'">
<div class="panel-heading">
<h4 class="panel-title">'.lang($group.'_settings_group').'</h4>
<h4 class="panel-title">'.lang($group.'_icon')." ".lang($group.'_display_name').$settingGroupTypeHtml.'</h4>
</div>
</a>
<div id="'.$group.'" data-myid="collapsible" class="panel-collapse collapse '.$isIn.'">
@@ -108,7 +130,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// text - textbox
if($set['Type'] == 'text' )
{
$input = '<input class="form-control" onChange="settingsChanged()" input" id="'.$set['Code_Name'].'" value="'.$set['Value'].'"/>';
$input = '<input class="form-control" onChange="settingsChanged()" id="'.$set['Code_Name'].'" value="'.$set['Value'].'"/>';
}
// password - hidden text
elseif ($set['Type'] == 'password')
@@ -196,12 +218,12 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
{
$input = $input.
'<div class="row form-group">
<div class="col-xs-6">
<div class="col-xs-5">
<input class="form-control" id="ipMask" type="text" placeholder="192.168.1.0/24"/>
</div>';
// Add interface button
$input = $input.
'<div class="col-xs-3">
'<div class="col-xs-4">
<input class="form-control " id="ipInterface" type="text" placeholder="eth0" />
</div>
<div class="col-xs-3"><button class="btn btn-primary" onclick="addInterface()" >Add</button></div>
@@ -221,6 +243,37 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// Remove all interfaces button
$input = $input.'<div><button class="btn btn-primary" onclick="removeInterfaces()">Remove all</button></div>';
}
// list
elseif ($set['Type'] == 'list')
{
$settingKeyOfLists[] = $set['Code_Name'];
$input = $input.
'<div class="row form-group">
<div class="col-xs-9">
<input class="form-control" type="text" id="'.$set['Code_Name'].'_input" placeholder="Enter value"/>
</div>';
// Add interface button
$input = $input.
'<div class="col-xs-3"><button class="btn btn-primary" onclick="addList'.$set['Code_Name'].'()" >Add</button></div>
</div>';
// list all interfaces as options
$input = $input.'<div class="form-group">
<select class="form-control" name="'.$set['Code_Name'].'" id="'.$set['Code_Name'].'" multiple readonly>';
$options = createArray($set['Value']);
foreach ($options as $option) {
$input = $input.'<option value="'.$option.'" disabled>'.$option.'</option>';
}
$input = $input.'</select></div>';
// Remove all interfaces button
$input = $input.'<div><button class="btn btn-primary" onclick="removeFromList'.$set['Code_Name'].'()">Remove last</button></div>';
}
$html = $html.$input;
@@ -280,7 +333,10 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
<script>
// number of settings has to be equal to
var settingsNumber = 68;
// display the name of the first person
// echo $settingsJson[0]->name;
var settingsNumber = <?php echo count($settingsJson->data)?>;
// Wrong number of settings processing
if(<?php echo count($settings)?> != settingsNumber)
@@ -288,7 +344,34 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
showModalOk('WARNING', "<?= lang("settings_missing")?>");
}
<?php
// generate javascript methods to handle add and remove items to lists
foreach($settingKeyOfLists as $settingKey )
{
$addList = 'function addList'.$settingKey.'()
{
input = $("#'.$settingKey.'_input").val();
$("#'.$settingKey.'").append($("<option disabled></option>").attr("value", input).text(input));
$("#'.$settingKey.'_input").val("");
settingsChanged();
}
';
$remList = 'function removeFromList'.$settingKey.'()
{
settingsChanged();
// $("#'.$settingKey.'").empty();
$("#'.$settingKey.'").find("option:last").remove();
}';
echo $remList;
echo $addList;
}
?>
// ---------------------------------------------------------
function addInterface()
@@ -328,17 +411,18 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// generate javascript to collect values
<?php
$noConversion = array('text', 'integer', 'password', 'readonly', 'selecttext', 'selectinteger', "multiselect");
$noConversion = array('text', 'integer', 'password', 'readonly', 'selecttext', 'selectinteger', 'multiselect');
foreach ($settings as $set) {
if(in_array($set['Type'] , $noConversion))
{
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", $("#'.$set["Code_Name"].'").val(), "'.$set["Type"].'" ]);';
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", "'.$set["Type"].'", $("#'.$set["Code_Name"].'").val() ]);';
}
elseif ($set['Type'] == "boolean")
{
echo 'temp = $("#'.$set["Code_Name"].'").is(":checked") ;';
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", temp, "'.$set["Type"].'" ]);';
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", "'.$set["Type"].'", temp ]);';
}
elseif ($set["Code_Name"] == "SCAN_SUBNETS")
{
@@ -349,7 +433,23 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
});
";
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", temps, "'.$set["Type"].'" ]);';
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", "'.$set["Type"].'", temps ]);';
}
elseif ($set['Type'] == "list")
{
echo 'console.log($("#'.$set["Code_Name"].'"));';
echo "var temps = [];
$( '#".$set["Code_Name"]." option' ).each( function( i, selected ) {
vl = $( selected ).val()
if (vl != '')
{
temps.push(vl);
}
});
console.log(temps);
";
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", "'.$set["Type"].'", temps ]);';
}
}

Some files were not shown because too many files have changed in this diff Show More