From df6e569d3594d59aa7db7ee331eb4dc18b2c000b Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Thu, 15 Jun 2017 23:46:00 +0100 Subject: [PATCH 01/12] make port mapping optional --- homeassistant/components/upnp.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index e6ad66d0b51ab..2e7a13a2c2a5b 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -10,14 +10,20 @@ import voluptuous as vol from homeassistant.const import (EVENT_HOMEASSISTANT_STOP) +from homeassistant.helpers import config_validation as cv _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['api'] DOMAIN = 'upnp' +CONF_ENABLE_PORT_MAPPING = 'port_mapping' +DEFAULT_PORT_MAPPING = 'true' + CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({}), + DOMAIN: vol.Schema({ + vol.Optional(CONF_ENABLE_PORT_MAPPING, default=True): cv.boolean, + }), }, extra=vol.ALLOW_EXTRA) @@ -40,13 +46,15 @@ def setup(hass, config): host = base_url.hostname external_port = internal_port = base_url.port - upnp.addportmapping( - external_port, 'TCP', host, internal_port, 'Home Assistant', '') + port_mapping = config[DOMAIN].get(CONF_ENABLE_PORT_MAPPING, DEFAULT_PORT_MAPPING) + if port_mapping: + upnp.addportmapping( + external_port, 'TCP', host, internal_port, 'Home Assistant', '') - def deregister_port(event): - """De-register the UPnP port mapping.""" - upnp.deleteportmapping(hass.config.api.port, 'TCP') + def deregister_port(event): + """De-register the UPnP port mapping.""" + upnp.deleteportmapping(hass.config.api.port, 'TCP') - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, deregister_port) + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, deregister_port) return True From 4813a824584031a6d23b7c97d05054bc72052206 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 16 Jun 2017 16:55:49 +0100 Subject: [PATCH 02/12] dependencies + improvements --- homeassistant/components/upnp.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index 2e7a13a2c2a5b..ae7aa22ee2e5d 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -1,5 +1,6 @@ """ This module will attempt to open a port in your router for Home Assistant. +It will also keep the status of the IGD found. For more details about this component, please refer to the documentation at https://home-assistant.io/components/upnp/ @@ -12,13 +13,14 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers import config_validation as cv +REQUIREMENTS = ['miniupnpc'] + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['api'] DOMAIN = 'upnp' CONF_ENABLE_PORT_MAPPING = 'port_mapping' -DEFAULT_PORT_MAPPING = 'true' CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -38,6 +40,8 @@ def setup(hass, config): upnp.discover() try: upnp.selectigd() + hass.states.set(DOMAIN+".igd", upnp.statusinfo()[0]) + except Exception: _LOGGER.exception("Error when attempting to discover an UPnP IGD") return False @@ -46,7 +50,7 @@ def setup(hass, config): host = base_url.hostname external_port = internal_port = base_url.port - port_mapping = config[DOMAIN].get(CONF_ENABLE_PORT_MAPPING, DEFAULT_PORT_MAPPING) + port_mapping = config[DOMAIN].get(CONF_ENABLE_PORT_MAPPING) if port_mapping: upnp.addportmapping( external_port, 'TCP', host, internal_port, 'Home Assistant', '') From 49fba3b164721e24a3b47feefd8fb9f3a699b2b5 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 16 Jun 2017 23:16:28 +0100 Subject: [PATCH 03/12] Added bytes and packets sensors from IGD --- homeassistant/components/upnp.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index ae7aa22ee2e5d..20f5677841579 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/upnp/ """ +import asyncio import logging from urllib.parse import urlsplit @@ -12,6 +13,7 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import discovery REQUIREMENTS = ['miniupnpc'] @@ -20,38 +22,52 @@ DEPENDENCIES = ['api'] DOMAIN = 'upnp' +DATA_UPNP = 'UPNP' + CONF_ENABLE_PORT_MAPPING = 'port_mapping' +CONF_UNITS = 'unit' + +UNITS = { + "Bytes": 1, + "KBytes": 1024, + "MBytes": 1024**2, + "GBytes": 1024**3, +} CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Optional(CONF_ENABLE_PORT_MAPPING, default=True): cv.boolean, + vol.Optional(CONF_UNITS, default="MBytes"): vol.In(UNITS), }), }, extra=vol.ALLOW_EXTRA) # pylint: disable=import-error, no-member, broad-except -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Register a port mapping for Home Assistant via UPnP.""" import miniupnpc upnp = miniupnpc.UPnP() + hass.data[DATA_UPNP] = upnp upnp.discoverdelay = 200 upnp.discover() try: upnp.selectigd() - hass.states.set(DOMAIN+".igd", upnp.statusinfo()[0]) - except Exception: _LOGGER.exception("Error when attempting to discover an UPnP IGD") return False - base_url = urlsplit(hass.config.api.base_url) - host = base_url.hostname - external_port = internal_port = base_url.port + unit = config[DOMAIN].get(CONF_UNITS) + discovery.load_platform(hass, 'sensor', DOMAIN, {'unit': unit }, config) port_mapping = config[DOMAIN].get(CONF_ENABLE_PORT_MAPPING) if port_mapping: + base_url = urlsplit(hass.config.api.base_url) + host = base_url.hostname + external_port = internal_port = base_url.port + upnp.addportmapping( external_port, 'TCP', host, internal_port, 'Home Assistant', '') @@ -62,3 +78,4 @@ def deregister_port(event): hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, deregister_port) return True + From cec02acfd3844d5ccb5588e45f6d4bc5c2c5ca5e Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 16 Jun 2017 23:38:10 +0100 Subject: [PATCH 04/12] flake8 check --- homeassistant/components/upnp.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index 20f5677841579..727968cc6443f 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -60,10 +60,10 @@ def async_setup(hass, config): return False unit = config[DOMAIN].get(CONF_UNITS) - discovery.load_platform(hass, 'sensor', DOMAIN, {'unit': unit }, config) + discovery.load_platform(hass, 'sensor', DOMAIN, {'unit': unit}, config) port_mapping = config[DOMAIN].get(CONF_ENABLE_PORT_MAPPING) - if port_mapping: + if port_mapping: base_url = urlsplit(hass.config.api.base_url) host = base_url.hostname external_port = internal_port = base_url.port @@ -78,4 +78,3 @@ def deregister_port(event): hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, deregister_port) return True - From 60c89b4ec42023e9564f5eb47a8836617e4759bc Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 16 Jun 2017 23:40:26 +0100 Subject: [PATCH 05/12] new sensor with upnp counters --- homeassistant/components/sensor/upnp.py | 76 +++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 homeassistant/components/sensor/upnp.py diff --git a/homeassistant/components/sensor/upnp.py b/homeassistant/components/sensor/upnp.py new file mode 100644 index 0000000000000..e462e88cb2d84 --- /dev/null +++ b/homeassistant/components/sensor/upnp.py @@ -0,0 +1,76 @@ +""" +Support for UPnP Sensors (IGD). + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.upnp/ +""" +import asyncio +import logging + +from homeassistant.components.upnp import DATA_UPNP, UNITS +from homeassistant.helpers.entity import Entity + +_LOGGER = logging.getLogger(__name__) + +""" sensor_type: [friendly_name, convert_unit, icon] """ +SENSOR_TYPES = { + 'byte_received': ['received bytes', True, 'mdi:server-network'], + 'byte_sent': ['sent bytes', True, 'mdi:server-network'], + 'packets_in': ['packets received', False, 'mdi:server-network'], + 'packets_out': ['packets sent', False, 'mdi:server-network'], +} + + +@asyncio.coroutine +def async_setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the IGD sensors.""" + upnp = hass.data[DATA_UPNP] + unit = discovery_info['unit'] + add_devices([ + IGDSensor(upnp, t, unit if SENSOR_TYPES[t][1] else None) + for t in SENSOR_TYPES]) + + +class IGDSensor(Entity): + """Representation of a UPnP IGD sensor.""" + def __init__(self, upnp, sensor_type, unit=""): + self._upnp = upnp + self.type = sensor_type + self.unit = unit + self.unit_factor = UNITS[unit] if unit is not None else 1 + self._name = 'IGD {}'.format(SENSOR_TYPES[sensor_type][0]) + self._state = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + if self._state is None: + return None + return format(self._state / self.unit_factor, '.1f') + + @property + def icon(self): + """Icon to use in the frontend, if any.""" + return SENSOR_TYPES[self.type][2] + + @property + def unit_of_measurement(self): + """Return the unit of measurement of this entity, if any.""" + return self.unit + + @asyncio.coroutine + def async_update(self): + """Get the latest information from the IGD.""" + if self.type == "byte_received": + self._state = self._upnp.totalbytereceived() + elif self.type == "byte_sent": + self._state = self._upnp.totalbytesent() + elif self.type == "packets_in": + self._state = self._upnp.totalpacketreceived() + elif self.type == "packets_out": + self._state = self._upnp.totalpacketsent() From 985975d6272ad7bab2350daf4df666baa7d0f969 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 16 Jun 2017 23:51:34 +0100 Subject: [PATCH 06/12] checks --- homeassistant/components/sensor/upnp.py | 2 ++ homeassistant/components/upnp.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/upnp.py b/homeassistant/components/sensor/upnp.py index e462e88cb2d84..694b8935e6ece 100644 --- a/homeassistant/components/sensor/upnp.py +++ b/homeassistant/components/sensor/upnp.py @@ -33,7 +33,9 @@ def async_setup_platform(hass, config, add_devices, discovery_info=None): class IGDSensor(Entity): """Representation of a UPnP IGD sensor.""" + def __init__(self, upnp, sensor_type, unit=""): + """Initialize the IGD sensor.""" self._upnp = upnp self.type = sensor_type self.unit = unit diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index 727968cc6443f..76297f43bd587 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -1,6 +1,5 @@ """ -This module will attempt to open a port in your router for Home Assistant. -It will also keep the status of the IGD found. +Will open a port in your router for Home Assistant and provide statistics. For more details about this component, please refer to the documentation at https://home-assistant.io/components/upnp/ From 49225addf2d8ec510f16cb657d4ed0592c6e4e10 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 16 Jun 2017 23:59:05 +0100 Subject: [PATCH 07/12] whitespaces in blank line --- homeassistant/components/sensor/upnp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/upnp.py b/homeassistant/components/sensor/upnp.py index 694b8935e6ece..0605713798bd1 100644 --- a/homeassistant/components/sensor/upnp.py +++ b/homeassistant/components/sensor/upnp.py @@ -33,7 +33,7 @@ def async_setup_platform(hass, config, add_devices, discovery_info=None): class IGDSensor(Entity): """Representation of a UPnP IGD sensor.""" - + def __init__(self, upnp, sensor_type, unit=""): """Initialize the IGD sensor.""" self._upnp = upnp From e12ac4e71dea624f8332ec35ef8cda2996ef862f Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Sat, 17 Jun 2017 17:29:26 +0100 Subject: [PATCH 08/12] requirements update --- homeassistant/components/sensor/upnp.py | 2 +- homeassistant/components/upnp.py | 2 +- requirements_all.txt | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/upnp.py b/homeassistant/components/sensor/upnp.py index 0605713798bd1..91b340fe764d8 100644 --- a/homeassistant/components/sensor/upnp.py +++ b/homeassistant/components/sensor/upnp.py @@ -28,7 +28,7 @@ def async_setup_platform(hass, config, add_devices, discovery_info=None): unit = discovery_info['unit'] add_devices([ IGDSensor(upnp, t, unit if SENSOR_TYPES[t][1] else None) - for t in SENSOR_TYPES]) + for t in SENSOR_TYPES], True) class IGDSensor(Entity): diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index 76297f43bd587..72d85bf8accd6 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -14,7 +14,7 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers import discovery -REQUIREMENTS = ['miniupnpc'] +REQUIREMENTS = ['miniupnpc==2.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 77719422c0ba1..c31b2e81d54ac 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -378,6 +378,9 @@ mficlient==0.3.0 # homeassistant.components.sensor.miflora miflora==0.1.16 +# homeassistant.components.upnp +miniupnpc==2.0 + # homeassistant.components.tts mutagen==1.37.0 From 9a4f39583b729efe00013fe784bc0f81cb547d50 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Sat, 17 Jun 2017 17:31:22 +0100 Subject: [PATCH 09/12] added sensor.upnp to .coveragerc --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 9ec3d70699075..26b87e37f0117 100644 --- a/.coveragerc +++ b/.coveragerc @@ -470,6 +470,7 @@ omit = homeassistant/components/sensor/transmission.py homeassistant/components/sensor/twitch.py homeassistant/components/sensor/uber.py + homeassistant/components/sensor/upnp.py homeassistant/components/sensor/ups.py homeassistant/components/sensor/usps.py homeassistant/components/sensor/vasttrafik.py From 5fc3712b60207a6a3afaf25f9da6569ec3592411 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Sat, 17 Jun 2017 20:05:47 +0100 Subject: [PATCH 10/12] downgrade miniupnpc Latest version of miniupnpc is 2.0, but pypi only has 1.9 Fortunately it is enough --- homeassistant/components/upnp.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index 72d85bf8accd6..df00da5be59c4 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -14,7 +14,7 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers import discovery -REQUIREMENTS = ['miniupnpc==2.0'] +REQUIREMENTS = ['miniupnpc==1.9'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index c31b2e81d54ac..219e68a67565c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -379,7 +379,7 @@ mficlient==0.3.0 miflora==0.1.16 # homeassistant.components.upnp -miniupnpc==2.0 +miniupnpc==1.9 # homeassistant.components.tts mutagen==1.37.0 From c127fcc546d0b511ca9812507d73ed3097ec32fd Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Sun, 18 Jun 2017 22:17:29 +0100 Subject: [PATCH 11/12] revert to non async MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit miniupnpc will do network calls, so this component can’t be moved to coroutine --- homeassistant/components/sensor/upnp.py | 8 +++----- homeassistant/components/upnp.py | 26 ++++++++++++------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/sensor/upnp.py b/homeassistant/components/sensor/upnp.py index 91b340fe764d8..feb73dbf9b82d 100644 --- a/homeassistant/components/sensor/upnp.py +++ b/homeassistant/components/sensor/upnp.py @@ -12,7 +12,7 @@ _LOGGER = logging.getLogger(__name__) -""" sensor_type: [friendly_name, convert_unit, icon] """ +# sensor_type: [friendly_name, convert_unit, icon] SENSOR_TYPES = { 'byte_received': ['received bytes', True, 'mdi:server-network'], 'byte_sent': ['sent bytes', True, 'mdi:server-network'], @@ -21,8 +21,7 @@ } -@asyncio.coroutine -def async_setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the IGD sensors.""" upnp = hass.data[DATA_UPNP] unit = discovery_info['unit'] @@ -65,8 +64,7 @@ def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" return self.unit - @asyncio.coroutine - def async_update(self): + def update(self): """Get the latest information from the IGD.""" if self.type == "byte_received": self._state = self._upnp.totalbytereceived() diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index df00da5be59c4..a058fdae85eda 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -4,7 +4,6 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/upnp/ """ -import asyncio import logging from urllib.parse import urlsplit @@ -42,8 +41,7 @@ # pylint: disable=import-error, no-member, broad-except -@asyncio.coroutine -def async_setup(hass, config): +def setup(hass, config): """Register a port mapping for Home Assistant via UPnP.""" import miniupnpc @@ -62,18 +60,20 @@ def async_setup(hass, config): discovery.load_platform(hass, 'sensor', DOMAIN, {'unit': unit}, config) port_mapping = config[DOMAIN].get(CONF_ENABLE_PORT_MAPPING) - if port_mapping: - base_url = urlsplit(hass.config.api.base_url) - host = base_url.hostname - external_port = internal_port = base_url.port + if not port_mapping: + return True - upnp.addportmapping( - external_port, 'TCP', host, internal_port, 'Home Assistant', '') + base_url = urlsplit(hass.config.api.base_url) + host = base_url.hostname + external_port = internal_port = base_url.port - def deregister_port(event): - """De-register the UPnP port mapping.""" - upnp.deleteportmapping(hass.config.api.port, 'TCP') + upnp.addportmapping( + external_port, 'TCP', host, internal_port, 'Home Assistant', '') - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, deregister_port) + def deregister_port(event): + """De-register the UPnP port mapping.""" + upnp.deleteportmapping(hass.config.api.port, 'TCP') + + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, deregister_port) return True From 03c4507c93b9ec6adedd9022366670de09c0e3ce Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Sun, 18 Jun 2017 22:18:22 +0100 Subject: [PATCH 12/12] hof hof forgot to remove import ot asyncio --- homeassistant/components/sensor/upnp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/sensor/upnp.py b/homeassistant/components/sensor/upnp.py index feb73dbf9b82d..e5acae6791655 100644 --- a/homeassistant/components/sensor/upnp.py +++ b/homeassistant/components/sensor/upnp.py @@ -4,7 +4,6 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.upnp/ """ -import asyncio import logging from homeassistant.components.upnp import DATA_UPNP, UNITS