PLG: add PIHOLEAPI_CONSIDER_ONLINE #1436

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2026-01-26 10:35:35 +11:00
parent f0960d2b84
commit c3d3826448
2 changed files with 74 additions and 29 deletions

View File

@@ -263,6 +263,34 @@
}
]
},
{
"function": "CONSIDER_ONLINE",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 300,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Online if last seen"
}
],
"description": [
{
"language_code": "en_us",
"string": "Time in seconds to consider a device online."
}
]
},
{
"function": "VERIFY_SSL",
"type": {

View File

@@ -196,57 +196,69 @@ def get_pihole_network_devices():
# ------------------------------------------------------------------
def gather_device_entries():
"""
Build a list of device entries suitable for Plugin_Objects.add_object.
Each entry is a dict with: mac, ip, name, macVendor, lastQuery
Build a list of device entries.
Online status is determined by comparing lastSeen (in seconds) vs the current time.
"""
entries = []
iface_map = get_pihole_interface_data() # mac -> [ips]
devices = get_pihole_network_devices()
now_ts = int(datetime.datetime.now().timestamp())
for device in devices:
hwaddr = device.get('hwaddr')
if not hwaddr or hwaddr == "00:00:00:00:00:00":
# Filter out invalid MACs/interfaces
if not hwaddr or hwaddr in ["00:00:00:00:00:00", "ip-::"]:
continue
macVendor = device.get('macVendor', '')
lastQuery = device.get('lastQuery')
# collect all IPs for this device
device_ips = device.get('ips', [])
if not device_ips:
continue
# 1. Find the freshest timestamp across all IPs for this MAC
# This ensures if the device is active on ANY IP, the MAC is considered online.
max_last_seen = 0
for ip_info in device_ips:
ls = ip_info.get('lastSeen', 0)
if ls > max_last_seen:
max_last_seen = ls
# 2. Determine online status: (Current Time - Last Seen) <= PIHOLEAPI_CONSIDER_ONLINE
# Math is in seconds.
if (now_ts - max_last_seen) <= PIHOLEAPI_CONSIDER_ONLINE:
is_online = True
else:
is_online = False
# 3. Skip if offline (and user doesn't want offline devices)
if not is_online and not PIHOLEAPI_GET_OFFLINE:
mylog('verbose', [f'[{pluginName}] Not online in the last {PIHOLEAPI_CONSIDER_ONLINE}s, import of offline disabled (PIHOLEAPI_GET_OFFLINE) skipping device: {device}.'])
continue
mac_vendor = device.get('macVendor', '')
# 4. Process each valid IP for the device
for ip_info in device_ips:
ip = ip_info.get('ip')
if not ip:
# Skip internal Pi-hole placeholders
if not ip or ip in ["0.0.0.0", "::"]:
mylog('verbose', [f'[{pluginName}] Not a valid ip ({ip}), skipping device: {device}.'])
continue
name = ip_info.get('name') or '(unknown)'
tmp_mac = hwaddr.lower()
# Determine if this device is "online"
online = any(ip in iplist for iplist in iface_map.values())
# Skip offline devices unless PIHOLEAPI_GET_OFFLINE=True
if not online and not PIHOLEAPI_GET_OFFLINE:
continue
tmpMac = hwaddr.lower()
# ensure fake mac if enabled
if PIHOLEAPI_FAKE_MAC and not is_mac(tmpMac):
tmpMac = string_to_fake_mac(ip)
# mark lastQuery as now if online, else keep original
last_query_val = str(now_ts) if online else str(lastQuery) if lastQuery else ''
# Handle Fake MAC logic for non-standard hardware addresses
if PIHOLEAPI_FAKE_MAC and not is_mac(tmp_mac):
tmp_mac = string_to_fake_mac(ip)
entries.append({
'mac': tmpMac,
'mac': tmp_mac,
'ip': ip,
'name': name,
'macVendor': macVendor,
'lastQuery': last_query_val
'macVendor': mac_vendor,
# Pass the Unix timestamp as a string for NAX tracking
'lastQuery': str(max_last_seen) if max_last_seen > 0 else ""
})
return entries
@@ -255,7 +267,7 @@ def gather_device_entries():
# ------------------------------------------------------------------
def main():
"""Main plugin entrypoint."""
global PIHOLEAPI_URL, PIHOLEAPI_PASSWORD, PIHOLEAPI_API_MAXCLIENTS, PIHOLEAPI_VERIFY_SSL, PIHOLEAPI_RUN_TIMEOUT, PIHOLEAPI_GET_OFFLINE
global PIHOLEAPI_URL, PIHOLEAPI_PASSWORD, PIHOLEAPI_API_MAXCLIENTS, PIHOLEAPI_VERIFY_SSL, PIHOLEAPI_RUN_TIMEOUT, PIHOLEAPI_GET_OFFLINE, PIHOLEAPI_CONSIDER_ONLINE
mylog('verbose', [f'[{pluginName}] start script.'])
@@ -272,6 +284,11 @@ def main():
PIHOLEAPI_VERIFY_SSL = get_setting_value('PIHOLEAPI_SSL_VERIFY')
PIHOLEAPI_RUN_TIMEOUT = get_setting_value('PIHOLEAPI_RUN_TIMEOUT')
PIHOLEAPI_GET_OFFLINE = get_setting_value('PIHOLEAPI_GET_OFFLINE')
PIHOLEAPI_CONSIDER_ONLINE = get_setting_value('PIHOLEAPI_CONSIDER_ONLINE')
# Fallback in case the setting is missing or returned as an empty string
if not isinstance(PIHOLEAPI_CONSIDER_ONLINE, int):
PIHOLEAPI_CONSIDER_ONLINE = 300
# Authenticate
if not pihole_api_auth():