Sure, you also need to setup folder watcher to watch ipbans.yaml and add the automation at the end
custom_components/unban_local/init.py
import logging
import os
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
PLATFORMS = []
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {})
ip_string = entry.data.get("banned_ips", "")
ips_to_unban = [ip.strip() for ip in ip_string.split(',') if ip.strip()]
hass.data[DOMAIN]["ips_to_unban"] = ips_to_unban
_LOGGER.info("Monitoring the following IPs for unbanning: %s", ips_to_unban)
ip_bans_file = hass.config.path("ip_bans.yaml")
async def handle_clear_local_bans(call: ServiceCall) -> None:
ips_to_check = hass.data[DOMAIN].get("ips_to_unban", [])
if not ips_to_check:
_LOGGER.warning("No IPs configured for unbanning.")
return
_LOGGER.info("Running local IP unban check...")
if not os.path.exists(ip_bans_file):
_LOGGER.debug("ip_bans.yaml does not exist. No action needed.")
return
try:
with open(ip_bans_file, "r", encoding="utf-8") as f:
lines = f.readlines()
unbanned_lines = []
ip_was_removed = False
skip_next_line = False
for i, line in enumerate(lines):
if skip_next_line:
skip_next_line = False
continue
line_stripped = line.strip()
triggered = False
for ip in ips_to_check:
ip_to_find = f"{ip}:"
if line_stripped.startswith(ip_to_find):
ip_was_removed = True
triggered = True
if (i + 1 < len(lines)) and "banned_at:" in lines[i+1]:
skip_next_line = True
break
if not triggered:
unbanned_lines.append(line)
if ip_was_removed:
_LOGGER.info("Found and removed a local IP ban. Updating ip_bans.yaml.")
with open(ip_bans_file, "w", encoding="utf-8") as f:
f.writelines(unbanned_lines)
else:
_LOGGER.info("No configured IPs were found in the ban list.")
except Exception as e:
_LOGGER.error("Error while processing ip_bans.yaml: %s", e)
hass.services.async_register(DOMAIN, "clear_bans", handle_clear_local_bans)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.services.async_remove(DOMAIN, "clear_bans")
if DOMAIN in hass.data:
hass.data.pop(DOMAIN)
_LOGGER.info("Successfully unloaded Local IP Unbanner.")
return True
custom_components/unban_local/config_flow.py
import logging
import socket
import voluptuous as vol
from homeassistant import config_entries
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
def _get_local_ips():
ips = {"127.0.0.1"}
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
s.connect(("8.8.8.8", 80))
lan_ip = s.getsockname()[0]
if lan_ip:
ips.add(lan_ip)
except Exception as e:
_LOGGER.warning("Could not auto-detect LAN IP address, please enter manually: %s", e)
finally:
if s:
s.close()
return ips
class UnbanLocalConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
async def async_step_user(self, user_input=None):
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
if user_input is not None:
return self.async_create_entry(title="Local IP Unbanner", data=user_input)
try:
detected_ips = await self.hass.async_add_executor_job(_get_local_ips)
suggested_ips = ", ".join(sorted(list(detected_ips)))
except Exception as e:
_LOGGER.warning("IP detection failed, suggesting defaults: %s", e)
suggested_ips = "127.0.0.1"
schema = vol.Schema(
{
vol.Required(
"banned_ips",
description={"suggested_value": suggested_ips},
): str,
}
)
return self.async_show_form(step_id="user", data_schema=schema)
custom_components/unban_local/const.py
DOMAIN = "unban_local"
custom_components/unban_local/manifest.json
{
"domain": "unban_local",
"name": "Local IP Unbanner",
"documentation": "https://github.com/xxxxx/ha-unban-local",
"codeowners": ["@xxxxx"],
"version": "0.0.1",
"iot_class": "local_polling",
"config_flow": true
}
custom_components/unban_local/services.yaml
# This file is intentionally blank.
Automation
alias: Reactive Local IP Unbanner
description: Triggers when ip_bans.yaml is modified to clear internal IP bans.
triggers:
- trigger: state
entity_id:
- event.folder_watcher_config_ip_bans
conditions: []
actions:
- action: notify.mobile_app_phone
metadata: {}
data:
message: ip_bans.yaml modified
title: Home Assistant
- action: unban_local.clear_bans
data: {}
mode: single