Skip to content

Commit

Permalink
Kill States Action (#304)
Browse files Browse the repository at this point in the history
* Kill States Action

* Update README.md
  • Loading branch information
Snuffy2 authored Oct 27, 2024
1 parent aeb5e0c commit 5fb9b2f
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 3 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ service: opnsense.reload_interface
data:
interface: wan
service: opnsense.kill_states
data:
ip_address: 192.168.0.100
# Will optionally return the number of states dropped for each client
action: opnsense.generate_vouchers
data:
validity: "14400" # seconds
Expand Down
1 change: 1 addition & 0 deletions custom_components/opnsense/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,4 @@
SERVICE_SEND_WOL = "send_wol"
SERVICE_RELOAD_INTERFACE = "reload_interface"
SERVICE_GENERATE_VOUCHERS = "generate_vouchers"
SERVICE_KILL_STATES = "kill_states"
14 changes: 14 additions & 0 deletions custom_components/opnsense/pyopnsense/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2133,3 +2133,17 @@ async def generate_vouchers(self, data: Mapping[str, Any]) -> list:

_LOGGER.debug(f"[generate_vouchers] vouchers: {vouchers}")
return vouchers

async def kill_states(self, ip_addr) -> Mapping[str, Any]:
payload: Mapping[str, Any] = {"filter": ip_addr}
response: Mapping[str, Any] | list = await self._post(
"/api/diagnostics/firewall/kill_states/",
payload=payload,
)
_LOGGER.debug(f"[kill_states] ip_addr: {ip_addr}, response: {response}")
if not isinstance(response, Mapping):
return {"success": False, "dropped_states": 0}
return {
"success": bool(response.get("result", None) == "ok"),
"dropped_states": response.get("dropped_states", 0),
}
57 changes: 54 additions & 3 deletions custom_components/opnsense/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
from typing import Any

import voluptuous as vol
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse
from homeassistant.core import (
HomeAssistant,
ServiceCall,
ServiceResponse,
SupportsResponse,
)
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import device_registry, entity_registry
Expand All @@ -13,6 +18,7 @@
OPNSENSE_CLIENT,
SERVICE_CLOSE_NOTICE,
SERVICE_GENERATE_VOUCHERS,
SERVICE_KILL_STATES,
SERVICE_RELOAD_INTERFACE,
SERVICE_RESTART_SERVICE,
SERVICE_SEND_WOL,
Expand Down Expand Up @@ -190,7 +196,7 @@ async def service_reload_interface(call: ServiceCall) -> None:
clients: list = await _get_clients(
call.data.get("device_id", []), call.data.get("entity_id", [])
)
success = None
success: bool | None = None
for client in clients:
response = await client.reload_interface(call.data.get("interface"))
_LOGGER.debug(
Expand All @@ -203,7 +209,7 @@ async def service_reload_interface(call: ServiceCall) -> None:
f"Reload Interface Failed: {call.data.get('interface')}"
)

async def service_generate_vouchers(call: ServiceCall) -> Mapping[str, Any]:
async def service_generate_vouchers(call: ServiceCall) -> ServiceResponse:
clients: list = await _get_clients(
call.data.get("device_id", []), call.data.get("entity_id", [])
)
Expand Down Expand Up @@ -231,6 +237,37 @@ async def service_generate_vouchers(call: ServiceCall) -> Mapping[str, Any]:
_LOGGER.debug(f"[service_generate_vouchers] vouchers: {final_vouchers}")
return final_vouchers

async def service_kill_states(call: ServiceCall) -> ServiceResponse:
clients: list = await _get_clients(
call.data.get("device_id", []), call.data.get("entity_id", [])
)
success: bool | None = None
response_list: list = []
for client in clients:
response: Mapping[str, Any] = await client.kill_states(
call.data.get("ip_addr")
)
_LOGGER.debug(
f"[service_kill_states] client: {client.name}, ip_addr: {call.data.get('ip_addr')}, response: {response}"
)
if response.get("success", False):
response_list.append(
{
"client_name": client.name,
"dropped_states": response.get("dropped_states", 0),
}
)
if success is None or success:
success = response.get("success", False)
if success is None or not success:
raise ServiceValidationError(
f"Kill States Failed: {call.data.get('ip_addr')}"
)
return_response: Mapping[str, Any] = {"dropped_states": response_list}
_LOGGER.debug(f"[service_kill_states] return_response: {return_response}")
if call.return_response:
return return_response

hass.services.async_register(
domain=DOMAIN,
service=SERVICE_CLOSE_NOTICE,
Expand Down Expand Up @@ -379,3 +416,17 @@ async def service_generate_vouchers(call: ServiceCall) -> Mapping[str, Any]:
service_func=service_generate_vouchers,
supports_response=SupportsResponse.ONLY,
)

hass.services.async_register(
domain=DOMAIN,
service=SERVICE_KILL_STATES,
schema=vol.Schema(
{
vol.Required("ip_addr"): vol.Any(cv.string),
vol.Optional("device_id"): vol.Any(cv.string),
vol.Optional("entity_id"): vol.Any(cv.string),
}
),
service_func=service_kill_states,
supports_response=SupportsResponse.OPTIONAL,
)
38 changes: 38 additions & 0 deletions custom_components/opnsense/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -436,3 +436,41 @@ generate_vouchers:
filter:
- integration: opnsense
domain: sensor

kill_states:
name: Kill States
fields:
ip_addr:
name: IP Address
description: "The IP Address to kill all states for. ipv4 or ipv6 accepted"
required: true
advanced: false
example: "192.168.0.100"
selector:
text:
multiple_opnsense:
name: Only needed if there is more than one OPNsense Router
collapsed: true
fields:
device_id:
name: OPNsense Device
description: Select the OPNsense Router to call the command on. If not specified, the command will be sent to all OPNsense Routers.
required: false
selector:
device:
multiple: false
filter:
- integration: opnsense
entity:
- domain: sensor
entity_id:
name: OPNsense Entity
description: Pick any sensor in the OPNsense Router you want to call the command on. If not specified, the command will be sent to all OPNsense Routers.
example: "sensor.opnsense_interface_lan_status"
required: false
selector:
entity:
multiple: false
filter:
- integration: opnsense
domain: sensor

0 comments on commit 5fb9b2f

Please sign in to comment.