Disable creating port mappings from UI, add discovery from component (#18565)
* Disable creating port mappings from UI, add discovery from component * Remove unused constant * Upgrade to async_upnp_client==0.13.6 and use manufacturer from device * Upgrade to async_upnp_client==0.13.7
This commit is contained in:
parent
5efc61feaf
commit
501b3f9927
12 changed files with 195 additions and 653 deletions
|
@ -44,6 +44,8 @@ SERVICE_SABNZBD = 'sabnzbd'
|
||||||
SERVICE_SAMSUNG_PRINTER = 'samsung_printer'
|
SERVICE_SAMSUNG_PRINTER = 'samsung_printer'
|
||||||
SERVICE_HOMEKIT = 'homekit'
|
SERVICE_HOMEKIT = 'homekit'
|
||||||
SERVICE_OCTOPRINT = 'octoprint'
|
SERVICE_OCTOPRINT = 'octoprint'
|
||||||
|
SERVICE_IGD = 'igd'
|
||||||
|
SERVICE_DLNA_DMR = 'dlna_dmr'
|
||||||
|
|
||||||
CONFIG_ENTRY_HANDLERS = {
|
CONFIG_ENTRY_HANDLERS = {
|
||||||
SERVICE_DAIKIN: 'daikin',
|
SERVICE_DAIKIN: 'daikin',
|
||||||
|
@ -53,6 +55,7 @@ CONFIG_ENTRY_HANDLERS = {
|
||||||
SERVICE_TELLDUSLIVE: 'tellduslive',
|
SERVICE_TELLDUSLIVE: 'tellduslive',
|
||||||
SERVICE_IKEA_TRADFRI: 'tradfri',
|
SERVICE_IKEA_TRADFRI: 'tradfri',
|
||||||
'sonos': 'sonos',
|
'sonos': 'sonos',
|
||||||
|
SERVICE_IGD: 'upnp',
|
||||||
}
|
}
|
||||||
|
|
||||||
SERVICE_HANDLERS = {
|
SERVICE_HANDLERS = {
|
||||||
|
@ -92,7 +95,7 @@ SERVICE_HANDLERS = {
|
||||||
|
|
||||||
OPTIONAL_SERVICE_HANDLERS = {
|
OPTIONAL_SERVICE_HANDLERS = {
|
||||||
SERVICE_HOMEKIT: ('homekit_controller', None),
|
SERVICE_HOMEKIT: ('homekit_controller', None),
|
||||||
'dlna_dmr': ('media_player', 'dlna_dmr'),
|
SERVICE_DLNA_DMR: ('media_player', 'dlna_dmr'),
|
||||||
}
|
}
|
||||||
|
|
||||||
CONF_IGNORE = 'ignore'
|
CONF_IGNORE = 'ignore'
|
||||||
|
|
|
@ -26,7 +26,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util import get_local_ip
|
from homeassistant.util import get_local_ip
|
||||||
|
|
||||||
REQUIREMENTS = ['async-upnp-client==0.13.2']
|
REQUIREMENTS = ['async-upnp-client==0.13.7']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import logging
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.components.upnp.const import DOMAIN as DATA_UPNP
|
from homeassistant.components.upnp.const import DOMAIN as DOMAIN_UPNP
|
||||||
from homeassistant.components.upnp.const import SIGNAL_REMOVE_SENSOR
|
from homeassistant.components.upnp.const import SIGNAL_REMOVE_SENSOR
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,8 +73,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
async_add_entities(sensors, True)
|
async_add_entities(sensors, True)
|
||||||
|
|
||||||
data = config_entry.data
|
data = config_entry.data
|
||||||
udn = data['udn']
|
if 'udn' in data:
|
||||||
device = hass.data[DATA_UPNP]['devices'][udn]
|
udn = data['udn']
|
||||||
|
else:
|
||||||
|
# any device will do
|
||||||
|
udn = list(hass.data[DOMAIN_UPNP]['devices'].keys())[0]
|
||||||
|
|
||||||
|
device = hass.data[DOMAIN_UPNP]['devices'][udn]
|
||||||
async_add_sensor(device)
|
async_add_sensor(device)
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,6 +105,17 @@ class UpnpSensor(Entity):
|
||||||
|
|
||||||
self.hass.async_create_task(self.async_remove())
|
self.hass.async_create_task(self.async_remove())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self):
|
||||||
|
"""Get device info."""
|
||||||
|
return {
|
||||||
|
'identifiers': {
|
||||||
|
(DOMAIN_UPNP, self.unique_id)
|
||||||
|
},
|
||||||
|
'name': self.name,
|
||||||
|
'via_hub': (DOMAIN_UPNP, self._device.udn),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RawUPnPIGDSensor(UpnpSensor):
|
class RawUPnPIGDSensor(UpnpSensor):
|
||||||
"""Representation of a UPnP/IGD sensor."""
|
"""Representation of a UPnP/IGD sensor."""
|
||||||
|
|
|
@ -4,32 +4,31 @@ 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
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/upnp/
|
https://home-assistant.io/components/upnp/
|
||||||
"""
|
"""
|
||||||
import asyncio
|
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||||
|
from homeassistant.helpers import config_entry_flow
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers import dispatcher
|
from homeassistant.helpers import dispatcher
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.helpers.typing import HomeAssistantType
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
from homeassistant.util import get_local_ip
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_ENABLE_PORT_MAPPING, CONF_ENABLE_SENSORS,
|
CONF_ENABLE_PORT_MAPPING, CONF_ENABLE_SENSORS,
|
||||||
CONF_HASS, CONF_LOCAL_IP, CONF_PORTS,
|
CONF_HASS, CONF_LOCAL_IP, CONF_PORTS,
|
||||||
CONF_UDN, CONF_SSDP_DESCRIPTION,
|
|
||||||
SIGNAL_REMOVE_SENSOR,
|
SIGNAL_REMOVE_SENSOR,
|
||||||
)
|
)
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .const import LOGGER as _LOGGER
|
from .const import LOGGER as _LOGGER
|
||||||
from .config_flow import async_ensure_domain_data
|
|
||||||
from .device import Device
|
from .device import Device
|
||||||
|
|
||||||
|
REQUIREMENTS = ['async-upnp-client==0.13.7']
|
||||||
REQUIREMENTS = ['async-upnp-client==0.13.2']
|
|
||||||
|
|
||||||
NOTIFICATION_ID = 'upnp_notification'
|
NOTIFICATION_ID = 'upnp_notification'
|
||||||
NOTIFICATION_TITLE = 'UPnP/IGD Setup'
|
NOTIFICATION_TITLE = 'UPnP/IGD Setup'
|
||||||
|
@ -83,78 +82,111 @@ def _substitute_hass_ports(ports, hass_port=None):
|
||||||
return ports
|
return ports
|
||||||
|
|
||||||
|
|
||||||
# config
|
async def async_discover_and_construct(hass, udn=None) -> Device:
|
||||||
|
"""Discovery devices and construct a Device for one."""
|
||||||
|
discovery_infos = await Device.async_discover(hass)
|
||||||
|
if not discovery_infos:
|
||||||
|
_LOGGER.info('No UPnP/IGD devices discovered')
|
||||||
|
return None
|
||||||
|
|
||||||
|
if udn:
|
||||||
|
# get the discovery info with specified UDN
|
||||||
|
filtered = [di for di in discovery_infos if di['udn'] == udn]
|
||||||
|
if not filtered:
|
||||||
|
_LOGGER.warning('Wanted UPnP/IGD device with UDN "%s" not found, '
|
||||||
|
'aborting', udn)
|
||||||
|
return None
|
||||||
|
discovery_info = filtered[0]
|
||||||
|
else:
|
||||||
|
# get the first/any
|
||||||
|
discovery_info = discovery_infos[0]
|
||||||
|
if len(discovery_infos) > 1:
|
||||||
|
_LOGGER.info('Detected multiple UPnP/IGD devices, using: %s',
|
||||||
|
discovery_info['igd_name'])
|
||||||
|
|
||||||
|
ssdp_description = discovery_info['ssdp_description']
|
||||||
|
return await Device.async_create_device(hass, ssdp_description)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistantType, config: ConfigType):
|
async def async_setup(hass: HomeAssistantType, config: ConfigType):
|
||||||
"""Register a port mapping for Home Assistant via UPnP."""
|
"""Set up UPnP component."""
|
||||||
await async_ensure_domain_data(hass)
|
conf_default = CONFIG_SCHEMA({DOMAIN: {}})[DOMAIN]
|
||||||
|
conf = config.get(DOMAIN, conf_default)
|
||||||
# ensure sane config
|
local_ip = await hass.async_add_executor_job(get_local_ip)
|
||||||
if DOMAIN not in config:
|
hass.data[DOMAIN] = {
|
||||||
return True
|
'config': conf,
|
||||||
upnp_config = config[DOMAIN]
|
'devices': {},
|
||||||
|
'local_ip': config.get(CONF_LOCAL_IP, local_ip),
|
||||||
# overridden local ip
|
'ports': conf.get('ports', {}),
|
||||||
if CONF_LOCAL_IP in upnp_config:
|
|
||||||
hass.data[DOMAIN]['local_ip'] = upnp_config[CONF_LOCAL_IP]
|
|
||||||
|
|
||||||
# determine ports
|
|
||||||
ports = {CONF_HASS: CONF_HASS} # default, port_mapping disabled by default
|
|
||||||
if CONF_PORTS in upnp_config:
|
|
||||||
# copy from config
|
|
||||||
ports = upnp_config[CONF_PORTS]
|
|
||||||
|
|
||||||
hass.data[DOMAIN]['auto_config'] = {
|
|
||||||
'active': True,
|
|
||||||
'enable_sensors': upnp_config[CONF_ENABLE_SENSORS],
|
|
||||||
'enable_port_mapping': upnp_config[CONF_ENABLE_PORT_MAPPING],
|
|
||||||
'ports': ports,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf is not None:
|
||||||
|
hass.async_create_task(hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={'source': config_entries.SOURCE_IMPORT}))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# config flow
|
|
||||||
async def async_setup_entry(hass: HomeAssistantType,
|
async def async_setup_entry(hass: HomeAssistantType,
|
||||||
config_entry: ConfigEntry):
|
config_entry: ConfigEntry):
|
||||||
"""Set up UPnP/IGD-device from a config entry."""
|
"""Set up UPnP/IGD device from a config entry."""
|
||||||
await async_ensure_domain_data(hass)
|
domain_data = hass.data[DOMAIN]
|
||||||
data = config_entry.data
|
conf = domain_data['config']
|
||||||
|
|
||||||
# build UPnP/IGD device
|
# discover and construct
|
||||||
ssdp_description = data[CONF_SSDP_DESCRIPTION]
|
device = await async_discover_and_construct(hass,
|
||||||
try:
|
config_entry.data.get('udn'))
|
||||||
device = await Device.async_create_device(hass, ssdp_description)
|
if not device:
|
||||||
except (asyncio.TimeoutError, aiohttp.ClientError):
|
_LOGGER.info('Unable to create UPnP/IGD, aborting')
|
||||||
_LOGGER.error('Unable to create upnp-device')
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# 'register'/save UDN
|
||||||
|
config_entry.data['udn'] = device.udn
|
||||||
hass.data[DOMAIN]['devices'][device.udn] = device
|
hass.data[DOMAIN]['devices'][device.udn] = device
|
||||||
|
hass.config_entries.async_update_entry(entry=config_entry,
|
||||||
|
data=config_entry.data)
|
||||||
|
|
||||||
# port mapping
|
# create device registry entry
|
||||||
if data.get(CONF_ENABLE_PORT_MAPPING):
|
device_registry = await dr.async_get_registry(hass)
|
||||||
local_ip = hass.data[DOMAIN]['local_ip']
|
device_registry.async_get_or_create(
|
||||||
ports = hass.data[DOMAIN]['auto_config']['ports']
|
config_entry_id=config_entry.entry_id,
|
||||||
_LOGGER.debug('Enabling port mappings: %s', ports)
|
connections={
|
||||||
|
(dr.CONNECTION_UPNP, device.udn)
|
||||||
|
},
|
||||||
|
identifiers={
|
||||||
|
(DOMAIN, device.udn)
|
||||||
|
},
|
||||||
|
name=device.name,
|
||||||
|
manufacturer=device.manufacturer,
|
||||||
|
)
|
||||||
|
|
||||||
hass_port = None
|
# set up sensors
|
||||||
if hasattr(hass, 'http'):
|
if conf.get(CONF_ENABLE_SENSORS):
|
||||||
hass_port = hass.http.server_port
|
|
||||||
ports = _substitute_hass_ports(ports, hass_port=hass_port)
|
|
||||||
await device.async_add_port_mappings(ports, local_ip)
|
|
||||||
|
|
||||||
# sensors
|
|
||||||
if data.get(CONF_ENABLE_SENSORS):
|
|
||||||
_LOGGER.debug('Enabling sensors')
|
_LOGGER.debug('Enabling sensors')
|
||||||
|
|
||||||
# register sensor setup handlers
|
# register sensor setup handlers
|
||||||
hass.async_create_task(hass.config_entries.async_forward_entry_setup(
|
hass.async_create_task(hass.config_entries.async_forward_entry_setup(
|
||||||
config_entry, 'sensor'))
|
config_entry, 'sensor'))
|
||||||
|
|
||||||
|
# set up port mapping
|
||||||
|
if conf.get(CONF_ENABLE_PORT_MAPPING):
|
||||||
|
_LOGGER.debug('Enabling port mapping')
|
||||||
|
local_ip = domain_data['local_ip']
|
||||||
|
ports = conf.get('ports', {})
|
||||||
|
|
||||||
|
hass_port = None
|
||||||
|
if hasattr(hass, 'http'):
|
||||||
|
hass_port = hass.http.server_port
|
||||||
|
|
||||||
|
ports = _substitute_hass_ports(ports, hass_port=hass_port)
|
||||||
|
await device.async_add_port_mappings(ports, local_ip)
|
||||||
|
|
||||||
|
# set up port mapping deletion on stop-hook
|
||||||
async def delete_port_mapping(event):
|
async def delete_port_mapping(event):
|
||||||
"""Delete port mapping on quit."""
|
"""Delete port mapping on quit."""
|
||||||
if data.get(CONF_ENABLE_PORT_MAPPING):
|
_LOGGER.debug('Deleting port mappings')
|
||||||
_LOGGER.debug('Deleting port mappings')
|
await device.async_delete_port_mappings()
|
||||||
await device.async_delete_port_mappings()
|
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, delete_port_mapping)
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, delete_port_mapping)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -162,25 +194,23 @@ async def async_setup_entry(hass: HomeAssistantType,
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistantType,
|
async def async_unload_entry(hass: HomeAssistantType,
|
||||||
config_entry: ConfigEntry):
|
config_entry: ConfigEntry):
|
||||||
"""Unload a config entry."""
|
"""Unload a UPnP/IGD device from a config entry."""
|
||||||
data = config_entry.data
|
udn = config_entry.data['udn']
|
||||||
udn = data[CONF_UDN]
|
|
||||||
|
|
||||||
if udn not in hass.data[DOMAIN]['devices']:
|
|
||||||
return True
|
|
||||||
device = hass.data[DOMAIN]['devices'][udn]
|
device = hass.data[DOMAIN]['devices'][udn]
|
||||||
|
|
||||||
# port mapping
|
# remove port mapping
|
||||||
if data.get(CONF_ENABLE_PORT_MAPPING):
|
_LOGGER.debug('Deleting port mappings')
|
||||||
_LOGGER.debug('Deleting port mappings')
|
await device.async_delete_port_mappings()
|
||||||
await device.async_delete_port_mappings()
|
|
||||||
|
|
||||||
# sensors
|
# remove sensors
|
||||||
if data.get(CONF_ENABLE_SENSORS):
|
_LOGGER.debug('Deleting sensors')
|
||||||
_LOGGER.debug('Deleting sensors')
|
dispatcher.async_dispatcher_send(hass, SIGNAL_REMOVE_SENSOR, device)
|
||||||
dispatcher.async_dispatcher_send(hass, SIGNAL_REMOVE_SENSOR, device)
|
|
||||||
|
|
||||||
# clear stored device
|
|
||||||
del hass.data[DOMAIN]['devices'][udn]
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
config_entry_flow.register_discovery_flow(
|
||||||
|
DOMAIN,
|
||||||
|
'UPnP/IGD',
|
||||||
|
Device.async_discover,
|
||||||
|
config_entries.CONN_CLASS_LOCAL_POLL)
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
"""Config flow for UPNP."""
|
|
||||||
import logging
|
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant import data_entry_flow
|
|
||||||
from homeassistant.util import get_local_ip
|
|
||||||
|
|
||||||
from .const import (
|
|
||||||
CONF_ENABLE_PORT_MAPPING, CONF_ENABLE_SENSORS,
|
|
||||||
CONF_SSDP_DESCRIPTION, CONF_UDN
|
|
||||||
)
|
|
||||||
from .const import DOMAIN
|
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_ensure_domain_data(hass):
|
|
||||||
"""Ensure hass.data is filled properly."""
|
|
||||||
hass.data[DOMAIN] = hass.data.get(DOMAIN, {})
|
|
||||||
hass.data[DOMAIN]['devices'] = hass.data[DOMAIN].get('devices', {})
|
|
||||||
hass.data[DOMAIN]['discovered'] = hass.data[DOMAIN].get('discovered', {})
|
|
||||||
hass.data[DOMAIN]['auto_config'] = hass.data[DOMAIN].get('auto_config', {
|
|
||||||
'active': False,
|
|
||||||
'enable_sensors': False,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
'ports': {'hass': 'hass'},
|
|
||||||
})
|
|
||||||
if 'local_ip' not in hass.data[DOMAIN]:
|
|
||||||
hass.data[DOMAIN]['local_ip'] = \
|
|
||||||
await hass.async_add_executor_job(get_local_ip)
|
|
||||||
|
|
||||||
|
|
||||||
@config_entries.HANDLERS.register(DOMAIN)
|
|
||||||
class UpnpFlowHandler(data_entry_flow.FlowHandler):
|
|
||||||
"""Handle a UPnP/IGD config flow."""
|
|
||||||
|
|
||||||
VERSION = 1
|
|
||||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _configured_upnp_igds(self):
|
|
||||||
"""Get all configured IGDs."""
|
|
||||||
return {
|
|
||||||
entry.data[CONF_UDN]: {
|
|
||||||
'udn': entry.data[CONF_UDN],
|
|
||||||
}
|
|
||||||
for entry in self.hass.config_entries.async_entries(DOMAIN)
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _discovered_upnp_igds(self):
|
|
||||||
"""Get all discovered entries."""
|
|
||||||
return self.hass.data[DOMAIN]['discovered']
|
|
||||||
|
|
||||||
def _store_discovery_info(self, discovery_info):
|
|
||||||
"""Add discovery info."""
|
|
||||||
udn = discovery_info['udn']
|
|
||||||
self.hass.data[DOMAIN]['discovered'][udn] = discovery_info
|
|
||||||
|
|
||||||
def _auto_config_settings(self):
|
|
||||||
"""Check if auto_config has been enabled."""
|
|
||||||
return self.hass.data[DOMAIN]['auto_config']
|
|
||||||
|
|
||||||
async def async_step_discovery(self, discovery_info):
|
|
||||||
"""
|
|
||||||
Handle a discovered UPnP/IGD.
|
|
||||||
|
|
||||||
This flow is triggered by the discovery component. It will check if the
|
|
||||||
host is already configured and delegate to the import step if not.
|
|
||||||
"""
|
|
||||||
await async_ensure_domain_data(self.hass)
|
|
||||||
|
|
||||||
if not discovery_info.get('udn') or not discovery_info.get('host'):
|
|
||||||
# Silently ignore incomplete/broken devices to prevent constant
|
|
||||||
# errors/warnings
|
|
||||||
_LOGGER.debug('UPnP device is missing the udn. Provided info: %r',
|
|
||||||
discovery_info)
|
|
||||||
return self.async_abort(reason='incomplete_device')
|
|
||||||
|
|
||||||
# store discovered device
|
|
||||||
discovery_info['friendly_name'] = discovery_info.get('host', '')
|
|
||||||
|
|
||||||
# add name if available
|
|
||||||
if discovery_info.get('name'):
|
|
||||||
discovery_info['friendly_name'] += ' ({name})'.format(
|
|
||||||
**discovery_info)
|
|
||||||
|
|
||||||
self._store_discovery_info(discovery_info)
|
|
||||||
|
|
||||||
# ensure not already discovered/configured
|
|
||||||
if discovery_info.get('udn') in self._configured_upnp_igds:
|
|
||||||
return self.async_abort(reason='already_configured')
|
|
||||||
|
|
||||||
# auto config?
|
|
||||||
auto_config = self._auto_config_settings()
|
|
||||||
if auto_config['active']:
|
|
||||||
import_info = {
|
|
||||||
'name': discovery_info['friendly_name'],
|
|
||||||
'enable_sensors': auto_config['enable_sensors'],
|
|
||||||
'enable_port_mapping': auto_config['enable_port_mapping'],
|
|
||||||
}
|
|
||||||
|
|
||||||
return await self._async_save_entry(import_info)
|
|
||||||
|
|
||||||
return await self.async_step_user()
|
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
|
||||||
"""Manual set up."""
|
|
||||||
await async_ensure_domain_data(self.hass)
|
|
||||||
|
|
||||||
# if user input given, handle it
|
|
||||||
user_input = user_input or {}
|
|
||||||
if 'name' in user_input:
|
|
||||||
if not user_input['enable_sensors'] and \
|
|
||||||
not user_input['enable_port_mapping']:
|
|
||||||
return self.async_abort(reason='no_sensors_or_port_mapping')
|
|
||||||
|
|
||||||
# ensure not already configured
|
|
||||||
configured_names = [
|
|
||||||
entry['friendly_name']
|
|
||||||
for udn, entry in self._discovered_upnp_igds.items()
|
|
||||||
if udn in self._configured_upnp_igds
|
|
||||||
]
|
|
||||||
if user_input['name'] in configured_names:
|
|
||||||
return self.async_abort(reason='already_configured')
|
|
||||||
|
|
||||||
return await self._async_save_entry(user_input)
|
|
||||||
|
|
||||||
# let user choose from all discovered, non-configured, UPnP/IGDs
|
|
||||||
names = [
|
|
||||||
entry['friendly_name']
|
|
||||||
for udn, entry in self._discovered_upnp_igds.items()
|
|
||||||
if udn not in self._configured_upnp_igds
|
|
||||||
]
|
|
||||||
if not names:
|
|
||||||
return self.async_abort(reason='no_devices_discovered')
|
|
||||||
|
|
||||||
return self.async_show_form(
|
|
||||||
step_id='user',
|
|
||||||
data_schema=vol.Schema(
|
|
||||||
OrderedDict([
|
|
||||||
(vol.Required('name'), vol.In(names)),
|
|
||||||
(vol.Optional('enable_sensors', default=False), bool),
|
|
||||||
(vol.Optional('enable_port_mapping', default=False), bool),
|
|
||||||
])
|
|
||||||
))
|
|
||||||
|
|
||||||
async def async_step_import(self, import_info):
|
|
||||||
"""Import a new UPnP/IGD as a config entry."""
|
|
||||||
await async_ensure_domain_data(self.hass)
|
|
||||||
|
|
||||||
return await self._async_save_entry(import_info)
|
|
||||||
|
|
||||||
async def _async_save_entry(self, import_info):
|
|
||||||
"""Store UPNP/IGD as new entry."""
|
|
||||||
await async_ensure_domain_data(self.hass)
|
|
||||||
|
|
||||||
# ensure we know the host
|
|
||||||
name = import_info['name']
|
|
||||||
discovery_infos = [info
|
|
||||||
for info in self._discovered_upnp_igds.values()
|
|
||||||
if info['friendly_name'] == name]
|
|
||||||
if not discovery_infos:
|
|
||||||
return self.async_abort(reason='host_not_found')
|
|
||||||
discovery_info = discovery_infos[0]
|
|
||||||
|
|
||||||
return self.async_create_entry(
|
|
||||||
title=discovery_info['name'],
|
|
||||||
data={
|
|
||||||
CONF_SSDP_DESCRIPTION: discovery_info['ssdp_description'],
|
|
||||||
CONF_UDN: discovery_info['udn'],
|
|
||||||
CONF_ENABLE_SENSORS: import_info['enable_sensors'],
|
|
||||||
CONF_ENABLE_PORT_MAPPING: import_info['enable_port_mapping'],
|
|
||||||
},
|
|
||||||
)
|
|
|
@ -7,8 +7,6 @@ CONF_ENABLE_SENSORS = 'sensors'
|
||||||
CONF_HASS = 'hass'
|
CONF_HASS = 'hass'
|
||||||
CONF_LOCAL_IP = 'local_ip'
|
CONF_LOCAL_IP = 'local_ip'
|
||||||
CONF_PORTS = 'ports'
|
CONF_PORTS = 'ports'
|
||||||
CONF_SSDP_DESCRIPTION = 'ssdp_description'
|
|
||||||
CONF_UDN = 'udn'
|
|
||||||
DOMAIN = 'upnp'
|
DOMAIN = 'upnp'
|
||||||
LOGGER = logging.getLogger('homeassistant.components.upnp')
|
LOGGER = logging.getLogger('homeassistant.components.upnp')
|
||||||
SIGNAL_REMOVE_SENSOR = 'upnp_remove_sensor'
|
SIGNAL_REMOVE_SENSOR = 'upnp_remove_sensor'
|
||||||
|
|
|
@ -18,6 +18,27 @@ class Device:
|
||||||
self._igd_device = igd_device
|
self._igd_device = igd_device
|
||||||
self._mapped_ports = []
|
self._mapped_ports = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def async_discover(cls, hass: HomeAssistantType):
|
||||||
|
"""Discovery UPNP/IGD devices."""
|
||||||
|
_LOGGER.debug('Discovering UPnP/IGD devices')
|
||||||
|
|
||||||
|
# discover devices
|
||||||
|
from async_upnp_client.igd import IgdDevice
|
||||||
|
discovery_infos = await IgdDevice.async_discover()
|
||||||
|
|
||||||
|
# add extra info and store devices
|
||||||
|
devices = []
|
||||||
|
for discovery_info in discovery_infos:
|
||||||
|
discovery_info['udn'] = discovery_info['usn'].split('::')[0]
|
||||||
|
discovery_info['ssdp_description'] = discovery_info['location']
|
||||||
|
discovery_info['source'] = 'async_upnp_client'
|
||||||
|
_LOGGER.debug('Discovered device: %s', discovery_info)
|
||||||
|
|
||||||
|
devices.append(discovery_info)
|
||||||
|
|
||||||
|
return devices
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def async_create_device(cls,
|
async def async_create_device(cls,
|
||||||
hass: HomeAssistantType,
|
hass: HomeAssistantType,
|
||||||
|
@ -34,7 +55,7 @@ class Device:
|
||||||
disable_state_variable_validation=True)
|
disable_state_variable_validation=True)
|
||||||
upnp_device = await factory.async_create_device(ssdp_description)
|
upnp_device = await factory.async_create_device(ssdp_description)
|
||||||
|
|
||||||
# wrap with async_upnp_client IgdDevice
|
# wrap with async_upnp_client.IgdDevice
|
||||||
from async_upnp_client.igd import IgdDevice
|
from async_upnp_client.igd import IgdDevice
|
||||||
igd_device = IgdDevice(upnp_device, None)
|
igd_device = IgdDevice(upnp_device, None)
|
||||||
|
|
||||||
|
@ -50,6 +71,11 @@ class Device:
|
||||||
"""Get the name."""
|
"""Get the name."""
|
||||||
return self._igd_device.name
|
return self._igd_device.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def manufacturer(self):
|
||||||
|
"""Get the manufacturer."""
|
||||||
|
return self._igd_device.manufacturer
|
||||||
|
|
||||||
async def async_add_port_mappings(self, ports, local_ip):
|
async def async_add_port_mappings(self, ports, local_ip):
|
||||||
"""Add port mappings."""
|
"""Add port mappings."""
|
||||||
if local_ip == '127.0.0.1':
|
if local_ip == '127.0.0.1':
|
||||||
|
|
|
@ -1,24 +1,15 @@
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
|
"title": "UPnP/IGD",
|
||||||
|
"step": {
|
||||||
|
"confirm": {
|
||||||
"title": "UPnP/IGD",
|
"title": "UPnP/IGD",
|
||||||
"step": {
|
"description": "Do you want to set up UPnP/IGD?"
|
||||||
"init": {
|
}
|
||||||
"title": "UPnP/IGD"
|
},
|
||||||
},
|
"abort": {
|
||||||
"user": {
|
"single_instance_allowed": "Only a single configuration of UPnP/IGD is necessary.",
|
||||||
"title": "Configuration options for the UPnP/IGD",
|
"no_devices_found": "No UPnP/IGD devices found on the network."
|
||||||
"data":{
|
|
||||||
"igd": "UPnP/IGD",
|
|
||||||
"enable_sensors": "Add traffic sensors",
|
|
||||||
"enable_port_mapping": "Enable port mapping for Home Assistant"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"abort": {
|
|
||||||
"no_devices_discovered": "No UPnP/IGDs discovered",
|
|
||||||
"incomplete_device": "Ignoring incomplete UPnP device",
|
|
||||||
"already_configured": "UPnP/IGD is already configured",
|
|
||||||
"no_sensors_or_port_mapping": "Enable at least sensors or port mapping"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ STORAGE_VERSION = 1
|
||||||
SAVE_DELAY = 10
|
SAVE_DELAY = 10
|
||||||
|
|
||||||
CONNECTION_NETWORK_MAC = 'mac'
|
CONNECTION_NETWORK_MAC = 'mac'
|
||||||
|
CONNECTION_UPNP = 'upnp'
|
||||||
CONNECTION_ZIGBEE = 'zigbee'
|
CONNECTION_ZIGBEE = 'zigbee'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ asterisk_mbox==0.5.0
|
||||||
|
|
||||||
# homeassistant.components.upnp
|
# homeassistant.components.upnp
|
||||||
# homeassistant.components.media_player.dlna_dmr
|
# homeassistant.components.media_player.dlna_dmr
|
||||||
async-upnp-client==0.13.2
|
async-upnp-client==0.13.7
|
||||||
|
|
||||||
# homeassistant.components.light.avion
|
# homeassistant.components.light.avion
|
||||||
# avion==0.10
|
# avion==0.10
|
||||||
|
|
|
@ -1,261 +0,0 @@
|
||||||
"""Tests for UPnP/IGD config flow."""
|
|
||||||
|
|
||||||
from homeassistant.components import upnp
|
|
||||||
from homeassistant.components.upnp import config_flow as upnp_config_flow
|
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_none_discovered(hass):
|
|
||||||
"""Test no device discovered flow."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'discovered': {}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await flow.async_step_user()
|
|
||||||
assert result['type'] == 'abort'
|
|
||||||
assert result['reason'] == 'no_devices_discovered'
|
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_already_configured(hass):
|
|
||||||
"""Test device already configured flow."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
|
|
||||||
# discovered device
|
|
||||||
udn = 'uuid:device_1'
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'discovered': {
|
|
||||||
udn: {
|
|
||||||
'friendly_name': '192.168.1.1 (Test device)',
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
'udn': udn,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# configured entry
|
|
||||||
MockConfigEntry(domain=upnp.DOMAIN, data={
|
|
||||||
'udn': udn,
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
}).add_to_hass(hass)
|
|
||||||
|
|
||||||
result = await flow.async_step_user({
|
|
||||||
'name': '192.168.1.1 (Test device)',
|
|
||||||
'enable_sensors': True,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
})
|
|
||||||
assert result['type'] == 'abort'
|
|
||||||
assert result['reason'] == 'already_configured'
|
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_no_sensors_no_port_mapping(hass):
|
|
||||||
"""Test single device, no sensors, no port_mapping."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
|
|
||||||
# discovered device
|
|
||||||
udn = 'uuid:device_1'
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'discovered': {
|
|
||||||
udn: {
|
|
||||||
'friendly_name': '192.168.1.1 (Test device)',
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
'udn': udn,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# configured entry
|
|
||||||
MockConfigEntry(domain=upnp.DOMAIN, data={
|
|
||||||
'udn': udn,
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
}).add_to_hass(hass)
|
|
||||||
|
|
||||||
result = await flow.async_step_user({
|
|
||||||
'name': '192.168.1.1 (Test device)',
|
|
||||||
'enable_sensors': False,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
})
|
|
||||||
assert result['type'] == 'abort'
|
|
||||||
assert result['reason'] == 'no_sensors_or_port_mapping'
|
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_discovered_form(hass):
|
|
||||||
"""Test single device discovered, show form flow."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
|
|
||||||
# discovered device
|
|
||||||
udn = 'uuid:device_1'
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'discovered': {
|
|
||||||
udn: {
|
|
||||||
'friendly_name': '192.168.1.1 (Test device)',
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
'udn': udn,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await flow.async_step_user()
|
|
||||||
assert result['type'] == 'form'
|
|
||||||
assert result['step_id'] == 'user'
|
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_two_discovered_form(hass):
|
|
||||||
"""Test two devices discovered, show form flow with two devices."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
|
|
||||||
# discovered device
|
|
||||||
udn_1 = 'uuid:device_1'
|
|
||||||
udn_2 = 'uuid:device_2'
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'discovered': {
|
|
||||||
udn_1: {
|
|
||||||
'friendly_name': '192.168.1.1 (Test device)',
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
'udn': udn_1,
|
|
||||||
},
|
|
||||||
udn_2: {
|
|
||||||
'friendly_name': '192.168.2.1 (Test device)',
|
|
||||||
'host': '192.168.2.1',
|
|
||||||
'udn': udn_2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await flow.async_step_user()
|
|
||||||
assert result['type'] == 'form'
|
|
||||||
assert result['step_id'] == 'user'
|
|
||||||
assert result['data_schema']({
|
|
||||||
'name': '192.168.1.1 (Test device)',
|
|
||||||
'enable_sensors': True,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
})
|
|
||||||
assert result['data_schema']({
|
|
||||||
'name': '192.168.2.1 (Test device)',
|
|
||||||
'enable_sensors': True,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
async def test_config_entry_created(hass):
|
|
||||||
"""Test config entry is created."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
|
|
||||||
# discovered device
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'discovered': {
|
|
||||||
'uuid:device_1': {
|
|
||||||
'friendly_name': '192.168.1.1 (Test device)',
|
|
||||||
'name': 'Test device 1',
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
|
||||||
'udn': 'uuid:device_1',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await flow.async_step_user({
|
|
||||||
'name': '192.168.1.1 (Test device)',
|
|
||||||
'enable_sensors': True,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
})
|
|
||||||
assert result['type'] == 'create_entry'
|
|
||||||
assert result['data'] == {
|
|
||||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
|
||||||
'udn': 'uuid:device_1',
|
|
||||||
'port_mapping': False,
|
|
||||||
'sensors': True,
|
|
||||||
}
|
|
||||||
assert result['title'] == 'Test device 1'
|
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_discovery_no_data(hass):
|
|
||||||
"""Test creation of device with auto_config."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
|
|
||||||
# auto_config active
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'auto_config': {
|
|
||||||
'active': True,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
'enable_sensors': True,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# discovered device
|
|
||||||
result = await flow.async_step_discovery({})
|
|
||||||
|
|
||||||
assert result['type'] == 'abort'
|
|
||||||
assert result['reason'] == 'incomplete_device'
|
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_discovery_auto_config_sensors(hass):
|
|
||||||
"""Test creation of device with auto_config."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
|
|
||||||
# auto_config active
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'auto_config': {
|
|
||||||
'active': True,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
'enable_sensors': True,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# discovered device
|
|
||||||
result = await flow.async_step_discovery({
|
|
||||||
'name': 'Test device 1',
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
|
||||||
'udn': 'uuid:device_1',
|
|
||||||
})
|
|
||||||
|
|
||||||
assert result['type'] == 'create_entry'
|
|
||||||
assert result['data'] == {
|
|
||||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
|
||||||
'udn': 'uuid:device_1',
|
|
||||||
'sensors': True,
|
|
||||||
'port_mapping': False,
|
|
||||||
}
|
|
||||||
assert result['title'] == 'Test device 1'
|
|
||||||
|
|
||||||
|
|
||||||
async def test_flow_discovery_auto_config_sensors_port_mapping(hass):
|
|
||||||
"""Test creation of device with auto_config, with port mapping."""
|
|
||||||
flow = upnp_config_flow.UpnpFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
|
|
||||||
# auto_config active, with port_mapping
|
|
||||||
hass.data[upnp.DOMAIN] = {
|
|
||||||
'auto_config': {
|
|
||||||
'active': True,
|
|
||||||
'enable_port_mapping': True,
|
|
||||||
'enable_sensors': True,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# discovered device
|
|
||||||
result = await flow.async_step_discovery({
|
|
||||||
'name': 'Test device 1',
|
|
||||||
'host': '192.168.1.1',
|
|
||||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
|
||||||
'udn': 'uuid:device_1',
|
|
||||||
})
|
|
||||||
|
|
||||||
assert result['type'] == 'create_entry'
|
|
||||||
assert result['data'] == {
|
|
||||||
'udn': 'uuid:device_1',
|
|
||||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
|
||||||
'sensors': True,
|
|
||||||
'port_mapping': True,
|
|
||||||
}
|
|
||||||
assert result['title'] == 'Test device 1'
|
|
|
@ -26,7 +26,7 @@ class MockDevice(Device):
|
||||||
@classmethod
|
@classmethod
|
||||||
async def async_create_device(cls, hass, ssdp_description):
|
async def async_create_device(cls, hass, ssdp_description):
|
||||||
"""Return self."""
|
"""Return self."""
|
||||||
return cls()
|
return cls('UDN')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def udn(self):
|
def udn(self):
|
||||||
|
@ -47,102 +47,10 @@ class MockDevice(Device):
|
||||||
self.removed_port_mappings.append(entry)
|
self.removed_port_mappings.append(entry)
|
||||||
|
|
||||||
|
|
||||||
async def test_async_setup_no_auto_config(hass):
|
|
||||||
"""Test async_setup."""
|
|
||||||
# setup component, enable auto_config
|
|
||||||
config = {
|
|
||||||
'discovery': {},
|
|
||||||
# no upnp
|
|
||||||
}
|
|
||||||
with MockDependency('netdisco.discovery'), \
|
|
||||||
patch('homeassistant.components.upnp.config_flow.get_local_ip',
|
|
||||||
return_value='192.168.1.10'):
|
|
||||||
await async_setup_component(hass, 'upnp', config)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert hass.data[upnp.DOMAIN]['auto_config'] == {
|
|
||||||
'active': False,
|
|
||||||
'enable_sensors': False,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
'ports': {'hass': 'hass'},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_setup_auto_config(hass):
|
|
||||||
"""Test async_setup."""
|
|
||||||
# setup component, enable auto_config
|
|
||||||
config = {
|
|
||||||
'discovery': {},
|
|
||||||
'upnp': {},
|
|
||||||
}
|
|
||||||
with MockDependency('netdisco.discovery'), \
|
|
||||||
patch('homeassistant.components.upnp.config_flow.get_local_ip',
|
|
||||||
return_value='192.168.1.10'):
|
|
||||||
await async_setup_component(hass, 'upnp', config)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert hass.data[upnp.DOMAIN]['auto_config'] == {
|
|
||||||
'active': True,
|
|
||||||
'enable_sensors': True,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
'ports': {'hass': 'hass'},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_setup_auto_config_port_mapping(hass):
|
|
||||||
"""Test async_setup."""
|
|
||||||
# setup component, enable auto_config
|
|
||||||
config = {
|
|
||||||
'discovery': {},
|
|
||||||
'upnp': {
|
|
||||||
'port_mapping': True,
|
|
||||||
'ports': {'hass': 'hass'},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
with MockDependency('netdisco.discovery'), \
|
|
||||||
patch('homeassistant.components.upnp.config_flow.get_local_ip',
|
|
||||||
return_value='192.168.1.10'):
|
|
||||||
await async_setup_component(hass, 'upnp', config)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert hass.data[upnp.DOMAIN]['auto_config'] == {
|
|
||||||
'active': True,
|
|
||||||
'enable_sensors': True,
|
|
||||||
'enable_port_mapping': True,
|
|
||||||
'ports': {'hass': 'hass'},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_setup_auto_config_no_sensors(hass):
|
|
||||||
"""Test async_setup."""
|
|
||||||
# setup component, enable auto_config
|
|
||||||
config = {
|
|
||||||
'discovery': {},
|
|
||||||
'upnp': {'sensors': False},
|
|
||||||
}
|
|
||||||
with MockDependency('netdisco.discovery'), \
|
|
||||||
patch('homeassistant.components.upnp.config_flow.get_local_ip',
|
|
||||||
return_value='192.168.1.10'):
|
|
||||||
await async_setup_component(hass, 'upnp', config)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert hass.data[upnp.DOMAIN]['auto_config'] == {
|
|
||||||
'active': True,
|
|
||||||
'enable_sensors': False,
|
|
||||||
'enable_port_mapping': False,
|
|
||||||
'ports': {'hass': 'hass'},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_setup_entry_default(hass):
|
async def test_async_setup_entry_default(hass):
|
||||||
"""Test async_setup_entry."""
|
"""Test async_setup_entry."""
|
||||||
udn = 'uuid:device_1'
|
udn = 'uuid:device_1'
|
||||||
entry = MockConfigEntry(domain=upnp.DOMAIN, data={
|
entry = MockConfigEntry(domain=upnp.DOMAIN)
|
||||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
|
||||||
'udn': udn,
|
|
||||||
'sensors': True,
|
|
||||||
'port_mapping': False,
|
|
||||||
})
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
'http': {},
|
'http': {},
|
||||||
|
@ -150,7 +58,7 @@ async def test_async_setup_entry_default(hass):
|
||||||
# no upnp
|
# no upnp
|
||||||
}
|
}
|
||||||
with MockDependency('netdisco.discovery'), \
|
with MockDependency('netdisco.discovery'), \
|
||||||
patch('homeassistant.components.upnp.config_flow.get_local_ip',
|
patch('homeassistant.components.upnp.get_local_ip',
|
||||||
return_value='192.168.1.10'):
|
return_value='192.168.1.10'):
|
||||||
await async_setup_component(hass, 'http', config)
|
await async_setup_component(hass, 'http', config)
|
||||||
await async_setup_component(hass, 'upnp', config)
|
await async_setup_component(hass, 'upnp', config)
|
||||||
|
@ -158,17 +66,23 @@ async def test_async_setup_entry_default(hass):
|
||||||
|
|
||||||
# mock homeassistant.components.upnp.device.Device
|
# mock homeassistant.components.upnp.device.Device
|
||||||
mock_device = MockDevice(udn)
|
mock_device = MockDevice(udn)
|
||||||
with patch.object(Device, 'async_create_device') as create_device:
|
discovery_infos = [{
|
||||||
|
'udn': udn,
|
||||||
|
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
||||||
|
}]
|
||||||
|
with patch.object(Device, 'async_create_device') as create_device, \
|
||||||
|
patch.object(Device, 'async_discover') as async_discover: # noqa:E125
|
||||||
|
|
||||||
create_device.return_value = mock_coro(return_value=mock_device)
|
create_device.return_value = mock_coro(return_value=mock_device)
|
||||||
with patch('homeassistant.components.upnp.config_flow.get_local_ip',
|
async_discover.return_value = mock_coro(return_value=discovery_infos)
|
||||||
return_value='192.168.1.10'):
|
|
||||||
assert await upnp.async_setup_entry(hass, entry) is True
|
|
||||||
|
|
||||||
# ensure device is stored/used
|
assert await upnp.async_setup_entry(hass, entry) is True
|
||||||
assert hass.data[upnp.DOMAIN]['devices'][udn] == mock_device
|
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
# ensure device is stored/used
|
||||||
await hass.async_block_till_done()
|
assert hass.data[upnp.DOMAIN]['devices'][udn] == mock_device
|
||||||
|
|
||||||
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# ensure no port-mappings created or removed
|
# ensure no port-mappings created or removed
|
||||||
assert not mock_device.added_port_mappings
|
assert not mock_device.added_port_mappings
|
||||||
|
@ -177,13 +91,9 @@ async def test_async_setup_entry_default(hass):
|
||||||
|
|
||||||
async def test_async_setup_entry_port_mapping(hass):
|
async def test_async_setup_entry_port_mapping(hass):
|
||||||
"""Test async_setup_entry."""
|
"""Test async_setup_entry."""
|
||||||
|
# pylint: disable=invalid-name
|
||||||
udn = 'uuid:device_1'
|
udn = 'uuid:device_1'
|
||||||
entry = MockConfigEntry(domain=upnp.DOMAIN, data={
|
entry = MockConfigEntry(domain=upnp.DOMAIN)
|
||||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
|
||||||
'udn': udn,
|
|
||||||
'sensors': False,
|
|
||||||
'port_mapping': True,
|
|
||||||
})
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
'http': {},
|
'http': {},
|
||||||
|
@ -194,15 +104,22 @@ async def test_async_setup_entry_port_mapping(hass):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
with MockDependency('netdisco.discovery'), \
|
with MockDependency('netdisco.discovery'), \
|
||||||
patch('homeassistant.components.upnp.config_flow.get_local_ip',
|
patch('homeassistant.components.upnp.get_local_ip',
|
||||||
return_value='192.168.1.10'):
|
return_value='192.168.1.10'):
|
||||||
await async_setup_component(hass, 'http', config)
|
await async_setup_component(hass, 'http', config)
|
||||||
await async_setup_component(hass, 'upnp', config)
|
await async_setup_component(hass, 'upnp', config)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
mock_device = MockDevice(udn)
|
mock_device = MockDevice(udn)
|
||||||
with patch.object(Device, 'async_create_device') as create_device:
|
discovery_infos = [{
|
||||||
|
'udn': udn,
|
||||||
|
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
||||||
|
}]
|
||||||
|
with patch.object(Device, 'async_create_device') as create_device, \
|
||||||
|
patch.object(Device, 'async_discover') as async_discover: # noqa:E125
|
||||||
|
|
||||||
create_device.return_value = mock_coro(return_value=mock_device)
|
create_device.return_value = mock_coro(return_value=mock_device)
|
||||||
|
async_discover.return_value = mock_coro(return_value=discovery_infos)
|
||||||
|
|
||||||
assert await upnp.async_setup_entry(hass, entry) is True
|
assert await upnp.async_setup_entry(hass, entry) is True
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue