diff --git a/custom_components/mikrotik_router/config_flow.py b/custom_components/mikrotik_router/config_flow.py index 0bc9582..e4eb981 100644 --- a/custom_components/mikrotik_router/config_flow.py +++ b/custom_components/mikrotik_router/config_flow.py @@ -37,6 +37,8 @@ from .const import ( DEFAULT_SENSOR_SIMPLE_QUEUES, CONF_SENSOR_NAT, DEFAULT_SENSOR_NAT, + CONF_SENSOR_MANGLE, + DEFAULT_SENSOR_MANGLE, CONF_SENSOR_SCRIPTS, DEFAULT_SENSOR_SCRIPTS, CONF_SENSOR_ENVIRONMENT, @@ -249,6 +251,12 @@ class MikrotikControllerOptionsFlowHandler(OptionsFlow): CONF_SENSOR_NAT, DEFAULT_SENSOR_NAT ), ): bool, + vol.Optional( + CONF_SENSOR_MANGLE, + default=self.config_entry.options.get( + CONF_SENSOR_MANGLE, DEFAULT_SENSOR_MANGLE + ), + ): bool, vol.Optional( CONF_SENSOR_SCRIPTS, default=self.config_entry.options.get( diff --git a/custom_components/mikrotik_router/const.py b/custom_components/mikrotik_router/const.py index 814fead..3f99885 100644 --- a/custom_components/mikrotik_router/const.py +++ b/custom_components/mikrotik_router/const.py @@ -38,6 +38,8 @@ CONF_SENSOR_SIMPLE_QUEUES = "sensor_simple_queues" DEFAULT_SENSOR_SIMPLE_QUEUES = False CONF_SENSOR_NAT = "sensor_nat" DEFAULT_SENSOR_NAT = False +CONF_SENSOR_MANGLE = "sensor_mangle" +DEFAULT_SENSOR_MANGLE = False CONF_SENSOR_SCRIPTS = "sensor_scripts" DEFAULT_SENSOR_SCRIPTS = False CONF_SENSOR_ENVIRONMENT = "sensor_environment" diff --git a/custom_components/mikrotik_router/helper.py b/custom_components/mikrotik_router/helper.py index 8be4a86..02518dd 100644 --- a/custom_components/mikrotik_router/helper.py +++ b/custom_components/mikrotik_router/helper.py @@ -168,6 +168,10 @@ def can_skip(entry, skip) -> bool: ret = True break + if val["value"] == "" and val["name"] not in entry: + ret = True + break + return ret diff --git a/custom_components/mikrotik_router/mikrotik_controller.py b/custom_components/mikrotik_router/mikrotik_controller.py index 73a5707..0045180 100644 --- a/custom_components/mikrotik_router/mikrotik_controller.py +++ b/custom_components/mikrotik_router/mikrotik_controller.py @@ -40,6 +40,8 @@ from .const import ( DEFAULT_SENSOR_SIMPLE_QUEUES, CONF_SENSOR_NAT, DEFAULT_SENSOR_NAT, + CONF_SENSOR_MANGLE, + DEFAULT_SENSOR_MANGLE, CONF_SENSOR_SCRIPTS, DEFAULT_SENSOR_SCRIPTS, CONF_SENSOR_ENVIRONMENT, @@ -74,6 +76,7 @@ class MikrotikControllerData: "bridge_host": {}, "arp": {}, "nat": {}, + "mangle": {}, "fw-update": {}, "script": {}, "queue": {}, @@ -192,6 +195,14 @@ class MikrotikControllerData: """Config entry option to not track ARP.""" return self.config_entry.options.get(CONF_SENSOR_NAT, DEFAULT_SENSOR_NAT) + # --------------------------- + # option_sensor_nat + # --------------------------- + @property + def option_sensor_mangle(self): + """Config entry option to not track ARP.""" + return self.config_entry.options.get(CONF_SENSOR_MANGLE, DEFAULT_SENSOR_MANGLE) + # --------------------------- # option_sensor_scripts # --------------------------- @@ -489,6 +500,9 @@ class MikrotikControllerData: if self.api.connected() and self.option_sensor_nat: await self.hass.async_add_executor_job(self.get_nat) + if self.api.connected() and self.option_sensor_mangle: + await self.hass.async_add_executor_job(self.get_mangle) + if self.api.connected(): await self.hass.async_add_executor_job(self.get_system_resource) @@ -739,6 +753,51 @@ class MikrotikControllerData: del self.data["nat"][uid] + # --------------------------- + # get_mangle + # --------------------------- + def get_mangle(self): + """Get Mangle data from Mikrotik""" + self.data["mangle"] = parse_api( + data=self.data["mangle"], + source=self.api.path("/ip/firewall/mangle"), + key=".id", + vals=[ + {"name": ".id"}, + {"name": "chain"}, + {"name": "action"}, + {"name": "comment"}, + {"name": "address-list"}, + {"name": "passthrough", "type": "bool", "default": False}, + {"name": "new-packet-mark"}, + {"name": "src-address-list"}, + {"name": "protocol", "default": "any"}, + {"name": "src-port", "default": "any"}, + {"name": "dst-port", "default": "any"}, + { + "name": "enabled", + "source": "disabled", + "type": "bool", + "reverse": True, + }, + ], + val_proc=[ + [ + {"name": "name"}, + {"action": "combine"}, + {"key": "protocol"}, + {"text": ":"}, + {"key": "dst-port"}, + ] + ], + skip=[ + {"name": "dynamic", "value": True}, + {"name": "action", "value": "jump"}, + {"name": "protocol", "value": ""}, + {"name": "dst-port", "value": ""}, + ], + ) + # --------------------------- # get_system_routerboard # --------------------------- diff --git a/custom_components/mikrotik_router/strings.json b/custom_components/mikrotik_router/strings.json index 53dd2b9..a107c05 100644 --- a/custom_components/mikrotik_router/strings.json +++ b/custom_components/mikrotik_router/strings.json @@ -35,14 +35,15 @@ }, "sensor_select": { "data": { - "sensor_port_tracker": "Port tracker", - "sensor_port_traffic": "Port traffic", + "sensor_port_tracker": "Port tracker sensors", + "sensor_port_traffic": "Port traffic sensors", "track_network_hosts": "Track network devices", - "sensor_client_traffic": "Client traffic", - "sensor_simple_queues": "Simple queues", - "sensor_nat": "NAT", - "sensor_scripts": "Scripts", - "sensor_environment": "Environment variables" + "sensor_client_traffic": "Client traffic sensors", + "sensor_simple_queues": "Simple queues switches", + "sensor_nat": "NAT switches", + "sensor_mangle": "Mangle switches", + "sensor_scripts": "Scripts switches", + "sensor_environment": "Environment variable sensors" }, "title": "Sensor selection" } diff --git a/custom_components/mikrotik_router/switch.py b/custom_components/mikrotik_router/switch.py index ac3200f..e29e265 100644 --- a/custom_components/mikrotik_router/switch.py +++ b/custom_components/mikrotik_router/switch.py @@ -38,6 +38,18 @@ DEVICE_ATTRIBUTES_NAT = [ "comment", ] +DEVICE_ATTRIBUTES_MANGLE = [ + "chain", + "action", + "address-list", + "passthrough", + "new-packet-mark", + "protocol", + "src-port", + "dst-port", + "comment", +] + DEVICE_ATTRIBUTES_SCRIPT = [ "last-started", "run-count", @@ -109,17 +121,18 @@ def update_items(inst, mikrotik_controller, async_add_entities, switches): # Add switches for sid, sid_uid, sid_name, sid_ref, sid_attr, sid_func in zip( # Data point name - ["interface", "nat", "script", "queue"], + ["interface", "nat", "mangle", "script", "queue"], # Data point unique id - ["name", "name", "name", "name"], + ["name", "name", "name", "name", "name"], # Entry Name - ["name", "name", "name", "name"], + ["name", "name", "comment", "name", "name"], # Entry Unique id - ["port-mac-address", "name", "name", "name"], + ["port-mac-address", "name", "name", "name", "name"], # Attr [ DEVICE_ATTRIBUTES_IFACE, DEVICE_ATTRIBUTES_NAT, + DEVICE_ATTRIBUTES_MANGLE, DEVICE_ATTRIBUTES_SCRIPT, DEVICE_ATTRIBUTES_QUEUE, ], @@ -127,6 +140,7 @@ def update_items(inst, mikrotik_controller, async_add_entities, switches): [ MikrotikControllerPortSwitch, MikrotikControllerNATSwitch, + MikrotikControllerMangleSwitch, MikrotikControllerScriptSwitch, MikrotikControllerQueueSwitch, ], @@ -379,6 +393,85 @@ class MikrotikControllerNATSwitch(MikrotikControllerSwitch): await self._ctrl.async_update() +# --------------------------- +# MikrotikControllerMangleSwitch +# --------------------------- +class MikrotikControllerMangleSwitch(MikrotikControllerSwitch): + """Representation of a Mangle switch.""" + + def __init__(self, inst, uid, mikrotik_controller, sid_data): + """Set up Mangle switch.""" + super().__init__(inst, uid, mikrotik_controller, sid_data) + + @property + def name(self) -> str: + """Return the name of the Mangle switch.""" + return f"{self._inst} Mangle {self._data['name']}" + + @property + def icon(self): + """Return the icon.""" + if not self._data["enabled"]: + icon = "mdi:bookmark-off-outline" + else: + icon = "mdi:bookmark-outline" + + return icon + + @property + def device_info(self): + """Return a Mangle switch description for device registry.""" + info = { + "identifiers": { + ( + DOMAIN, + "serial-number", + self._ctrl.data["routerboard"]["serial-number"], + "switch", + "Mangle", + ) + }, + "manufacturer": self._ctrl.data["resource"]["platform"], + "model": self._ctrl.data["resource"]["board-name"], + "name": f"{self._inst} Mangle", + } + return info + + async def async_turn_on(self): + """Turn on the switch.""" + path = "/ip/firewall/mangle" + param = ".id" + value = None + for uid in self._ctrl.data["mangle"]: + if ( + self._ctrl.data["mangle"][uid]["name"] + == f"{self._data['protocol']}:{self._data['dst-port']}" + ): + value = self._ctrl.data["mangle"][uid][".id"] + + mod_param = "disabled" + mod_value = False + self._ctrl.set_value(path, param, value, mod_param, mod_value) + await self._ctrl.force_update() + + async def async_turn_off(self): + """Turn on the switch.""" + path = "/ip/firewall/mangle" + param = ".id" + value = None + for uid in self._ctrl.data["mangle"]: + if ( + self._ctrl.data["mangle"][uid]["name"] + == f"{self._data['protocol']}:{self._data['dst-port']}" + ): + value = self._ctrl.data["mangle"][uid][".id"] + + mod_param = "disabled" + mod_value = True + self._ctrl.set_value(path, param, value, mod_param, mod_value) + await self._ctrl.async_update() + + # --------------------------- # MikrotikControllerScriptSwitch # ---------------------------