From fe74959b7eb54b0999e6551e9194ff32a5aa8e61 Mon Sep 17 00:00:00 2001 From: tomaae <23486452+tomaae@users.noreply.github.com> Date: Wed, 4 Dec 2019 16:09:30 +0100 Subject: [PATCH] added firmware update binary sensor --- custom_components/mikrotik_router/__init__.py | 5 + .../mikrotik_router/binary_sensor.py | 149 ++++++++++++++++++ .../mikrotik_router/mikrotik_controller.py | 29 ++++ 3 files changed, 183 insertions(+) create mode 100644 custom_components/mikrotik_router/binary_sensor.py diff --git a/custom_components/mikrotik_router/__init__.py b/custom_components/mikrotik_router/__init__.py index fedb373..e905e3a 100644 --- a/custom_components/mikrotik_router/__init__.py +++ b/custom_components/mikrotik_router/__init__.py @@ -56,6 +56,10 @@ async def async_setup_entry(hass, config_entry): 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") ) @@ -83,6 +87,7 @@ async def async_unload_entry(hass, config_entry): """Unload a config entry.""" mikrotik_controller = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] await hass.config_entries.async_forward_entry_unload(config_entry, "sensor") + 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 mikrotik_controller.async_reset() diff --git a/custom_components/mikrotik_router/binary_sensor.py b/custom_components/mikrotik_router/binary_sensor.py new file mode 100644 index 0000000..5606e92 --- /dev/null +++ b/custom_components/mikrotik_router/binary_sensor.py @@ -0,0 +1,149 @@ +"""Support for the Mikrotik Router binary sensor service.""" + +import logging +from homeassistant.core import callback +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.const import ( + CONF_NAME, + ATTR_ATTRIBUTION, +) + +from .const import ( + DOMAIN, + DATA_CLIENT, + ATTRIBUTION, +) + +_LOGGER = logging.getLogger(__name__) + +ATTR_LABEL = "label" +ATTR_GROUP = "group" +ATTR_PATH = "data_path" +ATTR_ATTR = "data_attr" + +SENSOR_TYPES = { + 'system_fwupdate': { + ATTR_LABEL: 'Firmware update', + ATTR_GROUP: "System", + ATTR_PATH: "fw-update", + ATTR_ATTR: "available", + }, +} + + +# --------------------------- +# async_setup_entry +# --------------------------- +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up device tracker for Mikrotik Router component.""" + 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 + + +# --------------------------- +# update_items +# --------------------------- +@callback +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.""" + info = { + "identifiers": {(DOMAIN, "serial-number", self.mikrotik_controller.data['routerboard']['serial-number'], "switch", "PORT")}, + "manufacturer": self.mikrotik_controller.data['resource']['platform'], + "model": self.mikrotik_controller.data['resource']['board-name'], + "name": SENSOR_TYPES[self.kind][ATTR_GROUP], + } + return info + + async def async_update(self): + """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/mikrotik_controller.py b/custom_components/mikrotik_router/mikrotik_controller.py index 356f7d9..9a090ab 100644 --- a/custom_components/mikrotik_router/mikrotik_controller.py +++ b/custom_components/mikrotik_router/mikrotik_controller.py @@ -36,6 +36,7 @@ class MikrotikControllerData(): self.data['interface'] = {} self.data['arp'] = {} self.data['nat'] = {} + self.data['fw-update'] = {} self.listeners = [] @@ -44,6 +45,7 @@ class MikrotikControllerData(): self.api = None async_track_time_interval(self.hass, self.force_update, self.option_scan_interval) + async_track_time_interval(self.hass, self.async_fwupdate_check, timedelta(hours=1)) return @@ -96,6 +98,17 @@ class MikrotikControllerData(): self.get_system_resource() return + # --------------------------- + # async_fwupdate_check + # --------------------------- + async def async_fwupdate_check(self): + """Update Mikrotik Controller data.""" + + self.get_firmare_update() + + async_dispatcher_send(self.hass, self.signal_update) + return + # --------------------------- # async_update # --------------------------- @@ -103,6 +116,9 @@ class MikrotikControllerData(): async def async_update(self): """Update Mikrotik Controller data.""" + if 'available' not in self.data['fw-update']: + await self.async_fwupdate_check() + self.get_interfaces() self.get_arp() self.get_nat() @@ -322,3 +338,16 @@ class MikrotikControllerData(): self.data['resource']['hdd-usage'] = "unknown" return + + # --------------------------- + # get_system_routerboard + # --------------------------- + def get_firmare_update(self): + data = self.api.path("/system/package/update") + for entry in data: + self.data['fw-update']['available'] = True if entry['status'] == "New version is available" else False + 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