diff --git a/custom_components/mikrotik_router/__init__.py b/custom_components/mikrotik_router/__init__.py index e2c6886..7ebae5e 100644 --- a/custom_components/mikrotik_router/__init__.py +++ b/custom_components/mikrotik_router/__init__.py @@ -42,32 +42,32 @@ async def async_setup_entry(hass, config_entry): username = config_entry.data[CONF_USERNAME] password = config_entry.data[CONF_PASSWORD] use_ssl = config_entry.data[CONF_SSL] - + mikrotik_controller = MikrotikControllerData(hass, config_entry, name, host, port, username, password, use_ssl) await mikrotik_controller.hwinfo_update() await mikrotik_controller.async_update() - + if not mikrotik_controller.data: raise ConfigEntryNotReady() - + hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = mikrotik_controller - + hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, "sensor") ) - + hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, "binary_sensor") ) - + hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, "device_tracker") ) - + hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, "switch") ) - + device_registry = await hass.helpers.device_registry.async_get_registry() device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, @@ -76,7 +76,7 @@ async def async_setup_entry(hass, config_entry): name=mikrotik_controller.data['routerboard']['model'], sw_version=mikrotik_controller.data['resource']['version'], ) - + return True diff --git a/custom_components/mikrotik_router/binary_sensor.py b/custom_components/mikrotik_router/binary_sensor.py index 5606e92..5937a0d 100644 --- a/custom_components/mikrotik_router/binary_sensor.py +++ b/custom_components/mikrotik_router/binary_sensor.py @@ -40,16 +40,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities): name = config_entry.data[CONF_NAME] mikrotik_controller = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] sensors = {} - + @callback def update_controller(): """Update the values of the controller.""" update_items(name, mikrotik_controller, async_add_entities, sensors) - + mikrotik_controller.listeners.append( async_dispatcher_connect(hass, mikrotik_controller.signal_update, update_controller) ) - + update_controller() return @@ -61,63 +61,63 @@ async def async_setup_entry(hass, config_entry, async_add_entities): def update_items(name, mikrotik_controller, async_add_entities, sensors): """Update sensor state from the controller.""" new_sensors = [] - + for sensor in SENSOR_TYPES: item_id = name + "-" + sensor if item_id in sensors: if sensors[item_id].enabled: sensors[item_id].async_schedule_update_ha_state() continue - + sensors[item_id] = MikrotikControllerBinarySensor(mikrotik_controller=mikrotik_controller, name=name, kind=sensor) new_sensors.append(sensors[item_id]) - + if new_sensors: async_add_entities(new_sensors, True) - + return - + class MikrotikControllerBinarySensor(BinarySensorDevice): """Define an Mikrotik Controller Binary Sensor.""" - + def __init__(self, mikrotik_controller, name, kind, uid=''): """Initialize.""" self.mikrotik_controller = mikrotik_controller self._name = name self.kind = kind self.uid = uid - + self._device_class = None self._state = None self._icon = None self._unit_of_measurement = None self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION} - + @property def name(self): """Return the name.""" if self.uid: return f"{self._name} {self.uid} {SENSOR_TYPES[self.kind][ATTR_LABEL]}" return f"{self._name} {SENSOR_TYPES[self.kind][ATTR_LABEL]}" - + @property def device_state_attributes(self): """Return the state attributes.""" return self._attrs - + @property def unique_id(self): """Return a unique_id for this entity.""" if self.uid: return f"{self._name.lower()}-{self.kind.lower()}-{self.uid.lower()}" return f"{self._name.lower()}-{self.kind.lower()}" - + @property def available(self): """Return True if entity is available.""" return bool(self.mikrotik_controller.data) - + @property def device_info(self): """Return a port description for device registry.""" @@ -133,17 +133,17 @@ class MikrotikControllerBinarySensor(BinarySensorDevice): """Synchronize state with controller.""" # await self.mikrotik_controller.async_update() return - + async def async_added_to_hass(self): """Port entity created.""" _LOGGER.debug("New sensor %s (%s)", self._name, self.kind) return - + @property def is_on(self): """Return true if sensor is on.""" val = False if SENSOR_TYPES[self.kind][ATTR_PATH] in self.mikrotik_controller.data and SENSOR_TYPES[self.kind][ATTR_ATTR] in self.mikrotik_controller.data[SENSOR_TYPES[self.kind][ATTR_PATH]]: val = self.mikrotik_controller.data[SENSOR_TYPES[self.kind][ATTR_PATH]][SENSOR_TYPES[self.kind][ATTR_ATTR]] - + return val diff --git a/custom_components/mikrotik_router/config_flow.py b/custom_components/mikrotik_router/config_flow.py index 5837321..d9ee507 100644 --- a/custom_components/mikrotik_router/config_flow.py +++ b/custom_components/mikrotik_router/config_flow.py @@ -44,17 +44,17 @@ class MikrotikControllerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self): """Initialize MikrotikControllerConfigFlow.""" return - + @staticmethod @callback def async_get_options_flow(config_entry): """Get the options flow for this handler.""" return MikrotikControllerOptionsFlowHandler(config_entry) - + async def async_step_import(self, user_input=None): """Occurs when a previously entry setup fails and is re-initiated.""" return await self.async_step_user(user_input) - + async def async_step_user(self, user_input=None): """Handle a flow initialized by the user.""" errors = {} @@ -62,7 +62,7 @@ class MikrotikControllerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # Check if instance with this name already exists if user_input[CONF_NAME] in configured_instances(self.hass): errors["base"] = "name_exists" - + # Test connection api = MikrotikAPI(host=user_input["host"], username=user_input["username"], @@ -72,14 +72,14 @@ class MikrotikControllerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) if not api.connect(): errors[CONF_HOST] = api.error - + # Save instance if not errors: return self.async_create_entry( title=user_input[CONF_NAME], data=user_input ) - + return self._show_config_form(host=user_input["host"], username=user_input["username"], password=user_input["password"], @@ -88,9 +88,9 @@ class MikrotikControllerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): use_ssl=user_input["ssl"], errors=errors ) - + return self._show_config_form(errors=errors) - + # --------------------------- # _show_config_form # --------------------------- @@ -115,22 +115,22 @@ class MikrotikControllerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # --------------------------- class MikrotikControllerOptionsFlowHandler(config_entries.OptionsFlow): """Handle options.""" - + def __init__(self, config_entry): """Initialize options flow.""" self.config_entry = config_entry self.options = dict(config_entry.options) - + async def async_step_init(self, user_input=None): """Manage the options.""" return await self.async_step_device_tracker(user_input) - + async def async_step_device_tracker(self, user_input=None): """Manage the device tracker options.""" if user_input is not None: self.options.update(user_input) return self.async_create_entry(title="", data=self.options) - + return self.async_show_form( step_id="device_tracker", data_schema=vol.Schema( diff --git a/custom_components/mikrotik_router/device_tracker.py b/custom_components/mikrotik_router/device_tracker.py index 90345cb..cbaa61f 100644 --- a/custom_components/mikrotik_router/device_tracker.py +++ b/custom_components/mikrotik_router/device_tracker.py @@ -43,16 +43,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities): name = config_entry.data[CONF_NAME] mikrotik_controller = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] tracked = {} - + @callback def update_controller(): """Update the values of the controller.""" update_items(name, mikrotik_controller, async_add_entities, tracked) - + mikrotik_controller.listeners.append( async_dispatcher_connect(hass, mikrotik_controller.signal_update, update_controller) ) - + update_controller() return @@ -64,7 +64,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): def update_items(name, mikrotik_controller, async_add_entities, tracked): """Update tracked device state from the controller.""" new_tracked = [] - + for uid in mikrotik_controller.data['interface']: if mikrotik_controller.data['interface'][uid]['type'] == "ether": item_id = name + "-" + mikrotik_controller.data['interface'][uid]['default-name'] @@ -72,13 +72,13 @@ def update_items(name, mikrotik_controller, async_add_entities, tracked): if tracked[item_id].enabled: tracked[item_id].async_schedule_update_ha_state() continue - + tracked[item_id] = MikrotikControllerPortDeviceTracker(name, uid, mikrotik_controller) new_tracked.append(tracked[item_id]) - + if new_tracked: async_add_entities(new_tracked) - + return @@ -87,57 +87,57 @@ def update_items(name, mikrotik_controller, async_add_entities, tracked): # --------------------------- class MikrotikControllerPortDeviceTracker(ScannerEntity): """Representation of a network port.""" - + def __init__(self, name, uid, mikrotik_controller): """Set up tracked port.""" self._name = name self._uid = uid self.mikrotik_controller = mikrotik_controller - + self._attrs = { ATTR_ATTRIBUTION: ATTRIBUTION, } - + @property def entity_registry_enabled_default(self): """Return if the entity should be enabled when first added to the entity registry.""" return True - + async def async_added_to_hass(self): """Port entity created.""" _LOGGER.debug("New port tracker %s (%s)", self._name, self.mikrotik_controller.data['interface'][self._uid]['port-mac-address']) return - + async def async_update(self): """Synchronize state with controller.""" # await self.mikrotik_controller.async_update() return - + @property def is_connected(self): """Return true if the port is connected to the network.""" return self.mikrotik_controller.data['interface'][self._uid]['running'] - + @property def source_type(self): """Return the source type of the port.""" return SOURCE_TYPE_ROUTER - + @property def name(self) -> str: """Return the name of the port.""" return f"{self._name} {self.mikrotik_controller.data['interface'][self._uid]['default-name']}" - + @property def unique_id(self) -> str: """Return a unique identifier for this port.""" return f"{self._name.lower()}-{self.mikrotik_controller.data['interface'][self._uid]['port-mac-address']}" - + @property def available(self) -> bool: """Return if controller is available.""" return self.mikrotik_controller.connected() - + @property def icon(self): """Return the icon.""" @@ -145,12 +145,12 @@ class MikrotikControllerPortDeviceTracker(ScannerEntity): icon = 'mdi:lan-connect' else: icon = 'mdi:lan-pending' - + if not self.mikrotik_controller.data['interface'][self._uid]['enabled']: icon = 'mdi:lan-disconnect' - + return icon - + @property def device_info(self): """Return a port description for device registry.""" @@ -161,14 +161,14 @@ class MikrotikControllerPortDeviceTracker(ScannerEntity): "name": self.mikrotik_controller.data['interface'][self._uid]['default-name'], } return info - + @property def device_state_attributes(self): """Return the port state attributes.""" attributes = self._attrs - + for variable in DEVICE_ATTRIBUTES: if variable in self.mikrotik_controller.data['interface'][self._uid]: attributes[variable] = self.mikrotik_controller.data['interface'][self._uid][variable] - + return attributes diff --git a/custom_components/mikrotik_router/mikrotik_controller.py b/custom_components/mikrotik_router/mikrotik_controller.py index 3ca558b..c89fac0 100644 --- a/custom_components/mikrotik_router/mikrotik_controller.py +++ b/custom_components/mikrotik_router/mikrotik_controller.py @@ -30,7 +30,7 @@ class MikrotikControllerData(): self.name = name self.hass = hass self.config_entry = config_entry - + self.data = {} self.data['routerboard'] = {} self.data['resource'] = {} @@ -39,18 +39,18 @@ class MikrotikControllerData(): self.data['nat'] = {} self.data['fw-update'] = {} self.data['script'] = {} - + self.listeners = [] - + self.api = MikrotikAPI(host, username, password, port, use_ssl) if not self.api.connect(): self.api = None - + async_track_time_interval(self.hass, self.force_update, self.option_scan_interval) async_track_time_interval(self.hass, self.force_fwupdate_check, timedelta(hours=1)) - + return - + # --------------------------- # force_update # --------------------------- @@ -58,7 +58,7 @@ class MikrotikControllerData(): """Trigger update by timer""" await self.async_update() return - + # --------------------------- # force_fwupdate_check # --------------------------- @@ -66,7 +66,7 @@ class MikrotikControllerData(): """Trigger hourly update by timer""" await self.async_fwupdate_check() return - + # --------------------------- # option_track_arp # --------------------------- @@ -74,7 +74,7 @@ class MikrotikControllerData(): def option_track_arp(self): """Config entry option to not track ARP.""" return self.config_entry.options.get(CONF_TRACK_ARP, DEFAULT_TRACK_ARP) - + # --------------------------- # option_scan_interval # --------------------------- @@ -83,7 +83,7 @@ class MikrotikControllerData(): """Config entry option scan interval.""" scan_interval = self.config_entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) return timedelta(seconds=scan_interval) - + # --------------------------- # signal_update # --------------------------- @@ -91,14 +91,14 @@ class MikrotikControllerData(): def signal_update(self): """Event to signal new data.""" return f"{DOMAIN}-update-{self.name}" - + # --------------------------- # connected # --------------------------- def connected(self): """Return connected state""" return self.api.connected() - + # --------------------------- # hwinfo_update # --------------------------- @@ -107,37 +107,37 @@ class MikrotikControllerData(): self.get_system_routerboard() self.get_system_resource() return - + # --------------------------- # async_fwupdate_check # --------------------------- async def async_fwupdate_check(self): """Update Mikrotik data""" - + self.get_firmare_update() - + async_dispatcher_send(self.hass, self.signal_update) return - + # --------------------------- # async_update # --------------------------- # @Throttle(DEFAULT_SCAN_INTERVAL) async def async_update(self): """Update Mikrotik data""" - + if 'available' not in self.data['fw-update']: await self.async_fwupdate_check() - + self.get_interface() self.get_arp() self.get_nat() self.get_system_resource() self.get_script() - + async_dispatcher_send(self.hass, self.signal_update) return - + # --------------------------- # async_reset # --------------------------- @@ -145,24 +145,24 @@ class MikrotikControllerData(): """Reset dispatchers""" for unsub_dispatcher in self.listeners: unsub_dispatcher() - + self.listeners = [] return True - + # --------------------------- # set_value # --------------------------- def set_value(self, path, param, value, mod_param, mod_value): """Change value using Mikrotik API""" return self.api.update(path, param, value, mod_param, mod_value) - + # --------------------------- # run_script # --------------------------- def run_script(self, name): """Run script using Mikrotik API""" return self.api.run_script(name) - + # --------------------------- # get_interface # --------------------------- @@ -172,11 +172,11 @@ class MikrotikControllerData(): for iface in ifaces: if 'default-name' not in iface: continue - + uid = iface['default-name'] if uid not in self.data['interface']: self.data['interface'][uid] = {} - + self.data['interface'][uid]['default-name'] = iface['default-name'] self.data['interface'][uid]['name'] = iface['name'] if 'name' in iface else iface['default-name'] self.data['interface'][uid]['type'] = iface['type'] if 'type' in iface else "unknown" @@ -191,15 +191,15 @@ class MikrotikControllerData(): self.data['interface'][uid]['tx-byte'] = iface['tx-byte'] if 'tx-byte' in iface else "" self.data['interface'][uid]['tx-queue-drop'] = iface['tx-queue-drop'] if 'tx-queue-drop' in iface else "" self.data['interface'][uid]['actual-mtu'] = iface['actual-mtu'] if 'actual-mtu' in iface else "" - + if 'client-ip-address' not in self.data['interface'][uid]: self.data['interface'][uid]['client-ip-address'] = "" - + if 'client-mac-address' not in self.data['interface'][uid]: self.data['interface'][uid]['client-mac-address'] = "" - + return - + # --------------------------- # get_arp # --------------------------- @@ -211,7 +211,7 @@ class MikrotikControllerData(): self.data['interface'][uid]['client-ip-address'] = "disabled" self.data['interface'][uid]['client-mac-address'] = "disabled" return False - + mac2ip = {} bridge_used = False data = self.api.path("/ip/arp") @@ -219,40 +219,40 @@ class MikrotikControllerData(): # Ignore invalid entries if entry['invalid']: continue - + # Do not add ARP detected on bridge if entry['interface'] == "bridge": bridge_used = True # Build address table on bridge if 'mac-address' in entry and 'address' in entry: mac2ip[entry['mac-address']] = entry['address'] - + continue - + # Get iface default-name from custom name uid = self.get_iface_from_entry(entry) if not uid: continue - + # Create uid arp dict if uid not in self.data['arp']: self.data['arp'][uid] = {} - + # Add data self.data['arp'][uid]['interface'] = uid self.data['arp'][uid]['mac-address'] = "multiple" if 'mac-address' in self.data['arp'][uid] else entry['mac-address'] self.data['arp'][uid]['address'] = "multiple" if 'address' in self.data['arp'][uid] else entry['address'] - + if bridge_used: self.update_bridge_hosts(mac2ip) - + # Map ARP to ifaces for uid in self.data['interface']: self.data['interface'][uid]['client-ip-address'] = self.data['arp'][uid]['address'] if uid in self.data['arp'] and 'address' in self.data['arp'][uid] else "" self.data['interface'][uid]['client-mac-address'] = self.data['arp'][uid]['mac-address'] if uid in self.data['arp'] and 'mac-address' in self.data['arp'][uid] else "" - + return True - + # --------------------------- # update_bridge_hosts # --------------------------- @@ -263,16 +263,16 @@ class MikrotikControllerData(): # Ignore port MAC if entry['local']: continue - + # Get iface default-name from custom name uid = self.get_iface_from_entry(entry) if not uid: continue - + # Create uid arp dict if uid not in self.data['arp']: self.data['arp'][uid] = {} - + # Add data self.data['arp'][uid]['interface'] = uid if 'mac-address' in self.data['arp'][uid]: @@ -281,12 +281,12 @@ class MikrotikControllerData(): else: self.data['arp'][uid]['mac-address'] = entry['mac-address'] self.data['arp'][uid]['address'] = "" - + if self.data['arp'][uid]['address'] == "" and self.data['arp'][uid]['mac-address'] in mac2ip: self.data['arp'][uid]['address'] = mac2ip[self.data['arp'][uid]['mac-address']] - + return - + # --------------------------- # get_iface_from_entry # --------------------------- @@ -297,9 +297,9 @@ class MikrotikControllerData(): if self.data['interface'][ifacename]['name'] == entry['interface']: uid = self.data['interface'][ifacename]['default-name'] break - + return uid - + # --------------------------- # get_nat # --------------------------- @@ -309,11 +309,11 @@ class MikrotikControllerData(): for entry in data: if entry['action'] != 'dst-nat': continue - + uid = entry['.id'] if uid not in self.data['nat']: self.data['nat'][uid] = {} - + self.data['nat'][uid]['name'] = entry['protocol'] + ':' + str(entry['dst-port']) self.data['nat'][uid]['protocol'] = entry['protocol'] if 'protocol' in entry else "" self.data['nat'][uid]['dst-port'] = entry['dst-port'] if 'dst-port' in entry else "" @@ -324,9 +324,9 @@ class MikrotikControllerData(): self.data['nat'][uid]['enabled'] = True if 'disabled' in entry and entry['disabled']: self.data['nat'][uid]['enabled'] = False - + return - + # --------------------------- # get_system_routerboard # --------------------------- @@ -338,9 +338,9 @@ class MikrotikControllerData(): self.data['routerboard']['model'] = entry['model'] if 'model' in entry else "unknown" self.data['routerboard']['serial-number'] = entry['serial-number'] if 'serial-number' in entry else "unknown" self.data['routerboard']['firmware'] = entry['current-firmware'] if 'current-firmware' in entry else "unknown" - + return - + # --------------------------- # get_system_resource # --------------------------- @@ -357,14 +357,14 @@ class MikrotikControllerData(): self.data['resource']['memory-usage'] = round(((entry['total-memory'] - entry['free-memory']) / entry['total-memory']) * 100) else: self.data['resource']['memory-usage'] = "unknown" - + if 'free-hdd-space' in entry and 'total-hdd-space' in entry: self.data['resource']['hdd-usage'] = round(((entry['total-hdd-space'] - entry['free-hdd-space']) / entry['total-hdd-space']) * 100) else: self.data['resource']['hdd-usage'] = "unknown" - + return - + # --------------------------- # get_system_routerboard # --------------------------- @@ -376,9 +376,9 @@ class MikrotikControllerData(): self.data['fw-update']['channel'] = entry['channel'] if 'channel' in entry else "unknown" self.data['fw-update']['installed-version'] = entry['installed-version'] if 'installed-version' in entry else "unknown" self.data['fw-update']['latest-version'] = entry['latest-version'] if 'latest-version' in entry else "unknown" - + return - + # --------------------------- # get_script # --------------------------- @@ -388,13 +388,13 @@ class MikrotikControllerData(): for entry in data: if 'name' not in entry: continue - + uid = entry['name'] if uid not in self.data['script']: self.data['script'][uid] = {} - + self.data['script'][uid]['name'] = entry['name'] self.data['script'][uid]['last-started'] = entry['last-started'] if 'last-started' in entry else "unknown" self.data['script'][uid]['run-count'] = entry['run-count'] if 'run-count' in entry else "unknown" - + return diff --git a/custom_components/mikrotik_router/mikrotikapi.py b/custom_components/mikrotik_router/mikrotikapi.py index 723a44d..bd39c2b 100644 --- a/custom_components/mikrotik_router/mikrotikapi.py +++ b/custom_components/mikrotik_router/mikrotikapi.py @@ -11,7 +11,7 @@ _LOGGER = logging.getLogger(__name__) # --------------------------- class MikrotikAPI: """Handle all communication with the Mikrotik API.""" - + def __init__(self, host, username, password, port=0, use_ssl=True, login_method="plain", encoding="utf-8"): """Initialize the Mikrotik Client.""" self._host = host @@ -22,15 +22,15 @@ class MikrotikAPI: self._login_method = login_method self._encoding = encoding self._ssl_wrapper = None - + self._connection = None self._connected = False self.error = "" - + # Default ports if not self._port: self._port = 8729 if self._use_ssl else 8728 - + # --------------------------- # connect # --------------------------- @@ -38,13 +38,13 @@ class MikrotikAPI: """Connect to Mikrotik device.""" self.error = "" self._connected = False - + kwargs = { "encoding": self._encoding, "login_methods": self._login_method, "port": self._port, } - + if self._use_ssl: if self._ssl_wrapper is None: ssl_context = ssl.create_default_context() @@ -52,7 +52,7 @@ class MikrotikAPI: ssl_context.verify_mode = ssl.CERT_NONE self._ssl_wrapper = ssl_context.wrap_socket kwargs["ssl_wrapper"] = self._ssl_wrapper - + try: self._connection = librouteros.connect(self._host, self._username, self._password, **kwargs) except ( @@ -69,9 +69,9 @@ class MikrotikAPI: else: _LOGGER.info("Mikrotik Connected to %s", self._host) self._connected = True - + return self._connected - + # --------------------------- # error_to_strings # --------------------------- @@ -80,16 +80,16 @@ class MikrotikAPI: self.error = "cannot_connect" if error == "invalid user name or password (6)": self.error = "wrong_login" - + return - + # --------------------------- # connected # --------------------------- def connected(self): """Return connected boolean.""" return self._connected - + # --------------------------- # path # --------------------------- @@ -98,7 +98,7 @@ class MikrotikAPI: if not self._connected or not self._connection: if not self.connect(): return None - + try: response = self._connection.path(path) tuple(response) @@ -115,9 +115,9 @@ class MikrotikAPI: ) as api_error: _LOGGER.error("Mikrotik %s connection error %s", self._host, api_error) return None - + return response if response else None - + # --------------------------- # update # --------------------------- @@ -126,23 +126,23 @@ class MikrotikAPI: response = self.path(path) if response is None: return False - + for tmp in response: if param not in tmp: continue - + if tmp[param] != value: continue - + params = { '.id': tmp['.id'], mod_param: mod_value } - + response.update(**params) - + return True - + # --------------------------- # run_script # --------------------------- @@ -151,15 +151,15 @@ class MikrotikAPI: response = self.path('/system/script') if response is None: return False - + for tmp in response: if 'name' not in tmp: continue - + if tmp['name'] != name: continue - + run = response('run', **{'.id': tmp['.id']}) tuple(run) - + return True diff --git a/custom_components/mikrotik_router/sensor.py b/custom_components/mikrotik_router/sensor.py index 28cde62..70ca08a 100644 --- a/custom_components/mikrotik_router/sensor.py +++ b/custom_components/mikrotik_router/sensor.py @@ -64,16 +64,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities): name = config_entry.data[CONF_NAME] mikrotik_controller = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] sensors = {} - + @callback def update_controller(): """Update the values of the controller.""" update_items(name, mikrotik_controller, async_add_entities, sensors) - + mikrotik_controller.listeners.append( async_dispatcher_connect(hass, mikrotik_controller.signal_update, update_controller) ) - + update_controller() return @@ -85,42 +85,42 @@ async def async_setup_entry(hass, config_entry, async_add_entities): def update_items(name, mikrotik_controller, async_add_entities, sensors): """Update sensor state from the controller.""" new_sensors = [] - + for sensor in SENSOR_TYPES: item_id = name + "-" + sensor if item_id in sensors: if sensors[item_id].enabled: sensors[item_id].async_schedule_update_ha_state() continue - + sensors[item_id] = MikrotikControllerSensor(mikrotik_controller=mikrotik_controller, name=name, kind=sensor) new_sensors.append(sensors[item_id]) - + if new_sensors: async_add_entities(new_sensors, True) - + return - + # --------------------------- # MikrotikControllerSensor # --------------------------- class MikrotikControllerSensor(Entity): """Define an Mikrotik Controller sensor.""" - + def __init__(self, mikrotik_controller, name, kind, uid=''): """Initialize.""" self.mikrotik_controller = mikrotik_controller self._name = name self.kind = kind self.uid = uid - + self._device_class = None self._state = None self._icon = None self._unit_of_measurement = None self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION} - + @property def name(self): """Return the name.""" @@ -134,7 +134,7 @@ class MikrotikControllerSensor(Entity): val = "unknown" if SENSOR_TYPES[self.kind][ATTR_PATH] in self.mikrotik_controller.data and SENSOR_TYPES[self.kind][ATTR_ATTR] in self.mikrotik_controller.data[SENSOR_TYPES[self.kind][ATTR_PATH]]: val = self.mikrotik_controller.data[SENSOR_TYPES[self.kind][ATTR_PATH]][SENSOR_TYPES[self.kind][ATTR_ATTR]] - + return val @property @@ -169,7 +169,7 @@ class MikrotikControllerSensor(Entity): def available(self): """Return True if entity is available.""" return bool(self.mikrotik_controller.data) - + @property def device_info(self): """Return a port description for device registry.""" @@ -185,7 +185,7 @@ class MikrotikControllerSensor(Entity): """Synchronize state with controller.""" # await self.mikrotik_controller.async_update() return - + async def async_added_to_hass(self): """Port entity created.""" _LOGGER.debug("New sensor %s (%s)", self._name, self.kind) diff --git a/custom_components/mikrotik_router/switch.py b/custom_components/mikrotik_router/switch.py index 2ec2524..6a632fe 100644 --- a/custom_components/mikrotik_router/switch.py +++ b/custom_components/mikrotik_router/switch.py @@ -58,16 +58,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities): name = config_entry.data[CONF_NAME] mikrotik_controller = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] switches = {} - + @callback def update_controller(): """Update the values of the controller.""" update_items(name, mikrotik_controller, async_add_entities, switches) - + mikrotik_controller.listeners.append( async_dispatcher_connect(hass, mikrotik_controller.signal_update, update_controller) ) - + update_controller() return @@ -79,7 +79,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): def update_items(name, mikrotik_controller, async_add_entities, switches): """Update device switch state from the controller.""" new_switches = [] - + # Add interface switches for uid in mikrotik_controller.data['interface']: if mikrotik_controller.data['interface'][uid]['type'] == "ether": @@ -88,10 +88,10 @@ def update_items(name, mikrotik_controller, async_add_entities, switches): if switches[item_id].enabled: switches[item_id].async_schedule_update_ha_state() continue - + switches[item_id] = MikrotikControllerPortSwitch(name, uid, mikrotik_controller) new_switches.append(switches[item_id]) - + # Add NAT switches for uid in mikrotik_controller.data['nat']: item_id = name + "-nat-" + mikrotik_controller.data['nat'][uid]['name'] @@ -99,10 +99,10 @@ def update_items(name, mikrotik_controller, async_add_entities, switches): if switches[item_id].enabled: switches[item_id].async_schedule_update_ha_state() continue - + switches[item_id] = MikrotikControllerNATSwitch(name, uid, mikrotik_controller) new_switches.append(switches[item_id]) - + # Add script switches for uid in mikrotik_controller.data['script']: item_id = name + "-script-" + mikrotik_controller.data['script'][uid]['name'] @@ -110,13 +110,13 @@ def update_items(name, mikrotik_controller, async_add_entities, switches): if switches[item_id].enabled: switches[item_id].async_schedule_update_ha_state() continue - + switches[item_id] = MikrotikControllerScriptSwitch(name, uid, mikrotik_controller) new_switches.append(switches[item_id]) - + if new_switches: async_add_entities(new_switches) - + return @@ -125,23 +125,23 @@ def update_items(name, mikrotik_controller, async_add_entities, switches): # --------------------------- class MikrotikControllerSwitch(SwitchDevice, RestoreEntity): """Representation of a network port switch.""" - + def __init__(self, name, uid, mikrotik_controller): """Set up switch.""" self._name = name self._uid = uid self.mikrotik_controller = mikrotik_controller - + async def async_added_to_hass(self): """Switch entity created.""" _LOGGER.debug("New switch %s (%s)", self._name, self._uid) return - + async def async_update(self): """Synchronize state with controller.""" # await self.mikrotik_controller.async_update() return - + @property def available(self) -> bool: """Return if controller is available.""" @@ -153,30 +153,30 @@ class MikrotikControllerSwitch(SwitchDevice, RestoreEntity): # --------------------------- class MikrotikControllerPortSwitch(MikrotikControllerSwitch): """Representation of a network port switch.""" - + def __init__(self, name, uid, mikrotik_controller): """Set up tracked port.""" super().__init__(name, uid, mikrotik_controller) - + self._attrs = { ATTR_ATTRIBUTION: ATTRIBUTION, } - + async def async_added_to_hass(self): """Port entity created.""" _LOGGER.debug("New port switch %s (%s)", self._name, self.mikrotik_controller.data['interface'][self._uid]['port-mac-address']) return - + @property def name(self) -> str: """Return the name of the port.""" return f"{self._name} port {self.mikrotik_controller.data['interface'][self._uid]['default-name']}" - + @property def unique_id(self) -> str: """Return a unique identifier for this port.""" return f"{self._name.lower()}-enable_switch-{self.mikrotik_controller.data['interface'][self._uid]['port-mac-address']}" - + @property def icon(self): """Return the icon.""" @@ -184,12 +184,12 @@ class MikrotikControllerPortSwitch(MikrotikControllerSwitch): icon = 'mdi:lan-connect' else: icon = 'mdi:lan-pending' - + if not self.mikrotik_controller.data['interface'][self._uid]['enabled']: icon = 'mdi:lan-disconnect' - + return icon - + @property def device_info(self): """Return a port description for device registry.""" @@ -200,16 +200,16 @@ class MikrotikControllerPortSwitch(MikrotikControllerSwitch): "name": self.mikrotik_controller.data['interface'][self._uid]['default-name'], } return info - + @property def device_state_attributes(self): """Return the port state attributes.""" attributes = self._attrs - + for variable in DEVICE_ATTRIBUTES_IFACE: if variable in self.mikrotik_controller.data['interface'][self._uid]: attributes[variable] = self.mikrotik_controller.data['interface'][self._uid][variable] - + return attributes async def async_turn_on(self): @@ -245,30 +245,30 @@ class MikrotikControllerPortSwitch(MikrotikControllerSwitch): # --------------------------- class MikrotikControllerNATSwitch(MikrotikControllerSwitch): """Representation of a NAT switch.""" - + def __init__(self, name, uid, mikrotik_controller): """Set up NAT switch.""" super().__init__(name, uid, mikrotik_controller) - + self._attrs = { ATTR_ATTRIBUTION: ATTRIBUTION, } - + async def async_added_to_hass(self): """NAT switch entity created.""" _LOGGER.debug("New port switch %s (%s)", self._name, self.mikrotik_controller.data['nat'][self._uid]['name']) return - + @property def name(self) -> str: """Return the name of the NAT switch.""" return f"{self._name} NAT {self.mikrotik_controller.data['nat'][self._uid]['name']}" - + @property def unique_id(self) -> str: """Return a unique identifier for this NAT switch.""" return f"{self._name.lower()}-nat_switch-{self.mikrotik_controller.data['nat'][self._uid]['name']}" - + @property def icon(self): """Return the icon.""" @@ -276,9 +276,9 @@ class MikrotikControllerNATSwitch(MikrotikControllerSwitch): icon = 'mdi:network-off-outline' else: icon = 'mdi:network-outline' - + return icon - + @property def device_info(self): """Return a NAT switch description for device registry.""" @@ -289,16 +289,16 @@ class MikrotikControllerNATSwitch(MikrotikControllerSwitch): "name": "NAT", } return info - + @property def device_state_attributes(self): """Return the NAT switch state attributes.""" attributes = self._attrs - + for variable in DEVICE_ATTRIBUTES_NAT: if variable in self.mikrotik_controller.data['nat'][self._uid]: attributes[variable] = self.mikrotik_controller.data['nat'][self._uid][variable] - + return attributes async def async_turn_on(self): @@ -334,35 +334,35 @@ class MikrotikControllerNATSwitch(MikrotikControllerSwitch): # --------------------------- class MikrotikControllerScriptSwitch(MikrotikControllerSwitch): """Representation of a script switch.""" - + def __init__(self, name, uid, mikrotik_controller): """Set up script switch.""" super().__init__(name, uid, mikrotik_controller) - + self._attrs = { ATTR_ATTRIBUTION: ATTRIBUTION, } - + async def async_added_to_hass(self): """Script switch entity created.""" _LOGGER.debug("New script switch %s (%s)", self._name, self.mikrotik_controller.data['script'][self._uid]['name']) return - + @property def name(self) -> str: """Return the name of the script switch.""" return f"{self._name} script {self.mikrotik_controller.data['script'][self._uid]['name']}" - + @property def unique_id(self) -> str: """Return a unique identifier for this script switch.""" return f"{self._name.lower()}-script_switch-{self.mikrotik_controller.data['script'][self._uid]['name']}" - + @property def icon(self): """Return the icon.""" return 'mdi:script-text-outline' - + @property def device_info(self): """Return a script switch description for device registry.""" @@ -373,16 +373,16 @@ class MikrotikControllerScriptSwitch(MikrotikControllerSwitch): "name": "Scripts", } return info - + @property def device_state_attributes(self): """Return the script switch state attributes.""" attributes = self._attrs - + for variable in DEVICE_ATTRIBUTES_SCRIPT: if variable in self.mikrotik_controller.data['script'][self._uid]: attributes[variable] = self.mikrotik_controller.data['script'][self._uid][variable] - + return attributes async def async_turn_on(self):