From 6b059489a62ac4c2008e7f79b1e65f1746c1f69d Mon Sep 17 00:00:00 2001 From: nielstron Date: Sun, 18 Mar 2018 17:26:33 +0100 Subject: [PATCH] Adding a discoverable Samsung Syncthru Printer sensor platform (#13134) * Added a simple component to support the BLNET Adds a component based on pyblnet, that hooks up the blnet to home assistant * Adds support for custimzation of blnet sensor devices * Setting up blnet as a platfrom * Updated use of state_attributes Now the friendly_name (and for digital values the mode) is set in the state_attributes whereas the name is defined as "blnet_(analog|digital)_{sensornumber}" so you can reliably add them to groups. * Added support for the SyncThru printer web service * Added pysyncthru to the requirements * Changed to Dependencis, import inside setup_platform * Switch back to REQUIREMENTS Looks like DEPENDENCIES is not meant for python packages but for other HA components * Fixed access to _attributes * Final fix * Several Bugfixes When the printer goes offline, the last state will be kept. Also now checks if the printer is reachable upon setup * Register syncthru as discoverable * Included possible conditions to monitor * Split the printer sensor in several seperate sensor entities * Fixed bug at sensor creation, pep8 conform * Bugfix * Bugfix * Removed Blnet components * Fixed unused import * Renamed discoverable to samsung_printer * Removed unused Attribute _friendly_name * Inserted missing space * Pinned requirements and added to coveragerc * Reduced redundancy by condensing into multiple sub-classes * Fixed indentation * Fixed super constructor calls * Fixed super constructor calls * Fixed format * Resolving style issues and using name instead of friendly_name * Pinned pysyncthru in requirements_all, having trouble with friendly_name * Iterating over dictionary instead of dict.keys() * ran gen_reqirements_all.py * Fixed flake 8 issues * Added a simple component to support the BLNET Adds a component based on pyblnet, that hooks up the blnet to home assistant * Implemented requested changes * raised dependecies to pysyncthru version that has timeouts * Raised required version for full timeout support * Adds support for custimzation of blnet sensor devices * Setting up blnet as a platfrom * Updated use of state_attributes Now the friendly_name (and for digital values the mode) is set in the state_attributes whereas the name is defined as "blnet_(analog|digital)_{sensornumber}" so you can reliably add them to groups. * Added support for the SyncThru printer web service * Added pysyncthru to the requirements * Removed Blnet components * Pinned requirements and added to coveragerc * Fixed indentation * Fixed format * Pinned pysyncthru in requirements_all, having trouble with friendly_name * ran gen_reqirements_all.py * Updated requirements_all * Renamed sensor objects, removed passing of hass entity * Removed merge artifacts * Reset syncthru to newest state * Updated requirements_all * switched to using the newest version of pysyncthru * Sorted coveragerc --- .coveragerc | 1 + homeassistant/components/discovery.py | 2 + homeassistant/components/sensor/syncthru.py | 233 ++++++++++++++++++++ requirements_all.txt | 3 + 4 files changed, 239 insertions(+) create mode 100644 homeassistant/components/sensor/syncthru.py diff --git a/.coveragerc b/.coveragerc index 5e1bbe67144..40fccd5e921 100644 --- a/.coveragerc +++ b/.coveragerc @@ -655,6 +655,7 @@ omit = homeassistant/components/sensor/supervisord.py homeassistant/components/sensor/swiss_hydrological_data.py homeassistant/components/sensor/swiss_public_transport.py + homeassistant/components/sensor/syncthru.py homeassistant/components/sensor/synologydsm.py homeassistant/components/sensor/systemmonitor.py homeassistant/components/sensor/sytadin.py diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 6ab7f42558b..eb53782d698 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -38,6 +38,7 @@ SERVICE_TELLDUSLIVE = 'tellstick' SERVICE_HUE = 'philips_hue' SERVICE_DECONZ = 'deconz' SERVICE_DAIKIN = 'daikin' +SERVICE_SAMSUNG_PRINTER = 'samsung_printer' SERVICE_HANDLERS = { SERVICE_HASS_IOS_APP: ('ios', None), @@ -53,6 +54,7 @@ SERVICE_HANDLERS = { SERVICE_HUE: ('hue', None), SERVICE_DECONZ: ('deconz', None), SERVICE_DAIKIN: ('daikin', None), + SERVICE_SAMSUNG_PRINTER: ('sensor', 'syncthru'), 'google_cast': ('media_player', 'cast'), 'panasonic_viera': ('media_player', 'panasonic_viera'), 'plex_mediaserver': ('media_player', 'plex'), diff --git a/homeassistant/components/sensor/syncthru.py b/homeassistant/components/sensor/syncthru.py new file mode 100644 index 00000000000..a24482bda01 --- /dev/null +++ b/homeassistant/components/sensor/syncthru.py @@ -0,0 +1,233 @@ +""" +Support for Samsung Printers with SyncThru web interface. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/sensor.syncthru/ +""" + +import logging +import voluptuous as vol + +from homeassistant.const import ( + CONF_RESOURCE, CONF_HOST, CONF_NAME, CONF_MONITORED_CONDITIONS) +from homeassistant.helpers.entity import Entity +import homeassistant.helpers.config_validation as cv +from homeassistant.components.sensor import PLATFORM_SCHEMA + +REQUIREMENTS = ['pysyncthru==0.3.1'] + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Samsung Printer' +DEFAULT_MONITORED_CONDITIONS = [ + 'toner_black', + 'toner_cyan', + 'toner_magenta', + 'toner_yellow', + 'drum_black', + 'drum_cyan', + 'drum_magenta', + 'drum_yellow', + 'tray_1', + 'tray_2', + 'tray_3', + 'tray_4', + 'tray_5', + 'output_tray_0', + 'output_tray_1', + 'output_tray_2', + 'output_tray_3', + 'output_tray_4', + 'output_tray_5', +] +COLORS = [ + 'black', + 'cyan', + 'magenta', + 'yellow' +] + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_RESOURCE): cv.url, + vol.Optional( + CONF_NAME, + default=DEFAULT_NAME + ): cv.string, + vol.Optional( + CONF_MONITORED_CONDITIONS, + default=DEFAULT_MONITORED_CONDITIONS + ): vol.All(cv.ensure_list, [vol.In(DEFAULT_MONITORED_CONDITIONS)]) +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the SyncThru component.""" + from pysyncthru import SyncThru, test_syncthru + + if discovery_info is not None: + host = discovery_info.get(CONF_HOST) + name = discovery_info.get(CONF_NAME, DEFAULT_NAME) + _LOGGER.debug("Discovered a new Samsung Printer: %s", discovery_info) + # Test if the discovered device actually is a syncthru printer + if not test_syncthru(host): + _LOGGER.error("No SyncThru Printer found at %s", host) + return + monitored = DEFAULT_MONITORED_CONDITIONS + else: + host = config.get(CONF_RESOURCE) + name = config.get(CONF_NAME) + monitored = config.get(CONF_MONITORED_CONDITIONS) + + # Main device, always added + try: + printer = SyncThru(host) + except TypeError: + # if an exception is thrown, printer cannot be set up + return + + printer.update() + devices = [SyncThruMainSensor(printer, name)] + + for key in printer.toner_status(filter_supported=True): + if 'toner_{}'.format(key) in monitored: + devices.append(SyncThruTonerSensor(printer, name, key)) + for key in printer.drum_status(filter_supported=True): + if 'drum_{}'.format(key) in monitored: + devices.append(SyncThruDrumSensor(printer, name, key)) + for key in printer.input_tray_status(filter_supported=True): + if 'tray_{}'.format(key) in monitored: + devices.append(SyncThruInputTraySensor(printer, name, key)) + for key in printer.output_tray_status(): + if 'output_tray_{}'.format(key) in monitored: + devices.append(SyncThruOutputTraySensor(printer, name, key)) + + add_devices(devices, True) + + +class SyncThruSensor(Entity): + """Implementation of an abstract Samsung Printer sensor platform.""" + + def __init__(self, syncthru, name): + """Initialize the sensor.""" + self.syncthru = syncthru + self._attributes = {} + self._state = None + self._name = name + self._icon = 'mdi:printer' + self._unit_of_measurement = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + return self._state + + @property + def icon(self): + """Return the icon of the device.""" + return self._icon + + @property + def unit_of_measurement(self): + """Return the unit of measuremnt.""" + return self._unit_of_measurement + + @property + def device_state_attributes(self): + """Return the state attributes of the device.""" + return self._attributes + + +class SyncThruMainSensor(SyncThruSensor): + """Implementation of the main sensor, monitoring the general state.""" + + def update(self): + """Get the latest data from SyncThru and update the state.""" + self.syncthru.update() + self._state = self.syncthru.device_status() + + +class SyncThruTonerSensor(SyncThruSensor): + """Implementation of a Samsung Printer toner sensor platform.""" + + def __init__(self, syncthru, name, color): + """Initialize the sensor.""" + super().__init__(syncthru, name) + self._name = "{} Toner {}".format(name, color) + self._color = color + self._unit_of_measurement = '%' + + def update(self): + """Get the latest data from SyncThru and update the state.""" + # Data fetching is taken care of through the Main sensor + + if self.syncthru.is_online(): + self._attributes = self.syncthru.toner_status( + ).get(self._color, {}) + self._state = self._attributes.get('remaining') + + +class SyncThruDrumSensor(SyncThruSensor): + """Implementation of a Samsung Printer toner sensor platform.""" + + def __init__(self, syncthru, name, color): + """Initialize the sensor.""" + super().__init__(syncthru, name) + self._name = "{} Drum {}".format(name, color) + self._color = color + self._unit_of_measurement = '%' + + def update(self): + """Get the latest data from SyncThru and update the state.""" + # Data fetching is taken care of through the Main sensor + + if self.syncthru.is_online(): + self._attributes = self.syncthru.drum_status( + ).get(self._color, {}) + self._state = self._attributes.get('remaining') + + +class SyncThruInputTraySensor(SyncThruSensor): + """Implementation of a Samsung Printer input tray sensor platform.""" + + def __init__(self, syncthru, name, number): + """Initialize the sensor.""" + super().__init__(syncthru, name) + self._name = "{} Tray {}".format(name, number) + self._number = number + + def update(self): + """Get the latest data from SyncThru and update the state.""" + # Data fetching is taken care of through the Main sensor + + if self.syncthru.is_online(): + self._attributes = self.syncthru.input_tray_status( + ).get(self._number, {}) + self._state = self._attributes.get('newError') + if self._state == '': + self._state = 'Ready' + + +class SyncThruOutputTraySensor(SyncThruSensor): + """Implementation of a Samsung Printer input tray sensor platform.""" + + def __init__(self, syncthru, name, number): + """Initialize the sensor.""" + super().__init__(syncthru, name) + self._name = "{} Output Tray {}".format(name, number) + self._number = number + + def update(self): + """Get the latest data from SyncThru and update the state.""" + # Data fetching is taken care of through the Main sensor + + if self.syncthru.is_online(): + self._attributes = self.syncthru.output_tray_status( + ).get(self._number, {}) + self._state = self._attributes.get('status') + if self._state == '': + self._state = 'Ready' diff --git a/requirements_all.txt b/requirements_all.txt index 37c7150c146..91173a1825c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -894,6 +894,9 @@ pysnmp==4.4.4 # homeassistant.components.notify.stride pystride==0.1.7 +# homeassistant.components.sensor.syncthru +pysyncthru==0.3.1 + # homeassistant.components.media_player.liveboxplaytv pyteleloisirs==3.3