diff --git a/custom_components/mikrotik_router/__init__.py b/custom_components/mikrotik_router/__init__.py index f175353..58c302b 100644 --- a/custom_components/mikrotik_router/__init__.py +++ b/custom_components/mikrotik_router/__init__.py @@ -67,6 +67,10 @@ async def async_setup_entry(hass, config_entry): hass.config_entries.async_forward_entry_setup(config_entry, "switch") ) + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(config_entry, "button") + ) + hass.services.async_register( DOMAIN, RUN_SCRIPT_COMMAND, controller.run_script, schema=SCRIPT_SCHEMA ) @@ -102,6 +106,7 @@ async def async_unload_entry(hass, config_entry): await hass.config_entries.async_forward_entry_unload(config_entry, "binary_sensor") await hass.config_entries.async_forward_entry_unload(config_entry, "device_tracker") await hass.config_entries.async_forward_entry_unload(config_entry, "switch") + await hass.config_entries.async_forward_entry_unload(config_entry, "button") hass.services.async_remove(DOMAIN, RUN_SCRIPT_COMMAND) await controller.async_reset() hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id) diff --git a/custom_components/mikrotik_router/button.py b/custom_components/mikrotik_router/button.py new file mode 100644 index 0000000..778b3b4 --- /dev/null +++ b/custom_components/mikrotik_router/button.py @@ -0,0 +1,216 @@ +"""Support for the Mikrotik Router buttons.""" + +import logging +from typing import Any, Dict + +from homeassistant.components.button import ButtonEntity +from homeassistant.const import CONF_NAME, ATTR_ATTRIBUTION +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.restore_state import RestoreEntity + +from .const import DOMAIN, DATA_CLIENT, ATTRIBUTION + +_LOGGER = logging.getLogger(__name__) + +DEVICE_ATTRIBUTES_SCRIPT = [ + "last-started", + "run-count", +] + + +# --------------------------- +# format_attribute +# --------------------------- +def format_attribute(attr): + res = attr.replace("-", " ") + res = res.capitalize() + res = res.replace(" ip ", " IP ") + res = res.replace(" mac ", " MAC ") + res = res.replace(" mtu", " MTU") + res = res.replace("Sfp", "SFP") + res = res.replace("Poe", "POE") + res = res.replace(" tx", " TX") + res = res.replace(" rx", " RX") + return res + + +# --------------------------- +# async_setup_entry +# --------------------------- +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up buttons for Mikrotik Router component.""" + inst = config_entry.data[CONF_NAME] + mikrotik_controller = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] + buttons = {} + + @callback + def update_controller(): + """Update the values of the controller.""" + update_items(inst, mikrotik_controller, async_add_entities, buttons) + + mikrotik_controller.listeners.append( + async_dispatcher_connect( + hass, mikrotik_controller.signal_update, update_controller + ) + ) + + update_controller() + + +# --------------------------- +# update_items +# --------------------------- +@callback +def update_items(inst, mikrotik_controller, async_add_entities, buttons): + """Update device button state from the controller.""" + new_buttons = [] + + # Add buttons + for sid, sid_uid, sid_name, sid_ref, sid_attr, sid_func in zip( + # Data point name + [ + "script", + ], + # Data point unique id + [ + "name", + ], + # Entry Name + [ + "name", + ], + # Entry Unique id + [ + "name", + ], + # Attr + [ + DEVICE_ATTRIBUTES_SCRIPT, + ], + # Button function + [ + MikrotikControllerScriptButton, + ], + ): + for uid in mikrotik_controller.data[sid]: + item_id = f"{inst}-{sid}-{mikrotik_controller.data[sid][uid][sid_uid]}" + + _LOGGER.debug("Updating button %s", item_id) + if item_id in buttons: + if buttons[item_id].enabled: + buttons[item_id].async_schedule_update_ha_state() + continue + + # Create new entity + sid_data = { + "sid": sid, + "sid_uid": sid_uid, + "sid_name": sid_name, + "sid_ref": sid_ref, + "sid_attr": sid_attr, + } + buttons[item_id] = sid_func(inst, uid, mikrotik_controller, sid_data) + new_buttons.append(buttons[item_id]) + + if new_buttons: + async_add_entities(new_buttons) + + +# --------------------------- +# MikrotikControllerButton +# --------------------------- +class MikrotikControllerButton(ButtonEntity, RestoreEntity): + """Representation of a button.""" + + def __init__(self, inst, uid, mikrotik_controller, sid_data): + """Initialize.""" + self._sid_data = sid_data + self._inst = inst + self._ctrl = mikrotik_controller + self._data = mikrotik_controller.data[self._sid_data["sid"]][uid] + + self._attrs = { + ATTR_ATTRIBUTION: ATTRIBUTION, + } + + async def async_added_to_hass(self): + """Run when entity about to be added to hass.""" + _LOGGER.debug( + "New button %s (%s %s)", + self._inst, + self._sid_data["sid"], + self._data[self._sid_data["sid_uid"]], + ) + + async def async_update(self): + """Synchronize state with controller.""" + + @property + def available(self) -> bool: + """Return if controller is available.""" + return self._ctrl.connected() + + @property + def name(self) -> str: + """Return the name.""" + return f"{self._inst} {self._sid_data['sid']} {self._data[self._sid_data['sid_name']]}" + + @property + def unique_id(self) -> str: + """Return a unique id for this entity.""" + return f"{self._inst.lower()}-{self._sid_data['sid']}_button-{self._data[self._sid_data['sid_ref']]}" + + @property + def extra_state_attributes(self) -> Dict[str, Any]: + """Return the state attributes.""" + attributes = self._attrs + + for variable in self._sid_data["sid_attr"]: + if variable in self._data: + attributes[format_attribute(variable)] = self._data[variable] + + return attributes + + async def async_press(self) -> None: + pass + + +# --------------------------- +# MikrotikControllerScriptButton +# --------------------------- +class MikrotikControllerScriptButton(MikrotikControllerButton): + """Representation of a script button.""" + + def __init__(self, inst, uid, mikrotik_controller, sid_data): + """Initialize.""" + super().__init__(inst, uid, mikrotik_controller, sid_data) + + @property + def icon(self) -> str: + """Return the icon.""" + return "mdi:script-text-outline" + + @property + def device_info(self) -> Dict[str, Any]: + """Return a description for device registry.""" + info = { + "identifiers": { + ( + DOMAIN, + "serial-number", + self._ctrl.data["routerboard"]["serial-number"], + "button", + "Scripts", + ) + }, + "manufacturer": self._ctrl.data["resource"]["platform"], + "model": self._ctrl.data["resource"]["board-name"], + "name": f"{self._inst} Scripts", + } + return info + + async def async_press(self) -> None: + """Process the button press.""" + self._ctrl.run_script(self._data["name"]) + await self._ctrl.force_update() diff --git a/custom_components/mikrotik_router/switch.py b/custom_components/mikrotik_router/switch.py index 194ba30..ed84927 100644 --- a/custom_components/mikrotik_router/switch.py +++ b/custom_components/mikrotik_router/switch.py @@ -131,11 +131,6 @@ DEVICE_ATTRIBUTES_KIDCONTROL = [ "sun", ] -DEVICE_ATTRIBUTES_SCRIPT = [ - "last-started", - "run-count", -] - DEVICE_ATTRIBUTES_QUEUE = [ "target", "download-rate", @@ -212,7 +207,6 @@ def update_items(inst, mikrotik_controller, async_add_entities, switches): "mangle", "filter", "ppp_secret", - "script", "queue", "kid-control", "kid-control", @@ -227,7 +221,6 @@ def update_items(inst, mikrotik_controller, async_add_entities, switches): "name", "name", "name", - "name", ], # Entry Name [ @@ -239,7 +232,6 @@ def update_items(inst, mikrotik_controller, async_add_entities, switches): "name", "name", "name", - "name", ], # Entry Unique id [ @@ -251,7 +243,6 @@ def update_items(inst, mikrotik_controller, async_add_entities, switches): "name", "name", "name", - "name", ], # Attr [ @@ -260,7 +251,6 @@ def update_items(inst, mikrotik_controller, async_add_entities, switches): DEVICE_ATTRIBUTES_MANGLE, DEVICE_ATTRIBUTES_FILTER, DEVICE_ATTRIBUTES_PPP_SECRET, - DEVICE_ATTRIBUTES_SCRIPT, DEVICE_ATTRIBUTES_QUEUE, DEVICE_ATTRIBUTES_KIDCONTROL, DEVICE_ATTRIBUTES_KIDCONTROL, @@ -272,7 +262,6 @@ def update_items(inst, mikrotik_controller, async_add_entities, switches): MikrotikControllerMangleSwitch, MikrotikControllerFilterSwitch, MikrotikControllerPPPSecretSwitch, - MikrotikControllerScriptSwitch, MikrotikControllerQueueSwitch, MikrotikControllerKidcontrolSwitch, MikrotikControllerKidcontrolPauseSwitch, @@ -817,54 +806,6 @@ class MikrotikControllerPPPSecretSwitch(MikrotikControllerSwitch): await self._ctrl.async_update() -# --------------------------- -# MikrotikControllerScriptSwitch -# --------------------------- -class MikrotikControllerScriptSwitch(MikrotikControllerSwitch): - """Representation of a script switch.""" - - def __init__(self, inst, uid, mikrotik_controller, sid_data): - """Initialize.""" - super().__init__(inst, uid, mikrotik_controller, sid_data) - - @property - def icon(self) -> str: - """Return the icon.""" - return "mdi:script-text-outline" - - @property - def device_info(self) -> Dict[str, Any]: - """Return a description for device registry.""" - info = { - "identifiers": { - ( - DOMAIN, - "serial-number", - self._ctrl.data["routerboard"]["serial-number"], - "switch", - "Scripts", - ) - }, - "manufacturer": self._ctrl.data["resource"]["platform"], - "model": self._ctrl.data["resource"]["board-name"], - "name": f"{self._inst} Scripts", - } - return info - - async def async_turn_on(self) -> None: - """Turn on the switch.""" - self._ctrl.run_script(self._data["name"]) - await self._ctrl.force_update() - - async def async_turn_off(self) -> None: - """Turn off the switch.""" - - @property - def is_on(self) -> bool: - """Return true if device is on.""" - return False - - # --------------------------- # MikrotikControllerQueueSwitch # --------------------------- diff --git a/hacs.json b/hacs.json index b5b2f4b..e3bfdc2 100644 --- a/hacs.json +++ b/hacs.json @@ -2,7 +2,7 @@ "name": "Mikrotik Router", "homeassistant": "2021.12.1", "iot_class": "local_poll", - "domains": ["device_tracker", "switch", "sensor", "binary_sensor"], + "domains": ["device_tracker", "switch", "button", "sensor", "binary_sensor"], "render_readme": false, "zip_release": true, "filename": "mikrotik_router.zip"