Compare commits

...

484 Commits

Author SHA1 Message Date
Hosted Weblate
b4001f54bb Merge branch 'origin/main' into Weblate. 2024-03-18 16:01:48 +01:00
gallegonovato
47d1740fdd Translated using Weblate (Spanish)
Currently translated at 100.0% (648 of 648 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-03-18 16:01:46 +01:00
github-actions[bot]
03d1e0e097 [🤖Automation] Update README with sponsors information 2024-03-18 11:53:42 +00:00
Jokob-sk
08ee4adddd Better first-load handling 🔄 2024-03-18 22:44:16 +11:00
Jokob-sk
1a221fabc9 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-03-18 22:02:41 +11:00
Jokob-sk
f412ca0636 Restart bug & docs 🩹 2024-03-18 22:02:29 +11:00
Anonymous
6beb2584e5 Translated using Weblate (Spanish)
Currently translated at 99.6% (646 of 648 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-03-17 15:18:08 +01:00
Anonymous
8419750bdd Translated using Weblate (German)
Currently translated at 89.5% (580 of 648 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-03-17 15:18:08 +01:00
Hosted Weblate
9364cea706 Merge branch 'origin/main' into Weblate. 2024-03-17 15:01:59 +01:00
gallegonovato
6ceff80ec5 Translated using Weblate (Spanish)
Currently translated at 100.0% (648 of 648 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-03-17 15:01:54 +01:00
github-actions[bot]
cc9e4c722a [🤖Automation] Update README with sponsors information 2024-03-17 11:53:52 +00:00
Jokob-sk
5e687e1bdb Netw root node fixes 🩹 2024-03-17 10:52:28 +11:00
Jokob-sk
d955e058e1 Multi edit ⚒ 2024-03-17 10:27:31 +11:00
Jokob-sk
0615611a49 UI tweaks 🩹 2024-03-17 09:41:16 +11:00
jokob-sk
59243813a8 Merge pull request #591 from Schlump/main
Add Pushover device support - thanks @Schlump 🙏
2024-03-17 08:23:32 +11:00
Schlump
195206c699 Update pushover.py 2024-03-16 15:34:22 +01:00
Schlump
2f170fb156 Update config.json 2024-03-16 15:34:10 +01:00
github-actions[bot]
e7764324dc [🤖Automation] Update README with sponsors information 2024-03-16 11:53:51 +00:00
Anonymous
958444984a Translated using Weblate (Spanish)
Currently translated at 98.9% (641 of 648 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-03-16 01:58:21 +01:00
Anonymous
594af4903e Translated using Weblate (German)
Currently translated at 89.5% (580 of 648 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-03-16 01:58:21 +01:00
Jokob-sk
dd4de7c5a3 UI tweaks 🩹 2024-03-16 11:54:37 +11:00
Jokob-sk
1fa49a7730 cache fixes 🩹 2024-03-16 11:24:24 +11:00
Jokob-sk
7c70d435e4 menu css fixes 🩹 2024-03-16 10:57:15 +11:00
Jokob-sk
2e159635c6 menuitem Integrations + docs 📚 2024-03-16 10:51:05 +11:00
Jokob-sk
23aa1e4e85 multi-edit floating, no-cache headers 🔘 2024-03-16 10:40:18 +11:00
Jokob-sk
a99dbaef78 sysinfo improvements 2024-03-16 09:42:21 +11:00
github-actions[bot]
06a1fa3512 [🤖Automation] Update README with sponsors information 2024-03-15 11:54:04 +00:00
github-actions[bot]
e2270b4439 [🤖Automation] Update README with sponsors information 2024-03-14 11:53:37 +00:00
github-actions[bot]
4c790c6ff2 [🤖Automation] Update README with sponsors information 2024-03-13 11:53:50 +00:00
github-actions[bot]
07432daa28 [🤖Automation] Update README with sponsors information 2024-03-12 11:53:38 +00:00
Jokob-sk
54901be437 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-03-11 23:15:21 +11:00
Jokob-sk
fb1e73d7d2 cleanup + drpdown fixes 2024-03-11 23:15:15 +11:00
github-actions[bot]
1401a24533 [🤖Automation] Update README with sponsors information 2024-03-11 11:53:39 +00:00
Jokob-sk
27ae11c1bc cleanup + log fixes 2024-03-11 07:57:49 +11:00
Jokob-sk
8817f8d0e9 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-03-10 23:07:04 +11:00
Jokob-sk
4f9e8c5ecd cleanup + fixes 2024-03-10 23:06:43 +11:00
github-actions[bot]
c201e98f5b [🤖Automation] Update README with sponsors information 2024-03-10 11:53:36 +00:00
Jokob-sk
7cd76178b1 cleanup + fixes 2024-03-10 22:48:49 +11:00
Jokob-sk
bcf4144364 cleanup + fixes 2024-03-10 22:41:33 +11:00
Jokob-sk
78352f77b7 cleanup + fixes 2024-03-10 22:21:43 +11:00
Jokob-sk
e38d2f9055 dynamic dropdown support in FE - core app feature 💠 2024-03-10 21:50:04 +11:00
Hosted Weblate
a66df76f74 Merge branch 'origin/main' into Weblate. 2024-03-10 00:01:57 +01:00
gallegonovato
a6d3c92d2a Translated using Weblate (Spanish)
Currently translated at 100.0% (643 of 643 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-03-10 00:01:55 +01:00
github-actions[bot]
9720a15e91 [🤖Automation] Update README with sponsors information 2024-03-09 11:53:39 +00:00
Jokob-sk
6a0033da75 lang updates 🌎 2024-03-09 08:27:39 +11:00
Jokob-sk
6fcdaf8843 Weblate 2024-03-09 08:22:35 +11:00
Anonymous
97fc553278 Translated using Weblate (Norwegian Bokmål)
Currently translated at 1.7% (11 of 646 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/nb_NO/
2024-03-08 22:19:18 +01:00
Anonymous
bb57080b06 Translated using Weblate (Russian)
Currently translated at 10.8% (70 of 646 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2024-03-08 22:17:41 +01:00
Anonymous
1310a4b751 Translated using Weblate (French)
Currently translated at 44.2% (286 of 646 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-03-08 22:17:41 +01:00
Anonymous
1c7e729036 Translated using Weblate (Spanish)
Currently translated at 99.5% (643 of 646 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-03-08 22:17:41 +01:00
Anonymous
d7af580488 Translated using Weblate (German)
Currently translated at 90.4% (584 of 646 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-03-08 22:17:40 +01:00
Jokob-sk
37e163e883 lang updates 🌎 2024-03-09 08:17:03 +11:00
Jokob-sk
785f7c03bf Menu updates🧭 2024-03-09 08:04:14 +11:00
github-actions[bot]
849c39d75d [🤖Automation] Update README with sponsors information 2024-03-08 11:54:03 +00:00
github-actions[bot]
c57e3e7557 [🤖Automation] Update README with sponsors information 2024-03-07 11:53:46 +00:00
github-actions[bot]
ddc747e192 [🤖Automation] Update README with sponsors information 2024-03-06 11:53:47 +00:00
Jokob-sk
92801d6ddc Menu updates🧭 2024-03-05 23:12:17 +11:00
github-actions[bot]
b6c464be6d [🤖Automation] Update README with sponsors information 2024-03-05 11:53:45 +00:00
Jokob-sk
be81668d6d Smooth scrolling 2024-03-05 16:17:52 +11:00
Jokob-sk
8e85abfda4 Paho MQTT Version slection support + JS/CSS fixes #580 🩹 2024-03-05 09:34:13 +11:00
github-actions[bot]
5559194617 [🤖Automation] Update README with sponsors information 2024-03-04 12:13:02 +00:00
github-actions[bot]
1cc5cd56f8 [🤖Automation] Update README with sponsors information 2024-03-03 11:53:52 +00:00
jokob-sk
9d4759898d Merge pull request #585 from vladaurosh/main
Adding lsblk package
2024-03-03 14:52:22 +11:00
root
178ff30938 Adding lsblk package 2024-03-03 03:46:27 +00:00
Jokob-sk
a44575926f caching fixes 🩹 2024-03-03 11:38:27 +11:00
Hosted Weblate
6a367c826e Merge branch 'origin/main' into Weblate. 2024-03-02 13:02:02 +01:00
gallegonovato
83336d3289 Translated using Weblate (Spanish)
Currently translated at 100.0% (644 of 644 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-03-02 13:02:00 +01:00
github-actions[bot]
12ba6fbdad [🤖Automation] Update README with sponsors information 2024-03-02 11:53:54 +00:00
Jokob-sk
17a4656c41 small fixes 🩹 #582 2024-03-02 14:20:12 +11:00
jokob-sk
7ef3fe5ac0 Merge pull request #583 from vladaurosh/main
Removing unneeded package, small tweaks
2024-03-02 13:59:15 +11:00
root
06c7ffa39e Fixing hardcoded path 2024-03-02 02:08:40 +00:00
root
9ac0163f20 removing unneeded package, small tweaks 2024-03-02 01:41:03 +00:00
Jokob-sk
0126e448cc alpine re-base cleanup 🧹 2024-03-02 10:29:45 +11:00
Jokob-sk
2362622cd0 alpine re-base cleanup 🧹 2024-03-02 10:06:32 +11:00
Jokob-sk
ca2df744df alpine re-base 2024-03-02 08:58:38 +11:00
jokob-sk
9420c41e7c Merge pull request #581 from vladaurosh/main
Adding support for alpine-based image - thanks to @vladaurosh 🙏
2024-03-02 08:30:14 +11:00
github-actions[bot]
a015466c7f [🤖Automation] Update README with sponsors information 2024-03-01 11:53:37 +00:00
root
89f2c28046 Adding support for alpine based image 2024-02-29 21:56:55 +00:00
github-actions[bot]
57d0680b6a [🤖Automation] Update README with sponsors information 2024-02-29 11:53:47 +00:00
Jokob-sk
ddd405f379 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-29 21:42:10 +11:00
Jokob-sk
3c38909b57 Multi edit CSS 2024-02-29 21:41:45 +11:00
github-actions[bot]
e830d1718e [🤖Automation] Update README with sponsors information 2024-02-28 11:53:39 +00:00
Jokob-sk
0ab78ffab7 merge 2024-02-28 21:12:10 +11:00
Jokob-sk
9685784452 Docs, Mass-delete 📚 2024-02-28 21:03:27 +11:00
Hosted Weblate
2a9085151f Merge branch 'origin/main' into Weblate. 2024-02-27 16:02:48 +01:00
Anonymous
c6fb35838a Translated using Weblate (German)
Currently translated at 91.2% (585 of 641 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-02-27 16:02:42 +01:00
github-actions[bot]
588ddf3cb3 [🤖Automation] Update README with sponsors information 2024-02-27 11:53:36 +00:00
Jokob-sk
84f96d72c8 Docs 📚 2024-02-27 08:07:11 +11:00
Jokob-sk
ed2ba9a435 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-27 08:04:04 +11:00
Jokob-sk
265d313719 mark TIMEOUT log entry as ERROR 2024-02-27 08:03:58 +11:00
Hosted Weblate
21ebc55335 Merge branch 'origin/main' into Weblate. 2024-02-26 16:02:00 +01:00
gallegonovato
a7bfc6f6f6 Translated using Weblate (Spanish)
Currently translated at 100.0% (641 of 641 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-26 16:01:58 +01:00
github-actions[bot]
369fc44183 [🤖Automation] Update README with sponsors information 2024-02-26 11:53:58 +00:00
github-actions[bot]
6a2a56e059 [🤖Automation] Update README with sponsors information 2024-02-25 11:53:44 +00:00
Jokob-sk
b0008ebd3f Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-25 09:45:34 +11:00
Jokob-sk
c624bfeae0 Docs 📚 2024-02-25 09:44:41 +11:00
github-actions[bot]
624d7499a5 [🤖Automation] Update README with sponsors information 2024-02-24 11:53:56 +00:00
Jokob-sk
4f5fbb1316 Multi-edit ✏ #571 2024-02-24 13:30:18 +11:00
github-actions[bot]
7d715493a6 [🤖Automation] Update README with sponsors information 2024-02-23 11:53:55 +00:00
github-actions[bot]
0c92bf8d0a [🤖Automation] Update README with sponsors information 2024-02-22 11:53:38 +00:00
github-actions[bot]
ea51b93263 [🤖Automation] Update README with sponsors information 2024-02-21 11:53:55 +00:00
github-actions[bot]
539556e01d [🤖Automation] Update README with sponsors information 2024-02-20 11:53:38 +00:00
Hosted Weblate
43e9fc324f Merge branch 'origin/main' into Weblate. 2024-02-20 10:19:54 +01:00
Safeguard
e6e4620e13 Translated using Weblate (Russian)
Currently translated at 11.1% (71 of 636 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2024-02-20 10:19:53 +01:00
github-actions[bot]
6963e0b507 [🤖Automation] Update README with sponsors information 2024-02-19 11:53:40 +00:00
Jokob-sk
460f8038f2 weblate 2024-02-19 21:37:45 +11:00
Jokob-sk
ee2e228e15 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-19 07:36:19 +11:00
Jokob-sk
15ab54f5d5 Hide labels in Online Presence #569 2024-02-19 07:36:03 +11:00
github-actions[bot]
2226b9ff39 [🤖Automation] Update README with sponsors information 2024-02-18 11:53:34 +00:00
Jokob-sk
d4b701653e PAHO MQTT API v2 2024-02-18 11:38:56 +11:00
Jokob-sk
34f5658516 ALWAYS_FRESH_INSTALL fix 2024-02-18 09:48:22 +11:00
Jokob-sk
ea7dfa832d docs 📚 2024-02-18 09:22:14 +11:00
Jokob-sk
9bbd549d93 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-18 08:28:13 +11:00
Jokob-sk
7424cf4645 docs 📚 + work on #569 2024-02-18 08:28:08 +11:00
github-actions[bot]
19f767a887 [🤖Automation] Update README with sponsors information 2024-02-17 11:53:36 +00:00
Jokob-sk
57d9024ed3 BACKUPS.md docs 📚 2024-02-17 10:08:00 +11:00
Jokob-sk
87dd1cdf2d ALWAYS_FRESH_INSTALL variable 2024-02-17 08:51:10 +11:00
Jokob-sk
fdf381d565 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-17 07:26:15 +11:00
Jokob-sk
20e29ecd15 Empty Devices error #568🩹 2024-02-17 07:25:42 +11:00
github-actions[bot]
a6ce702487 [🤖Automation] Update README with sponsors information 2024-02-16 11:53:42 +00:00
Jokob-sk
fa3949db05 weblate 2024-02-16 08:07:51 +11:00
jokob-sk
0fa7bd2486 Translated using Weblate (German)
Currently translated at 92.1% (586 of 636 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-02-15 22:06:43 +01:00
Hosted Weblate
c5101f6c2d Merge branch 'origin/main' into Weblate. 2024-02-15 22:02:12 +01:00
gallegonovato
ace3368b6a Translated using Weblate (Spanish)
Currently translated at 100.0% (636 of 636 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-15 22:02:09 +01:00
Jokob-sk
458cf609ab UNFIMP missing Vendor mapping #567 2024-02-16 07:28:49 +11:00
github-actions[bot]
ec923cfebc [🤖Automation] Update README with sponsors information 2024-02-15 11:53:58 +00:00
Hosted Weblate
75bcf5ba60 Merge branch 'origin/main' into Weblate. 2024-02-14 21:53:40 +01:00
gallegonovato
4efc94b8db Translated using Weblate (Spanish)
Currently translated at 100.0% (635 of 635 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-14 21:53:37 +01:00
Jokob-sk
af452adc56 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-15 07:52:52 +11:00
Jokob-sk
0b45ead574 New Device checkbox tooltip 2024-02-15 07:52:35 +11:00
github-actions[bot]
a9947f17d5 [🤖Automation] Update README with sponsors information 2024-02-14 11:53:52 +00:00
Jokob-sk
356b9c6888 Weblate 2024-02-14 20:41:49 +11:00
gallegonovato
142e67df36 Translated using Weblate (Spanish)
Currently translated at 100.0% (633 of 633 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-14 10:34:42 +01:00
Jokob-sk
21ed86acc2 My Devices fix 2024-02-14 20:30:04 +11:00
Jokob-sk
3839732a64 Random MAC setting UI_NOT_RANDOM_MAC #565 2024-02-14 20:18:12 +11:00
Jokob-sk
a2d0794410 set pwd changes #565 2024-02-14 07:57:24 +11:00
Jokob-sk
4a88478efe Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-14 07:57:01 +11:00
Jokob-sk
1720e065cc set pwd changes #565 2024-02-14 07:56:45 +11:00
Hosted Weblate
5a08aeeb1d Merge branch 'origin/main' into Weblate. 2024-02-13 16:51:55 +01:00
gallegonovato
bc8d0091c3 Translated using Weblate (Spanish)
Currently translated at 99.5% (630 of 633 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-13 16:51:55 +01:00
Anonymous
ce1a325190 Translated using Weblate (French)
Currently translated at 45.1% (286 of 633 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-02-13 16:51:54 +01:00
Anonymous
e09d638c45 Translated using Weblate (Spanish)
Currently translated at 99.3% (629 of 633 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-13 16:51:54 +01:00
Anonymous
2469fd93c0 Translated using Weblate (German)
Currently translated at 90.8% (575 of 633 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-02-13 16:51:54 +01:00
github-actions[bot]
a9913d0759 [🤖Automation] Update README with sponsors information 2024-02-13 11:53:43 +00:00
Jokob-sk
0d756c9e70 docs 📚 + deviceDetails fixes 2024-02-13 08:16:00 +11:00
Jokob-sk
daa03e106b docs 📚 2024-02-13 07:43:47 +11:00
Jokob-sk
e99d26b044 docs 2024-02-12 23:13:31 +11:00
Jokob-sk
42594c0602 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-12 22:55:15 +11:00
Jokob-sk
d96127c93e My Devices filter #548 2024-02-12 22:54:49 +11:00
github-actions[bot]
f4718b69c5 [🤖Automation] Update README with sponsors information 2024-02-12 11:53:42 +00:00
Jokob-sk
34af7b25e0 localization 🌍 2024-02-12 20:35:21 +11:00
Jokob-sk
11ba11e016 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-12 20:34:10 +11:00
Jokob-sk
f5cfc365d3 docs 2024-02-12 20:34:06 +11:00
Gooseman
e2828c3869 Translated using Weblate (French)
Currently translated at 45.4% (287 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-02-12 10:32:47 +01:00
Hosted Weblate
1e54cd42ce Merge branch 'origin/main' into Weblate. 2024-02-12 01:43:50 +01:00
Gooseman
0a986a8571 Translated using Weblate (French)
Currently translated at 29.1% (184 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-02-12 01:43:50 +01:00
Gooseman
85c49398bc Translated using Weblate (French)
Currently translated at 29.1% (184 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-02-12 01:43:49 +01:00
ButterflyOfFire
0a47eca844 Translated using Weblate (French)
Currently translated at 29.1% (184 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-02-12 01:43:48 +01:00
gallegonovato
3aa16f8abf Translated using Weblate (Spanish)
Currently translated at 100.0% (631 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-12 01:43:47 +01:00
github-actions[bot]
c48596e865 [🤖Automation] Update README with sponsors information 2024-02-11 11:54:01 +00:00
Hosted Weblate
88982de4f4 Merge branch 'origin/main' into Weblate. 2024-02-11 12:20:00 +01:00
gallegonovato
b0c2c85584 Translated using Weblate (Spanish)
Currently translated at 99.6% (629 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-11 12:19:59 +01:00
Allan Nordhøy
65ee425645 Translated using Weblate (Norwegian Bokmål)
Currently translated at 1.7% (11 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/nb_NO/
2024-02-11 12:19:58 +01:00
gallegonovato
d26fdb7f50 Translated using Weblate (Spanish)
Currently translated at 99.5% (628 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-11 12:19:58 +01:00
Allan Nordhøy
6134324cfd Translated using Weblate (English (United States))
Currently translated at 99.8% (630 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/en_US/
2024-02-11 12:19:58 +01:00
Jokob-sk
61c99a3d8e translation work 🗣 2024-02-11 09:46:16 +11:00
Anonymous
821a166c61 Translated using Weblate (Spanish)
Currently translated at 90.6% (572 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-10 23:40:25 +01:00
Anonymous
5c4914eeec Translated using Weblate (German)
Currently translated at 91.9% (580 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-02-10 23:40:24 +01:00
Jokob-sk
b85e8e9f15 translation work 🗣 2024-02-11 09:39:37 +11:00
Jokob-sk
2a53298fda translation work 🗣 2024-02-11 09:34:40 +11:00
jokob-sk
4ac333e067 Translated using Weblate (German)
Currently translated at 92.0% (581 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-02-10 23:33:00 +01:00
Jokob-sk
001cb38924 translation work 🗣 2024-02-11 09:28:37 +11:00
Anonymous
10f93d40ff Translated using Weblate (French)
Currently translated at 0.4% (3 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-02-10 23:02:12 +01:00
gallegonovato
0d9b8807b3 Translated using Weblate (Spanish)
Currently translated at 90.8% (573 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-10 23:02:12 +01:00
Anonymous
1b0fb346ef Translated using Weblate (Spanish)
Currently translated at 90.8% (573 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-02-10 23:02:12 +01:00
Anonymous
700b9ebfec Translated using Weblate (German)
Currently translated at 91.9% (580 of 631 strings)

Translation: PiAlert/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2024-02-10 23:02:07 +01:00
Jokob-sk
2cdf6e9bf3 translation work 🗣 2024-02-11 08:48:50 +11:00
Jokob-sk
5a8d8f0828 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-11 08:04:52 +11:00
Jokob-sk
246de74ad4 translation work 🗣 2024-02-11 08:04:29 +11:00
github-actions[bot]
ba6f7d5a60 [🤖Automation] Update README with sponsors information 2024-02-10 11:53:44 +00:00
Jokob-sk
f474561593 Weblate + docs + plugins cleanup ♻ 2024-02-10 14:10:43 +11:00
Jokob-sk
b4e292bf5c Weblate 2024-02-10 08:30:34 +11:00
Anonymous
7d32bbafad Translated using Weblate (French)
Currently translated at 100.0% (2 of 2 strings)

Translation: PiAlert/PiAlert-core
Translate-URL: https://hosted.weblate.org/projects/pialert/pialert-core/fr/
2024-02-09 22:26:46 +01:00
Anonymous
5e7d640e98 Translated using Weblate (English (United States))
Currently translated at 99.8% (629 of 630 strings)

Translation: PiAlert/PiAlert-core
Translate-URL: https://hosted.weblate.org/projects/pialert/pialert-core/en_US/
2024-02-09 22:26:45 +01:00
Anonymous
cefbf019c6 Translated using Weblate (Spanish)
Currently translated at 99.8% (639 of 640 strings)

Translation: PiAlert/PiAlert-core
Translate-URL: https://hosted.weblate.org/projects/pialert/pialert-core/es/
2024-02-09 22:26:45 +01:00
Anonymous
1b877ddcfb Translated using Weblate (German)
Currently translated at 99.6% (659 of 661 strings)

Translation: PiAlert/PiAlert-core
Translate-URL: https://hosted.weblate.org/projects/pialert/pialert-core/de/
2024-02-09 22:26:44 +01:00
Jokob-sk
61f740397b Weblate 2024-02-10 08:25:12 +11:00
Jokob-sk
ca389e3824 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-10 08:08:33 +11:00
Jokob-sk
4eb955b19a Weblate 2024-02-10 08:07:32 +11:00
Anonymous
08364a26db Translated using Weblate (Spanish)
Currently translated at 99.8% (639 of 640 strings)

Translation: PiAlert/PiAlert-core
Translate-URL: https://hosted.weblate.org/projects/pialert/pialert-core/es/
2024-02-09 21:50:08 +01:00
github-actions[bot]
b70b395860 [🤖Automation] Update README with sponsors information 2024-02-09 11:53:49 +00:00
Jokob-sk
169617bdd6 404 error in the _front.log + work in progress icon #556 🚑 2024-02-09 20:50:55 +11:00
jokob-sk
3f75ead025 Merge pull request #559 from Schlump/main
Add Pushover Support by @Schlump 🙏
2024-02-09 07:24:42 +11:00
schlump
37396aad71 Add Pushover Support 2024-02-08 17:17:40 +01:00
github-actions[bot]
02de8f39b0 [🤖Automation] Update README with sponsors information 2024-02-08 11:53:46 +00:00
Jokob-sk
b33104fc5d Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-08 19:31:27 +11:00
Jokob-sk
13351a5db6 404 error in the browser log #556 🚑 2024-02-08 19:31:18 +11:00
github-actions[bot]
d566034421 [🤖Automation] Update README with sponsors information 2024-02-07 11:54:02 +00:00
github-actions[bot]
bd58885237 [🤖Automation] Update README with sponsors information 2024-02-06 11:53:37 +00:00
Jokob-sk
508a2d67b9 New Devices Columns #556 🚑 2024-02-06 09:01:55 +11:00
Jokob-sk
6b39a29838 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-06 08:09:23 +11:00
Jokob-sk
50bcd8813a NSLOOKUP v0.1.8🚑 2024-02-06 08:08:56 +11:00
github-actions[bot]
7542761571 [🤖Automation] Update README with sponsors information 2024-02-05 11:53:47 +00:00
Jokob-sk
f80d4eef4a Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-05 20:51:19 +11:00
Jokob-sk
02771cf399 Workflows v0.1.13 🔀 2024-02-05 20:51:00 +11:00
Jokob-sk
6c5e0d4907 Workflows v0.1.12 2024-02-05 08:14:12 +11:00
github-actions[bot]
295f5af1eb [🤖Automation] Update README with sponsors information 2024-02-04 11:53:42 +00:00
Jokob-sk
c2446147f6 Network fixes 🚑 2024-02-04 19:05:23 +11:00
Jokob-sk
519cf9f69a Workflows v0.1.1 🆕 2024-02-04 13:17:41 +11:00
Jokob-sk
528caa900c Blank fr_f.json #547 🆕 2024-02-04 08:10:49 +11:00
Jokob-sk
fc371a6e92 NEWDEV IP filter + dark pach for overlay #539 🆕 2024-02-03 23:32:14 +11:00
Jokob-sk
476d646d72 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-03 23:29:04 +11:00
Jokob-sk
a0e1d1a404 NEWDEV IP filter + dark pach for overlay #539 🆕 2024-02-03 23:24:41 +11:00
github-actions[bot]
3940761e9e [🤖Automation] Update README with sponsors information 2024-02-03 11:53:39 +00:00
Hosted Weblate
722800e9c3 Merge branch 'origin/main' into Weblate. 2024-02-03 04:34:34 +01:00
jokob-sk
cabc1ed81f Translated using Weblate (German)
Currently translated at 99.8% (660 of 661 strings)

Translation: PiAlert/PiAlert-core
Translate-URL: https://hosted.weblate.org/projects/pialert/pialert-core/de/
2024-02-03 04:34:31 +01:00
jokob-sk
b456748641 Translated using Weblate (English (United States))
Currently translated at 99.8% (604 of 605 strings)

Translation: PiAlert/PiAlert-core
Translate-URL: https://hosted.weblate.org/projects/pialert/pialert-core/en_US/
2024-02-03 04:08:51 +01:00
jokob-sk
091dc72f63 Translated using Weblate (German)
Currently translated at 99.8% (660 of 661 strings)

Translation: PiAlert/PiAlert-core
Translate-URL: https://hosted.weblate.org/projects/pialert/pialert-core/de/
2024-02-03 03:47:39 +01:00
Jokob-sk
36e143f262 Weblate setup 0.1 🆕 2024-02-03 12:53:29 +11:00
Jokob-sk
9dbf80ddcf NEWDEV MAC filter #539 🆕 2024-02-03 11:28:04 +11:00
Jokob-sk
47bdf65673 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-03 07:46:20 +11:00
Jokob-sk
d58af0f4e0 NTFY priority #553 🚑 2024-02-03 07:45:57 +11:00
github-actions[bot]
fd98b52752 [🤖Automation] Update README with sponsors information 2024-02-02 11:53:52 +00:00
Jokob-sk
853a7eebd0 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-02 07:55:49 +11:00
Jokob-sk
de41f02098 Maintenance -> Delete unknown devices improvement #551 🚑 2024-02-02 07:55:31 +11:00
jokob-sk
122edc8003 Merge pull request #542 from leiweibau/patch-1
Translation work de_de.json - thanks to @leiweibau 🙏
2024-02-02 07:45:39 +11:00
github-actions[bot]
6874d7c111 [🤖Automation] Update README with sponsors information 2024-02-01 11:53:40 +00:00
Jokob-sk
d14957c2b5 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-02-01 07:57:17 +11:00
Jokob-sk
f5e39b8281 NSLOOKUP v0.1.7 2024-02-01 07:56:57 +11:00
github-actions[bot]
7137c6af8e [🤖Automation] Update README with sponsors information 2024-01-31 11:53:40 +00:00
Jokob-sk
a6ac7991e5 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-01-30 23:09:27 +11:00
Jokob-sk
7250fb490f Sponsorship auto-generate v0.1.6 2024-01-30 23:09:24 +11:00
github-actions[bot]
b3e51f7318 [🤖Automation] Update README with sponsors information 2024-01-30 11:55:15 +00:00
Jokob-sk
f51040a589 Sponsorship auto-generate v0.1.5 2024-01-30 22:46:07 +11:00
Jokob-sk
d6e47541a5 Sponsorship auto-generate v0.1.5 2024-01-30 22:45:49 +11:00
Jokob-sk
8260759f12 NSLOOKUP v0.1.6 2024-01-30 22:26:03 +11:00
Jokob-sk
9e66ac78f8 NSLOOKUP v0.1.5 2024-01-30 22:24:01 +11:00
Jokob-sk
7b4b43463a NSLOOKUP v0.1.5 2024-01-30 22:19:59 +11:00
Jokob-sk
4e721fa8c6 Sponsorship auto-generate v0.1.3 2024-01-30 21:45:26 +11:00
Jokob-sk
635d285274 Sponsorship auto-generate v0.1.2 2024-01-30 21:43:43 +11:00
Jokob-sk
b2231a592d Reverse DNS docs + NEW status priority #549 2024-01-30 21:32:03 +11:00
github-actions[bot]
79f2c4a1b0 [🤖Automation] Update README with sponsors information 2024-01-29 20:53:21 +00:00
github-actions[bot]
84dbc37312 [🤖Automation] Update README with sponsors information 2024-01-28 20:56:07 +00:00
Jokob-sk
efef2da546 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-01-29 07:44:45 +11:00
Jokob-sk
f05c90063c Sponsorship auto-generate v0.1.41 2024-01-29 07:44:32 +11:00
github-actions[bot]
e39c36b124 [🤖Automation] Update README with sponsors information 2024-01-28 12:49:39 +00:00
Jokob-sk
131c83826a NSLOOKUP v0.1.2 debug 2024-01-28 23:36:11 +11:00
Jokob-sk
5f5f52251a Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-01-28 23:32:33 +11:00
Jokob-sk
1f9fc71416 Sponsorship auto-generate v0.1.50 2024-01-28 23:32:27 +11:00
github-actions[bot]
3577c2143e [🤖Automation] Update README with sponsors information 2024-01-28 11:48:33 +00:00
Jokob-sk
8e603fd5f9 Sponsorship auto-generate v0.1.40 2024-01-28 22:37:44 +11:00
Jokob-sk
1c87fb1284 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-01-28 22:37:22 +11:00
github-actions[bot]
bde1ef93cf [🤖Automation] Update README with sponsors information 2024-01-28 11:32:15 +00:00
Jokob-sk
132d6413b5 warning build fixes 2024-01-28 22:24:24 +11:00
Jokob-sk
ca82970070 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-01-28 22:21:45 +11:00
Jokob-sk
d1e46b29d0 Sponsorship auto-generate v0.1.39 + columns fix in maintenance 2024-01-28 22:21:16 +11:00
github-actions[bot]
8fa5eb9725 [🤖Automation] Update README with sponsors information 2024-01-28 11:15:16 +00:00
Jokob-sk
bb25685691 Sponsorship auto-generate v0.1.38 2024-01-28 22:07:47 +11:00
Jokob-sk
75316a70b9 Sponsorship auto-generate v0.1.37 2024-01-28 21:46:37 +11:00
Jokob-sk
194d996f22 Sponsorship auto-generate v0.1.36 2024-01-28 21:30:57 +11:00
Jokob-sk
64418d11fc Sponsorship auto-generate v0.1.35 2024-01-28 21:10:15 +11:00
Jokob-sk
815c140f11 Sponsorship auto-generate v0.1.34 2024-01-28 20:48:54 +11:00
Jokob-sk
5bc8b51633 Sponsorship auto-generate v0.1.33 2024-01-28 14:57:59 +11:00
Jokob-sk
d807c6d0e5 Sponsorship auto-generate v0.1.32 2024-01-28 14:55:55 +11:00
Jokob-sk
a986b8c3dd Sponsorship auto-generate v0.1.31 2024-01-28 13:49:37 +11:00
Jokob-sk
ee5cf9baa4 Sponsorship auto-generate v0.1.3 2024-01-28 13:48:50 +11:00
Jokob-sk
a6a495d242 Sponsorship auto-generate v0.1.2 2024-01-28 13:47:49 +11:00
Jokob-sk
4486c57b48 Sponsorship auto-generate v0.1.1 2024-01-28 12:37:58 +11:00
Jokob-sk
7347a63f37 docs📚 2024-01-28 11:27:22 +11:00
Jokob-sk
edb3ee8c86 docs📚 2024-01-28 11:25:28 +11:00
Jokob-sk
a7227ca715 Sponsorship auto-generate v0.1 + docs 2024-01-28 11:08:32 +11:00
Jokob-sk
5cfe0bf713 NSLOOKUP v0.1.1 + docs 2024-01-27 11:11:19 +11:00
Jokob-sk
d18a59944b NSLOOKUP v0.1 2024-01-26 09:23:55 +11:00
leiweibau
ba6b971bc4 Update de_de.json 04 2024-01-23 21:30:46 +01:00
leiweibau
f772bb0e26 Update de_de.json 03 2024-01-23 16:43:24 +01:00
leiweibau
556ee04eac Update de_de.json 02 2024-01-23 13:29:56 +01:00
leiweibau
09fc16d873 Update de_de.json 01 2024-01-23 13:20:01 +01:00
Jokob-sk
f40f99aac9 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2024-01-21 08:10:40 +11:00
jokob-sk
7e3cc88f2b Merge pull request #536 from leiweibau/patch-2
Update jokob-sk graph_online_history.js - thank you @leiweibau 🙏
2024-01-21 08:08:23 +11:00
jokob-sk
4b556ae8b4 Merge pull request #537 from leiweibau/patch-1
Update jokob-sk de_de.json 01 - thank you @leiweibau 🙏
2024-01-21 08:07:38 +11:00
leiweibau
8e8d61a0e0 Update jokob-sk graph_online_history.js
Depending on how many devices are contained in the list, the y-axis may contain floating point numbers. This does not make sense.
2024-01-20 15:00:47 +01:00
leiweibau
d0b7ed4b85 Update jokob-sk de_de.json 01 2024-01-20 14:53:38 +01:00
Jokob-sk
4b1c184338 docs📚 2024-01-18 17:47:01 +11:00
Jokob-sk
508b0c2d83 dig utility name discovery work #534 2024-01-18 17:10:07 +11:00
Jokob-sk
b354e72489 update issue template 2024-01-16 07:25:17 +11:00
Jokob-sk
90bfa70d1b update_vendors work #533 🔃 2024-01-13 08:56:05 +11:00
Jokob-sk
7561a8478d update_vendors work #533 🔃 2024-01-13 08:42:31 +11:00
Jokob-sk
b5afdb2bce docs📚 2024-01-08 08:08:51 +11:00
Jokob-sk
5207162d0a clickable settings cards ⚙ 2024-01-07 11:40:09 +11:00
Jokob-sk
8eecc54217 docs 📚 2024-01-07 11:18:52 +11:00
Jokob-sk
ddfd0d3cb3 docs 📚, links to plugin docs 2024-01-07 10:26:08 +11:00
Jokob-sk
bcc5b2f28a Issue templates 2024-01-06 18:59:09 +11:00
Jokob-sk
2e6be21cd9 Issue templates 2024-01-06 13:12:00 +11:00
Jokob-sk
abb28c4e5b Issue templates 2024-01-06 12:48:30 +11:00
Jokob-sk
44f0ba0924 MQTT settings to select what is send #364📩 2024-01-06 12:09:59 +11:00
Jokob-sk
a6f5e6c499 MQTT enable scheduler #522 2024-01-06 10:26:39 +11:00
Jokob-sk
d992edf6b4 MQTT more logging #522 2024-01-05 18:42:32 +11:00
Jokob-sk
d7ba540377 renamed DIG_GET_IP_ARG to INTRNT_DIG_GET_IP_ARG v0.2 2024-01-05 08:33:30 +11:00
Jokob-sk
30c95f0d5e renamed DIG_GET_IP_ARG to INTRNT_DIG_GET_IP_ARG v0.1 2024-01-05 08:29:27 +11:00
Jokob-sk
b670e3a8b1 renamed DIG_GET_IP_ARG to INTRNT_DIG_GET_IP_ARG 2024-01-05 08:18:52 +11:00
Jokob-sk
467e24d167 ARMv6 test #527 - added to prod image 2024-01-05 08:00:54 +11:00
Jokob-sk
7b70d61dd8 ARMv6 test #527 2024-01-05 07:42:43 +11:00
Jokob-sk
d530576e9b NTFPRCS work + docs 2024-01-04 15:28:43 +11:00
Jokob-sk
cff6f6393d PIHOLE_CMD_OLD bug 2024-01-04 14:37:22 +11:00
Jokob-sk
f33d753cc1 work #504 - New Dev + Events filter setting⚙ 2024-01-04 14:33:26 +11:00
Jokob-sk
6809688623 docs 📚 2024-01-04 13:26:28 +11:00
Jokob-sk
68de633143 work on #523 - ip v6 ordering support 0.1 2024-01-04 09:03:35 +11:00
Jokob-sk
a3e21ac17d work on #523 - ip v6 ordering support 2024-01-03 07:31:32 +11:00
Jokob-sk
c8267f75fa Re-initialize PIHOLE_CMD + docs 2024-01-02 08:06:00 +11:00
Jokob-sk
9e6a52ca4b work #520 - install logging 2024-01-01 10:15:25 +11:00
Jokob-sk
13c68efb8a PiHole work #513 2023-12-21 21:17:05 +11:00
Jokob-sk
47a3f7073b _meta unbound #519 2023-12-18 07:55:41 +11:00
Jokob-sk
8e0eb6a480 Notifications refactor and #242 📩 2023-12-17 22:43:50 +11:00
Jokob-sk
911c897b00 Notifications refactor 🏗 2023-12-17 10:39:14 +11:00
Jokob-sk
b9650d3cf5 Disabling cache to fix build issues 🩹 2023-12-16 20:11:01 +11:00
Jokob-sk
3c959a7920 Clenup & prune attempt 8 🧪 2023-12-16 20:06:35 +11:00
Jokob-sk
5e170da542 Clenup & prune attempt 7 🧪 2023-12-16 19:47:42 +11:00
Jokob-sk
63932fb5bc Clenup & prune attempt 6 🧪 2023-12-16 17:05:20 +11:00
Jokob-sk
741c0f9ede Clenup & prune attempt 5 🧪 2023-12-16 17:04:12 +11:00
Jokob-sk
08abbabaad Clenup & prune attempt 4 🧪 2023-12-16 17:01:50 +11:00
Jokob-sk
65c8f81afd Clenup & prune attempt 3 🧪 2023-12-16 17:00:18 +11:00
Jokob-sk
80958c2e3f Clenup & prune attempt 2 🧪 2023-12-16 16:58:08 +11:00
Jokob-sk
233873704d Clenup & prune attempt 🧪 2023-12-16 16:50:54 +11:00
Jokob-sk
90322c4747 Devices view spinner #509🔃 2023-12-16 16:33:01 +11:00
jokob-sk
57e6a330be Merge pull request #518 from LouisOb/main
FIX unable to send mail with publisher mail plugin - thanks to @LouisOb 🙏
2023-12-15 20:48:05 +00:00
loberer
0f86b05ce5 FIX email_smtp.py: smtp_timeout was undefined in scope of send_mail 2023-12-15 13:26:33 +01:00
Jokob-sk
9dd3a0a2d1 skip invalid dhcp.leases entries #516🩹 2023-12-11 11:12:08 +11:00
Jokob-sk
20f847c6d8 fix MQTT entity names for Home Assistant #514🩹 2023-12-08 07:48:59 +11:00
Jokob-sk
8cd20ab343 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-12-08 07:33:27 +11:00
Jokob-sk
de5dfa9d06 fix SNMP discovery + other #512🩹 2023-12-08 07:32:50 +11:00
jokob-sk
19fe6d53d5 Merge pull request #511 from mscreations/version-check-fix
Fix date parsing for release check - thanks to @mscreations - appreciate it - I've been flat out IRL these days 🙏
2023-11-29 00:25:18 +00:00
Jon
37fa7fe8a8 Fix date parsing for release check
Fixes an issue with date parsing for update check
2023-11-28 19:07:50 -05:00
Jokob-sk
5ec13d89ec fix 2 vendor overwrite #509🩹 2023-11-22 19:22:07 +11:00
Jokob-sk
a0a5410af9 fix 1 cycling thru devices #509🩹 2023-11-22 19:00:52 +11:00
Jokob-sk
b234e1c859 fix Unconfigurable root #507🩹 2023-11-22 08:16:07 +11:00
Jokob-sk
cd761a058f Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-11-22 08:07:52 +11:00
Jokob-sk
bf137a9755 fix UNFIMP #508🩹 2023-11-22 08:07:31 +11:00
jokob-sk
81cfa72b72 Merge pull request #505 from lorki97/fix/hw-install
fix: Hardware installation - thanks so much @lorki97 🙏
2023-11-17 21:28:39 +11:00
Markus Lorenz
c15b5bba5c Merge branch 'main' into fix/hw-install 2023-11-16 10:17:53 +01:00
Markus Lorenz
c7913c389f Extend HW install docs 2023-11-16 10:14:18 +01:00
Jokob-sk
fc8d17788a docs 📚 2023-11-16 07:43:10 +11:00
Jokob-sk
ff72b45f7c docs 📚 2023-11-16 07:41:59 +11:00
Markus Lorenz
692cf9305d More refactoring 2023-11-15 15:18:48 +01:00
Markus Lorenz
790e98d8a7 Remove empty buildtimestamp.txt 2023-11-15 14:40:26 +01:00
Markus Lorenz
0bd985282f Refactor shell scripts 2023-11-15 14:35:34 +01:00
Markus Lorenz
1e75eeab4c Create buildtimestamp.txt if not exists, fix shellcheck warnings 2023-11-15 14:09:52 +01:00
Markus Lorenz
a0d34876cc Fix web root 2023-11-15 12:38:07 +01:00
Markus Lorenz
c14fa5606d Disable default NGINX site 2023-11-15 12:25:17 +01:00
Markus Lorenz
aab910f68a Change default port to 20211 as in docker container 2023-11-15 11:56:57 +01:00
Markus Lorenz
b9a7516eb8 Change NGINX config file name and install directory 2023-11-15 11:44:51 +01:00
Markus Lorenz
5cf453d4fb Change web files install directory 2023-11-15 11:08:06 +01:00
jokob-sk
ff40a5acc0 Merge pull request #502 from jasonehines/main
fixed typos by @jasonehines 🙏
2023-11-11 23:42:49 +11:00
Jokob-sk
7e2559c229 Cleanup & docs 📚 2023-11-11 12:27:21 +11:00
Jason Hines
64d6f8be92 fixed typos 2023-11-10 17:29:57 -05:00
Jokob-sk
c91e428e77 Cleanup and MAINT plugin v0.1 🔌 2023-11-11 08:48:10 +11:00
jokob-sk
ee35f35794 Merge pull request #498 from jhonderson/bug-db-cleanup-deleting-new-devices
Fix DB Clean plugin new devices deletion bug - thanks @jhonderson 🙏
2023-11-11 07:32:48 +11:00
Jhon Cardenas
7492a07244 Fix DB Clean plugin device deletion bug 2023-11-09 12:18:43 -08:00
jokob-sk
3f3143452e Merge pull request #497 from jasonehines/patch-1
Update config.json - thanks @jasonehines 🙏
2023-11-09 18:36:53 +11:00
Jason Hines
e2e5a10e7e Update config.json
Changed sql query to sort by dev_MAC. Should resolve https://github.com/jokob-sk/Pi.Alert/issues/496
2023-11-08 17:30:46 -05:00
Jokob-sk
85335bcdbb Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-11-09 07:05:07 +11:00
Jokob-sk
93420b1f86 Settinsg work ⚒ 2023-11-09 07:04:30 +11:00
jokob-sk
b7d60ea818 Merge pull request #490 from silverbios/set-listener-address
Add option to set IP Address for web interface in Docker env - thanks to @silverbios 🙏
2023-11-09 07:03:19 +11:00
silverbios
0551cd1eea Updated README file for Docker 2023-11-08 00:07:34 +03:30
Jokob-sk
b86f1d75b5 Name matching fixes 🩹 2023-11-05 10:17:35 +11:00
Jokob-sk
89fb5c9b3b Online history work 👷 2023-11-05 09:40:04 +11:00
Jokob-sk
f5d8a9fc8c Setting icons work 👷‍♀️ 2023-11-04 12:46:38 +11:00
Jokob-sk
973cd60893 Name matching fixes 🩹 2023-11-04 12:06:12 +11:00
silverbios
da0130da4b Add ip enviroment for docker version 2023-11-01 13:25:56 +03:30
Jokob-sk
c8e494596e Install rewrite v2.1 2023-10-29 22:33:15 +11:00
Jokob-sk
baec65fde7 Install rewrite v2 2023-10-29 22:10:53 +11:00
Jokob-sk
9f5884c4e7 MQTT fix 🩹 2023-10-28 08:27:29 +11:00
Jokob-sk
4767dec6b8 Network fixes #475 2023-10-28 07:49:22 +11:00
Jokob-sk
536ef9ec46 Notification Report page rewrite v0.2 + cleanup📩 2023-10-26 20:30:51 +11:00
Jokob-sk
fd162ff98a Notification Report page rewrite v0.1📩 2023-10-25 22:35:07 +11:00
Jokob-sk
0ed24dac0a Frontend user events rewrite v0.1 2023-10-25 08:11:57 +11:00
Jokob-sk
e434a686c6 Settings overview dashboard + #462 work 2023-10-24 20:38:44 +11:00
Jokob-sk
138a899e34 Settings overview dashboard 2023-10-22 22:15:22 +11:00
Jokob-sk
ae7533cec0 Feature request - configurable arp-scan args #486 🎁 2023-10-22 09:41:38 +11:00
Jokob-sk
55e398dd10 WEBHOOK conversion + cleanup work🎣 2023-10-22 09:29:25 +11:00
Jokob-sk
fdd199935a PUSHSAFER + cleanup work⤵ 2023-10-19 21:59:06 +11:00
Jokob-sk
346a22f2f6 NTFY work⤵ 2023-10-19 21:26:03 +11:00
Jokob-sk
5d64433be0 PLUGINS, NTFY, handleEmpty work⤵ 2023-10-19 08:08:24 +11:00
Jokob-sk
1a3cf49c00 PERMISSIONS, MQTT, Maintenance work⤵ 2023-10-18 22:35:36 +11:00
Jokob-sk
9dd456bd2c MQTT, INSTALL scripts work⤵ 2023-10-17 07:38:49 +11:00
Jokob-sk
1b9d4223c5 MQTT, DHCPLSS work🔌 2023-10-16 20:28:30 +11:00
Jokob-sk
2a4ac2f2be MQTT, DHCPLSS work🔌 2023-10-15 22:39:21 +11:00
Jokob-sk
a2f3666134 install scripts rework 📦 2023-10-15 16:56:41 +11:00
Jokob-sk
1435ecac67 install scripts rework 📦 2023-10-15 16:46:04 +11:00
Jokob-sk
897112e466 MQTT rework v0.4, install scripts rework, Traefik docs 📦 2023-10-15 16:37:32 +11:00
Jokob-sk
31e1116483 MQTT rework v0.3 📩 2023-10-14 23:02:43 +11:00
Jokob-sk
7da9bf03a3 Settings UI improvements ⚙ 2023-10-14 18:57:16 +11:00
Jokob-sk
8ad63ba07d MQTT rework v0.1 + Settings UI improvements ⚙ 2023-10-14 15:35:09 +11:00
Jokob-sk
a3702fed94 Debug output for #474 2023-10-14 11:38:44 +11:00
Jokob-sk
3e3e8fa797 Device list rework v0.4 🔨 2023-10-13 22:16:31 +11:00
Jokob-sk
f3b64748aa #479 work 🔨 2023-10-13 21:30:08 +11:00
Jokob-sk
257e46df55 Docs + Device list rework v0.3 + #479 work 🔨 2023-10-13 20:53:04 +11:00
jokob-sk
3c856c010a Merge pull request #470 from lorki97/feat/german-translation
Add missing German translations - thanks to @lorki97 🙏
2023-10-13 20:48:54 +11:00
Jokob-sk
f87ea210c7 Docs + Device list rework v0.2 🔨 2023-10-12 21:45:05 +11:00
Jokob-sk
d433d8e956 Docs + Device list rework 🔨 2023-10-11 21:02:07 +11:00
Jokob-sk
879d7b674b Notification rework - SMTP v0.3 - working 2023-10-10 19:15:52 +11:00
Markus Lorenz
21d47f5d0d Fix spacing 2023-10-10 09:53:04 +02:00
Markus Lorenz
e7d5c1e5fe Add german translation to dhcp_leases plugin 2023-10-10 09:52:36 +02:00
Markus Lorenz
5c08b06ace Format dhcp_leases config file 2023-10-09 16:44:55 +02:00
Markus Lorenz
bb10b865f9 Add german translation to ddns_update plugin 2023-10-09 16:13:30 +02:00
Markus Lorenz
557eb8d09e Format ddns_update config file 2023-10-09 15:56:19 +02:00
Markus Lorenz
a69ce7b85d Add german translation to internet_speedtest plugin 2023-10-09 13:46:33 +02:00
Markus Lorenz
b5649e3c7b Format internet_speedtest config file 2023-10-09 13:32:34 +02:00
Markus Lorenz
1ebae57f48 Add german translation to undiscoverables plugin 2023-10-09 13:20:42 +02:00
Markus Lorenz
6c619bf6f7 Format undiscoverables config file 2023-10-09 12:55:50 +02:00
Markus Lorenz
cfb4bbe907 Add german readme to internet_ip plugin, format config file 2023-10-09 11:38:25 +02:00
Markus Lorenz
c708718e78 Fix grammar 2023-10-09 11:36:06 +02:00
Markus Lorenz
1a02d34e85 Add german readme to arp_scan plugin, format config file 2023-10-09 11:32:55 +02:00
Markus Lorenz
dcf785b900 Add german translation to vendor_update plugin 2023-10-09 11:20:15 +02:00
Markus Lorenz
88bbae7c84 Add german translation to internet_ip plugin 2023-10-09 10:57:44 +02:00
Markus Lorenz
9485b5adfb Add german translation to arp_scan plugin 2023-10-09 10:22:18 +02:00
Markus Lorenz
e2d475100e Merge branch 'main' into feat/german-translation 2023-10-09 09:20:49 +02:00
Markus Lorenz
22d3169d07 Reorder keys 2023-10-09 09:17:23 +02:00
Markus Lorenz
ebe7b9e9e6 Add translations for settings general section 2023-10-09 08:47:26 +02:00
Jokob-sk
78c18aa100 Notification rework - SMTP v0.3 - WIP👷‍♂️ 2023-10-08 22:49:50 +11:00
Jokob-sk
bd9f68bb27 Notification rework - SMTP v0.3 - WIP👷‍♂️ 2023-10-08 22:19:54 +11:00
Jokob-sk
1e693abfc4 Notification rework - SMTP v0.2 - WIP👷‍♂️ 2023-10-08 22:00:24 +11:00
Jokob-sk
e4a64a11bd Notification rework - SMTP v0.1 - WIP👷‍♂️ 2023-10-08 16:54:13 +11:00
Jokob-sk
43c57f00d0 Notification rework + docs + devDetails 2023-10-08 16:28:15 +11:00
jokob-sk
122bb29e99 Merge pull request #476 from ScottRoach/show-device-icon
Show current device icon as it changes - this is nice - thanks @ScottRoach 🙏
2023-10-08 04:51:06 +00:00
jokob-sk
bc8f95d30c Merge pull request #475 from ScottRoach/network-cleanup
Network cleanup - thanks @ScottRoach 🙏
2023-10-08 03:53:59 +00:00
Jokob-sk
be4e0acdfc Notification rework - Apprise v1 - working 2023-10-08 14:52:22 +11:00
Jokob-sk
79c47015f4 Notification rework v0.5 2023-10-08 11:15:10 +11:00
Jokob-sk
d4b590a9fc Notification rework v0.4 2023-10-07 18:04:33 +11:00
Scott Roach
e018fe2995 Related CSS for network icon/text alignment 2023-10-06 23:21:34 -07:00
Scott Roach
4aad8c12f8 Space out network icons, fix invalid markup, and overall slight cleanup 2023-10-06 23:20:22 -07:00
Scott Roach
93c45d7157 Show current device icon as it changes 2023-10-06 23:18:45 -07:00
Jokob-sk
695f1593c6 Notification rework v0.3 2023-10-07 13:00:28 +11:00
Jokob-sk
eb7b7b57ab Notification rework v0.2 2023-10-06 22:53:15 +11:00
Markus Lorenz
e8e8260856 WIP: Add translations for settings general section 2023-10-06 13:22:02 +02:00
Markus Lorenz
50b576134a Added missing translation keys, translated network, maintenance tabs 2023-10-06 10:20:11 +02:00
Jokob-sk
2476a36661 Notification rework v0.1 2023-10-06 08:16:45 +11:00
Jokob-sk
2aa984b147 Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-10-06 08:10:31 +11:00
Jokob-sk
16de261477 Notification rework v0.1 2023-10-06 08:10:18 +11:00
jokob-sk
9dfc574bde Merge pull request #468 from ScottRoach/main
Include device vendor in event notifications by @ScottRoach 🙏
2023-10-05 05:17:31 +00:00
Scott Roach
9072c37589 Merge branch 'jokob-sk:main' into main 2023-10-04 17:54:07 -07:00
Scott Roach
095a71bc8f Include vendor in event notifications 2023-10-04 17:53:46 -07:00
Jokob-sk
2b057d339c Network bug - selectable Internet parent #467 2023-10-05 07:15:00 +11:00
Jokob-sk
1e0552cc13 Network bug #465 2023-10-04 21:45:35 +11:00
Jokob-sk
eea0bf66db Merge branch 'main' of https://github.com/jokob-sk/Pi.Alert 2023-10-04 21:20:27 +11:00
jokob-sk
0741b396ef Merge pull request #466 from lorki97/main
Fix device types not loading in device details page
2023-10-04 10:04:09 +00:00
Markus Lorenz
865f3eabd8 Fix call to unused and removed getNetworkTypes 2023-10-04 11:43:50 +02:00
Jokob-sk
f5ba9b524d Docs 2023-10-03 20:43:22 +11:00
Jokob-sk
654253c953 Docs 2023-10-03 20:32:39 +11:00
Jokob-sk
1711cbfe2d Plugin:Speedtest v0.1 2023-10-03 20:18:34 +11:00
Jokob-sk
71c20e159d Docs 2023-10-02 16:54:54 +11:00
Jokob-sk
205c143782 Docs 2023-10-02 16:51:51 +11:00
Jokob-sk
ed7c919201 Docs + on HW install v0.2 2023-10-02 16:25:15 +11:00
Jokob-sk
842014160b Docs + on HW install v0.1 2023-10-02 14:29:45 +11:00
Jokob-sk
2a0f464c63 docs + #457 work 2023-10-01 16:09:17 +11:00
Jokob-sk
c412f025ca docs 2023-10-01 09:20:21 +11:00
jokob-sk
299371b38c Merge pull request #459 from nimec01/webhook-signatures
Add webhook signatures with amazing docs - thanks to @nimec01 🙏
2023-09-30 12:47:23 +00:00
Nick
d57c38bb3d add WEBHOOK_SECRET name and description 2023-09-30 13:27:38 +02:00
Nick
59231739a2 fix spelling 2023-09-30 13:23:42 +02:00
Nick
7ba6941aed Merge remote-tracking branch 'origin/main' into main 2023-09-30 11:52:44 +02:00
Nick
95f9b348cd secure webhooks using signatures 2023-09-30 11:48:49 +02:00
Jokob-sk
ebeeb6c3a5 Better version handling #458 + docs 2023-09-30 09:53:15 +10:00
Jokob-sk
07367a2ca3 NETWORK_DEVICE_TYPES #452 2023-09-22 08:22:28 +10:00
Jokob-sk
3d848a70c7 ddns plugin 0.1 + internet_ip 0.4 2023-09-21 07:52:52 +10:00
Jokob-sk
c5d1cd919a internet_ip plugin 0.3 2023-09-20 22:20:41 +10:00
Jokob-sk
c08b70a38d internet_ip plugin 0.2 2023-09-20 21:53:22 +10:00
Jokob-sk
add9800f42 internet_ip plugin 2023-09-19 07:48:53 +10:00
Jokob-sk
1395dd9fb5 vendor_update plugin 2023-09-18 14:59:49 +10:00
Jokob-sk
12b89f7e24 vendor_update plugin 2023-09-18 14:29:44 +10:00
Jokob-sk
0ab5ca32a6 vendor_update plugin 2023-09-18 08:36:11 +10:00
Jokob-sk
19f42e60ba vendor_update plugin 2023-09-18 08:31:41 +10:00
Jokob-sk
a5b952f18c db_cleanup plugin 2023-09-17 21:46:05 +10:00
Jokob-sk
80de181827 Docs, Readme, Donations 2023-09-17 11:31:41 +10:00
Jokob-sk
44856f9c04 Loading spinner + app_state.json, settings work 2023-09-16 21:14:34 +10:00
Jokob-sk
40c6c65ee5 Loading spinner 2023-09-16 09:57:38 +10:00
Jokob-sk
6cb4439d59 pialert_app_state.json 2023-09-16 08:34:14 +10:00
Jokob-sk
03970f985e better statuses, unifi setting type change 2023-09-15 20:55:39 +10:00
Jokob-sk
31a94b779f DHCPSRVS bugfix - past results 2023-09-15 20:29:50 +10:00
Jokob-sk
f9a400c34c Optimize init, code clenup 2023-09-15 20:25:48 +10:00
jokob-sk
5cea39c9c0 Merge pull request #442 from tuhriel/unifi_full_import
Added functionality to perform a full import
2023-09-15 10:09:41 +00:00
Jokob-sk
99cd07658e SMTP docs 2023-09-14 22:23:59 +10:00
jokob-sk
11fcaa2538 Update pialert.conf
pialert.conf cleanup
2023-09-14 07:28:04 +10:00
pi@skippy
8af961ff3f unifi full import debugged 2023-09-13 00:09:06 +02:00
pi@skippy
10ee4a17a5 resolved merge conflict in unifi import config file 2023-09-13 00:08:25 +02:00
stefan@pc
bbe0ba0389 added full import functionality 2023-09-12 23:30:41 +02:00
226 changed files with 74342 additions and 7681 deletions

View File

@@ -5,9 +5,16 @@
.gitignore
docker-compose.yml
Dockerfile
Dockerfile.debian
dockerfiles/LICENSE
dockerfiles/README.md
dockerfiles/README_ES.md
docs
LICENSE.txt
README.md
CONTRIBUTING
FUNDING.yml
config/.gitignore
db/.gitignore
pialert/README.md
pialert/README_ES.md

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

38
.github/ISSUE_TEMPLATE/feature_request.yml vendored Executable file
View File

@@ -0,0 +1,38 @@
name: Feature Request
description: 'Suggest an idea for PiAlert'
labels: ['Feature request']
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the feature you are requesting.
options:
- label: I have searched the existing open and closed issues
required: true
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe
description: A clear and concise description of what the problem is.
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: true
- type: textarea
attributes:
label: Anything else?
description: |
Links? References? Mockups? Anything that will give us more context about the feature you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: true

View File

@@ -1,46 +0,0 @@
---
name: I have an issue
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---
## Describe the issue
> When submitting an issue ❗[enable debug](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_TIPS.md)❗ and [have a look at the docs](https://github.com/jokob-sk/Pi.Alert/tree/main/docs)
[describe your issue]
## Paste your `pialert.conf` (remove personal info)
```
paste_here
```
## Paste your `docker-compose.yml` and `.env` (remove personal info)
`docker-compose.yml`
```
paste_here
```
`.env`
```
paste_here
```
## Screenshots
[If applicable, add screenshots to help explain your problem.]
## Paste last few lines from `pialert.log`
> You can use `tail -100 /home/pi/pialert/front/log/pialert.log`
```bash
# paste code below

76
.github/ISSUE_TEMPLATE/i-have-an-issue.yml vendored Executable file
View File

@@ -0,0 +1,76 @@
name: Bug Report
description: 'When submitting an issue enable debug and have a look at the docs.'
labels: ['bug 🐛']
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the bug you encountered.
options:
- label: I have searched the existing open and closed issues and I checked the docs https://github.com/jokob-sk/Pi.Alert/tree/main/docs
required: true
- type: textarea
attributes:
label: Current Behavior
description: A concise description of what you're experiencing.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. With these settings...
2. With this config...
3. Run '...'
4. See error...
validations:
required: false
- type: textarea
attributes:
label: pialert.conf
description: |
Paste your `pialert.conf` (remove personal info)
render: python
validations:
required: false
- type: textarea
attributes:
label: docker-compose.yml
description: |
Paste your `docker-compose.yml`
render: python
validations:
required: false
- type: dropdown
attributes:
label: What branch are you running?
options:
- Production
- Dev
validations:
required: true
- type: textarea
attributes:
label: pialert.log
description: |
Logs with debug enabled (https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_TIPS.md) ⚠
***Generally speaking, all bug reports should have logs provided.***
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
You can use `tail -100 /home/pi/pialert/front/log/pialert.log` in teh container if you have troubles getting to the log files.
validations:
required: false
- type: checkboxes
attributes:
label: Debug enabled
description: I confirm I enabled `debug`
options:
- label: I have read and followed the steps in the wiki link above and provided the required debug logs and the log section covers the time when the issue occurs.
required: true

View File

@@ -1,4 +1,4 @@
name: ci-package-cleaner
name: 🤖Automation - ci-package-cleaner
on:

View File

@@ -23,13 +23,13 @@ jobs:
github.repository == 'jokob-sk/Pi.Alert'
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Set up dynamic build ARGs
id: getargs
@@ -61,7 +61,7 @@ jobs:
type=sha
- name: Log in to Github Container registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: jokob-sk
@@ -69,18 +69,23 @@ jobs:
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# # Disable this after use
# - name: Prune Docker Builder
# run: docker builder prune --force
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=ghcr.io/jokob-sk/pi.alert:buildcache
cache-to: type=registry,ref=ghcr.io/jokob-sk/pi.alert:buildcache,mode=max
# # ⚠ disable cache if build is failing to download debian packages
# cache-from: type=registry,ref=ghcr.io/jokob-sk/pi.alert:buildcache
# cache-to: type=registry,ref=ghcr.io/jokob-sk/pi.alert:buildcache,mode=max

View File

@@ -13,7 +13,7 @@ on:
release:
types: [published]
tags:
- '*.*.*'
- '*.[1-9]+[0-9]?.[1-9]+*'
jobs:
docker:
runs-on: ubuntu-latest
@@ -26,10 +26,10 @@ jobs:
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Set up dynamic build ARGs
id: getargs
@@ -59,7 +59,7 @@ jobs:
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
- name: Log in to Github Container registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: jokob-sk
@@ -67,7 +67,7 @@ jobs:
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -76,9 +76,10 @@ jobs:
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=ghcr.io/jokob-sk/pi.alert:buildcache
cache-to: type=registry,ref=ghcr.io/jokob-sk/pi.alert:buildcache,mode=max
# # ⚠ disable cache if build is failing to download debian packages
# cache-from: type=registry,ref=ghcr.io/jokob-sk/pi.alert:buildcache
# cache-to: type=registry,ref=ghcr.io/jokob-sk/pi.alert:buildcache,mode=max

29
.github/workflows/update_sponsors_table.yml vendored Executable file
View File

@@ -0,0 +1,29 @@
name: 🤖Automation - Update Sponsors Table
on:
schedule:
- cron: '50 11 * * *' # Set your preferred schedule (UTC)
jobs:
update-table:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r update_sponsors_requirements.txt # If you have any Python dependencies
- name: Update Sponsors Table
run: |
python update_sponsors.py
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.vscode
.DS_Store
config/*
config/pialert.conf
db/*
db/pialert.db
@@ -15,3 +16,5 @@ __pycache__/
**/last_result.log
**/script.log
**/pialert.conf_bak
**/pialert.db_bak

View File

@@ -6,7 +6,7 @@ The issue tracker is the preferred channel for bug reports, features requests an
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 [🛑 Common issues](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_TIPS.md#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.
## Pull-requests (PRs)

View File

@@ -1,54 +1,53 @@
FROM debian:bullseye-slim
FROM alpine:3.19 as builder
# default UID and GID
ENV USER=pi USER_ID=1000 USER_GID=1000 PORT=20211
#TZ=Europe/London
ARG INSTALL_DIR=/home/pi
ENV PYTHONUNBUFFERED 1
# 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 apk add --no-cache bash python3 \
&& python -m venv /opt/venv
RUN apt-get update \
&& 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 systemctl usbutils traceroute -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 \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /var/www/html \
&& ln -s /home/pi/pialert/front /var/www/html
# Enable venv
ENV PATH="/opt/venv/bin:$PATH"
# create pi user and group
# add root and www-data to pi group so they can r/w files and db
RUN groupadd --gid "${USER_GID}" "${USER}" && \
useradd \
--uid ${USER_ID} \
--gid ${USER_GID} \
--create-home \
--shell /bin/bash \
${USER} && \
usermod -a -G ${USER_GID} root && \
usermod -a -G ${USER_GID} www-data
COPY . ${INSTALL_DIR}/pialert/
COPY --chmod=775 --chown=${USER_ID}:${USER_GID} . /home/pi/pialert/
RUN pip install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet \
&& bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \
&& bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'pialert-cli' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
# Pi.Alert
RUN rm /etc/nginx/sites-available/default \
&& ln -s /home/pi/pialert/install/default /etc/nginx/sites-available/default \
&& sed -ie 's/listen 80/listen '${PORT}'/g' /etc/nginx/sites-available/default \
# run the hardware vendors update
&& /home/pi/pialert/back/update_vendors.sh \
# Create a backup of the pialert.conf to be used if the user didn't supply a configuration file
&& cp /home/pi/pialert/config/pialert.conf /home/pi/pialert/back/pialert.conf_bak \
# Create a backup of the pialert.db to be used if the user didn't supply a database
&& cp /home/pi/pialert/db/pialert.db /home/pi/pialert/back/pialert.db_bak \
# Create a buildtimestamp.txt to later check if a new version was released
&& date +%s > /home/pi/pialert/front/buildtimestamp.txt
# second stage
FROM alpine:3.19 as runner
ENTRYPOINT ["tini", "--"]
ARG INSTALL_DIR=/home/pi
CMD ["/home/pi/pialert/dockerfiles/start.sh"]
COPY --from=builder /opt/venv /opt/venv
# Enable venv
ENV PATH="/opt/venv/bin:$PATH"
# default port and listen address
ENV PORT=20211 LISTEN_ADDR=0.0.0.0
# needed for s6-overlay
ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
## command to build docker: DOCKER_BUILDKIT=1 docker build . --iidfile dockerID
# ❗ IMPORTANT - if you modify this file modify the /install/install_dependecies.sh file as well ❗
RUN apk update --no-cache \
&& apk add --no-cache bash zip lsblk gettext-envsubst sudo mtr s6-overlay \
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap traceroute net-tools net-snmp-tools bind-tools awake ca-certificates \
&& apk add --no-cache sqlite php82 php82-fpm php82-cgi php82-curl php82-sqlite3 php82-session \
&& apk add --no-cache python3 nginx \
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
&& bash -c "install -d -m 750 -o nginx -g www-data ${INSTALL_DIR} ${INSTALL_DIR}/pialert" \
&& rm -f /etc/nginx/http.d/default.conf
COPY --from=builder --chown=nginx:www-data ${INSTALL_DIR}/pialert/ ${INSTALL_DIR}/pialert/
RUN ${INSTALL_DIR}/pialert/dockerfiles/pre-setup.sh
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=2 \
CMD curl -sf -o /dev/null ${LISTEN_ADDR}:${PORT}/api/app_state.json
ENTRYPOINT ["/init"]

50
Dockerfile.debian Executable file
View File

@@ -0,0 +1,50 @@
FROM debian:bookworm-slim
# default UID and GID
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
RUN apt-get install sudo -y
# create pi user and group
# add root and www-data to pi group so they can r/w files and db
RUN groupadd --gid "${USER_GID}" "${USER}" && \
useradd \
--uid ${USER_ID} \
--gid ${USER_GID} \
--create-home \
--shell /bin/bash \
${USER} && \
usermod -a -G ${USER_GID} root && \
usermod -a -G ${USER_GID} www-data
COPY --chmod=775 --chown=${USER_ID}:${USER_GID} . /home/pi/pialert/
# ❗ IMPORTANT - if you modify this file modify the /install/install_dependecies.debian.sh file as well ❗
RUN apt-get install -y \
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 systemctl usbutils traceroute
# Alternate dependencies
RUN apt-get install nginx nginx-core mtr php-fpm php8.2-fpm php-cli php8.2 php8.2-sqlite3 -y
RUN phpenmod -v 8.2 sqlite3
# Setup virtual python environment and use pip3 to install packages
RUN apt-get install -y python3-venv
RUN python3 -m venv myenv
RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet"
# Create a buildtimestamp.txt to later check if a new version was released
RUN date +%s > /home/pi/pialert/front/buildtimestamp.txt
CMD ["/home/pi/pialert/install/start.debian.sh"]

192
README.md
View File

@@ -1,99 +1,139 @@
# Pi.Alert
<!--- --------------------------------------------------------------------- --->
# 💻🔍 Network security scanner & notification framework
💻🔍 WIFI / LAN intruder detector.
Get visibility of what's going on on your WIFI/LAN network. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugins](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins#readme) with auto-generated UI and in-build notification system.
Scans for devices connected to your WIFI / LAN and alerts you if new and unknown devices are found.
![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)
[![Docker Size](https://img.shields.io/docker/image-size/jokobsk/pi.alert?label=Size&logo=Docker&color=0aa8d2&logoColor=fff)](https://hub.docker.com/r/jokobsk/pi.alert)
[![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)
[![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/Pi.Alert?color=0aa8d2&logoColor=fff&logo=GitHub)](https://github.com/jokob-sk/Pi.Alert/releases)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/jokob-sk?style=social)](https://github.com/sponsors/jokob-sk)
🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📑 [Docker guide](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md) | 🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases) | 📚 [All Docs](https://github.com/jokob-sk/Pi.Alert/tree/main/docs)
| 🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📑 [Docker guide](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md) |🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases) | 📚 [All Docs](https://github.com/jokob-sk/Pi.Alert/tree/main/docs) |
|----------------------|----------------------| ----------------------| ----------------------|
## 🔍 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**. Discovery & scan methods include:
- **arp-scan**. The arp-scan system utility is used to search for devices on the network using arp frames.
- **Pi-hole - DB import**. The PiHole database is used as a source for events for devices
- **Pi-hole - DHCP leases**. Import of devices from the PiHole dhcp.leases file
- **Generic DHCP leases**. Import of devices from the generic dhcp.leases file
- **UNIFI import**. Import of devices from the UNIFI controller
- **SNMP-enabled router import**. Import of devices from an SNMP-enabled router
## 🧩 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)
- [Home Assistant](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/HOME_ASSISTANT.md)
- [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 scripts monitoring and framework for extending the app
# 📥 Installation
<!--- --------------------------------------------------------------------- --->
⚠ Only tested as a [docker container - follow the guide 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 check instructions for [pucherot's original code](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] |
| ![Main screen][main] | ![Screen 1][screen1] | ![Screen 5][screen5] |
|----------------------|----------------------| ----------------------|
<details>
<summary>📷 Click for more screenshots</summary>
| ![Screen 3][screen3] | ![Screen 4][screen4] | ![Screen 6][screen6] |
|----------------------|----------------------|----------------------|
| ![Screen 8][screen8] | ![Report 2][report2] | ![Screen 9][screen9] |
</details>
### 🔗 Other Alternatives
<details>
<summary>❓ Why use PiAlert?</summary>
- [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)
<hr>
### 📚 Documentation
Most of us don't know what's going on on our home network, but we want our family and data to be safe. _Command-line tools_ are great, but the output can be _hard to understand_ and action if you are not a network specialist.
- 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)
PiAlert gives you peace of mind. _Visualize and immediately report 📬_ what is going on in your network - this is the first step to enhance your _network security 🔐_.
PiAlert combines several network and other scanning tools 🔍 with notifications 📧 into one user-friendly package 📦.
Set up a _kill switch ☠_ for your network via a smart plug with the available [Home Assistant](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/HOME_ASSISTANT.md) integration. Implement custom automations with the [CSV device Exports 📤](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins/csv_backup), [Webhooks](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md), or [API endpoints](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md) features.
Extend the app if you want to create your own scanner [Plugin](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins#readme) and handle the results and notifications in PiAlert.
Looking forward to your contributions if you decide to share your work with the community ❤.
</details>
## Scan Methods, Notifications, Integration, Extension system
| Features | Details |
|-------------|-------------|
| 🔍 | The app scans your network for, **New devices**, **New connections** (re-connections), **Disconnections**, **"Always Connected" devices down**, Devices **IP changes** and **Internet IP address changes**. Discovery & scan methods include: **arp-scan**. **Pi-hole - DB import**, **Pi-hole - DHCP leases import**, **Generic DHCP leases import**. **UNIFI controller import**, **SNMP-enabled router import**. Check the [Plugins](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins#readme) docs for more info on individual scans. |
|📧 | Send notifications to more than 80+ services, including Telegram via [Apprise](https://hub.docker.com/r/caronc/apprise), or use [Pushsafer](https://www.pushsafer.com/), [Pushover](https://www.pushover.net/), or [NTFY](https://ntfy.sh/). |
|🧩 | Feed your data and device changes into [Home Assistant](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/HOME_ASSISTANT.md), read [API endpoints](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md), or use [Webhooks](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md) to setup custom automation flows. |
| | Build your own scanners with the [Plugin system](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins#readme) |
## Installation & Documentation
<!--- --------------------------------------------------------------------- --->
| Docs | Link |
|-------------|-------------|
| 📥🐳 | [Docker instructions](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md)
| 📥💻 | [HW install (experimental 🧪)](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/HW_INSTALL.md) |
| 📚 | [All Documentation](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/README.md) (App Usage and Configuration) |
> Other Alternatives
>
> - Check out [leiweibau's on HW installed fork](https://github.com/leiweibau/Pi.Alert/) (maintained)
> - Check instructions for [pucherot's original code](https://github.com/pucherot/Pi.Alert/) (unmaintained)
> - [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)
## ❤ Support me for...
- I don't get burned out and the app survives longer🔥🤯
- Regular updates to keep your data and family safe 🔄
- Better and more functionality
- Quicker and better support with issues 🆘
| [![GitHub](https://i.imgur.com/emsRCPh.png)](https://github.com/sponsors/jokob-sk) | [![Buy Me A Coffee](https://i.imgur.com/pIM6YXL.png)](https://www.buymeacoffee.com/jokobsk) | [![Patreon](https://i.imgur.com/MuYsrq1.png)](https://www.patreon.com/user?u=84385063) |
| --- | --- | --- |
<details>
<summary>Click for more ways to donate</summary>
<hr>
- Bitcoin: `1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM`
- Ethereum: `0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7`
📧 Email me at [jokob@duck.com](mailto:jokob@duck.com?subject=PiAlert) if you want to get in touch or if I should add other sponsorship platforms.
</details>
### ⭐ Sponsors
[![GitHub Sponsors](https://img.shields.io/github/sponsors/jokob-sk?style=social)](https://github.com/sponsors/jokob-sk)
Thank you to all the wonderful people who are sponsoring this project (=preventing my burnout🔥🤯):
<!-- SPONSORS-LIST DO NOT MODIFY BELOW -->
| All Sponsors |
|---|
<!-- SPONSORS-LIST DO NOT MODIFY ABOVE -->
### 🙏Contributors
This project would be nothing without the amazing work of the community, with special thanks to:
> [pucherot/Pi.Alert](https://github.com/pucherot/Pi.Alert) (the original creator of PiAlert), [leiweibau](https://github.com/leiweibau/Pi.Alert): Dark mode (and much more), [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 translations), [Data-Monkey](https://github.com/Data-Monkey), (Split-up of the python.py file and more), [cvc90](https://github.com/cvc90) (Spanish translation and various UI work) to name a few...
Here is everyone that helped and contributed to this project:
<a href="https://github.com/jokob-sk/pi.alert/graphs/contributors">
<img src="https://contri-graphy.yourselfhosted.com/graph?repo=jokob-sk/pi.alert&format=svg" />
</a>
## Everything else
<!--- --------------------------------------------------------------------- --->
### 🌍 Translations
Proudly using [Weblate](https://hosted.weblate.org/projects/pialert/).
<a href="https://hosted.weblate.org/engage/pialert/">
<img src="https://hosted.weblate.org/widget/pialert/core/multi-auto.svg" alt="Translation status" />
</a>
Help out and suggest languages in the [online portal of Weblate](https://hosted.weblate.org/projects/pialert/core/).
### 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
This code is a collaborative body of work, with special thanks to:
- 🏆 [pucherot/Pi.Alert](https://github.com/pucherot/Pi.Alert) is the original creator od PiAlert
- [leiweibau](https://github.com/leiweibau/Pi.Alert): Dark mode (and much more)
- [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
- [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"

View File

@@ -15,13 +15,12 @@
#
# Scan multiple interfaces (eth1 and eth0):
# SCAN_SUBNETS = [ '192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0' ]
SCAN_SUBNETS=['192.168.1.0/24 --interface=eth1']
PRINT_LOG=False
TIMEZONE='Europe/Berlin'
PIALERT_WEB_PROTECTION=False
PIALERT_WEB_PASSWORD='8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'
INCLUDED_SECTIONS=['internet','new_devices','down_devices','events']
SCAN_CYCLE_MINUTES=5
DAYS_TO_KEEP_EVENTS=90
# Used for generating links in emails. Make sure not to add a trailing slash!
REPORT_DASHBOARD_URL='http://pi.alert'
@@ -93,25 +92,6 @@ DDNS_PASSWORD='A0000000B0000000C0000000D0000000'
DDNS_UPDATE_URL='https://api.dynu.com/nic/update?'
# PiHole
#---------------------------
# if enabled you need to map '/etc/pihole/pihole-FTL.db' in docker-compose.yml
PIHOLE_ACTIVE=False
# if enabled you need to map '/etc/pihole/dhcp.leases' in docker-compose.yml
DHCP_ACTIVE=False
# Pholus
#---------------------------
PHOLUS_ACTIVE=False
PHOLUS_TIMEOUT=120
PHOLUS_FORCE=False
PHOLUS_DAYS_DATA=7
PHOLUS_RUN='once'
PHOLUS_RUN_TIMEOUT=600
PHOLUS_RUN_SCHD='0 4 * * *'
#-------------------IMPORTANT INFO-------------------#
# This file is ingested by a python script, so if #
# modified it needs to use python syntax #

View File

@@ -46,6 +46,7 @@
<th width='120px' style='color:#F0F0F0' bgcolor='#909090' >Event Type</th>
<th width='120px' style='color:#F0F0F0' bgcolor='#909090' >Device name</th>
<th width='120px' style='color:#F0F0F0' bgcolor='#909090' >Comments</th>
<th width='120px' style='color:#F0F0F0' bgcolor='#909090' >Device Vendor</th>
</tr>
<tr>
<td><a href="http://192.168.1.1:20211/deviceDetails.php?mac=00:00:00:ef:a5:6c">00:00:00:ef:a5:6c</a></td>
@@ -54,6 +55,7 @@
<td>New Device</td>
<td>(name not found)</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="http://192.168.1.1:20211/deviceDetails.php?mac=00:00:00:ef:a5:6c">00:00:00:ef:a5:6c</a></td>
@@ -62,6 +64,7 @@
<td>New Device</td>
<td>(name not found)</td>
<td></td>
<td></td>
</tr>
</table>
</td>
@@ -83,6 +86,7 @@
<th width='120px' style='color:#F0F0F0' bgcolor='#909090' >Event Type</th>
<th width='120px' style='color:#F0F0F0' bgcolor='#909090' >Device name</th>
<th width='120px' style='color:#F0F0F0' bgcolor='#909090' >Comments</th>
<th width='120px' style='color:#F0F0F0' bgcolor='#909090' >Device Vendor</th>
</tr>
<tr>
<td><a href="http://192.168.1.1:20211/deviceDetails.php?mac=00:00:00:ef:a5:6c">00:00:00:ef:a5:6c</a></td>

View File

@@ -38,7 +38,7 @@
<tr>
<td height=200 valign=top style="padding: 10px">
<INTERNET_TABLE>
<NEW_DEVICES_TABLE>
<DOWN_DEVICES_TABLE>
<EVENTS_TABLE>

View File

@@ -1,8 +1,7 @@
Report Date: <REPORT_DATE>
Server: <SERVER_NAME>
<SECTION_NEW_DEVICES>
<SECTION_DEVICES_DOWN>
<SECTION_EVENTS>
<SECTION_INTERNET>
<NEW_DEVICES_TABLE>
<DOWN_DEVICES_TABLE>
<EVENTS_TABLE>
<PLUGINS_TABLE>

View File

@@ -43,7 +43,6 @@
<tr>
<td height=200 valign=top style="padding: 10px">
<INTERNET_TABLE>
<NEW_DEVICES_TABLE>
<DOWN_DEVICES_TABLE>
<EVENTS_TABLE>

View File

@@ -1,4 +1,5 @@
#!/bin/sh
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
# Pi.Alert
# Open Source Network Guard / WIFI & LAN intrusion detector
@@ -14,22 +15,21 @@
# /usr/share/ieee-data
# /var/lib/ieee-data
# ----------------------------------------------------------------------
echo "---------------------------------------------------------"
echo "[INSTALL] Run update_vendors.sh"
echo "---------------------------------------------------------"
# ----------------------------------------------------------------------
echo Updating... /usr/share/ieee-data/
cd /usr/share/ieee-data/
cd /usr/share/ieee-data/ || { echo "could not enter /usr/share/ieee-data directory"; exit 1; }
sudo mkdir -p 2_backup
sudo cp *.txt 2_backup
sudo cp *.csv 2_backup
sudo cp -- *.txt 2_backup
sudo cp -- *.csv 2_backup
echo ""
echo Download Start
echo ""
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 \
sudo curl "$1" -LO https://standards-oui.ieee.org/oui28/mam.csv \
-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 \
@@ -42,15 +42,18 @@ echo Download Finished
# ----------------------------------------------------------------------
echo ""
echo Updating... /usr/share/arp-scan/
cd /usr/share/arp-scan
cd /usr/share/arp-scan || { echo "could not enter /usr/share/arp-scan directory"; exit 1; }
sudo mkdir -p 2_backup
sudo cp *.txt 2_backup
sudo cp -- *.txt 2_backup
# Update from /usb/lib/ieee-data
sudo get-iab -v
sudo get-oui -v
# make files readable
sudo chmod +r /usr/share/arp-scan/ieee-oui.txt
# Update from ieee website
# sudo get-iab -v -u http://standards-oui.ieee.org/iab/iab.txt
# sudo get-oui -v -u http://standards-oui.ieee.org/oui/oui.txt

View File

@@ -17,68 +17,103 @@
"title": "Pi.Alert Notifications",
"title_link": "",
"text": {
"internet": [],
"new_devices": [{
"new_devices_meta": {
"title": "New devices",
"columnNames": [
"MAC",
"Datetime",
"IP",
"Event Type",
"Device name",
"Comments"
]
},
"new_devices": [
{
"MAC": "74:ac:74:ac:74:ac",
"Datetime": "2023-01-30 22:15:09",
"IP": "192.168.1.1",
"Event Type": "New Device",
"Device name": "(name not found)",
"Comments": null
}],
"Comments": null,
"Device Vendor": null
}
],
"down_devices_meta": {
"title": "Down devices",
"columnNames": [
"MAC",
"Datetime",
"IP",
"Event Type",
"Device name",
"Comments"
]
},
"down_devices": [],
"events": [{
"events_meta": {
"title": "Events",
"columnNames": [
"MAC",
"Datetime",
"IP",
"Event Type",
"Device name",
"Comments"
]
},
"events": [
{
"MAC": "74:ac:74:ac:74:ac",
"Datetime": "2023-01-30 22:15:09",
"IP": "192.168.1.92",
"Event Type": "Disconnected",
"Device name": "(name not found)",
"Comments": null
}, {
"Comments": null,
"Device Vendor": null
},
{
"MAC": "74:ac:74:ac:74:ac",
"Datetime": "2023-01-30 22:15:09",
"IP": "192.168.1.150",
"Event Type": "Disconnected",
"Device name": "(name not found)",
"Comments": null
}],
"ports": [{
"new": {
"Name": "New device",
"MAC": "74:ac:74:ac:74:ac",
"Port": "22/tcp",
"State": "open",
"Service": "ssh",
"Extra": ""
"Comments": null,
"Device Vendor": null
}
}, {
"new": {
"Name": "New device",
"MAC": "74:ac:74:ac:74:ac",
"Port": "53/tcp",
"State": "open",
"Service": "domain",
"Extra": ""
],
"plugins_meta": {
"title": "Plugins",
"columnNames": [
"Plugin",
"Object_PrimaryID",
"Object_SecondaryID",
"DateTimeChanged",
"Watched_Value1",
"Watched_Value2",
"Watched_Value3",
"Watched_Value4",
"Status"
]
},
"plugins": [
{
"Index": 138,
"Plugin": "INTRSPD",
"Object_PrimaryID": "Speedtest",
"Object_SecondaryID": "2023-10-08 02:01:16+02:00",
"DateTimeCreated": "2023-10-08 02:01:16",
"DateTimeChanged": "2023-10-08 02:32:15",
"Watched_Value1": "-1",
"Watched_Value2": "-1",
"Watched_Value3": "null",
"Watched_Value4": "null",
"Status": "missing-in-last-scan",
"Extra": "null",
"UserData": "null",
"ForeignKey": "null"
}
}, {
"new": {
"Name": "New device",
"MAC": "74:ac:74:ac:74:ac",
"Port": "80/tcp",
"State": "open",
"Service": "http",
"Extra": ""
}
}, {
"new": {
"Name": "New device",
"MAC": "74:ac:74:ac:74:ac",
"Port": "443/tcp",
"State": "open",
"Service": "https",
"Extra": ""
}
}]
]
}
}
]

2
config/.gitignore vendored Executable file
View File

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

2
db/.gitignore vendored Executable file
View File

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

View File

@@ -11,28 +11,31 @@ services:
network_mode: host
# restart: unless-stopped
volumes:
- ${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
# - ${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
# - ${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
- ${APP_DATA_LOCATION}/pialert/dhcp_samples/dhcp1.leases:/mnt/dhcp1.leases
- ${APP_DATA_LOCATION}/pialert/dhcp_samples/dhcp2.leases:/mnt/dhcp2.leases
- ${APP_DATA_LOCATION}/pialert/dhcp_samples/pihole_dhcp_full.leases:/etc/pihole/dhcp.leases
- ${APP_DATA_LOCATION}/pialert/dhcp_samples/pihole_dhcp_2.leases:/etc/pihole/dhcp2.leases
- ${APP_DATA_LOCATION}/pihole/etc-pihole/pihole-FTL.db:/etc/pihole/pihole-FTL.db
- ${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}/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
- ${APP_DATA_LOCATION}/pialert/php.ini:/etc/php/8.2/fpm/php.ini
- ${DEV_LOCATION}/install:/home/pi/pialert/install
- ${DEV_LOCATION}/front/css:/home/pi/pialert/front/css
- ${DEV_LOCATION}/back/update_vendors.sh:/home/pi/pialert/back/update_vendors.sh
- ${DEV_LOCATION}/front/lib/AdminLTE:/home/pi/pialert/front/lib/AdminLTE
- ${DEV_LOCATION}/front/js:/home/pi/pialert/front/js
- ${DEV_LOCATION}/install/start.debian.sh:/home/pi/pialert/install/start.debian.sh
- ${DEV_LOCATION}/install/user-mapping.debian.sh:/home/pi/pialert/install/user-mapping.debian.sh
- ${DEV_LOCATION}/install/install.debian.sh:/home/pi/pialert/install/install.debian.sh
- ${DEV_LOCATION}/install/install_dependencies.debian.sh:/home/pi/pialert/install/install_dependencies.debian.sh
- ${DEV_LOCATION}/front/api:/home/pi/pialert/front/api
- ${DEV_LOCATION}/front/php:/home/pi/pialert/front/php
- ${DEV_LOCATION}/front/deviceDetails.php:/home/pi/pialert/front/deviceDetails.php
@@ -49,7 +52,9 @@ services:
- ${DEV_LOCATION}/front/settings.php:/home/pi/pialert/front/settings.php
- ${DEV_LOCATION}/front/systeminfo.php:/home/pi/pialert/front/systeminfo.php
- ${DEV_LOCATION}/front/report.php:/home/pi/pialert/front/report.php
- ${DEV_LOCATION}/front/flows.php:/home/pi/pialert/front/flows.php
- ${DEV_LOCATION}/front/workflows.php:/home/pi/pialert/front/workflows.php
- ${DEV_LOCATION}/front/appEventsCore.php:/home/pi/pialert/front/appEventsCore.php
- ${DEV_LOCATION}/front/multiEditCore.php:/home/pi/pialert/front/multiEditCore.php
- ${DEV_LOCATION}/front/donations.php:/home/pi/pialert/front/donations.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
@@ -59,3 +64,5 @@ services:
- PORT=${PORT}
- HOST_USER_ID=${HOST_USER_ID}
- HOST_USER_GID=${HOST_USER_GID}
# ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders
- ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL}

View File

@@ -1,12 +1,13 @@
[![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)
[![Docker Size](https://img.shields.io/docker/image-size/jokobsk/pi.alert?label=Size&logo=Docker&color=0aa8d2&logoColor=fff)](https://hub.docker.com/r/jokobsk/pi.alert)
[![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)
![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/Pi.Alert?color=0aa8d2&logoColor=fff&logo=GitHub)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/jokob-sk?style=social)](https://github.com/sponsors/jokob-sk)
# 🐳 A docker image for Pi.Alert
# PiAlert 💻🔍 Network security scanner & notification framework
🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📑 [Docker instructions](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md) | 🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases) | 📚 [All Docs](https://github.com/jokob-sk/Pi.Alert/tree/main/docs)
| 🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📑 [Docker guide](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md) |🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases) | 📚 [All Docs](https://github.com/jokob-sk/Pi.Alert/tree/main/docs) |
|----------------------|----------------------| ----------------------| ----------------------|
<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" />
@@ -15,10 +16,12 @@
<img src="https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/network.png" width="300px" />
</a>
> [!NOTE]
> There is also an experimental 🧪 [bare-metal install](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/HW_INSTALL.md) method available.
## 📕 Basic Usage
- You will have to run the container on the host network, e.g:
- You will have to run the container on the `host` network, e.g:
```yaml
docker run -d --rm --network=host \
@@ -27,36 +30,42 @@ docker run -d --rm --network=host \
-e TZ=Europe/Berlin \
-e PORT=20211 \
jokobsk/pi.alert:latest
```
- The initial scan can take up-to 15min (with 50 devices and MQTT). Subsequent ones 3 and 5 minutes so wait that long for all of the scans to run.
```
- The initial scan can take up to 15min (with 50 devices and MQTT). Subsequent ones 3 and 5 minutes so wait that long for all of the scans to run.
### Docker environment variables
| Variable | Description | Default |
| :------------- |:-------------| -----:|
| `PORT` |Port of the web interface | `20211` |
| `LISTEN_ADDR` |Set the specific IP Address for the listener address for the nginx webserver (web interface). This could be useful when using multiple subnets to hide the web interface from all untrusted networks. | `0.0.0.0` |
|`TZ` |Time zone to display stats correctly. Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `Europe/Berlin` |
|`HOST_USER_GID` |User ID (UID) to map the user in the container to a server user with sufficient read&write permissions on the mapped files | `1000` |
|`HOST_USER_ID` |User Group ID (GID) to map the user group in the container to a server user group with sufficient read&write permissions on the mapped files | `1000` |
|`ALWAYS_FRESH_INSTALL` | Setting `ALWAYS_FRESH_INSTALL=true` will delete the content of the `/db` & `/config` folders. For testing purposes. Can be coupled with [watchtower](https://github.com/containrrr/watchtower) to have an always freshly installed `pi.alert`/`_dev` image. | `N/A` |
### Docker paths
| | Path | Description |
| :------------- | :------------- |:-------------|
| **Required** | `:/home/pi/pialert/config` | Folder which will contain the `pialert.conf` file (see below for details) |
| **Required** | `:/home/pi/pialert/db` | Folder which will contain the `pialert.db` file |
|Optional| `:/home/pi/pialert/front/log` | Logs folder useful for debugging if you have issues setting up the container |
|Optional| `:/etc/pihole/pihole-FTL.db` | PiHole's `pihole-FTL.db` database file. Required if you want to use PiHole |
|Optional| `:/etc/pihole/dhcp.leases` | PiHole's `dhcp.leases` file. Required if you want to use PiHole `dhcp.leases` file. This has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry. (the path in the container must contain `pihole`)|
|Optional| `:/home/pi/pialert/front/api` | A simple [API endpoint](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md) containing static (but regularly updated) json and other files. |
|Optional| `:/home/pi/pialert/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](/front/plugins/README.md). |
> [!NOTE]
> See also [Backup strategies](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/BACKUPS.md).
| Required | Path | Description |
| :------------- | :------------- | :-------------|
| ✅ | `:/home/pi/pialert/config` | Folder which will contain the `pialert.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md)) files (see below for details) |
| ✅ | `:/home/pi/pialert/db` | Folder which will contain the `pialert.db` file |
| | `:/home/pi/pialert/front/log` | Logs folder useful for debugging if you have issues setting up the container |
| | `:/etc/pihole/pihole-FTL.db` | PiHole's `pihole-FTL.db` database file. Required if you want to use PiHole DB mapping. |
| | `:/etc/pihole/dhcp.leases` | PiHole's `dhcp.leases` file. Required if you want to use PiHole `dhcp.leases` file. This has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry (the path in the container must contain `pihole`)|
| | `:/home/pi/pialert/front/api` | A simple [API endpoint](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md) containing static (but regularly updated) json and other files. |
| | `:/home/pi/pialert/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/README.md). |
| | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/REVERSE_DNS.md). |
### Config (`pialert.conf`)
### Modify the config (`pialert.conf`) only if UI is not available
- 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.
- If unavailable, the app generates a default `pialert.conf` and `pialert.db` file on the first run.
#### Important settings
@@ -79,15 +88,35 @@ There are 2 approaches how to get PiHole devices imported. Via the PiHole import
* `DHCPLSS_RUN`: You need to map `:/etc/pihole/dhcp.leases` in the `docker-compose.yml` file if you enable this setting.
* The above setting has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry (the path in the container must contain `pihole` as PiHole uses a different format of the `dhcp.leases` file).
> [!NOTE]
> It's recommended to use the same schedule interval for all plugins responsible for discovering new devices.
#### 🧭 Community guides
Use the official installation guides at first and use community content as suplementary material. Open an issue if you'd like to add your link to the list 🙏
- 📄 [How to Install Pi.Alert on Your Synology NAS - Marius hosting (English)](https://mariushosting.com/how-to-install-pi-alert-on-your-synology-nas/) (Updated frequently)
- 📄 [Using the PiAlert Network Security Scanner on a Raspberry Pi - PiMyLifeUp (English)](https://pimylifeup.com/raspberry-pi-pialert/)
- ▶ [How to Setup Pi.Alert on Your Synology NAS - Digital Aloha (English)](https://www.youtube.com/watch?v=M4YhpuRFaUg)
- 📄 [시놀/헤놀에서 네트워크 스캐너 Pi.Alert Docker로 설치 및 사용하기 (Korean)](https://blog.dalso.org/article/%EC%8B%9C%EB%86%80-%ED%97%A4%EB%86%80%EC%97%90%EC%84%9C-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%8A%A4%EC%BA%90%EB%84%88-pi-alert-docker%EB%A1%9C-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%82%AC%EC%9A%A9) (July 2023)
- 📄 [网络入侵探测器Pi.Alert (Chinese)](https://codeantenna.com/a/VgUvIAjZ7J) (May 2023)
- ▶ [Pi.Alert auf Synology & Docker by - Jürgen Barth (German)](https://www.youtube.com/watch?v=-ouvA2UNu-A) (March 2023)
- ▶ [Top Docker Container for Home Server Security - VirtualizationHowto (English)](https://www.youtube.com/watch?v=tY-w-enLF6Q) (March 2023)
- ▶ [Pi.Alert or WatchYourLAN can alert you to unknown devices appearing on your WiFi or LAN network - Danie van der Merwe (English)](https://www.youtube.com/watch?v=v6an9QG2xF0) (November 2022)
> Ordered by last update time.
### **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).
⚠ Check also common issues and [debugging tips](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_TIPS.md).
## 📄 Examples
> [!NOTE]
> You can bulk-update devices via the [CSV import method](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md).
## 📄 docker-compose.yml Examples
### Example 1
@@ -211,16 +240,21 @@ Courtesy of [pbek](https://github.com/pbek). The volume `pialert_db` is used by
## 🏅 Recognitions
Big thanks to <a href="https://github.com/Macleykun">@Macleykun</a> for help and tips&tricks for Dockerfile(s):
Big thanks to <a href="https://github.com/Macleykun">@Macleykun</a> & for help and tips&tricks for Dockerfile(s) and <a href="https://github.com/vladaurosh">@vladaurosh</a> for Alpine re-base help.
<a href="https://github.com/Macleykun">
<img src="https://avatars.githubusercontent.com/u/26381427?size=50">
</a>
## ❤ Support me
## ☕ Support me
Get:
- Regular updates to keep your data and family safe 🔄
- Better and more functionality
- I don't get burned out and the app survives longer🔥🤯
- Quicker and better support with issues 🆘
- Less grumpy 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>
| [![GitHub](https://i.imgur.com/emsRCPh.png)](https://github.com/sponsors/jokob-sk) | [![Buy Me A Coffee](https://i.imgur.com/pIM6YXL.png)](https://www.buymeacoffee.com/jokobsk) | [![Patreon](https://i.imgur.com/MuYsrq1.png)](https://www.patreon.com/user?u=84385063) |
| --- | --- | --- |
BTC: 1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM
- Bitcoin: `1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM`
- Ethereum: `0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7`
> 📧 Email me at [jokob@duck.com](mailto:jokob@duck.com?subject=PiAlert) if you want to get in touch or if I should add other sponsorship platforms.

31
dockerfiles/pre-setup.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/bin/bash
export INSTALL_DIR=/home/pi
# php-fpm setup
install -d -o nginx -g www-data /run/php/
sed -i "/^;pid/c\pid = /run/php/php8.2-fpm.pid" /etc/php82/php-fpm.conf
sed -i "/^listen/c\listen = /run/php/php8.2-fpm.sock" /etc/php82/php-fpm.d/www.conf
sed -i "/^;listen.owner/c\listen.owner = nginx" /etc/php82/php-fpm.d/www.conf
sed -i "/^;listen.group/c\listen.group = www-data" /etc/php82/php-fpm.d/www.conf
sed -i "/^user/c\user = nginx" /etc/php82/php-fpm.d/www.conf
sed -i "/^group/c\group = www-data" /etc/php82/php-fpm.d/www.conf
# s6 overlay setup
mkdir -p /etc/s6-overlay/s6-rc.d/{SetupOneshot,php-fpm/dependencies.d,nginx/dependencies.d}
mkdir -p /etc/s6-overlay/s6-rc.d/{SetupOneshot,php-fpm/dependencies.d,nginx/dependencies.d,pialert/dependencies.d}
echo "oneshot" > /etc/s6-overlay/s6-rc.d/SetupOneshot/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/php-fpm/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/nginx/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/pialert/type
echo -e "${INSTALL_DIR}/pialert/dockerfiles/setup.sh" > /etc/s6-overlay/s6-rc.d/SetupOneshot/up
echo -e "#!/bin/execlineb -P\n/usr/sbin/php-fpm82 -F" > /etc/s6-overlay/s6-rc.d/php-fpm/run
echo -e '#!/bin/execlineb -P\nnginx -g "daemon off;"' > /etc/s6-overlay/s6-rc.d/nginx/run
echo -e '#!/bin/execlineb -P\n\nwith-contenv\nimportas -i PORT PORT\nif { echo "[INSTALL] 🚀 Starting app - navigate to your <server IP>:${PORT}" }' > /etc/s6-overlay/s6-rc.d/pialert/run
echo -e "python ${INSTALL_DIR}/pialert/pialert" >> /etc/s6-overlay/s6-rc.d/pialert/run
touch /etc/s6-overlay/s6-rc.d/user/contents.d/{SetupOneshot,php-fpm,nginx} /etc/s6-overlay/s6-rc.d/{php-fpm,nginx}/dependencies.d/SetupOneshot
touch /etc/s6-overlay/s6-rc.d/user/contents.d/{SetupOneshot,php-fpm,nginx,pialert} /etc/s6-overlay/s6-rc.d/{php-fpm,nginx,pialert}/dependencies.d/SetupOneshot
touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/php-fpm
touch /etc/s6-overlay/s6-rc.d/pialert/dependencies.d/nginx
rm -f $0

79
dockerfiles/setup.sh Executable file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/with-contenv bash
echo "---------------------------------------------------------"
echo "[INSTALL] Run setup.sh"
echo "---------------------------------------------------------"
export INSTALL_DIR=/home/pi # Specify the installation directory here
# DO NOT CHANGE ANYTHING BELOW THIS LINE!
NGINX_CONFIG_FILE=/etc/nginx/http.d/pialert.conf
OUI_FILE="/usr/share/arp-scan/ieee-oui.txt" # Define the path to ieee-oui.txt and ieee-iab.txt
FILEDB="${INSTALL_DIR}/pialert/db/pialert.db"
# DO NOT CHANGE ANYTHING ABOVE THIS LINE!
# Check if script is run as root
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root. Please use 'sudo'."
exit 1
fi
echo "[INSTALL] Copy starter pialert.db and pialert.conf if they don't exist"
# DANGER ZONE: ALWAYS_FRESH_INSTALL
if [ "$ALWAYS_FRESH_INSTALL" = true ]; then
echo "[INSTALL] ❗ ALERT /db and /config folders are cleared because the ALWAYS_FRESH_INSTALL is set to: $ALWAYS_FRESH_INSTALL"
# Delete content of "$INSTALL_DIR/pialert/config/"
rm -rf "$INSTALL_DIR/pialert/config/"*
# Delete content of "$INSTALL_DIR/pialert/db/"
rm -rf "$INSTALL_DIR/pialert/db/"*
fi
# Copy starter pialert.db and pialert.conf if they don't exist
cp -na "${INSTALL_DIR}/pialert/back/pialert.conf" "${INSTALL_DIR}/pialert/config/pialert.conf"
cp -na "${INSTALL_DIR}/pialert/back/pialert.db" "${FILEDB}"
# if custom variables not set we do not need to do anything
if [ -n "${TZ}" ]; then
FILECONF="${INSTALL_DIR}/pialert/config/pialert.conf"
echo "[INSTALL] Setup timezone"
sed -i "\#^TIMEZONE=#c\TIMEZONE='${TZ}'" "${FILECONF}"
fi
echo "[INSTALL] Setup NGINX"
echo "Setting webserver to address ($LISTEN_ADDR) and port ($PORT)"
envsubst '$INSTALL_DIR $LISTEN_ADDR $PORT' < "${INSTALL_DIR}/pialert/install/pialert.template.conf" > "${NGINX_CONFIG_FILE}"
# Run the hardware vendors update at least once
echo "[INSTALL] Run the hardware vendors update"
# Check if ieee-oui.txt or ieee-iab.txt exist
if [ -f "${OUI_FILE}" ]; then
echo "The file ieee-oui.txt exists. Skipping update_vendors.sh..."
else
echo "The file ieee-oui.txt does not exist. Running update_vendors..."
# Run the update_vendors.sh script
if [ -f "${INSTALL_DIR}/pialert/back/update_vendors.sh" ]; then
"${INSTALL_DIR}/pialert/back/update_vendors.sh"
else
echo "update_vendors.sh script not found in ${INSTALL_DIR}."
fi
fi
# Create an empty log files
# Create the execution_queue.log and pialert_front.log files if they don't exist
touch "${INSTALL_DIR}"/pialert/front/log/{execution_queue.log,pialert_front.log,pialert.php_errors.log,stderr.log,stdout.log}
echo "[INSTALL] Fixing permissions after copied starter config & DB"
chown -R nginx:www-data "${INSTALL_DIR}"/pialert/{config,front/log,db}
chmod 750 "${INSTALL_DIR}"/pialert/{config,front/log,db}
chmod 640 "${INSTALL_DIR}"/pialert/{config,front/log,db}/*
# Check if buildtimestamp.txt doesn't exist
if [ ! -f "${INSTALL_DIR}/pialert/front/buildtimestamp.txt" ]; then
# Create buildtimestamp.txt
date +%s > "${INSTALL_DIR}/pialert/front/buildtimestamp.txt"
chown nginx:www-data "${INSTALL_DIR}/pialert/front/buildtimestamp.txt"
fi

View File

@@ -1,33 +0,0 @@
#!/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 [ -n "${PORT}" ]; then
sed -ie 's/listen 20211/listen '${PORT}'/g' /etc/nginx/sites-available/default
fi
# I hope this will fix DB permission issues going forward
FILEDB=/home/pi/pialert/db/pialert.db
if [ -f "$FILEDB" ]; then
chown -R www-data:www-data /home/pi/pialert/db/pialert.db
fi
chmod -R a+rw /home/pi/pialert/front/log
chmod -R a+rw /home/pi/pialert/config
/etc/init.d/php7.4-fpm start
/etc/init.d/nginx start
# cron -f
#python /home/pi/pialert/back/pialert.py
# echo "[DEBUG] DATA MONKEY VERSION ..."
python /home/pi/pialert/pialert/

View File

@@ -1,29 +0,0 @@
#!/bin/bash
if [ -z "${USER}" ]; then
echo "We need USER to be set!"; exit 100
fi
# if both not set we do not need to do anything
if [ -z "${HOST_USER_ID}" -a -z "${HOST_USER_GID}" ]; then
echo "Nothing to do here." ; exit 0
fi
# reset user_?id to either new id or if empty old (still one of above
# might not be set)
USER_ID=${HOST_USER_ID:=$USER_ID}
USER_GID=${HOST_USER_GID:=$USER_GID}
LINE=$(grep -F "${USER}" /etc/passwd)
# replace all ':' with a space and create array
array=( ${LINE//:/ } )
# home is 5th element
USER_HOME=${array[4]}
sed -i -e "s/^${USER}:\([^:]*\):[0-9]*:[0-9]*/${USER}:\1:${USER_ID}:${USER_GID}/" /etc/passwd
sed -i -e "s/^${USER}:\([^:]*\):[0-9]*/${USER}:\1:${USER_GID}/" /etc/group
chown -R ${USER_ID}:${USER_GID} ${USER_HOME}
exec su - "${USER}"

View File

@@ -28,6 +28,7 @@ You can access the following files:
| `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. |
| `app_state.json` | Contains the current application state. |
Current/latest state of the aforementioned files depends on your settings.

82
docs/BACKUPS.md Executable file
View File

@@ -0,0 +1,82 @@
# 💾 Backing things up
> [!NOTE]
> To backup 99% of your configuration backup at least the `/config` folder. Please read the whole page (or at least "Scenario 2: Corrupted database") for details.
There are 3 artifacts that can be used to backup the application:
| File | Description | Limitations |
|-----------------------|-------------------------------|-------------------------------|
| `/db/pialert.db` | Database file(s) | The database file might be in an uncommitted state or corrupted |
| `/config/pialert.conf` | Configuration file | Doesn't contain settings from the Maintenance section |
| `/config/devices.csv` | CSV file containing device information | Doesn't contain historical data |
## Data and cackup storage
To decide on a backup strategy, check where the data is stored:
### Core Configuration
The core application configuration is in the `pialert.conf` file (See [Settings System](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SETTINGS_SYSTEM.md) for details), such as:
- Notification settings
- Scanner settings
- Scheduled maintenance settings
- UI configuration (80%)
### Core Device Data
The core device data is backed up to the `devices_<timestamp>.csv` file via the [CSV Backup `CSVBCKP` Plugin](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins/csv_backup). This file contains data, such as:
- Device names
- Device Icons
- Device Network configuration
- Device categorization
### Historical data
Historical data is stored in the `pialert.db` database (See [Database overview](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DATABASE.md) for details). This data includes:
- Plugin objects
- Plugin historical entries
- History of Events, Notifications, Workflow Events
- Presence History
## 🧭 Backup strategies
The safest approach to backups is to backup all of the above, by taking regular file system backups (I use [Kopia](https://github.com/kopia/kopia)).
Arguably, the most time is spent setting up the device list, so if only one file is kept I'd recommend to have a latest backup of the `devices_<timestamp>.csv` file, followed by the `pialert.conf` file.
### Scenario 1: Full backup
End-result: Full restore
#### Source artifacts:
- `/db/pialert.db` (uncorrupted)
- `/config/pialert.conf`
#### Recovery:
To restore the application map the above files as described in the [Setup documentation](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md#docker-paths).
### Scenario 2: Corrupted database
End-result: Partial restore (historical data & configurations from the Maintenance section will be missing)
#### Source artifacts:
- `/config/pialert.conf`
- `/config/devices_<timestamp>.csv` or `/config/devices.csv`
#### Recovery:
Even with a corrupted database you can recover what I would argue is 99% of the configuration (except of a couple of settings under Maintenance).
- map the `/config/pialert.conf` file as described in the [Setup documentation](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md#docker-paths).
- rename the `devices_<timestamp>.csv` to `devices.csv` and place it in the `/config` folder
- Restore the `devices.csv` backup via the [Maintenance section](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md)

View File

@@ -12,7 +12,7 @@
| CurrentScan | Result of the current scan | ![Screen1][screen1] |
| Devices | The main devices database that also contains the Network tree mappings. If `ScanCycle` is set to `0` device is not scanned. | ![Screen2][screen2] |
| Events | Used to collect connection/disconnection events. | ![Screen4][screen4] |
| Online_History | Used to display the `Device presence over time` chart | ![Screen6][screen6] |
| Online_History | Used to display the `Device presence` 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] |
| 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] |

View File

@@ -8,6 +8,7 @@ Check the the HTTP response of the failing backend call by following these steps
![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/api/table_devices.json?nocache=1704141103121`
- `http://<pialert URL>:20211/php/server/devices.php?action=getDevicesTotals`
- `http://<pialert URL>:20211/php/server/devices.php?action=getDevicesList&status=all`
@@ -15,15 +16,19 @@ Check the the HTTP response of the failing backend call by following these steps
For reference, the above queries should return results in the following format:
First URL:
## First URL:
- Should yield a valid JSON file
## Second URL:
![array][array]
Second URL:
## Third 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.
You can copy and paste any JSON result (result of the First and Third 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"

74
docs/DEBUG_PLUGINS.md Executable file
View File

@@ -0,0 +1,74 @@
# Troubleshooting plugins
## High-level overview
If a Plugin supplies data to the main app it's doine either vie a SQL query or via a script that updates the `last_result.log` file in the plugin folder (`front/plugins/<plugin>`).
For a more in-depth overview on how plugins work check the [Plugins development docs](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/README.md).
### Prerequisites
- Make sure you read and followed the specific plugin setup instructions.
- Ensure you have [debug enabled (see More Logging)](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_TIPS.md#1-more-logging-)
### Potential issues
- Bugs
- Unexpected input (e.g. special characters in names)
- Dependencies changed how data is output
#### Incorrect input data
Input data from the plugin might cause mapping issues in specific edge cases. Look for a corresponding section in the `pialert.log` file, for example notice the first line of the execution run of the `PIHOLE` plugin below:
```
17:31:05 [Scheduler] - Scheduler run for PIHOLE: YES
17:31:05 [Plugin utils] ---------------------------------------------
17:31:05 [Plugin utils] display_name: PiHole (Device sync)
17:31:05 [Plugins] CMD: SELECT n.hwaddr AS Object_PrimaryID, {s-quote}null{s-quote} AS Object_SecondaryID, datetime() AS DateTime, na.ip AS Watched_Value1, n.lastQuery AS Watched_Value2, na.name AS Watched_Value3, n.macVendor AS Watched_Value4, {s-quote}null{s-quote} AS Extra, n.hwaddr AS ForeignKey FROM EXTERNAL_PIHOLE.Network AS n LEFT JOIN EXTERNAL_PIHOLE.Network_Addresses AS na ON na.network_id = n.id WHERE n.hwaddr NOT LIKE {s-quote}ip-%{s-quote} AND n.hwaddr is not {s-quote}00:00:00:00:00:00{s-quote} AND na.ip is not null
17:31:05 [Plugins] setTyp: subnets
17:31:05 [Plugin utils] Flattening the below array
17:31:05 ['192.168.1.0/24 --interface=eth1']
17:31:05 [Plugin utils] isinstance(arr, list) : False | isinstance(arr, str) : True
17:31:05 [Plugins] Resolved value: 192.168.1.0/24 --interface=eth1
17:31:05 [Plugins] Convert to Base64: True
17:31:05 [Plugins] base64 value: b'MTkyLjE2OC4xLjAvMjQgLS1pbnRlcmZhY2U9ZXRoMQ=='
17:31:05 [Plugins] Timeout: 10
17:31:05 [Plugins] Executing: SELECT n.hwaddr AS Object_PrimaryID, 'null' AS Object_SecondaryID, datetime() AS DateTime, na.ip AS Watched_Value1, n.lastQuery AS Watched_Value2, na.name AS Watched_Value3, n.macVendor AS Watched_Value4, 'null' AS Extra, n.hwaddr AS ForeignKey FROM EXTERNAL_PIHOLE.Network AS n LEFT JOIN EXTERNAL_PIHOLE.Network_Addresses AS na ON na.network_id = n.id WHERE n.hwaddr NOT LIKE 'ip-%' AND n.hwaddr is not '00:00:00:00:00:00' AND na.ip is not null
17:31:05 [Plugins] SUCCESS, received 2 entries
17:31:05 [Plugins] sqlParam entries: [(0, 'PIHOLE', '01:01:01:01:01:01', 'null', 'null', '2023-12-25 06:31:05', '172.30.0.1', 0, 'aaaa', 'vvvvvvvvv', 'not-processed', 'null', 'null', '01:01:01:01:01:01'), (0, 'PIHOLE', '02:42:ac:1e:00:02', 'null', 'null', '2023-12-25 06:31:05', '172.30.0.2', 0, 'dddd', 'vvvvv2222', 'not-processed', 'null', 'null', '02:42:ac:1e:00:02')]
17:31:05 [Plugins] Processing : PIHOLE
17:31:05 [Plugins] Existing objects from Plugins_Objects: 4
17:31:05 [Plugins] Logged events from the plugin run : 2
17:31:05 [Plugins] pluginEvents count: 2
17:31:05 [Plugins] pluginObjects count: 4
17:31:05 [Plugins] events_to_insert count: 0
17:31:05 [Plugins] history_to_insert count: 4
17:31:05 [Plugins] objects_to_insert count: 0
17:31:05 [Plugins] objects_to_update count: 4
17:31:05 [Plugin utils] In pluginEvents there are 2 events with the status "watched-not-changed"
17:31:05 [Plugin utils] In pluginObjects there are 2 events with the status "missing-in-last-scan"
17:31:05 [Plugin utils] In pluginObjects there are 2 events with the status "watched-not-changed"
17:31:05 [Plugins] Mapping objects to database table: CurrentScan
17:31:05 [Plugins] SQL query for mapping: INSERT into CurrentScan ( "cur_MAC", "cur_IP", "cur_LastQuery", "cur_Name", "cur_Vendor", "cur_ScanMethod") VALUES ( ?, ?, ?, ?, ?, ?)
17:31:05 [Plugins] SQL sqlParams for mapping: [('01:01:01:01:01:01', '172.30.0.1', 0, 'aaaa', 'vvvvvvvvv', 'PIHOLE'), ('02:42:ac:1e:00:02', '172.30.0.2', 0, 'dddd', 'vvvvv2222', 'PIHOLE')]
17:31:05 [API] Update API starting
17:31:06 [API] Updating table_plugins_history.json file in /front/api
```
In the above output notice the section logging how many events are produced by the plugin:
```
17:31:05 [Plugins] Existing objects from Plugins_Objects: 4
17:31:05 [Plugins] Logged events from the plugin run : 2
17:31:05 [Plugins] pluginEvents count: 2
17:31:05 [Plugins] pluginObjects count: 4
17:31:05 [Plugins] events_to_insert count: 0
17:31:05 [Plugins] history_to_insert count: 4
17:31:05 [Plugins] objects_to_insert count: 0
17:31:05 [Plugins] objects_to_update count: 4
```
These values, if formatted correctly, will also show up in the UI:
![Plugins table](/docs/img/DEBUG_PLUGINS/plugin_objects_pihole.png)

View File

@@ -56,7 +56,7 @@ services:
* 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 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.
* 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)
* If still facing issues, try to map the pialert.db file (⚠ not folder) to `:/home/pi/pialert/db/pialert.db` (see [docker-compose Examples](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md#-docker-composeyml-examples) for details)
### Container restarts / crashes

34
docs/DEVICES_BULK_EDITING.md Executable file
View File

@@ -0,0 +1,34 @@
# 🖊 Multi-editing via the UI
> [!NOTE]
> Make sure you have your backups saved and restorable before doing any mass edits. Check [Backup strategies](/docs/BACKUPS.md).
You can select devices in the _Devices_ view by selecting devices to edit and then clicking the _Multi-edit_ button or via the _Maintenance_ > _Multi-Edit_ section.
![Maintenance > Multi-edit](/docs/img/DEVICES_BULK_EDITING/MULTI-EDIT.gif)
# 📝Bulk-edit devices via CSV Export/Import
> [!NOTE]
> As always, backup everything, just in case.
1. In _Maintenance_ > _Backup / Restore_ click the _CSV Export_ button.
2. A `devices.csv` is generated in the `/config` folder
3. Edit the `devices.csv` file however you like.
![Maintenance > CSV Export](/docs/img/DEVICES_BULK_EDITING/MAINTENANCE_CSV_EXPORT.png)
> [!NOTE]
> The file containing a list of Devices including the Network relationships between Network Nodes and connected devices. You can also trigger this by acessing this URL: `<your pialert url>/php/server/devices.php?action=ExportCSV` or via the `CSV Backup` plugin. (💡 You can schedule this)
![Settings > CSV Backup](/docs/img/DEVICES_BULK_EDITING/CSV_BACKUP_SETTINGS.png)
> [!NOTE]
> Keep Linux line endings (suggested editors: Nano, Notepad++)
![Nodepad++ line endings](/docs/img/DEVICES_BULK_EDITING/NOTEPAD++.png)

View File

@@ -9,6 +9,11 @@ To edit device information:
- Press the "Save" button
> [!NOTE]
>
> [Bulk-edit devices](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md) by using the _CSV Export_ functionality in the _Maintenance_ section.
![Device Details][screen1]
@@ -41,8 +46,7 @@ To edit device information:
- **Alert All Events**: Send a notification in each event (connection,
disconnection, IP Changed, ...)
- **Alert Down**: Send a notification when the device is down
- *(Userful with "always connected" devices: Router, AP, Camera, Alexa,
...)*
- *(Userful with "always connected" devices: Camera, Alexa,...)*
- **Skip repeated notifications during**: Do not send more than one
notification to this device for X hours
- *(Useful to avoid notification saturation on devices that frequently

View File

@@ -22,11 +22,12 @@ PiAlert comes with MQTT support, allowing you to show all detected devices as de
- User
- Password
4. Ope the `PiAlert` > `Settings` > `MQTT` settings group
4. Open the _PiAlert_ > _Settings_ > _MQTT_ settings group
- Enable MQTT
- Fill in the details from above
- Fill in remaining settings as per description
![Configuration Example][configuration]
## 📷 Screenshots
@@ -35,6 +36,7 @@ PiAlert comes with MQTT support, allowing you to show all detected devices as de
| ![Screen 3][list] | ![Screen 4][overview] |
[configuration]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Configuration.png "configuration"
[sensors]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Device-as-Sensors.png "sensors"
[history]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Device-Presence-History.png "history"
[list]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Devices-List.png "list"

49
docs/HW_INSTALL.md Executable file
View File

@@ -0,0 +1,49 @@
# How to install PiAlert on the server hardware
To download and install PiAlert on the hardware/server directly use the `curl` or `wget` commands at the bottom of this page.
> [!NOTE]
> This is an Experimental feature 🧪 and it relies on community support.
>
> There is no guarantee that the install script or any other script will gracefully handle other installed software.
> Data loss is a possibility, **it is recommended to install PiAlert using the supplied Docker image**.
A warning to the installation method below: Piping to bash is [controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash) and may
be dangerous, as you cannot see the code that's about to be executed on your system.
Alternatively you can download the installation script `install/install.debian.sh` from the repository and check the code yourself (beware other scripts are
downloaded too - only from this repo).
PiAlert will be installed in `home/pi/pialert/` and run on port number `20211`.
Some facts about what and where something will be changed/installed by the HW install setup (may not contain everything!):
- `/home/pi/pialert` directory will be deleted and newly created
- `/home/pi/pialert` will contain the whole repository (downloaded by `install/install.debian.sh`)
- The default NGINX site `/etc/nginx/sites-enabled/default` will be disabled (sym-link deleted or backed up to `sites-available`)
- `/var/www/html/pialert` directory will be deleted and newly created
- `/etc/nginx/conf.d/pialert.conf` will be sym-linked to `/home/pi/pialert/install/pialert.debian.conf`
- Some files (IEEE device vendors info, ...) will be created in the directory where the installation script is executed
## Limitations
- No system service is provided. PiAlert must be started using `/home/pi/pialert/install/start.debian.sh`.
- No checks for other running software is done.
- Only tested to work on Debian Bookworm (Debian 12).
- **EXPERIMENTAL** and not recommended way to install PiAlert.
## 📥 Installation via CURL
```bash
curl -o install.debian.sh https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/install/install.debian.sh && sudo chmod +x install.debian.sh && sudo ./install.debian.sh
```
## 📥 Installation via WGET
```bash
wget https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/install/install.debian.sh -O install.debian.sh && sudo chmod +x install.debian.sh && sudo ./install.debian.sh
```
These commands will download the `install.debian.sh` script from the GitHub repository, make it executable with `chmod`, and then run it using `./install.debian.sh`.
Make sure you have the necessary permissions to execute the script.

View File

@@ -1,6 +1,6 @@
## 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).
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.
![Raspberry Pi with a brand icon](/docs/img/ICONS/devices-icons.png)
@@ -8,6 +8,8 @@ Icons are used to visually distinguish devices in the app in most of the device
You can assign icons individually on each device in the Details tab.
![preview](/docs/img/ICONS/device_icons_preview.gif)
![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:

View File

@@ -1,6 +1,6 @@
## How to setup your Network page
Make sure you have a root device with the MAC `Internet` (No other MAC addresses are currently supported as the root node).
Make sure you have a root device with the MAC `Internet` (No other MAC addresses are currently supported as the root node) set to a network device type (e.g.: **Type**:`Router`).
> 💡 Tip: You can add dummy devices via the [Undiscoverables plugin](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/undiscoverables/README.md)
@@ -8,19 +8,22 @@ Make sure you have a root device with the MAC `Internet` (No other MAC addresses
## ⚡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.
* Go to a Device you want to use as network device (network nodes, such as a Switch).
* 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 (you can create a custom network type device with in Settings -> General -> `NETWORK_DEVICE_TYPES`).
* 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.
* If port is empty or 0 a wifi icon is rendered, otherwise a ethernet port icon
* You can now assign the Unassigend devices to the network node.
* If port is empty or 0 a wifi icon is rendered, otherwise a ethernet port icon.
> [!NOTE]
>
> [Bulk-edit devices](/docs/DEVICES_BULK_EDITING.md) by using the _CSV Export_ functionality in the _Maintenance_ section. You can use this to fix `Internet` node assignment issues.
## 🔍Detailed example:
In this example you will setup a device named `rapberrypi` as a `Switch` in our network.
### 1) Device details page
### 1. Device details page
- Go to the `Devices` (1) page:
@@ -29,23 +32,23 @@ In this example you will setup a device named `rapberrypi` as a `Switch` in our
- In the (2) `Details` tab navigate to the the `Type` (3) dropdown and select the type `Switch` (4).
> Note: Only the following device types will show up as selectable Network nodes ( = devices you can connect other devices to):
> AP, Firewall, Gateway, Hypervisor, PLC, Powerline, Router, Switch, USB LAN Adapter, USB WIFI Adapter and WLAN.
> AP, Firewall, Gateway, Hypervisor, PLC, Powerline, Router, Switch, USB LAN Adapter, USB WIFI Adapter and WLAN. Custom types can be added via the `NETWORK_DEVICE_TYPES` setting.
- 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)
### 1) Network page
### 2. Network page
- Navigate to your `Network` (1) page:
![Network page](/docs/img/NETWORK_TREE/Network_Page.png)
- Notice the newly added `raspberrypi` (2) tab which now represents a network node, also showing up in the tree (3).
- As we asssigned the `raspberrypi` in the previous 1) Device details page section to the `Internet` parent network node in step (6), the link is also showing up in the tree diagram (4)
- As we asssigned the `raspberrypi` in the previous (1) Device details page section to the `Internet` parent network node in step (6), the link is also showing up in the tree diagram (4)
- We can now assign the device `(AppleTV)` (5) to this `raspberrypi` node, representing a network Switch in this example
### 1) Network page with 2 levels
### 3. Network page with 2 levels
- After clicking the `Assign` button in the previous section, the `(AppleTV)` (1) device is now connected to our `raspberrypi` (2).
@@ -57,3 +60,4 @@ In this example you will setup a device named `rapberrypi` as a `Switch` in our

View File

@@ -16,6 +16,8 @@ WIFI's**, in this way, Pi.Alert will be able to identify the device, and it
will not identify it as a new device every so often (every time IOS or Android
decides to change the MAC).
**Random MACs** are recognized by the characters "2", "6", "A", or "E" as the 2nd character in the Mac address. You can disable specific prefixes to be detected as random MAC addresses by specifying the `UI_NOT_RANDOM_MAC` setting.
## IOS
![ios][ios]

View File

@@ -1,36 +1,56 @@
## 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.
<details>
<summary>:information_source: In the app hover over settings or fields/labels or click blue in-app ❔ (question-mark) icons to get to relevant documentation pages.</summary>
![In-app help](/docs/img/GENERAL/in-app-help.png)
![In-app help](/docs/img/GENERAL/in-app-help.png)
</details>
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/)
#### 🐳 Docker (Fully supported)
- The main installation method is as a [docker container - follow these instructions here](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md).
#### 💻 Bare-metal / On-server (Experimental/community supported 🧪)
- [(Experimental 🧪) On-hardware instructions](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/HW_INSTALL.md)
- Alternative bare-metal install forks:
- [leiweibau's fork](https://github.com/leiweibau/Pi.Alert/) (maintained)
- [pucherot's original code](https://github.com/pucherot/Pi.Alert/) (un-maintained)
### 📚 Table of contents
#### 📥 Initial Setup
- [Subnets and VLANs configuration for arp-scan](/docs/SUBNETS.md)
- [SMTP server config](/docs/SMTP.md)
- [Custom Icon configuration and support](/docs/ICONS.md)
- [Better name resolution with Reverse DNS](/docs/REVERSE_DNS.md)
- [Network treemap configuration](/docs/NETWORK_TREE.md)
- [Backups](/docs/BACKUPS.md)
#### 🐛 Debugging help & tips
- [Debugging tips](/docs/DEBUG_TIPS.md)
- [Debugging UI not showing](/docs/WEB_UI_PORT_DEBUG.md)
- [Invalid JSON errors debug help](/docs/DEBUG_INVALID_JSON.md)
- [Troubleshooting Plugins](/docs/DEBUG_PLUGINS.md)
#### 🔝 Popular/Suggested
- [Network treemap 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)
- [Home Assistant](/docs/HOME_ASSISTANT.md)
- [Bulk edit devices](/docs/DEVICES_BULK_EDITING.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
@@ -50,6 +70,7 @@ There is also an in-app Help / FAQ section that should be answering frequently a
- [Settings system](/docs/SETTINGS_SYSTEM.md)
- [New Version notifications](/docs/VERSIONS.md)
- [Frontend development tips](/docs/FRONTEND_DEVELOPMENT.md)
- [Webhook secrets](/docs/WEBHOOK_SECRET.md)
Feel free to suggest or submit new docs via a PR.
@@ -86,7 +107,7 @@ 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.
4. New features code should ideally be re-usable for different purposes, not for a very narrow use case.
5. New functionality should ideally be implemented via the Plugins system, if possible.
Suggested test cases:
@@ -94,7 +115,7 @@ 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)
- Email, Apprise (e.g. via Telegram), webhook (e.g. via Discord), MQTT (e.g. via Home Assistant)
- Saving settings
- Test a couple of plugins
- Check the Error log for anything unusual
@@ -108,7 +129,7 @@ Some additional context:
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 [🛑 Common issues](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_TIPS.md#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.
* When submitting an issue ❗[enable debug](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_TIPS.md)❗

66
docs/REVERSE_DNS.md Executable file
View File

@@ -0,0 +1,66 @@
## Setting up better name discovery with Reverse DNS
If you are running a DNS server, such as **AdGuard**, set up **Private reverse DNS servers** for a better name resolution on your network. Enabling this setting will enable PiAlert to execute dig and nslookup commands to automatically resolve device names based on their IP addresses.
> Example 1: Reverse DNS `disabled`
>
> ```
> jokob@Synology-NAS:/$ nslookup 192.168.1.58
> ** server can't find 58.1.168.192.in-addr.arpa: NXDOMAIN
>
> ```
> Example 2: Reverse DNS `enabled`
>
> ```
> jokob@Synology-NAS:/$ nslookup 192.168.1.58
> 45.1.168.192.in-addr.arpa name = jokob-NUC.localdomain.
> ```
### Enabling reverse DNS in AdGuard
1. Navigate to **Settings** -> **DNS Settings**
2. Locate **Private reverse DNS servers**
3. Enter your router IP address, such as `192.168.1.1`
4. Make sure you have **Use private reverse DNS resolvers** ticked.
5. Click **Apply** to save your settings.
### Using a custom resolv.conf file
You can configure a custom **/etc/resolv.conf** file in **docker-compose.yml** and set the nameserver to your LAN DNS server (e.g.: Pi-Hole). See the relevant [resolv.conf man](https://www.man7.org/linux/man-pages/man5/resolv.conf.5.html) entry for details.
#### docker-compose.yml:
```yaml
version: "3"
services:
pialert:
container_name: pialert
image: "jokobsk/pi.alert:latest"
restart: unless-stopped
volumes:
- ./config/pialert.conf:/home/pi/pialert/config/pialert.conf
- ./pialert_db:/home/pi/pialert/db
- ./log:/home/pi/pialert/front/log
- ./config/resolv.conf:/etc/resolv.conf # Mapping the /resolv.conf file for better name resolution
environment:
- TZ=Europe/Berlin
- PORT=20211
- HOST_USER_ID=1000
- HOST_USER_GID=1000
ports:
- "20211:20211"
network_mode: host
```
#### ./config/resolv.conf:
The most important below is the `nameserver` entry (you can add multiple):
```
nameserver 192.168.178.11
options edns0 trust-ad
search example.com
```

View File

@@ -2,6 +2,10 @@
> Submitted by amazing [cvc90](https://github.com/cvc90) 🙏
> [!NOTE]
> There are 2 NGINX files for PiAlert, one for the bare-metal Debian install (`pialert.debian.conf`), and one for the docker container (`pialert.template.conf`). Both can be found in the [install](https://github.com/jokob-sk/Pi.Alert/tree/main/install) folder. Map, or use, the one appropriate for your setup.
## NGINX HTTP Configuration (Direct Path)
1. On your NGINX server, create a new file called /etc/nginx/sites-available/pialert
@@ -346,5 +350,134 @@ location ^~ /pialert/ {
```
## Traefik
> Submitted by [Isegrimm](https://github.com/Isegrimm) 🙏 (based on this [discussion](https://github.com/jokob-sk/Pi.Alert/discussions/449#discussioncomment-7281442))
Asuming the user already has a working Traefik setup, this is what's needed to make Pi.Alert work at a URL like www.domain.com/pialert/.
Note: Everything in these configs assumes '**www.domain.com**' as your domainname and '**section31**' as an arbitrary name for your certificate setup. You will have to substitute these with your own.
Also, I use the prefix '**pialert**'. If you want to use another prefix, change it in these files: dynamic.toml and default.
Content of my yaml-file (this is the generic Traefik config, which defines which ports to listen on, redirect http to https and sets up the certificate process).
It also contains Authelia, which I use for authentication.
This part contains nothing specific to Pi.Alert.
```yaml
version: '3.8'
services:
traefik:
image: traefik
container_name: traefik
command:
- "--api=true"
- "--api.insecure=true"
- "--api.dashboard=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--providers.file.filename=/traefik-config/dynamic.toml"
- "--providers.file.watch=true"
- "--log.level=ERROR"
- "--certificatesresolvers.section31.acme.email=postmaster@domain.com"
- "--certificatesresolvers.section31.acme.storage=/traefik-config/acme.json"
- "--certificatesresolvers.section31.acme.httpchallenge=true"
- "--certificatesresolvers.section31.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- /appl/docker/traefik/config:/traefik-config
depends_on:
- authelia
restart: unless-stopped
authelia:
container_name: authelia
image: authelia/authelia:latest
ports:
- "9091:9091"
volumes:
- /appl/docker/authelia:/config
restart: u
nless-stopped
```
Snippet of the dynamic.toml file (referenced in the yml-file above) that defines the config for Pi.Alert:
The following are self-defined keywords, everything else is traefik keywords:
- pialert-router
- pialert-service
- auth
- pialert-stripprefix
```toml
[http.routers]
[http.routers.pialert-router]
entryPoints = ["websecure"]
rule = "Host(`www.domain.com`) && PathPrefix(`/pialert`)"
service = "pialert-service"
middlewares = "auth,pialert-stripprefix"
[http.routers.pialert-router.tls]
certResolver = "section31"
[[http.routers.pialert-router.tls.domains]]
main = "www.domain.com"
[http.services]
[http.services.pialert-service]
[[http.services.pialert-service.loadBalancer.servers]]
url = "http://internal-ip-address:20211/"
[http.middlewares]
[http.middlewares.auth.forwardAuth]
address = "http://authelia:9091/api/verify?rd=https://www.domain.com/authelia/"
trustForwardHeader = true
authResponseHeaders = ["Remote-User", "Remote-Groups", "Remote-Name", "Remote-Email"]
[http.middlewares.pialert-stripprefix.stripprefix]
prefixes = "/pialert"
forceSlash = false
```
To make Pi.Alert work with this setup I modified the default file at `/etc/nginx/sites-available/default` in the docker container by copying it to my local filesystem, adding the changes as specified by [cvc90](https://github.com/cvc90) and mounting the new file into the docker container, overwriting the original one. By mapping the file instead of changing the file in-place, the changes persist if an updated dockerimage is pulled. This is also a downside when the default file is updated, so I only use this as a temporary solution, until the dockerimage is updated with this change.
Default-file:
```
server {
listen 80 default_server;
root /var/www/html;
index index.php;
#rewrite /pialert/(.*) / permanent;
add_header X-Forwarded-Prefix "/pialert" always;
proxy_set_header X-Forwarded-Prefix "/pialert";
location ~* \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_connect_timeout 75;
fastcgi_send_timeout 600;
fastcgi_read_timeout 600;
}
}
```
Mapping the updated file (on the local filesystem at `/appl/docker/pialert/default`) into the docker container:
```bash
docker run -d --rm --network=host \
--name=pi.alert \
-v /appl/docker/pialert/config:/home/pi/pialert/config \
-v /appl/docker/pialert/db:/home/pi/pialert/db \
-v /appl/docker/pialert/default:/etc/nginx/sites-available/default \
-e TZ=Europe/Amsterdam \
-e PORT=20211 \
jokobsk/pi.alert:latest
```

View File

@@ -2,7 +2,7 @@
This is an explanation how settings are handled intended for anyone thinking about writing their own plugin or contributing to the project.
If you are a user of the app, settings should be described in the `Settings` section of the app. Open an issue if you'd like to clarify any of the settings.
If you are a user of the app, settings have a detailed description in the _Settings_ section of the app. Open an issue if you'd like to clarify any of the settings.
### 🛢 Data storage
@@ -10,7 +10,7 @@ The source of truth for user-defined values is the `pialert.conf` file. Editing
#### Settings database table
The `Settings` database table contains settings for App run purposes. The table is recreated every time the App restarts. The settings are loaded from the source-of-truth, that is the `pialert.conf` file. A high-level overview on the databse structure can be found in the [database documentation](/docs/DATABASE.md).
The `Settings` database table contains settings for App run purposes. The table is recreated every time the App restarts. The settings are loaded from the source-of-truth, that is the `pialert.conf` file. A high-level overview on the database structure can be found in the [database documentation](/docs/DATABASE.md).
#### table_settings.json
@@ -25,14 +25,14 @@ The json file is also cached on the client-side local storage of the browser.
> [!NOTE]
> This is the source of truth for settings. User-defined values in this files always override default values specified in the Plugin definition.
The App generates two `pialert.conf` entries for every setting (Since version 23.8+). One entry is the setting value, the second is the `__metadata` associated with the setting. This `__metadata` entry contains the full setting definition in JSON format. This should helps the future extensibility of the Settings system.
The App generates two `pialert.conf` entries for every setting (Since version 23.8+). One entry is the setting value, the second is the `__metadata` associated with the setting. This `__metadata` entry contains the full setting definition in JSON format. Currently unused, but intended to be used in future to extend the Settings system.
#### Plugin settings
> [!NOTE]
> This is the preferred way adding settings going forward. I'll be likely migrating all app settings into plugin-based settings.
Plugin settings are loaded dynamically from the `config.json` of individual plugins. If a setting isn't defined in the `pialert.conf` file, it is initialized via the `default_value` property of a setting from the `config.json` file. Check the [Plugins documentation](/front/plugins/README.md), section `⚙ Setting object structure` for details on the structure of the setting.
Plugin settings are loaded dynamically from the `config.json` of individual plugins. If a setting isn't defined in the `pialert.conf` file, it is initialized via the `default_value` property of a setting from the `config.json` file. Check the [Plugins documentation](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/README.md#-setting-object-structure), section `⚙ Setting object structure` for details on the structure of the setting.
![Screen 1][screen1]

41
docs/SMTP.md Executable file
View File

@@ -0,0 +1,41 @@
# 📧 SMTP guides
## Using the GMX SMTP server
1. Go to your GMX account https://account.gmx.com
2. Under Security Options enable 2FA (Two-factor authentication)
3. Under Security Options generate an Application-specific password
4. Home -> Email Settings -> POP3 & IMAP -> Enable access to this account via POP3 and IMAP
5. In PiAlert specify these settings:
```python
REPORT_MAIL=True
SMTP_SERVER='mail.gmx.com'
SMTP_PORT=465
SMTP_USER='gmx_email@gmx.com'
SMTP_PASS='<your Application-specific password>'
SMTP_SKIP_TLS=True
SMTP_FORCE_SSL=True
SMTP_SKIP_LOGIN=False
REPORT_FROM='gmx_email@gmx.com' # this has to be the same email as in SMTP_USER
REPORT_TO='some_target_email@gmail.com'
```
## Using the Gmail SMTP server
1. Create an app password by following the instructions from Google, you need to Enable 2FA for this to work.
[https://support.google.com/accounts/answer/185833](https://support.google.com/accounts/answer/185833)
2. Specify the following settings:
```python
REPORT_MAIL=True
SMTP_SKIP_TLS=True
SMTP_FORCE_SSL=True
SMTP_PORT=465
SMTP_SERVER='smtp.gmail.com'
SMTP_PASS='16-digit passcode from google'
REPORT_TO='some_target_email@gmail.com'
```

View File

@@ -1,15 +0,0 @@
## Use the Gmail SMTP server
1) Create an app password by following the instructions from Google, you need to Enable 2FA for this to work.
[https://support.google.com/accounts/answer/185833](https://support.google.com/accounts/answer/185833)
2) Specify the following settings:
```python
SMTP_SKIP_TLS=True
SMTP_FORCE_SSL=True
SMTP_PORT=465
SMTP_SERVER='smtp.gmail.com'
SMTP_PASS='16-digit passcode from google'
```

View File

@@ -2,12 +2,20 @@
You need to specify the network interface and the network mask. You can also configure multiple subnets and specify VLANS (see exceptions below).
> [!TIP]
> You may need to increase the time between scans `ARPSCAN_RUN_SCHD` and the timeout `ARPSCAN_RUN_TIMEOUT` settings when adding more subnets. If the timeout setting is exceeded, the scan is cancelled to prevent application hanging from rogue plugins. Check [debugging plugins](/docs/DEBUG_PLUGINS.md) for more tips.
## Examples
> [!NOTE]
> Please use the UI to configure settings as that ensures that the config file is in the correct format. Edit `pialert.conf` directly only when really necessary.
> ![settings](/front/plugins/arp_scan/arp-scan-settings.png)
* 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 -vlan=107']`
## Explanation
### Network mask
@@ -25,9 +33,12 @@ Specify the network filter (which **significantly** speeds up the scan process).
**Example value: `--interface=eth0`**
The adapter will probably be `eth0` or `eth1`. (Run `iwconfig` in the container to find your interface name(s))
The adapter will probably be `eth0` or `eth1`. (Check `System info` > `Network Hardware` or run `iwconfig` in the container to find your interface name(s))
> Run `iwconfig` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`).
![Network hardware](/docs/img/SUBNETS/system_info-network_hardware.png)
> [!TIP]
> Alterantive to `iwconfig` run `ip -o link show | awk -F': ' '!/lo|vir|docker/ {print $2}'` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`).
### VLANs

View File

@@ -1,5 +1,7 @@
### Create a simple n8n workflow
N8N can be used for more advanced conditional notification use cases. For example, you want only to get notified if two out of a specified list of devices is down. Or you can use other plugins to process the notifiations further. The below is a simple example of sending an email on a webhook.
![n8n workflow](/docs/img/WEBHOOK_N8N/n8n_workflow.png)
### Specify your email template

38
docs/WEBHOOK_SECRET.md Executable file
View File

@@ -0,0 +1,38 @@
# Webhook Secrets
## How does the signing work?
Pi.Alert will use the configured secret to create a hash signature of the request body. This SHA256-HMAC signature will appear in the `X-Webhook-Signature` header of each request to the webhook target URL. You can use the value of this header to validate the request was sent by Pi.Alert.
## Activating webhook signatures
All you need to do in order to add a signature to the request headers is to set the `WEBHOOK_SECRET` config value to a non-empty string.
## Validating webhook deliveries
There are a few things to keep in mind when validating the webhook delivery:
- Pi.Alert uses an HMAC hex digest to compute the hash
- The signature in the `X-Webhook-Signature` header always starts with `sha256=`
- The hash signature is generated using the configured `WEBHOOK_SECRET` and the request body.
- Never use a plain `==` operator. Instead, consider using a method like [`secure_compare`](https://www.rubydoc.info/gems/rack/Rack%2FUtils:secure_compare) or [`crypto.timingSafeEqual`](https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b), which performs a "constant time" string comparison to help mitigate certain timing attacks against regular equality operators, or regular loops in JIT-optimized languages.
## Testing the webhook payload validation
You can use the following secret and payload to verify that your implementation is working correctly.
`secret`: 'this is my secret'
`payload`: '{"test":"this is a test body"}'
If your implementation is correct, the signature you generated should match the following:
`signature`: bed21fcc34f98e94fd71c7edb75e51a544b4a3b38b069ebaaeb19bf4be8147e9
`X-Webhook-Signature`: sha256=bed21fcc34f98e94fd71c7edb75e51a544b4a3b38b069ebaaeb19bf4be8147e9
## More information
If you want to learn more about webhook security, take a look at [GitHub's webhook documentation](https://docs.github.com/en/webhooks/about-webhooks).
You can find examples for validating a webhook delivery [here](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries#examples).

12
docs/WEB_UI_PORT_DEBUG.md Executable file
View File

@@ -0,0 +1,12 @@
# Debugging inaccessible UI
When opening an issue please :
1. Include a screenshot of what you see when accessing `HTTP://<your rpi IP>/20211` (or your custom port)
1. [Follow steps 1, 2, 3, 4 on this page](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_TIPS.md)
1. Execute the following in the container to see the processes and their ports and submit a screenshot of the result:
1. `sudo apt-get install lsof`
1. `sudo lsof -i`
![lsof ports](/docs/img/WEB_UI_PORT_DEBUG/container_port.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

95
front/appEventsCore.php Executable file
View File

@@ -0,0 +1,95 @@
<section class="content">
<div class="nav-tabs-custom app-event-content" style="margin-bottom: 0px;">
<ul id="tabs-location" class="nav nav-tabs col-sm-2">
<li class="left-nav"><a class="col-sm-12" href="#" id="" data-toggle="tab">Events</a></li>
</ul>
<div id="tabs-content-location" class="tab-content col-sm-10">
<table class="table table-striped" id="appevents-table" data-my-dbtable="AppEvents"></table>
</div>
</div>
</section>
<script>
// show loading dialog
showSpinner()
$(document).ready(function() {
// Load JSON data from the provided URL
$.getJSON('/api/table_appevents.json', function(data) {
// Process the JSON data and generate UI dynamically
processData(data)
// hide loading dialog
hideSpinner()
});
});
function processData(data) {
// Create an object to store unique ObjectType values as app event identifiers
var appEventIdentifiers = {};
// Array to accumulate data for DataTable
var allData = [];
// Iterate through the data and generate tabs and content dynamically
$.each(data.data, function(index, item) {
// Accumulate data for DataTable
allData.push(item);
});
// Initialize DataTable for all app events
$('#appevents-table').DataTable({
data: allData,
paging: true,
lengthChange: true,
lengthMenu: [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
searching: true,
ordering: true,
info: true,
autoWidth: false,
pageLength: 25, // Set the default paging to 25
columns: [
{ data: 'DateTimeCreated', title: getString('AppEvents_DateTimeCreated') },
{ data: 'AppEventType', title: getString('AppEvents_Type') },
{ data: 'ObjectType', title: getString('AppEvents_ObjectType') },
{ data: 'ObjectPrimaryID', title: getString('AppEvents_ObjectPrimaryID') },
{ data: 'ObjectSecondaryID', title: getString('AppEvents_ObjectSecondaryID') },
{ data: 'ObjectStatus', title: getString('AppEvents_ObjectStatus') },
{ data: 'Extra', title: getString('AppEvents_Extra') },
{ data: 'ObjectPlugin', title: getString('AppEvents_Plugin') },
// Add other columns as needed
],
// Add column-specific configurations if needed
columnDefs: [
{ className: 'text-center', targets: [3] },
{ width: '80px', targets: [6] },
// ... Add other columnDefs as needed
// Full MAC
{targets: [3, 4],
'createdCell': function (td, cellData, rowData, row, col) {
if (!emptyArr.includes(cellData)){
$(td).html (createDeviceLink(cellData));
} else {
$(td).html ('');
}
} },
]
});
// Activate the first tab
$('#tabs-location li:first-child').addClass('active');
$('#tabs-content-location .tab-pane:first-child').addClass('active');
}
</script>
<!-- Datatable -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css"/>
<script src="lib/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="lib/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>

View File

@@ -723,3 +723,8 @@ input[type="password"]::-webkit-caps-lock-indicator {
top: 0.01em;
font-size: 3.25em;
}
.pa_semitransparent-panel{
background-color: #000 !important;
}

View File

@@ -173,6 +173,7 @@
@media (max-width: 767px) {
.main-header .logo {
width: 100%;
display:none;
}
.main-header .navbar {
@@ -459,35 +460,7 @@
border-right: 5px solid #606060;
}
/* -----------------------------------------------------------------------------
Spin
----------------------------------------------------------------------------- */
.pa_semitransparent-panel {
position: absolute;
width: 100%; /*calc (100% -40px);*/
height: 100%;
left: 0;
top: 0;
display: block;
opacity: 0.8;
background-color: #fff;
z-index: 99;
}
.pa_spinner {
position: absolute;
left: 0;
right: 0;
top: 20px;
margin-left: auto;
margin-right: auto;
padding: 15px;
width: 200px;
background-color: #fff;
z-index: 100;
}
/* -----------------------------------------------------------------------------
Notification float banner
@@ -612,8 +585,24 @@ height: 50px;
.infobox_label {
font-size: 16px !important;
}
/* --------------------------------------------------------- */
/* report */
/* --------------------------------------------------------- */
/*settings*/
#notificationData textarea{
width: 100%;
}
#notificationData pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
.string { color: green; }
.number { color: darkorange; }
.boolean { color: blue; }
.null { color: magenta; }
.key { color: red; }
/* --------------------------------------------------------- */
/* settings */
/* --------------------------------------------------------- */
@media (max-width: 767px) {
/* hide on mobile */
@@ -651,6 +640,52 @@ height: 50px;
display: none;
}
.settingswrap .enabled-disabled-icon
{
float: right;
}
.settings-group
{
font-size: 20px;
padding-top: 7px;
padding-bottom: 9px;
}
.overview-section .small-box .icon
{
font-size: 38px;
top:0px;
}
.overview-section
{
border: solid;
border-width: medium;
border-width: medium;
border-width: 1px;
border-radius: 15px;
margin-bottom: 3px;
}
.settings-group i{
font-size: 16px;
}
.overview-group
{
font-size: 20px;
padding-top: 7px;
padding-bottom: 9px;
}
.overview-group i{
font-size: 16px;
}
.table_row {
padding: 3px;
width:100%;
@@ -700,6 +735,18 @@ height: 50px;
/* Settings */
#settingsPage .overview-setting-value{
display:unset;
}
#settingsPage .panel-title{
/* display: inline-block; */
/* width: 120px; */
white-space: nowrap;
overflow: hidden !important;
text-overflow: ellipsis;
}
.settings_content {
padding: 10px;
/* background-color: #272c30; */
@@ -746,12 +793,12 @@ input[readonly] {
}
/* Devices */
.drp-edit
{
cursor: pointer;
#txtIconFA {
min-width: 18px;
}
.new-version
.info-icon-nav
{
top: -6px;
position: absolute;
@@ -762,7 +809,7 @@ input[readonly] {
.pointer
{
cursor:pointer;
cursor: pointer;
}
.drag
@@ -824,6 +871,7 @@ input[readonly] {
{
float:left;
display:inline;
text-align: center;
}
#networkTree .portBckgIcon
@@ -845,13 +893,13 @@ input[readonly] {
width: 25px;;
float:left;
display:inline;
text-align: center;
}
#networkTree .netCollapse
{
display: block;
position: absolute;
margin-left: 170px;
top: -3px;
font-size: large;
left: -15px;
}
@@ -911,26 +959,137 @@ input[readonly] {
/*Hidden special button*/
@media (max-width: 464px) {
@media (max-width: 365px) {
#back-button {
display: none;
}
}
@media (max-width: 432px) {
@media (max-width: 335px) {
#next-button {
display: none;
}
}
@media (max-width: 400px) {
@media (max-width: 300px) {
#reload-button {
display: none;
}
}
@media (max-width: 365px) {
@media (max-width: 300px) {
#fullscreen-button {
display: none;
}
}
@media (max-width: 500px) {
.header-server-time {
display: none;
}
}
#settingsPage .small-box .inner .card-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* -----------------------------------------------------------------------------
Spin
----------------------------------------------------------------------------- */
.pa_semitransparent-panel {
position: absolute;
width: 100%; /*calc (100% -40px);*/
height: 100%;
left: 0;
top: 0;
display: block;
opacity: 0.8;
background-color: #fff;
z-index: 99;
}
.pa_spinner {
position: fixed;
left: 0;
right: 0;
top: 100px;
margin-left: auto;
margin-right: auto;
padding: 15px;
width: 200px;
background-color: #fff;
z-index: 100;
}
#loadingSpinner
{
z-index: 100;
}
/* Multi-edit adjustements */
.box-header
{
min-height: 55px;
}
.red-hover-border:hover
{
border-color: red !important;
border-width: 1px;
border-style: solid;
}
.red-hover-background:hover
{
background-color: red !important;
}
#multi-edit-form .form-group
{
height: 45px;
}
.pia-top-left-logo
{
height:50px;
}
/* -----------------------------------------------------------------------------
Floating edit button
----------------------------------------------------------------------------- */
#multiEditPlc
{
position: fixed;
bottom: 50px;
right: 0px;
z-index: 10;
}
table.dataTable tbody > tr.selected
{
color:red;
}
/* -----------------------------------------------------------------------------
Donations
----------------------------------------------------------------------------- */
.donations .box
{
padding:15px;
margin-bottom: 0px;
}
.donations .box-header
{
color:15px;
}
.donations h3
{
margin-top: 10px;
}

View File

@@ -196,9 +196,9 @@
</label>
<div class="col-sm-9">
<div class="input-group">
<span class="input-group-addon"><i class="fa" id="txtIconFA" onclick="editDrp('txtIcon');"></i></span>
<input class="form-control" id="txtIcon" type="text" value="--">
<span class="input-group-addon" title='<?= lang('DevDetail_button_OverwriteIcons_Tooltip');?>'><i class="fa fa-copy pointer" onclick="askOverwriteIconType();"></i></span>
<span class="input-group-addon"><i class="fa fa-pencil pointer" onclick="editDrp('txtIcon');"></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>
@@ -325,7 +325,7 @@
<div class="form-group" title="<?= lang('DevDetail_Network_Node_hover');?>">
<label class="col-sm-3 control-label"><?= lang('DevDetail_MainInfo_Network');?></label>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group parentNetworkNode">
<input class="form-control" id="txtNetworkNodeMac" type="text" value="--">
<span class="input-group-addon"><i title="<?= lang('DevDetail_GoToNetworkNode');?>" class="fa fa-square-up-right pointer" onclick="goToNetworkNode('txtNetworkNodeMac');"></i></span>
@@ -412,7 +412,7 @@
</div>
<!-- New Device -->
<div class="form-group">
<div class="form-group" title="<?= lang('DevDetail_EveandAl_NewDevice_Tooltip');?>">
<label class="col-sm-5 control-label"><?= lang('DevDetail_EveandAl_NewDevice');?>:</label>
<div class="col-sm-7" style="padding-top:6px;">
<input class="checkbox orange hidden" id="chkNewDevice" type="checkbox">
@@ -542,14 +542,6 @@
<!-- tab page 3 ------------------------------------------------------------ -->
<div class="tab-pane fade table-responsive" id="panPresence">
<!-- spinner -->
<div id="loading" style="display: none">
<div class="pa_semitransparent-panel"></div>
<div class="panel panel-default pa_spinner">
<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td><td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td></table>
</div>
</div>
<!-- Calendar -->
<div id="calendar">
</div>
@@ -652,14 +644,15 @@ if ($ENABLED_DARKMODE === True) {
// ------------------------------------------------------------
function getDevicesList()
{
// Read cache
devicesList = getCache('devicesList');
// Read cache (skip cookie expiry check)
devicesList = getCache('devicesListAll_JSON', true);
if (devicesList != '') {
devicesList = JSON.parse (devicesList);
} else {
devicesList = [];
}
return devicesList;
}
@@ -757,6 +750,11 @@ function main () {
}
});
// Show device icon as it changes
$('#txtIcon').on('change input', function() {
$('#txtIconFA').removeClass().addClass(`fa fa-${$(this).val()} pointer`)
});
});
});
@@ -844,6 +842,8 @@ function initializeCombo (dropdownId, queryAction, txtDataField, useCache) {
{
// get data from server
$.get('php/server/devices.php?action='+queryAction, function(data) {
// console.log(data)
var listData = JSON.parse(data);
var order = 1;
@@ -1136,9 +1136,9 @@ function initializeCalendar () {
loading: function( isLoading, view ) {
if (isLoading) {
$('#loading').show();
showSpinner()
} else {
$('#loading').hide();
hideSpinner()
}
}
@@ -1288,23 +1288,34 @@ function getDeviceData (readAllData=false) {
devicesList = getDevicesList();
// handle empty dev_Network_Node_MAC_ADDR
networkParentMac = deviceData['dev_Network_Node_MAC_ADDR']
if(networkParentMac)
{
networkParentMacName = getDeviceDataByMacAddress(deviceData['dev_Network_Node_MAC_ADDR'], "dev_Name")
} else
{
networkParentMacName = '--'
}
$('#txtMAC').val (deviceData['dev_MAC']);
$('#txtName').val (deviceData['dev_Name']);
$('#txtOwner').val (deviceData['dev_Owner']);
$('#txtDeviceType').val (deviceData['dev_DeviceType']);
$('#txtVendor').val (deviceData['dev_Vendor']);
$('#txtIcon').val (initDefault(deviceData['dev_Icon'], 'laptop'));
$('#txtIcon').trigger('change')
if (deviceData['dev_Favorite'] == 1) {$('#chkFavorite').iCheck('check');} else {$('#chkFavorite').iCheck('uncheck');}
$('#txtGroup').val (deviceData['dev_Group']);
$('#txtLocation').val (deviceData['dev_Location']);
$('#txtComments').val (deviceData['dev_Comments']);
$('#txtNetworkNodeMac').val ( getDeviceDataByMacAddress(deviceData['dev_Network_Node_MAC_ADDR'], "dev_Name")) ;
$('#txtNetworkNodeMac').val ( networkParentMacName) ;
$('#txtNetworkNodeMac').attr ('data-mynodemac', deviceData['dev_Network_Node_MAC_ADDR']);
$('#txtNetworkPort').val (deviceData['dev_Network_Node_port']);
// disabling network node configuration if root Internet node
$('#txtNetworkNodeMac').prop('readonly', mac == 'Internet' );
$('#txtNetworkPort').prop('readonly', mac == 'Internet' );
toggleNetworkConfiguration(mac == 'Internet')
$('#txtFirstConnection').val (deviceData['dev_FirstConnection']);
$('#txtLastConnection').val (deviceData['dev_LastConnection']);
@@ -1327,6 +1338,7 @@ function getDeviceData (readAllData=false) {
// Check if device is part of the devicesList
pos = devicesList.findIndex(item => item.rowid == deviceData['rowid']);
if (pos == -1) {
devicesList.push({"rowid" : deviceData['rowid'], "mac" : deviceData['dev_MAC'], "name": deviceData['dev_Name'], "type": deviceData['dev_DeviceType']});
pos=0;
@@ -1400,14 +1412,12 @@ function performSwitch(direction)
// get new mac from the devicesList. Don't change to the commented out line below, the mac query string in the URL isn't updated yet!
// mac = params.mac;
mac = devicesList[pos].mac.toString();
mac = devicesList[pos].dev_MAC.toString();
setCache("piaDeviceDetailsMac", mac);
getDeviceData (true);
// reload current tab
reloadTab()
}
// -----------------------------------------------------------------------------
@@ -1461,6 +1471,9 @@ function setDeviceData (direction='', refreshCallback='') {
window.onbeforeunload = null;
somethingChanged = false;
// refresh API
updateApi()
// Callback fuction
if (typeof refreshCallback == 'function') {
refreshCallback(direction);
@@ -1468,6 +1481,9 @@ function setDeviceData (direction='', refreshCallback='') {
});
}
// -----------------------------------------------------------------------------
function askSkipNotifications () {
// Check MAC
@@ -1634,36 +1650,10 @@ function deleteDevice () {
// Deactivate controls
$('#panDetails :input').attr('disabled', true);
// refresh API
updateApi()
}
// -----------------------------------------------------------------------------
function askDeleteDevice () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device
showModalWarning ('Delete Device', 'Are you sure you want to delete this device?<br>(maybe you prefer to archive it)',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteDevice');
}
// -----------------------------------------------------------------------------
function deleteDevice () {
// Check MAC
if (mac == '') {
return;
}
// Delete device
$.get('php/server/devices.php?action=deleteDevice&mac='+ mac, function(msg) {
showMessage (msg);
});
// Deactivate controls
$('#panDetails :input').attr('disabled', true);
}
// -----------------------------------------------------------------------------
function getSessionsPresenceEvents () {
@@ -1710,6 +1700,7 @@ function setTextValue (textElement, textValue) {
$('#'+textElement).attr ('data-myvalue', textValue);
$('#'+textElement).val (textValue);
}
$('#'+textElement).trigger('change')
}
// -----------------------------------------------------------------------------
@@ -1804,15 +1795,27 @@ window.onload = function async()
{
initializeTabsNew();
reloadTab();
}
//-----------------------------------------------------------------------------------
function reloadTab()
// Disables network configuration for the root node
function toggleNetworkConfiguration(disable)
{
// tab loaded without switching
$('#txtNetworkNodeMac').prop('readonly', true ); // disable direct input as should only be selected via the dropdown
if(disable)
{
// $('#txtNetworkNodeMac').val(getString('Network_Root_Unconfigurable'));
// $('#txtNetworkPort').val(getString('Network_Root_Unconfigurable'));
$('#txtNetworkPort').prop('readonly', true );
$('.parentNetworkNode .input-group-btn').hide();
}
else
{
$('#txtNetworkPort').prop('readonly', false );
$('.parentNetworkNode .input-group-btn').show();
}
}
</script>

View File

@@ -129,7 +129,7 @@
beforeSend: function() { $('#scanoutput').addClass("ajax_scripts_loading"); },
complete: function() { $('#scanoutput').removeClass("ajax_scripts_loading"); },
success: function(data, textStatus) {
console.log(data);
// console.log(data);
$("#scanoutput").html(data);
}
})

View File

@@ -42,9 +42,9 @@
<!-- top small box 1 ------------------------------------------------------- -->
<div class="row">
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('all');">
<a href="#" onclick="javascript: initializeDatatable('my');">
<div class="small-box bg-aqua">
<div class="inner"><h3 id="devicesAll"> -- </h3>
<div class="inner"><h3 id="devicesMy"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_AllDevices');?></p>
</div>
<div class="icon"><i class="fa fa-laptop text-aqua-40"></i></div>
@@ -54,7 +54,7 @@
<!-- top small box 2 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('connected');">
<a href="#" onclick="javascript: initializeDatatable('connected');">
<div class="small-box bg-green">
<div class="inner"><h3 id="devicesConnected"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_Connected');?></p>
@@ -66,7 +66,7 @@
<!-- top small box 3 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('favorites');">
<a href="#" onclick="javascript: initializeDatatable('favorites');">
<div class="small-box bg-yellow">
<div class="inner"><h3 id="devicesFavorites"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_Favorites');?></p>
@@ -78,7 +78,7 @@
<!-- top small box 4 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('new');">
<a href="#" onclick="javascript: initializeDatatable('new');">
<div class="small-box bg-yellow">
<div class="inner"><h3 id="devicesNew"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_NewDevices');?></p>
@@ -90,7 +90,7 @@
<!-- top small box 5 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('down');">
<a href="#" onclick="javascript: initializeDatatable('down');">
<div class="small-box bg-red">
<div class="inner"><h3 id="devicesDown"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_DownAlerts');?></p>
@@ -102,7 +102,7 @@
<!-- top small box 6 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('archived');">
<a href="#" onclick="javascript: initializeDatatable('archived');">
<div class="small-box bg-gray top_small_box_gray_text">
<div class="inner"><h3 id="devicesArchived"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_Archived');?></p>
@@ -148,7 +148,9 @@
<!-- box-header -->
<div class="box-header">
<h3 id="tableDevicesTitle" class="box-title text-gray">Devices</h3>
<div class=" col-md-10 ">
<h3 id="tableDevicesTitle" class="box-title text-gray "></h3>
</div>
</div>
<!-- table -->
@@ -173,6 +175,7 @@
<!-- ----------------------------------------------------------------------- -->
</section>
<!-- /.content -->
<div id="multiEditPlc" class="col-md-2"></div>
</div>
<!-- /.content-wrapper -->
@@ -186,9 +189,10 @@
<!-- ----------------------------------------------------------------------- -->
<!-- Datatable -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net/css/select.dataTables.min.css">
<script src="lib/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="lib/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
<script src="lib/AdminLTE/bower_components/datatables.net/js/dataTables.select.min.js"></script>
<!-- page script ----------------------------------------------------------- -->
<script>
@@ -198,9 +202,32 @@
var tableRows = 10;
var tableOrder = [[3,'desc'], [0,'asc']];
var tableColumnHide = [];
var columnsStr = '[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]';
var tableColumnOrder = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18];
var tableColumnVisible = tableColumnOrder;
//initialize the table headers in the correct order
var headersDefaultOrder = [
getString('Device_TableHead_Name'),
getString('Device_TableHead_Owner'),
getString('Device_TableHead_Type'),
getString('Device_TableHead_Icon'),
getString('Device_TableHead_Favorite'),
getString('Device_TableHead_Group'),
getString('Device_TableHead_FirstSession'),
getString('Device_TableHead_LastSession'),
getString('Device_TableHead_LastIP'),
getString('Device_TableHead_MAC'),
getString('Device_TableHead_Status'),
getString('Device_TableHead_MAC_full'),
getString('Device_TableHead_LastIPOrder'),
getString('Device_TableHead_Rowid'),
getString('Device_TableHead_Parent_MAC'),
getString('Device_TableHead_Connected_Devices'),
getString('Device_TableHead_Location'),
getString('Device_TableHead_Vendor'),
getString('Device_TableHead_Port')
];
// Read parameters & Initialize components
main();
@@ -209,6 +236,8 @@
// -----------------------------------------------------------------------------
function main () {
handleLoadingDialog()
// get from cookie if available (need to use decodeURI as saved as part of URI in PHP)
cookieColumnsVisibleStr = decodeURI(getCookie("Front_Devices_Columns_Visible")).replaceAll('%2C',',')
@@ -217,6 +246,8 @@ function main () {
// get visible columns
$.get('php/server/parameters.php?action=get&expireMinutes=525600&defaultValue='+defaultValue+'&parameter=Front_Devices_Columns_Visible&skipcache', function(data) {
handle_locked_DB(data)
// save which columns are in the Devices page visible
tableColumnVisible = numberArrayFromString(data);
@@ -228,32 +259,11 @@ function main () {
// get the custom order specified by the user
$.get('php/server/parameters.php?action=get&expireMinutes=525600&defaultValue='+defaultValue+'&parameter=Front_Devices_Columns_Order&skipcache', function(data) {
handle_locked_DB(data)
// 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');?>',
'<?= lang('Device_TableHead_Type');?>',
'<?= lang('Device_TableHead_Icon');?>',
'<?= lang('Device_TableHead_Favorite');?>',
'<?= lang('Device_TableHead_Group');?>',
'<?= lang('Device_TableHead_FirstSession');?>',
'<?= lang('Device_TableHead_LastSession');?>',
'<?= lang('Device_TableHead_LastIP');?>',
'<?= lang('Device_TableHead_MAC');?>',
'<?= lang('Device_TableHead_Status');?>',
'<?= lang('Device_TableHead_MAC_full');?>',
'<?= lang('Device_TableHead_LastIPOrder');?>',
'<?= lang('Device_TableHead_Rowid');?>',
'<?= lang('Device_TableHead_Parent_MAC');?>',
'<?= lang('Device_TableHead_Connected_Devices');?>',
'<?= lang('Device_TableHead_Location');?>',
'<?= lang('Device_TableHead_Vendor');?>'
];
html = '';
for(index = 0; index < tableColumnOrder.length; index++)
@@ -285,11 +295,15 @@ function main () {
}
// Initialize components with parameters
initializeDatatable();
// query data
getDevicesTotals();
getDevicesList (deviceStatus);
initializeDatatable(getUrlAnchor('my'));
// check if dat outdated and show spinner if so
handleLoadingDialog()
});
});
});
@@ -297,8 +311,6 @@ function main () {
}
// -----------------------------------------------------------------------------
var tableColumnHide = [];
// mapping the default order to the user specified one
function mapIndx(oldIndex)
{
@@ -311,9 +323,134 @@ function mapIndx(oldIndex)
}
}
// -----------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Query total numbers of Devices by status
//------------------------------------------------------------------------------
function getDevicesTotals(devicesData) {
let resultJSON = "";
if (getCache("getDevicesTotals") !== "") {
resultJSON = getCache("getDevicesTotals");
} else {
// combined query
const devices = filterDataByStatus(devicesData, 'my');
const connectedDevices = filterDataByStatus(devicesData, 'connected');
const favoritesDevices = filterDataByStatus(devicesData, 'favorites');
const newDevices = filterDataByStatus(devicesData, 'new');
const downDevices = filterDataByStatus(devicesData, 'down');
const archivedDevices = filterDataByStatus(devicesData, 'archived');
$('#devicesMy').html (devices.length);
$('#devicesConnected').html (connectedDevices.length);
$('#devicesFavorites').html (favoritesDevices.length);
$('#devicesNew').html (newDevices.length);
$('#devicesDown').html (downDevices.length);
$('#devicesArchived').html (archivedDevices.length);
// save to cache
setCache("getDevicesTotals", resultJSON);
}
console.log(resultJSON);
}
// -----------------------------------------------------------------------------
// Define a function to filter data based on deviceStatus
function filterDataByStatus(data, status) {
return data.filter(function(item) {
switch (status) {
case 'my':
to_display = getSetting('UI_MY_DEVICES');
let result = true;
if (!to_display.includes('down') && item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0) {
result = false;
} else if (!to_display.includes('new') && item.dev_NewDevice === 1) {
result = false;
} else if (!to_display.includes('archived') && item.dev_Archived === 1) {
result = false;
} else if (!to_display.includes('offline') && item.dev_PresentLastScan === 0) {
result = false;
} else if (!to_display.includes('online') && item.dev_PresentLastScan === 1) {
result = false;
}
return result; // Include all items for 'my' status
case 'connected':
return item.dev_PresentLastScan === 1;
case 'favorites':
return item.dev_Favorite === 1;
case 'new':
return item.dev_NewDevice === 1;
case 'down':
return (item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0) || item.dev_PresentLastScan === 0;
case 'archived':
return item.dev_Archived === 1;
default:
return true; // Include all items for unknown statuses
}
});
}
// -----------------------------------------------------------------------------
function getDeviceStatus(item)
{
if(item.dev_NewDevice === 1)
{
return 'New';
}
else if(item.dev_PresentLastScan === 1)
{
return 'On-line';
}
else if(item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0)
{
return 'Down';
}
else if(item.dev_Archived === 1)
{
return 'Archived';
}
else if(item.dev_PresentLastScan === 0)
{
return 'Off-line';
}
return "Unknown status"
}
// -----------------------------------------------------------------------------
function initializeDatatable (status) {
if(!status)
{
status = 'my'
}
// Save status selected
deviceStatus = status;
// Define color & title for the status selected
switch (deviceStatus) {
case 'my': tableTitle = getString('Device_Shortcut_AllDevices'); color = 'aqua'; break;
case 'connected': tableTitle = getString('Device_Shortcut_Connected'); color = 'green'; break;
case 'favorites': tableTitle = getString('Device_Shortcut_Favorites'); color = 'yellow'; break;
case 'new': tableTitle = getString('Device_Shortcut_NewDevices'); color = 'yellow'; break;
case 'down': tableTitle = getString('Device_Shortcut_DownAlerts'); color = 'red'; break;
case 'archived': tableTitle = getString('Device_Shortcut_Archived'); color = 'gray'; break;
default: tableTitle = getString('Device_Shortcut_Devices'); color = 'gray'; break;
}
// Set title and color
$('#tableDevicesTitle')[0].className = 'box-title text-'+ color;
$('#tableDevicesBox')[0].className = 'box box-'+ color;
$('#tableDevicesTitle').html (tableTitle);
function initializeDatatable () {
for(i = 0; i < tableColumnOrder.length; i++)
{
// hide this column if not in the tableColumnVisible variable (we need to keep the MAC address (index 11) for functionality reasons)
@@ -323,11 +460,68 @@ function initializeDatatable () {
}
}
$.get('api/table_devices.json?nocache=' + Date.now(), function(result) {
// query data
getDevicesTotals(result.data);
// Filter the data based on deviceStatus
var filteredData = filterDataByStatus(result.data, deviceStatus);
// Convert JSON data into the desired format
var dataArray = {
data: filteredData.map(function(item) {
var originalRow = [
item.dev_Name || "",
item.dev_Owner || "",
item.dev_DeviceType || "",
item.dev_Icon || "",
item.dev_Favorite || "",
item.dev_Group || "",
// ---
item.dev_FirstConnection || "",
item.dev_LastConnection || "",
item.dev_LastIP || "",
(isRandomMAC(item.dev_MAC)) || "", // Check if randomized MAC
getDeviceStatus(item) || "",
item.dev_MAC || "", // hidden
formatIPlong(item.dev_LastIP) || "", // IP orderable
item.rowid || "",
item.dev_Network_Node_MAC_ADDR || "",
getNumberOfChildren(item.dev_MAC, result.data) || 0,
item.dev_Location || "",
item.dev_Vendor || "",
item.dev_Network_Node_port || 0
];
var newRow = [];
// reorder data based on user-defined columns order
for (index = 0; index < tableColumnOrder.length; index++) {
newRow.push(originalRow[tableColumnOrder[index]]);
}
return newRow;
})
};
// TODO displayed columns
// Check if the DataTable already exists
if ($.fn.dataTable.isDataTable('#tableDevices')) {
// The DataTable exists, so destroy it
var table = $('#tableDevices').DataTable();
table.clear().destroy();
}
var table=
$('#tableDevices').DataTable({
'data' : dataArray["data"],
'paging' : true,
'lengthChange' : true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, '<?= lang('Device_Tablelenght_all');?>']],
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]],
'searching' : true,
'ordering' : true,
@@ -337,25 +531,36 @@ function initializeDatatable () {
// Parameters
'pageLength' : tableRows,
'order' : tableOrder,
// 'order' : [[3,'desc'], [0,'asc']],
'select' : true, // Enable selection
'columnDefs' : [
{visible: false, targets: tableColumnHide },
{className: 'text-center', targets: [mapIndx(3), mapIndx(4), mapIndx(9), mapIndx(10), mapIndx(15)] },
{className: 'text-center', targets: [mapIndx(3), mapIndx(4), mapIndx(9), mapIndx(10), mapIndx(15), mapIndx(18)] },
{width: '80px', targets: [mapIndx(6), mapIndx(7), mapIndx(15)] },
{width: '30px', targets: [mapIndx(10), mapIndx(13)] },
{width: '30px', targets: [mapIndx(10), mapIndx(13), mapIndx(18)] },
{orderData: [mapIndx(12)], targets: mapIndx(8) },
// Device Name
{targets: [mapIndx(0)],
'createdCell': function (td, cellData, rowData, row, col) {
// console.log(cellData)
$(td).html ('<b class="anonymizeDev"><a href="deviceDetails.php?mac='+ rowData[mapIndx(11)] +'" class="">'+ cellData +'</a></b>');
} },
// Connected Devices
{targets: [mapIndx(15)],
'createdCell': function (td, cellData, rowData, row, col) {
// check if this is a network device
if(getSetting("NETWORK_DEVICE_TYPES").includes(`'${rowData[mapIndx(2)]}'`) )
{
$(td).html ('<b><a href="./network.php?mac='+ rowData[mapIndx(11)] +'" class="">'+ cellData +'</a></b>');
}
else
{
$(td).html (`<i class="fa-solid fa-xmark" title="${getString("Device_Table_Not_Network_Device")}"></i>`)
}
} },
// Icon
@@ -407,6 +612,7 @@ function initializeDatatable () {
// Random MAC
{targets: [mapIndx(9)],
'createdCell': function (td, cellData, rowData, row, col) {
// console.log(cellData)
if (cellData == 1){
$(td).html ('<i data-toggle="tooltip" data-placement="right" title="Random MAC" style="font-size: 16px;" class="text-yellow glyphicon glyphicon-random"></i>');
} else {
@@ -417,6 +623,8 @@ function initializeDatatable () {
// Status color
{targets: [mapIndx(10)],
'createdCell': function (td, cellData, rowData, row, col) {
// console.log(cellData)
switch (cellData) {
case 'Down': color='red'; break;
case 'New': color='yellow'; break;
@@ -459,6 +667,30 @@ function initializeDatatable () {
setCache ('devicesList', getDevicesFromTable(table) );
} );
// add multi-edit button
$('#multiEditPlc').append(
`<button type="submit" id="multiEdit" class="btn btn-primary" style="display:none" onclick="multiEditDevices();">
<i class="fa fa-pencil pointer" ></i> ${getString("Device_MultiEdit")}
</button>`)
// Event listener for row selection in DataTable
$('#tableDevices').on('click', 'tr', function (e) {
setTimeout(function(){
// Check if any row is selected
var anyRowSelected = $('#tableDevices tr.selected').length > 0;
console.log(anyRowSelected);
// Toggle visibility of element with ID 'multiEdit'
$('#multiEdit').toggle(anyRowSelected);
}, 200);
});
});
};
@@ -490,58 +722,91 @@ function getDevicesFromTable(table)
return JSON.stringify (result)
}
// -----------------------------------------------------------------------------
function getDevicesTotals () {
// stop timer
stopTimerRefreshData();
// get totals and put in boxes
$.get('php/server/devices.php?action=getDevicesTotals', function(data) {
var totalsDevices = JSON.parse(data);
$('#devicesAll').html (totalsDevices[0].toLocaleString());
$('#devicesConnected').html (totalsDevices[1].toLocaleString());
$('#devicesFavorites').html (totalsDevices[2].toLocaleString());
$('#devicesNew').html (totalsDevices[3].toLocaleString());
$('#devicesDown').html (totalsDevices[4].toLocaleString());
$('#devicesArchived').html (totalsDevices[5].toLocaleString());
// Timer for refresh data
newTimerRefreshData (getDevicesTotals);
} );
}
// -----------------------------------------------------------------------------
function getDeviceColumns () {
function getNumberOfChildren(mac, devices)
{
childrenCount = 0;
}
// -----------------------------------------------------------------------------
function getDevicesList (status) {
// Save status selected
deviceStatus = status;
$.each(devices, function(index, dev) {
// Define color & title for the status selected
switch (deviceStatus) {
case 'all': tableTitle = '<?= lang('Device_Shortcut_AllDevices');?>'; color = 'aqua'; break;
case 'connected': tableTitle = '<?= lang('Device_Shortcut_Connected');?>'; color = 'green'; break;
case 'favorites': tableTitle = '<?= lang('Device_Shortcut_Favorites');?>'; color = 'yellow'; break;
case 'new': tableTitle = '<?= lang('Device_Shortcut_NewDevices');?>'; color = 'yellow'; break;
case 'down': tableTitle = '<?= lang('Device_Shortcut_DownAlerts');?>'; color = 'red'; break;
case 'archived': tableTitle = '<?= lang('Device_Shortcut_Archived');?>'; color = 'gray'; break;
default: tableTitle = '<?= lang('Device_Shortcut_Devices');?>'; color = 'gray'; break;
if(dev.dev_Network_Node_MAC_ADDR != null && dev.dev_Network_Node_MAC_ADDR.trim() == mac.trim())
{
childrenCount++;
}
// Set title and color
$('#tableDevicesTitle')[0].className = 'box-title text-'+ color;
$('#tableDevicesBox')[0].className = 'box box-'+ color;
$('#tableDevicesTitle').html (tableTitle);
});
// Define new datasource URL and reload
$('#tableDevices').DataTable().ajax.url(
'php/server/devices.php?action=getDevicesList&status=' + deviceStatus).load();
};
return childrenCount;
}
// -----------------------------------------------------------------------------
function handleLoadingDialog()
{
$.get('log/execution_queue.log?nocache=' + Date.now(), function(data) {
if(data.includes("update_api|devices"))
{
showSpinner("devices_old")
setTimeout("handleLoadingDialog()", 1000);
} else if ($("#loadingSpinner").is(":visible"))
{
hideSpinner();
location.reload();
}
})
}
// -----------------------------------------------------------------------------
// Function collects selected devices in the DataTable and redirects the user to
// the Miantenance section with a 'macs' query string identifying selected devices
function multiEditDevices()
{
rows = $('#tableDevices')[0].rows
// Initialize an empty array to store selected rows
var selectedRows = [];
console.log($('#tableDevices')[0].rows);
// Loop through each row in the HTML collection
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
// Check if the row has the 'selected' class
if (row.classList.contains('selected')) {
// If selected, push the row's data to the selectedRows array
selectedRows.push(row);
}
}
// Now, selectedRows contains all selected rows
console.log(selectedRows);
var devicesDataTableData = $('#tableDevices').dataTable().fnGetData();
var selectedDevices = [];
for (var i = 0; i < selectedRows.length; i++) {
selectedDevices.push(devicesDataTableData[selectedRows[i]._DT_RowIndex]);
}
// Now, selectedDevices contains all selected devices
console.log(selectedDevices);
macs = ""
for (var i = 0; i < selectedDevices.length; i++) {
macs += selectedDevices[i][mapIndx(11)] + ","; // [11] == MAC
}
// redirect to the Maintenance section
window.location.href = window.location.origin + '/maintenance.php#tab_multiEdit?macs=' + macs.slice(0, -1);
}
</script>
<script src="js/pialert_common.js"></script>

View File

@@ -1,22 +1,70 @@
<?php
require 'php/templates/header.php';
?>
<script src="js/pialert_common.js"></script>
<div id="donationsPage" class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
<i class="fa fa-heart"></i>
</h1>
</section>
<div id="settingsPage" class="content-wrapper">
<p>
<!-- Main content ---------------------------------------------------------- -->
<section class="content donations">
<div id="donationsText" class="box box-solid"></div>
<div class="content-header">
<h3 class="box-title " id="donationsPlatforms"></h3>
</div>
<div class="box box-solid">
<div class="box-body">
<div class="col-sm-2">
<a target="_blank" href="https://github.com/sponsors/jokob-sk">
<img alt="Sponsor Me on GitHub" src="https://i.imgur.com/X6p5ACK.png" width="150px">
</a>
</div>
<div class="col-sm-2">
<a target="_blank" href="https://www.buymeacoffee.com/jokobsk">
<img alt="Buy Me A Coffee" src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" width="117px" height="30px">
</a>
</div>
<div class="col-sm-2">
<a target="_blank" href="https://www.patreon.com/user?u=84385063">
<img alt="Support me on patreon" src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Patreon_logo_with_wordmark.svg/512px-Patreon_logo_with_wordmark.svg.png" width="117px">
</a>
</p>
<p>
BTC: 1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM
</p>
</div>
</div>
</div>
</div>
<div class="content-header">
<h3 class="box-title " id="donationsOthers"></h3>
</div>
<div class="box box-solid">
<div class="box-body">
<div class="col-sm-12">
<ul>
<li>Bitcoin: <code>1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM</code></li>
<li>Ethereum: <code>0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7</code></li>
</ul>
</div>
</div>
</div>
<div>
</section>
</div> <!-- End of class="content-wrapper" -->
<script>
function init()
{
$("#donationsText").html(getString("Donations_Text"))
$("#pageTitle").append(getString("Donations_Title"))
$("#donationsPlatforms").append(getString("Donations_Platforms"))
$("#donationsOthers").append(getString("Donations_Others"))
}
init();
</script>
<?php
require 'php/templates/footer.php';
?>

View File

@@ -99,8 +99,8 @@ if ($ENABLED_DARKMODE === True) {
$BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/boxed-bg-dark.png\');"';
} else { $BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/background.png\');"';}
?>
<link rel="stylesheet" href="/front/css/offline-font.css">
<!-- /var/www/html/pialert/css/offline-font.css -->
<link rel="stylesheet" href="/css/offline-font.css">
</head>
<body class="hold-transition login-page">
<div class="login-box login-custom">

18
front/js/db_methods.js Executable file
View File

@@ -0,0 +1,18 @@
// -----------------------------------------------------------------------------
// General utilities to interact with teh database
// -----------------------------------------------------------------------------
// Read data and place intotarget location, callback processies the results
function readData(sqlQuery, processDataCallback, valuesArray, targetLocation) {
var apiUrl = `php/server/dbHelper.php?action=read&rawSql=${encodeURIComponent(sqlQuery)}`;
$.get(apiUrl, function(data) {
// Process the JSON data using the provided callback function
data = JSON.parse(data)
var htmlResult = processDataCallback(data, valuesArray);
// Place the resulting HTML into the specified placeholder div
$("#" + targetLocation).replaceWith(htmlResult);
});
}

View File

@@ -1,10 +1,10 @@
function pia_draw_graph_online_history(pia_js_graph_online_history_time, pia_js_graph_online_history_ondev, pia_js_graph_online_history_dodev, pia_js_graph_online_history_ardev) {
var xValues = pia_js_graph_online_history_time;
new Chart("OnlineChart", {
type: "bar",
data: {
labels: xValues,
datasets: [{
// alert("dev presence")
// Data object for online status
onlineData = {
label: 'Online',
data: pia_js_graph_online_history_ondev,
borderColor: "rgba(0, 166, 89)",
@@ -13,19 +13,58 @@ function pia_draw_graph_online_history(pia_js_graph_online_history_time, pia_js_
pointStyle: 'circle',
pointRadius: 3,
pointHoverRadius: 3
}, {
};
// Data object for offline status
offlineData = {
label: 'Offline/Down',
data: pia_js_graph_online_history_dodev,
borderColor: "rgba(222, 74, 56)",
fill: true,
backgroundColor: "rgba(222, 74, 56, .6)",
}, {
};
// Data object for archived status
archivedData = {
label: 'Archived',
data: pia_js_graph_online_history_ardev,
borderColor: "rgba(220,220,220)",
fill: true,
backgroundColor: "rgba(220,220,220, .6)",
}]
};
// Array to store datasets
datasets = [];
// Get UI presence settings
showStats = getSetting("UI_PRESENCE");
// Check if 'online' status should be displayed
if(showStats.includes("online"))
{
datasets.push(onlineData); // Add onlineData to datasets array
}
// Check if 'offline' status should be displayed
if(showStats.includes("offline"))
{
datasets.push(offlineData); // Add offlineData to datasets array
}
// Check if 'archived' status should be displayed
if(showStats.includes("archived"))
{
datasets.push(archivedData); // Add archivedData to datasets array
}
new Chart("OnlineChart", {
type: "bar",
scaleIntegersOnly: true,
data: {
labels: xValues,
datasets: datasets
},
options: {
legend: {

View File

@@ -1,14 +1,13 @@
function handleVersion(){
//--------------------------------------------------------------
// Handle the UI changes to show or hide notifications about a new version
function versionUpdateUI(){
release_timestamp = getCookie("release_timestamp")
isNewVersion = getCookie("isNewVersion")
if(release_timestamp != "")
{
build_timestamp = parseInt($('#version').attr("data-build-time").match( /\d+/g ).join(''))
console.log(isNewVersion)
// if the release_timestamp is older by 10 min or more as the build timestamp then there is a new release available
if(release_timestamp > build_timestamp + 600 )
if(isNewVersion != "false")
{
console.log("New release!")
// handling the navigation menu icon
@@ -27,54 +26,26 @@ function handleVersion(){
{
$(maintenanceDiv).attr("class", $(maintenanceDiv).attr("class").replace("myhidden", ""))
}
}
}
}
//--------------------------------------------------------------
//--------------------------------------------------------------
// Checks if a new version is available via the global app_state.json
function checkIfNewVersionAvailable()
{
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
function getVersion()
{
release_timestamp = getCookie("release_timestamp")
release_timestampNum = Number(release_timestamp)
// logging
console.log(`Latest release in cookie: ${new Date(release_timestampNum*1000)}`)
// no cached value available
if(release_timestamp == "")
{
$.get('https://api.github.com/repos/jokob-sk/Pi.Alert/releases').done(function(response) {
// Handle successful response
var releases = response;
console.log(response)
if(releases.length > 0)
{
release_datetime = releases[0].published_at; // get latest release
release_timestamp = new Date(release_datetime).getTime() / 1000;
// console.log(appState["isNewVersionChecked"])
// console.log(appState["isNewVersion"])
// cache value
setCookie("release_timestamp", release_timestamp, 30);
setCookie("isNewVersion", appState["isNewVersion"], 30);
setCookie("isNewVersionChecked", appState["isNewVersionChecked"], 30);
handleVersion();
}
}).fail(function(jqXHR, textStatus, errorThrown) {
$('.version').append(`<p>Github API: ${errorThrown} (${jqXHR.status}), ${jqXHR.responseJSON.message}</p>`)
});
} else
{
// cache is available, just call the handler
handleVersion()
}
}
versionUpdateUI();
})
}
// handle the dispaly of the NEW icon
getVersion()
checkIfNewVersionAvailable()

View File

@@ -26,13 +26,17 @@ var settingsJSON = {}
function getCache(key, noCookie = false)
{
// check cache
if(sessionStorage.getItem(key))
cachedValue = localStorage.getItem(key)
// console.log(cachedValue);
if(cachedValue)
{
// check if not expired
if(noCookie || getCookie(key + '_session_expiry') != "")
{
return sessionStorage.getItem(key);
}
// // check if not expired
// if(noCookie || getCookie(key + '_session_expiry') != "")
// {
return cachedValue;
// }
}
return "";
@@ -41,13 +45,13 @@ function getCache(key, noCookie = false)
// -----------------------------------------------------------------------------
function setCache(key, data, expirationMinutes='')
{
sessionStorage.setItem(key, data);
localStorage.setItem(key, data);
// create cookie if expiration set to handle refresh of data
if (expirationMinutes != '')
{
setCookie (key + '_session_expiry', 'OK', expirationMinutes='')
}
// // create cookie if expiration set to handle refresh of data
// if (expirationMinutes != '')
// {
// setCookie ('cache_session_expiry', 'OK', 1)
// }
}
@@ -108,24 +112,77 @@ function deleteAllCookies() {
// -----------------------------------------------------------------------------
// Get settings from the .json file generated by the python backend
// and cache them, if available, with options
// -----------------------------------------------------------------------------
function cacheSettings()
{
$.get('api/table_settings.json', function(res) {
$.get('api/table_settings.json?nocache=' + Date.now(), function(resSet) {
settingsJSON = res;
$.get('api/plugins.json?nocache=' + Date.now(), function(resPlug) {
data = settingsJSON["data"];
pluginsData = resPlug["data"];
settingsData = resSet["data"];
settingsData.forEach((set) => {
resolvedOptions = createArray(set.Options)
setPlugObj = {};
options_params = [];
// proceed only if first option item contains something to resolve
if( !set.Code_Name.includes("__metadata") &&
resolvedOptions.length != 0 &&
resolvedOptions[0].includes("{value}"))
{
// get setting definition from the plugin config if available
setPlugObj = getPluginSettingObject(pluginsData, set.Code_Name)
// check if options contains parameters and resolve
if(setPlugObj != {} && setPlugObj["options_params"])
{
// get option_params for {value} resolution
options_params = setPlugObj["options_params"]
if(options_params != [])
{
// handles only strings of length == 1
resolvedOptions = `["${resolveParams(options_params, resolvedOptions[0])}"]`
}
}
}
data.forEach((set) => {
setCache(`pia_set_${set.Code_Name}`, set.Value)
setCache(`pia_set_opt_${set.Code_Name}`, resolvedOptions)
});
}).then(() => handleSuccess('cacheSettings')).catch(() => handleFailure('cacheSettings', cacheSettings)); // handle AJAX synchronization
})
}
// -----------------------------------------------------------------------------
// Get a setting value by key
function getSettingOptions (key) {
// handle initial load to make sure everything is set-up and cached
// handleFirstLoad()
result = getCache(`pia_set_opt_${key}`, true);
if (result == "")
{
console.log(`Setting options with key "${key}" not found`)
}
return result;
}
// -----------------------------------------------------------------------------
// Get a setting value by key
function getSetting (key) {
// handle initial load to make sure everything is set-up and cached
// handleFirstLoad()
result = getCache(`pia_set_${key}`, true);
if (result == "")
@@ -143,40 +200,45 @@ function cacheStrings()
{
// handle core strings and translations
var allLanguages = ["en_us","es_es","de_de"]; // needs to be same as in lang.php
var allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "ru_ru", "nb_no"]; // needs to be same as in lang.php
allLanguages.forEach(function (language_code) {
$.get(`php/templates/language/${language_code}.json`, function(res) {
Object.entries(res).forEach(([language, translations]) => {
Object.entries(translations).forEach(([key, value]) => {
// store as key - value pairs in session
setCache(`pia_lang_${key}_${language}`, value)
$.get(`php/templates/language/${language_code}.json?nocache=${Date.now()}`, function (res) {
// Iterate over each language
Object.entries(res).forEach(([key, value]) => {
// Store translations for each key-value pair
setCache(`pia_lang_${key}_${language_code}`, value)
});
});
});
})
});
// handle strings and translations from plugins
$.get('api/table_plugins_language_strings.json', function(res) {
$.get(`api/table_plugins_language_strings.json?nocache=${Date.now()}`, function(res) {
data = res["data"];
data.forEach((langString) => {
setCache(`pia_lang_${langString.String_Key}_${langString.Language_Code}`, langString.String_Value)
});
})
}).then(() => handleSuccess('cacheStrings')).catch(() => handleFailure('cacheStrings', cacheStrings)); // handle AJAX synchronization
}
// Get translated language string
function getString (key) {
// handle initial laod to make sure everything is set-up and cached
handleFirstLoad()
UI_LANG = getSetting("UI_LANG");
// //
// if(UI_LANG == "")
// {
// }
lang_code = 'en_us';
switch(UI_LANG)
@@ -190,17 +252,22 @@ function getString (key) {
case 'German':
lang_code = 'de_de';
break;
case 'French':
lang_code = 'fr_fr';
break;
case 'Norwegian':
lang_code = 'nb_no';
break;
case 'Russian':
lang_code = 'ru_ru';
break;
}
result = getCache(`pia_lang_${key}_${lang_code}`, true);
if(isEmpty(result))
{
console.log(`pia_lang_${key}_${lang_code}`)
console.log(key)
result = getCache(`pia_lang_${key}_en_us`, true);
console.log(result)
}
return result;
@@ -209,7 +276,10 @@ function getString (key) {
// -----------------------------------------------------------------------------
// Modal dialog handling
// -----------------------------------------------------------------------------
function showModalOk (title, message, callbackFunction = null) {
function showModalOK (title, message, callbackFunction) {
showModalOk (title, message, callbackFunction)
}
function showModalOk (title, message, callbackFunction) {
// set captions
$('#modal-ok-title').html (title);
$('#modal-ok-message').html (message);
@@ -225,6 +295,8 @@ function showModalOk (title, message, callbackFunction = null) {
// Show modal
$('#modal-ok').modal('show');
}
// -----------------------------------------------------------------------------
function showModalDefault (title, message, btnCancel, btnOK, callbackFunction) {
// set captions
$('#modal-default-title').html (title);
@@ -256,13 +328,17 @@ function showModalDefaultStrParam (title, message, btnCancel, btnOK, callbackFun
}
// -----------------------------------------------------------------------------
function showModalWarning (title, message, btnCancel, btnOK, callbackFunction) {
function showModalWarning (title, message, btnCancel=getString('Gen_Cancel'), btnOK=getString('Gen_Okay'), callbackFunction=null) {
// set captions
$('#modal-warning-title').html (title);
$('#modal-warning-message').html (message);
$('#modal-warning-cancel').html (btnCancel);
$('#modal-warning-OK').html (btnOK);
if ( callbackFunction != null)
{
modalCallbackFunction = callbackFunction;
}
// Show modal
$('#modal-warning').modal('show');
@@ -307,6 +383,32 @@ function showMessage (textMessage="") {
}
// -----------------------------------------------------------------------------
// String utilities
// -----------------------------------------------------------------------------
function jsonSyntaxHighlight(json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2);
}
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
// -----------------------------------------------------------------------------
// General utilities
// -----------------------------------------------------------------------------
@@ -323,6 +425,22 @@ function sanitize(data)
return data.replace(/(\r\n|\n|\r)/gm,"").replace(/[^\x00-\x7F]/g, "")
}
// -----------------------------------------------------------------------------
// Check and handle locked database
function handle_locked_DB(data)
{
if(data.includes('database is locked'))
{
// console.log(data)
showSpinner()
setTimeout(function() {
location.reload();
}, 5000);
}
}
// -----------------------------------------------------------------------------
function numberArrayFromString(data)
{
@@ -377,6 +495,19 @@ function saveData(functionName, id, value) {
}
// -----------------------------------------------------------------------------
// create a link to the device
function createDeviceLink(input)
{
if(checkMacOrInternet(input))
{
return `<span class="anonymizeMac"><a href="/deviceDetails.php?mac=${input}" target="_blank">${getNameByMacAddress(input)}</a><span>`
}
return input;
}
// -----------------------------------------------------------------------------
// remove an item from an array
function removeItemFromArray(arr, value) {
@@ -408,6 +539,34 @@ function settingsChanged()
}
// -----------------------------------------------------------------------------
// Get Anchor from URL
function getUrlAnchor(defaultValue){
target = defaultValue
var url = window.location.href;
if (url.includes("#")) {
// default selection
selectedTab = defaultValue
// the #target from the url
target = window.location.hash.substr(1)
// get only the part between #...?
if(target.includes('?'))
{
target = target.split('?')[0]
}
return target
}
}
// -----------------------------------------------------------------------------
// get query string from URL
function getQueryString(key){
params = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
@@ -415,6 +574,36 @@ function getQueryString(key){
tmp = params[key]
if(emptyArr.includes(tmp))
{
var queryParams = {};
fullUrl = window.location.toString();
// console.log(fullUrl);
if (fullUrl.includes('?')) {
var queryString = fullUrl.split('?')[1];
// Split the query string into individual parameters
var paramsArray = queryString.split('&');
// Loop through the parameters array
paramsArray.forEach(function(param) {
// Split each parameter into key and value
var keyValue = param.split('=');
var keyTmp = decodeURIComponent(keyValue[0]);
var value = decodeURIComponent(keyValue[1] || '');
// Store key-value pair in the queryParams object
queryParams[keyTmp] = value;
});
}
// console.log(queryParams);
tmp = queryParams[key]
}
result = emptyArr.includes(tmp) ? "" : tmp;
return result
@@ -457,10 +646,37 @@ function debugTimer () {
// -----------------------------------------------------------------------------
// Open url in new tab
function openInNewTab (url) {
window.open(url, "_blank");
}
// -----------------------------------------------------------------------------
// Navigate to URL if the current URL is not in the provided list of URLs
function openUrl(urls) {
var currentUrl = window.location.href;
var mainUrl = currentUrl.match(/^.*?(?=#|\?|$)/)[0]; // Extract main URL
var isMatch = false;
$.each(urls,function(index, obj){
// remove . for comaprison if in the string, e.g.: ./devices.php
arrayUrl = obj.replace('.','')
// check if we are on a url contained in the array
if(mainUrl.includes(arrayUrl))
{
isMatch = true;
}
});
// if we are not, redirect
if (isMatch == false) {
window.location.href = urls[0]; // Redirect to the first URL in the list if not found
}
}
// -----------------------------------------------------------------------------
function navigateToDeviceWithIp (ip) {
@@ -490,18 +706,153 @@ function getNameByMacAddress(macAddress) {
}
// -----------------------------------------------------------------------------
//
// Check if MAC or Internet
function checkMacOrInternet(inputStr) {
// Regular expression pattern for matching a MAC address
const macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
if (inputStr.toLowerCase() === 'internet') {
return true;
} else if (macPattern.test(inputStr)) {
return true;
} else {
return false;
}
}
// -----------------------------------------------------------------------------
// A function used to make the IP address orderable
function isValidIPv6(ipAddress) {
// Regular expression for IPv6 validation
const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,7}:|^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$|^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}$|^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}$|^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})$/;
return ipv6Regex.test(ipAddress);
}
function formatIPlong(ipAddress) {
if (ipAddress.includes(':') && isValidIPv6(ipAddress)) {
const parts = ipAddress.split(':');
return parts.reduce((acc, part, index) => {
if (part === '') {
const remainingGroups = 8 - parts.length + 1;
return acc << (16 * remainingGroups);
}
const hexValue = parseInt(part, 16);
return acc | (hexValue << (112 - index * 16));
}, 0);
} else {
// Handle IPv4 address
const parts = ipAddress.split('.');
if (parts.length !== 4) {
console.log("⚠ Invalid IPv4 address: " + ipAddress);
return -1; // or any other default value indicating an error
}
return (parseInt(parts[0]) << 24) |
(parseInt(parts[1]) << 16) |
(parseInt(parts[2]) << 8) |
parseInt(parts[3]);
}
}
// -----------------------------------------------------------------------------
// Check if MAC is a random one
function isRandomMAC(mac)
{
isRandom = false;
isRandom = ["2", "6", "A", "E", "a", "e"].includes(mac[1]);
// if detected as random, make sure it doesn't start with a prefix which teh suer doesn't want to mark as random
if(isRandom)
{
$.each(createArray(getSetting("UI_NOT_RANDOM_MAC")), function(index, prefix) {
if(mac.startsWith(prefix))
{
isRandom = false;
}
});
}
return isRandom;
}
// ---------------------------------------------------------
// Generate an array object from a string representation of an array
function createArray(input) {
// Empty array
if (input === '[]') {
return [];
}
// Regex pattern for brackets
const patternBrackets = /(^\s*\[)|(\]\s*$)/g;
const replacement = '';
// Remove brackets
const noBrackets = input.replace(patternBrackets, replacement);
const options = [];
// Detect the type of quote used after the opening bracket
const firstChar = noBrackets.trim()[0];
const isDoubleQuoted = firstChar === '"';
const isSingleQuoted = firstChar === "'";
// Create array while handling commas within quoted segments
let currentSegment = '';
let withinQuotes = false;
for (let i = 0; i < noBrackets.length; i++) {
const char = noBrackets[i];
if ((char === '"' && !isSingleQuoted) || (char === "'" && !isDoubleQuoted)) {
withinQuotes = !withinQuotes;
}
if (char === ',' && !withinQuotes) {
options.push(currentSegment.trim());
currentSegment = '';
} else {
currentSegment += char;
}
}
// Push the last segment
options.push(currentSegment.trim());
// Remove quotes based on detected type
options.forEach((item, index) => {
let trimmedItem = item.trim();
// Check if the string starts and ends with the same type of quote
if ((isDoubleQuoted && trimmedItem.startsWith('"') && trimmedItem.endsWith('"')) ||
(isSingleQuoted && trimmedItem.startsWith("'") && trimmedItem.endsWith("'"))) {
// Remove the quotes
trimmedItem = trimmedItem.substring(1, trimmedItem.length - 1);
}
options[index] = trimmedItem;
});
return options;
}
// -----------------------------------------------------------------------------
// A function to get a device property using the mac address as key and DB column nakme as parameter
// for the value to be returned
function getDeviceDataByMacAddress(macAddress, dbColumn) {
const sessionDataKey = 'devicesListAll_JSON';
const sessionData = sessionStorage.getItem(sessionDataKey);
const devicesCache = getCache(sessionDataKey);
if (!sessionData) {
console.log(`Session variable "${sessionDataKey}" not found.`);
if (!devicesCache || devicesCache == "") {
console.error(`Session variable "${sessionDataKey}" not found.`);
return "Unknown";
}
const devices = JSON.parse(sessionData);
const devices = JSON.parse(devicesCache);
for (const device of devices) {
if (device["dev_MAC"].toLowerCase() === macAddress.toLowerCase()) {
@@ -520,12 +871,27 @@ function initDeviceListAll_JSON()
$.get('api/table_devices.json', function(data) {
console.log(data)
// console.log(data)
devicesListAll_JSON = data["data"]
setCache('devicesListAll_JSON', JSON.stringify(devicesListAll_JSON))
});
devicesListAll_JSON_str = JSON.stringify(devicesListAll_JSON)
if(devicesListAll_JSON_str == "")
{
setTimeout(() => {
initDeviceListAll_JSON()
}, 1000);
}
// console.log("devicesListAll_JSON_str");
// console.log(devicesListAll_JSON_str);
setCache('devicesListAll_JSON', devicesListAll_JSON_str)
// console.log(getCache('devicesListAll_JSON'))
}).then(() => handleSuccess('initDeviceListAll_JSON')).catch(() => handleFailure('initDeviceListAll_JSON', initDeviceListAll_JSON)); // handle AJAX synchronization
}
@@ -537,12 +903,308 @@ function isEmpty(value)
return emptyArr.includes(value)
}
// -----------------------------------------------------------------------------
// Generate a GUID
function getGuid() {
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
// -----------------------------------------------------------------------------
// UI
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Loading Spinner overlay
// -----------------------------------------------------------------------------
function showSpinner(stringKey='Loading')
{
if($("#loadingSpinner").length)
{
$("#loadingSpinner").show();
}
else{
html = `
<!-- spinner -->
<div id="loadingSpinner" style="display: block">
<div class="pa_semitransparent-panel"></div>
<div class="panel panel-default pa_spinner">
<table>
<td width="130px" align="middle">${getString(stringKey)}</td>
<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td>
</table>
</div>
</div>
`
$(".wrapper").append(html)
}
}
// -----------------------------------------------------------------------------
function hideSpinner()
{
$("#loadingSpinner").hide()
}
// --------------------------------------------------------
// Calls a backend function to add a front-end event to an execution queue
function updateApi()
{
// value has to be in format event|param. e.g. run|ARPSCAN
action = `update_api|devices,appevents`
$.ajax({
method: "POST",
url: "php/server/util.php",
data: { function: "addToExecutionQueue", action: action },
success: function(data, textStatus) {
console.log(data)
}
})
}
// -----------------------------------------------------------------------------
// handling smooth scrolling
// -----------------------------------------------------------------------------
function setupSmoothScrolling() {
// Function to scroll to the element
function scrollToElement(id) {
$('html, body').animate({
scrollTop: $("#" + id).offset().top - 50
}, 1000);
}
// Scroll to the element when clicking on anchor links
$('a[href*="#"]').on('click', function(event) {
var href = $(this).attr('href');
if (href !=='#' && href && href.includes('#') && !$(this).is('[data-toggle="collapse"]')) {
var id = href.substring(href.indexOf("#") + 1); // Get the ID from the href attribute
if ($("#" + id).length > 0) {
event.preventDefault(); // Prevent default anchor behavior
scrollToElement(id); // Scroll to the element
}
}
});
// Check if there's an ID in the URL and scroll to it
var url = window.location.href;
if (url.includes("#")) {
var idFromURL = url.substring(url.indexOf("#") + 1);
if ($("#" + idFromURL).length > 0) {
scrollToElement(idFromURL);
}
}
}
// -------------------------------------------------------------------
// Function to check if options_params contains a parameter with type "sql"
function hasSqlType(params) {
for (let param of params) {
if (param.type === "sql") {
return true; // Found a parameter with type "sql"
}
}
return false; // No parameter with type "sql" found
}
// -------------------------------------------------------------------
// Function to check if string is SQL query
function isSQLQuery(query) {
// Regular expression to match common SQL keywords and syntax with word boundaries
var sqlRegex = /\b(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|FROM|JOIN|WHERE|SET|VALUES|GROUP BY|ORDER BY|LIMIT)\b/i;
return sqlRegex.test(query);
}
// -------------------------------------------------------------------
// Get corresponding plugin setting object
function getPluginSettingObject(pluginsData, setting_key, unique_prefix ) {
result = {}
unique_prefix == undefined ? unique_prefix = setting_key.split("_")[0] : unique_prefix = unique_prefix;
$.each(pluginsData, function (i, plgnObj){
// go thru plugins
if(plgnObj.unique_prefix == unique_prefix)
{
// go thru plugin settings
$.each(plgnObj["settings"], function (j, setObj){
if(`${unique_prefix}_${setObj.function}` == setting_key)
{
result = setObj
}
});
}
});
return result
}
// -------------------------------------------------------------------
// Resolve all option parameters
function resolveParams(params, template) {
params.forEach(param => {
// Check if the template includes the parameter name
if (template.includes("{" + param.name + "}")) {
// If the parameter type is 'setting', retrieve setting value
if (param.type == "setting") {
var value = getSetting(param.value);
// Replace placeholder with setting value
template = template.replace("{" + param.name + "}", value);
} else {
// If the parameter type is not 'setting', use the provided value
template = template.replace("{" + param.name + "}", param.value);
}
}
});
// Log the resolved template
// console.log(template);
// Return the resolved template
return template;
}
// -----------------------------------------------------------------------------
// check if two arrays contain same values even if out of order
function arraysContainSameValues(arr1, arr2) {
// Sort and stringify arrays, then compare
return JSON.stringify(arr1.slice().sort()) === JSON.stringify(arr2.slice().sort());
}
// -----------------------------------------------------------------------------
// initialize
cacheSettings()
cacheStrings()
initDeviceListAll_JSON()
// -----------------------------------------------------------------------------
// Define a unique key for storing the flag in sessionStorage
var sessionStorageKey = "myScriptExecuted_pialert_common";
// -----------------------------------------------------------------------------
// Clearing all the caches
function clearCache()
{
resetInitializedFlag()
window.location.reload()
}
// -----------------------------------------------------------------------------
function resetInitializedFlag()
{
// Clear local storage
localStorage.clear();
// Set the flag in sessionStorage to indicate that the code and cahce needs to be reloaded
sessionStorage.setItem(sessionStorageKey, "false");
}
// -----------------------------------------------------------------------------
// check if cache needs to be refreshed because of setting changes
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
console.log(appState["settingsImported"]*1000)
importedMiliseconds = parseInt((appState["settingsImported"]*1000));
lastReloaded = parseInt(sessionStorage.getItem(sessionStorageKey + '_time'));
if(importedMiliseconds > lastReloaded)
{
console.log("Cache needs to be refreshed because of setting changes");
setTimeout(() => {
resetInitializedFlag()
location.reload();
}, 500);
}
});
// -----------------------------------------------------------------------------
// Display spinner and reload page if not yet initialized
function handleFirstLoad()
{
if(!pialert_common_init)
{
setTimeout(function() {
location.reload();
}, 1000);
}
}
// -----------------------------------------------------------------------------
// Check if the code has been executed before by checking sessionStorage
var pialert_common_init = sessionStorage.getItem(sessionStorageKey) === "true";
// Define a function that will execute the code only once
function executeOnce() {
if (!pialert_common_init) {
resetInitializedFlag()
showSpinner()
// to keep track of completed AJAX calls
completedCalls = []
completedCalls_final = ['cacheSettings', 'cacheStrings', 'initDeviceListAll_JSON'];
// Your initialization code here
cacheSettings();
cacheStrings();
initDeviceListAll_JSON();
}
}
// -----------------------------------------------------------------------------
// Function to handle successful completion of an AJAX call
const handleSuccess = (callName) => {
console.log(`AJAX call ${callName} successful`);
// store completed call
completedCalls.push(callName)
onAllCallsComplete();
};
// -----------------------------------------------------------------------------
// Function to handle failure of an AJAX call
const handleFailure = (callName, callback) => {
// Handle AJAX call failure here
console.error(`AJAX call ${callName} failed`);
// try until successful
callback()
};
// -----------------------------------------------------------------------------
// Function to execute when all AJAX calls have completed
const onAllCallsComplete = () => {
// Check if all three AJAX calls have completed
if (arraysContainSameValues(completedCalls,completedCalls_final)) {
// Set the flag in sessionStorage to indicate that the code has been executed
// and save time when last time the page was initialized
sessionStorage.setItem(sessionStorageKey, "true");
const millisecondsNow = Date.now();
sessionStorage.setItem(sessionStorageKey + '_time', millisecondsNow);
console.log("init pialert_common.js");
}
};
// Call the function to execute the code
executeOnce();
console.log("init pialert_common.js")

171
front/js/settings_utils.js Executable file
View File

@@ -0,0 +1,171 @@
// -------------------------------------------------------------------
// Get all plugin prefixes of a given type
function getPluginsByType(pluginsData, pluginType, onlyEnabled)
{
var result = []
pluginsData.forEach((plug) => {
if(plug.plugin_type == pluginType)
{
// collect all, or if only enabled, check if NOT disabled
if (onlyEnabled == false || (onlyEnabled && getSetting(plug.unique_prefix + '_RUN') != 'disabled')) {
result.push(plug.unique_prefix)
}
}
});
return result;
}
// -------------------------------------------------------------------
// Get plugin type base on prefix
function getPluginCodeName(pluginsData, prefix)
{
var result = ""
pluginsData.forEach((plug) => {
if (plug.unique_prefix == prefix ) {
id = plug.code_name;
// console.log(id)
result = plug.code_name;
}
});
return result;
}
// -------------------------------------------------------------------
// Get plugin type base on prefix
function getPluginType(pluginsData, prefix)
{
var result = "core"
pluginsData.forEach((plug) => {
if (plug.unique_prefix == prefix ) {
id = plug.plugin_type;
// console.log(id)
result = plug.plugin_type;
}
});
return result;
}
// -------------------------------------------------------------------
// Generate plugin HTML card based on prefixes in an array
function pluginCards(prefixesOfEnabledPlugins, includeSettings)
{
html = ""
prefixesOfEnabledPlugins.forEach((prefix) => {
includeSettings_html = ''
includeSettings.forEach((set) => {
includeSettings_html += `
<a href="#${prefix + '_' + set}" onclick="toggleAllSettings()">
<div class="overview-setting-value pointer" title="${prefix + '_' + set}">
<code>${getSetting(prefix + '_' + set)}</code>
</div>
</a>
`
});
html += `
<div class="col-sm-4 ">
<div class="small-box bg-green " >
<div class="inner ">
<a href="#${prefix}_header" onclick="toggleAllSettings('open')">
<h5 class="card-title">
<b>${getString(prefix+"_display_name")}</b>
</h5>
</a>
${includeSettings_html}
</div>
<a href="#${prefix}_header" onclick="toggleAllSettings('open')">
<div class="icon"> ${getString(prefix+"_icon")} </div>
</a>
</div>
</div>
`
});
return html;
}
// -----------------------------------------------------------------------------
// Open or close all settings
// -----------------------------------------------------------------------------
function toggleAllSettings(openOrClose = '')
{
inStr = ' in';
allOpen = true;
openIcon = 'fa-angle-double-down';
closeIcon = 'fa-angle-double-up';
$('.panel-collapse').each(function(){
if($(this).attr('class').indexOf(inStr) == -1)
{
allOpen = false;
}
})
if(allOpen == false || openOrClose == 'open')
{
// open all
$('div[data-myid="collapsible"]').each(function(){$(this).attr('class', 'panel-collapse collapse in')})
$('div[data-myid="collapsible"]').each(function(){$(this).attr('style', 'height:inherit')})
$('#toggleSettings').attr('class', $('#toggleSettings').attr('class').replace(openIcon, closeIcon))
}
else{
// close all
$('div[data-myid="collapsible"]').each(function(){$(this).attr('class', 'panel-collapse collapse ')})
$('#toggleSettings').attr('class', $('#toggleSettings').attr('class').replace(closeIcon, openIcon))
}
}
// -------------------------------------------------------------------
// Checks if all schedules are the same
function schedulesAreSynchronized(prefixesOfEnabledPlugins, pluginsData)
{
plug_schedules = []
prefixesOfEnabledPlugins.forEach((prefix) => {
pluginsData.forEach((plug) => {
if (plug.unique_prefix == prefix) {
plug_schedules.push(getSetting(prefix+"_RUN_SCHD").replace(/\s/g, "")) // replace all white characters to compare them easier
}
});
});
// Check if all plug_schedules are the same
if (plug_schedules.length > 0) {
const firstSchedule = plug_schedules[0];
return plug_schedules.every((schedule) => schedule === firstSchedule);
}
return true; // Return true if no schedules are found
}

194
front/js/ui_components.js Executable file
View File

@@ -0,0 +1,194 @@
/* -----------------------------------------------------------------------------
* Pi.Alert
* Open Source Network Guard / WIFI & LAN intrusion detector
*
* ui_components.js - Front module. Common UI components
*-------------------------------------------------------------------------------
# jokob jokob@duck.com GNU GPLv3
----------------------------------------------------------------------------- */
// -----------------------------------------------------------------------------
// Initialize device selectors / pickers fields
// -----------------------------------------------------------------------------
function initDeviceSelectors() {
console.log(devicesList)
// Retrieve device list from session variable
var devicesListAll_JSON = getCache('devicesListAll_JSON');
var devicesList = JSON.parse(devicesListAll_JSON);
console.log(devicesList);
// Check if both device list exists
if (devicesListAll_JSON) {
// Parse the JSON string to get the device list array
var devicesList = JSON.parse(devicesListAll_JSON);
var selectorFieldsHTML = ''
// Loop through the devices list
devicesList.forEach(function(device) {
selectorFieldsHTML += `<option value="${device.dev_MAC}">${device.dev_Name}</option>`;
});
selector = `<div class="db_info_table_row col-sm-12" >
<div class="form-group" >
<div class="input-group col-sm-12 " >
<select class="form-control select2 select2-hidden-accessible" multiple="" style="width: 100%;" tabindex="-1" aria-hidden="true">
${selectorFieldsHTML}
</select>
</div>
</div>
</div>`
// Find HTML elements with class "deviceSelector" and append selector field
$('.deviceSelector').append(selector);
}
// Initialize selected items after a delay so selected macs are available in the context
setTimeout(function(){
// Retrieve MAC addresses from query string or cache
var macs = getQueryString('macs') || getCache('selectedDevices');
if(macs)
{
// Split MAC addresses if they are comma-separated
macs = macs.split(',');
console.log(macs)
// Loop through macs to be selected list
macs.forEach(function(mac) {
// Create the option and append to Select2
var option = new Option($('.deviceSelector select option[value="' + mac + '"]').html(), mac, true, true);
$('.deviceSelector select').append(option).trigger('change');
});
}
}, 10);
}
// --------------------------------------------------------
//Initialize Select2 Elements and make them sortable
$(function () {
// Iterate over each Select2 dropdown
$('.select2').each(function() {
var selectEl = $(this).select2();
// Apply sortable functionality to the dropdown's dropdown-container
selectEl.next().children().children().children().sortable({
containment: 'parent',
update: function () {
var sortedValues = $(this).children().map(function() {
return $(this).attr('title');
}).get();
var sortedOptions = selectEl.find('option').sort(function(a, b) {
return sortedValues.indexOf($(a).text()) - sortedValues.indexOf($(b).text());
});
// Replace all options in selectEl
selectEl.empty().append(sortedOptions);
// Trigger change event on Select2
selectEl.trigger('change');
}
});
});
});
// -----------------------------------------------------------------------------
// Initiate dropdown
function initSettingDropdown(settingKey, valuesArray, targetLocation)
{
var optionsHtml = ""
var targetLocation_options = settingKey + "_initSettingDropdown"
optionsArray = createArray(getSettingOptions(settingKey))
// check if the result is a SQL query
if(isSQLQuery(optionsArray[0]))
{
optionsHtml += `<option id="${targetLocation_options}"></option>`;
readData(optionsArray[0], generateDropdownOptions, valuesArray, targetLocation_options);
} else // this should be already an array, e.g. from a setting or pre-defined
{
optionsArray.forEach(option => {
let selected = valuesArray.includes(option) ? 'selected' : '';
optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
});
// Replace the specified placeholder div with the resulting HTML
setTimeout(() => {
$("#" + targetLocation).replaceWith(optionsHtml);
}, 50);
}
}
// -----------------------------------------------------------------------------
// Data processors
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Processor to generate options for a dropdown menu
function generateDropdownOptions(data, valuesArray) {
var optionsHtml = "";
data.forEach(function(item) {
let selected = valuesArray.includes(item.id) ? 'selected' : '';
optionsHtml += `<option value="${item.id}" ${selected}>${item.name}</option>`;
});
return `${optionsHtml}`;
}
// -----------------------------------------------------------------------------
// Processor to generate a list
function generateList(data, valuesArray) {
var listHtml = "";
data.forEach(function(item) {
let selected = valuesArray.includes(item.id) ? 'selected' : '';
listHtml += `<li ${selected}>${item.name}</li>`;
});
listHtml += "";
return listHtml;
}
// -----------------------------------------------------------------------------
// initialize
// -----------------------------------------------------------------------------
initDeviceSelectors();
console.log("init ui_components.js")

View File

@@ -0,0 +1 @@
table.dataTable tbody>tr.selected,table.dataTable tbody>tr>.selected{background-color:#b0bed9}table.dataTable.stripe tbody>tr.odd.selected,table.dataTable.stripe tbody>tr.odd>.selected,table.dataTable.display tbody>tr.odd.selected,table.dataTable.display tbody>tr.odd>.selected{background-color:#acbad4}table.dataTable.hover tbody>tr.selected:hover,table.dataTable.hover tbody>tr>.selected:hover,table.dataTable.display tbody>tr.selected:hover,table.dataTable.display tbody>tr>.selected:hover{background-color:#aab7d1}table.dataTable.order-column tbody>tr.selected>.sorting_1,table.dataTable.order-column tbody>tr.selected>.sorting_2,table.dataTable.order-column tbody>tr.selected>.sorting_3,table.dataTable.order-column tbody>tr>.selected,table.dataTable.display tbody>tr.selected>.sorting_1,table.dataTable.display tbody>tr.selected>.sorting_2,table.dataTable.display tbody>tr.selected>.sorting_3,table.dataTable.display tbody>tr>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody>tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody>tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody>tr.odd>.selected,table.dataTable.order-column.stripe tbody>tr.odd>.selected{background-color:#a6b4cd}table.dataTable.display tbody>tr.even>.selected,table.dataTable.order-column.stripe tbody>tr.even>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.selected:hover>.sorting_1,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody>tr.selected:hover>.sorting_2,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody>tr.selected:hover>.sorting_3,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_3{background-color:#a5b2cb}table.dataTable.display tbody>tr:hover>.selected,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.order-column.hover tbody>tr:hover>.selected,table.dataTable.order-column.hover tbody>tr>.selected:hover{background-color:#a2aec7}table.dataTable tbody td.select-checkbox,table.dataTable tbody th.select-checkbox{position:relative}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody td.select-checkbox:after,table.dataTable tbody th.select-checkbox:before,table.dataTable tbody th.select-checkbox:after{display:block;position:absolute;top:1.2em;left:50%;width:12px;height:12px;box-sizing:border-box}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody th.select-checkbox:before{content:" ";margin-top:-6px;margin-left:-6px;border:1px solid black;border-radius:3px}table.dataTable tr.selected td.select-checkbox:after,table.dataTable tr.selected th.select-checkbox:after{content:"✓";font-size:20px;margin-top:-19px;margin-left:-6px;text-align:center;text-shadow:1px 1px #b0bed9,-1px -1px #b0bed9,1px -1px #b0bed9,-1px 1px #b0bed9}table.dataTable.compact tbody td.select-checkbox:before,table.dataTable.compact tbody th.select-checkbox:before{margin-top:-12px}table.dataTable.compact tr.selected td.select-checkbox:after,table.dataTable.compact tr.selected th.select-checkbox:after{margin-top:-16px}div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:.5em}@media screen and (max-width: 640px){div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0;display:block}}

View File

@@ -0,0 +1,38 @@
/*!
Copyright 2015-2021 SpryMedia Ltd.
This source file is free software, available under the following license:
MIT license - http://datatables.net/license/mit
This source file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
For details please refer to: http://www.datatables.net/extensions/select
Select for DataTables 1.3.3
2015-2021 SpryMedia Ltd - datatables.net/license/mit
*/
(function(h){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(q){return h(q,window,document)}):"object"===typeof exports?module.exports=function(q,t){q||(q=window);t&&t.fn.dataTable||(t=require("datatables.net")(q,t).$);return h(t,q,q.document)}:h(jQuery,window,document)})(function(h,q,t,n){function E(a,b,c){var d=function(g,f){if(g>f){var k=f;f=g;g=k}var l=!1;return a.columns(":visible").indexes().filter(function(p){p===g&&(l=!0);return p===f?(l=!1,!0):l})};var e=
function(g,f){var k=a.rows({search:"applied"}).indexes();if(k.indexOf(g)>k.indexOf(f)){var l=f;f=g;g=l}var p=!1;return k.filter(function(u){u===g&&(p=!0);return u===f?(p=!1,!0):p})};a.cells({selected:!0}).any()||c?(d=d(c.column,b.column),c=e(c.row,b.row)):(d=d(0,b.column),c=e(0,b.row));c=a.cells(c,d).flatten();a.cells(b,{selected:!0}).any()?a.cells(c).deselect():a.cells(c).select()}function A(a){var b=a.settings()[0]._select.selector;h(a.table().container()).off("mousedown.dtSelect",b).off("mouseup.dtSelect",
b).off("click.dtSelect",b);h("body").off("click.dtSelect"+a.table().node().id.replace(/[^a-zA-Z0-9\-_]/g,"-"))}function F(a){var b=h(a.table().container()),c=a.settings()[0],d=c._select.selector,e;b.on("mousedown.dtSelect",d,function(g){if(g.shiftKey||g.metaKey||g.ctrlKey)b.css("-moz-user-select","none").one("selectstart.dtSelect",d,function(){return!1});q.getSelection&&(e=q.getSelection())}).on("mouseup.dtSelect",d,function(){b.css("-moz-user-select","")}).on("click.dtSelect",d,function(g){var f=
a.select.items();if(e){var k=q.getSelection();if((!k.anchorNode||h(k.anchorNode).closest("table")[0]===a.table().node())&&k!==e)return}k=a.settings()[0];var l=a.settings()[0].oClasses.sWrapper.trim().replace(/ +/g,".");if(h(g.target).closest("div."+l)[0]==a.table().container()&&(l=a.cell(h(g.target).closest("td, th")),l.any())){var p=h.Event("user-select.dt");r(a,p,[f,l,g]);p.isDefaultPrevented()||(p=l.index(),"row"===f?(f=p.row,B(g,a,k,"row",f)):"column"===f?(f=l.index().column,B(g,a,k,"column",
f)):"cell"===f&&(f=l.index(),B(g,a,k,"cell",f)),k._select_lastCell=p)}});h("body").on("click.dtSelect"+a.table().node().id.replace(/[^a-zA-Z0-9\-_]/g,"-"),function(g){!c._select.blurable||h(g.target).parents().filter(a.table().container()).length||0===h(g.target).parents("html").length||h(g.target).parents("div.DTE").length||x(c,!0)})}function r(a,b,c,d){if(!d||a.flatten().length)"string"===typeof b&&(b+=".dt"),c.unshift(a),h(a.table().node()).trigger(b,c)}function I(a){var b=a.settings()[0];if(b._select.info&&
b.aanFeatures.i&&"api"!==a.select.style()){var c=a.rows({selected:!0}).flatten().length,d=a.columns({selected:!0}).flatten().length,e=a.cells({selected:!0}).flatten().length,g=function(f,k,l){f.append(h('<span class="select-item"/>').append(a.i18n("select."+k+"s",{_:"%d "+k+"s selected",0:"",1:"1 "+k+" selected"},l)))};h.each(b.aanFeatures.i,function(f,k){k=h(k);f=h('<span class="select-info"/>');g(f,"row",c);g(f,"column",d);g(f,"cell",e);var l=k.children("span.select-info");l.length&&l.remove();
""!==f.text()&&k.append(f)})}}function J(a){var b=new m.Api(a);a.aoRowCreatedCallback.push({fn:function(c,d,e){d=a.aoData[e];d._select_selected&&h(c).addClass(a._select.className);c=0;for(e=a.aoColumns.length;c<e;c++)(a.aoColumns[c]._select_selected||d._selected_cells&&d._selected_cells[c])&&h(d.anCells[c]).addClass(a._select.className)},sName:"select-deferRender"});b.on("preXhr.dt.dtSelect",function(c,d){if(d===b.settings()[0]){var e=b.rows({selected:!0}).ids(!0).filter(function(f){return f!==n}),
g=b.cells({selected:!0}).eq(0).map(function(f){var k=b.row(f.row).id(!0);return k?{row:k,column:f.column}:n}).filter(function(f){return f!==n});b.one("draw.dt.dtSelect",function(){b.rows(e).select();g.any()&&g.each(function(f){b.cells(f.row,f.column).select()})})}});b.on("draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt",function(){I(b)});b.on("destroy.dtSelect",function(){b.rows({selected:!0}).deselect();A(b);b.off(".dtSelect")})}function G(a,b,c,d){var e=a[b+"s"]({search:"applied"}).indexes();
d=h.inArray(d,e);var g=h.inArray(c,e);if(a[b+"s"]({selected:!0}).any()||-1!==d){if(d>g){var f=g;g=d;d=f}e.splice(g+1,e.length);e.splice(0,d)}else e.splice(h.inArray(c,e)+1,e.length);a[b](c,{selected:!0}).any()?(e.splice(h.inArray(c,e),1),a[b+"s"](e).deselect()):a[b+"s"](e).select()}function x(a,b){if(b||"single"===a._select.style)a=new m.Api(a),a.rows({selected:!0}).deselect(),a.columns({selected:!0}).deselect(),a.cells({selected:!0}).deselect()}function B(a,b,c,d,e){var g=b.select.style(),f=b.select.toggleable(),
k=b[d](e,{selected:!0}).any();if(!k||f)"os"===g?a.ctrlKey||a.metaKey?b[d](e).select(!k):a.shiftKey?"cell"===d?E(b,e,c._select_lastCell||null):G(b,d,e,c._select_lastCell?c._select_lastCell[d]:null):(a=b[d+"s"]({selected:!0}),k&&1===a.flatten().length?b[d](e).deselect():(a.deselect(),b[d](e).select())):"multi+shift"==g?a.shiftKey?"cell"===d?E(b,e,c._select_lastCell||null):G(b,d,e,c._select_lastCell?c._select_lastCell[d]:null):b[d](e).select(!k):b[d](e).select(!k)}function y(a,b){return function(c){return c.i18n("buttons."+
a,b)}}function C(a){a=a._eventNamespace;return"draw.dt.DT"+a+" select.dt.DT"+a+" deselect.dt.DT"+a}function K(a,b){return-1!==h.inArray("rows",b.limitTo)&&a.rows({selected:!0}).any()||-1!==h.inArray("columns",b.limitTo)&&a.columns({selected:!0}).any()||-1!==h.inArray("cells",b.limitTo)&&a.cells({selected:!0}).any()?!0:!1}var m=h.fn.dataTable;m.select={};m.select.version="1.3.3";m.select.init=function(a){var b=a.settings()[0],c=b.oInit.select,d=m.defaults.select;c=c===n?d:c;d="row";var e="api",g=!1,
f=!0,k=!0,l="td, th",p="selected",u=!1;b._select={};!0===c?(e="os",u=!0):"string"===typeof c?(e=c,u=!0):h.isPlainObject(c)&&(c.blurable!==n&&(g=c.blurable),c.toggleable!==n&&(f=c.toggleable),c.info!==n&&(k=c.info),c.items!==n&&(d=c.items),e=c.style!==n?c.style:"os",u=!0,c.selector!==n&&(l=c.selector),c.className!==n&&(p=c.className));a.select.selector(l);a.select.items(d);a.select.style(e);a.select.blurable(g);a.select.toggleable(f);a.select.info(k);b._select.className=p;h.fn.dataTable.ext.order["select-checkbox"]=
function(z,L){return this.api().column(L,{order:"index"}).nodes().map(function(H){return"row"===z._select.items?h(H).parent().hasClass(z._select.className):"cell"===z._select.items?h(H).hasClass(z._select.className):!1})};!u&&h(a.table().node()).hasClass("selectable")&&a.select.style("os")};h.each([{type:"row",prop:"aoData"},{type:"column",prop:"aoColumns"}],function(a,b){m.ext.selector[b.type].push(function(c,d,e){d=d.selected;var g=[];if(!0!==d&&!1!==d)return e;for(var f=0,k=e.length;f<k;f++){var l=
c[b.prop][e[f]];(!0===d&&!0===l._select_selected||!1===d&&!l._select_selected)&&g.push(e[f])}return g})});m.ext.selector.cell.push(function(a,b,c){b=b.selected;var d=[];if(b===n)return c;for(var e=0,g=c.length;e<g;e++){var f=a.aoData[c[e].row];(!0===b&&f._selected_cells&&!0===f._selected_cells[c[e].column]||!(!1!==b||f._selected_cells&&f._selected_cells[c[e].column]))&&d.push(c[e])}return d});var v=m.Api.register,w=m.Api.registerPlural;v("select()",function(){return this.iterator("table",function(a){m.select.init(new m.Api(a))})});
v("select.blurable()",function(a){return a===n?this.context[0]._select.blurable:this.iterator("table",function(b){b._select.blurable=a})});v("select.toggleable()",function(a){return a===n?this.context[0]._select.toggleable:this.iterator("table",function(b){b._select.toggleable=a})});v("select.info()",function(a){return a===n?this.context[0]._select.info:this.iterator("table",function(b){b._select.info=a})});v("select.items()",function(a){return a===n?this.context[0]._select.items:this.iterator("table",
function(b){b._select.items=a;r(new m.Api(b),"selectItems",[a])})});v("select.style()",function(a){return a===n?this.context[0]._select.style:this.iterator("table",function(b){b._select.style=a;b._select_init||J(b);var c=new m.Api(b);A(c);"api"!==a&&F(c);r(new m.Api(b),"selectStyle",[a])})});v("select.selector()",function(a){return a===n?this.context[0]._select.selector:this.iterator("table",function(b){A(new m.Api(b));b._select.selector=a;"api"!==b._select.style&&F(new m.Api(b))})});w("rows().select()",
"row().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("row",function(c,d){x(c);c.aoData[d]._select_selected=!0;h(c.aoData[d].nTr).addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",["row",b[d]],!0)});return this});w("columns().select()","column().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("column",function(c,d){x(c);c.aoColumns[d]._select_selected=!0;d=(new m.Api(c)).column(d);h(d.header()).addClass(c._select.className);
h(d.footer()).addClass(c._select.className);d.nodes().to$().addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",["column",b[d]],!0)});return this});w("cells().select()","cell().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("cell",function(c,d,e){x(c);d=c.aoData[d];d._selected_cells===n&&(d._selected_cells=[]);d._selected_cells[e]=!0;d.anCells&&h(d.anCells[e]).addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",
["cell",b.cells(b[d]).indexes().toArray()],!0)});return this});w("rows().deselect()","row().deselect()",function(){var a=this;this.iterator("row",function(b,c){b.aoData[c]._select_selected=!1;b._select_lastCell=null;h(b.aoData[c].nTr).removeClass(b._select.className)});this.iterator("table",function(b,c){r(a,"deselect",["row",a[c]],!0)});return this});w("columns().deselect()","column().deselect()",function(){var a=this;this.iterator("column",function(b,c){b.aoColumns[c]._select_selected=!1;var d=
new m.Api(b),e=d.column(c);h(e.header()).removeClass(b._select.className);h(e.footer()).removeClass(b._select.className);d.cells(null,c).indexes().each(function(g){var f=b.aoData[g.row],k=f._selected_cells;!f.anCells||k&&k[g.column]||h(f.anCells[g.column]).removeClass(b._select.className)})});this.iterator("table",function(b,c){r(a,"deselect",["column",a[c]],!0)});return this});w("cells().deselect()","cell().deselect()",function(){var a=this;this.iterator("cell",function(b,c,d){c=b.aoData[c];c._selected_cells[d]=
!1;c.anCells&&!b.aoColumns[d]._select_selected&&h(c.anCells[d]).removeClass(b._select.className)});this.iterator("table",function(b,c){r(a,"deselect",["cell",a[c]],!0)});return this});var D=0;h.extend(m.ext.buttons,{selected:{text:y("selected","Selected"),className:"buttons-selected",limitTo:["rows","columns","cells"],init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){d.enable(K(a,c))});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}},selectedSingle:{text:y("selectedSingle",
"Selected single"),className:"buttons-selected-single",init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){var e=a.rows({selected:!0}).flatten().length+a.columns({selected:!0}).flatten().length+a.cells({selected:!0}).flatten().length;d.enable(1===e)});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}},selectAll:{text:y("selectAll","Select all"),className:"buttons-select-all",action:function(){this[this.select.items()+"s"]().select()}},selectNone:{text:y("selectNone",
"Deselect all"),className:"buttons-select-none",action:function(){x(this.settings()[0],!0)},init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){var e=a.rows({selected:!0}).flatten().length+a.columns({selected:!0}).flatten().length+a.cells({selected:!0}).flatten().length;d.enable(0<e)});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}}});h.each(["Row","Column","Cell"],function(a,b){var c=b.toLowerCase();m.ext.buttons["select"+b+"s"]={text:y("select"+
b+"s","Select "+c+"s"),className:"buttons-select-"+c+"s",action:function(){this.select.items(c)},init:function(d){var e=this;d.on("selectItems.dt.DT",function(g,f,k){e.active(k===c)})}}});h(t).on("preInit.dt.dtSelect",function(a,b){"dt"===a.namespace&&m.select.init(new m.Api(b))});return m.select});

View File

@@ -174,6 +174,12 @@ $db->close();
<?php echo date("Y-m-d", ((int)file_get_contents( "buildtimestamp.txt")));?>
</div>
</div>
<div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_Running_Version');?></div>
<div class="db_info_table_cell">
<?php include 'php/templates/version.php'; ?>
</div>
</div>
<div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_database_path');?></div>
<div class="db_info_table_cell">
@@ -237,6 +243,12 @@ $db->close();
<?= lang('Maintenance_Tools_Tab_Logging');?>
</a>
</li>
<li>
<a id="tab_multiEdit_id" href="#tab_multiEdit" data-toggle="tab">
<i class="fa fa-pencil pointer" ></i>
<?= lang('Device_MultiEdit');?>
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab_Settings">
@@ -389,7 +401,7 @@ $db->close();
<!-- ---------------------------Logging-------------------------------------------- -->
<div class="tab-pane" id="tab_Logging">
<div class="db_info_table">
<div class="log-area">
<div class="log-area box box-solid box-primary">
<div class="row logs-row">
<textarea id="pialert_log" class="logs" cols="70" rows="10" wrap='off' readonly >
<?php
@@ -415,7 +427,7 @@ $db->close();
</div>
</div>
</div>
<div class="log-area">
<div class="log-area box box-solid box-primary">
<div class="row logs-row">
<textarea id="pialert_front_log" class="logs" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/pialert_front.log" ); ?>
</textarea>
@@ -431,7 +443,7 @@ $db->close();
</div>
</div>
</div>
<div class="log-area">
<div class="log-area box box-solid box-primary">
<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>
@@ -447,44 +459,18 @@ $db->close();
</div>
</div>
</div>
<div class="log-area">
<div class="log-area box box-solid box-primary ">
<div class="row logs-row">
<textarea id="pialert_pholus_lastrun_log" class="logs logs-small" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/pialert_pholus_lastrun.log" ); ?>
<textarea id="nginx_error_log" class="logs logs-small" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "/var/log/nginx/error.log" ); ?>
</textarea>
</div>
<div class="row logs-row" >
<div>
<div class="log-file">pialert_pholus_lastrun.log<div class="logs-size"><?php echo number_format((filesize("./log/pialert_pholus_lastrun.log") / 1000000),2,",",".") . ' MB';?>
<span class="span-padding"><a href="./log/pialert_pholus_lastrun.log"><i class="fa fa-download"></i> </a></span>
</div></div>
<div class="log-purge">
<button class="btn btn-primary" onclick="logManage('pialert_pholus_lastrun.log','cleanLog')"><?= lang('Gen_Purge');?></button>
<div class="log-file" title="/var/log/nginx/error.log">nginx/error.log</div>
</div>
</div>
</div>
</div>
<div class="log-area">
<div class="row logs-row">
<textarea id="IP_changes_log" class="logs logs-small" cols="70" rows="10" readonly><?php echo file_get_contents( "./log/IP_changes.log" ); ?>
</textarea>
</div>
<div class="row logs-row" >
<div>
<div class="log-file">IP_changes.log<div class="logs-size"><?php echo number_format((filesize("./log/IP_changes.log") / 1000000),2,",",".") . ' MB';?>
<span class="span-padding"><a href="./log/IP_changes.log"><i class="fa fa-download"></i> </a></span>
</div></div>
<div class="log-purge">
<button class="btn btn-primary" onclick="logManage('IP_changes.log','cleanLog')"><?= lang('Gen_Purge');?></button>
</div>
</div>
</div>
</div>
<div class="log-area">
<div class="log-area box box-solid box-primary">
<div class="row logs-row">
<textarea id="stdout_log" class="logs logs-small" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/stdout.log" ); ?>
</textarea>
@@ -501,7 +487,7 @@ $db->close();
</div>
</div>
<div class="log-area">
<div class="log-area box box-solid box-primary">
<div class="row logs-row">
<textarea id="stderr_log" class="logs logs-small" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/stderr.log" ); ?>
</textarea>
@@ -521,8 +507,21 @@ $db->close();
</div>
</div>
<!-- ---------------------------Bulk edit -------------------------------------------- -->
<div class="tab-pane" id="tab_multiEdit">
<div class="db_info_table">
<div class="box box-solid">
<?php
require 'multiEditCore.php';
?>
</div>
</div>
</div>
</div>
<!-- ------------------------------------------------------------------------------ -->
</div>
<div class="box-body" style="text-align: center;">
@@ -780,11 +779,19 @@ function performLogManage() {
showModalOk ('Result', data );
}
})
}
}
// --------------------------------------------------------
// scroll down the log areas
function scrollDown()
{
setTimeout(() => {
var elementToCheck = $("#tab_Logging_id");
// Check if the parent <li> is active
if (elementToCheck.parent().hasClass("active")) {
{
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++) {
@@ -797,6 +804,12 @@ function scrollDown()
}
}
}
}
}, 200);
}
// --------------------------------------------------------
@@ -836,6 +849,8 @@ function saveSelectedColumns () {
function initializeSelectedColumns () {
$.get('php/server/parameters.php?action=get&expireMinutes=525600&defaultValue='+colDefaultOrderTxt+'&parameter=Front_Devices_Columns_Visible', function(data) {
handle_locked_DB(data)
tableColumnShow = numberArrayFromString(data);
for(i=0; i < tableColumnShow.length; i++)
@@ -845,30 +860,11 @@ function initializeSelectedColumns () {
$("#columnsSelect").append(option).trigger('change');
$(option).attr('eee','eee')
}
});
}
// --------------------------------------------------------
//Initialize Select2 Elements and make them sortable
$(function () {
selectEl = $('.select2').select2();
selectEl.next().children().children().children().sortable({
containment: 'parent', stop: function (event, ui) {
ui.item.parent().children('[title]').each(function () {
var title = $(this).attr('title');
var original = $( 'option:contains(' + title + ')', selectEl ).first();
original.detach();
selectEl.append(original)
});
selectEl.change();
}
});
});
@@ -877,6 +873,8 @@ $(function () {
// --------------------------------------------------------
function initializeTabs () {
setTimeout(function() {
key = "activeMaintenanceTab"
// default selection
@@ -885,10 +883,25 @@ function initializeTabs () {
// the #target from the url
target = window.location.hash.substr(1)
// get only the part between #...?
if(target.includes('?'))
{
target = target.split('?')[0]
}
console.log(target);
// update cookie if target specified
if(target != "")
{
setCache(key, target+'_id') // _id is added so it doesn't conflict with AdminLTE tab behavior
if (!selectedTab.endsWith("_id")) {
selectedTab = target + "_id";
}
setCache(key, selectedTab) // _id is added so it doesn't conflict with AdminLTE tab behavior
}
// get the tab id from the cookie (already overriden by the target)
@@ -908,41 +921,31 @@ function initializeTabs () {
// events on tab change
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href") // activated tab
if(target == "#tab_Logging")
{
scrollDown();
}
});
}, 50);
}
// --------------------------------------------------------
// save language in a cookie
$('#langselector').on('change', function (e) {
var optionSelected = $("option:selected", this);
var valueSelected = this.value;
setCookie("language",valueSelected )
location.reload();
});
// --------------------------------------------------------
// load footer asynchronously not to block the page load/other sections
window.onload = function asyncFooter()
{
initializeSelectedColumns();
scrollDown();
initializeTabs();
$("#lastCommit").append('<a href="https://github.com/jokob-sk/Pi.Alert/commits" target="_blank"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/jokob-sk/pi.alert/main?logo=github"></a>');
$("#lastDockerUpdate").append(
'<a href="https://hub.docker.com/r/jokobsk/pi.alert/tags" target="_blank"><img alt="Docker last pushed" src="https://img.shields.io/badge/dynamic/json?color=blue&label=Last%20pushed&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"></a>');
'<a href="https://github.com/jokob-sk/Pi.Alert/releases" target="_blank"><img alt="Docker last pushed" src="https://img.shields.io/github/v/release/jokob-sk/Pi.Alert?color=0aa8d2&logoColor=fff&logo=GitHub&label=Latest"></a>');
}
// scroll to the latest log entrie sat teh bottom of the file
</script>
<link rel="stylesheet" href="lib/AdminLTE/bower_components/select2/dist/css/select2.min.css">
@@ -950,6 +953,3 @@ window.onload = function asyncFooter()
<script src="lib/AdminLTE/bower_components/jquery-ui/jquery-ui.min.js"></script>
<!-- ----------------------------------------------------------------------- -->
<script src="js/pialert_common.js"></script>

268
front/multiEditCore.php Executable file
View File

@@ -0,0 +1,268 @@
<div class="col-md-12">
<div class="box box-default">
<div class="box-header">
<h3 class="box-title"><?= lang('Gen_Selected_Devices');?></h3>
</div>
<div class="deviceSelector"></div>
<div class="callout callout-warning">
<h4><?= lang('Gen_Warning');?></h4>
<p><?= lang('Device_MultiEdit_Backup');?></p>
</div>
</div>
<div class="col-md-12">
<div class="box box-default">
<div class="box-header">
<h3 class="box-title"><?= lang('Device_MultiEdit_Fields');?></h3>
</div>
<div class="box-body">
<form id="multi-edit-form">
<!-- Form fields will be appended here -->
</form>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="box box-default">
<div class="box-header ">
<h3 class="box-title"><?= lang('Device_MultiEdit_MassActions');?></h3>
</div>
<div class="box-body">
<div class="col-md-2" style="">
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red" id="btnDeleteMAC" onclick="askDeleteSelectedDevices()"><?= lang('Maintenance_Tool_del_selecteddev');?></button>
</div>
<div class="col-md-10"><?= lang('Maintenance_Tool_del_selecteddev_text');?></div>
</div>
</div>
</div>
</div>
<script defer>
// -------------------------------------------------------------------
// Get plugin and settings data from API endpoints
function getData(){
$.get('api/table_settings.json?nocache=' + Date.now(), function(res) {
settingsData = res["data"];
excludedColumns = ["NEWDEV_dev_MAC", "NEWDEV_dev_FirstConnection", "NEWDEV_dev_LastConnection", "NEWDEV_dev_LastNotification", "NEWDEV_dev_LastIP", "NEWDEV_dev_StaticIP", "NEWDEV_dev_ScanCycle", "NEWDEV_dev_PresentLastScan" ]
const relevantColumns = settingsData.filter(set =>
set.Group === "NEWDEV" &&
set.Code_Name.includes("_dev_") &&
!excludedColumns.includes(set.Code_Name) &&
!set.Code_Name.includes("__metadata")
);
const generateSimpleForm = columns => {
const form = $('#multi-edit-form');
const numColumns = 2; // Number of columns
// Calculate number of elements per column
const elementsPerColumn = Math.ceil(columns.length / numColumns);
// Divide columns equally
for (let i = 0; i < numColumns; i++) {
const column = $('<div>').addClass('col-md-6');
// Append form groups to the column
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) {
let inputType;
switch (columns[j].Type) {
case 'integer.checkbox':
case 'checkbox':
inputType = 'checkbox';
break;
case 'text.select':
inputType = 'text.select';
break;
default:
inputType = 'text';
break;
}
// Add classes specifically for checkboxes
if (inputType === 'text.select') {
targetLocation = columns[j].Code_Name + "_initSettingDropdown"
initSettingDropdown(columns[j].Code_Name, [], targetLocation)
input = `<select class="form-control"
id="${columns[j].Code_Name}"
data-my-column="${columns[j].Code_Name}"
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option>
</select>`
} else {
if (inputType === 'checkbox') {
inputClass = 'checkbox';
} else {
inputClass = 'form-control';
}
input = `<input class="${inputClass}"
id="${columns[j].Code_Name}"
data-my-column="${columns[j].Code_Name}"
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}"
type="${inputType}">`
}
const inputEntry = `<div class="form-group col-sm-12" >
<label class="col-sm-3 control-label">${columns[j].Display_Name}</label>
<div class="col-sm-9">
<div class="input-group red-hover-border">
${input}
<span class="input-group-addon pointer red-hover-background" onclick="massUpdateField('${columns[j].Code_Name}');" title="${getString('Device_MultiEdit_Tooltip')}">
<i class="fa fa-save"></i>
</span>
</div>
</div>
</div>`
column.append(inputEntry);
}
form.append(column);
}
};
console.log(relevantColumns)
generateSimpleForm(relevantColumns);
})
}
// -----------------------------------------------------------------------------
// Get selected devices Macs
function selectorMacs () {
return $('.deviceSelector select').val().join(',');
}
// -----------------------------------------------------------------------------
// Update specified field over the specified DB column and selected entry(ies)
function massUpdateField(id) {
// Get the input element
var inputElement = $(`#${id}`);
console.log(inputElement);
console.log(id);
// Initialize columnValue variable
var columnValue;
// Check the type of the input element
if (inputElement.is(':checkbox')) {
// For checkboxes, set the value to 1 if checked, otherwise set it to 0
columnValue = inputElement.is(':checked') ? 1 : 0;
} else {
// For other input types (like textboxes), simply retrieve their values
columnValue = inputElement.val();
}
var targetColumns = inputElement.attr('data-my-targetColumns');
console.log(targetColumns);
console.log(columnValue);
// update selected
executeAction('update', 'dev_MAC', selectorMacs(), targetColumns, columnValue )
}
// -----------------------------------------------------------------------------
// action: Represents the action to be performed, a CRUD operation like "update", "delete", etc.
// whereColumnName: Specifies the name of the column used in the WHERE or SELECT statement for filtering.
// key: Represents the unique identifier of the row or record to be acted upon.
// targetColumns: Indicates the columns to be updated or affected by the action.
// newTargetColumnValue: Specifies the new value to be assigned to the specified column(s).
function executeAction(action, whereColumnName, key, targetColumns, newTargetColumnValue )
{
$.get(`php/server/dbHelper.php?action=${action}&dbtable=Devices&columnName=${whereColumnName}&id=${key}&columns=${targetColumns}&values=${newTargetColumnValue}`, function(data) {
// console.log(data);
if (sanitize(data) == 'OK') {
showMessage(getString('Gen_DataUpdatedUITakesTime'));
// Remove navigation prompt "Are you sure you want to leave..."
window.onbeforeunload = null;
// update API endpoints to refresh the UI
updateApi()
} else {
showMessage(getString('Gen_LockedDB'));
}
});
}
// -----------------------------------------------------------------------------
// Ask to delete selected devices
function askDeleteSelectedDevices () {
// Ask
showModalWarning(
getString('Maintenance_Tool_del_alldev_noti'),
getString('Gen_AreYouSure'),
getString('Gen_Cancel'),
getString('Gen_Delete'),
'deleteSelectedDevices');
}
// -----------------------------------------------------------------------------
// Delete selected devices
function deleteSelectedDevices()
{
executeAction('delete', 'dev_MAC', selectorMacs() )
}
getData();
</script>
<!-- ----------------------------------------------------------------------- -->
<script src="js/ui_components.js"></script>
<script src="js/db_methods.js"></script>
<!-- ----------------------------------------------------------------------- -->

View File

@@ -8,7 +8,6 @@
define('circle_online', '<div class="badge bg-green text-white" style="width: 10px; height: 10px; padding:2px; margin-top: -25px;">&nbsp;</div>');
define('circle_offline', '<div class="badge bg-red text-white" style="width: 10px; height: 10px; padding:2px; margin-top: -25px;">&nbsp;</div>');
$NETWORKTYPES = getNetworkTypes();
?>
<!-- Page ------------------------------------------------------------------ -->
@@ -125,7 +124,10 @@
</td>
<td>
<a href="./network.php?mac='.$idParentMac.'">
<b class="anonymize">'.$idParentMac.' <i class="fa fa-square-up-right"></i></b>
<b class="anonymize">
<span class="mac-to-name" my-data-mac="'.$node_parent_mac.'">'.$node_parent_mac.' </span>
<i class="fa fa-square-up-right"></i>
</b>
</a>
</td>
</tr>
@@ -268,6 +270,8 @@
// \
// PC (leaf) <------- leafs are not included in this SQL query
$networkDeviceTypes = str_replace("]", "",(str_replace("[", "", getSettingValue("NETWORK_DEVICE_TYPES"))));
$sql = "SELECT node_name, node_mac, online, node_type, node_ports_count, parent_mac, node_icon
FROM
(
@@ -278,7 +282,7 @@
a.dev_Network_Node_MAC_ADDR as parent_mac,
a.dev_Icon as node_icon
FROM Devices a
WHERE a.dev_DeviceType in ('AP', 'Gateway', 'Firewall', 'Hypervisor', 'Powerline', 'Switch', 'WLAN', 'PLC', 'Router','USB LAN Adapter', 'USB WIFI Adapter', 'Internet')
WHERE a.dev_DeviceType in (".$networkDeviceTypes.")
) t1
LEFT JOIN
(
@@ -450,13 +454,20 @@
<script src="lib/treeviz/index.js"></script>
<script src="lib/treeviz/require.js"></script>
<script src="js/pialert_common.js"></script>
<script>
$.get('php/server/devices.php?action=getDevicesList&status=all&forceDefaultOrder', function(data) {
rawData = JSON.parse (data)
if(rawData["data"] == "")
{
showModalOK (getString('Gen_Warning'), getString('Network_NoDevices'))
return;
}
devicesListnew = rawData["data"].map(item => { return {
"name":item[0],
"type":item[2],
@@ -551,6 +562,7 @@
function getHierarchy()
{
for(i in deviceListGlobal)
{
if(deviceListGlobal[i].mac == 'Internet')
@@ -595,26 +607,37 @@
}
// ---------------------------------------------------------------------------
// Handle network node click - select correct tab in teh bottom table
function handleNodeClick(event)
{
console.log(event.target.offsetParent.offsetParent)
console.log(event.target.offsetParent)
const targetTabMAC = $(event.target.offsetParent).attr("data-mytreemacmain");
const targetTabMAC = $(event.target.offsetParent.offsetParent).attr("data-mytreemacmain");
var targetTab = $(`a[data-mytabmac="${targetTabMAC}"]`);
// Simulate a click event on the target tab
targetTab.click();
}
// ---------------------------------------------------------------------------
var myTree;
var treeAreaHeight = 800;
var visibleTreeArea = $(window).height()-135;
var treeAreaHeight = visibleTreeArea > 800 ? 800 : visibleTreeArea;
var emSize;
var nodeHeight;
var sizeCoefficient = 1
function initTree(myHierarchy)
{
if(myHierarchy.type == "")
{
showModalOk(getString('Network_Configuration_Error'), getString('Network_Root_Not_Configured'))
return;
}
// calculate the font size of the leaf nodes to fit everything into the tree area
leafNodesCount == 0 ? 1 : leafNodesCount;
emSize = ((treeAreaHeight/(25*leafNodesCount)).toFixed(2));
@@ -625,11 +648,8 @@
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${$('.content-header').width()}px`)
console.log('here')
myTree = Treeviz.create({
htmlId: "networkTree",
renderNode: nodeData => {
var fontSize = "font-size:"+emSize+"em;";
@@ -638,26 +658,48 @@
(port == "" || port == 0 ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
// Build HTML for individual nodes in the network diagram
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ? "<div class='netIcon ' ><i class='fa fa-"+nodeData.data.icon +"'></i></div>" : "";
devicePort = `<div class='netPort ' style="width:${emSize*2.7}em;height:${emSize*2.7}em" >${port}</div> <div class="portBckgIcon" style="margin-left:-${emSize*2.5}em;">${portBckgIcon}</div>`;
collapseExpandIcon = nodeData.data.hiddenChildren ? "square-plus" :"square-minus";
collapseExpandHtml = (nodeData.data.hasChildren) ? "<div class='netCollapse' style='font-size:"+emSize*2.5+"em;' data-mytreepath='"+nodeData.data.path+"' data-mytreemac='"+nodeData.data.mac+"'><i class='fa fa-"+ collapseExpandIcon +" pointer'></i></div>" : "";
statusCss = " netStatus-" + nodeData.data.status;
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ?
`<div class="netIcon">
<i class="fa fa-${nodeData.data.icon}"></i>
</div>` : "";
devicePort = `<div class="netPort"
style="width:${emSize*sizeCoefficient}em;height:${emSize*sizeCoefficient}em">
${port}</div>
<div class="portBckgIcon"
style="margin-left:-${emSize*sizeCoefficient}em;">
${portBckgIcon}
</div>`;
collapseExpandIcon = nodeData.data.hiddenChildren ?
"square-plus" : "square-minus";
// generate +/- icon if node has children nodes
collapseExpandHtml = nodeData.data.hasChildren ?
`<div class="netCollapse"
style="font-size:${emSize*sizeCoefficient}em;top:${1/2*emSize*sizeCoefficient}em"
data-mytreepath="${nodeData.data.path}"
data-mytreemac="${nodeData.data.mac}">
<i class="fa fa-${collapseExpandIcon} pointer"></i>
</div>` : "";
selectedNodeMac = $(".nav-tabs-custom .active a").attr('data-mytabmac')
highlightedCss = nodeData.data.mac == selectedNodeMac ? " highlightedNode" : "";
highlightedCss = nodeData.data.mac == selectedNodeMac ?
" highlightedNode" : "";
return result = `<div class='box ${(nodeData.data.hasChildren)? "pointer":""} ${statusCss} ${highlightedCss}'
data-mytreemacmain='${nodeData.data.mac}'
style='height:${nodeData.settings.nodeHeight}px;${fontSize}
// css indicating online/offline status
statusCss = ` netStatus-${nodeData.data.status}`;
return result = `<div class="box ${nodeData.data.hasChildren ? "pointer":""} ${statusCss} ${highlightedCss}"
data-mytreemacmain="${nodeData.data.mac}"
style="height:${nodeData.settings.nodeHeight}px;${fontSize}"
>
<div class='netNodeText '>\
<div class="netNodeText">
<strong>${devicePort} ${deviceIcon}
<span class='spanNetworkTree anonymizeDev'>${nodeData.data.name}</span>\
<span class="spanNetworkTree anonymizeDev">${nodeData.data.name}</span>
</strong>
${collapseExpandHtml}
</div></div>`;
</div>
</div>`;
},
onNodeClick: nodeData => {
@@ -679,11 +721,12 @@
relationnalField: "children",
});
console.log('vvvv')
console.log(myHierarchy)
console.log('^^^^^^^')
myTree.refresh(myHierarchy);
// hide spinning icon
hideSpinner()
}
// ---------------------------------------------------------------------------
@@ -731,6 +774,25 @@
}
// ---------------------------------------------------------------------------
function initDeviceNamesFromMACs()
{
$('.mac-to-name').each(function() {
var dataMacValue = $(this).attr('my-data-mac');
if(dataMacValue =="" )
{
$(this).html(getString("Network_Root"))
}
else{
$(this).html(getNameByMacAddress(dataMacValue));
}
});
}
// ---------------------------------------------------------------------------
function initButtons()
{
@@ -754,24 +816,38 @@
// init the Assign buttons
$('#unassignedDevices button[data-myleafmac]').each(function(){
$(this).attr('onclick', 'updateLeaf("'+$(this).attr('data-myleafmac')+'","'+currentNodeMac+'")')
$(this).attr('onclick', `updateLeaf("${$(this).attr('data-myleafmac')}","${currentNodeMac}")`)
});
// init Unassign buttons
$('#assignedDevices button[data-myleafmac]').each(function(){
$(this).attr('onclick', 'updateLeaf("'+$(this).attr('data-myleafmac')+'","")')
$(this).attr('onclick', `updateLeaf("${$(this).attr('data-myleafmac')}","")`)
});
}
// ---------------------------------------------------------------------------
function updateLeaf(leafMac,nodeMac)
{
console.log(leafMac)
console.log(nodeMac)
console.log(leafMac) // child
console.log(nodeMac) // parent
console.log(nodeMac != "") // parent
// prevent the assignment of the Internet root node avoiding recursion when generating the network tree topology
if(leafMac.toLowerCase().includes('internet') && nodeMac != "")
{
showMessage(getString('Network_Cant_Assign'))
}
else{
saveData('updateNetworkLeaf', leafMac, nodeMac);
setTimeout("location.reload();", 1000); // refresh page after 1s
setTimeout("location.reload();", 500); // refresh page
}
}
// show spinning icon
showSpinner()
// init device names where macs are used
initDeviceNamesFromMACs();
// init selected (first) tab
initTab();

View File

@@ -56,6 +56,10 @@
$columns = $_REQUEST['columns'];
}
if (isset ($_REQUEST['rawSql'])) {
$rawSql = $_REQUEST['rawSql'];
}
if (isset ($_REQUEST['dbtable'])) {
$dbtable = $_REQUEST['dbtable'];
}
@@ -64,92 +68,148 @@
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 'create': create($defaultValue, $expireMinutes, $dbtable, $columns, $values ); break;
case 'read' : read($rawSql); break;
case 'update': update($columnName, $id, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
case 'delete': delete($columnName, $id, $dbtable); break;
default: logServerConsole ('Action: '. $action); break;
}
}
//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
function read($rawSql) {
global $db;
// Construct the SQL query to select values
$sql = $rawSql;
// Execute the SQL query
$result = $db->query($sql);
// Check if the query executed successfully
if (! $result == TRUE) {
// Output an error message if the query failed
echo "Error reading data\n\n " .$sql." \n\n". $db->lastErrorMsg();
return;
} else
{
// Output $result
// Fetching rows from the result object and storing them in an array
$rows = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$rows[] = $row;
}
// Converting the array to JSON
$json = json_encode($rows);
// Outputting the JSON
echo $json;
return;
}
}
//------------------------------------------------------------------------------
// update
//------------------------------------------------------------------------------
function update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
function update($columnName, $id, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
global $db;
// handle one or multiple columns
if(strpos($columns, ',') !== false)
{
// Handle one or multiple columns
if(strpos($columns, ',') !== false) {
$columnsArr = explode(",", $columns);
}else
{
} else {
$columnsArr = array($columns);
}
// handle one or multiple values
if(strpos($values, ',') !== false)
{
// Handle one or multiple values
if(strpos($values, ',') !== false) {
$valuesArr = explode(",", $values);
} else
{
} else {
$valuesArr = array($values);
}
$columnValues = '';
$index = 0;
foreach($columnsArr as $column)
{
$columnValues = $columnValues .' "' .$column.'" = "'.$valuesArr[$index] . '",' ;
$index = $index + 1;
// Handle one or multiple IDs
if(strpos($id, ',') !== false) {
$idsArr = explode(",", $id);
$idsPlaceholder = rtrim(str_repeat('?,', count($idsArr)), ',');
} else {
$idsArr = array($id);
$idsPlaceholder = '?';
}
$columnValues = substr($columnValues, 0, -1);
// Build column-value pairs string
$columnValues = '';
foreach($columnsArr as $column) {
$columnValues .= '"' . $column . '" = ?,';
}
// Remove trailing comma
$columnValues = rtrim($columnValues, ',');
// Update value
$sql = 'UPDATE '.$dbtable.' SET '. $columnValues .'
WHERE "'. $columnName .'"="'. $id.'"';
$result = $db->query($sql);
// Construct the SQL query
$sql = 'UPDATE ' . $dbtable . ' SET ' . $columnValues . ' WHERE ' . $columnName . ' IN (' . $idsPlaceholder . ')';
if (! $result == TRUE) {
echo "Error updating parameter\n\n$sql \n\n". $db->lastErrorMsg();
// Prepare the statement
$stmt = $db->prepare($sql);
// Check for errors
if(!$stmt) {
echo "Error preparing statement: " . $db->lastErrorMsg();
return;
}
// Bind the parameters
$paramTypes = str_repeat('s', count($columnsArr));
foreach($valuesArr as $i => $value) {
$stmt->bindValue($i + 1, $value);
}
foreach($idsArr as $i => $idValue) {
$stmt->bindValue(count($valuesArr) + $i + 1, $idValue);
}
// Execute the statement
$result = $stmt->execute();
$changes = $db->changes();
if ($changes == 0) {
// Insert new value
create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values);
create( $defaultValue, $expireMinutes, $dbtable, $columns, $values);
}
// update cache
$uniqueHash = hash('ripemd160', $dbtable . $columns);
setCache($uniqueHash, $values, $expireMinutes);
echo 'OK';
echo 'OK' ;
}
//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
function create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values)
function create( $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();
echo "NOT IMPLEMENTED!\n\n";
return;
}
// // 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;
// }
}
//------------------------------------------------------------------------------
@@ -159,35 +219,49 @@ function delete($columnName, $id, $dbtable)
{
global $db;
// handle one or multiple ids
// Handle one or multiple ids
if(strpos($id, ',') !== false)
{
$idsArr = explode(",", $id);
}else
} else
{
$idsArr = array($id);
}
// Initialize an empty string to store the comma-separated list of IDs
$idsStr = "";
foreach ($idsArr as $item)
// Iterate over each ID
foreach ($idsArr as $index => $item)
{
$idsStr = $idsStr . '"' .$item.'"';
// Append the current ID to the string
$idsStr .= '"' . $item . '"';
// Add a comma if the current ID is not the last one
if ($index < count($idsArr) - 1) {
$idsStr .= ', ';
}
}
// Insert new value
// Construct the SQL query to delete entries based on the given IDs
$sql = 'DELETE FROM '.$dbtable.' WHERE "'.$columnName.'" IN ('. $idsStr .')';
// Execute the SQL query
$result = $db->query($sql);
// Check if the query executed successfully
if (! $result == TRUE) {
echo "Error deleting entry\n\n$sql \n\n". $db->lastErrorMsg();
// Output an error message if the query failed
echo "Error deleting entry\n\n".$sql." \n\n". $db->lastErrorMsg();
return;
} else
{
echo lang('Gen_DataUpdatedUITakesTime');
// Output 'OK' if the deletion was successful
echo 'OK' ;
return;
}
}
?>

View File

@@ -77,7 +77,7 @@ function getDeviceData() {
// Device Data
$sql = 'SELECT rowid, *,
CASE WHEN dev_AlertDeviceDown=1 AND dev_PresentLastScan=0 THEN "Down"
CASE WHEN dev_AlertDeviceDown !=0 AND dev_PresentLastScan=0 THEN "Down"
WHEN dev_PresentLastScan=1 THEN "On-line"
ELSE "Off-line" END as dev_Status
FROM Devices
@@ -92,7 +92,7 @@ function getDeviceData() {
$deviceData['dev_FirstConnection'] = formatDate ($row['dev_FirstConnection']); // Date formated
$deviceData['dev_LastConnection'] = formatDate ($row['dev_LastConnection']); // Date formated
$deviceData['dev_RandomMAC'] = ( in_array($mac[1], array("2","6","A","E","a","e")) ? 1 : 0);
$deviceData['dev_RandomMAC'] = isRandomMAC($mac);
// Count Totals
$condition = ' WHERE eve_MAC="'. $mac .'" AND eve_DateTime >= '. $periodDate;
@@ -225,7 +225,7 @@ function deleteUnknownDevices() {
global $db;
// sql
$sql = 'DELETE FROM Devices WHERE dev_Name="(unknown)"';
$sql = 'DELETE FROM Devices WHERE dev_Name="(unknown)" OR dev_Name="(name not found)"';
// execute sql
$result = $db->query($sql);
@@ -545,7 +545,7 @@ function getDevicesTotals() {
// combined query
$result = $db->query(
'SELECT
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('all').') as devices,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('my').') as devices,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('connected').') as connected,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('favorites').') as favorites,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('new').') as new,
@@ -626,7 +626,7 @@ function getDevicesList() {
$sql = 'SELECT * FROM (
SELECT rowid, *, CASE
WHEN t1.dev_AlertDeviceDown=1 AND t1.dev_PresentLastScan=0 THEN "Down"
WHEN t1.dev_AlertDeviceDown !=0 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
@@ -646,16 +646,18 @@ function getDevicesList() {
$tableData = array();
while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
$defaultOrder = array ($row['dev_Name'],
$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),
( isRandomMAC($row['dev_MAC']) ),
$row['dev_Status'],
$row['dev_MAC'], // MAC (hidden)
formatIPlong ($row['dev_LastIP']), // IP orderable
@@ -688,6 +690,32 @@ function getDevicesList() {
}
//------------------------------------------------------------------------------
// Determine if Random MAC
//------------------------------------------------------------------------------
function isRandomMAC($mac) {
$isRandom = false;
// if detected as random, make sure it doesn't start with a prefix which teh suer doesn't want to mark as random
$setting = getSettingValue("UI_NOT_RANDOM_MAC");
$prefixes = createArray($setting);
$isRandom = in_array($mac[1], array("2", "6", "A", "E", "a", "e"));
// If detected as random, make sure it doesn't start with a prefix which the user doesn't want to mark as random
if ($isRandom) {
foreach ($prefixes as $prefix) {
if (strpos($mac, $prefix) === 0) {
$isRandom = false;
break;
}
}
}
return $isRandom;
}
//------------------------------------------------------------------------------
// Query the List of devices for calendar
//------------------------------------------------------------------------------
@@ -755,7 +783,12 @@ function getNetworkNodes() {
global $db;
// Device Data
$sql = 'SELECT * FROM Devices WHERE dev_DeviceType in ( "AP", "Gateway", "Firewall", "Hypervisor", "Powerline", "Switch", "WLAN", "PLC", "Router","USB LAN Adapter", "USB WIFI Adapter")';
$networkDeviceTypes = str_replace("]", "",(str_replace("[", "", getSettingValue("NETWORK_DEVICE_TYPES"))));
$sql = 'SELECT * FROM Devices WHERE dev_DeviceType in ( '. $networkDeviceTypes .' )';
// echo $sql;
$result = $db->query($sql);
@@ -852,8 +885,6 @@ function getDevices() {
function getDeviceTypes() {
global $db;
$networkTypes = getNetworkTypes();
// SQL
$sql = 'SELECT DISTINCT 9 as dev_Order, dev_DeviceType
FROM Devices
@@ -1001,8 +1032,8 @@ function getLocations() {
// ----------------------------------------------------------------------------------------
function updateNetworkLeaf()
{
$nodeMac = $_REQUEST['value'];
$leafMac = $_REQUEST['id'];
$nodeMac = $_REQUEST['value']; // parent
$leafMac = $_REQUEST['id']; // child
if ((false === filter_var($nodeMac , FILTER_VALIDATE_MAC) && $nodeMac != "Internet" && $nodeMac != "") || false === filter_var($leafMac , FILTER_VALIDATE_MAC) ) {
throw new Exception('Invalid mac address');
@@ -1129,10 +1160,11 @@ function copyFromDevice() {
function getDeviceCondition ($deviceStatus) {
switch ($deviceStatus) {
case 'all': return 'WHERE dev_Archived=0'; break;
case 'my': return 'WHERE dev_Archived=0'; break;
case 'connected': return 'WHERE dev_Archived=0 AND dev_PresentLastScan=1'; break;
case 'favorites': return 'WHERE dev_Archived=0 AND dev_Favorite=1'; break;
case 'new': return 'WHERE dev_Archived=0 AND dev_NewDevice=1'; break;
case 'down': return 'WHERE dev_Archived=0 AND dev_AlertDeviceDown=1 AND dev_PresentLastScan=0'; break;
case 'down': return 'WHERE dev_Archived=0 AND dev_AlertDeviceDown !=0 AND dev_PresentLastScan=0'; break;
case 'archived': return 'WHERE dev_Archived=1'; break;
default: return 'WHERE 1=0'; break;
}

View File

@@ -13,28 +13,47 @@ require dirname(__FILE__).'/../templates/skinUI.php';
$FUNCTION = [];
$SETTINGS = [];
$ACTION = "";
// init request params
if(array_key_exists('function', $_REQUEST) != FALSE)
{
$FUNCTION = $_REQUEST['function'];
}
if(array_key_exists('settings', $_REQUEST) != FALSE)
{
$SETTINGS = $_REQUEST['settings'];
}
// call functions based on requested params
if ($FUNCTION == 'savesettings')
{
switch ($FUNCTION) {
case 'savesettings':
saveSettings();
}
elseif ($FUNCTION == 'cleanLog')
{
break;
case 'cleanLog':
cleanLog($SETTINGS);
break;
case 'addToExecutionQueue':
if(array_key_exists('action', $_REQUEST) != FALSE)
{
$ACTION = $_REQUEST['action'];
}
addToExecutionQueue($ACTION);
break;
default:
// Handle any other cases or errors if needed
break;
}
//------------------------------------------------------------------------------
// Formatting data functions
//------------------------------------------------------------------------------
@@ -166,7 +185,7 @@ function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFil
echo '<script>alert(escape("'.$message.'"));</script>';
}
// F12 Browser console
// F12 Browser dev console
if($logConsole)
{
echo '<script>console.log(escape("'.str_replace('"',"'",$message).'"));</script>';
@@ -175,16 +194,26 @@ function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFil
//File
if($logFile)
{
if (is_writable($logFolderPath.$log_file)) {
if(file_exists($logFolderPath.$log_file) != 1) // file doesn't exist, create one
{
$log = fopen($logFolderPath.$log_file, "w") or die("Unable to open file!");
}else // file exists, append
{
$log = fopen($logFolderPath.$log_file, "a") or die("Unable to open file!");
$log = fopen($logFolderPath.$log_file, "a") or die("Unable to open file - Permissions issue!");
}
fwrite($log, "[".$timestamp. "] " . str_replace('<br>',"\n ",str_replace('<br/>',"\n ",$message)).PHP_EOL."" );
fclose($log);
} else {
echo 'The file is not writable: '.$logFolderPath.$log_file;
}
}
//echo
@@ -195,6 +224,25 @@ function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFil
}
// Adds an action to perform into the execution_queue.log file
function addToExecutionQueue($action)
{
global $logFolderPath, $timestamp;
$logFile = 'execution_queue.log';
$fullPath = $logFolderPath . $logFile;
// Open the file or skip if it can't be opened
if ($file = fopen($fullPath, 'a')) {
fwrite($file, "[" . $timestamp . "]|" . $action . PHP_EOL);
fclose($file);
displayMessage('Action "'.$action.'" added to the execution queue.', false, true, true, true);
} else {
displayMessage('Log file not found or couldn\'t be created.', false, true, true, true);
}
}
// ----------------------------------------------------------------------------------------
function cleanLog($logFile)
{
@@ -328,13 +376,18 @@ function saveSettings()
$txt = $txt."#-------------------IMPORTANT INFO-------------------#\n";
// open new file and write the new configuration
$newConfig = fopen($fullConfPath, "w") or die("Unable to open file!");
fwrite($newConfig, $txt);
fclose($newConfig);
// Create a temporary file
$tempConfPath = $fullConfPath . ".tmp";
displayMessage("<br/>Settings saved to the <code>".$config_file."</code> file.
<br/><br/>Backup of the previous ".$config_file." created here: <br/><br/><code>".$new_name."</code><br/><br/>
<b>Note:</b> Wait at least <b>5s</b> for the changes to reflect in the UI. (longer if for example a <a href='#state'>Scan is running</a>)",
// Write your changes to the temporary file
$tempConfig = fopen($tempConfPath, "w") or die("Unable to open file!");
fwrite($tempConfig, $txt);
fclose($tempConfig);
// Replace the original file with the temporary file
rename($tempConfPath, $fullConfPath);
displayMessage("<br/>Settings saved to the <code>pialert.conf</code> file.<br/><br/>A time-stamped backup of the previous file created. <br/><br/> Reloading...<br/>",
FALSE, TRUE, TRUE, TRUE);
}
@@ -351,6 +404,38 @@ function getString ($codeName, $default) {
return $default;
}
// -------------------------------------------------------------------------------------------
function getSettingValue($codeName) {
// Define the JSON endpoint URL
$url = dirname(__FILE__).'/../../../front/api/table_settings.json';
// Fetch the JSON data
$json = file_get_contents($url);
// Check if the JSON data was successfully fetched
if ($json === false) {
return 'Could not get json data';
}
// Decode the JSON data
$data = json_decode($json, true);
// Check if the JSON decoding was successful
if (json_last_error() !== JSON_ERROR_NONE) {
return 'Could not decode json data';
}
// Search for the setting by Code_Name
foreach ($data['data'] as $setting) {
if ($setting['Code_Name'] === $codeName) {
return $setting['Value'];
// echo $setting['Value'];
}
}
// Return false if the setting was not found
return 'Could not find setting '.$codeName;
}
// -------------------------------------------------------------------------------------------
@@ -421,16 +506,7 @@ function handleNull ($text, $default = "") {
}
// -------------------------------------------------------------------------------------------
// Currently unused - should be source of truth for network types (or define somewhere else?)
function getNetworkTypes(){
$array = array(
"AP", "Gateway", "Firewall", "Hypervisor", "Powerline", "Switch", "WLAN", "PLC", "Router","USB LAN Adapter", "USB WIFI Adapter"
);
return $array;
}
// -------------------------------------------------------------------------------------------
function getDevicesColumns(){

View File

@@ -27,9 +27,8 @@
<div class="pull-right no-hidden-xs">
<!-- Pi.Alert footer with url -->
<?php
echo '<a href="https://github.com/jokob-sk/Pi.Alert" target="_blank">Pi.Alert</a>';
?>
<a href="https://github.com/jokob-sk/Pi.Alert" target="_blank">Pi.Alert</a>
</div>
</footer>
@@ -56,7 +55,7 @@
<!-- <script src="lib/AdminLTE/bower_components/fastclick/lib/fastclick.js"></script> -->
<!-- Pi.Alert -------------------------------------------------------------- -->
<script src="js/pialert_common.js"></script>
<script src="js/handle_version.js"></script>
</body>

View File

@@ -28,16 +28,17 @@ require dirname(__FILE__).'/security.php';
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- ----------------------------------------------------------------------- -->
<!-- REQUIRED JS SCRIPTS -->
<!-- jQuery 3 -->
<script src="lib/AdminLTE/bower_components/jquery/dist/jquery.min.js"></script>
<script src="js/pialert_common.js"></script>
<!-- Bootstrap 3.3.7 -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/bootstrap/dist/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/fontawesome.min.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/solid.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/brands.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/v5-font-face.css">
@@ -83,8 +84,13 @@ if ($ENABLED_DARKMODE === True) {
<script>
function updateState(){
getParam("state","Back_App_State", true)
setTimeout("updateState()", 5000);
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
document.getElementById('state').innerHTML = appState["currentState"].replaceAll('"', '');
setTimeout("updateState()", 1000);
})
}
function show_pia_servertime() {
@@ -99,12 +105,6 @@ if ($ENABLED_DARKMODE === True) {
setTimeout("show_pia_servertime()", 1000);
}
// refresh page on focus - adds a lot of SQL queries overhead onto the DB - disabling for now
// document.addEventListener("visibilitychange",()=>{
// if(document.visibilityState==="visible"){
// window.location.href = window.location.href.split('#')[0];
// }
// })
</script>
@@ -112,7 +112,7 @@ if ($ENABLED_DARKMODE === True) {
<!-- ----------------------------------------------------------------------- -->
<!-- Layout Boxed Yellow -->
<body class="hold-transition <?php echo $pia_skin_selected;?> sidebar-mini" <?php echo $BACKGROUND_IMAGE_PATCH;?> onLoad="show_pia_servertime();" >
<body class="hold-transition fixed <?php echo $pia_skin_selected;?> sidebar-mini" <?php echo $BACKGROUND_IMAGE_PATCH;?> onLoad="show_pia_servertime();" >
<!-- Site wrapper -->
<div class="wrapper">
@@ -123,9 +123,14 @@ if ($ENABLED_DARKMODE === True) {
<!-- Logo -->
<a href="devices.php" class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini">P<b>a</b></span>
<span class="logo-mini">
<img src="img/pialertLogoWhite.png" class="pia-top-left-logo" alt="Pi.Alert Logo"/>
</span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg">Pi<b>.Alert</b></span>
<span class="logo-lg">Pi<b>.Alert</b>
</span>
</a>
<!-- ----------------------------------------------------------------------- -->
@@ -146,9 +151,9 @@ if ($ENABLED_DARKMODE === True) {
<li>
<a id="next-button" href="javascript:history.go(1);" role="button" span class='of-bt-icon'><i class='fa fa-arrow-right'></i></a>
</li>
<!-- Reload -->
<!-- Clear cache & Reload -->
<li>
<a id="reload-button" href='#' role="button" span class='of-bt-icon' onclick='location.reload()'><i class='fa fa-repeat'></i></a>
<a id="reload-button" href='#' role="button" span class='of-bt-icon' onclick='clearCache()'><i class='fa fa-repeat'></i></a>
</li>
<!-- Full Screen -->
<li>
@@ -184,7 +189,7 @@ if ($ENABLED_DARKMODE === True) {
<img src="img/pialertLogoWhite.png" class="img-circle" alt="Pi.Alert Logo" style="border-color:transparent; height: 50px; width: 50px; margin-top:15px;">
<p style="float: right; width: 200px">
<?= lang('About_Title');?>
<small><?= lang('About_Design');?> Raspberry Pi</small>
<small><?= lang('About_Design');?> Docker</small>
</p>
</li>
@@ -209,62 +214,172 @@ if ($ENABLED_DARKMODE === True) {
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel (optional) -->
<div class="user-panel">
<a href="." class="logo">
<img src="img/pialertLogoGray80.png" class="img-responsive" alt="Pi.Alert Logo"/>
</a>
</div>
<!-- search form (Optional) -->
<!-- DELETED -->
<!-- Sidebar Menu -->
<!-- Navigation Mneu -->
<ul class="sidebar-menu" data-widget="tree">
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('devices.php', 'deviceDetails.php') ) ){ echo 'active'; } ?>">
<a href="devices.php"><i class="fa fa-laptop"></i> <span><?= lang('Navigation_Devices');?></span></a>
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('devices.php', 'deviceDetails.php') ) ){ echo 'active menu-open'; } ?>">
<a href="#" onclick="openUrl(['./devices.php', './deviceDetails.php'])">
<i class="fa fa-fw fa-laptop"></i> <span><?= lang('Navigation_Devices');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('devices.php', 'deviceDetails.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="devices.php#my" onclick="initializeDatatable('my')" > <?= lang("Device_Shortcut_AllDevices");?> </a>
</li>
<li>
<a href="devices.php#connected" onclick="initializeDatatable('connected')" > <?= lang("Device_Shortcut_Connected");?> </a>
</li>
<li>
<a href="devices.php#favorites" onclick="initializeDatatable('favorites')" > <?= lang("Device_Shortcut_Favorites");?> </a>
</li>
<li>
<a href="devices.php#new" onclick="initializeDatatable('new')" > <?= lang("Device_Shortcut_NewDevices");?> </a>
</li>
<li>
<a href="devices.php#down" onclick="initializeDatatable('down')" > <?= lang("Device_Shortcut_DownAlerts");?> </a>
</li>
<li>
<a href="devices.php#archived" onclick="initializeDatatable('archived')" > <?= lang("Device_Shortcut_Archived");?> </a>
</li>
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php') ) ){ echo 'active'; } ?>">
<a href="presence.php"><i class="fa fa-calendar"></i> <span><?= lang('Navigation_Presence');?></span></a>
</ul>
</li>
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('events.php') ) ){ echo 'active'; } ?>">
<a href="events.php"><i class="fa fa-bolt"></i> <span><?= lang('Navigation_Events');?></span></a>
<!-- Monitoring menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php', 'report.php', 'events.php' ) ) ){ echo 'active menu-open'; } ?>">
<a href="#">
<i class="fa fa-fw fa-chart-bar"></i> <span><?= lang('Navigation_Monitoring');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php', 'report.php', 'events.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="presence.php"> <?= lang("Navigation_Presence");?> </a>
</li>
<li>
<a href="events.php"> <?= lang("Navigation_Events");?> </a>
</li>
<li>
<a href="report.php"> <?= lang("Navigation_Report");?> </a>
</li>
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('report.php') ) ){ echo 'active'; } ?>">
<a href="report.php"><i class="fa fa-flag"></i> <span><?= lang('Navigation_Report');?></span></a>
</ul>
</li>
<!-- Network menu item -->
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('network.php') ) ){ echo 'active'; } ?>">
<a href="network.php"><i class="fa fa-fw fa-network-wired"></i> <span><?= 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"><i class="fa fa-fw fa-plug"></i> <span><?= lang('Navigation_Plugins');?></span></a>
<!-- Maintenance menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'active menu-open'; } ?>">
<a href="#" onclick="openUrl(['./maintenance.php'])">
<div class="info-icon-nav myhidden" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">🆕</div>
<i class="fa fa-fw fa-wrench"></i> <span><?= lang('Navigation_Maintenance');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="maintenance.php#tab_Settings" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_UISettings");?> </a>
</li>
<li>
<a href="maintenance.php#tab_DBTools" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_Tools");?> </a>
</li>
<li>
<a href="maintenance.php#tab_BackupRestore" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_BackupRestore");?> </a>
</li>
<li>
<a href="maintenance.php#tab_Logging" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_Logging");?> </a>
</li>
<li>
<a href="maintenance.php#tab_multiEdit" onclick="initializeTabs()"> <?= lang("Device_MultiEdit");?> </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>
</ul>
</li>
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('settings.php') ) ){ echo 'active'; } ?>">
<a href="settings.php"><i class="fa fa-cog"></i> <span><?= lang('Navigation_Settings');?></span></a>
<!-- Settings menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('settings.php') ) ){ echo 'active menu-open'; } ?>">
<a href="#" onclick="openUrl(['./settings.php'])">
<i class="fa fa-fw fa-cog"></i> <span><?= lang('Navigation_Settings');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('settings.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="settings.php#pageTitle"> <?= lang("settings_enabled");?> </a>
</li>
<!-- <li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('flows.php') ) ){ echo 'active'; } ?>">
<a href="flows.php"><i class="fa fa-shuffle"></i> <span><?= lang('Navigation_Flows');?></span></a>
</li> -->
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('systeminfo.php') ) ){ echo 'active'; } ?>">
<a href="systeminfo.php"><i class="fa fa-microchip"></i> <span><?= lang('Navigation_SystemInfo');?></span></a>
<li>
<a href="settings.php#core_content_header"> <?= lang("settings_core_label");?> </a>
</li>
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('help_faq.php') ) ){ echo 'active'; } ?>">
<a href="help_faq.php"><i class="fa fa-question"></i> <span><?= lang('Navigation_HelpFAQ');?></span></a>
<li>
<a href="settings.php#system_content_header"> <?= lang("settings_system_label");?> </a>
</li>
<!-- <li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('donations.php') ) ){ echo 'active'; } ?>">
<a href="donations.php"><i class="fa fa-heart"></i> <span><?= lang('Navigation_Donations');?></span></a>
</li> -->
<li>
<a href="settings.php#device_scanner_content_header"> <?= lang("settings_device_scanners_label");?> </a>
</li>
<li>
<a href="settings.php#other_content_header"> <?= lang("settings_other_scanners_label");?> </a>
</li>
<li>
<a href="settings.php#publisher_content_header"> <?= lang("settings_publishers_label");?> </a>
</li>
</ul>
</li>
<!-- Integrations menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'workflows.php' ) ) ){ echo 'active menu-open'; } ?>">
<a href="#">
<i class="fa fa-fw fa-plug"></i> <span><?= lang('Navigation_Integrations');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'workflows.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<div class="info-icon-nav work-in-progress"> </div>
<a href="workflows.php"><?= lang('Navigation_Workflows');?></a>
</li>
<li>
<a href="plugins.php"><?= lang("Navigation_Plugins");?> </a>
</li>
</ul>
</li>
<!-- About menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('donations.php', 'help_faq.php', 'systeminfo.php' ) ) ){ echo 'active menu-open'; } ?>">
<a href="#">
<i class="fa fa-fw fa-info"></i> <span><?= lang('Navigation_About');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('donations.php', 'help_faq.php', 'systeminfo.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="donations.php"> <?= lang("Navigation_Donations");?> </a>
</li>
<li>
<a href="help_faq.php"> <?= lang("Navigation_HelpFAQ");?> </a>
</li>
<li>
<a href="systeminfo.php"> <?= lang("Navigation_SystemInfo");?> </a>
</li>
</ul>
</li>
</ul>
<!-- /.sidebar-menu -->
@@ -272,31 +387,25 @@ if ($ENABLED_DARKMODE === True) {
<!-- /.sidebar -->
</aside>
<script src="js/pialert_common.js"></script>
<script defer>
// Generate work-in-progress icons
function workInProgress() {
if($(".work-in-progress").html().trim() == "")
{
$(".work-in-progress").append(`
<a href="https://github.com/jokob-sk/Pi.Alert/issues" target="_blank">
<b class="pointer" title="${getString("Gen_Work_In_Progress")}">🦺</b>
</a>
`)
}
}
//--------------------------------------------------------------
//--------------------------------------------------------------
function getParam(targetId, key, skipCache = false) {
skipCacheQuery = "";
if(skipCache)
{
skipCacheQuery = "&skipcache";
}
// get parameter value
$.get('php/server/parameters.php?action=get&defaultValue=NULL&parameter='+ key + skipCacheQuery, function(data) {
var result = data;
document.getElementById(targetId).innerHTML = result.replaceAll('"', '');
});
}
//--------------------------------------------------------------
function toggleFullscreen() {
@@ -316,5 +425,6 @@ if ($ENABLED_DARKMODE === True) {
// Update server state in the header
updateState()
workInProgress()
</script>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,650 @@
{
"API_CUSTOM_SQL_description": "",
"API_CUSTOM_SQL_name": "Point de terminaison personnalis\u00e9",
"API_display_name": "API",
"API_icon": "",
"About_Design": "Con\u00e7u pour\u202f:",
"About_Exit": "Quitter",
"About_Title": "Open Source Network Guard",
"AppEvents_DateTimeCreated": "Journalis\u00e9",
"AppEvents_Extra": "Extra",
"AppEvents_GUID": "",
"AppEvents_Helper1": "",
"AppEvents_Helper2": "",
"AppEvents_Helper3": "",
"AppEvents_ObjectForeignKey": "Cl\u00e9 \u00e9trang\u00e8re",
"AppEvents_ObjectIndex": "Index",
"AppEvents_ObjectIsArchived": "Est archiv\u00e9 (au moment de l'enregistrement)",
"AppEvents_ObjectIsNew": "",
"AppEvents_ObjectPlugin": "Greffon li\u00e9",
"AppEvents_ObjectPrimaryID": "",
"AppEvents_ObjectSecondaryID": "",
"AppEvents_ObjectStatus": "Statut (au moment de l'enregistrement)",
"AppEvents_ObjectStatusColumn": "Colonne d'\u00e9tat",
"AppEvents_ObjectType": "Type d'objet",
"AppEvents_Plugin": "Greffon",
"AppEvents_Type": "Type",
"BackDevDetail_Actions_Ask_Run": "",
"BackDevDetail_Actions_Not_Registered": "",
"BackDevDetail_Actions_Title_Run": "",
"BackDevDetail_Copy_Ask": "",
"BackDevDetail_Copy_Title": "Copier les d\u00e9tails",
"BackDevDetail_Tools_WOL_error": "",
"BackDevDetail_Tools_WOL_okay": "",
"BackDevices_Arpscan_disabled": "",
"BackDevices_Arpscan_enabled": "",
"BackDevices_Backup_CopError": "",
"BackDevices_Backup_Failed": "",
"BackDevices_Backup_okay": "",
"BackDevices_DBTools_DelDevError_a": "Erreur lors de la suppression de l'appareil",
"BackDevices_DBTools_DelDevError_b": "Erreur lors de la suppression des appareils",
"BackDevices_DBTools_DelDev_a": "Appareil supprim\u00e9",
"BackDevices_DBTools_DelDev_b": "Appareils supprim\u00e9s",
"BackDevices_DBTools_DelEvents": "\u00c9v\u00e9nements supprim\u00e9s",
"BackDevices_DBTools_DelEventsError": "Erreur lors de la suppression des \u00e9v\u00e9nements",
"BackDevices_DBTools_ImportCSV": "Les appareils du fichier CSV ont \u00e9t\u00e9 import\u00e9s avec succ\u00e8s.",
"BackDevices_DBTools_ImportCSVError": "Le fichier CSV n'a pas pu \u00eatre import\u00e9. Assurez-vous que le format est correct.",
"BackDevices_DBTools_ImportCSVMissing": "Le fichier CSV est introuvable sous <b>/config/devices.csv.</b>",
"BackDevices_DBTools_Purge": "Les sauvegardes les plus anciennes ont \u00e9t\u00e9 supprim\u00e9es",
"BackDevices_DBTools_UpdDev": "Appareil mis \u00e0 jour avec succ\u00e8s",
"BackDevices_DBTools_UpdDevError": "Erreur lors de la mise \u00e0 jour de l'appareil",
"BackDevices_DBTools_Upgrade": "Base de donn\u00e9es mise \u00e0 niveau avec succ\u00e8s",
"BackDevices_DBTools_UpgradeError": "La mise \u00e0 niveau de la base de donn\u00e9es a \u00e9chou\u00e9",
"BackDevices_Device_UpdDevError": "Erreur de mise \u00e0 jour des appareils, essayez plus tard. La base de donn\u00e9es est probablement bloqu\u00e9e en raison d'une t\u00e2che en cours.",
"BackDevices_Restore_CopError": "La base de donn\u00e9es originale n'a pas pu \u00eatre sauvegard\u00e9e.",
"BackDevices_Restore_Failed": "\u00c9chec de la restauration. Veuillez restaurer la sauvegarde manuellement.",
"BackDevices_Restore_okay": "Restauration ex\u00e9cut\u00e9e avec succ\u00e8s.",
"BackDevices_darkmode_disabled": "Mode sombre d\u00e9sactiv\u00e9",
"BackDevices_darkmode_enabled": "Mode sombre activ\u00e9",
"DAYS_TO_KEEP_EVENTS_description": "",
"DAYS_TO_KEEP_EVENTS_name": "Supprimer les \u00e9v\u00e9nements plus anciens que",
"DevDetail_Copy_Device_Title": "",
"DevDetail_Copy_Device_Tooltip": "",
"DevDetail_EveandAl_AlertAllEvents": "Alerter tous les \u00e9v\u00e9nements",
"DevDetail_EveandAl_AlertDown": "",
"DevDetail_EveandAl_Archived": "Archiv\u00e9",
"DevDetail_EveandAl_NewDevice": "",
"DevDetail_EveandAl_NewDevice_Tooltip": "",
"DevDetail_EveandAl_RandomMAC": "MAC al\u00e9atoire",
"DevDetail_EveandAl_ScanCycle": "",
"DevDetail_EveandAl_ScanCycle_a": "",
"DevDetail_EveandAl_ScanCycle_z": "",
"DevDetail_EveandAl_Skip": "",
"DevDetail_EveandAl_Title": "",
"DevDetail_Events_CheckBox": "Masquer les \u00e9v\u00e9nements de connexion",
"DevDetail_GoToNetworkNode": "",
"DevDetail_Icon": "Ic\u00f4ne",
"DevDetail_Icon_Descr": "",
"DevDetail_Loading": "Chargement\u00a0\u2026",
"DevDetail_MainInfo_Comments": "Observations",
"DevDetail_MainInfo_Favorite": "Favori",
"DevDetail_MainInfo_Group": "Groupe",
"DevDetail_MainInfo_Location": "Emplacement",
"DevDetail_MainInfo_Name": "Nom",
"DevDetail_MainInfo_Network": "",
"DevDetail_MainInfo_Network_Port": "<i class=\"fa fa-ethernet\"></i> Port",
"DevDetail_MainInfo_Network_Title": "<i class=\"fa fa-network-wired\"></i> R\u00e9seau",
"DevDetail_MainInfo_Owner": "Propri\u00e9taire",
"DevDetail_MainInfo_Title": "<i class=\"fa fa-pencil\"></i> Informations principales",
"DevDetail_MainInfo_Type": "Type",
"DevDetail_MainInfo_Vendor": "Fabriquant",
"DevDetail_MainInfo_mac": "MAC",
"DevDetail_Network_Node_hover": "",
"DevDetail_Network_Port_hover": "",
"DevDetail_Nmap_Scans": "",
"DevDetail_Nmap_Scans_desc": "",
"DevDetail_Nmap_buttonDefault": "",
"DevDetail_Nmap_buttonDefault_text": "",
"DevDetail_Nmap_buttonDetail": "",
"DevDetail_Nmap_buttonDetail_text": "",
"DevDetail_Nmap_buttonFast": "",
"DevDetail_Nmap_buttonFast_text": "",
"DevDetail_Nmap_buttonSkipDiscovery": "",
"DevDetail_Nmap_buttonSkipDiscovery_text": "",
"DevDetail_Nmap_resultsLink": "",
"DevDetail_Owner_hover": "",
"DevDetail_Periodselect_All": "",
"DevDetail_Periodselect_LastMonth": "",
"DevDetail_Periodselect_LastWeek": "",
"DevDetail_Periodselect_LastYear": "",
"DevDetail_Periodselect_today": "Aujourd'hui",
"DevDetail_Run_Actions_Title": "",
"DevDetail_Run_Actions_Tooltip": "",
"DevDetail_SessionInfo_FirstSession": "Premi\u00e8re session",
"DevDetail_SessionInfo_LastIP": "Derni\u00e8re IP",
"DevDetail_SessionInfo_LastSession": "Derni\u00e8re session",
"DevDetail_SessionInfo_StaticIP": "IP statique",
"DevDetail_SessionInfo_Status": "\u00c9tat",
"DevDetail_SessionInfo_Title": "<i class=\"fa fa-calendar\"></i> Info de session",
"DevDetail_SessionTable_Additionalinfo": "Informations suppl\u00e9mentaires",
"DevDetail_SessionTable_Connection": "Connexion",
"DevDetail_SessionTable_Disconnection": "D\u00e9connection",
"DevDetail_SessionTable_Duration": "Dur\u00e9e",
"DevDetail_SessionTable_IP": "IP",
"DevDetail_SessionTable_Order": "",
"DevDetail_Shortcut_CurrentStatus": "\u00c9tat actuel",
"DevDetail_Shortcut_DownAlerts": "Alertes de panne",
"DevDetail_Shortcut_Presence": "Pr\u00e9sence",
"DevDetail_Shortcut_Sessions": "Sessions",
"DevDetail_Tab_Details": "",
"DevDetail_Tab_Events": "",
"DevDetail_Tab_EventsTableDate": "Date",
"DevDetail_Tab_EventsTableEvent": "Type d'\u00e9v\u00e9nement",
"DevDetail_Tab_EventsTableIP": "IP",
"DevDetail_Tab_EventsTableInfo": "Informations compl\u00e9mentaires",
"DevDetail_Tab_Nmap": "",
"DevDetail_Tab_NmapEmpty": "",
"DevDetail_Tab_NmapTableExtra": "Extra",
"DevDetail_Tab_NmapTableHeader": "",
"DevDetail_Tab_NmapTableIndex": "Index",
"DevDetail_Tab_NmapTablePort": "Port",
"DevDetail_Tab_NmapTableService": "Service",
"DevDetail_Tab_NmapTableState": "\u00c9tat",
"DevDetail_Tab_NmapTableText": "",
"DevDetail_Tab_NmapTableTime": "Heure",
"DevDetail_Tab_Plugins": "",
"DevDetail_Tab_Presence": "",
"DevDetail_Tab_Sessions": "<i class=\"fa fa-list-ol\"></i> Sessions",
"DevDetail_Tab_Tools": "",
"DevDetail_Tab_Tools_Internet_Info_Description": "",
"DevDetail_Tab_Tools_Internet_Info_Error": "",
"DevDetail_Tab_Tools_Internet_Info_Start": "",
"DevDetail_Tab_Tools_Internet_Info_Title": "",
"DevDetail_Tab_Tools_Nslookup_Description": "",
"DevDetail_Tab_Tools_Nslookup_Error": "",
"DevDetail_Tab_Tools_Nslookup_Start": "",
"DevDetail_Tab_Tools_Nslookup_Title": "Nslookup",
"DevDetail_Tab_Tools_Speedtest_Description": "",
"DevDetail_Tab_Tools_Speedtest_Start": "",
"DevDetail_Tab_Tools_Speedtest_Title": "Test de d\u00e9bit en ligne",
"DevDetail_Tab_Tools_Traceroute_Description": "",
"DevDetail_Tab_Tools_Traceroute_Error": "",
"DevDetail_Tab_Tools_Traceroute_Start": "",
"DevDetail_Tab_Tools_Traceroute_Title": "Traceroute",
"DevDetail_Tools_WOL": "",
"DevDetail_Tools_WOL_noti": "",
"DevDetail_Tools_WOL_noti_text": "",
"DevDetail_Type_hover": "",
"DevDetail_Vendor_hover": "",
"DevDetail_WOL_Title": "",
"DevDetail_button_Delete": "Supprimer l'appareil",
"DevDetail_button_DeleteEvents": "Supprimer les \u00e9v\u00e9nements",
"DevDetail_button_DeleteEvents_Warning": "",
"DevDetail_button_OverwriteIcons": "",
"DevDetail_button_OverwriteIcons_Tooltip": "",
"DevDetail_button_OverwriteIcons_Warning": "",
"DevDetail_button_Reset": "",
"DevDetail_button_Save": "Enregistrer",
"Device_MultiEdit": "",
"Device_MultiEdit_Backup": "",
"Device_MultiEdit_Fields": "",
"Device_MultiEdit_MassActions": "",
"Device_MultiEdit_Tooltip": "",
"Device_Searchbox": "Rechercher",
"Device_Shortcut_AllDevices": "Tous les appareils",
"Device_Shortcut_Archived": "Archiv\u00e9",
"Device_Shortcut_Connected": "Connect\u00e9",
"Device_Shortcut_Devices": "Appareils",
"Device_Shortcut_DownAlerts": "",
"Device_Shortcut_Favorites": "Favoris",
"Device_Shortcut_NewDevices": "Nouveaux appareils",
"Device_Shortcut_OnlineChart": "Pr\u00e9sence de l'appareil",
"Device_TableHead_Connected_Devices": "Connexions",
"Device_TableHead_Favorite": "Favori",
"Device_TableHead_FirstSession": "Premi\u00e8re session",
"Device_TableHead_Group": "Groupe",
"Device_TableHead_Icon": "Ic\u00f4ne",
"Device_TableHead_LastIP": "",
"Device_TableHead_LastIPOrder": "",
"Device_TableHead_LastSession": "Derni\u00e8re session",
"Device_TableHead_Location": "Emplacement",
"Device_TableHead_MAC": "",
"Device_TableHead_MAC_full": "Adresse MAC",
"Device_TableHead_Name": "Nom",
"Device_TableHead_Owner": "Propri\u00e9taire",
"Device_TableHead_Parent_MAC": "",
"Device_TableHead_Port": "Port",
"Device_TableHead_RowID": "",
"Device_TableHead_Rowid": "",
"Device_TableHead_Status": "\u00c9tat",
"Device_TableHead_Type": "Type",
"Device_TableHead_Vendor": "Fabriquant",
"Device_Table_Not_Network_Device": "",
"Device_Table_info": "",
"Device_Table_nav_next": "Suivant",
"Device_Table_nav_prev": "Pr\u00e9c\u00e9dent",
"Device_Tablelenght": "",
"Device_Tablelenght_all": "",
"Device_Title": "Appareils",
"Donations_Others": "Autres",
"Donations_Platforms": "Plateformes de sponsoring",
"Donations_Text": "",
"Donations_Title": "Dons",
"ENABLE_PLUGINS_description": "",
"ENABLE_PLUGINS_name": "",
"Email_display_name": "Messagerie",
"Email_icon": "",
"Events_Loading": "Chargement\u00a0\u2026",
"Events_Periodselect_All": "Toutes les informations",
"Events_Periodselect_LastMonth": "Le mois dernier",
"Events_Periodselect_LastWeek": "La semaine derni\u00e8re",
"Events_Periodselect_LastYear": "L'ann\u00e9e derni\u00e8re",
"Events_Periodselect_today": "Aujourd'hui",
"Events_Searchbox": "Rechercher",
"Events_Shortcut_AllEvents": "Tous les \u00e9v\u00e8nements",
"Events_Shortcut_DownAlerts": "Alertes de panne",
"Events_Shortcut_Events": "\u00c9v\u00e8nements",
"Events_Shortcut_MissSessions": "Sessions manquantes",
"Events_Shortcut_NewDevices": "Nouveaux appareils",
"Events_Shortcut_Sessions": "Sessions",
"Events_Shortcut_VoidSessions": "Sessions annul\u00e9es",
"Events_TableHead_AdditionalInfo": "Informations compl\u00e9mentaires",
"Events_TableHead_Connection": "Connexion",
"Events_TableHead_Date": "Date",
"Events_TableHead_Device": "Dispositif",
"Events_TableHead_Disconnection": "D\u00e9connexion",
"Events_TableHead_Duration": "Dur\u00e9e",
"Events_TableHead_DurationOrder": "Ordre de dur\u00e9e",
"Events_TableHead_EventType": "Type d'\u00e9v\u00e9nement",
"Events_TableHead_IP": "IP",
"Events_TableHead_IPOrder": "",
"Events_TableHead_Order": "",
"Events_TableHead_Owner": "Propri\u00e9taire",
"Events_Table_info": "",
"Events_Table_nav_next": "Suivant",
"Events_Table_nav_prev": "Pr\u00e9c\u00e9dent",
"Events_Tablelenght": "",
"Events_Tablelenght_all": "",
"Events_Title": "\u00c9v\u00e8nements",
"Gen_Action": "Action",
"Gen_AreYouSure": "",
"Gen_Backup": "",
"Gen_Cancel": "Annuler",
"Gen_Copy": "Lancer",
"Gen_DataUpdatedUITakesTime": "",
"Gen_Delete": "Supprimer",
"Gen_DeleteAll": "",
"Gen_Error": "Erreur",
"Gen_LockedDB": "",
"Gen_Okay": "OK",
"Gen_Purge": "Purger",
"Gen_ReadDocs": "",
"Gen_Restore": "",
"Gen_Run": "Lancer",
"Gen_Save": "Enregistrer",
"Gen_Saved": "Enregistr\u00e9",
"Gen_Selected_Devices": "",
"Gen_Switch": "Basculer",
"Gen_Upd": "",
"Gen_Upd_Fail": "",
"Gen_Warning": "Avertissement",
"Gen_Work_In_Progress": "",
"General_display_name": "G\u00e9n\u00e9ral",
"General_icon": "",
"HRS_TO_KEEP_NEWDEV_description": "",
"HRS_TO_KEEP_NEWDEV_name": "",
"HelpFAQ_Cat_Detail": "D\u00e9tails",
"HelpFAQ_Cat_Detail_300_head": "",
"HelpFAQ_Cat_Detail_300_text_a": "",
"HelpFAQ_Cat_Detail_300_text_b": "",
"HelpFAQ_Cat_Detail_301_head_a": "",
"HelpFAQ_Cat_Detail_301_head_b": "",
"HelpFAQ_Cat_Detail_301_text": "",
"HelpFAQ_Cat_Detail_302_head_a": "",
"HelpFAQ_Cat_Detail_302_head_b": "",
"HelpFAQ_Cat_Detail_302_text": "",
"HelpFAQ_Cat_Detail_303_head": "",
"HelpFAQ_Cat_Detail_303_text": "",
"HelpFAQ_Cat_Device_200_head": "",
"HelpFAQ_Cat_Device_200_text": "",
"HelpFAQ_Cat_General": "G\u00e9n\u00e9ral",
"HelpFAQ_Cat_General_100_head": "L'horloge en haut \u00e0 droite et les heures des \u00e9v\u00e9nements/pr\u00e9sence ne sont pas correctes (d\u00e9calage horaire).",
"HelpFAQ_Cat_General_100_text_a": "",
"HelpFAQ_Cat_General_100_text_b": "",
"HelpFAQ_Cat_General_100_text_c": "",
"HelpFAQ_Cat_General_101_head": "",
"HelpFAQ_Cat_General_101_text": "",
"HelpFAQ_Cat_General_102_head": "",
"HelpFAQ_Cat_General_102_text": "",
"HelpFAQ_Cat_General_102docker_head": "",
"HelpFAQ_Cat_General_102docker_text": "",
"HelpFAQ_Cat_General_103_head": "",
"HelpFAQ_Cat_General_103_text": "",
"HelpFAQ_Cat_Network_600_head": "",
"HelpFAQ_Cat_Network_600_text": "",
"HelpFAQ_Cat_Network_601_head": "",
"HelpFAQ_Cat_Network_601_text": "",
"HelpFAQ_Cat_Presence_400_head": "",
"HelpFAQ_Cat_Presence_400_text": "",
"HelpFAQ_Cat_Presence_401_head": "",
"HelpFAQ_Cat_Presence_401_text": "",
"HelpFAQ_Title": "Aide / FAQ",
"LOG_LEVEL_description": "",
"LOG_LEVEL_name": "",
"Loading": "Chargement\u00a0\u2026",
"Login_Box": "",
"Login_Default_PWD": "",
"Login_Psw-box": "Mot de passe",
"Login_Psw_alert": "",
"Login_Psw_folder": "",
"Login_Psw_new": "",
"Login_Psw_run": "",
"Login_Remember": "",
"Login_Remember_small": "",
"Login_Submit": "",
"Login_Toggle_Alert_headline": "",
"Login_Toggle_Info": "",
"Login_Toggle_Info_headline": "",
"Maintenance_Running_Version": "Version install\u00e9e",
"Maintenance_Status": "\u00c9tat",
"Maintenance_Title": "Outils d'entretien",
"Maintenance_Tool_ExportCSV": "Exportation CSV",
"Maintenance_Tool_ExportCSV_noti": "Exportation CSV",
"Maintenance_Tool_ExportCSV_noti_text": "\u00cates-vous s\u00fbr de vouloir g\u00e9n\u00e9rer un fichier CSV\u202f?",
"Maintenance_Tool_ExportCSV_text": "",
"Maintenance_Tool_ImportCSV": "Importation CSV",
"Maintenance_Tool_ImportCSV_noti": "Importation CSV",
"Maintenance_Tool_ImportCSV_noti_text": "\u00cates-vous s\u00fbr de vouloir importer le fichier CSV\u202f? Cela \u00e9crasera compl\u00e8tement les appareils de votre base de donn\u00e9es.",
"Maintenance_Tool_ImportCSV_text": "",
"Maintenance_Tool_arpscansw": "Basculer l'arp-Scan (activ\u00e9/d\u00e9sactiv\u00e9)",
"Maintenance_Tool_arpscansw_noti": "Activer ou d\u00e9sactiver l'arp-Scan",
"Maintenance_Tool_arpscansw_noti_text": "Une fois le scan d\u00e9sactiv\u00e9, il reste d\u00e9sactiv\u00e9 jusqu'\u00e0 ce qu'il soit r\u00e9activ\u00e9.",
"Maintenance_Tool_arpscansw_text": "",
"Maintenance_Tool_backup": "",
"Maintenance_Tool_backup_noti": "",
"Maintenance_Tool_backup_noti_text": "",
"Maintenance_Tool_backup_text": "",
"Maintenance_Tool_check_visible": "",
"Maintenance_Tool_darkmode": "",
"Maintenance_Tool_darkmode_noti": "",
"Maintenance_Tool_darkmode_noti_text": "",
"Maintenance_Tool_darkmode_text": "",
"Maintenance_Tool_del_ActHistory": "",
"Maintenance_Tool_del_ActHistory_noti": "",
"Maintenance_Tool_del_ActHistory_noti_text": "",
"Maintenance_Tool_del_ActHistory_text": "",
"Maintenance_Tool_del_alldev": "",
"Maintenance_Tool_del_alldev_noti": "",
"Maintenance_Tool_del_alldev_noti_text": "",
"Maintenance_Tool_del_alldev_text": "",
"Maintenance_Tool_del_allevents": "",
"Maintenance_Tool_del_allevents30": "",
"Maintenance_Tool_del_allevents30_noti": "",
"Maintenance_Tool_del_allevents30_noti_text": "",
"Maintenance_Tool_del_allevents30_text": "",
"Maintenance_Tool_del_allevents_noti": "",
"Maintenance_Tool_del_allevents_noti_text": "",
"Maintenance_Tool_del_allevents_text": "",
"Maintenance_Tool_del_empty_macs": "",
"Maintenance_Tool_del_empty_macs_noti": "",
"Maintenance_Tool_del_empty_macs_noti_text": "",
"Maintenance_Tool_del_empty_macs_text": "",
"Maintenance_Tool_del_selecteddev": "",
"Maintenance_Tool_del_selecteddev_text": "",
"Maintenance_Tool_del_unknowndev": "",
"Maintenance_Tool_del_unknowndev_noti": "",
"Maintenance_Tool_del_unknowndev_noti_text": "",
"Maintenance_Tool_del_unknowndev_text": "",
"Maintenance_Tool_displayed_columns_text": "",
"Maintenance_Tool_drag_me": "",
"Maintenance_Tool_order_columns_text": "",
"Maintenance_Tool_purgebackup": "",
"Maintenance_Tool_purgebackup_noti": "",
"Maintenance_Tool_purgebackup_noti_text": "",
"Maintenance_Tool_purgebackup_text": "",
"Maintenance_Tool_restore": "",
"Maintenance_Tool_restore_noti": "",
"Maintenance_Tool_restore_noti_text": "",
"Maintenance_Tool_restore_text": "",
"Maintenance_Tool_upgrade_database_noti": "",
"Maintenance_Tool_upgrade_database_noti_text": "",
"Maintenance_Tool_upgrade_database_text": "",
"Maintenance_Tools_Tab_BackupRestore": "",
"Maintenance_Tools_Tab_Logging": "Journaux",
"Maintenance_Tools_Tab_Settings": "Param\u00e8tres",
"Maintenance_Tools_Tab_Tools": "Outils",
"Maintenance_Tools_Tab_UISettings": "Param\u00e8tres de l'interface",
"Maintenance_arp_status": "",
"Maintenance_arp_status_off": "est actuellement d\u00e9sactiv\u00e9",
"Maintenance_arp_status_on": "",
"Maintenance_built_on": "Construit sur",
"Maintenance_current_version": "Vous \u00eates \u00e0 jour. D\u00e9couvrez sur quoi <a href=\"https://github.com/jokob-sk/Pi.Alert/issues/138\" target=\"_blank\">je travaille</a>.",
"Maintenance_database_backup": "Sauvegardes de base de donn\u00e9es",
"Maintenance_database_backup_found": "des sauvegardes ont \u00e9t\u00e9 trouv\u00e9es",
"Maintenance_database_backup_total": "utilisation totale du disque",
"Maintenance_database_lastmod": "Derni\u00e8re modification",
"Maintenance_database_path": "Chemin de la base de donn\u00e9es",
"Maintenance_database_rows": "",
"Maintenance_database_size": "",
"Maintenance_lang_selector_apply": "Appliquer",
"Maintenance_lang_selector_empty": "",
"Maintenance_lang_selector_lable": "",
"Maintenance_lang_selector_text": "",
"Maintenance_new_version": "",
"Maintenance_themeselector_apply": "Appliquer",
"Maintenance_themeselector_empty": "",
"Maintenance_themeselector_lable": "",
"Maintenance_themeselector_text": "",
"Maintenance_version": "",
"NETWORK_DEVICE_TYPES_description": "",
"NETWORK_DEVICE_TYPES_name": "",
"Navigation_About": "",
"Navigation_Devices": "Appareils",
"Navigation_Donations": "Dons",
"Navigation_Events": "\u00c9v\u00e8nements",
"Navigation_HelpFAQ": "Aide / FAQ",
"Navigation_Integrations": "",
"Navigation_Maintenance": "",
"Navigation_Monitoring": "",
"Navigation_Network": "R\u00e9seau",
"Navigation_Plugins": "Greffons",
"Navigation_Presence": "Pr\u00e9sence",
"Navigation_Report": "",
"Navigation_Settings": "Param\u00e8tres",
"Navigation_SystemInfo": "Infos syst\u00e8me",
"Navigation_Workflows": "Flux de travail",
"Network_Assign": "",
"Network_Cant_Assign": "",
"Network_Configuration_Error": "",
"Network_Connected": "",
"Network_ManageAdd": "",
"Network_ManageAdd_Name": "",
"Network_ManageAdd_Name_text": "",
"Network_ManageAdd_Port": "",
"Network_ManageAdd_Port_text": "",
"Network_ManageAdd_Submit": "",
"Network_ManageAdd_Type": "",
"Network_ManageAdd_Type_text": "",
"Network_ManageAssign": "Assigner",
"Network_ManageDel": "",
"Network_ManageDel_Name": "",
"Network_ManageDel_Name_text": "",
"Network_ManageDel_Submit": "Supprimer",
"Network_ManageDevices": "",
"Network_ManageEdit": "",
"Network_ManageEdit_ID": "",
"Network_ManageEdit_ID_text": "",
"Network_ManageEdit_Name": "",
"Network_ManageEdit_Name_text": "",
"Network_ManageEdit_Port": "",
"Network_ManageEdit_Port_text": "",
"Network_ManageEdit_Submit": "",
"Network_ManageEdit_Type": "",
"Network_ManageEdit_Type_text": "",
"Network_ManageLeaf": "",
"Network_ManageUnassign": "",
"Network_NoAssignedDevices": "",
"Network_NoDevices": "",
"Network_Node": "",
"Network_Node_Name": "",
"Network_Parent": "",
"Network_Root": "",
"Network_Root_Not_Configured": "",
"Network_Root_Unconfigurable": "",
"Network_Table_Hostname": "Nom de h\u00f4te",
"Network_Table_IP": "IP",
"Network_Table_State": "\u00c9tat",
"Network_Title": "",
"Network_UnassignedDevices": "",
"PIALERT_WEB_PASSWORD_description": "",
"PIALERT_WEB_PASSWORD_name": "",
"PIALERT_WEB_PROTECTION_description": "",
"PIALERT_WEB_PROTECTION_name": "",
"PLUGINS_KEEP_HIST_description": "",
"PLUGINS_KEEP_HIST_name": "",
"Plugins_DeleteAll": "",
"Plugins_Filters_Mac": "",
"Plugins_History": "",
"Plugins_Objects": "",
"Plugins_Out_of": "",
"Plugins_Unprocessed_Events": "\u00c9v\u00e9nements non trait\u00e9s",
"Plugins_no_control": "",
"Presence_CalHead_day": "jour",
"Presence_CalHead_lang": "",
"Presence_CalHead_month": "mois",
"Presence_CalHead_quarter": "trimestre",
"Presence_CalHead_week": "semaine",
"Presence_CalHead_year": "ann\u00e9e",
"Presence_CallHead_Devices": "Appareils",
"Presence_Loading": "Chargement\u00a0\u2026",
"Presence_Shortcut_AllDevices": "",
"Presence_Shortcut_Archived": "Archiv\u00e9",
"Presence_Shortcut_Connected": "Connect\u00e9",
"Presence_Shortcut_Devices": "Appareils",
"Presence_Shortcut_DownAlerts": "",
"Presence_Shortcut_Favorites": "Favoris",
"Presence_Shortcut_NewDevices": "",
"Presence_Title": "",
"REPORT_DASHBOARD_URL_description": "",
"REPORT_DASHBOARD_URL_name": "",
"REPORT_ERROR": "",
"REPORT_MAIL_description": "",
"REPORT_MAIL_name": "",
"REPORT_TITLE": "",
"RandomMAC_hover": "",
"SCAN_SUBNETS_description": "",
"SYSTEM_TITLE": "Informations syst\u00e8me",
"Setting_Override": "",
"Setting_Override_Description": "",
"Settings_Metadata_Toggle": "",
"Settings_device_Scanners_desync": "",
"Settings_device_Scanners_desync_popup": "",
"Speedtest_Results": "",
"Systeminfo_CPU": "Processeur",
"Systeminfo_CPU_Cores": "C\u0153urs de processeur\u202f:",
"Systeminfo_CPU_Name": "Nom du processeur\u202f:",
"Systeminfo_CPU_Speed": "Vitesse du CPU\u202f:",
"Systeminfo_CPU_Temp": "Temp\u00e9rature du processeur\u202f:",
"Systeminfo_CPU_Vendor": "Fabriquant du processeur\u202f:",
"Systeminfo_Client_Resolution": "R\u00e9solution du navigateur\u202f:",
"Systeminfo_Client_User_Agent": "Agent utilisateur\u202f:",
"Systeminfo_General": "G\u00e9n\u00e9ral",
"Systeminfo_General_Date": "Date\u202f:",
"Systeminfo_General_Date2": "Date 2\u202f:",
"Systeminfo_General_Full_Date": "Date compl\u00e8te\u202f:",
"Systeminfo_General_TimeZone": "Fuseau horaire\u202f:",
"Systeminfo_Memory": "M\u00e9moire",
"Systeminfo_Memory_Total_Memory": "M\u00e9moire totale\u202f:",
"Systeminfo_Memory_Usage": "Utilisation de la m\u00e9moire:",
"Systeminfo_Memory_Usage_Percent": "% de la m\u00e9moire\u202f:",
"Systeminfo_Motherboard": "Carte m\u00e8re",
"Systeminfo_Motherboard_BIOS": "BIOS\u202f:",
"Systeminfo_Motherboard_BIOS_Date": "Date du BIOS\u202f:",
"Systeminfo_Motherboard_BIOS_Vendor": "Fabriquant du BIOS\u202f:",
"Systeminfo_Motherboard_Manufactured": "Fabriqu\u00e9 par\u202f:",
"Systeminfo_Motherboard_Name": "Nom\u202f:",
"Systeminfo_Motherboard_Revision": "R\u00e9vision\u202f:",
"Systeminfo_Network": "R\u00e9seau",
"Systeminfo_Network_Accept_Encoding": "Accepter l'encodage\u202f:",
"Systeminfo_Network_Accept_Language": "Accepter la langue\u202f:",
"Systeminfo_Network_Connection_Port": "Port de connexion\u202f:",
"Systeminfo_Network_HTTP_Host": "H\u00f4te HTTP\u202f:",
"Systeminfo_Network_HTTP_Referer": "R\u00e9f\u00e9rent HTTP\u202f:",
"Systeminfo_Network_HTTP_Referer_String": "Pas de r\u00e9f\u00e9rent HTTP",
"Systeminfo_Network_Hardware": "Mat\u00e9riel r\u00e9seau",
"Systeminfo_Network_Hardware_Interface_Mask": "",
"Systeminfo_Network_Hardware_Interface_Name": "",
"Systeminfo_Network_Hardware_Interface_RX": "",
"Systeminfo_Network_Hardware_Interface_TX": "",
"Systeminfo_Network_IP": "IP Internet\u202f:",
"Systeminfo_Network_IP_Connection": "Connexion IP\u202f:",
"Systeminfo_Network_IP_Server": "IP du serveur\u202f:",
"Systeminfo_Network_MIME": "MIME\u202f:",
"Systeminfo_Network_Request_Method": "M\u00e9thode de demande\u202f:",
"Systeminfo_Network_Request_Time": "Heure de la demande\u202f:",
"Systeminfo_Network_Request_URI": "URI de la demande\u202f:",
"Systeminfo_Network_Secure_Connection": "Connexion s\u00e9curis\u00e9e\u202f:",
"Systeminfo_Network_Secure_Connection_String": "",
"Systeminfo_Network_Server_Name": "Nom du serveur\u202f:",
"Systeminfo_Network_Server_Name_String": "Nom du serveur introuvable",
"Systeminfo_Network_Server_Query": "Requ\u00eate du serveur\u202f:",
"Systeminfo_Network_Server_Query_String": "Aucune cha\u00eene de requ\u00eate",
"Systeminfo_Network_Server_Version": "Version du serveur\u202f:",
"Systeminfo_Services": "Services",
"Systeminfo_Services_Description": "Description du service",
"Systeminfo_Services_Name": "Nom du service",
"Systeminfo_Storage": "Stockage",
"Systeminfo_Storage_Device": "Appareil\u202f:",
"Systeminfo_Storage_Mount": "Point de montage\u202f:",
"Systeminfo_Storage_Size": "Taille\u202f:",
"Systeminfo_Storage_Type": "Type\u202f:",
"Systeminfo_Storage_Usage": "",
"Systeminfo_Storage_Usage_Free": "Libre\u202f:",
"Systeminfo_Storage_Usage_Mount": "",
"Systeminfo_Storage_Usage_Total": "Total\u202f:",
"Systeminfo_Storage_Usage_Used": "Utilis\u00e9\u202f:",
"Systeminfo_System": "Syst\u00e8me",
"Systeminfo_System_AVG": "",
"Systeminfo_System_Architecture": "Architecture\u202f:",
"Systeminfo_System_Kernel": "Noyau\u202f:",
"Systeminfo_System_OSVersion": "",
"Systeminfo_System_Running_Processes": "Processus en cours\u202f:",
"Systeminfo_System_System": "Syst\u00e8me\u202f:",
"Systeminfo_System_Uname": "",
"Systeminfo_System_Uptime": "",
"Systeminfo_This_Client": "",
"Systeminfo_USB_Devices": "",
"TIMEZONE_description": "",
"TIMEZONE_name": "",
"UI_LANG_description": "S\u00e9lectionnez votre langue pr\u00e9f\u00e9r\u00e9 de l\u2019interface. Aidez \u00e0 traduire ou sugg\u00e9rez des langues dans le portail en ligne de <a href=\"https://hosted.weblate.org/projects/pialert/core/\" target=\"_blank\">Weblate</a>.",
"UI_LANG_name": "",
"UI_MY_DEVICES_description": "",
"UI_MY_DEVICES_name": "",
"UI_NOT_RANDOM_MAC_description": "",
"UI_NOT_RANDOM_MAC_name": "",
"UI_PRESENCE_description": "",
"UI_PRESENCE_name": "",
"devices_old": "",
"general_event_description": "",
"general_event_title": "",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "",
"report_time": "",
"run_event_icon": "",
"run_event_tooltip": "",
"settings_core_icon": "",
"settings_core_label": "",
"settings_device_scanners": "",
"settings_device_scanners_icon": "",
"settings_device_scanners_label": "Scanners d'appareils",
"settings_enabled": "Param\u00e8tres activ\u00e9s",
"settings_enabled_icon": "",
"settings_expand_all": "Tout d\u00e9velopper",
"settings_imported": "",
"settings_imported_label": "Param\u00e8tres import\u00e9s",
"settings_missing": "",
"settings_missing_block": "",
"settings_old": "Importation des param\u00e8tres et r\u00e9initialisation...",
"settings_other_scanners": "",
"settings_other_scanners_icon": "",
"settings_other_scanners_label": "",
"settings_publishers": "",
"settings_publishers_icon": "",
"settings_publishers_label": "\u00c9diteurs",
"settings_saved": "",
"settings_system_icon": "",
"settings_system_label": "Syst\u00e8me",
"test_event_icon": "",
"test_event_tooltip": ""
}

View File

@@ -5,7 +5,7 @@
// ###################################
$defaultLang = "en_us";
$allLanguages = ["en_us","es_es","de_de"];
$allLanguages = ["en_us","es_es","de_de", "nb_no", "ru_ru", "fr_fr"];
global $db;
@@ -13,6 +13,9 @@ $result = $db->querySingle("SELECT Value FROM Settings WHERE Code_Name = 'UI_LAN
switch($result){
case 'Spanish': $pia_lang_selected = 'es_es'; break;
case 'German': $pia_lang_selected = 'de_de'; break;
case 'Norwegian': $pia_lang_selected = 'nb_no'; break;
case 'Russian': $pia_lang_selected = 'ru_ru'; break;
case 'French': $pia_lang_selected = 'fr_fr'; break;
default: $pia_lang_selected = 'en_us'; break;
}
@@ -31,9 +34,6 @@ function getLanguageDataFromJson()
{
global $allLanguages;
// Default language
$defaultLanguage = 'en_us';
// Array to hold the language data from the JSON files
$languageData = [];
@@ -44,20 +44,18 @@ function getLanguageDataFromJson()
if (file_exists($jsonFilePath)) {
$data = json_decode(file_get_contents($jsonFilePath), true);
// Use the default language if the key is not found
$languageData[$language] = $data[$language] ?? $data[$defaultLanguage] ?? [];
// Adjusting for the changed JSON format
$languageData[$language] = $data;
} else {
// Handle the case where the JSON file doesn't exist
// For example, you might want to log an error message
echo 'File not found: '.$jsonFilePath;
echo 'File not found: ' . $jsonFilePath;
}
}
return $languageData;
}
// Merge the JSON data with the SQL data, giving priority to SQL data for overlapping keys
function mergeLanguageData($jsonLanguageData, $sqlLanguageData)
{
@@ -76,7 +74,8 @@ function mergeLanguageData($jsonLanguageData, $sqlLanguageData)
function lang($key)
{
global $pia_lang_selected, $lang, $defaultLang, $strings, $db;
global $pia_lang_selected, $strings;
// Get the data from JSON files
$languageData = getLanguageDataFromJson();
@@ -87,7 +86,7 @@ function lang($key)
$mergedLanguageData = mergeLanguageData($languageData, $sqlLanguageData);
// Check if the key exists in the selected language
if (isset($mergedLanguageData[$pia_lang_selected][$key])) {
if (isset($mergedLanguageData[$pia_lang_selected][$key]) && $mergedLanguageData[$pia_lang_selected][$key] != '') {
$result = $mergedLanguageData[$pia_lang_selected][$key];
} else {
// If key not found in selected language, use "en_us" as fallback

View File

@@ -0,0 +1,37 @@
import json
import os
import sys
def merge_translations(main_file, other_files):
# Load main file
with open(main_file, 'r') as f:
main_data = json.load(f)
# Get keys and sort them alphabetically
keys = sorted(main_data.keys())
# Sort the keys alphabetically in the main file
main_data = {k: main_data[k] for k in keys}
# Rewrite sorted main file
with open(main_file, 'w') as f:
json.dump(main_data, f, indent=4)
# Merge keys into other files
for file_name in other_files:
with open(file_name, 'r+') as f:
data = json.load(f)
for key in keys:
if key not in data:
data[key] = ""
# Sort the keys alphabetically for each language
data = {k: data[k] for k in sorted(data.keys())}
f.seek(0)
json.dump(data, f, indent=4)
f.truncate()
if __name__ == "__main__":
current_path = os.path.dirname(os.path.abspath(__file__))
json_files = ["en_us.json", "de_de.json", "es_es.json", "fr_fr.json", "nb_no.json", "ru_ru.json"]
file_paths = [os.path.join(current_path, file) for file in json_files]
merge_translations(file_paths[0], file_paths[1:])

View File

@@ -0,0 +1,650 @@
{
"API_CUSTOM_SQL_description": "",
"API_CUSTOM_SQL_name": "",
"API_display_name": "",
"API_icon": "",
"About_Design": "",
"About_Exit": "",
"About_Title": "",
"AppEvents_DateTimeCreated": "",
"AppEvents_Extra": "",
"AppEvents_GUID": "",
"AppEvents_Helper1": "",
"AppEvents_Helper2": "",
"AppEvents_Helper3": "",
"AppEvents_ObjectForeignKey": "",
"AppEvents_ObjectIndex": "",
"AppEvents_ObjectIsArchived": "",
"AppEvents_ObjectIsNew": "",
"AppEvents_ObjectPlugin": "",
"AppEvents_ObjectPrimaryID": "Prim\u00e6r-ID",
"AppEvents_ObjectSecondaryID": "Sekund\u00e6r-ID",
"AppEvents_ObjectStatus": "Status (ved loggf\u00f8ringstidspunkt)",
"AppEvents_ObjectStatusColumn": "Statuskolonne",
"AppEvents_ObjectType": "Objekttype",
"AppEvents_Plugin": "Programtillegg",
"AppEvents_Type": "Type",
"BackDevDetail_Actions_Ask_Run": "Utf\u00f8r handlingen?",
"BackDevDetail_Actions_Not_Registered": "",
"BackDevDetail_Actions_Title_Run": "Utf\u00f8r handling",
"BackDevDetail_Copy_Ask": "",
"BackDevDetail_Copy_Title": "Kopier detaljer",
"BackDevDetail_Tools_WOL_error": "Kommandoen ble IKKE kj\u00f8rt.",
"BackDevDetail_Tools_WOL_okay": "",
"BackDevices_Arpscan_disabled": "",
"BackDevices_Arpscan_enabled": "",
"BackDevices_Backup_CopError": "",
"BackDevices_Backup_Failed": "",
"BackDevices_Backup_okay": "",
"BackDevices_DBTools_DelDevError_a": "",
"BackDevices_DBTools_DelDevError_b": "",
"BackDevices_DBTools_DelDev_a": "Enhet slettet",
"BackDevices_DBTools_DelDev_b": "",
"BackDevices_DBTools_DelEvents": "",
"BackDevices_DBTools_DelEventsError": "",
"BackDevices_DBTools_ImportCSV": "",
"BackDevices_DBTools_ImportCSVError": "",
"BackDevices_DBTools_ImportCSVMissing": "",
"BackDevices_DBTools_Purge": "",
"BackDevices_DBTools_UpdDev": "",
"BackDevices_DBTools_UpdDevError": "",
"BackDevices_DBTools_Upgrade": "",
"BackDevices_DBTools_UpgradeError": "",
"BackDevices_Device_UpdDevError": "",
"BackDevices_Restore_CopError": "",
"BackDevices_Restore_Failed": "",
"BackDevices_Restore_okay": "",
"BackDevices_darkmode_disabled": "",
"BackDevices_darkmode_enabled": "",
"DAYS_TO_KEEP_EVENTS_description": "",
"DAYS_TO_KEEP_EVENTS_name": "",
"DevDetail_Copy_Device_Title": "",
"DevDetail_Copy_Device_Tooltip": "",
"DevDetail_EveandAl_AlertAllEvents": "",
"DevDetail_EveandAl_AlertDown": "",
"DevDetail_EveandAl_Archived": "",
"DevDetail_EveandAl_NewDevice": "",
"DevDetail_EveandAl_NewDevice_Tooltip": "",
"DevDetail_EveandAl_RandomMAC": "",
"DevDetail_EveandAl_ScanCycle": "",
"DevDetail_EveandAl_ScanCycle_a": "",
"DevDetail_EveandAl_ScanCycle_z": "",
"DevDetail_EveandAl_Skip": "",
"DevDetail_EveandAl_Title": "",
"DevDetail_Events_CheckBox": "",
"DevDetail_GoToNetworkNode": "",
"DevDetail_Icon": "",
"DevDetail_Icon_Descr": "",
"DevDetail_Loading": "",
"DevDetail_MainInfo_Comments": "",
"DevDetail_MainInfo_Favorite": "",
"DevDetail_MainInfo_Group": "",
"DevDetail_MainInfo_Location": "",
"DevDetail_MainInfo_Name": "",
"DevDetail_MainInfo_Network": "",
"DevDetail_MainInfo_Network_Port": "",
"DevDetail_MainInfo_Network_Title": "",
"DevDetail_MainInfo_Owner": "",
"DevDetail_MainInfo_Title": "",
"DevDetail_MainInfo_Type": "",
"DevDetail_MainInfo_Vendor": "",
"DevDetail_MainInfo_mac": "",
"DevDetail_Network_Node_hover": "",
"DevDetail_Network_Port_hover": "",
"DevDetail_Nmap_Scans": "",
"DevDetail_Nmap_Scans_desc": "",
"DevDetail_Nmap_buttonDefault": "",
"DevDetail_Nmap_buttonDefault_text": "",
"DevDetail_Nmap_buttonDetail": "",
"DevDetail_Nmap_buttonDetail_text": "",
"DevDetail_Nmap_buttonFast": "",
"DevDetail_Nmap_buttonFast_text": "",
"DevDetail_Nmap_buttonSkipDiscovery": "",
"DevDetail_Nmap_buttonSkipDiscovery_text": "",
"DevDetail_Nmap_resultsLink": "",
"DevDetail_Owner_hover": "",
"DevDetail_Periodselect_All": "",
"DevDetail_Periodselect_LastMonth": "",
"DevDetail_Periodselect_LastWeek": "",
"DevDetail_Periodselect_LastYear": "",
"DevDetail_Periodselect_today": "",
"DevDetail_Run_Actions_Title": "",
"DevDetail_Run_Actions_Tooltip": "",
"DevDetail_SessionInfo_FirstSession": "",
"DevDetail_SessionInfo_LastIP": "",
"DevDetail_SessionInfo_LastSession": "",
"DevDetail_SessionInfo_StaticIP": "",
"DevDetail_SessionInfo_Status": "",
"DevDetail_SessionInfo_Title": "",
"DevDetail_SessionTable_Additionalinfo": "",
"DevDetail_SessionTable_Connection": "",
"DevDetail_SessionTable_Disconnection": "",
"DevDetail_SessionTable_Duration": "",
"DevDetail_SessionTable_IP": "",
"DevDetail_SessionTable_Order": "",
"DevDetail_Shortcut_CurrentStatus": "",
"DevDetail_Shortcut_DownAlerts": "",
"DevDetail_Shortcut_Presence": "",
"DevDetail_Shortcut_Sessions": "",
"DevDetail_Tab_Details": "",
"DevDetail_Tab_Events": "",
"DevDetail_Tab_EventsTableDate": "",
"DevDetail_Tab_EventsTableEvent": "",
"DevDetail_Tab_EventsTableIP": "",
"DevDetail_Tab_EventsTableInfo": "",
"DevDetail_Tab_Nmap": "",
"DevDetail_Tab_NmapEmpty": "",
"DevDetail_Tab_NmapTableExtra": "",
"DevDetail_Tab_NmapTableHeader": "",
"DevDetail_Tab_NmapTableIndex": "",
"DevDetail_Tab_NmapTablePort": "",
"DevDetail_Tab_NmapTableService": "",
"DevDetail_Tab_NmapTableState": "",
"DevDetail_Tab_NmapTableText": "",
"DevDetail_Tab_NmapTableTime": "",
"DevDetail_Tab_Plugins": "",
"DevDetail_Tab_Presence": "",
"DevDetail_Tab_Sessions": "",
"DevDetail_Tab_Tools": "",
"DevDetail_Tab_Tools_Internet_Info_Description": "",
"DevDetail_Tab_Tools_Internet_Info_Error": "",
"DevDetail_Tab_Tools_Internet_Info_Start": "",
"DevDetail_Tab_Tools_Internet_Info_Title": "",
"DevDetail_Tab_Tools_Nslookup_Description": "",
"DevDetail_Tab_Tools_Nslookup_Error": "",
"DevDetail_Tab_Tools_Nslookup_Start": "",
"DevDetail_Tab_Tools_Nslookup_Title": "",
"DevDetail_Tab_Tools_Speedtest_Description": "",
"DevDetail_Tab_Tools_Speedtest_Start": "",
"DevDetail_Tab_Tools_Speedtest_Title": "",
"DevDetail_Tab_Tools_Traceroute_Description": "",
"DevDetail_Tab_Tools_Traceroute_Error": "",
"DevDetail_Tab_Tools_Traceroute_Start": "",
"DevDetail_Tab_Tools_Traceroute_Title": "",
"DevDetail_Tools_WOL": "",
"DevDetail_Tools_WOL_noti": "",
"DevDetail_Tools_WOL_noti_text": "",
"DevDetail_Type_hover": "",
"DevDetail_Vendor_hover": "",
"DevDetail_WOL_Title": "",
"DevDetail_button_Delete": "",
"DevDetail_button_DeleteEvents": "",
"DevDetail_button_DeleteEvents_Warning": "",
"DevDetail_button_OverwriteIcons": "",
"DevDetail_button_OverwriteIcons_Tooltip": "",
"DevDetail_button_OverwriteIcons_Warning": "",
"DevDetail_button_Reset": "",
"DevDetail_button_Save": "",
"Device_MultiEdit": "",
"Device_MultiEdit_Backup": "",
"Device_MultiEdit_Fields": "",
"Device_MultiEdit_MassActions": "",
"Device_MultiEdit_Tooltip": "",
"Device_Searchbox": "",
"Device_Shortcut_AllDevices": "",
"Device_Shortcut_Archived": "",
"Device_Shortcut_Connected": "",
"Device_Shortcut_Devices": "",
"Device_Shortcut_DownAlerts": "",
"Device_Shortcut_Favorites": "",
"Device_Shortcut_NewDevices": "",
"Device_Shortcut_OnlineChart": "",
"Device_TableHead_Connected_Devices": "",
"Device_TableHead_Favorite": "",
"Device_TableHead_FirstSession": "",
"Device_TableHead_Group": "",
"Device_TableHead_Icon": "",
"Device_TableHead_LastIP": "",
"Device_TableHead_LastIPOrder": "",
"Device_TableHead_LastSession": "",
"Device_TableHead_Location": "",
"Device_TableHead_MAC": "",
"Device_TableHead_MAC_full": "",
"Device_TableHead_Name": "",
"Device_TableHead_Owner": "",
"Device_TableHead_Parent_MAC": "",
"Device_TableHead_Port": "",
"Device_TableHead_RowID": "",
"Device_TableHead_Rowid": "",
"Device_TableHead_Status": "",
"Device_TableHead_Type": "",
"Device_TableHead_Vendor": "",
"Device_Table_Not_Network_Device": "",
"Device_Table_info": "",
"Device_Table_nav_next": "",
"Device_Table_nav_prev": "",
"Device_Tablelenght": "",
"Device_Tablelenght_all": "",
"Device_Title": "",
"Donations_Others": "",
"Donations_Platforms": "",
"Donations_Text": "",
"Donations_Title": "",
"ENABLE_PLUGINS_description": "",
"ENABLE_PLUGINS_name": "",
"Email_display_name": "",
"Email_icon": "",
"Events_Loading": "",
"Events_Periodselect_All": "",
"Events_Periodselect_LastMonth": "",
"Events_Periodselect_LastWeek": "",
"Events_Periodselect_LastYear": "",
"Events_Periodselect_today": "",
"Events_Searchbox": "",
"Events_Shortcut_AllEvents": "",
"Events_Shortcut_DownAlerts": "",
"Events_Shortcut_Events": "",
"Events_Shortcut_MissSessions": "",
"Events_Shortcut_NewDevices": "",
"Events_Shortcut_Sessions": "",
"Events_Shortcut_VoidSessions": "",
"Events_TableHead_AdditionalInfo": "",
"Events_TableHead_Connection": "",
"Events_TableHead_Date": "",
"Events_TableHead_Device": "",
"Events_TableHead_Disconnection": "",
"Events_TableHead_Duration": "",
"Events_TableHead_DurationOrder": "",
"Events_TableHead_EventType": "",
"Events_TableHead_IP": "",
"Events_TableHead_IPOrder": "",
"Events_TableHead_Order": "",
"Events_TableHead_Owner": "",
"Events_Table_info": "",
"Events_Table_nav_next": "",
"Events_Table_nav_prev": "",
"Events_Tablelenght": "",
"Events_Tablelenght_all": "",
"Events_Title": "",
"Gen_Action": "",
"Gen_AreYouSure": "",
"Gen_Backup": "",
"Gen_Cancel": "",
"Gen_Copy": "",
"Gen_DataUpdatedUITakesTime": "",
"Gen_Delete": "",
"Gen_DeleteAll": "",
"Gen_Error": "",
"Gen_LockedDB": "",
"Gen_Okay": "",
"Gen_Purge": "",
"Gen_ReadDocs": "",
"Gen_Restore": "",
"Gen_Run": "",
"Gen_Save": "",
"Gen_Saved": "",
"Gen_Selected_Devices": "",
"Gen_Switch": "",
"Gen_Upd": "",
"Gen_Upd_Fail": "",
"Gen_Warning": "",
"Gen_Work_In_Progress": "",
"General_display_name": "",
"General_icon": "",
"HRS_TO_KEEP_NEWDEV_description": "",
"HRS_TO_KEEP_NEWDEV_name": "",
"HelpFAQ_Cat_Detail": "",
"HelpFAQ_Cat_Detail_300_head": "",
"HelpFAQ_Cat_Detail_300_text_a": "",
"HelpFAQ_Cat_Detail_300_text_b": "",
"HelpFAQ_Cat_Detail_301_head_a": "",
"HelpFAQ_Cat_Detail_301_head_b": "",
"HelpFAQ_Cat_Detail_301_text": "",
"HelpFAQ_Cat_Detail_302_head_a": "",
"HelpFAQ_Cat_Detail_302_head_b": "",
"HelpFAQ_Cat_Detail_302_text": "",
"HelpFAQ_Cat_Detail_303_head": "",
"HelpFAQ_Cat_Detail_303_text": "",
"HelpFAQ_Cat_Device_200_head": "",
"HelpFAQ_Cat_Device_200_text": "",
"HelpFAQ_Cat_General": "",
"HelpFAQ_Cat_General_100_head": "",
"HelpFAQ_Cat_General_100_text_a": "",
"HelpFAQ_Cat_General_100_text_b": "",
"HelpFAQ_Cat_General_100_text_c": "",
"HelpFAQ_Cat_General_101_head": "",
"HelpFAQ_Cat_General_101_text": "",
"HelpFAQ_Cat_General_102_head": "",
"HelpFAQ_Cat_General_102_text": "",
"HelpFAQ_Cat_General_102docker_head": "",
"HelpFAQ_Cat_General_102docker_text": "",
"HelpFAQ_Cat_General_103_head": "",
"HelpFAQ_Cat_General_103_text": "",
"HelpFAQ_Cat_Network_600_head": "",
"HelpFAQ_Cat_Network_600_text": "",
"HelpFAQ_Cat_Network_601_head": "",
"HelpFAQ_Cat_Network_601_text": "",
"HelpFAQ_Cat_Presence_400_head": "",
"HelpFAQ_Cat_Presence_400_text": "",
"HelpFAQ_Cat_Presence_401_head": "",
"HelpFAQ_Cat_Presence_401_text": "",
"HelpFAQ_Title": "",
"LOG_LEVEL_description": "",
"LOG_LEVEL_name": "",
"Loading": "",
"Login_Box": "",
"Login_Default_PWD": "",
"Login_Psw-box": "",
"Login_Psw_alert": "",
"Login_Psw_folder": "",
"Login_Psw_new": "",
"Login_Psw_run": "",
"Login_Remember": "",
"Login_Remember_small": "",
"Login_Submit": "",
"Login_Toggle_Alert_headline": "",
"Login_Toggle_Info": "",
"Login_Toggle_Info_headline": "",
"Maintenance_Running_Version": "",
"Maintenance_Status": "",
"Maintenance_Title": "",
"Maintenance_Tool_ExportCSV": "",
"Maintenance_Tool_ExportCSV_noti": "",
"Maintenance_Tool_ExportCSV_noti_text": "",
"Maintenance_Tool_ExportCSV_text": "",
"Maintenance_Tool_ImportCSV": "",
"Maintenance_Tool_ImportCSV_noti": "",
"Maintenance_Tool_ImportCSV_noti_text": "",
"Maintenance_Tool_ImportCSV_text": "",
"Maintenance_Tool_arpscansw": "",
"Maintenance_Tool_arpscansw_noti": "",
"Maintenance_Tool_arpscansw_noti_text": "",
"Maintenance_Tool_arpscansw_text": "",
"Maintenance_Tool_backup": "",
"Maintenance_Tool_backup_noti": "",
"Maintenance_Tool_backup_noti_text": "",
"Maintenance_Tool_backup_text": "",
"Maintenance_Tool_check_visible": "",
"Maintenance_Tool_darkmode": "",
"Maintenance_Tool_darkmode_noti": "",
"Maintenance_Tool_darkmode_noti_text": "",
"Maintenance_Tool_darkmode_text": "",
"Maintenance_Tool_del_ActHistory": "",
"Maintenance_Tool_del_ActHistory_noti": "",
"Maintenance_Tool_del_ActHistory_noti_text": "",
"Maintenance_Tool_del_ActHistory_text": "",
"Maintenance_Tool_del_alldev": "",
"Maintenance_Tool_del_alldev_noti": "",
"Maintenance_Tool_del_alldev_noti_text": "",
"Maintenance_Tool_del_alldev_text": "",
"Maintenance_Tool_del_allevents": "",
"Maintenance_Tool_del_allevents30": "",
"Maintenance_Tool_del_allevents30_noti": "",
"Maintenance_Tool_del_allevents30_noti_text": "",
"Maintenance_Tool_del_allevents30_text": "",
"Maintenance_Tool_del_allevents_noti": "",
"Maintenance_Tool_del_allevents_noti_text": "",
"Maintenance_Tool_del_allevents_text": "",
"Maintenance_Tool_del_empty_macs": "",
"Maintenance_Tool_del_empty_macs_noti": "",
"Maintenance_Tool_del_empty_macs_noti_text": "",
"Maintenance_Tool_del_empty_macs_text": "",
"Maintenance_Tool_del_selecteddev": "",
"Maintenance_Tool_del_selecteddev_text": "",
"Maintenance_Tool_del_unknowndev": "",
"Maintenance_Tool_del_unknowndev_noti": "",
"Maintenance_Tool_del_unknowndev_noti_text": "",
"Maintenance_Tool_del_unknowndev_text": "",
"Maintenance_Tool_displayed_columns_text": "",
"Maintenance_Tool_drag_me": "",
"Maintenance_Tool_order_columns_text": "",
"Maintenance_Tool_purgebackup": "",
"Maintenance_Tool_purgebackup_noti": "",
"Maintenance_Tool_purgebackup_noti_text": "",
"Maintenance_Tool_purgebackup_text": "",
"Maintenance_Tool_restore": "",
"Maintenance_Tool_restore_noti": "",
"Maintenance_Tool_restore_noti_text": "",
"Maintenance_Tool_restore_text": "",
"Maintenance_Tool_upgrade_database_noti": "",
"Maintenance_Tool_upgrade_database_noti_text": "",
"Maintenance_Tool_upgrade_database_text": "",
"Maintenance_Tools_Tab_BackupRestore": "",
"Maintenance_Tools_Tab_Logging": "",
"Maintenance_Tools_Tab_Settings": "",
"Maintenance_Tools_Tab_Tools": "",
"Maintenance_Tools_Tab_UISettings": "",
"Maintenance_arp_status": "",
"Maintenance_arp_status_off": "",
"Maintenance_arp_status_on": "",
"Maintenance_built_on": "",
"Maintenance_current_version": "",
"Maintenance_database_backup": "",
"Maintenance_database_backup_found": "",
"Maintenance_database_backup_total": "",
"Maintenance_database_lastmod": "",
"Maintenance_database_path": "",
"Maintenance_database_rows": "",
"Maintenance_database_size": "",
"Maintenance_lang_selector_apply": "",
"Maintenance_lang_selector_empty": "",
"Maintenance_lang_selector_lable": "",
"Maintenance_lang_selector_text": "",
"Maintenance_new_version": "",
"Maintenance_themeselector_apply": "",
"Maintenance_themeselector_empty": "",
"Maintenance_themeselector_lable": "",
"Maintenance_themeselector_text": "",
"Maintenance_version": "",
"NETWORK_DEVICE_TYPES_description": "",
"NETWORK_DEVICE_TYPES_name": "",
"Navigation_About": "",
"Navigation_Devices": "",
"Navigation_Donations": "",
"Navigation_Events": "",
"Navigation_HelpFAQ": "",
"Navigation_Integrations": "",
"Navigation_Maintenance": "",
"Navigation_Monitoring": "",
"Navigation_Network": "",
"Navigation_Plugins": "",
"Navigation_Presence": "",
"Navigation_Report": "",
"Navigation_Settings": "",
"Navigation_SystemInfo": "",
"Navigation_Workflows": "",
"Network_Assign": "",
"Network_Cant_Assign": "",
"Network_Configuration_Error": "",
"Network_Connected": "",
"Network_ManageAdd": "",
"Network_ManageAdd_Name": "",
"Network_ManageAdd_Name_text": "",
"Network_ManageAdd_Port": "",
"Network_ManageAdd_Port_text": "",
"Network_ManageAdd_Submit": "",
"Network_ManageAdd_Type": "",
"Network_ManageAdd_Type_text": "",
"Network_ManageAssign": "",
"Network_ManageDel": "",
"Network_ManageDel_Name": "",
"Network_ManageDel_Name_text": "",
"Network_ManageDel_Submit": "",
"Network_ManageDevices": "",
"Network_ManageEdit": "",
"Network_ManageEdit_ID": "",
"Network_ManageEdit_ID_text": "",
"Network_ManageEdit_Name": "",
"Network_ManageEdit_Name_text": "",
"Network_ManageEdit_Port": "",
"Network_ManageEdit_Port_text": "",
"Network_ManageEdit_Submit": "",
"Network_ManageEdit_Type": "",
"Network_ManageEdit_Type_text": "",
"Network_ManageLeaf": "",
"Network_ManageUnassign": "",
"Network_NoAssignedDevices": "",
"Network_NoDevices": "",
"Network_Node": "",
"Network_Node_Name": "",
"Network_Parent": "",
"Network_Root": "",
"Network_Root_Not_Configured": "",
"Network_Root_Unconfigurable": "",
"Network_Table_Hostname": "",
"Network_Table_IP": "",
"Network_Table_State": "",
"Network_Title": "",
"Network_UnassignedDevices": "",
"PIALERT_WEB_PASSWORD_description": "",
"PIALERT_WEB_PASSWORD_name": "",
"PIALERT_WEB_PROTECTION_description": "",
"PIALERT_WEB_PROTECTION_name": "",
"PLUGINS_KEEP_HIST_description": "",
"PLUGINS_KEEP_HIST_name": "",
"Plugins_DeleteAll": "",
"Plugins_Filters_Mac": "",
"Plugins_History": "",
"Plugins_Objects": "",
"Plugins_Out_of": "",
"Plugins_Unprocessed_Events": "",
"Plugins_no_control": "",
"Presence_CalHead_day": "",
"Presence_CalHead_lang": "",
"Presence_CalHead_month": "",
"Presence_CalHead_quarter": "",
"Presence_CalHead_week": "",
"Presence_CalHead_year": "",
"Presence_CallHead_Devices": "",
"Presence_Loading": "",
"Presence_Shortcut_AllDevices": "",
"Presence_Shortcut_Archived": "",
"Presence_Shortcut_Connected": "",
"Presence_Shortcut_Devices": "",
"Presence_Shortcut_DownAlerts": "",
"Presence_Shortcut_Favorites": "",
"Presence_Shortcut_NewDevices": "",
"Presence_Title": "",
"REPORT_DASHBOARD_URL_description": "",
"REPORT_DASHBOARD_URL_name": "",
"REPORT_ERROR": "",
"REPORT_MAIL_description": "",
"REPORT_MAIL_name": "",
"REPORT_TITLE": "",
"RandomMAC_hover": "",
"SCAN_SUBNETS_description": "",
"SYSTEM_TITLE": "",
"Setting_Override": "",
"Setting_Override_Description": "",
"Settings_Metadata_Toggle": "",
"Settings_device_Scanners_desync": "",
"Settings_device_Scanners_desync_popup": "",
"Speedtest_Results": "",
"Systeminfo_CPU": "",
"Systeminfo_CPU_Cores": "",
"Systeminfo_CPU_Name": "",
"Systeminfo_CPU_Speed": "",
"Systeminfo_CPU_Temp": "",
"Systeminfo_CPU_Vendor": "",
"Systeminfo_Client_Resolution": "",
"Systeminfo_Client_User_Agent": "",
"Systeminfo_General": "",
"Systeminfo_General_Date": "",
"Systeminfo_General_Date2": "",
"Systeminfo_General_Full_Date": "",
"Systeminfo_General_TimeZone": "",
"Systeminfo_Memory": "",
"Systeminfo_Memory_Total_Memory": "",
"Systeminfo_Memory_Usage": "",
"Systeminfo_Memory_Usage_Percent": "",
"Systeminfo_Motherboard": "",
"Systeminfo_Motherboard_BIOS": "",
"Systeminfo_Motherboard_BIOS_Date": "",
"Systeminfo_Motherboard_BIOS_Vendor": "",
"Systeminfo_Motherboard_Manufactured": "",
"Systeminfo_Motherboard_Name": "",
"Systeminfo_Motherboard_Revision": "",
"Systeminfo_Network": "",
"Systeminfo_Network_Accept_Encoding": "",
"Systeminfo_Network_Accept_Language": "",
"Systeminfo_Network_Connection_Port": "",
"Systeminfo_Network_HTTP_Host": "",
"Systeminfo_Network_HTTP_Referer": "",
"Systeminfo_Network_HTTP_Referer_String": "",
"Systeminfo_Network_Hardware": "",
"Systeminfo_Network_Hardware_Interface_Mask": "",
"Systeminfo_Network_Hardware_Interface_Name": "",
"Systeminfo_Network_Hardware_Interface_RX": "",
"Systeminfo_Network_Hardware_Interface_TX": "",
"Systeminfo_Network_IP": "",
"Systeminfo_Network_IP_Connection": "",
"Systeminfo_Network_IP_Server": "",
"Systeminfo_Network_MIME": "",
"Systeminfo_Network_Request_Method": "",
"Systeminfo_Network_Request_Time": "",
"Systeminfo_Network_Request_URI": "",
"Systeminfo_Network_Secure_Connection": "",
"Systeminfo_Network_Secure_Connection_String": "",
"Systeminfo_Network_Server_Name": "",
"Systeminfo_Network_Server_Name_String": "",
"Systeminfo_Network_Server_Query": "",
"Systeminfo_Network_Server_Query_String": "",
"Systeminfo_Network_Server_Version": "",
"Systeminfo_Services": "",
"Systeminfo_Services_Description": "",
"Systeminfo_Services_Name": "",
"Systeminfo_Storage": "",
"Systeminfo_Storage_Device": "",
"Systeminfo_Storage_Mount": "",
"Systeminfo_Storage_Size": "",
"Systeminfo_Storage_Type": "",
"Systeminfo_Storage_Usage": "",
"Systeminfo_Storage_Usage_Free": "",
"Systeminfo_Storage_Usage_Mount": "",
"Systeminfo_Storage_Usage_Total": "",
"Systeminfo_Storage_Usage_Used": "",
"Systeminfo_System": "",
"Systeminfo_System_AVG": "",
"Systeminfo_System_Architecture": "",
"Systeminfo_System_Kernel": "",
"Systeminfo_System_OSVersion": "",
"Systeminfo_System_Running_Processes": "",
"Systeminfo_System_System": "",
"Systeminfo_System_Uname": "",
"Systeminfo_System_Uptime": "",
"Systeminfo_This_Client": "",
"Systeminfo_USB_Devices": "",
"TIMEZONE_description": "",
"TIMEZONE_name": "",
"UI_LANG_description": "",
"UI_LANG_name": "",
"UI_MY_DEVICES_description": "",
"UI_MY_DEVICES_name": "",
"UI_NOT_RANDOM_MAC_description": "",
"UI_NOT_RANDOM_MAC_name": "",
"UI_PRESENCE_description": "",
"UI_PRESENCE_name": "",
"devices_old": "",
"general_event_description": "",
"general_event_title": "",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "",
"report_time": "",
"run_event_icon": "",
"run_event_tooltip": "",
"settings_core_icon": "",
"settings_core_label": "",
"settings_device_scanners": "",
"settings_device_scanners_icon": "",
"settings_device_scanners_label": "",
"settings_enabled": "",
"settings_enabled_icon": "",
"settings_expand_all": "",
"settings_imported": "",
"settings_imported_label": "",
"settings_missing": "",
"settings_missing_block": "",
"settings_old": "",
"settings_other_scanners": "",
"settings_other_scanners_icon": "",
"settings_other_scanners_label": "",
"settings_publishers": "",
"settings_publishers_icon": "",
"settings_publishers_label": "",
"settings_saved": "",
"settings_system_icon": "",
"settings_system_label": "",
"test_event_icon": "",
"test_event_tooltip": ""
}

View File

@@ -0,0 +1,650 @@
{
"API_CUSTOM_SQL_description": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 SQL-\u0437\u0430\u043f\u0440\u043e\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b JSON, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432\u044b\u0434\u0430\u0442\u044c \u0435\u0433\u043e \u0447\u0435\u0440\u0435\u0437 <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> file endpoint</a>.",
"API_CUSTOM_SQL_name": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0430\u044f \u043a\u043e\u043d\u0435\u0447\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430",
"API_display_name": "API",
"API_icon": "<i class=\"fa fa-arrow-down-up-across-line\"></i>",
"About_Design": "\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d:",
"About_Exit": "\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f",
"About_Title": "\u0421\u0435\u0442\u0435\u0432\u0430\u044f \u0437\u0430\u0449\u0438\u0442\u0430 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c",
"AppEvents_DateTimeCreated": "\u0416\u0443\u0440\u043d\u0430\u043b",
"AppEvents_Extra": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e",
"AppEvents_GUID": "GUID \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f",
"AppEvents_Helper1": "\u041f\u043e\u043c\u043e\u0449\u043d\u0438\u043a 1",
"AppEvents_Helper2": "\u041f\u043e\u043c\u043e\u0449\u043d\u0438\u043a 2",
"AppEvents_Helper3": "\u041f\u043e\u043c\u043e\u0449\u043d\u0438\u043a 3",
"AppEvents_ObjectForeignKey": "\u0412\u043d\u0435\u0448\u043d\u0438\u0439 \u043a\u043b\u044e\u0447",
"AppEvents_ObjectIndex": "\u0418\u043d\u0434\u0435\u043a\u0441",
"AppEvents_ObjectIsArchived": "\u0410\u0440\u0445\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043e (\u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443)",
"AppEvents_ObjectIsNew": "\u041d\u043e\u0432\u044b\u0439 (\u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443)",
"AppEvents_ObjectPlugin": "\u0421\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d",
"AppEvents_ObjectPrimaryID": "\u041f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 ID",
"AppEvents_ObjectSecondaryID": "\u0412\u0442\u043e\u0440\u0438\u0447\u043d\u044b\u0439 ID",
"AppEvents_ObjectStatus": "\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 (\u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443)",
"AppEvents_ObjectStatusColumn": "\u041a\u043e\u043b\u043e\u043d\u043a\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f",
"AppEvents_ObjectType": "\u0422\u0438\u043f \u043e\u0431\u044a\u0435\u043a\u0442\u0430",
"AppEvents_Plugin": "\u041f\u043b\u0430\u0433\u0438\u043d",
"AppEvents_Type": "\u0422\u0438\u043f",
"BackDevDetail_Actions_Ask_Run": "\u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435?",
"BackDevDetail_Actions_Not_Registered": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043d\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e:\u00b7 ",
"BackDevDetail_Actions_Title_Run": "\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435",
"BackDevDetail_Copy_Ask": "\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0437 \u0432\u044b\u043f\u0430\u0434\u0430\u044e\u0449\u0435\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 (\u0432\u0441\u0435 \u043d\u0430 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043e)?",
"BackDevDetail_Copy_Title": "\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u0438",
"BackDevDetail_Tools_WOL_error": "\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u041d\u0415 \u0431\u044b\u043b\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.",
"BackDevDetail_Tools_WOL_okay": "\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0431\u044b\u043b\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.",
"BackDevices_Arpscan_disabled": "Arp \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u043e",
"BackDevices_Arpscan_enabled": "Arp \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e",
"BackDevices_Backup_CopError": "\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c.",
"BackDevices_Backup_Failed": "\u0420\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e. \u0410\u0440\u0445\u0438\u0432 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d \u0438\u043b\u0438 \u043f\u0443\u0441\u0442.",
"BackDevices_Backup_okay": "\u0420\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u0441 \u043d\u043e\u0432\u044b\u043c \u0430\u0440\u0445\u0438\u0432\u043e\u043c",
"BackDevices_DBTools_DelDevError_a": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",
"BackDevices_DBTools_DelDevError_b": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432",
"BackDevices_DBTools_DelDev_a": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u043e",
"BackDevices_DBTools_DelDev_b": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u044b",
"BackDevices_DBTools_DelEvents": "\u0421\u043e\u0431\u044b\u0442\u0438\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u044b",
"BackDevices_DBTools_DelEventsError": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439",
"BackDevices_DBTools_ImportCSV": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 CSV \u0431\u044b\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b.",
"BackDevices_DBTools_ImportCSVError": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b CSV. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0444\u043e\u0440\u043c\u0430\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439.",
"BackDevices_DBTools_ImportCSVMissing": "CSV-\u0444\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u0432<b>/config/devices.csv.</b>",
"BackDevices_DBTools_Purge": "\u0421\u0430\u043c\u044b\u0435 \u0441\u0442\u0430\u0440\u044b\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0435 \u043a\u043e\u043f\u0438\u0438 \u0431\u044b\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u044b",
"BackDevices_DBTools_UpdDev": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e",
"BackDevices_DBTools_UpdDevError": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",
"BackDevices_DBTools_Upgrade": "\u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430",
"BackDevices_DBTools_UpgradeError": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c",
"BackDevices_Device_UpdDevError": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435. \u0412\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0438\u0437-\u0437\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.",
"BackDevices_Restore_CopError": "\u0418\u0441\u0445\u043e\u0434\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c.",
"BackDevices_Restore_Failed": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u0443\u044e \u043a\u043e\u043f\u0438\u044e \u0432\u0440\u0443\u0447\u043d\u0443\u044e.",
"BackDevices_Restore_okay": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e.",
"BackDevices_darkmode_disabled": "\u0422\u0435\u043c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d",
"BackDevices_darkmode_enabled": "\u0422\u0435\u043c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u043a\u043b\u044e\u0447\u0435\u043d",
"DAYS_TO_KEEP_EVENTS_description": "\u042d\u0442\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f. \u0417\u0434\u0435\u0441\u044c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0434\u043d\u0435\u0439, \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445. \u0412\u0441\u0435 \u0441\u0442\u0430\u0440\u044b\u0435 \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c\u0441\u044f. \u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c\u043e \u043a \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u043b\u0430\u0433\u0438\u043d\u0430.",
"DAYS_TO_KEEP_EVENTS_name": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0441\u0442\u0430\u0440\u0448\u0435",
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> \u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",
"DevDetail_Copy_Device_Tooltip": "\u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0437 \u0440\u0430\u0441\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0435\u0433\u043e\u0441\u044f \u0441\u043f\u0438\u0441\u043a\u0430. \u0412\u0441\u0435 \u043d\u0430 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043e",
"DevDetail_EveandAl_AlertAllEvents": "\u041e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u0435 \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445",
"DevDetail_EveandAl_AlertDown": "\u041e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u0435 \u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438",
"DevDetail_EveandAl_Archived": "\u0410\u0440\u0445\u0438\u0432",
"DevDetail_EveandAl_NewDevice": "\u041d\u043e\u0432\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
"DevDetail_EveandAl_NewDevice_Tooltip": "\u0411\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u00ab\u041d\u043e\u0432\u043e\u0435\u00bb \u0434\u043b\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u0441\u043f\u0438\u0441\u043a\u0438, \u043a\u043e\u0433\u0434\u0430 \u0444\u0438\u043b\u044c\u0442\u0440 \u00ab\u041d\u043e\u0432\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u00bb \u0430\u043a\u0442\u0438\u0432\u0435\u043d. \u041d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f.",
"DevDetail_EveandAl_RandomMAC": "\u0421\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0439 MAC-\u0430\u0434\u0440\u0435\u0441",
"DevDetail_EveandAl_ScanCycle": "\u0421\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
"DevDetail_EveandAl_ScanCycle_a": "\u0421\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
"DevDetail_EveandAl_ScanCycle_z": "\u041d\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
"DevDetail_EveandAl_Skip": "",
"DevDetail_EveandAl_Title": "",
"DevDetail_Events_CheckBox": "",
"DevDetail_GoToNetworkNode": "",
"DevDetail_Icon": "",
"DevDetail_Icon_Descr": "",
"DevDetail_Loading": "",
"DevDetail_MainInfo_Comments": "",
"DevDetail_MainInfo_Favorite": "",
"DevDetail_MainInfo_Group": "",
"DevDetail_MainInfo_Location": "",
"DevDetail_MainInfo_Name": "",
"DevDetail_MainInfo_Network": "",
"DevDetail_MainInfo_Network_Port": "",
"DevDetail_MainInfo_Network_Title": "",
"DevDetail_MainInfo_Owner": "",
"DevDetail_MainInfo_Title": "",
"DevDetail_MainInfo_Type": "",
"DevDetail_MainInfo_Vendor": "",
"DevDetail_MainInfo_mac": "",
"DevDetail_Network_Node_hover": "",
"DevDetail_Network_Port_hover": "",
"DevDetail_Nmap_Scans": "",
"DevDetail_Nmap_Scans_desc": "",
"DevDetail_Nmap_buttonDefault": "",
"DevDetail_Nmap_buttonDefault_text": "",
"DevDetail_Nmap_buttonDetail": "",
"DevDetail_Nmap_buttonDetail_text": "",
"DevDetail_Nmap_buttonFast": "",
"DevDetail_Nmap_buttonFast_text": "",
"DevDetail_Nmap_buttonSkipDiscovery": "",
"DevDetail_Nmap_buttonSkipDiscovery_text": "",
"DevDetail_Nmap_resultsLink": "",
"DevDetail_Owner_hover": "",
"DevDetail_Periodselect_All": "",
"DevDetail_Periodselect_LastMonth": "",
"DevDetail_Periodselect_LastWeek": "",
"DevDetail_Periodselect_LastYear": "",
"DevDetail_Periodselect_today": "",
"DevDetail_Run_Actions_Title": "",
"DevDetail_Run_Actions_Tooltip": "",
"DevDetail_SessionInfo_FirstSession": "",
"DevDetail_SessionInfo_LastIP": "",
"DevDetail_SessionInfo_LastSession": "",
"DevDetail_SessionInfo_StaticIP": "",
"DevDetail_SessionInfo_Status": "",
"DevDetail_SessionInfo_Title": "",
"DevDetail_SessionTable_Additionalinfo": "",
"DevDetail_SessionTable_Connection": "",
"DevDetail_SessionTable_Disconnection": "",
"DevDetail_SessionTable_Duration": "",
"DevDetail_SessionTable_IP": "",
"DevDetail_SessionTable_Order": "",
"DevDetail_Shortcut_CurrentStatus": "",
"DevDetail_Shortcut_DownAlerts": "",
"DevDetail_Shortcut_Presence": "",
"DevDetail_Shortcut_Sessions": "",
"DevDetail_Tab_Details": "",
"DevDetail_Tab_Events": "",
"DevDetail_Tab_EventsTableDate": "",
"DevDetail_Tab_EventsTableEvent": "",
"DevDetail_Tab_EventsTableIP": "",
"DevDetail_Tab_EventsTableInfo": "",
"DevDetail_Tab_Nmap": "",
"DevDetail_Tab_NmapEmpty": "",
"DevDetail_Tab_NmapTableExtra": "",
"DevDetail_Tab_NmapTableHeader": "",
"DevDetail_Tab_NmapTableIndex": "",
"DevDetail_Tab_NmapTablePort": "",
"DevDetail_Tab_NmapTableService": "",
"DevDetail_Tab_NmapTableState": "",
"DevDetail_Tab_NmapTableText": "",
"DevDetail_Tab_NmapTableTime": "",
"DevDetail_Tab_Plugins": "",
"DevDetail_Tab_Presence": "",
"DevDetail_Tab_Sessions": "",
"DevDetail_Tab_Tools": "",
"DevDetail_Tab_Tools_Internet_Info_Description": "",
"DevDetail_Tab_Tools_Internet_Info_Error": "",
"DevDetail_Tab_Tools_Internet_Info_Start": "",
"DevDetail_Tab_Tools_Internet_Info_Title": "",
"DevDetail_Tab_Tools_Nslookup_Description": "",
"DevDetail_Tab_Tools_Nslookup_Error": "",
"DevDetail_Tab_Tools_Nslookup_Start": "",
"DevDetail_Tab_Tools_Nslookup_Title": "",
"DevDetail_Tab_Tools_Speedtest_Description": "",
"DevDetail_Tab_Tools_Speedtest_Start": "",
"DevDetail_Tab_Tools_Speedtest_Title": "",
"DevDetail_Tab_Tools_Traceroute_Description": "",
"DevDetail_Tab_Tools_Traceroute_Error": "",
"DevDetail_Tab_Tools_Traceroute_Start": "",
"DevDetail_Tab_Tools_Traceroute_Title": "",
"DevDetail_Tools_WOL": "",
"DevDetail_Tools_WOL_noti": "",
"DevDetail_Tools_WOL_noti_text": "",
"DevDetail_Type_hover": "",
"DevDetail_Vendor_hover": "",
"DevDetail_WOL_Title": "",
"DevDetail_button_Delete": "",
"DevDetail_button_DeleteEvents": "",
"DevDetail_button_DeleteEvents_Warning": "",
"DevDetail_button_OverwriteIcons": "",
"DevDetail_button_OverwriteIcons_Tooltip": "",
"DevDetail_button_OverwriteIcons_Warning": "",
"DevDetail_button_Reset": "",
"DevDetail_button_Save": "",
"Device_MultiEdit": "",
"Device_MultiEdit_Backup": "",
"Device_MultiEdit_Fields": "",
"Device_MultiEdit_MassActions": "",
"Device_MultiEdit_Tooltip": "",
"Device_Searchbox": "",
"Device_Shortcut_AllDevices": "",
"Device_Shortcut_Archived": "",
"Device_Shortcut_Connected": "",
"Device_Shortcut_Devices": "",
"Device_Shortcut_DownAlerts": "",
"Device_Shortcut_Favorites": "",
"Device_Shortcut_NewDevices": "",
"Device_Shortcut_OnlineChart": "",
"Device_TableHead_Connected_Devices": "",
"Device_TableHead_Favorite": "",
"Device_TableHead_FirstSession": "",
"Device_TableHead_Group": "",
"Device_TableHead_Icon": "",
"Device_TableHead_LastIP": "",
"Device_TableHead_LastIPOrder": "",
"Device_TableHead_LastSession": "",
"Device_TableHead_Location": "",
"Device_TableHead_MAC": "",
"Device_TableHead_MAC_full": "",
"Device_TableHead_Name": "",
"Device_TableHead_Owner": "",
"Device_TableHead_Parent_MAC": "",
"Device_TableHead_Port": "",
"Device_TableHead_RowID": "",
"Device_TableHead_Rowid": "",
"Device_TableHead_Status": "",
"Device_TableHead_Type": "",
"Device_TableHead_Vendor": "",
"Device_Table_Not_Network_Device": "",
"Device_Table_info": "",
"Device_Table_nav_next": "",
"Device_Table_nav_prev": "",
"Device_Tablelenght": "",
"Device_Tablelenght_all": "",
"Device_Title": "",
"Donations_Others": "",
"Donations_Platforms": "",
"Donations_Text": "",
"Donations_Title": "",
"ENABLE_PLUGINS_description": "",
"ENABLE_PLUGINS_name": "",
"Email_display_name": "",
"Email_icon": "",
"Events_Loading": "",
"Events_Periodselect_All": "",
"Events_Periodselect_LastMonth": "",
"Events_Periodselect_LastWeek": "",
"Events_Periodselect_LastYear": "",
"Events_Periodselect_today": "",
"Events_Searchbox": "",
"Events_Shortcut_AllEvents": "",
"Events_Shortcut_DownAlerts": "",
"Events_Shortcut_Events": "",
"Events_Shortcut_MissSessions": "",
"Events_Shortcut_NewDevices": "",
"Events_Shortcut_Sessions": "",
"Events_Shortcut_VoidSessions": "",
"Events_TableHead_AdditionalInfo": "",
"Events_TableHead_Connection": "",
"Events_TableHead_Date": "",
"Events_TableHead_Device": "",
"Events_TableHead_Disconnection": "",
"Events_TableHead_Duration": "",
"Events_TableHead_DurationOrder": "",
"Events_TableHead_EventType": "",
"Events_TableHead_IP": "",
"Events_TableHead_IPOrder": "",
"Events_TableHead_Order": "",
"Events_TableHead_Owner": "",
"Events_Table_info": "",
"Events_Table_nav_next": "",
"Events_Table_nav_prev": "",
"Events_Tablelenght": "",
"Events_Tablelenght_all": "",
"Events_Title": "",
"Gen_Action": "",
"Gen_AreYouSure": "",
"Gen_Backup": "",
"Gen_Cancel": "",
"Gen_Copy": "",
"Gen_DataUpdatedUITakesTime": "",
"Gen_Delete": "",
"Gen_DeleteAll": "",
"Gen_Error": "",
"Gen_LockedDB": "",
"Gen_Okay": "",
"Gen_Purge": "",
"Gen_ReadDocs": "",
"Gen_Restore": "",
"Gen_Run": "",
"Gen_Save": "",
"Gen_Saved": "",
"Gen_Selected_Devices": "",
"Gen_Switch": "",
"Gen_Upd": "",
"Gen_Upd_Fail": "",
"Gen_Warning": "",
"Gen_Work_In_Progress": "",
"General_display_name": "",
"General_icon": "",
"HRS_TO_KEEP_NEWDEV_description": "",
"HRS_TO_KEEP_NEWDEV_name": "",
"HelpFAQ_Cat_Detail": "",
"HelpFAQ_Cat_Detail_300_head": "",
"HelpFAQ_Cat_Detail_300_text_a": "",
"HelpFAQ_Cat_Detail_300_text_b": "",
"HelpFAQ_Cat_Detail_301_head_a": "",
"HelpFAQ_Cat_Detail_301_head_b": "",
"HelpFAQ_Cat_Detail_301_text": "",
"HelpFAQ_Cat_Detail_302_head_a": "",
"HelpFAQ_Cat_Detail_302_head_b": "",
"HelpFAQ_Cat_Detail_302_text": "",
"HelpFAQ_Cat_Detail_303_head": "",
"HelpFAQ_Cat_Detail_303_text": "",
"HelpFAQ_Cat_Device_200_head": "",
"HelpFAQ_Cat_Device_200_text": "",
"HelpFAQ_Cat_General": "",
"HelpFAQ_Cat_General_100_head": "",
"HelpFAQ_Cat_General_100_text_a": "",
"HelpFAQ_Cat_General_100_text_b": "",
"HelpFAQ_Cat_General_100_text_c": "",
"HelpFAQ_Cat_General_101_head": "",
"HelpFAQ_Cat_General_101_text": "",
"HelpFAQ_Cat_General_102_head": "",
"HelpFAQ_Cat_General_102_text": "",
"HelpFAQ_Cat_General_102docker_head": "",
"HelpFAQ_Cat_General_102docker_text": "",
"HelpFAQ_Cat_General_103_head": "",
"HelpFAQ_Cat_General_103_text": "",
"HelpFAQ_Cat_Network_600_head": "",
"HelpFAQ_Cat_Network_600_text": "",
"HelpFAQ_Cat_Network_601_head": "",
"HelpFAQ_Cat_Network_601_text": "",
"HelpFAQ_Cat_Presence_400_head": "",
"HelpFAQ_Cat_Presence_400_text": "",
"HelpFAQ_Cat_Presence_401_head": "",
"HelpFAQ_Cat_Presence_401_text": "",
"HelpFAQ_Title": "",
"LOG_LEVEL_description": "",
"LOG_LEVEL_name": "",
"Loading": "",
"Login_Box": "",
"Login_Default_PWD": "",
"Login_Psw-box": "",
"Login_Psw_alert": "",
"Login_Psw_folder": "",
"Login_Psw_new": "",
"Login_Psw_run": "",
"Login_Remember": "",
"Login_Remember_small": "",
"Login_Submit": "",
"Login_Toggle_Alert_headline": "",
"Login_Toggle_Info": "",
"Login_Toggle_Info_headline": "",
"Maintenance_Running_Version": "",
"Maintenance_Status": "",
"Maintenance_Title": "",
"Maintenance_Tool_ExportCSV": "",
"Maintenance_Tool_ExportCSV_noti": "",
"Maintenance_Tool_ExportCSV_noti_text": "",
"Maintenance_Tool_ExportCSV_text": "",
"Maintenance_Tool_ImportCSV": "",
"Maintenance_Tool_ImportCSV_noti": "",
"Maintenance_Tool_ImportCSV_noti_text": "",
"Maintenance_Tool_ImportCSV_text": "",
"Maintenance_Tool_arpscansw": "",
"Maintenance_Tool_arpscansw_noti": "",
"Maintenance_Tool_arpscansw_noti_text": "",
"Maintenance_Tool_arpscansw_text": "",
"Maintenance_Tool_backup": "",
"Maintenance_Tool_backup_noti": "",
"Maintenance_Tool_backup_noti_text": "",
"Maintenance_Tool_backup_text": "",
"Maintenance_Tool_check_visible": "",
"Maintenance_Tool_darkmode": "",
"Maintenance_Tool_darkmode_noti": "",
"Maintenance_Tool_darkmode_noti_text": "",
"Maintenance_Tool_darkmode_text": "",
"Maintenance_Tool_del_ActHistory": "",
"Maintenance_Tool_del_ActHistory_noti": "",
"Maintenance_Tool_del_ActHistory_noti_text": "",
"Maintenance_Tool_del_ActHistory_text": "",
"Maintenance_Tool_del_alldev": "",
"Maintenance_Tool_del_alldev_noti": "",
"Maintenance_Tool_del_alldev_noti_text": "",
"Maintenance_Tool_del_alldev_text": "",
"Maintenance_Tool_del_allevents": "",
"Maintenance_Tool_del_allevents30": "",
"Maintenance_Tool_del_allevents30_noti": "",
"Maintenance_Tool_del_allevents30_noti_text": "",
"Maintenance_Tool_del_allevents30_text": "",
"Maintenance_Tool_del_allevents_noti": "",
"Maintenance_Tool_del_allevents_noti_text": "",
"Maintenance_Tool_del_allevents_text": "",
"Maintenance_Tool_del_empty_macs": "",
"Maintenance_Tool_del_empty_macs_noti": "",
"Maintenance_Tool_del_empty_macs_noti_text": "",
"Maintenance_Tool_del_empty_macs_text": "",
"Maintenance_Tool_del_selecteddev": "",
"Maintenance_Tool_del_selecteddev_text": "",
"Maintenance_Tool_del_unknowndev": "",
"Maintenance_Tool_del_unknowndev_noti": "",
"Maintenance_Tool_del_unknowndev_noti_text": "",
"Maintenance_Tool_del_unknowndev_text": "",
"Maintenance_Tool_displayed_columns_text": "",
"Maintenance_Tool_drag_me": "",
"Maintenance_Tool_order_columns_text": "",
"Maintenance_Tool_purgebackup": "",
"Maintenance_Tool_purgebackup_noti": "",
"Maintenance_Tool_purgebackup_noti_text": "",
"Maintenance_Tool_purgebackup_text": "",
"Maintenance_Tool_restore": "",
"Maintenance_Tool_restore_noti": "",
"Maintenance_Tool_restore_noti_text": "",
"Maintenance_Tool_restore_text": "",
"Maintenance_Tool_upgrade_database_noti": "",
"Maintenance_Tool_upgrade_database_noti_text": "",
"Maintenance_Tool_upgrade_database_text": "",
"Maintenance_Tools_Tab_BackupRestore": "",
"Maintenance_Tools_Tab_Logging": "",
"Maintenance_Tools_Tab_Settings": "",
"Maintenance_Tools_Tab_Tools": "",
"Maintenance_Tools_Tab_UISettings": "",
"Maintenance_arp_status": "",
"Maintenance_arp_status_off": "",
"Maintenance_arp_status_on": "",
"Maintenance_built_on": "",
"Maintenance_current_version": "",
"Maintenance_database_backup": "",
"Maintenance_database_backup_found": "",
"Maintenance_database_backup_total": "",
"Maintenance_database_lastmod": "",
"Maintenance_database_path": "",
"Maintenance_database_rows": "",
"Maintenance_database_size": "",
"Maintenance_lang_selector_apply": "",
"Maintenance_lang_selector_empty": "",
"Maintenance_lang_selector_lable": "",
"Maintenance_lang_selector_text": "",
"Maintenance_new_version": "",
"Maintenance_themeselector_apply": "",
"Maintenance_themeselector_empty": "",
"Maintenance_themeselector_lable": "",
"Maintenance_themeselector_text": "",
"Maintenance_version": "",
"NETWORK_DEVICE_TYPES_description": "",
"NETWORK_DEVICE_TYPES_name": "",
"Navigation_About": "",
"Navigation_Devices": "",
"Navigation_Donations": "",
"Navigation_Events": "",
"Navigation_HelpFAQ": "",
"Navigation_Integrations": "",
"Navigation_Maintenance": "",
"Navigation_Monitoring": "",
"Navigation_Network": "",
"Navigation_Plugins": "",
"Navigation_Presence": "",
"Navigation_Report": "",
"Navigation_Settings": "",
"Navigation_SystemInfo": "",
"Navigation_Workflows": "",
"Network_Assign": "",
"Network_Cant_Assign": "",
"Network_Configuration_Error": "",
"Network_Connected": "",
"Network_ManageAdd": "",
"Network_ManageAdd_Name": "",
"Network_ManageAdd_Name_text": "",
"Network_ManageAdd_Port": "",
"Network_ManageAdd_Port_text": "",
"Network_ManageAdd_Submit": "",
"Network_ManageAdd_Type": "",
"Network_ManageAdd_Type_text": "",
"Network_ManageAssign": "",
"Network_ManageDel": "",
"Network_ManageDel_Name": "",
"Network_ManageDel_Name_text": "",
"Network_ManageDel_Submit": "",
"Network_ManageDevices": "",
"Network_ManageEdit": "",
"Network_ManageEdit_ID": "",
"Network_ManageEdit_ID_text": "",
"Network_ManageEdit_Name": "",
"Network_ManageEdit_Name_text": "",
"Network_ManageEdit_Port": "",
"Network_ManageEdit_Port_text": "",
"Network_ManageEdit_Submit": "",
"Network_ManageEdit_Type": "",
"Network_ManageEdit_Type_text": "",
"Network_ManageLeaf": "",
"Network_ManageUnassign": "",
"Network_NoAssignedDevices": "",
"Network_NoDevices": "",
"Network_Node": "",
"Network_Node_Name": "",
"Network_Parent": "",
"Network_Root": "",
"Network_Root_Not_Configured": "",
"Network_Root_Unconfigurable": "",
"Network_Table_Hostname": "",
"Network_Table_IP": "",
"Network_Table_State": "",
"Network_Title": "",
"Network_UnassignedDevices": "",
"PIALERT_WEB_PASSWORD_description": "",
"PIALERT_WEB_PASSWORD_name": "",
"PIALERT_WEB_PROTECTION_description": "",
"PIALERT_WEB_PROTECTION_name": "",
"PLUGINS_KEEP_HIST_description": "",
"PLUGINS_KEEP_HIST_name": "",
"Plugins_DeleteAll": "",
"Plugins_Filters_Mac": "",
"Plugins_History": "",
"Plugins_Objects": "",
"Plugins_Out_of": "",
"Plugins_Unprocessed_Events": "",
"Plugins_no_control": "",
"Presence_CalHead_day": "",
"Presence_CalHead_lang": "",
"Presence_CalHead_month": "",
"Presence_CalHead_quarter": "",
"Presence_CalHead_week": "",
"Presence_CalHead_year": "",
"Presence_CallHead_Devices": "",
"Presence_Loading": "",
"Presence_Shortcut_AllDevices": "",
"Presence_Shortcut_Archived": "",
"Presence_Shortcut_Connected": "",
"Presence_Shortcut_Devices": "",
"Presence_Shortcut_DownAlerts": "",
"Presence_Shortcut_Favorites": "",
"Presence_Shortcut_NewDevices": "",
"Presence_Title": "",
"REPORT_DASHBOARD_URL_description": "",
"REPORT_DASHBOARD_URL_name": "",
"REPORT_ERROR": "",
"REPORT_MAIL_description": "",
"REPORT_MAIL_name": "",
"REPORT_TITLE": "",
"RandomMAC_hover": "",
"SCAN_SUBNETS_description": "",
"SYSTEM_TITLE": "",
"Setting_Override": "",
"Setting_Override_Description": "",
"Settings_Metadata_Toggle": "",
"Settings_device_Scanners_desync": "",
"Settings_device_Scanners_desync_popup": "",
"Speedtest_Results": "",
"Systeminfo_CPU": "",
"Systeminfo_CPU_Cores": "",
"Systeminfo_CPU_Name": "",
"Systeminfo_CPU_Speed": "",
"Systeminfo_CPU_Temp": "",
"Systeminfo_CPU_Vendor": "",
"Systeminfo_Client_Resolution": "",
"Systeminfo_Client_User_Agent": "",
"Systeminfo_General": "",
"Systeminfo_General_Date": "",
"Systeminfo_General_Date2": "",
"Systeminfo_General_Full_Date": "",
"Systeminfo_General_TimeZone": "",
"Systeminfo_Memory": "",
"Systeminfo_Memory_Total_Memory": "",
"Systeminfo_Memory_Usage": "",
"Systeminfo_Memory_Usage_Percent": "",
"Systeminfo_Motherboard": "",
"Systeminfo_Motherboard_BIOS": "",
"Systeminfo_Motherboard_BIOS_Date": "",
"Systeminfo_Motherboard_BIOS_Vendor": "",
"Systeminfo_Motherboard_Manufactured": "",
"Systeminfo_Motherboard_Name": "",
"Systeminfo_Motherboard_Revision": "",
"Systeminfo_Network": "",
"Systeminfo_Network_Accept_Encoding": "",
"Systeminfo_Network_Accept_Language": "",
"Systeminfo_Network_Connection_Port": "",
"Systeminfo_Network_HTTP_Host": "",
"Systeminfo_Network_HTTP_Referer": "",
"Systeminfo_Network_HTTP_Referer_String": "",
"Systeminfo_Network_Hardware": "",
"Systeminfo_Network_Hardware_Interface_Mask": "",
"Systeminfo_Network_Hardware_Interface_Name": "",
"Systeminfo_Network_Hardware_Interface_RX": "",
"Systeminfo_Network_Hardware_Interface_TX": "",
"Systeminfo_Network_IP": "",
"Systeminfo_Network_IP_Connection": "",
"Systeminfo_Network_IP_Server": "",
"Systeminfo_Network_MIME": "",
"Systeminfo_Network_Request_Method": "",
"Systeminfo_Network_Request_Time": "",
"Systeminfo_Network_Request_URI": "",
"Systeminfo_Network_Secure_Connection": "",
"Systeminfo_Network_Secure_Connection_String": "",
"Systeminfo_Network_Server_Name": "",
"Systeminfo_Network_Server_Name_String": "",
"Systeminfo_Network_Server_Query": "",
"Systeminfo_Network_Server_Query_String": "",
"Systeminfo_Network_Server_Version": "",
"Systeminfo_Services": "",
"Systeminfo_Services_Description": "",
"Systeminfo_Services_Name": "",
"Systeminfo_Storage": "",
"Systeminfo_Storage_Device": "",
"Systeminfo_Storage_Mount": "",
"Systeminfo_Storage_Size": "",
"Systeminfo_Storage_Type": "",
"Systeminfo_Storage_Usage": "",
"Systeminfo_Storage_Usage_Free": "",
"Systeminfo_Storage_Usage_Mount": "",
"Systeminfo_Storage_Usage_Total": "",
"Systeminfo_Storage_Usage_Used": "",
"Systeminfo_System": "",
"Systeminfo_System_AVG": "",
"Systeminfo_System_Architecture": "",
"Systeminfo_System_Kernel": "",
"Systeminfo_System_OSVersion": "",
"Systeminfo_System_Running_Processes": "",
"Systeminfo_System_System": "",
"Systeminfo_System_Uname": "",
"Systeminfo_System_Uptime": "",
"Systeminfo_This_Client": "",
"Systeminfo_USB_Devices": "",
"TIMEZONE_description": "",
"TIMEZONE_name": "",
"UI_LANG_description": "",
"UI_LANG_name": "",
"UI_MY_DEVICES_description": "",
"UI_MY_DEVICES_name": "",
"UI_NOT_RANDOM_MAC_description": "",
"UI_NOT_RANDOM_MAC_name": "",
"UI_PRESENCE_description": "",
"UI_PRESENCE_name": "",
"devices_old": "",
"general_event_description": "",
"general_event_title": "",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "",
"report_time": "",
"run_event_icon": "",
"run_event_tooltip": "",
"settings_core_icon": "",
"settings_core_label": "",
"settings_device_scanners": "",
"settings_device_scanners_icon": "",
"settings_device_scanners_label": "",
"settings_enabled": "",
"settings_enabled_icon": "",
"settings_expand_all": "",
"settings_imported": "",
"settings_imported_label": "",
"settings_missing": "",
"settings_missing_block": "",
"settings_old": "",
"settings_other_scanners": "",
"settings_other_scanners_icon": "",
"settings_other_scanners_label": "",
"settings_publishers": "",
"settings_publishers_icon": "",
"settings_publishers_label": "",
"settings_saved": "",
"settings_system_icon": "",
"settings_system_label": "",
"test_event_icon": "",
"test_event_tooltip": ""
}

View File

@@ -4,8 +4,6 @@
require 'php/templates/notification.php';
?>
<script src="js/pialert_common.js"></script>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">

View File

@@ -1,37 +1,51 @@
## 📚 Docs for individual plugins
> Community translations of this file (might be out-of-date): <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/README_ES.md">Spanish(<img src="https://github.com/lipis/flag-icons/blob/main/flags/4x3/es.svg" alt="README_ES.md" style="height: 16px !important;width: 20px !important;padding-inline:3px !important;">)</a>, <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/README_DE.md">German(<img src="https://github.com/lipis/flag-icons/blob/main/flags/4x3/de.svg" alt="README_DE.md" style="height: 16px !important;width: 20px !important;padding-inline:3px !important;">)</a>
### 🏴 Community translations of this file
# 📚 Docs for individual plugins
* <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/README_ES.md">
<img src="https://github.com/lipis/flag-icons/blob/main/flags/4x3/es.svg" alt="README_ES.md" style="height: 20px !important;width: 20px !important;"> Spanish (Spain)
</a>
>[!NOTE]
> Please check this [Plugins debugging guide](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEBUG_PLUGINS.md) and the corresponding Plugin documentation in the below table if you are facing issues.
* <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/README_DE.md">
<img src="https://github.com/lipis/flag-icons/blob/main/flags/4x3/de.svg" alt="README_DE.md" style="height: 20px !important;width: 20px !important;"> German (Germany)
</a>
## 🔌 Plugins & 📚 Docs
### 🔌 Plugins & 📚 Docs
| Required | CurrentScan | Unique Prefix | Data source | Type | Link + Docs |
|----------|-------------|---------------|--------------------|----------------|---------------------------------------------------------------------|
| | | APPRISE | Script | 💬 publisher | 📚[_publisher_apprise](/front/plugins/_publisher_apprise/) |
| | Yes | ARPSCAN | Script | 🔍dev scanner | 📚[arp_scan](/front/plugins/arp_scan/) |
| | | CSVBCKP | Script | ⚙ system | 📚[csv_backup](/front/plugins/csv_backup/) |
| Yes* | | DBCLNP | Script | ⚙ system | 📚[db_cleanup](/front/plugins/db_cleanup/) |
| | | DDNS | Script | ⚙ system | 📚[ddns_update](/front/plugins/ddns_update/) |
| | Yes | DHCPLSS | Script | 🔍dev scanner | 📚[dhcp_leases](/front/plugins/dhcp_leases/) |
| | | DHCPSRVS | Script | ♻ other | 📚[dhcp_servers](/front/plugins/dhcp_servers/) |
| | Yes | INTRNT | Script | 🔍dev scanner | 📚[internet_ip](/front/plugins/internet_ip/) |
| | | INTRSPD | Script | ♻ other | 📚[internet_speedtest](/front/plugins/internet_speedtest/) |
| | | MAINT | Script | ⚙ system | 📚[maintenance](/front/plugins/maintenance/) |
| | | MQTT | Script | 💬 publisher | 📚[_publisher_mqtt](/front/plugins/_publisher_mqtt/) |
| Yes | | NEWDEV | Template | ⚙ system | 📚[newdev_template](/front/plugins/newdev_template/) |
| | | NMAP | Script | ♻ other | 📚[nmap_scan](/front/plugins/nmap_scan/) |
| | | NSLOOKUP | Script | ♻ other | 📚[nslookup_scan](/front/plugins/nslookup_scan/) |
| Yes | | NTFPRCS | Template | ⚙ system | 📚[notification_processing](/front/plugins/notification_processing/)|
| | | NTFY | Script | 💬 publisher | 📚[_publisher_ntfy](/front/plugins/_publisher_ntfy/) |
| | | PHOLUS | Script | ♻ other | 📚[pholus_scan](/front/plugins/pholus_scan/) |
| | Yes | PIHOLE | External SQLite DB | 🔍dev scanner | 📚[pihole_scan](/front/plugins/pihole_scan/) |
| | | PUSHSAFER | Script | 💬 publisher | 📚[_publisher_pushsafer](/front/plugins/_publisher_pushsafer/) |
| | | PUSHOVER | Script | 💬 publisher | 📚[_pushover_pushsafer](/front/plugins/_publisher_pushover/) |
| | | SETPWD | Script | ⚙ system | 📚[set_password](/front/plugins/set_password/) |
| | | SMTP | Script | 💬 publisher | 📚[_publisher_email](/front/plugins/_publisher_email/) |
| | Yes | SNMPDSC | Script | 🔍dev scanner | 📚[snmp_discovery](/front/plugins/snmp_discovery/) |
| | Yes** | UNDIS | Script | ♻ other | 📚[undiscoverables](/front/plugins/undiscoverables/) |
| | Yes | UNFIMP | Script | 🔍dev scanner | 📚[unifi_import](/front/plugins/unifi_import/) |
| | | VNDRPDT | Script | ⚙ system | 📚[vendor_update](/front/plugins/vendor_update/) |
| | | WEBHOOK | Script | 💬 publisher | 📚[_publisher_webhook](/front/plugins/_publisher_webhook/) |
| | | WEBMON | Script | ♻ other | 📚[website_monitor](/front/plugins/website_monitor/) |
| N/A | | N/A | SQL query | | N/A, but the External SQLite DB plugins work similarly |
| Required | CurrentScan | Unique Prefix | Plugin Type | Link + Docs |
|-------------|-------------|-----------------------|------------------------|----------------------------------------------------------|
| | Yes | ARPSCAN | Script | [arp_scan](/front/plugins/arp_scan/) |
| | | CSVBCKP | Script | [csv_backup](/front/plugins/csv_backup/) |
| | Yes | DHCPLSS | Script | [dhcp_leases](/front/plugins/dhcp_leases/) |
| | | DHCPSRVS | Script | [dhcp_servers](/front/plugins/dhcp_servers/) |
| Yes | | NEWDEV | Template | [newdev_template](/front/plugins/newdev_template/) |
| | | NMAP | Script | [nmap_scan](/front/plugins/nmap_scan/) |
| | Yes | PIHOLE | External SQLite DB | [pihole_scan](/front/plugins/pihole_scan/) |
| | | SETPWD | Script | [set_password](/front/plugins/set_password/) |
| | | SNMPDSC | Script | [snmp_discovery](/front/plugins/snmp_discovery/) |
| | Yes* | UNDIS | Script | [undiscoverables](/front/plugins/undiscoverables/) |
| | Yes | UNFIMP | Script | [unifi_import](/front/plugins/unifi_import/) |
| | | WEBMON | Script | [website_monitor](/front/plugins/website_monitor/) |
| N/A | | N/A | SQL query | No example available, but the External SQLite based plugins work very similar |
>* The Undiscoverables plugin (`UNDIS`) inserts only user-specified dummy devices.
> \* The database cleanup plugin (`DBCLNP`) is not _required_ but the app will become unusable after a while if not executed.
>
> \** The Undiscoverables plugin (`UNDIS`) inserts only user-specified dummy devices.
> [!NOTE]
> You soft-disable plugins via Settings or completely ignore plugins by placing a `ignore_plugin` file into the plugin directory. The difference is that ignored plugins don't show up anywhere in the UI (Settings, Device details, Plugins pages). The app skips ignored plugins completely. Device-detecting plugins insert values into the `CurrentScan` database table. The plugins that are not required are safe to ignore, however it makes sense to have a least some device-detecting plugins (that insert entries into the `CurrentScan` table) enabled, such as ARPSCAN or PIHOLE.
> You soft-disable plugins via Settings or completely ignore plugins by placing a `ignore_plugin` file into the plugin directory. The difference is that ignored plugins don't show up anywhere in the UI (Settings, Device details, Plugins pages). The app skips ignored plugins completely. Device-detecting plugins insert values into the `CurrentScan` database table. The plugins that are not required are safe to ignore, however it makes sense to have a least some device-detecting plugins (that insert entries into the `CurrentScan` table) enabled, such as `ARPSCAN` or `PIHOLE`.
> It's recommended to use the same schedule interval for all plugins responsible for discovering new devices.
@@ -73,13 +87,13 @@ Follow the below very carefully and check example plugin(s) if you'd like to wri
* 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:
## ❗ Known limitations:
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. (race condition?)
* Existing plugin objects are sometimes not interpreted correctly and a new object is created instead, resulting in duplicate entries. (race condition?)
* Occasional (experienced twice) hanging of processing plugin script file.
UI displays outdated values until the API endpoints get refreshed.
* UI displays outdated values until the API endpoints get refreshed.
## Plugin file structure overview
@@ -98,7 +112,10 @@ More on specifics below.
### Column order and values
| Order | Represented Column | Required | Description |
> [!IMPORTANT]
> Spend some time reading and trying to understand the below table. This is the interface between the Plugins and the core application.
| Order | Represented Column | Value 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 |
@@ -115,17 +132,21 @@ More on specifics below.
# config.json structure
The `config.json` file is the manifest of the plugin. It contains mainly settings definitions and the mapping of Plugin objects to PiAlert objects.
## Supported data sources
Currently, these data sources are supported (valid `data_source` value).
| Name | `data_source` value | Needs to return a "table" | Overview (more details on this page below) |
| Name | `data_source` value | Needs to return a "table"* | Overview (more details on this page below) |
|----------------------|----------------------|----------------------|----------------------|
| Script | `script` | no | Executes any linux command in the `CMD` setting. |
| Pialert DB query | `pialert-db-query` | yes | Executes a SQL query on the PiAlert database in the `CMD` setting. |
| Template | `template` | no | Used to generate internal settings, such as default values. |
| External SQLite DB query | `sqlite-db-query` | yes | Executes a SQL query from the `CMD` setting on an external SQLite database mapped in the `DB_PATH` setting. |
| Plugin type | `plugin_type` | no | Specifies the type of the plugin and in which section the Plugin settings are displayed (`<general|system|scanner|other|publisher>`). |
> * "Needs to return a "table"" means that the application expects a `last_result.log` file with some results. It's not a blocker, however warnings in the `pialert.log` might be logged.
> 🔎Example
>```json
@@ -142,7 +163,7 @@ You can show or hide the UI on the "Plugins" page and "Plugins" tab for a plugin
### "data_source": "script"
If the `data_source` is set to `script` the `CMD` setting (that you specify in the `settings` array section in the `config.json`) contains an executable Linux command, that usually generates a `last_result.log` file (not required if you don't import any data into the app). This file needs to be stored in the same folder as the plugin.
If the `data_source` is set to `script` the `CMD` setting (that you specify in the `settings` array section in the `config.json`) contains an executable Linux command, that usually generates a `last_result.log` file (not required if you don't import any data into the app). The `last_result.log` file needs to be saved in the same folder as the plugin.
> [!IMPORTANT]
> A lot of the work is taken care of by the [`plugin_helper.py` library](/front/plugins/plugin_helper.py). You don't need to manage the `last_result.log` file if using the helper objects. Check other `script.py` of other plugins for details (The [Undicoverables plugins `script.py` file](/front/plugins/undiscoverables/script.py) is a good example).
@@ -183,7 +204,7 @@ https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|
### "data_source": "pialert-db-query"
If the `data_source` 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.
If the `data_source` 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.
@@ -230,11 +251,13 @@ This SQL query is executed on the `pialert.db` SQLite database file.
### "data_source": "template"
Used to initialize internal settings. Check the `newdev_template` plugin for details.
In most cases, it is used to initialize settings. Check the `newdev_template` plugin for details.
### "data_source": "sqlite-db-query"
You can execute a SQL query on an external database connected to the current PiALert database via a temporary `EXTERNAL_<unique prefix>.` prefix. For example for `PIHOLE` (`"unique_prefix": "PIHOLE"`) it is `EXTERNAL_PIHOLE.`. The external SQLite database file has to be mapped in the container to the path specified in the `DB_PATH` setting:
You can execute a SQL query on an external database connected to the current PiALert database via a temporary `EXTERNAL_<unique prefix>.` prefix.
For example for `PIHOLE` (`"unique_prefix": "PIHOLE"`) it is `EXTERNAL_PIHOLE.`. The external SQLite database file has to be mapped in the container to the path specified in the `DB_PATH` setting:
> 🔎Example
>
@@ -258,7 +281,7 @@ You can execute a SQL query on an external database connected to the current PiA
> ...
>```
The actual SQL query you want to execute is then stored as a `CMD` setting, similar to the `pialert-db-query` plugin type The format has to adhere to the format outlined in the "Column order and values" section above.
The actual SQL query you want to execute is then stored as a `CMD` setting, similar to a Plugin of the `pialert-db-query` plugin type. The format has to adhere to the format outlined in the "Column order and values" section above.
> 🔎Example
>
@@ -284,7 +307,7 @@ The actual SQL query you want to execute is then stored as a `CMD` setting, simi
## 🕳 Filters
Plugin entries can be filtered in the UI based on values entered into filter fields. The `txtMacFilter` textbox/field contains the Mac address of the currently viewed device or simply a Mac address that's available in the `mac` query string.
Plugin entries can be filtered in the UI based on values entered into filter fields. The `txtMacFilter` textbox/field contains the Mac address of the currently viewed device, or simply a Mac address that's available in the `mac` query string (`<url>?mac=aa:22:aa:22:aa:22:aa`).
| Property | Required | Description |
|----------------------|----------------------|----------------------|
@@ -294,7 +317,7 @@ Plugin entries can be filtered in the UI based on values entered into filter fie
| `compare_js_template` | yes | JavaScript code used to convert left and right side of the equation. `{value}` is replaced with input values. |
| `compare_use_quotes` | yes | If `true` then the end result of the `compare_js_template` i swrapped in `"` quotes. Use to compare strings. |
Filters are only applied if a filter is specified and the `txtMacFilter` is not `undefined` or empty (`--`).
Filters are only applied if a filter is specified, and the `txtMacFilter` is not `undefined`, or empty (`--`).
> 🔎Example:
>
@@ -310,7 +333,7 @@ Plugin entries can be filtered in the UI based on values entered into filter fie
> ],
> ```
>
>1. On the `pluginsCore.php` page is an input field with the `txtMacFilter` id:
>1. On the `pluginsCore.php` page is an input field with the id `txtMacFilter`:
>
>```html
><input class="form-control" id="txtMacFilter" type="text" value="--">
@@ -327,7 +350,7 @@ Plugin entries can be filtered in the UI based on values entered into filter fie
>6. This results in for example this code:
>
>```javascript
> // left part of teh expression coming from compare_column and right from the input field
> // left part of the expression coming from compare_column and right from the input field
> // notice the added quotes ()") around the left and right part of teh expression
> "eval('ac:82:ac:82:ac:82".toString()')" == "eval('ac:82:ac:82:ac:82".toString()')"
>```
@@ -336,7 +359,7 @@ Plugin entries can be filtered in the UI based on values entered into filter fie
### 🗺 Mapping the plugin results into a database table
Plugin results are always inserted into the standard `Plugin_Objects` database table. Optionally, PiAlert can take the results of the plugin execution and insert these results into an additional database table. This is enabled by with the property `"mapped_to_table"` in the `config.json` file. The mapping of the columns is defined in the `database_column_definitions` array.
Plugin results are always inserted into the standard `Plugin_Objects` database table. Optionally, PiAlert can take the results of the plugin execution, and insert these results into an additional database table. This is enabled by with the property `"mapped_to_table"` in the `config.json` file. The mapping of the columns is defined in the `database_column_definitions` array.
> [!NOTE]
> If results are mapped to the `CurrentScan` table, the data is then included into the regular scan loop, so for example notification for devices are sent out.
@@ -344,7 +367,7 @@ Plugin results are always inserted into the standard `Plugin_Objects` database t
>🔍 Example:
>
>For example, this approach is used to implement the `DHCPLSS` plugin. The script parses all supplied "dhcp.leases" files, gets 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 `CurrentScan` database table in the PiAlert database. All this is achieved by:
>For example, this approach is used to implement the `DHCPLSS` plugin. The script parses all supplied "dhcp.leases" files, gets the results in the generic table format outlined in the "Column order and values" section above, takes individual values, and inserts them into the `CurrentScan` 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": "CurrentScan"` in the root of the `config.json` file as shown below:
>
@@ -359,7 +382,7 @@ Plugin results are always inserted into the standard `Plugin_Objects` database t
> ...
>}
>```
>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 `cur_MAC` column in the PiAlert database `CurrentScan` table. Notice the `"mapped_to_column": "cur_MAC"` key-value pair in the sample below.
>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 `cur_MAC` column in the PiAlert database table `CurrentScan`. Notice the `"mapped_to_column": "cur_MAC"` key-value pair in the sample below.
>
>```json
>{
@@ -378,10 +401,10 @@ Plugin results are always inserted into the standard `Plugin_Objects` database t
> }
>```
>
>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.
>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 columns via the `"mapped_to_column"` property in the column definitions.
> [!NOTE]
> You can create a column mapping with a default value via the `mapped_to_column_data` property. This means that the value of the given column will always be this value. Taht also menas that the `"column": "NameDoesntMatter"` is not important as there is no databse source column.
> You can create a column mapping with a default value via the `mapped_to_column_data` property. This means that the value of the given column will always be this value. That also menas that the `"column": "NameDoesntMatter"` is not important as there is no database source column.
>🔍 Example:
@@ -408,6 +431,17 @@ Plugin results are always inserted into the standard `Plugin_Objects` database t
#### params
> [!IMPORTANT]
> An esier way to access settings in scripts is the `get_setting_value` method.
> ```python
> from helper import get_setting_value
>
> ...
> NTFY_TOPIC = get_setting_value('NTFY_TOPIC')
> ...
>
> ```
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:
@@ -532,8 +566,8 @@ Required attributes are:
| `"name"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
| `"description"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
| (optional) `"events"` | Specifies whether to generate an execution button next to the input field of the setting. Supported values: |
| | - `test` |
| | - `run` |
| | - `"test"` - For notification plugins testing |
| | - `"run"` - Regular plugins testing |
| (optional) `"override_value"` | Used to determine a user-defined override for the setting. Useful for template-based plugins, where you can choose to leave the current value or override it with the value defined in the setting. (Work in progress) |
| (optional) `"events"` | Used to trigger the plugin. Usually used on the `RUN` setting. Not fully tested in all scenarios. Will show a play button next to the setting. After clicking, an event is generated for the backend in the `Parameters` database table to process the front-end event on the next run. |
@@ -546,15 +580,16 @@ You can have any `"function": "my_custom_name"` custom name, however, the ones l
| ------- | ----------- |
| `RUN` | (required) Specifies when the service is executed. |
| | Supported Options: |
| | - "disabled" - not run |
| | - "disabled" - do not run |
| | - "once" - run on app start or on settings saved |
| | - "schedule" - if included, then a `RUN_SCHD` setting needs to be specified to determine the schedule |
| | - "always_after_scan" - run always after a scan is finished |
| | - "before_name_updates" - run before device names are updated (for name discovery plugins) |
| | - "on_new_device" - run when a new device is detected |
| | - "before_config_save" - run before the config is marked as saved. Useful if your plugin needs to modify the `pialert.conf` file. |
| `RUN_SCHD` | (required if you include the above `RUN` function) Cron-like scheduling is used if the `RUN` setting is set to `schedule`. |
| `RUN_SCHD` | (required if you include "schedule" in the above `RUN` function) Cron-like scheduling is used if the `RUN` setting is set to `schedule`. |
| `CMD` | (required) Specifies the command that 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). |
| `API_SQL` | (not implemented) 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) Specifies the maximum execution time of the script. If not specified, a default value of 10 seconds is used to prevent hanging. |
| `WATCH` | (optional) Specifies which database columns are watched for changes for this particular plugin. If not specified, no notifications are sent. |
| `REPORT_ON` | (optional) Specifies when to send a notification. Supported options are: |
@@ -564,7 +599,6 @@ You can have any `"function": "my_custom_name"` custom name, however, the ones l
| | - `missing-in-last-scan` - if the object is missing compared to previous scans |
> 🔎 Example:
>
> ```json
@@ -610,9 +644,11 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
| Supported Types | Description |
| -------------- | ----------- |
| `label` | Displays a column only. |
| `text` | Makes a column editable, and a save icon is displayed next to it. See below for information on `threshold`, `replace`. |
| `textarea_readonly` | Generates a read only text area and cleans up the text to display it somewhat formatted with new lines preserved. |
| See below for information on `threshold`, `replace`. | |
| | |
| `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. |
| `options_params` Property | Used in conjunction with a `"options": "[{value}]"` template and `text.select`. Can specify SQL query (needs to return 2 columns `SELECT dev_Name as name, dev_Mac as id`) or Setting (not tested) to populate the dropdown. Check example below or have a look at the `NEWDEV` plugin `config.json` file. |
| `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is 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, which is compared to the "value." If the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value". |
| `regex` | Applies a regex to the value. The `options` array contains objects with an `type` (must be set to `regex`) and `param` (must contain the regex itself) property. |
@@ -624,12 +660,33 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
| `url` | The value is considered to be a URL, so a link is generated. |
| `textbox_save` | Generates an editable and saveable text box that saves values in the database. Primarily intended for the `UserData` database column in the `Plugins_Objects` table. |
| `url_http_https` | Generates two links with the `https` and `http` prefix as lock icons. |
| `eval` | Evaluates as JavaScript. Use the variable `value` to use the given column value as input (e.g. `'<b>${value}<b>'` (replace ' with ` in your code) ) |
> [!NOTE]
> Supports chaining. You can chain multiple resolvers with `.`. For example `regex.url_http_https`. This will apply the `regex` resolver and then the `url_http_https` resolver.
```json
"function": "dev_DeviceType",
"type": "text.select",
"maxLength": 30,
"default_value": "",
"options": ["{value}"],
"options_params" : [
{
"name" : "value",
"type" : "sql",
"value" : "SELECT '' as id, '' as name UNION SELECT dev_DeviceType as id, dev_DeviceType as name FROM (SELECT dev_DeviceType FROM Devices UNION SELECT 'Smartphone' UNION SELECT 'Tablet' UNION SELECT 'Laptop' UNION SELECT 'PC' UNION SELECT 'Printer' UNION SELECT 'Server' UNION SELECT 'NAS' UNION SELECT 'Domotic' UNION SELECT 'Game Console' UNION SELECT 'SmartTV' UNION SELECT 'Clock' UNION SELECT 'House Appliance' UNION SELECT 'Phone' UNION SELECT 'AP' UNION SELECT 'Gateway' UNION SELECT 'Firewall' UNION SELECT 'Switch' UNION SELECT 'WLAN' UNION SELECT 'Router' UNION SELECT 'Other') AS all_devices ORDER BY id;"
},
{
"name" : "uilang",
"type" : "setting",
"value" : "UI_LANG"
}
]
```
```json
{

View File

@@ -381,7 +381,7 @@ Plugin results are always inserted into the standard `Plugin_Objects` database t
>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.
> [!NOTE]
> You can create a column mapping with a default value via the `mapped_to_column_data` property. This means that the value of the given column will always be this value. Taht also menas that the `"column": "NameDoesntMatter"` is not important as there is no databse source column.
> You can create a column mapping with a default value via the `mapped_to_column_data` property. This means that the value of the given column will always be this value. Taht also menas that the `"column": "NameDoesntMatter"` is not important as there is no database source column.
>🔍 Beispiel:
@@ -554,7 +554,7 @@ You can have any `"function": "my_custom_name"` custom name, however, the ones l
| | - "before_config_save" - run before the config is marked as saved. Useful if your plugin needs to modify the `pialert.conf` file. |
| `RUN_SCHD` | (required if you include the above `RUN` function) Cron-like scheduling is used if the `RUN` setting is set to `schedule`. |
| `CMD` | (required) Specifies the command that 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). |
| `API_SQL` | (not implemented) 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) Specifies the maximum execution time of the script. If not specified, a default value of 10 seconds is used to prevent hanging. |
| `WATCH` | (optional) Specifies which database columns are watched for changes for this particular plugin. If not specified, no notifications are sent. |
| `REPORT_ON` | (optional) Specifies when to send a notification. Supported options are: |

View File

@@ -0,0 +1,8 @@
## Overview
[Apprise](https://hub.docker.com/r/caronc/apprise) is a notification gateway/publisher that allows you to push notifications to 80+ different services.
### Usage
- Go to settings and fill in relevant details.

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