Files
2026-09-14 23:01:02 +00:00

6865 lines
144 KiB
HTML

<!DOCTYPE html><html lang="en" class="no-js"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="The main documentation resource for NetAlertX - Network visibility &amp; Asset intelligence application">
<link rel="canonical" href="https://docs.netalertx.com/PLUGINS_DEV/">
<link rel="prev" href="../DEV_PORTS_HOST_MODE/">
<link rel="next" href="../PLUGINS_DEV_QUICK_START/">
<link rel="icon" href="../img/netalertx_docs.png">
<meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.21">
<title>Overview - Documentation</title>
<link rel="stylesheet" href="../assets/stylesheets/main.66ac8b77.min.css">
<link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&amp;display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<link rel="stylesheet" href="../stylesheets/custom.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
<!-- Google Tag Manager -->
<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-KCRSGLP8J2"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-KCRSGLP8J2');
</script>
<!-- End Google Tag Manager -->
<link href="../assets/stylesheets/glightbox.min.css" rel="stylesheet"><script src="../assets/javascripts/glightbox.min.js"></script><style id="glightbox-style">
html.glightbox-open { overflow: initial; height: 100%; }
.gslide-title { margin-top: 0px; user-select: text; }
.gslide-desc { color: #666; user-select: text; }
.gslide-image img { background: white; }
.gscrollbar-fixer { padding-right: 15px; }
.gdesc-inner { font-size: 0.75rem; }
body[data-md-color-scheme="slate"] .gdesc-inner { background: var(--md-default-bg-color); }
body[data-md-color-scheme="slate"] .gslide-title { color: var(--md-default-fg-color); }
body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color); }
</style></head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="indigo" data-md-color-accent="deep-purple">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#plugin-development-guide" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KCRSGLP8J2" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href=".." title="Documentation" class="md-header__button md-logo" aria-label="Documentation" data-md-component="logo">
<img src="../img/netalertx_docs.png" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"></path></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Documentation
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Overview
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="indigo" data-md-color-accent="deep-purple" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2a7 7 0 0 0-7 7c0 2.38 1.19 4.47 3 5.74V17a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-2.26c1.81-1.27 3-3.36 3-5.74a7 7 0 0 0-7-7M9 21a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-1H9v1Z"></path></svg>
</label>
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="black" data-md-color-accent="deep-purple" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_0" hidden="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7M9 21v-1h6v1a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1m3-17a5 5 0 0 0-5 5c0 2.05 1.23 3.81 3 4.58V16h4v-2.42c1.77-.77 3-2.53 3-4.58a5 5 0 0 0-5-5Z"></path></svg>
</label>
</form>
<script>var media,input,key,value,palette=__md_get("__palette");if(palette&&palette.color){"(prefers-color-scheme)"===palette.color.media&&(media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']"),palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent"));for([key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"></path></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required="">
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"></path></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard="" data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7 0-.24-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91 1.61 0 2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08Z"></path></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"></path></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix="">
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://github.com/netalertx/NetAlertX/" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"></path></svg>
</div>
<div class="md-source__repository">
GitHub
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href=".." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../INSTALLATION/" class="md-tabs__link">
Installation
</a>
</li>
<li class="md-tabs__item">
<a href="../SUBNETS/" class="md-tabs__link">
Setup
</a>
</li>
<li class="md-tabs__item">
<a href="../DEVICE_MANAGEMENT/" class="md-tabs__link">
Device guides
</a>
</li>
<li class="md-tabs__item">
<a href="../PIHOLE_GUIDE/" class="md-tabs__link">
Other guides
</a>
</li>
<li class="md-tabs__item">
<a href="../DEBUG_TIPS/" class="md-tabs__link">
Troubleshooting
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../DEV_ENV_SETUP/" class="md-tabs__link">
Development
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href=".." title="Documentation" class="md-nav__button md-logo" aria-label="Documentation" data-md-component="logo">
<img src="../img/netalertx_docs.png" alt="logo">
</a>
Documentation
</label>
<div class="md-nav__source">
<a href="https://github.com/netalertx/NetAlertX/" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"></path></svg>
</div>
<div class="md-source__repository">
GitHub
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href=".." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2">
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="0">
<span class="md-ellipsis">
Installation
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Installation
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../INSTALLATION/" class="md-nav__link">
<span class="md-ellipsis">
Installation options
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../INITIAL_SETUP/" class="md-nav__link">
<span class="md-ellipsis">
Quick setup
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_3">
<label class="md-nav__link" for="__nav_2_3" id="__nav_2_3_label" tabindex="0">
<span class="md-ellipsis">
Docker
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_3">
<span class="md-nav__icon md-icon"></span>
Docker
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../DOCKER_INSTALLATION/" class="md-nav__link">
<span class="md-ellipsis">
Docker guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DOCKER_COMPOSE/" class="md-nav__link">
<span class="md-ellipsis">
Docker Compose
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../FILE_PERMISSIONS/" class="md-nav__link">
<span class="md-ellipsis">
Docker file permissions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../UPDATES/" class="md-nav__link">
<span class="md-ellipsis">
Docker updates
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DOCKER_MAINTENANCE/" class="md-nav__link">
<span class="md-ellipsis">
Docker maintenance
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_4">
<label class="md-nav__link" for="__nav_2_4" id="__nav_2_4_label" tabindex="0">
<span class="md-ellipsis">
Docker startup troubleshooting
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_4">
<span class="md-nav__icon md-icon"></span>
Docker startup troubleshooting
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../docker-troubleshooting/arp-flux-sysctls/" class="md-nav__link">
<span class="md-ellipsis">
ARP flux sysctls
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/aufs-capabilities/" class="md-nav__link">
<span class="md-ellipsis">
Aufs capabilities
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/excessive-capabilities/" class="md-nav__link">
<span class="md-ellipsis">
Excessive capabilities
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/file-permissions/" class="md-nav__link">
<span class="md-ellipsis">
File permissions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/incorrect-user/" class="md-nav__link">
<span class="md-ellipsis">
Incorrect user
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/missing-capabilities/" class="md-nav__link">
<span class="md-ellipsis">
Missing capabilities
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/mount-configuration-issues/" class="md-nav__link">
<span class="md-ellipsis">
Mount issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/network-mode/" class="md-nav__link">
<span class="md-ellipsis">
Network mode
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/nginx-configuration-mount/" class="md-nav__link">
<span class="md-ellipsis">
Nginx mount
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/port-conflicts/" class="md-nav__link">
<span class="md-ellipsis">
Port conflicts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/read-only-filesystem/" class="md-nav__link">
<span class="md-ellipsis">
Read only
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../docker-troubleshooting/running-as-root/" class="md-nav__link">
<span class="md-ellipsis">
Running as root
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_5">
<label class="md-nav__link" for="__nav_2_5" id="__nav_2_5_label" tabindex="0">
<span class="md-ellipsis">
Other
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_5">
<span class="md-nav__icon md-icon"></span>
Other
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../SYNOLOGY_GUIDE/" class="md-nav__link">
<span class="md-ellipsis">
Synology guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DOCKER_PORTAINER/" class="md-nav__link">
<span class="md-ellipsis">
Portainer Stacks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../COMMUNITY_GUIDES/" class="md-nav__link">
<span class="md-ellipsis">
Community guides
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../HW_INSTALL/" class="md-nav__link">
<span class="md-ellipsis">
Bare-metal (Experimental)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../MIGRATION/" class="md-nav__link">
<span class="md-ellipsis">
Migration guide
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_6">
<label class="md-nav__link" for="__nav_2_6" id="__nav_2_6_label" tabindex="0">
<span class="md-ellipsis">
Help
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_6">
<span class="md-nav__icon md-icon"></span>
Help
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../COMMON_ISSUES/" class="md-nav__link">
<span class="md-ellipsis">
Common issues
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3">
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0">
<span class="md-ellipsis">
Setup
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Setup
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3_1">
<label class="md-nav__link" for="__nav_3_1" id="__nav_3_1_label" tabindex="0">
<span class="md-ellipsis">
Getting started
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_3_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3_1">
<span class="md-nav__icon md-icon"></span>
Getting started
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../SUBNETS/" class="md-nav__link">
<span class="md-ellipsis">
Subnets
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PLUGINS_OVERVIEW/" class="md-nav__link">
<span class="md-ellipsis">
Enable Plugins
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3_1_3">
<label class="md-nav__link" for="__nav_3_1_3" id="__nav_3_1_3_label" tabindex="0">
<span class="md-ellipsis">
Plugins reference
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="3" aria-labelledby="__nav_3_1_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3_1_3">
<span class="md-nav__icon md-icon"></span>
Plugins reference
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../plugins/_publisher_apprise/" class="md-nav__link">
<span class="md-ellipsis">
Apprise publisher (APPRISE)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/_publisher_email/" class="md-nav__link">
<span class="md-ellipsis">
Email publisher (SMTP) (SMTP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/_publisher_mqtt/" class="md-nav__link">
<span class="md-ellipsis">
MQTT publisher (MQTT)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/_publisher_ntfy/" class="md-nav__link">
<span class="md-ellipsis">
NTFY publisher (NTFY)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/_publisher_pushover/" class="md-nav__link">
<span class="md-ellipsis">
Pushover publisher (PUSHOVER)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/_publisher_pushsafer/" class="md-nav__link">
<span class="md-ellipsis">
Pushsafer publisher (PUSHSAFER)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/_publisher_telegram/" class="md-nav__link">
<span class="md-ellipsis">
Telegram publisher (TELEGRAM)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/_publisher_webhook/" class="md-nav__link">
<span class="md-ellipsis">
Webhook publisher (WEBHOOK)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/adguard_export/" class="md-nav__link">
<span class="md-ellipsis">
AdGuard (Device Export) (ADGUARDEXP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/adguard_import/" class="md-nav__link">
<span class="md-ellipsis">
AdGuard (Device import) (ADGUARDIMP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/arp_scan/" class="md-nav__link">
<span class="md-ellipsis">
Arp-Scan (Network scan) (ARPSCAN)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/asuswrt_import/" class="md-nav__link">
<span class="md-ellipsis">
AsusWRT device import (ASUSWRT)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/avahi_scan/" class="md-nav__link">
<span class="md-ellipsis">
AVAHISCAN (Naming) (AVAHISCAN)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/csv_backup/" class="md-nav__link">
<span class="md-ellipsis">
CSV backup (CSVBCKP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/custom_props/" class="md-nav__link">
<span class="md-ellipsis">
Custom properties (CUSTPROP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/db_cleanup/" class="md-nav__link">
<span class="md-ellipsis">
DB cleanup (DBCLNP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/ddns_update/" class="md-nav__link">
<span class="md-ellipsis">
DDNS update (DDNS)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/dhcp_leases/" class="md-nav__link">
<span class="md-ellipsis">
DHCP Leases (Device import) (DHCPLSS)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/dhcp_leases/ASUS_ROUTERS/" class="md-nav__link">
<span class="md-ellipsis">
Configuring the DHCPLSS plugin to import clients from the YazFi plugin
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/dhcp_servers/" class="md-nav__link">
<span class="md-ellipsis">
Rogue DHCP (DHCPSRVS)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/dig_scan/" class="md-nav__link">
<span class="md-ellipsis">
Dig (Name resolution) (DIGSCAN)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/freebox/" class="md-nav__link">
<span class="md-ellipsis">
Freebox (FREEBOX)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/fritzbox/" class="md-nav__link">
<span class="md-ellipsis">
FRITZ!Box (FRITZBOX)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/icmp_scan/" class="md-nav__link">
<span class="md-ellipsis">
ICMP (Status check) (ICMP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/internet_ip/" class="md-nav__link">
<span class="md-ellipsis">
Internet check (INTRNT)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/internet_speedtest/" class="md-nav__link">
<span class="md-ellipsis">
Internet speedtest (INTRSPD)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/ipneigh/" class="md-nav__link">
<span class="md-ellipsis">
IP Neigh (IPNEIGH)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/kea_api/" class="md-nav__link">
<span class="md-ellipsis">
Kea DHCP API (KEALSS)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/luci_import/" class="md-nav__link">
<span class="md-ellipsis">
Luci rpc import (Device import) (LUCIRPC)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/maintenance/" class="md-nav__link">
<span class="md-ellipsis">
Maintenance (MAINT)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/mikrotik_scan/" class="md-nav__link">
<span class="md-ellipsis">
Mikrotik (Device discovery) (MTSCAN)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/nbtscan_scan/" class="md-nav__link">
<span class="md-ellipsis">
NBTSCAN (Naming) (NBTSCAN)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/newdev_template/" class="md-nav__link">
<span class="md-ellipsis">
New Devices (NEWDEV)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/nmap_dev_scan/" class="md-nav__link">
<span class="md-ellipsis">
NMAP Device discovery (NMAPDEV)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/nmap_scan/" class="md-nav__link">
<span class="md-ellipsis">
Services &amp; Ports (NMAP) (NMAP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/notification_processing/" class="md-nav__link">
<span class="md-ellipsis">
Notification Processing (NTFPRCS)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/nslookup_scan/" class="md-nav__link">
<span class="md-ellipsis">
NSLOOKUP (Naming) (NSLOOKUP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/omada_sdn_imp/" class="md-nav__link">
<span class="md-ellipsis">
OMADA SDN import (do not use) (OMDSDN)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/omada_sdn_openapi/" class="md-nav__link">
<span class="md-ellipsis">
Omada SDN using OpenAPI (OMDSDNOPENAPI)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/pihole_api_scan/" class="md-nav__link">
<span class="md-ellipsis">
PiHole API scan (PIHOLEAPI)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/pihole_monitor/" class="md-nav__link">
<span class="md-ellipsis">
Pi-hole Monitor (PIHOLEMON)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/pihole_scan/" class="md-nav__link">
<span class="md-ellipsis">
PiHole (Device sync) (PIHOLE)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/rest_import/" class="md-nav__link">
<span class="md-ellipsis">
REST Import (RSTIMPRT)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/set_password/" class="md-nav__link">
<span class="md-ellipsis">
Set password (SETPWD)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/snmp_discovery/" class="md-nav__link">
<span class="md-ellipsis">
SNMP discovery (SNMPDSC)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/sync/" class="md-nav__link">
<span class="md-ellipsis">
Sync Hub (SYNC)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/ui_settings/" class="md-nav__link">
<span class="md-ellipsis">
UI Settings (UI)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/unifi_api_import/" class="md-nav__link">
<span class="md-ellipsis">
UniFi import (API) (UNIFIAPI)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/unifi_import/" class="md-nav__link">
<span class="md-ellipsis">
UniFi import (UNFIMP)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/vendor_update/" class="md-nav__link">
<span class="md-ellipsis">
Vendor update (VNDRPDT)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/wake_on_lan/" class="md-nav__link">
<span class="md-ellipsis">
Wake on Lan (WOL) (WOL)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/website_monitor/" class="md-nav__link">
<span class="md-ellipsis">
Services &amp; Web monitor (WEBMON)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../plugins/workflows/" class="md-nav__link">
<span class="md-ellipsis">
Workflows (WORKFLOWS)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../BACKUPS/" class="md-nav__link">
<span class="md-ellipsis">
Backups
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../SECURITY_FEATURES/" class="md-nav__link">
<span class="md-ellipsis">
Security features
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../SECURITY/" class="md-nav__link">
<span class="md-ellipsis">
Security considerations
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3_2">
<label class="md-nav__link" for="__nav_3_2" id="__nav_3_2_label" tabindex="0">
<span class="md-ellipsis">
Advanced guides
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_3_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3_2">
<span class="md-nav__icon md-icon"></span>
Advanced guides
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../REMOTE_NETWORKS/" class="md-nav__link">
<span class="md-ellipsis">
Remote networks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../NOTIFICATIONS/" class="md-nav__link">
<span class="md-ellipsis">
Notifications guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../NOTIFICATION_TEMPLATES/" class="md-nav__link">
<span class="md-ellipsis">
Notification text templates
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PUID_PGID_SECURITY/" class="md-nav__link">
<span class="md-ellipsis">
Custom PUID/GUID
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../NAME_RESOLUTION/" class="md-nav__link">
<span class="md-ellipsis">
Name resolution
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../AUTHELIA/" class="md-nav__link">
<span class="md-ellipsis">
Authelia guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PERFORMANCE/" class="md-nav__link">
<span class="md-ellipsis">
Performance guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../REVERSE_DNS/" class="md-nav__link">
<span class="md-ellipsis">
Reverse DNS
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../REVERSE_PROXY/" class="md-nav__link">
<span class="md-ellipsis">
Reverse proxy
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../WORKFLOWS/" class="md-nav__link">
<span class="md-ellipsis">
Workflows
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../WORKFLOW_EXAMPLES/" class="md-nav__link">
<span class="md-ellipsis">
Workflow examples
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DOCKER_SWARM/" class="md-nav__link">
<span class="md-ellipsis">
Docker Swarm
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3_3">
<label class="md-nav__link" for="__nav_3_3" id="__nav_3_3_label" tabindex="0">
<span class="md-ellipsis">
Best practice guides
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_3_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3_3">
<span class="md-nav__icon md-icon"></span>
Best practice guides
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../ADVISORY_EYES_ON_GLASS/" class="md-nav__link">
<span class="md-ellipsis">
NOC / Eyes-on-glass dashboard
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../ADVISORY_MULTI_NETWORK/" class="md-nav__link">
<span class="md-ellipsis">
Multi-network (VLAN / routing) monitoring
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../ADVISORY_MULTI_SITE_MONITORING/" class="md-nav__link">
<span class="md-ellipsis">
Multi-site (MSP / customer / branch) monitoring
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3_4">
<label class="md-nav__link" for="__nav_3_4" id="__nav_3_4_label" tabindex="0">
<span class="md-ellipsis">
Help
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_3_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3_4">
<span class="md-nav__icon md-icon"></span>
Help
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../COMMON_ISSUES/" class="md-nav__link">
<span class="md-ellipsis">
Common issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../RANDOM_MAC/" class="md-nav__link">
<span class="md-ellipsis">
Random MAC
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4">
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
Device guides
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Device guides
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_1">
<label class="md-nav__link" for="__nav_4_1" id="__nav_4_1_label" tabindex="0">
<span class="md-ellipsis">
Editing devices
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_1">
<span class="md-nav__icon md-icon"></span>
Editing devices
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../DEVICE_MANAGEMENT/" class="md-nav__link">
<span class="md-ellipsis">
Management
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEVICES_BULK_EDITING/" class="md-nav__link">
<span class="md-ellipsis">
Bulk editing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../CUSTOM_PROPERTIES/" class="md-nav__link">
<span class="md-ellipsis">
Custom properties
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEVICE_DISPLAY_SETTINGS/" class="md-nav__link">
<span class="md-ellipsis">
Device display settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEVICE_FILTERS/" class="md-nav__link">
<span class="md-ellipsis">
Device views filtering
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../SESSION_INFO/" class="md-nav__link">
<span class="md-ellipsis">
Session info
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEVICE_FIELD_LOCK/" class="md-nav__link">
<span class="md-ellipsis">
Field Lock/Unlock
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_2">
<label class="md-nav__link" for="__nav_4_2" id="__nav_4_2_label" tabindex="0">
<span class="md-ellipsis">
Device auditing
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_2">
<span class="md-nav__icon md-icon"></span>
Device auditing
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../DEVICE_CHANGE_LOG/" class="md-nav__link">
<span class="md-ellipsis">
Change log
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEVICE_PRESENCE/" class="md-nav__link">
<span class="md-ellipsis">
Presence
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEVICE_SOURCE_FIELDS/" class="md-nav__link">
<span class="md-ellipsis">
Device source fields
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_3">
<label class="md-nav__link" for="__nav_4_3" id="__nav_4_3_label" tabindex="0">
<span class="md-ellipsis">
Icons and network topology
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_3">
<span class="md-nav__icon md-icon"></span>
Icons and network topology
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../ICONS/" class="md-nav__link">
<span class="md-ellipsis">
Icons
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../NETWORK_TREE/" class="md-nav__link">
<span class="md-ellipsis">
Network topology
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5">
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
<span class="md-ellipsis">
Other guides
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Other guides
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../PIHOLE_GUIDE/" class="md-nav__link">
<span class="md-ellipsis">
Pi-hole guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../HOME_ASSISTANT/" class="md-nav__link">
<span class="md-ellipsis">
Home Assistant
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../SMTP/" class="md-nav__link">
<span class="md-ellipsis">
Common email services
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../REST_IMPORT/" class="md-nav__link">
<span class="md-ellipsis">
Rest import
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../WEBHOOK_N8N/" class="md-nav__link">
<span class="md-ellipsis">
Webhooks (n8n)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6">
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
<span class="md-ellipsis">
Troubleshooting
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_6">
<span class="md-nav__icon md-icon"></span>
Troubleshooting
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../DEBUG_TIPS/" class="md-nav__link">
<span class="md-ellipsis">
General tips
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../COMMON_ISSUES/" class="md-nav__link">
<span class="md-ellipsis">
Common issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../LOGGING/" class="md-nav__link">
<span class="md-ellipsis">
Inspecting logs
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEBUG_API_SERVER/" class="md-nav__link">
<span class="md-ellipsis">
API server issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEBUG_INVALID_JSON/" class="md-nav__link">
<span class="md-ellipsis">
Invalid JSON issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEBUG_PHP/" class="md-nav__link">
<span class="md-ellipsis">
PHP backend issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEBUG_PLUGINS/" class="md-nav__link">
<span class="md-ellipsis">
Plugin issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../WEB_UI_PORT_DEBUG/" class="md-nav__link">
<span class="md-ellipsis">
Web UI port issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../WORKFLOWS_DEBUGGING/" class="md-nav__link">
<span class="md-ellipsis">
Workflows issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../FIX_OFFLINE_DETECTION/" class="md-nav__link">
<span class="md-ellipsis">
Incorrect offline detection
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7" checked="">
<label class="md-nav__link" for="__nav_7" id="__nav_7_label" tabindex="">
<span class="md-ellipsis">
Development
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_7">
<span class="md-nav__icon md-icon"></span>
Development
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7_1" checked="">
<label class="md-nav__link" for="__nav_7_1" id="__nav_7_1_label" tabindex="">
<span class="md-ellipsis">
Plugin and app development
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_7_1_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_7_1">
<span class="md-nav__icon md-icon"></span>
Plugin and app development
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../DEV_ENV_SETUP/" class="md-nav__link">
<span class="md-ellipsis">
Environment setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../BUILDS/" class="md-nav__link">
<span class="md-ellipsis">
Builds
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEV_DEVCONTAINER/" class="md-nav__link">
<span class="md-ellipsis">
Devcontainer
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEV_PORTS_HOST_MODE/" class="md-nav__link">
<span class="md-ellipsis">
Devcontainer ports
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7_1_5" checked="">
<label class="md-nav__link" for="__nav_7_1_5" id="__nav_7_1_5_label" tabindex="0">
<span class="md-ellipsis">
Custom plugins
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="3" aria-labelledby="__nav_7_1_5_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_7_1_5">
<span class="md-nav__icon md-icon"></span>
Custom plugins
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Overview
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Overview
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix="">
<li class="md-nav__item">
<a href="#quick-links" class="md-nav__link">
<span class="md-ellipsis">
Quick Links
</span>
</a>
<nav class="md-nav" aria-label="Quick Links">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#getting-started" class="md-nav__link">
<span class="md-ellipsis">
🚀 Getting Started
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#core-concepts" class="md-nav__link">
<span class="md-ellipsis">
📚 Core Concepts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#architecture" class="md-nav__link">
<span class="md-ellipsis">
🏗️ Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
🐛 Troubleshooting
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#video-tutorial" class="md-nav__link">
<span class="md-ellipsis">
🎥 Video Tutorial
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#screenshots" class="md-nav__link">
<span class="md-ellipsis">
📸 Screenshots
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#use-cases" class="md-nav__link">
<span class="md-ellipsis">
Use Cases
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#limitations-notes" class="md-nav__link">
<span class="md-ellipsis">
Limitations &amp; Notes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#plugin-development-workflow" class="md-nav__link">
<span class="md-ellipsis">
Plugin Development Workflow
</span>
</a>
<nav class="md-nav" aria-label="Plugin Development Workflow">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#step-1-understand-the-basics" class="md-nav__link">
<span class="md-ellipsis">
Step 1: Understand the Basics
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#step-2-create-your-plugin" class="md-nav__link">
<span class="md-ellipsis">
Step 2: Create Your Plugin
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#step-3-configure-display" class="md-nav__link">
<span class="md-ellipsis">
Step 3: Configure &amp; Display
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#step-4-deploy-test" class="md-nav__link">
<span class="md-ellipsis">
Step 4: Deploy &amp; Test
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#plugin-file-structure" class="md-nav__link">
<span class="md-ellipsis">
Plugin File Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#plugin-manifest-configjson" class="md-nav__link">
<span class="md-ellipsis">
Plugin Manifest (config.json)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#full-reference-below" class="md-nav__link">
<span class="md-ellipsis">
Full Reference (Below)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#data-contract-output-format" class="md-nav__link">
<span class="md-ellipsis">
Data Contract &amp; Output Format
</span>
</a>
<nav class="md-nav" aria-label="Data Contract &amp; Output Format">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#the-9-mandatory-columns" class="md-nav__link">
<span class="md-ellipsis">
The 9 Mandatory Columns
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#configjson-settings-configuration" class="md-nav__link">
<span class="md-ellipsis">
Config.json: Settings &amp; Configuration
</span>
</a>
<nav class="md-nav" aria-label="Config.json: Settings &amp; Configuration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#setting-object-structure" class="md-nav__link">
<span class="md-ellipsis">
Setting Object Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#reserved-function-names" class="md-nav__link">
<span class="md-ellipsis">
Reserved Function Names
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#conventions-checklist" class="md-nav__link">
<span class="md-ellipsis">
Conventions Checklist
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#filters-data-display" class="md-nav__link">
<span class="md-ellipsis">
Filters &amp; Data Display
</span>
</a>
<nav class="md-nav" aria-label="Filters &amp; Data Display">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#filters" class="md-nav__link">
<span class="md-ellipsis">
Filters
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#database-mapping" class="md-nav__link">
<span class="md-ellipsis">
Database Mapping
</span>
</a>
<nav class="md-nav" aria-label="Database Mapping">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#static-value-mapping" class="md-nav__link">
<span class="md-ellipsis">
Static Value Mapping
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#import-behavior-columns-scancreatesdevice-scannotificationmode-scanpresence" class="md-nav__link">
<span class="md-ellipsis">
Import Behavior Columns (scanCreatesDevice, scanNotificationMode, scanPresence)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#persisting-plugin-data-state-config-files" class="md-nav__link">
<span class="md-ellipsis">
Persisting Plugin Data (State &amp; Config Files)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#ui-component-types" class="md-nav__link">
<span class="md-ellipsis">
UI Component Types
</span>
</a>
<nav class="md-nav" aria-label="UI Component Types">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#common-display-types" class="md-nav__link">
<span class="md-ellipsis">
Common Display Types
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#quick-reference-key-concepts" class="md-nav__link">
<span class="md-ellipsis">
Quick Reference: Key Concepts
</span>
</a>
<nav class="md-nav" aria-label="Quick Reference: Key Concepts">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#plugin-output-format" class="md-nav__link">
<span class="md-ellipsis">
Plugin Output Format
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#plugin-metadata-configjson" class="md-nav__link">
<span class="md-ellipsis">
Plugin Metadata (config.json)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#reserved-settings" class="md-nav__link">
<span class="md-ellipsis">
Reserved Settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#display-types" class="md-nav__link">
<span class="md-ellipsis">
Display Types
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#tools-references" class="md-nav__link">
<span class="md-ellipsis">
Tools &amp; References
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../PLUGINS_DEV_QUICK_START/" class="md-nav__link">
<span class="md-ellipsis">
Quick start
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PLUGINS_DEV_DATA_CONTRACT/" class="md-nav__link">
<span class="md-ellipsis">
Data contract
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PLUGINS_IMPORT_BEHAVIOR/" class="md-nav__link">
<span class="md-ellipsis">
Import behavior
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PLUGINS_DEV_SETTINGS/" class="md-nav__link">
<span class="md-ellipsis">
Settings system
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PLUGINS_DEV_DATASOURCES/" class="md-nav__link">
<span class="md-ellipsis">
Data sources
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PLUGINS_DEV_UI_COMPONENTS/" class="md-nav__link">
<span class="md-ellipsis">
UI components
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../PLUGINS_DEV_CONFIG/" class="md-nav__link">
<span class="md-ellipsis">
Config lifecycle
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../FRONTEND_DEVELOPMENT/" class="md-nav__link">
<span class="md-ellipsis">
Frontend development
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DATABASE/" class="md-nav__link">
<span class="md-ellipsis">
Database
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../SETTINGS_SYSTEM/" class="md-nav__link">
<span class="md-ellipsis">
Settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../VERSIONS/" class="md-nav__link">
<span class="md-ellipsis">
Versions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../DEVICE_HEURISTICS/" class="md-nav__link">
<span class="md-ellipsis">
Icon and type guessing
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7_2">
<label class="md-nav__link" for="__nav_7_2" id="__nav_7_2_label" tabindex="">
<span class="md-ellipsis">
API
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_7_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_7_2">
<span class="md-nav__icon md-icon"></span>
API
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../API/" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_DEVICES/" class="md-nav__link">
<span class="md-ellipsis">
Devices collection
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_DEVICE/" class="md-nav__link">
<span class="md-ellipsis">
Device
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_DEVICE_FIELD_LOCK/" class="md-nav__link">
<span class="md-ellipsis">
Device field Lock
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_SESSIONS/" class="md-nav__link">
<span class="md-ellipsis">
Sessions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_SETTINGS/" class="md-nav__link">
<span class="md-ellipsis">
Settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_EVENTS/" class="md-nav__link">
<span class="md-ellipsis">
Events
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_MESSAGING_IN_APP/" class="md-nav__link">
<span class="md-ellipsis">
Messaging in-app
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_METRICS/" class="md-nav__link">
<span class="md-ellipsis">
Metrics
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_NETTOOLS/" class="md-nav__link">
<span class="md-ellipsis">
Net tools
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_ONLINEHISTORY/" class="md-nav__link">
<span class="md-ellipsis">
Online history
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_SYNC/" class="md-nav__link">
<span class="md-ellipsis">
Sync
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_LOGS/" class="md-nav__link">
<span class="md-ellipsis">
Logs
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_SSE/" class="md-nav__link">
<span class="md-ellipsis">
SSE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_GRAPHQL/" class="md-nav__link">
<span class="md-ellipsis">
GraphQL
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_DBQUERY/" class="md-nav__link">
<span class="md-ellipsis">
DB query
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_MCP/" class="md-nav__link">
<span class="md-ellipsis">
MCP
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_TESTS/" class="md-nav__link">
<span class="md-ellipsis">
Tests
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../API_OLD/" class="md-nav__link">
<span class="md-ellipsis">
SUPERSEDED OLD API Overview
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7_3">
<label class="md-nav__link" for="__nav_7_3" id="__nav_7_3_label" tabindex="">
<span class="md-ellipsis">
Integrations
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_7_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_7_3">
<span class="md-nav__icon md-icon"></span>
Integrations
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../WEBHOOK_SECRET/" class="md-nav__link">
<span class="md-ellipsis">
Webhook secret
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../HELPER_SCRIPTS/" class="md-nav__link">
<span class="md-ellipsis">
Helper scripts
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix="">
<li class="md-nav__item">
<a href="#quick-links" class="md-nav__link">
<span class="md-ellipsis">
Quick Links
</span>
</a>
<nav class="md-nav" aria-label="Quick Links">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#getting-started" class="md-nav__link">
<span class="md-ellipsis">
🚀 Getting Started
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#core-concepts" class="md-nav__link">
<span class="md-ellipsis">
📚 Core Concepts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#architecture" class="md-nav__link">
<span class="md-ellipsis">
🏗️ Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
🐛 Troubleshooting
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#video-tutorial" class="md-nav__link">
<span class="md-ellipsis">
🎥 Video Tutorial
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#screenshots" class="md-nav__link">
<span class="md-ellipsis">
📸 Screenshots
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#use-cases" class="md-nav__link">
<span class="md-ellipsis">
Use Cases
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#limitations-notes" class="md-nav__link">
<span class="md-ellipsis">
Limitations &amp; Notes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#plugin-development-workflow" class="md-nav__link">
<span class="md-ellipsis">
Plugin Development Workflow
</span>
</a>
<nav class="md-nav" aria-label="Plugin Development Workflow">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#step-1-understand-the-basics" class="md-nav__link">
<span class="md-ellipsis">
Step 1: Understand the Basics
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#step-2-create-your-plugin" class="md-nav__link">
<span class="md-ellipsis">
Step 2: Create Your Plugin
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#step-3-configure-display" class="md-nav__link">
<span class="md-ellipsis">
Step 3: Configure &amp; Display
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#step-4-deploy-test" class="md-nav__link">
<span class="md-ellipsis">
Step 4: Deploy &amp; Test
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#plugin-file-structure" class="md-nav__link">
<span class="md-ellipsis">
Plugin File Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#plugin-manifest-configjson" class="md-nav__link">
<span class="md-ellipsis">
Plugin Manifest (config.json)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#full-reference-below" class="md-nav__link">
<span class="md-ellipsis">
Full Reference (Below)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#data-contract-output-format" class="md-nav__link">
<span class="md-ellipsis">
Data Contract &amp; Output Format
</span>
</a>
<nav class="md-nav" aria-label="Data Contract &amp; Output Format">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#the-9-mandatory-columns" class="md-nav__link">
<span class="md-ellipsis">
The 9 Mandatory Columns
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#configjson-settings-configuration" class="md-nav__link">
<span class="md-ellipsis">
Config.json: Settings &amp; Configuration
</span>
</a>
<nav class="md-nav" aria-label="Config.json: Settings &amp; Configuration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#setting-object-structure" class="md-nav__link">
<span class="md-ellipsis">
Setting Object Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#reserved-function-names" class="md-nav__link">
<span class="md-ellipsis">
Reserved Function Names
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#conventions-checklist" class="md-nav__link">
<span class="md-ellipsis">
Conventions Checklist
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#filters-data-display" class="md-nav__link">
<span class="md-ellipsis">
Filters &amp; Data Display
</span>
</a>
<nav class="md-nav" aria-label="Filters &amp; Data Display">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#filters" class="md-nav__link">
<span class="md-ellipsis">
Filters
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#database-mapping" class="md-nav__link">
<span class="md-ellipsis">
Database Mapping
</span>
</a>
<nav class="md-nav" aria-label="Database Mapping">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#static-value-mapping" class="md-nav__link">
<span class="md-ellipsis">
Static Value Mapping
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#import-behavior-columns-scancreatesdevice-scannotificationmode-scanpresence" class="md-nav__link">
<span class="md-ellipsis">
Import Behavior Columns (scanCreatesDevice, scanNotificationMode, scanPresence)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#persisting-plugin-data-state-config-files" class="md-nav__link">
<span class="md-ellipsis">
Persisting Plugin Data (State &amp; Config Files)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#ui-component-types" class="md-nav__link">
<span class="md-ellipsis">
UI Component Types
</span>
</a>
<nav class="md-nav" aria-label="UI Component Types">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#common-display-types" class="md-nav__link">
<span class="md-ellipsis">
Common Display Types
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#quick-reference-key-concepts" class="md-nav__link">
<span class="md-ellipsis">
Quick Reference: Key Concepts
</span>
</a>
<nav class="md-nav" aria-label="Quick Reference: Key Concepts">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#plugin-output-format" class="md-nav__link">
<span class="md-ellipsis">
Plugin Output Format
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#plugin-metadata-configjson" class="md-nav__link">
<span class="md-ellipsis">
Plugin Metadata (config.json)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#reserved-settings" class="md-nav__link">
<span class="md-ellipsis">
Reserved Settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#display-types" class="md-nav__link">
<span class="md-ellipsis">
Display Types
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#tools-references" class="md-nav__link">
<span class="md-ellipsis">
Tools &amp; References
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<a href="https://github.com/netalertx/NetAlertX/blob/main/docs/PLUGINS_DEV.md" title="Edit this page" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4v-2m10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1 2.1 2.1Z"></path></svg>
</a>
<a href="https://github.com/netalertx/NetAlertX/raw/main/docs/PLUGINS_DEV.md" title="View source of this page" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 18c.56 0 1 .44 1 1s-.44 1-1 1-1-.44-1-1 .44-1 1-1m0-3c-2.73 0-5.06 1.66-6 4 .94 2.34 3.27 4 6 4s5.06-1.66 6-4c-.94-2.34-3.27-4-6-4m0 6.5a2.5 2.5 0 0 1-2.5-2.5 2.5 2.5 0 0 1 2.5-2.5 2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5M9.27 20H6V4h7v5h5v4.07c.7.08 1.36.25 2 .49V8l-6-6H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h4.5a8.15 8.15 0 0 1-1.23-2Z"></path></svg>
</a>
<h1 id="plugin-development-guide">Plugin Development Guide</h1>
<p>This comprehensive guide covers how to build plugins for NetAlertX.</p>
<div class="admonition tip">
<p class="admonition-title">Tip</p>
<p><strong>New to plugin development?</strong> Start with the <a href="../PLUGINS_DEV_QUICK_START/">Quick Start Guide</a> to get a working plugin in 5 minutes.</p>
</div>
<p>NetAlertX comes with a plugin system to feed events from third-party scripts into the UI and then send notifications, if desired. The highlighted core functionality this plugin system supports:</p>
<ul>
<li><strong>Dynamic UI generation</strong> - Automatically create tables for discovered objects</li>
<li><strong>Data filtering</strong> - Filter and link values in the Devices UI</li>
<li><strong>User settings</strong> - Surface plugin configuration in the Settings UI</li>
<li><strong>Rich display types</strong> - Color-coded badges, links, formatted text, and more</li>
<li><strong>Database integration</strong> - Import plugin data into NetAlertX tables like <code>CurrentScan</code> or <code>Devices</code></li>
</ul>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>For a high-level overview of how the <code>config.json</code> is used and its lifecycle, see the <a href="../PLUGINS_DEV_CONFIG/">config.json Lifecycle Guide</a>.</p>
</div>
<h2 id="quick-links">Quick Links</h2>
<h3 id="getting-started">🚀 Getting Started</h3>
<ul>
<li><strong><a href="../PLUGINS_DEV_QUICK_START/">Quick Start Guide</a></strong> - Create a working plugin in 5 minutes</li>
<li><strong><a href="../DEV_ENV_SETUP/">Development Environment Setup</a></strong> - Set up your local development environment</li>
</ul>
<h3 id="core-concepts">📚 Core Concepts</h3>
<ul>
<li><strong><a href="../PLUGINS_DEV_DATA_CONTRACT/">Data Contract</a></strong> - The exact output format plugins must follow (9-13 columns, pipe-delimited)</li>
<li><strong><a href="../PLUGINS_DEV_DATASOURCES/">Data Sources</a></strong> - How plugins retrieve data (scripts, databases, templates)</li>
<li><strong><a href="../PLUGINS_DEV_SETTINGS/">Plugin Settings System</a></strong> - Let users configure your plugin via the UI</li>
<li><strong><a href="../PLUGINS_DEV_UI_COMPONENTS/">UI Components</a></strong> - Display plugin results with color coding, links, and more</li>
</ul>
<h3 id="architecture">🏗️ Architecture</h3>
<ul>
<li><strong><a href="../PLUGINS_DEV_CONFIG/">Plugin Config Lifecycle</a></strong> - How <code>config.json</code> is loaded and used</li>
<li><strong><a href="#full-reference-below">Full Plugin Development Reference</a></strong> - Comprehensive details on all aspects</li>
</ul>
<h3 id="troubleshooting">🐛 Troubleshooting</h3>
<ul>
<li><strong><a href="../DEBUG_PLUGINS/">Debugging Plugins</a></strong> - Troubleshoot plugin issues</li>
<li><strong><a href="https://github.com/netalertx/NetAlertX/tree/main/server/plugins">Plugin Examples</a></strong> - Study existing plugins as reference implementations</li>
</ul>
<h3 id="video-tutorial">🎥 Video Tutorial</h3>
<p><a href="https://youtu.be/cdbxlwiWhv8"><img alt="Watch the video" src="../img/YouTube_thumbnail.png"></a></p>
<h3 id="screenshots">📸 Screenshots</h3>
<table>
<thead>
<tr>
<th><a class="glightbox" data-type="image" data-width="auto" data-height="auto" href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins.png" data-desc-position="bottom"><img alt="Screen 1" src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins.png" title="Screen 1"></a></th>
<th><a class="glightbox" data-type="image" data-width="auto" data-height="auto" href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_settings.png" data-desc-position="bottom"><img alt="Screen 2" src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_settings.png" title="Screen 2"></a></th>
<th><a class="glightbox" data-type="image" data-width="auto" data-height="auto" href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_json_settings.png" data-desc-position="bottom"><img alt="Screen 3" src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_json_settings.png" title="Screen 3"></a></th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="glightbox" data-type="image" data-width="auto" data-height="auto" href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_json_ui.png" data-desc-position="bottom"><img alt="Screen 4" src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_json_ui.png" title="Screen 4"></a></td>
<td><a class="glightbox" data-type="image" data-width="auto" data-height="auto" href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_device_details.png" data-desc-position="bottom"><img alt="Screen 5" src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_device_details.png" title="Screen 5"></a></td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="use-cases">Use Cases</h2>
<p>Plugins are infinitely flexible. Here are some examples:</p>
<ul>
<li><strong>Device Discovery</strong> - Scan networks using ARP, mDNS, DHCP leases, or custom protocols</li>
<li><strong>Service Monitoring</strong> - Monitor web services, APIs, or network services for availability</li>
<li><strong>Integration</strong> - Import devices from PiHole, Home Assistant, Unifi, or other systems</li>
<li><strong>Enrichment</strong> - Add data like geolocation, threat intelligence, or asset metadata</li>
<li><strong>Alerting</strong> - Send notifications to Slack, Discord, Telegram, email, or webhooks</li>
<li><strong>Reporting</strong> - Generate insights from existing NetAlertX database (open ports, recent changes, etc.)</li>
<li><strong>Custom Logic</strong> - Create fake devices, trigger automations, or implement custom heuristics</li>
</ul>
<p>If you can imagine it and script it, you can build a plugin.</p>
<h2 id="limitations-notes">Limitations &amp; Notes</h2>
<ul>
<li>Plugin data is deduplicated hourly (same Primary ID + Secondary ID + User Data = duplicate removed)</li>
<li>Currently, only <code>CurrentScan</code> table supports update/overwrite of existing objects</li>
<li>Plugin results must follow the strict <a href="../PLUGINS_DEV_DATA_CONTRACT/">Data Contract</a></li>
<li>Plugins run with the same permissions as the NetAlertX process</li>
<li>External dependencies must be installed in the container</li>
</ul>
<h2 id="plugin-development-workflow">Plugin Development Workflow</h2>
<h3 id="step-1-understand-the-basics">Step 1: Understand the Basics</h3>
<ol>
<li>Read <a href="../PLUGINS_DEV_QUICK_START/">Quick Start Guide</a> - 5 minute overview</li>
<li>Study the <a href="../PLUGINS_DEV_DATA_CONTRACT/">Data Contract</a> - Understand the output format</li>
<li>Choose a <a href="../PLUGINS_DEV_DATASOURCES/">Data Source</a> - Where does your data come from?</li>
</ol>
<h3 id="step-2-create-your-plugin">Step 2: Create Your Plugin</h3>
<ol>
<li>Copy the <code>__template</code> plugin folder (see below for structure)</li>
<li>Update <code>config.json</code> with your plugin metadata</li>
<li>Implement <code>script.py</code> (or configure alternative data source)</li>
<li>Test locally in the devcontainer</li>
</ol>
<h3 id="step-3-configure-display">Step 3: Configure &amp; Display</h3>
<ol>
<li>Define <a href="../PLUGINS_DEV_SETTINGS/">Settings</a> for user configuration</li>
<li>Design <a href="../PLUGINS_DEV_UI_COMPONENTS/">UI Components</a> for result display</li>
<li>Map to database tables if needed (for notifications, etc.)</li>
</ol>
<h3 id="step-4-deploy-test">Step 4: Deploy &amp; Test</h3>
<ol>
<li>Restart the backend</li>
<li>Test via Settings → Plugin Settings</li>
<li>Verify results in UI and logs</li>
<li>Check <code>/tmp/log/plugins/last_result.&lt;PREFIX&gt;.log</code></li>
</ol>
<p>See <a href="../PLUGINS_DEV_QUICK_START/">Quick Start Guide</a> for detailed step-by-step instructions.</p>
<h2 id="plugin-file-structure">Plugin File Structure</h2>
<p>Every plugin lives in its own folder under <code>/app/server/plugins/</code>.</p>
<blockquote>
<p><strong>Important:</strong> Folder name must match the <code>"code_name"</code> value in <code>config.json</code></p>
</blockquote>
<div class="highlight"><pre><span></span><code>/app/server/plugins/
├── __template/ # Copy this as a starting point
│ ├── config.json # Plugin manifest (configuration)
│ ├── script.py # Your plugin logic (optional, depends on data_source)
│ └── README.md # Setup and usage documentation
├── my_plugin/ # Your new plugin
│ ├── config.json # REQUIRED - Plugin manifest
│ ├── script.py # OPTIONAL - Python script (if using script data source)
│ ├── README.md # REQUIRED - Documentation for users
│ └── other_files... # Your supporting files
</code></pre></div>
<h2 id="plugin-manifest-configjson">Plugin Manifest (config.json)</h2>
<p>The <code>config.json</code> file is the <strong>plugin manifest</strong> - it tells NetAlertX everything about your plugin:</p>
<ul>
<li><strong>Metadata:</strong> Plugin name, description, icon</li>
<li><strong>Execution:</strong> When to run, what command to run, timeout</li>
<li><strong>Settings:</strong> User-configurable options</li>
<li><strong>Data contract:</strong> Column definitions and how to display results</li>
<li><strong>Integration:</strong> Database mappings, notifications, filters</li>
</ul>
<p><strong>Example minimal config.json:</strong></p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"code_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my_plugin"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"unique_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MYPLN"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"display_name"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"language_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en_us"</span><span class="p">,</span><span class="w"> </span><span class="nt">"string"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Plugin"</span><span class="p">}],</span>
<span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"language_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en_us"</span><span class="p">,</span><span class="w"> </span><span class="nt">"string"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My awesome plugin"</span><span class="p">}],</span>
<span class="w"> </span><span class="nt">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fa-plug"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"data_source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"script"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"execution_order"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Layer_0"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"settings"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"function"</span><span class="p">:</span><span class="w"> </span><span class="s2">"RUN"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"type"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nt">"dataType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w"> </span><span class="nt">"elements"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"elementType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"select"</span><span class="p">,</span><span class="w"> </span><span class="nt">"elementOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> </span><span class="nt">"transformers"</span><span class="p">:</span><span class="w"> </span><span class="p">[]}]},</span>
<span class="w"> </span><span class="nt">"default_value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"disabled"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"options"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"disabled"</span><span class="p">,</span><span class="w"> </span><span class="s2">"once"</span><span class="p">,</span><span class="w"> </span><span class="s2">"schedule"</span><span class="p">],</span>
<span class="w"> </span><span class="nt">"localized"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"name"</span><span class="p">],</span>
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"language_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en_us"</span><span class="p">,</span><span class="w"> </span><span class="nt">"string"</span><span class="p">:</span><span class="w"> </span><span class="s2">"When to run"</span><span class="p">}]</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"function"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CMD"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"type"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nt">"dataType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w"> </span><span class="nt">"elements"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"elementType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"input"</span><span class="p">,</span><span class="w"> </span><span class="nt">"elementOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> </span><span class="nt">"transformers"</span><span class="p">:</span><span class="w"> </span><span class="p">[]}]},</span>
<span class="w"> </span><span class="nt">"default_value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"python3 /app/server/plugins/my_plugin/script.py"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"localized"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"name"</span><span class="p">],</span>
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"language_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en_us"</span><span class="p">,</span><span class="w"> </span><span class="nt">"string"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Command"</span><span class="p">}]</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">],</span>
<span class="w"> </span><span class="nt">"database_column_definitions"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span>
<span class="p">}</span>
</code></pre></div>
<blockquote>
<p>For comprehensive <code>config.json</code> documentation, see <a href="../PLUGINS_DEV_CONFIG/">PLUGINS_DEV_CONFIG.md</a></p>
</blockquote>
<h2 id="full-reference-below">Full Reference (Below)</h2>
<p>The sections below provide complete reference documentation for all plugin development topics. Use the quick links above to jump to specific sections, or read sequentially for a deep dive.</p>
<p>More on specifics below.</p>
<hr>
<h2 id="data-contract-output-format">Data Contract &amp; Output Format</h2>
<p>For detailed information on plugin output format, see <strong><a href="../PLUGINS_DEV_DATA_CONTRACT/">PLUGINS_DEV_DATA_CONTRACT.md</a></strong>.</p>
<p>Quick reference:
- <strong>Format:</strong> Pipe-delimited (<code>|</code>) text file
- <strong>Location:</strong> <code>/tmp/log/plugins/last_result.&lt;PREFIX&gt;.log</code>
- <strong>Columns:</strong> 9 required + 4 optional = 13 maximum
- <strong>Helper:</strong> Use <code>plugin_helper.py</code> for easy formatting</p>
<h3 id="the-9-mandatory-columns">The 9 Mandatory Columns</h3>
<table>
<thead>
<tr>
<th>Column</th>
<th>Name</th>
<th>Required</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>objectPrimaryId</td>
<td><strong>YES</strong></td>
<td><code>"device_name"</code> or <code>"192.168.1.1"</code></td>
</tr>
<tr>
<td>1</td>
<td>objectSecondaryId</td>
<td>no</td>
<td><code>"secondary_id"</code> or <code>null</code></td>
</tr>
<tr>
<td>2</td>
<td>DateTime</td>
<td><strong>YES</strong></td>
<td><code>"2023-01-02 15:56:30"</code></td>
</tr>
<tr>
<td>3</td>
<td>watchedValue1</td>
<td><strong>YES</strong></td>
<td><code>"online"</code> or <code>"200"</code></td>
</tr>
<tr>
<td>4</td>
<td>watchedValue2</td>
<td>no</td>
<td><code>"ip_address"</code> or <code>null</code></td>
</tr>
<tr>
<td>5</td>
<td>watchedValue3</td>
<td>no</td>
<td><code>null</code></td>
</tr>
<tr>
<td>6</td>
<td>watchedValue4</td>
<td>no</td>
<td><code>null</code></td>
</tr>
<tr>
<td>7</td>
<td>Extra</td>
<td>no</td>
<td><code>"additional data"</code> or <code>null</code></td>
</tr>
<tr>
<td>8</td>
<td>ForeignKey</td>
<td>no</td>
<td><code>"aa:bb:cc:dd:ee:ff"</code> or <code>null</code></td>
</tr>
</tbody>
</table>
<p>See <a href="../PLUGINS_DEV_DATA_CONTRACT/">Data Contract</a> for examples, validation, and debugging tips.</p>
<hr>
<h2 id="configjson-settings-configuration">Config.json: Settings &amp; Configuration</h2>
<p>For detailed settings documentation, see <strong><a href="../PLUGINS_DEV_SETTINGS/">PLUGINS_DEV_SETTINGS.md</a></strong> and <strong><a href="../PLUGINS_DEV_DATASOURCES/">PLUGINS_DEV_DATASOURCES.md</a></strong>.</p>
<h3 id="setting-object-structure">Setting Object Structure</h3>
<p>Every setting in your plugin has this structure:</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"function"</span><span class="p">:</span><span class="w"> </span><span class="s2">"UNIQUE_CODE"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"type"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nt">"dataType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w"> </span><span class="nt">"elements"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="err">...</span><span class="p">]},</span>
<span class="w"> </span><span class="nt">"default_value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"options"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="err">...</span><span class="p">],</span>
<span class="w"> </span><span class="nt">"localized"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"name"</span><span class="p">,</span><span class="w"> </span><span class="s2">"description"</span><span class="p">],</span>
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"language_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en_us"</span><span class="p">,</span><span class="w"> </span><span class="nt">"string"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Display Name"</span><span class="p">}],</span>
<span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"language_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en_us"</span><span class="p">,</span><span class="w"> </span><span class="nt">"string"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Help text"</span><span class="p">}]</span>
<span class="p">}</span>
</code></pre></div>
<h3 id="reserved-function-names">Reserved Function Names</h3>
<p>These control core plugin behavior:</p>
<table>
<thead>
<tr>
<th>Function</th>
<th>Purpose</th>
<th>Required</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>RUN</code></td>
<td>When to execute</td>
<td><strong>YES</strong></td>
<td><code>disabled</code>, <code>once</code>, <code>schedule</code>, <code>always_after_scan</code>, <code>before_name_updates</code>, <code>on_new_device</code></td>
</tr>
<tr>
<td><code>RUN_SCHD</code></td>
<td>Cron schedule</td>
<td>If <code>RUN=schedule</code></td>
<td>Cron format: <code>"0 * * * *"</code></td>
</tr>
<tr>
<td><code>CMD</code></td>
<td>Command to run</td>
<td><strong>YES</strong></td>
<td>Shell command or script path</td>
</tr>
<tr>
<td><code>RUN_TIMEOUT</code></td>
<td>Max execution time</td>
<td>optional</td>
<td>Seconds: <code>"60"</code></td>
</tr>
<tr>
<td><code>WATCH</code></td>
<td>Monitor for changes</td>
<td>optional</td>
<td>Column names</td>
</tr>
<tr>
<td><code>REPORT_ON</code></td>
<td>When to notify</td>
<td>optional</td>
<td><code>new</code>, <code>watched-changed</code>, <code>watched-not-changed</code>, <code>missing-in-last-scan</code></td>
</tr>
<tr>
<td><code>DB_PATH</code></td>
<td>External DB path</td>
<td>If using SQLite</td>
<td><code>/path/to/db.db</code></td>
</tr>
<tr>
<td><code>IMPORT_ON</code></td>
<td>Gate whether this run's rows are promoted into <code>CurrentScan</code></td>
<td>optional</td>
<td>Boolean. Only affects plugins with <code>mapped_to_table: "CurrentScan"</code> — see <a href="#database-mapping">Database Mapping</a> below.</td>
</tr>
</tbody>
</table>
<p>See <a href="../PLUGINS_DEV_SETTINGS/">PLUGINS_DEV_SETTINGS.md</a> for full component types and examples.</p>
<h3 id="conventions-checklist">Conventions Checklist</h3>
<p>Check your plugin against these repo-wide conventions before opening a PR (verified against <code>server/plugins/*/config.json</code>):</p>
<ul>
<li><strong><code>RUN</code> defaults to <code>"disabled"</code>.</strong> True for the large majority of plugins; only core maintenance plugins (<code>csv_backup</code>, <code>db_cleanup</code>, <code>maintenance</code>, <code>vendor_update</code>) default to <code>schedule</code>. A new optional plugin should load disabled until the user configures it.</li>
<li><strong>Pick <code>RUN_SCHD</code> from precedent, not an arbitrary value.</strong> Check the closest existing plugin for its schedule (e.g. <code>pihole_api_scan</code> uses <code>*/5 * * * *</code>) rather than inventing a new cadence — consistency keeps first-time setup predictable across plugins.</li>
<li><strong><code>RUN_TIMEOUT</code> is a subprocess kill-timer, not a per-request budget.</strong> The core plugin runner (<code>server/plugin.py</code>) passes this same value as the hard timeout for the <em>entire</em> script (<code>subprocess</code> <code>timeout=</code>). If your script makes multiple sequential network calls (e.g. two upstream instances, or a per-device lookup in a loop), don't also reuse <code>RUN_TIMEOUT</code> as each individual call's timeout — one slow call can then consume the whole budget and get the process killed before it writes its result file, silently dropping the entire run. Two correct alternatives, depending on the shape of your loop:</li>
<li><strong>Looping over a config-declared, known-length list</strong> (e.g. a subnets or IPs setting) — mark that <code>params</code> entry with <code>"timeoutMultiplier": true</code> in <code>config.json</code>. The framework then multiplies the <em>outer</em> kill-timeout by that list's length before running your script, so each iteration can safely use the full <code>RUN_TIMEOUT</code> internally. See <code>arp_scan/config.json</code>'s <code>subnets</code> param for a working example.</li>
<li><strong>Looping over a runtime-variable-length collection</strong> (e.g. a notification queue, where length isn't known until the script runs) — <code>timeoutMultiplier</code> doesn't apply here since there's no config-declared count. Instead, divide the <em>inner</em> per-call timeout down using <code>plugin_helper.per_item_timeout(run_timeout, item_count)</code>, so N sequential calls can't collectively exceed the outer budget. See <code>server/plugins/_publisher_ntfy/ntfy.py</code>'s notification loop for a working example.</li>
<li><strong>Reuse existing core settings instead of duplicating them.</strong> If NetAlertX already has a concept your plugin needs (e.g. <code>API_TOKEN</code> for its own GraphQL/API endpoint), read it with <code>get_setting_value("API_TOKEN")</code> rather than adding a plugin-specific <code>&lt;PREFIX&gt;_API_TOKEN</code> — see <code>server/plugins/sync/sync.py</code> for the pattern.</li>
<li><strong>Keep <code>description</code> strings short.</strong> They render directly in the Settings UI. Put implementation rationale and design trade-offs in the plugin's README or code comments, not the UI-facing description.</li>
<li><strong>For "one or more instances of the same thing," use the nested array + popup-form settings pattern</strong>, not a fixed hardcoded count (e.g. "primary"/"secondary"). See <code>rest_import</code> (<code>RSTIMPRT</code>)'s <code>imports</code> setting for a working example — it also gives each instance its own sub-settings (URL, credentials, per-instance flags) for free.</li>
<li><strong>Persist plugin state under <code>dbFolderPath</code>, config artifacts under <code>configPath</code></strong> — see <a href="#persisting-plugin-data-state--config-files">Persisting Plugin Data</a> below.</li>
<li><strong>A setting's <code>dataType</code> and <code>default_value</code> must actually agree.</strong> <code>dataType: "array"</code> (or <code>"object"</code>) means <code>default_value</code> must be a real JSON literal for that shape — <code>'["default"]'</code>, not the bare string <code>"default"</code>. <code>setting_value_to_python_type()</code> (<code>server/helper.py</code>) <code>json.loads()</code>s the default at runtime; a bare string fails that parse, silently logs a decode error, and returns <code>[]</code> instead of your intended default — this shipped for real in <code>devParentRelType</code>/<code>UI_theme</code>/<code>UI_TOPOLOGY_ORDER</code> before being caught. If <code>elementOptions</code> already sets <code>multiple</code>/<code>orderable: "false"</code>, that's a strong signal the setting is actually scalar and <code>dataType</code> should be <code>"string"</code>, not <code>"array"</code>, regardless of what UI widget (<code>select</code>, etc.) renders it.</li>
</ul>
<hr>
<h2 id="filters-data-display">Filters &amp; Data Display</h2>
<p>For comprehensive display configuration, see <strong><a href="../PLUGINS_DEV_UI_COMPONENTS/">PLUGINS_DEV_UI_COMPONENTS.md</a></strong>.</p>
<h3 id="filters">Filters</h3>
<p>Control which rows display in the UI:</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"data_filters"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"compare_column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"objectPrimaryId"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"compare_operator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"=="</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"compare_field_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"txtMacFilter"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"compare_js_template"</span><span class="p">:</span><span class="w"> </span><span class="s2">"'{value}'.toString()"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"compare_use_quotes"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">]</span>
<span class="p">}</span>
</code></pre></div>
<p>See <a href="../PLUGINS_DEV_UI_COMPONENTS/#filters">UI Components: Filters</a> for full documentation.</p>
<hr>
<h2 id="database-mapping">Database Mapping</h2>
<p>To import plugin data into NetAlertX tables for device discovery or notifications:</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"mapped_to_table"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CurrentScan"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"database_column_definitions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"objectPrimaryId"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"mapped_to_column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"scanMac"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"show"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"device_mac"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"localized"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"name"</span><span class="p">],</span>
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="nt">"language_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en_us"</span><span class="p">,</span><span class="w"> </span><span class="nt">"string"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MAC Address"</span><span class="p">}]</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">]</span>
<span class="p">}</span>
</code></pre></div>
<p>See <a href="../PLUGINS_DEV_UI_COMPONENTS/#mapping-to-database-tables">UI Components: Database Mapping</a> for full documentation.</p>
<h3 id="static-value-mapping">Static Value Mapping</h3>
<p>To always map a static value (not read from plugin output):</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NameDoesntMatter"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"mapped_to_column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"scanSourcePlugin"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"mapped_to_column_data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MYPLN"</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<h3 id="import-behavior-columns-scancreatesdevice-scannotificationmode-scanpresence">Import Behavior Columns (<code>scanCreatesDevice</code>, <code>scanNotificationMode</code>, <code>scanPresence</code>)</h3>
<p>Three optional columns on <code>CurrentScan</code> control what happens once a row reaches it — see <a href="../PLUGINS_IMPORT_BEHAVIOR/">Plugin Import Behavior</a> for the full contract (allowed values, defaults, downstream effects). All three default to today's behavior if never mapped, so existing plugins need no changes.</p>
<p>Most plugins map a single static value for the whole import via <code>mapped_to_column_data</code> — e.g. an enrichment-only plugin that should never originate a new device:</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NameDoesntMatter"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"mapped_to_column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"scanCreatesDevice"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"mapped_to_column_data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>A plugin sophisticated enough to know per-row whether an entry is a live/active sighting (e.g. a DHCP lease with a <code>state</code> field) can instead map a per-row value via the normal <code>mapped_to_column</code> mechanism, the same way any other data-carrying column is mapped:</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"watchedValue1"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"mapped_to_column"</span><span class="p">:</span><span class="w"> </span><span class="s2">"scanPresence"</span>
<span class="p">}</span>
</code></pre></div>
<hr>
<h2 id="persisting-plugin-data-state-config-files">Persisting Plugin Data (State &amp; Config Files)</h2>
<p>Plugin settings (<code>config.json</code>) are already persisted for you. If your plugin also needs to write its <strong>own files</strong> to disk between runs — a cache, a "what did I already do" tracker, an exported artifact — pick the right base path from <code>const.py</code> rather than hardcoding one:</p>
<table>
<thead>
<tr>
<th>Purpose</th>
<th>Import from <code>const</code></th>
<th>Default path</th>
<th>Use for</th>
</tr>
</thead>
<tbody>
<tr>
<td>Internal state</td>
<td><code>dbFolderPath</code></td>
<td><code>/data/db</code></td>
<td>Bookkeeping the user never edits directly: sync state, dedupe caches, "managed items" trackers, etc.</td>
</tr>
<tr>
<td>Config artifacts</td>
<td><code>configPath</code></td>
<td><code>/data/config</code></td>
<td>Files that are conceptually configuration: exports meant to be reviewed/edited by the user, generated config snippets, backups.</td>
</tr>
</tbody>
</table>
<div class="highlight"><pre><span></span><code><span class="kn">from</span><span class="w"> </span><span class="nn">const</span><span class="w"> </span><span class="kn">import</span> <span class="n">dbFolderPath</span><span class="p">,</span> <span class="n">configPath</span>
<span class="n">STATE_FILE</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">dbFolderPath</span><span class="p">,</span> <span class="sa">f</span><span class="s2">"state.</span><span class="si">{</span><span class="n">pluginName</span><span class="si">}</span><span class="s2">.json"</span><span class="p">)</span>
</code></pre></div>
<p><strong>Why it matters:</strong> <code>/data/db</code> and <code>/data/config</code> are separate mount points. Users can point <code>/data/db</code> at fast/ephemeral storage (its contents are usually rebuildable) and <code>/data/config</code> at durable, backed-up storage — or the reverse, depending on their setup. Don't hardcode <code>/app/db</code>, <code>/app/config</code>, or write loose files directly under the bare data root (<code>dataPath</code>); those bypass this separation, and <code>/app/...</code> paths are the pre-<code>v25.10.1</code> legacy layout (see <a href="../MIGRATION/">MIGRATION.md</a>).</p>
<p>If you rename or move where a plugin stores its state file across a release, migrate the old file on startup instead of silently dropping user state — see <code>server/plugins/adguard_export/script.py</code> for a worked example.</p>
<hr>
<h2 id="ui-component-types">UI Component Types</h2>
<p>Plugin results are displayed in the web interface using various component types. See <strong><a href="../PLUGINS_DEV_UI_COMPONENTS/">PLUGINS_DEV_UI_COMPONENTS.md</a></strong> for complete documentation.</p>
<h3 id="common-display-types">Common Display Types</h3>
<p>See <a href="../PLUGINS_DEV_SETTINGS/">PLUGINS_DEV_SETTINGS.md</a> for complete settings documentation, and <a href="../PLUGINS_DEV_DATASOURCES/">PLUGINS_DEV_DATASOURCES.md</a> for data source details.</p>
<h2 id="quick-reference-key-concepts">Quick Reference: Key Concepts</h2>
<h3 id="plugin-output-format">Plugin Output Format</h3>
<p></p><div class="highlight"><pre><span></span><code>objectPrimaryId|objectSecondaryId|DateTime|watchedValue1|watchedValue2|watchedValue3|watchedValue4|Extra|ForeignKey
</code></pre></div>
9 required columns, 4 optional helpers = 13 max<p></p>
<p>See: <a href="../PLUGINS_DEV_DATA_CONTRACT/">Data Contract</a></p>
<h3 id="plugin-metadata-configjson">Plugin Metadata (config.json)</h3>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"code_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my_plugin"</span><span class="p">,</span><span class="w"> </span><span class="c1">// Folder name</span>
<span class="w"> </span><span class="nt">"unique_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MYPLN"</span><span class="p">,</span><span class="w"> </span><span class="c1">// Settings prefix</span>
<span class="w"> </span><span class="nt">"display_name"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="err">...</span><span class="p">],</span><span class="w"> </span><span class="c1">// UI label</span>
<span class="w"> </span><span class="nt">"data_source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"script"</span><span class="p">,</span><span class="w"> </span><span class="c1">// Where data comes from</span>
<span class="w"> </span><span class="nt">"settings"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="err">...</span><span class="p">],</span><span class="w"> </span><span class="c1">// User configurable</span>
<span class="w"> </span><span class="nt">"database_column_definitions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="err">...</span><span class="p">]</span><span class="w"> </span><span class="c1">// How to display</span>
<span class="p">}</span>
</code></pre></div>
<p>See: <a href="./">Full Guide</a>, <a href="../PLUGINS_DEV_SETTINGS/">Settings</a></p>
<h3 id="reserved-settings">Reserved Settings</h3>
<ul>
<li><code>RUN</code> - When to execute (disabled, once, schedule, always_after_scan, etc.)</li>
<li><code>RUN_SCHD</code> - Cron schedule</li>
<li><code>CMD</code> - Command/script to execute</li>
<li><code>RUN_TIMEOUT</code> - Max execution time</li>
<li><code>WATCH</code> - Monitor for changes</li>
<li><code>REPORT_ON</code> - Notification trigger</li>
</ul>
<p>See: <a href="../PLUGINS_DEV_SETTINGS/">Settings System</a></p>
<h3 id="display-types">Display Types</h3>
<p><code>label</code>, <code>device_mac</code>, <code>device_ip</code>, <code>url</code>, <code>threshold</code>, <code>replace</code>, <code>regex</code>, <code>textbox_save</code>, and more.</p>
<p>See: <a href="../PLUGINS_DEV_UI_COMPONENTS/">UI Components</a></p>
<hr>
<h2 id="tools-references">Tools &amp; References</h2>
<ul>
<li><strong>Template Plugin:</strong> <code>/app/server/plugins/__template/</code> - Start here!</li>
<li><strong>Helper Library:</strong> <code>/app/server/plugins/plugin_helper.py</code> - Use for output formatting</li>
<li><strong>Settings Helper:</strong> <code>/app/server/helper.py</code> - Use <code>get_setting_value()</code> in scripts</li>
<li><strong>Example Plugins:</strong> <code>/app/server/plugins/*/</code> - Study working implementations</li>
<li><strong>Logs:</strong> <code>/tmp/log/plugins/</code> - Plugin output and execution logs</li>
<li><strong>Backend Logs:</strong> <code>/tmp/log/app.log</code> - Core system logs</li>
<li><strong>Persistent state:</strong> <code>dbFolderPath</code> (<code>/data/db</code>) via <code>from const import dbFolderPath</code> - see <a href="#persisting-plugin-data-state--config-files">Persisting Plugin Data</a></li>
<li><strong>Config artifacts:</strong> <code>configPath</code> (<code>/data/config</code>) via <code>from const import configPath</code> - see <a href="#persisting-plugin-data-state--config-files">Persisting Plugin Data</a></li>
</ul>
<hr>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12Z"></path></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer">
<a href="../DEV_PORTS_HOST_MODE/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Devcontainer ports">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"></path></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
Devcontainer ports
</div>
</div>
</a>
<a href="../PLUGINS_DEV_QUICK_START/" class="md-footer__link md-footer__link--next" aria-label="Next: Quick start">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Quick start
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4Z"></path></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "..", "features": ["announce.dismiss", "content.action.edit", "content.action.view", "content.code.annotate", "content.code.copy", "content.tooltips", "navigation.footer", "navigation.indexes", "navigation.sections", "navigation.tabs", "navigation.top", "navigation.tracking", "search.highlight", "search.share", "search.suggest", "toc.follow"], "search": "../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
<script src="../assets/javascripts/bundle.a7c05c9e.min.js"></script>
<script id="init-glightbox">const lightbox = GLightbox({"touchNavigation": true, "loop": false, "zoomable": true, "draggable": true, "openEffect": "zoom", "closeEffect": "zoom", "slideEffect": "slide"});
document$.subscribe(()=>{ lightbox.reload(); });
</script></body></html>